forked from maxence-charriere/go-app
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathui.go
More file actions
155 lines (126 loc) · 3.75 KB
/
ui.go
File metadata and controls
155 lines (126 loc) · 3.75 KB
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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package app
import (
"log"
"runtime"
"sync"
"github.com/murlokswarm/markup"
"github.com/satori/go.uuid"
)
var (
// UIChan is a channel which take a func as payload.
// When the app package is initialized, it creates a goroutine dedicated to
// execute UI related tasks in order to avoid to deal with concurrency when
// programming a component.
// UIChan allows to enqueue UI related tasks that should be executed in this
// goroutine.
// When implementing a driver, driver and component callbacks should be
// called through this channel.
UIChan = make(chan func(), 256)
elements ElementStorer
)
// Elementer is the interface that describes an app element.
// It wraps the ID method which allow to keep and retrieve a element.
//
// Driver implementation:
// - Elements().Add() should be called when an element is created.
// - Elements().Remove() should be called when an element is closed.
type Elementer interface {
// ID returns the identifier of the element.
ID() uuid.UUID
}
// ElementStorer is the interface that describes a element store.
// It keep a reference of the elements used by the app and allows to retrieve
// them when needed.
// Implementation should be thread safe.
type ElementStorer interface {
// Add adds an element in the store.
// Should be called in a driver implementation when a native element is
// created.
Add(e Elementer)
// Remove removes an element from the store.
// Should be called in a driver implementation when a native element is
// closed or removed.
Remove(e Elementer)
// Len returns the numbers of element in use.
Len() int
// Get returns the element with id.
// ok will be false if there is no element with id.
Get(id uuid.UUID) (e Elementer, ok bool)
}
// Contexter is the interface that describes an element where a component can be
// mounted and rendered. e.g. a window.
type Contexter interface {
Elementer
// Mount mounts the component c into the context. The context is displayed
// with the default appearance of the component.
//
// Driver implementation:
// - Should call markup.Mount()
Mount(c Componer)
// Component returns the component mounted with Mount().
// Returns nil if there is no component mounted.
Component() Componer
// Render renders the DOM node targeted into the sync description s.
// s contains info specifying if the node and its children should be
// replaced or just updated.
// Should be called in a driver implementation.
Render(s markup.Sync)
}
// Elements returns the element store containing all the elements in use by the
// app.
func Elements() ElementStorer {
return elements
}
// Context returns the context where c is mounted.
// Panic if there is no context where c is mounted.
func Context(c Componer) Contexter {
root := markup.Root(c)
elem, ok := Elements().Get(root.ContextID)
if !ok {
log.Panicf("no context with id %v in use", root.ContextID)
}
return elem.(Contexter)
}
func init() {
runtime.LockOSThread()
go startUIGoroutine()
elements = newElementStore()
}
func startUIGoroutine() {
for f := range UIChan {
f()
}
}
type elementStore struct {
mutex sync.Mutex
elems map[uuid.UUID]Elementer
}
func newElementStore() *elementStore {
return &elementStore{
elems: map[uuid.UUID]Elementer{},
}
}
func (s *elementStore) Add(e Elementer) {
s.mutex.Lock()
defer s.mutex.Unlock()
if _, ok := s.elems[e.ID()]; ok {
log.Panicf("[%s %T] is already registered", e.ID(), e)
}
s.elems[e.ID()] = e
}
func (s *elementStore) Remove(e Elementer) {
s.mutex.Lock()
defer s.mutex.Unlock()
delete(s.elems, e.ID())
}
func (s *elementStore) Len() int {
s.mutex.Lock()
defer s.mutex.Unlock()
return len(s.elems)
}
func (s *elementStore) Get(id uuid.UUID) (e Elementer, ok bool) {
s.mutex.Lock()
defer s.mutex.Unlock()
e, ok = s.elems[id]
return
}