/
pubsub.go
89 lines (75 loc) · 1.96 KB
/
pubsub.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
package prefs
import (
"github.com/diamondburned/gotk4/pkg/gtk/v4"
"github.com/diamondburned/gotkit/gtkutil"
)
type funcBox struct{ f func() }
// Pubsub provides a simple publish-subscribe API. This instance is safe to use
// concurrently.
type Pubsub struct {
funcs map[*funcBox]struct{}
pubing bool
}
// NewPubsub creates a new Pubsub instance.
func NewPubsub() *Pubsub {
return &Pubsub{
funcs: make(map[*funcBox]struct{}),
}
}
// Pubsubber returns itself.
func (p *Pubsub) Pubsubber() *Pubsub { return p }
// Publish publishes changes to all subscribe routines.
func (p *Pubsub) Publish() {
gtkutil.InvokeMain(func() {
// Prevent infinite recursion and break up the call chain.
if p.pubing {
return
}
p.pubing = true
for f := range p.funcs {
f.f()
}
p.pubing = false
})
}
// SubscribeWidget subscribes the given widget and callback to changes. If rm is
// called, then the subscription is removed. The given callback will be called
// once in the receiving goroutine to signal a change. It is guaranteed for the
// callback to only be consistently called on that goroutine.
func (p *Pubsub) SubscribeWidget(widget gtk.Widgetter, f func()) {
var unsub func()
w := gtk.BaseWidget(widget)
w.ConnectMap(func() {
unsub = p.Subscribe(f)
})
if w.Mapped() {
unsub = p.Subscribe(f)
}
w.ConnectUnmap(func() {
if unsub != nil {
unsub()
unsub = nil
}
})
}
// Subscribe adds f into the pubsub's subscription queue. f will always be
// invoked in the main thread.
func (p *Pubsub) Subscribe(f func()) (rm func()) {
b := &funcBox{f}
gtkutil.InvokeMain(func() {
p.funcs[b] = struct{}{}
f()
})
return func() {
gtkutil.InvokeMain(func() {
delete(p.funcs, b)
})
}
}
// SubscribeInit is like Subscribe, except you can't unsubscribe, the callback
// is not called, and the method is not thread-safe. It is only meant to be
// called in init() functions.
func (p *Pubsub) SubscribeInit(f func()) {
b := &funcBox{f}
p.funcs[b] = struct{}{}
}