1 package cview
2
3 import (
4 "sync"
5
6 "github.com/gdamore/tcell/v2"
7 )
8
9
10 type frameText struct {
11 Text string
12 Header bool
13 Align int
14 Color tcell.Color
15 }
16
17
18
19 type Frame struct {
20 *Box
21
22
23 primitive Primitive
24
25
26 text []*frameText
27
28
29 top, bottom, header, footer, left, right int
30
31 sync.RWMutex
32 }
33
34
35
36 func NewFrame(primitive Primitive) *Frame {
37 box := NewBox()
38
39 f := &Frame{
40 Box: box,
41 primitive: primitive,
42 top: 1,
43 bottom: 1,
44 header: 1,
45 footer: 1,
46 left: 1,
47 right: 1,
48 }
49
50 f.focus = f
51
52 return f
53 }
54
55
56
57
58
59
60
61 func (f *Frame) AddText(text string, header bool, align int, color tcell.Color) {
62 f.Lock()
63 defer f.Unlock()
64
65 f.text = append(f.text, &frameText{
66 Text: text,
67 Header: header,
68 Align: align,
69 Color: color,
70 })
71 }
72
73
74 func (f *Frame) Clear() {
75 f.Lock()
76 defer f.Unlock()
77
78 f.text = nil
79 }
80
81
82
83
84 func (f *Frame) SetBorders(top, bottom, header, footer, left, right int) {
85 f.Lock()
86 defer f.Unlock()
87
88 f.top, f.bottom, f.header, f.footer, f.left, f.right = top, bottom, header, footer, left, right
89 }
90
91
92 func (f *Frame) Draw(screen tcell.Screen) {
93 if !f.GetVisible() {
94 return
95 }
96
97 f.Box.Draw(screen)
98
99 f.Lock()
100 defer f.Unlock()
101
102
103 x, top, width, height := f.GetInnerRect()
104 bottom := top + height - 1
105 x += f.left
106 top += f.top
107 bottom -= f.bottom
108 width -= f.left + f.right
109 if width <= 0 || top >= bottom {
110 return
111 }
112
113
114 var rows [6]int
115 topMax := top
116 bottomMin := bottom
117 for _, text := range f.text {
118
119 var y int
120 if text.Header {
121 y = top + rows[text.Align]
122 rows[text.Align]++
123 if y >= bottomMin {
124 continue
125 }
126 if y+1 > topMax {
127 topMax = y + 1
128 }
129 } else {
130 y = bottom - rows[3+text.Align]
131 rows[3+text.Align]++
132 if y <= topMax {
133 continue
134 }
135 if y-1 < bottomMin {
136 bottomMin = y - 1
137 }
138 }
139
140
141 Print(screen, []byte(text.Text), x, y, width, text.Align, text.Color)
142 }
143
144
145 if topMax > top {
146 top = topMax + f.header
147 }
148 if bottomMin < bottom {
149 bottom = bottomMin - f.footer
150 }
151 if top > bottom {
152 return
153 }
154 f.primitive.SetRect(x, top, width, bottom+1-top)
155
156
157 f.primitive.Draw(screen)
158 }
159
160
161 func (f *Frame) Focus(delegate func(p Primitive)) {
162 f.Lock()
163 primitive := f.primitive
164 defer f.Unlock()
165
166 delegate(primitive)
167 }
168
169
170 func (f *Frame) HasFocus() bool {
171 f.RLock()
172 defer f.RUnlock()
173
174 focusable, ok := f.primitive.(Focusable)
175 if ok {
176 return focusable.HasFocus()
177 }
178 return false
179 }
180
181
182 func (f *Frame) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
183 return f.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
184 if !f.InRect(event.Position()) {
185 return false, nil
186 }
187
188
189 return f.primitive.MouseHandler()(action, event, setFocus)
190 })
191 }
192
View as plain text