1 package tview
2
3 import (
4 "github.com/gdamore/tcell/v2"
5 )
6
7
8
9
10
11
12
13
14
15
16 type Box struct {
17
18 x, y, width, height int
19
20
21 innerX, innerY, innerWidth, innerHeight int
22
23
24 paddingTop, paddingBottom, paddingLeft, paddingRight int
25
26
27 backgroundColor tcell.Color
28
29
30 dontClear bool
31
32
33
34 border bool
35
36
37 borderStyle tcell.Style
38
39
40 title string
41
42
43 titleColor tcell.Color
44
45
46 titleAlign int
47
48
49
50
51 hasFocus bool
52
53
54
55 focus, blur func()
56
57
58
59
60 inputCapture func(event *tcell.EventKey) *tcell.EventKey
61
62
63 draw func(screen tcell.Screen, x, y, width, height int) (int, int, int, int)
64
65
66
67
68 mouseCapture func(action MouseAction, event *tcell.EventMouse) (MouseAction, *tcell.EventMouse)
69 }
70
71
72 func NewBox() *Box {
73 b := &Box{
74 width: 15,
75 height: 10,
76 innerX: -1,
77 backgroundColor: Styles.PrimitiveBackgroundColor,
78 borderStyle: tcell.StyleDefault.Foreground(Styles.BorderColor).Background(Styles.PrimitiveBackgroundColor),
79 titleColor: Styles.TitleColor,
80 titleAlign: AlignCenter,
81 }
82 return b
83 }
84
85
86 func (b *Box) SetBorderPadding(top, bottom, left, right int) *Box {
87 b.paddingTop, b.paddingBottom, b.paddingLeft, b.paddingRight = top, bottom, left, right
88 return b
89 }
90
91
92
93 func (b *Box) GetRect() (int, int, int, int) {
94 return b.x, b.y, b.width, b.height
95 }
96
97
98
99
100 func (b *Box) GetInnerRect() (int, int, int, int) {
101 if b.innerX >= 0 {
102 return b.innerX, b.innerY, b.innerWidth, b.innerHeight
103 }
104 x, y, width, height := b.GetRect()
105 if b.border {
106 x++
107 y++
108 width -= 2
109 height -= 2
110 }
111 x, y, width, height = x+b.paddingLeft,
112 y+b.paddingTop,
113 width-b.paddingLeft-b.paddingRight,
114 height-b.paddingTop-b.paddingBottom
115 if width < 0 {
116 width = 0
117 }
118 if height < 0 {
119 height = 0
120 }
121 return x, y, width, height
122 }
123
124
125
126
127
128
129 func (b *Box) SetRect(x, y, width, height int) {
130 b.x = x
131 b.y = y
132 b.width = width
133 b.height = height
134 b.innerX = -1
135 }
136
137
138
139
140
141
142
143
144
145 func (b *Box) SetDrawFunc(handler func(screen tcell.Screen, x, y, width, height int) (int, int, int, int)) *Box {
146 b.draw = handler
147 return b
148 }
149
150
151
152 func (b *Box) GetDrawFunc() func(screen tcell.Screen, x, y, width, height int) (int, int, int, int) {
153 return b.draw
154 }
155
156
157
158
159
160
161 func (b *Box) WrapInputHandler(inputHandler func(*tcell.EventKey, func(p Primitive))) func(*tcell.EventKey, func(p Primitive)) {
162 return func(event *tcell.EventKey, setFocus func(p Primitive)) {
163 if b.inputCapture != nil {
164 event = b.inputCapture(event)
165 }
166 if event != nil && inputHandler != nil {
167 inputHandler(event, setFocus)
168 }
169 }
170 }
171
172
173 func (b *Box) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
174 return b.WrapInputHandler(nil)
175 }
176
177
178
179
180
181
182
183
184
185
186
187 func (b *Box) SetInputCapture(capture func(event *tcell.EventKey) *tcell.EventKey) *Box {
188 b.inputCapture = capture
189 return b
190 }
191
192
193
194 func (b *Box) GetInputCapture() func(event *tcell.EventKey) *tcell.EventKey {
195 return b.inputCapture
196 }
197
198
199
200
201
202
203 func (b *Box) WrapMouseHandler(mouseHandler func(MouseAction, *tcell.EventMouse, func(p Primitive)) (bool, Primitive)) func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
204 return func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
205 if b.mouseCapture != nil {
206 action, event = b.mouseCapture(action, event)
207 }
208 if event != nil && mouseHandler != nil {
209 consumed, capture = mouseHandler(action, event, setFocus)
210 }
211 return
212 }
213 }
214
215
216 func (b *Box) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
217 return b.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
218 if action == MouseLeftDown && b.InRect(event.Position()) {
219 setFocus(b)
220 consumed = true
221 }
222 return
223 })
224 }
225
226
227
228
229
230
231
232
233
234 func (b *Box) SetMouseCapture(capture func(action MouseAction, event *tcell.EventMouse) (MouseAction, *tcell.EventMouse)) *Box {
235 b.mouseCapture = capture
236 return b
237 }
238
239
240
241 func (b *Box) InRect(x, y int) bool {
242 rectX, rectY, width, height := b.GetRect()
243 return x >= rectX && x < rectX+width && y >= rectY && y < rectY+height
244 }
245
246
247
248 func (b *Box) GetMouseCapture() func(action MouseAction, event *tcell.EventMouse) (MouseAction, *tcell.EventMouse) {
249 return b.mouseCapture
250 }
251
252
253 func (b *Box) SetBackgroundColor(color tcell.Color) *Box {
254 b.backgroundColor = color
255 b.borderStyle = b.borderStyle.Background(color)
256 return b
257 }
258
259
260
261 func (b *Box) SetBorder(show bool) *Box {
262 b.border = show
263 return b
264 }
265
266
267 func (b *Box) SetBorderStyle(style tcell.Style) *Box {
268 b.borderStyle = style
269 return b
270 }
271
272
273 func (b *Box) SetBorderColor(color tcell.Color) *Box {
274 b.borderStyle = b.borderStyle.Foreground(color)
275 return b
276 }
277
278
279
280
281
282 func (b *Box) SetBorderAttributes(attr tcell.AttrMask) *Box {
283 b.borderStyle = b.borderStyle.Attributes(attr)
284 return b
285 }
286
287
288 func (b *Box) GetBorderAttributes() tcell.AttrMask {
289 _, _, attr := b.borderStyle.Decompose()
290 return attr
291 }
292
293
294 func (b *Box) GetBorderColor() tcell.Color {
295 color, _, _ := b.borderStyle.Decompose()
296 return color
297 }
298
299
300 func (b *Box) GetBackgroundColor() tcell.Color {
301 return b.backgroundColor
302 }
303
304
305 func (b *Box) SetTitle(title string) *Box {
306 b.title = title
307 return b
308 }
309
310
311 func (b *Box) GetTitle() string {
312 return b.title
313 }
314
315
316 func (b *Box) SetTitleColor(color tcell.Color) *Box {
317 b.titleColor = color
318 return b
319 }
320
321
322
323 func (b *Box) SetTitleAlign(align int) *Box {
324 b.titleAlign = align
325 return b
326 }
327
328
329 func (b *Box) Draw(screen tcell.Screen) {
330 b.DrawForSubclass(screen, b)
331 }
332
333
334
335
336
337
338
339 func (b *Box) DrawForSubclass(screen tcell.Screen, p Primitive) {
340
341 if b.width <= 0 || b.height <= 0 {
342 return
343 }
344
345
346 background := tcell.StyleDefault.Background(b.backgroundColor)
347 if !b.dontClear {
348 for y := b.y; y < b.y+b.height; y++ {
349 for x := b.x; x < b.x+b.width; x++ {
350 screen.SetContent(x, y, ' ', nil, background)
351 }
352 }
353 }
354
355
356 if b.border && b.width >= 2 && b.height >= 2 {
357 var vertical, horizontal, topLeft, topRight, bottomLeft, bottomRight rune
358 if p.HasFocus() {
359 horizontal = Borders.HorizontalFocus
360 vertical = Borders.VerticalFocus
361 topLeft = Borders.TopLeftFocus
362 topRight = Borders.TopRightFocus
363 bottomLeft = Borders.BottomLeftFocus
364 bottomRight = Borders.BottomRightFocus
365 } else {
366 horizontal = Borders.Horizontal
367 vertical = Borders.Vertical
368 topLeft = Borders.TopLeft
369 topRight = Borders.TopRight
370 bottomLeft = Borders.BottomLeft
371 bottomRight = Borders.BottomRight
372 }
373 for x := b.x + 1; x < b.x+b.width-1; x++ {
374 screen.SetContent(x, b.y, horizontal, nil, b.borderStyle)
375 screen.SetContent(x, b.y+b.height-1, horizontal, nil, b.borderStyle)
376 }
377 for y := b.y + 1; y < b.y+b.height-1; y++ {
378 screen.SetContent(b.x, y, vertical, nil, b.borderStyle)
379 screen.SetContent(b.x+b.width-1, y, vertical, nil, b.borderStyle)
380 }
381 screen.SetContent(b.x, b.y, topLeft, nil, b.borderStyle)
382 screen.SetContent(b.x+b.width-1, b.y, topRight, nil, b.borderStyle)
383 screen.SetContent(b.x, b.y+b.height-1, bottomLeft, nil, b.borderStyle)
384 screen.SetContent(b.x+b.width-1, b.y+b.height-1, bottomRight, nil, b.borderStyle)
385
386
387 if b.title != "" && b.width >= 4 {
388 printed, _ := Print(screen, b.title, b.x+1, b.y, b.width-2, b.titleAlign, b.titleColor)
389 if len(b.title)-printed > 0 && printed > 0 {
390 _, _, style, _ := screen.GetContent(b.x+b.width-2, b.y)
391 fg, _, _ := style.Decompose()
392 Print(screen, string(SemigraphicsHorizontalEllipsis), b.x+b.width-2, b.y, 1, AlignLeft, fg)
393 }
394 }
395 }
396
397
398 if b.draw != nil {
399 b.innerX, b.innerY, b.innerWidth, b.innerHeight = b.draw(screen, b.x, b.y, b.width, b.height)
400 } else {
401
402 b.innerX = -1
403 b.innerX, b.innerY, b.innerWidth, b.innerHeight = b.GetInnerRect()
404 }
405 }
406
407
408
409
410
411
412 func (b *Box) SetFocusFunc(callback func()) *Box {
413 b.focus = callback
414 return b
415 }
416
417
418
419
420
421
422 func (b *Box) SetBlurFunc(callback func()) *Box {
423 b.blur = callback
424 return b
425 }
426
427
428 func (b *Box) Focus(delegate func(p Primitive)) {
429 b.hasFocus = true
430 if b.focus != nil {
431 b.focus()
432 }
433 }
434
435
436 func (b *Box) Blur() {
437 if b.blur != nil {
438 b.blur()
439 }
440 b.hasFocus = false
441 }
442
443
444 func (b *Box) HasFocus() bool {
445 return b.hasFocus
446 }
447
View as plain text