1package tview 2 3import ( 4 "github.com/gdamore/tcell" 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 a border around another primitive. The top area 16// (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. 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. 34func NewFrame(primitive Primitive) *Frame { 35 box := NewBox() 36 37 f := &Frame{ 38 Box: box, 39 primitive: primitive, 40 top: 1, 41 bottom: 1, 42 header: 1, 43 footer: 1, 44 left: 1, 45 right: 1, 46 } 47 48 f.focus = f 49 50 return f 51} 52 53// AddText adds text to the frame. Set "header" to true if the text is to appear 54// in the header, above the contained primitive. Set it to false for it to 55// appear in the footer, below the contained primitive. "align" must be one of 56// the Align constants. Rows in the header are printed top to bottom, rows in 57// the footer are printed bottom to top. Note that long text can overlap as 58// different alignments will be placed on the same row. 59func (f *Frame) AddText(text string, header bool, align int, color tcell.Color) *Frame { 60 f.text = append(f.text, &frameText{ 61 Text: text, 62 Header: header, 63 Align: align, 64 Color: color, 65 }) 66 return f 67} 68 69// Clear removes all text from the frame. 70func (f *Frame) Clear() *Frame { 71 f.text = nil 72 return f 73} 74 75// SetBorders sets the width of the frame borders as well as "header" and 76// "footer", the vertical space between the header and footer text and the 77// contained primitive (does not apply if there is no text). 78func (f *Frame) SetBorders(top, bottom, header, footer, left, right int) *Frame { 79 f.top, f.bottom, f.header, f.footer, f.left, f.right = top, bottom, header, footer, left, right 80 return f 81} 82 83// Draw draws this primitive onto the screen. 84func (f *Frame) Draw(screen tcell.Screen) { 85 f.Box.Draw(screen) 86 87 // Calculate start positions. 88 x, top, width, height := f.GetInnerRect() 89 bottom := top + height - 1 90 x += f.left 91 top += f.top 92 bottom -= f.bottom 93 width -= f.left + f.right 94 if width <= 0 || top >= bottom { 95 return // No space left. 96 } 97 98 // Draw text. 99 var rows [6]int // top-left, top-center, top-right, bottom-left, bottom-center, bottom-right. 100 topMax := top 101 bottomMin := bottom 102 for _, text := range f.text { 103 // Where do we place this text? 104 var y int 105 if text.Header { 106 y = top + rows[text.Align] 107 rows[text.Align]++ 108 if y >= bottomMin { 109 continue 110 } 111 if y+1 > topMax { 112 topMax = y + 1 113 } 114 } else { 115 y = bottom - rows[3+text.Align] 116 rows[3+text.Align]++ 117 if y <= topMax { 118 continue 119 } 120 if y-1 < bottomMin { 121 bottomMin = y - 1 122 } 123 } 124 125 // Draw text. 126 Print(screen, text.Text, x, y, width, text.Align, text.Color) 127 } 128 129 // Set the size of the contained primitive. 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// Focus is called when this primitive receives focus. 146func (f *Frame) Focus(delegate func(p Primitive)) { 147 delegate(f.primitive) 148} 149 150// HasFocus returns whether or not this primitive has focus. 151func (f *Frame) HasFocus() bool { 152 focusable, ok := f.primitive.(Focusable) 153 if ok { 154 return focusable.HasFocus() 155 } 156 return false 157} 158