1 package cview
2
3 import (
4 "sync"
5
6 "github.com/gdamore/tcell/v2"
7 )
8
9
10 const (
11 FlexRow = iota
12 FlexColumn
13 )
14
15
16 type flexItem struct {
17 Item Primitive
18 FixedSize int
19 Proportion int
20 Focus bool
21 }
22
23
24
25
26
27 type Flex struct {
28 *Box
29
30
31 items []*flexItem
32
33
34 direction int
35
36
37
38 fullScreen bool
39
40 sync.RWMutex
41 }
42
43
44
45
46
47
48
49
50
51
52 func NewFlex() *Flex {
53 f := &Flex{
54 Box: NewBox(),
55 direction: FlexColumn,
56 }
57 f.SetBackgroundTransparent(true)
58 f.focus = f
59 return f
60 }
61
62
63
64 func (f *Flex) GetDirection() int {
65 f.RLock()
66 defer f.RUnlock()
67 return f.direction
68 }
69
70
71
72 func (f *Flex) SetDirection(direction int) {
73 f.Lock()
74 defer f.Unlock()
75
76 f.direction = direction
77 }
78
79
80
81 func (f *Flex) SetFullScreen(fullScreen bool) {
82 f.Lock()
83 defer f.Unlock()
84
85 f.fullScreen = fullScreen
86 }
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101 func (f *Flex) AddItem(item Primitive, fixedSize, proportion int, focus bool) {
102 f.Lock()
103 defer f.Unlock()
104
105 if item == nil {
106 item = NewBox()
107 item.SetVisible(false)
108 }
109
110 f.items = append(f.items, &flexItem{Item: item, FixedSize: fixedSize, Proportion: proportion, Focus: focus})
111 }
112
113
114
115 func (f *Flex) AddItemAtIndex(index int, item Primitive, fixedSize, proportion int, focus bool) {
116 f.Lock()
117 defer f.Unlock()
118 newItem := &flexItem{Item: item, FixedSize: fixedSize, Proportion: proportion, Focus: focus}
119
120 if index == 0 {
121 f.items = append([]*flexItem{newItem}, f.items...)
122 } else {
123 f.items = append(f.items[:index], append([]*flexItem{newItem}, f.items[index:]...)...)
124 }
125 }
126
127
128
129 func (f *Flex) RemoveItem(p Primitive) {
130 f.Lock()
131 defer f.Unlock()
132
133 for index := len(f.items) - 1; index >= 0; index-- {
134 if f.items[index].Item == p {
135 f.items = append(f.items[:index], f.items[index+1:]...)
136 }
137 }
138 }
139
140
141
142
143 func (f *Flex) ResizeItem(p Primitive, fixedSize, proportion int) {
144 f.Lock()
145 defer f.Unlock()
146
147 for _, item := range f.items {
148 if item.Item == p {
149 item.FixedSize = fixedSize
150 item.Proportion = proportion
151 }
152 }
153 }
154
155
156 func (f *Flex) Draw(screen tcell.Screen) {
157 if !f.GetVisible() {
158 return
159 }
160
161 f.Box.Draw(screen)
162
163 f.Lock()
164 defer f.Unlock()
165
166
167
168
169 if f.fullScreen {
170 width, height := screen.Size()
171 f.SetRect(0, 0, width, height)
172 }
173
174
175 x, y, width, height := f.GetInnerRect()
176 var proportionSum int
177 distSize := width
178 if f.direction == FlexRow {
179 distSize = height
180 }
181 for _, item := range f.items {
182 if item.FixedSize > 0 {
183 distSize -= item.FixedSize
184 } else {
185 proportionSum += item.Proportion
186 }
187 }
188
189
190 pos := x
191 if f.direction == FlexRow {
192 pos = y
193 }
194 for _, item := range f.items {
195 size := item.FixedSize
196 if size <= 0 {
197 if proportionSum > 0 {
198 size = distSize * item.Proportion / proportionSum
199 distSize -= size
200 proportionSum -= item.Proportion
201 } else {
202 size = 0
203 }
204 }
205 if item.Item != nil {
206 if f.direction == FlexColumn {
207 item.Item.SetRect(pos, y, size, height)
208 } else {
209 item.Item.SetRect(x, pos, width, size)
210 }
211 }
212 pos += size
213
214 if item.Item != nil {
215 if item.Item.GetFocusable().HasFocus() {
216 defer item.Item.Draw(screen)
217 } else {
218 item.Item.Draw(screen)
219 }
220 }
221 }
222 }
223
224
225 func (f *Flex) Focus(delegate func(p Primitive)) {
226 f.Lock()
227
228 for _, item := range f.items {
229 if item.Item != nil && item.Focus {
230 f.Unlock()
231 delegate(item.Item)
232 return
233 }
234 }
235
236 f.Unlock()
237 }
238
239
240 func (f *Flex) HasFocus() bool {
241 f.RLock()
242 defer f.RUnlock()
243
244 for _, item := range f.items {
245 if item.Item != nil && item.Item.GetFocusable().HasFocus() {
246 return true
247 }
248 }
249 return false
250 }
251
252
253 func (f *Flex) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
254 return f.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
255 if !f.InRect(event.Position()) {
256 return false, nil
257 }
258
259
260 for _, item := range f.items {
261 if item.Item == nil {
262 continue
263 }
264
265 consumed, capture = item.Item.MouseHandler()(action, event, setFocus)
266 if consumed {
267 return
268 }
269 }
270
271 return
272 })
273 }
274
View as plain text