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