1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package views
16
17 import (
18 "github.com/gdamore/tcell/v2"
19 )
20
21
22
23 type BoxLayout struct {
24 view View
25 orient Orientation
26 style tcell.Style
27 cells []*boxLayoutCell
28 width int
29 height int
30 changed bool
31
32 WidgetWatchers
33 }
34
35 type boxLayoutCell struct {
36 widget Widget
37 fill float64
38 pad int
39 frac float64
40 view *ViewPort
41 }
42
43 func (b *BoxLayout) hLayout() {
44 w, h := b.view.Size()
45
46 totf := 0.0
47 for _, c := range b.cells {
48 x, y := c.widget.Size()
49 totf += c.fill
50 b.width += x
51 if y > b.height {
52 b.height = y
53 }
54 c.pad = 0
55 c.frac = 0
56 }
57
58 extra := w - b.width
59 if extra < 0 {
60 extra = 0
61 }
62 resid := extra
63 if totf == 0 {
64 resid = 0
65 }
66
67 for _, c := range b.cells {
68 if c.fill > 0 {
69 c.frac = float64(extra) * c.fill / totf
70 c.pad = int(c.frac)
71 c.frac -= float64(c.pad)
72 resid -= c.pad
73 }
74 }
75
76
77
78
79 for resid > 0 {
80 var best *boxLayoutCell
81 for _, c := range b.cells {
82 if c.fill == 0 {
83 continue
84 }
85 if best == nil || c.frac > best.frac {
86 best = c
87 }
88 }
89 best.pad++
90 best.frac = 0
91 resid--
92 }
93
94 x, y, xinc := 0, 0, 0
95 for _, c := range b.cells {
96 cw, _ := c.widget.Size()
97
98 xinc = cw + c.pad
99 cw += c.pad
100
101 c.view.Resize(x, y, cw, h)
102 c.widget.Resize()
103 x += xinc
104 }
105 }
106
107 func (b *BoxLayout) vLayout() {
108 w, h := b.view.Size()
109
110 totf := 0.0
111 for _, c := range b.cells {
112 x, y := c.widget.Size()
113 b.height += y
114 totf += c.fill
115 if x > b.width {
116 b.width = x
117 }
118 c.pad = 0
119 c.frac = 0
120 }
121
122 extra := h - b.height
123 if extra < 0 {
124 extra = 0
125 }
126
127 resid := extra
128 if totf == 0 {
129 resid = 0
130 }
131
132 for _, c := range b.cells {
133 if c.fill > 0 {
134 c.frac = float64(extra) * c.fill / totf
135 c.pad = int(c.frac)
136 c.frac -= float64(c.pad)
137 resid -= c.pad
138 }
139 }
140
141
142
143
144 for resid > 0 {
145 var best *boxLayoutCell
146 for _, c := range b.cells {
147 if c.fill == 0 {
148 continue
149 }
150 if best == nil || c.frac > best.frac {
151 best = c
152 }
153 }
154 best.pad++
155 best.frac = 0
156 resid--
157 }
158
159 x, y, yinc := 0, 0, 0
160 for _, c := range b.cells {
161 _, ch := c.widget.Size()
162
163 yinc = ch + c.pad
164 ch += c.pad
165 c.view.Resize(x, y, w, ch)
166 c.widget.Resize()
167 y += yinc
168 }
169 }
170
171 func (b *BoxLayout) layout() {
172 if b.view == nil {
173 return
174 }
175 b.width, b.height = 0, 0
176 switch b.orient {
177 case Horizontal:
178 b.hLayout()
179 case Vertical:
180 b.vLayout()
181 default:
182 panic("Bad orientation")
183 }
184 b.changed = false
185 }
186
187
188 func (b *BoxLayout) Resize() {
189 b.layout()
190
191
192 for i := range b.cells {
193 b.cells[i].widget.Resize()
194 }
195 b.PostEventWidgetResize(b)
196 }
197
198
199 func (b *BoxLayout) Draw() {
200
201 if b.view == nil {
202 return
203 }
204 if b.changed {
205 b.layout()
206 }
207 b.view.Fill(' ', b.style)
208 for i := range b.cells {
209 b.cells[i].widget.Draw()
210 }
211 }
212
213
214 func (b *BoxLayout) Size() (int, int) {
215 return b.width, b.height
216 }
217
218
219 func (b *BoxLayout) SetView(view View) {
220 b.changed = true
221 b.view = view
222 for _, c := range b.cells {
223 c.view.SetView(view)
224 }
225 }
226
227
228
229
230
231 func (b *BoxLayout) HandleEvent(ev tcell.Event) bool {
232 switch ev.(type) {
233 case *EventWidgetContent:
234
235 b.changed = true
236 b.PostEventWidgetContent(b)
237 return true
238 }
239 for _, c := range b.cells {
240 if c.widget.HandleEvent(ev) {
241 return true
242 }
243 }
244 return false
245 }
246
247
248 func (b *BoxLayout) AddWidget(widget Widget, fill float64) {
249 c := &boxLayoutCell{
250 widget: widget,
251 fill: fill,
252 view: NewViewPort(b.view, 0, 0, 0, 0),
253 }
254 widget.SetView(c.view)
255 b.cells = append(b.cells, c)
256 b.changed = true
257 widget.Watch(b)
258 b.layout()
259 b.PostEventWidgetContent(b)
260 }
261
262
263
264
265 func (b *BoxLayout) InsertWidget(index int, widget Widget, fill float64) {
266 c := &boxLayoutCell{
267 widget: widget,
268 fill: fill,
269 view: NewViewPort(b.view, 0, 0, 0, 0),
270 }
271 c.widget.SetView(c.view)
272 if index < 0 {
273 index = 0
274 }
275 if index > len(b.cells) {
276 index = len(b.cells)
277 }
278 b.cells = append(b.cells, c)
279 copy(b.cells[index+1:], b.cells[index:])
280 b.cells[index] = c
281 widget.Watch(b)
282 b.layout()
283 b.PostEventWidgetContent(b)
284 }
285
286
287 func (b *BoxLayout) RemoveWidget(widget Widget) {
288 changed := false
289 for i := 0; i < len(b.cells); i++ {
290 if b.cells[i].widget == widget {
291 b.cells = append(b.cells[:i], b.cells[i+1:]...)
292 changed = true
293 }
294 }
295 if !changed {
296 return
297 }
298 b.changed = true
299 widget.Unwatch(b)
300 b.layout()
301 b.PostEventWidgetContent(b)
302 }
303
304
305 func (b *BoxLayout) Widgets() []Widget {
306 w := make([]Widget, 0, len(b.cells))
307 for _, c := range b.cells {
308 w = append(w, c.widget)
309 }
310 return w
311 }
312
313
314 func (b *BoxLayout) SetOrientation(orient Orientation) {
315 if b.orient != orient {
316 b.orient = orient
317 b.changed = true
318 b.PostEventWidgetContent(b)
319 }
320 }
321
322
323 func (b *BoxLayout) SetStyle(style tcell.Style) {
324 b.style = style
325 b.PostEventWidgetContent(b)
326 }
327
328
329 func NewBoxLayout(orient Orientation) *BoxLayout {
330 return &BoxLayout{orient: orient}
331 }
332
View as plain text