]> git.0d.be Git - barnard.git/blob - uiterm/ui.go
31114c7aed1bd15995dccbcadacd1414e038a7b6
[barnard.git] / uiterm / ui.go
1 package uiterm
2
3 import (
4         "errors"
5         "sync/atomic"
6
7         "github.com/nsf/termbox-go"
8 )
9
10 type KeyListener func(ui *Ui, key Key)
11
12 type UiManager interface {
13         OnUiInitialize(ui *Ui)
14         OnUiResize(ui *Ui, width, height int)
15 }
16
17 type Ui struct {
18         Fg, Bg Attribute
19
20         close   chan bool
21         manager UiManager
22
23         drawCount     int32
24         elements      map[string]*uiElement
25         activeElement *uiElement
26
27         keyListeners map[Key][]KeyListener
28 }
29
30 type uiElement struct {
31         Name           string
32         X0, Y0, X1, Y1 int
33         View           View
34 }
35
36 func New(manager UiManager) *Ui {
37         ui := &Ui{
38                 close:        make(chan bool, 10),
39                 elements:     make(map[string]*uiElement),
40                 manager:      manager,
41                 keyListeners: make(map[Key][]KeyListener),
42         }
43         return ui
44 }
45
46 func (ui *Ui) Close() {
47         if termbox.IsInit {
48                 ui.close <- true
49         }
50 }
51
52 func (ui *Ui) Refresh() {
53         if termbox.IsInit {
54                 ui.beginDraw()
55                 defer ui.endDraw()
56
57                 termbox.Clear(termbox.Attribute(ui.Fg), termbox.Attribute(ui.Bg))
58                 termbox.HideCursor()
59                 for _, element := range ui.elements {
60                         element.View.uiDraw()
61                 }
62         }
63 }
64
65 func (ui *Ui) beginDraw() {
66         atomic.AddInt32(&ui.drawCount, 1)
67 }
68
69 func (ui *Ui) endDraw() {
70         if count := atomic.AddInt32(&ui.drawCount, -1); count == 0 {
71                 termbox.Flush()
72         }
73 }
74
75 func (ui *Ui) Active() string {
76         return ui.activeElement.Name
77 }
78
79 func (ui *Ui) SetActive(name string) {
80         element, _ := ui.elements[name]
81         if ui.activeElement != nil {
82                 ui.activeElement.View.uiSetActive(false)
83         }
84         ui.activeElement = element
85         if element != nil {
86                 element.View.uiSetActive(true)
87         }
88         ui.Refresh()
89 }
90
91 func (ui *Ui) Run() error {
92         if termbox.IsInit {
93                 return nil
94         }
95         if err := termbox.Init(); err != nil {
96                 return nil
97         }
98         defer termbox.Close()
99         termbox.SetInputMode(termbox.InputAlt)
100
101         events := make(chan termbox.Event)
102         go func() {
103                 for {
104                         events <- termbox.PollEvent()
105                 }
106         }()
107
108         ui.manager.OnUiInitialize(ui)
109         width, height := termbox.Size()
110         ui.manager.OnUiResize(ui, width, height)
111         ui.Refresh()
112
113         for {
114                 select {
115                 case <-ui.close:
116                         return nil
117                 case event := <-events:
118                         switch event.Type {
119                         case termbox.EventResize:
120                                 ui.manager.OnUiResize(ui, event.Width, event.Height)
121                                 ui.Refresh()
122                         case termbox.EventKey:
123                                 if event.Ch != 0 {
124                                         ui.onCharacterEvent(event.Ch)
125                                 } else {
126                                         ui.onKeyEvent(Modifier(event.Mod), Key(event.Key))
127                                 }
128                         }
129                 }
130         }
131 }
132
133 func (ui *Ui) onCharacterEvent(ch rune) {
134         if ui.activeElement != nil {
135                 ui.activeElement.View.uiCharacterEvent(ch)
136         }
137 }
138
139 func (ui *Ui) onKeyEvent(mod Modifier, key Key) {
140         if ui.keyListeners[key] != nil {
141                 for _, listener := range ui.keyListeners[key] {
142                         listener(ui, key)
143                 }
144         }
145         if ui.activeElement != nil {
146                 ui.activeElement.View.uiKeyEvent(mod, key)
147         }
148 }
149
150 func (ui *Ui) Add(name string, view View) error {
151         if _, ok := ui.elements[name]; ok {
152                 return errors.New("view already exists")
153         }
154         ui.elements[name] = &uiElement{
155                 Name: name,
156                 View: view,
157         }
158         view.uiInitialize(ui)
159         return nil
160 }
161
162 func (ui *Ui) SetBounds(name string, x0, y0, x1, y1 int) error {
163         element, ok := ui.elements[name]
164         if !ok {
165                 return errors.New("view does not exist")
166         }
167         element.X0, element.Y0, element.X1, element.Y1 = x0, y0, x1, y1
168         element.View.uiSetBounds(x0, y0, x1, y1)
169         return nil
170 }
171
172 func (ui *Ui) AddKeyListener(listener KeyListener, key Key) {
173         ui.keyListeners[key] = append(ui.keyListeners[key], listener)
174 }