2017-12-15 22:03:01 +00:00
|
|
|
package tview
|
|
|
|
|
|
|
|
import (
|
2020-10-18 12:15:57 +00:00
|
|
|
"github.com/gdamore/tcell/v2"
|
2017-12-15 22:03:01 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// frameText holds information about a line of text shown in the frame.
|
|
|
|
type frameText struct {
|
|
|
|
Text string // The text to be displayed.
|
|
|
|
Header bool // true = place in header, false = place in footer.
|
|
|
|
Align int // One of the Align constants.
|
|
|
|
Color tcell.Color // The text color.
|
|
|
|
}
|
|
|
|
|
2020-03-27 17:41:44 +00:00
|
|
|
// Frame is a wrapper which adds space around another primitive. In addition,
|
|
|
|
// the top area (header) and the bottom area (footer) may also contain text.
|
2018-01-07 15:39:06 +00:00
|
|
|
//
|
|
|
|
// See https://github.com/rivo/tview/wiki/Frame for an example.
|
2017-12-15 22:03:01 +00:00
|
|
|
type Frame struct {
|
2017-12-16 21:48:26 +00:00
|
|
|
*Box
|
2017-12-15 22:03:01 +00:00
|
|
|
|
2020-08-18 10:05:43 +00:00
|
|
|
// The contained primitive. May be nil.
|
2017-12-15 22:03:01 +00:00
|
|
|
primitive Primitive
|
|
|
|
|
|
|
|
// The lines of text to be displayed.
|
|
|
|
text []*frameText
|
|
|
|
|
|
|
|
// Border spacing.
|
|
|
|
top, bottom, header, footer, left, right int
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewFrame returns a new frame around the given primitive. The primitive's
|
2020-08-18 10:05:43 +00:00
|
|
|
// size will be changed to fit within this frame. The primitive may be nil, in
|
|
|
|
// which case no other primitive is embedded in the frame.
|
2017-12-15 22:03:01 +00:00
|
|
|
func NewFrame(primitive Primitive) *Frame {
|
2017-12-16 21:48:26 +00:00
|
|
|
box := NewBox()
|
|
|
|
|
|
|
|
f := &Frame{
|
|
|
|
Box: box,
|
2017-12-15 22:03:01 +00:00
|
|
|
primitive: primitive,
|
|
|
|
top: 1,
|
|
|
|
bottom: 1,
|
|
|
|
header: 1,
|
|
|
|
footer: 1,
|
|
|
|
left: 1,
|
|
|
|
right: 1,
|
|
|
|
}
|
2017-12-16 21:48:26 +00:00
|
|
|
|
|
|
|
return f
|
2017-12-15 22:03:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// AddText adds text to the frame. Set "header" to true if the text is to appear
|
|
|
|
// in the header, above the contained primitive. Set it to false for it to
|
|
|
|
// appear in the footer, below the contained primitive. "align" must be one of
|
|
|
|
// the Align constants. Rows in the header are printed top to bottom, rows in
|
|
|
|
// the footer are printed bottom to top. Note that long text can overlap as
|
|
|
|
// different alignments will be placed on the same row.
|
|
|
|
func (f *Frame) AddText(text string, header bool, align int, color tcell.Color) *Frame {
|
|
|
|
f.text = append(f.text, &frameText{
|
|
|
|
Text: text,
|
|
|
|
Header: header,
|
|
|
|
Align: align,
|
|
|
|
Color: color,
|
|
|
|
})
|
|
|
|
return f
|
|
|
|
}
|
|
|
|
|
2018-01-01 20:50:20 +00:00
|
|
|
// Clear removes all text from the frame.
|
|
|
|
func (f *Frame) Clear() *Frame {
|
2017-12-20 19:54:49 +00:00
|
|
|
f.text = nil
|
|
|
|
return f
|
|
|
|
}
|
|
|
|
|
2017-12-15 22:03:01 +00:00
|
|
|
// SetBorders sets the width of the frame borders as well as "header" and
|
|
|
|
// "footer", the vertical space between the header and footer text and the
|
|
|
|
// contained primitive (does not apply if there is no text).
|
|
|
|
func (f *Frame) SetBorders(top, bottom, header, footer, left, right int) *Frame {
|
|
|
|
f.top, f.bottom, f.header, f.footer, f.left, f.right = top, bottom, header, footer, left, right
|
|
|
|
return f
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw draws this primitive onto the screen.
|
|
|
|
func (f *Frame) Draw(screen tcell.Screen) {
|
2020-11-17 18:33:25 +00:00
|
|
|
f.Box.DrawForSubclass(screen, f)
|
2017-12-15 22:03:01 +00:00
|
|
|
|
|
|
|
// Calculate start positions.
|
2017-12-26 00:07:30 +00:00
|
|
|
x, top, width, height := f.GetInnerRect()
|
2017-12-21 17:08:53 +00:00
|
|
|
bottom := top + height - 1
|
2017-12-26 00:07:30 +00:00
|
|
|
x += f.left
|
2017-12-15 22:03:01 +00:00
|
|
|
top += f.top
|
|
|
|
bottom -= f.bottom
|
2017-12-26 00:07:30 +00:00
|
|
|
width -= f.left + f.right
|
|
|
|
if width <= 0 || top >= bottom {
|
2017-12-15 22:03:01 +00:00
|
|
|
return // No space left.
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw text.
|
|
|
|
var rows [6]int // top-left, top-center, top-right, bottom-left, bottom-center, bottom-right.
|
|
|
|
topMax := top
|
|
|
|
bottomMin := bottom
|
|
|
|
for _, text := range f.text {
|
|
|
|
// Where do we place this text?
|
|
|
|
var y int
|
|
|
|
if text.Header {
|
|
|
|
y = top + rows[text.Align]
|
|
|
|
rows[text.Align]++
|
|
|
|
if y >= bottomMin {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if y+1 > topMax {
|
|
|
|
topMax = y + 1
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
y = bottom - rows[3+text.Align]
|
|
|
|
rows[3+text.Align]++
|
|
|
|
if y <= topMax {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if y-1 < bottomMin {
|
|
|
|
bottomMin = y - 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw text.
|
2017-12-26 00:07:30 +00:00
|
|
|
Print(screen, text.Text, x, y, width, text.Align, text.Color)
|
2017-12-15 22:03:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Set the size of the contained primitive.
|
2020-08-18 10:05:43 +00:00
|
|
|
if f.primitive != nil {
|
|
|
|
if topMax > top {
|
|
|
|
top = topMax + f.header
|
|
|
|
}
|
|
|
|
if bottomMin < bottom {
|
|
|
|
bottom = bottomMin - f.footer
|
|
|
|
}
|
|
|
|
if top > bottom {
|
|
|
|
return // No space for the primitive.
|
|
|
|
}
|
|
|
|
f.primitive.SetRect(x, top, width, bottom+1-top)
|
2017-12-15 22:03:01 +00:00
|
|
|
|
2020-08-18 10:05:43 +00:00
|
|
|
// Finally, draw the contained primitive.
|
|
|
|
f.primitive.Draw(screen)
|
|
|
|
}
|
2017-12-15 22:03:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Focus is called when this primitive receives focus.
|
2017-12-18 19:04:52 +00:00
|
|
|
func (f *Frame) Focus(delegate func(p Primitive)) {
|
2020-08-18 10:05:43 +00:00
|
|
|
if f.primitive != nil {
|
|
|
|
delegate(f.primitive)
|
|
|
|
} else {
|
|
|
|
f.hasFocus = true
|
|
|
|
}
|
2017-12-15 22:03:01 +00:00
|
|
|
}
|
|
|
|
|
2017-12-16 21:48:26 +00:00
|
|
|
// HasFocus returns whether or not this primitive has focus.
|
|
|
|
func (f *Frame) HasFocus() bool {
|
2020-08-18 10:05:43 +00:00
|
|
|
if f.primitive == nil {
|
|
|
|
return f.hasFocus
|
|
|
|
}
|
2020-11-17 18:33:25 +00:00
|
|
|
return f.primitive.HasFocus()
|
2017-12-16 21:48:26 +00:00
|
|
|
}
|
2019-11-04 05:34:46 +00:00
|
|
|
|
2020-01-24 20:40:34 +00:00
|
|
|
// MouseHandler returns the mouse handler for this primitive.
|
2020-02-14 02:09:09 +00:00
|
|
|
func (f *Frame) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
|
|
|
|
return f.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
|
2020-01-24 20:40:34 +00:00
|
|
|
if !f.InRect(event.Position()) {
|
2020-02-14 02:09:09 +00:00
|
|
|
return false, nil
|
2020-01-24 20:40:34 +00:00
|
|
|
}
|
2020-03-27 17:41:44 +00:00
|
|
|
|
|
|
|
// Pass mouse events on to contained primitive.
|
2020-08-18 10:05:43 +00:00
|
|
|
if f.primitive != nil {
|
|
|
|
return f.primitive.MouseHandler()(action, event, setFocus)
|
|
|
|
}
|
|
|
|
|
|
|
|
return false, nil
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// InputHandler returns the handler for this primitive.
|
|
|
|
func (f *Frame) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
|
|
|
|
return f.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
|
|
|
|
if f.primitive == nil {
|
|
|
|
return
|
|
|
|
}
|
2020-11-17 18:33:25 +00:00
|
|
|
if f.primitive.HasFocus() {
|
2020-08-18 10:05:43 +00:00
|
|
|
if handler := f.primitive.InputHandler(); handler != nil {
|
|
|
|
handler(event, setFocus)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2020-01-24 20:40:34 +00:00
|
|
|
})
|
2019-11-04 05:34:46 +00:00
|
|
|
}
|