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
102
103 func (f *Flex) AddItem(item Primitive, fixedSize, proportion int, focus bool) {
104 f.Lock()
105 defer f.Unlock()
106
107 if item == nil {
108 item = NewBox()
109 }
110
111 f.items = append(f.items, &flexItem{Item: item, FixedSize: fixedSize, Proportion: proportion, Focus: focus})
112 }
113
114
115
116 func (f *Flex) AddItemAtIndex(index int, item Primitive, fixedSize, proportion int, focus bool) {
117 f.Lock()
118 defer f.Unlock()
119 newItem := &flexItem{Item: item, FixedSize: fixedSize, Proportion: proportion, Focus: focus}
120
121 if index == 0 {
122 f.items = append([]*flexItem{newItem}, f.items...)
123 } else {
124 f.items = append(f.items[:index], append([]*flexItem{newItem}, f.items[index:]...)...)
125 }
126 }
127
128
129
130 func (f *Flex) RemoveItem(p Primitive) {
131 f.Lock()
132 defer f.Unlock()
133
134 for index := len(f.items) - 1; index >= 0; index-- {
135 if f.items[index].Item == p {
136 f.items = append(f.items[:index], f.items[index+1:]...)
137 }
138 }
139 }
140
141
142
143
144 func (f *Flex) ResizeItem(p Primitive, fixedSize, proportion int) {
145 f.Lock()
146 defer f.Unlock()
147
148 for _, item := range f.items {
149 if item.Item == p {
150 item.FixedSize = fixedSize
151 item.Proportion = proportion
152 }
153 }
154 }
155
156
157 func (f *Flex) Draw(screen tcell.Screen) {
158 if !f.GetVisible() {
159 return
160 }
161
162 f.Box.Draw(screen)
163
164 f.Lock()
165 defer f.Unlock()
166
167
168
169
170 if f.fullScreen {
171 width, height := screen.Size()
172 f.SetRect(0, 0, width, height)
173 }
174
175
176 x, y, width, height := f.GetInnerRect()
177 var proportionSum int
178 distSize := width
179 if f.direction == FlexRow {
180 distSize = height
181 }
182 for _, item := range f.items {
183 if item.FixedSize > 0 {
184 distSize -= item.FixedSize
185 } else {
186 proportionSum += item.Proportion
187 }
188 }
189
190
191 pos := x
192 if f.direction == FlexRow {
193 pos = y
194 }
195 for _, item := range f.items {
196 size := item.FixedSize
197 if size <= 0 {
198 if proportionSum > 0 {
199 size = distSize * item.Proportion / proportionSum
200 distSize -= size
201 proportionSum -= item.Proportion
202 } else {
203 size = 0
204 }
205 }
206 if item.Item != nil {
207 if f.direction == FlexColumn {
208 item.Item.SetRect(pos, y, size, height)
209 } else {
210 item.Item.SetRect(x, pos, width, size)
211 }
212 }
213 pos += size
214
215 if item.Item != nil {
216 if item.Item.GetFocusable().HasFocus() {
217 defer item.Item.Draw(screen)
218 } else {
219 item.Item.Draw(screen)
220 }
221 }
222 }
223 }
224
225
226 func (f *Flex) Focus(delegate func(p Primitive)) {
227 f.Lock()
228
229 for _, item := range f.items {
230 if item.Item != nil && item.Focus {
231 f.Unlock()
232 delegate(item.Item)
233 return
234 }
235 }
236
237 f.Unlock()
238 }
239
240
241 func (f *Flex) HasFocus() bool {
242 f.RLock()
243 defer f.RUnlock()
244
245 for _, item := range f.items {
246 if item.Item != nil && item.Item.GetFocusable().HasFocus() {
247 return true
248 }
249 }
250 return false
251 }
252
253
254 func (f *Flex) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
255 return f.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
256 if !f.InRect(event.Position()) {
257 return false, nil
258 }
259
260
261 for _, item := range f.items {
262 if item.Item == nil {
263 continue
264 }
265
266 consumed, capture = item.Item.MouseHandler()(action, event, setFocus)
267 if consumed {
268 return
269 }
270 }
271
272 return
273 })
274 }
275
View as plain text