1package tview 2 3import ( 4 "github.com/gdamore/tcell/v2" 5) 6 7// frameText holds information about a line of text shown in the frame. 8type 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. 19type 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 32// NewFrame returns a new frame around the given primitive. The primitive's 33// size will be changed to fit within this frame. The primitive may be nil, in 34// which case no other primitive is embedded in the frame. 35func NewFrame(primitive Primitive) *Frame { 36 box := NewBox() 37 38 f := &Frame{ 39 Box: box, 40 primitive: primitive, 41 top: 1, 42 bottom: 1, 43 header: 1, 44 footer: 1, 45 left: 1, 46 right: 1, 47 } 48 49 return f 50} 51 52// AddText adds text to the frame. Set "header" to true if the text is to appear 53// in the header, above the contained primitive. Set it to false for it to 54// appear in the footer, below the contained primitive. "align" must be one of 55// the Align constants. Rows in the header are printed top to bottom, rows in 56// the footer are printed bottom to top. Note that long text can overlap as 57// different alignments will be placed on the same row. 58func (f *Frame) AddText(text string, header bool, align int, color tcell.Color) *Frame { 59 f.text = append(f.text, &frameText{ 60 Text: text, 61 Header: header, 62 Align: align, 63 Color: color, 64 }) 65 return f 66} 67 68// Clear removes all text from the frame. 69func (f *Frame) Clear() *Frame { 70 f.text = nil 71 return f 72} 73 74// SetBorders sets the width of the frame borders as well as "header" and 75// "footer", the vertical space between the header and footer text and the 76// contained primitive (does not apply if there is no text). 77func (f *Frame) SetBorders(top, bottom, header, footer, left, right int) *Frame { 78 f.top, f.bottom, f.header, f.footer, f.left, f.right = top, bottom, header, footer, left, right 79 return f 80} 81 82// Draw draws this primitive onto the screen. 83func (f *Frame) Draw(screen tcell.Screen) { 84 f.Box.DrawForSubclass(screen, f) 85 86 // Calculate start positions. 87 x, top, width, height := f.GetInnerRect() 88 bottom := top + height - 1 89 x += f.left 90 top += f.top 91 bottom -= f.bottom 92 width -= f.left + f.right 93 if width <= 0 || top >= bottom { 94 return // No space left. 95 } 96 97 // Draw text. 98 var rows [6]int // top-left, top-center, top-right, bottom-left, bottom-center, bottom-right. 99 topMax := top 100 bottomMin := bottom 101 for _, text := range f.text { 102 // Where do we place this text? 103 var y int 104 if text.Header { 105 y = top + rows[text.Align] 106 rows[text.Align]++ 107 if y >= bottomMin { 108 continue 109 } 110 if y+1 > topMax { 111 topMax = y + 1 112 } 113 } else { 114 y = bottom - rows[3+text.Align] 115 rows[3+text.Align]++ 116 if y <= topMax { 117 continue 118 } 119 if y-1 < bottomMin { 120 bottomMin = y - 1 121 } 122 } 123 124 // Draw text. 125 Print(screen, text.Text, x, y, width, text.Align, text.Color) 126 } 127 128 // Set the size of the contained primitive. 129 if f.primitive != nil { 130 if topMax > top { 131 top = topMax + f.header 132 } 133 if bottomMin < bottom { 134 bottom = bottomMin - f.footer 135 } 136 if top > bottom { 137 return // No space for the primitive. 138 } 139 f.primitive.SetRect(x, top, width, bottom+1-top) 140 141 // Finally, draw the contained primitive. 142 f.primitive.Draw(screen) 143 } 144} 145 146// Focus is called when this primitive receives focus. 147func (f *Frame) Focus(delegate func(p Primitive)) { 148 if f.primitive != nil { 149 delegate(f.primitive) 150 } else { 151 f.hasFocus = true 152 } 153} 154 155// HasFocus returns whether or not this primitive has focus. 156func (f *Frame) HasFocus() bool { 157 if f.primitive == nil { 158 return f.hasFocus 159 } 160 return f.primitive.HasFocus() 161} 162 163// MouseHandler returns the mouse handler for this primitive. 164func (f *Frame) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { 165 return f.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) { 166 if !f.InRect(event.Position()) { 167 return false, nil 168 } 169 170 // Pass mouse events on to contained primitive. 171 if f.primitive != nil { 172 return f.primitive.MouseHandler()(action, event, setFocus) 173 } 174 175 return false, nil 176 }) 177} 178 179// InputHandler returns the handler for this primitive. 180func (f *Frame) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { 181 return f.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) { 182 if f.primitive == nil { 183 return 184 } 185 if f.primitive.HasFocus() { 186 if handler := f.primitive.InputHandler(); handler != nil { 187 handler(event, setFocus) 188 return 189 } 190 } 191 }) 192} 193