frame.go (6252B)
1 package tview 2 3 import ( 4 "github.com/gdamore/tcell/v2" 5 ) 6 7 // frameText holds information about a line of text shown in the frame. 8 type frameText struct { 9 Text string // The text to be displayed. 10 Header bool // true = place in header, false = place in footer. 11 Align int // One of the Align constants. 12 Color tcell.Color // The text color. 13 } 14 15 // Frame is a wrapper which adds space around another primitive. In addition, 16 // the top area (header) and the bottom area (footer) may also contain text. 17 // 18 // See https://github.com/rivo/tview/wiki/Frame for an example. 19 type Frame struct { 20 *Box 21 22 // The contained primitive. May be nil. 23 primitive Primitive 24 25 // The lines of text to be displayed. 26 text []*frameText 27 28 // Border spacing. 29 top, bottom, header, footer, left, right int 30 31 // Keep a reference in case we need it when we change the primitive. 32 setFocus func(p Primitive) 33 } 34 35 // NewFrame returns a new frame around the given primitive. The primitive's 36 // size will be changed to fit within this frame. The primitive may be nil, in 37 // which case no other primitive is embedded in the frame. 38 func NewFrame(primitive Primitive) *Frame { 39 box := NewBox() 40 41 f := &Frame{ 42 Box: box, 43 primitive: primitive, 44 top: 1, 45 bottom: 1, 46 header: 1, 47 footer: 1, 48 left: 1, 49 right: 1, 50 } 51 52 return f 53 } 54 55 // SetPrimitive replaces the contained primitive with the given one. To remove 56 // a primitive, set it to nil. 57 func (f *Frame) SetPrimitive(p Primitive) *Frame { 58 var hasFocus bool 59 if f.primitive != nil { 60 hasFocus = f.primitive.HasFocus() 61 } 62 f.primitive = p 63 if hasFocus && f.setFocus != nil { 64 f.setFocus(p) // Restore focus. 65 } 66 return f 67 } 68 69 // GetPrimitive returns the primitive contained in this frame. 70 func (f *Frame) GetPrimitive() Primitive { 71 return f.primitive 72 } 73 74 // AddText adds text to the frame. Set "header" to true if the text is to appear 75 // in the header, above the contained primitive. Set it to false for it to 76 // appear in the footer, below the contained primitive. "align" must be one of 77 // the Align constants. Rows in the header are printed top to bottom, rows in 78 // the footer are printed bottom to top. Note that long text can overlap as 79 // different alignments will be placed on the same row. 80 func (f *Frame) AddText(text string, header bool, align int, color tcell.Color) *Frame { 81 f.text = append(f.text, &frameText{ 82 Text: text, 83 Header: header, 84 Align: align, 85 Color: color, 86 }) 87 return f 88 } 89 90 // Clear removes all text from the frame. 91 func (f *Frame) Clear() *Frame { 92 f.text = nil 93 return f 94 } 95 96 // SetBorders sets the width of the frame borders as well as "header" and 97 // "footer", the vertical space between the header and footer text and the 98 // contained primitive (does not apply if there is no text). 99 func (f *Frame) SetBorders(top, bottom, header, footer, left, right int) *Frame { 100 f.top, f.bottom, f.header, f.footer, f.left, f.right = top, bottom, header, footer, left, right 101 return f 102 } 103 104 // Draw draws this primitive onto the screen. 105 func (f *Frame) Draw(screen tcell.Screen) { 106 f.Box.DrawForSubclass(screen, f) 107 108 // Calculate start positions. 109 x, top, width, height := f.GetInnerRect() 110 bottom := top + height - 1 111 x += f.left 112 top += f.top 113 bottom -= f.bottom 114 width -= f.left + f.right 115 if width <= 0 || top >= bottom { 116 return // No space left. 117 } 118 119 // Draw text. 120 var rows [6]int // top-left, top-center, top-right, bottom-left, bottom-center, bottom-right. 121 topMax := top 122 bottomMin := bottom 123 for _, text := range f.text { 124 // Where do we place this text? 125 var y int 126 if text.Header { 127 y = top + rows[text.Align] 128 rows[text.Align]++ 129 if y >= bottomMin { 130 continue 131 } 132 if y+1 > topMax { 133 topMax = y + 1 134 } 135 } else { 136 y = bottom - rows[3+text.Align] 137 rows[3+text.Align]++ 138 if y <= topMax { 139 continue 140 } 141 if y-1 < bottomMin { 142 bottomMin = y - 1 143 } 144 } 145 146 // Draw text. 147 Print(screen, text.Text, x, y, width, text.Align, text.Color) 148 } 149 150 // Set the size of the contained primitive. 151 if f.primitive != nil { 152 if topMax > top { 153 top = topMax + f.header 154 } 155 if bottomMin < bottom { 156 bottom = bottomMin - f.footer 157 } 158 if top > bottom { 159 return // No space for the primitive. 160 } 161 f.primitive.SetRect(x, top, width, bottom+1-top) 162 163 // Finally, draw the contained primitive. 164 f.primitive.Draw(screen) 165 } 166 } 167 168 // Focus is called when this primitive receives focus. 169 func (f *Frame) Focus(delegate func(p Primitive)) { 170 f.setFocus = delegate 171 if f.primitive != nil { 172 delegate(f.primitive) 173 } else { 174 f.Box.Focus(delegate) 175 } 176 } 177 178 // HasFocus returns whether or not this primitive has focus. 179 func (f *Frame) HasFocus() bool { 180 if f.primitive == nil { 181 return f.Box.HasFocus() 182 } 183 return f.primitive.HasFocus() 184 } 185 186 // MouseHandler returns the mouse handler for this primitive. 187 func (f *Frame) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { 188 return f.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { 189 if !f.InRect(event.Position()) { 190 return false, nil 191 } 192 193 // Pass mouse events on to contained primitive. 194 if f.primitive != nil { 195 consumed, capture = f.primitive.MouseHandler()(action, event, setFocus) 196 if consumed { 197 return true, capture 198 } 199 } 200 201 // Clicking on the frame parts. 202 if action == MouseLeftDown { 203 setFocus(f) 204 consumed = true 205 } 206 207 return 208 }) 209 } 210 211 // InputHandler returns the handler for this primitive. 212 func (f *Frame) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { 213 return f.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) { 214 if f.primitive == nil { 215 return 216 } 217 if handler := f.primitive.InputHandler(); handler != nil { 218 handler(event, setFocus) 219 return 220 } 221 }) 222 } 223 224 // PasteHandler returns the handler for this primitive. 225 func (f *Frame) PasteHandler() func(pastedText string, setFocus func(p Primitive)) { 226 return f.WrapPasteHandler(func(pastedText string, setFocus func(p Primitive)) { 227 if f.primitive == nil { 228 return 229 } 230 if handler := f.primitive.PasteHandler(); handler != nil { 231 handler(pastedText, setFocus) 232 return 233 } 234 }) 235 }