diff options
Diffstat (limited to 'vendor/maunium.net/go/tview/box.go')
-rw-r--r-- | vendor/maunium.net/go/tview/box.go | 378 |
1 files changed, 378 insertions, 0 deletions
diff --git a/vendor/maunium.net/go/tview/box.go b/vendor/maunium.net/go/tview/box.go new file mode 100644 index 0000000..1bcbff0 --- /dev/null +++ b/vendor/maunium.net/go/tview/box.go @@ -0,0 +1,378 @@ +package tview + +import ( + "maunium.net/go/tcell" +) + +// Box implements Primitive with a background and optional elements such as a +// border and a title. Most subclasses keep their content contained in the box +// but don't necessarily have to. +// +// Note that all classes which subclass from Box will also have access to its +// functions. +// +// See https://github.com/rivo/tview/wiki/Box for an example. +type Box struct { + // The position of the rect. + x, y, width, height int + + // The inner rect reserved for the box's content. + innerX, innerY, innerWidth, innerHeight int + + // Border padding. + paddingTop, paddingBottom, paddingLeft, paddingRight int + + // The box's background color. + backgroundColor tcell.Color + + // Whether or not a border is drawn, reducing the box's space for content by + // two in width and height. + border bool + + // The color of the border. + borderColor tcell.Color + + // The title. Only visible if there is a border, too. + title string + + // The color of the title. + titleColor tcell.Color + + // The alignment of the title. + titleAlign int + + // Provides a way to find out if this box has focus. We always go through + // this interface because it may be overridden by implementing classes. + focus Focusable + + // Whether or not this box has focus. + hasFocus bool + + // If set to true, the inner rect of this box will be within the screen at the + // last time the box was drawn. + clampToScreen bool + + // An optional capture function which receives a key event and returns the + // event to be forwarded to the primitive's default input handler (nil if + // nothing should be forwarded). + inputCapture func(event *tcell.EventKey) *tcell.EventKey + + // An optional capture function which receives a mouse event and returns the + // event to be forwarded to the primitive's default mouse handler (nil if + // nothing should be forwarded). + mouseCapture func(event *tcell.EventMouse) *tcell.EventMouse + + // An optional function which is called before the box is drawn. + draw func(screen tcell.Screen, x, y, width, height int) (int, int, int, int) +} + +// NewBox returns a Box without a border. +func NewBox() *Box { + b := &Box{ + width: 15, + height: 10, + innerX: -1, // Mark as uninitialized. + backgroundColor: Styles.PrimitiveBackgroundColor, + borderColor: Styles.BorderColor, + titleColor: Styles.TitleColor, + titleAlign: AlignCenter, + clampToScreen: true, + } + b.focus = b + return b +} + +// SetBorderPadding sets the size of the borders around the box content. +func (b *Box) SetBorderPadding(top, bottom, left, right int) *Box { + b.paddingTop, b.paddingBottom, b.paddingLeft, b.paddingRight = top, bottom, left, right + return b +} + +// GetRect returns the current position of the rectangle, x, y, width, and +// height. +func (b *Box) GetRect() (int, int, int, int) { + return b.x, b.y, b.width, b.height +} + +// GetInnerRect returns the position of the inner rectangle (x, y, width, +// height), without the border and without any padding. +func (b *Box) GetInnerRect() (int, int, int, int) { + if b.innerX >= 0 { + return b.innerX, b.innerY, b.innerWidth, b.innerHeight + } + x, y, width, height := b.GetRect() + if b.border { + x++ + y++ + width -= 2 + height -= 2 + } + return x + b.paddingLeft, + y + b.paddingTop, + width - b.paddingLeft - b.paddingRight, + height - b.paddingTop - b.paddingBottom +} + +// SetRect sets a new position of the primitive. +func (b *Box) SetRect(x, y, width, height int) { + b.x = x + b.y = y + b.width = width + b.height = height +} + +// SetDrawFunc sets a callback function which is invoked after the box primitive +// has been drawn. This allows you to add a more individual style to the box +// (and all primitives which extend it). +// +// The function is provided with the box's dimensions (set via SetRect()). It +// must return the box's inner dimensions (x, y, width, height) which will be +// returned by GetInnerRect(), used by descendent primitives to draw their own +// content. +func (b *Box) SetDrawFunc(handler func(screen tcell.Screen, x, y, width, height int) (int, int, int, int)) *Box { + b.draw = handler + return b +} + +// GetDrawFunc returns the callback function which was installed with +// SetDrawFunc() or nil if no such function has been installed. +func (b *Box) GetDrawFunc() func(screen tcell.Screen, x, y, width, height int) (int, int, int, int) { + return b.draw +} + +// WrapInputHandler wraps an input handler (see InputHandler()) with the +// functionality to capture input (see SetInputCapture()) before passing it +// on to the provided (default) input handler. +// +// This is only meant to be used by subclassing primitives. +func (b *Box) WrapInputHandler(inputHandler func(*tcell.EventKey, func(p Primitive))) func(*tcell.EventKey, func(p Primitive)) { + return func(event *tcell.EventKey, setFocus func(p Primitive)) { + if b.inputCapture != nil { + event = b.inputCapture(event) + } + if event != nil && inputHandler != nil { + inputHandler(event, setFocus) + } + } +} + +// InputHandler returns nil. +func (b *Box) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) { + return b.WrapInputHandler(nil) +} + +// SetInputCapture installs a function which captures key events before they are +// forwarded to the primitive's default key event handler. This function can +// then choose to forward that key event (or a different one) to the default +// handler by returning it. If nil is returned, the default handler will not +// be called. +// +// Providing a nil handler will remove a previously existing handler. +func (b *Box) SetInputCapture(capture func(event *tcell.EventKey) *tcell.EventKey) *Box { + b.inputCapture = capture + return b +} + +// GetInputCapture returns the function installed with SetInputCapture() or nil +// if no such function has been installed. +func (b *Box) GetInputCapture() func(event *tcell.EventKey) *tcell.EventKey { + return b.inputCapture +} + +// WrapMouseHandler wraps a mouse handler (see MouseHandler()) with the +// functionality to capture mouse events (see SetMouseCapture()) before passing it +// on to the provided (default) mouse handler. +// +// This is only meant to be used by subclassing primitives. +func (b *Box) WrapMouseHandler(mouseHandler func(*tcell.EventMouse, func(p Primitive))) func(*tcell.EventMouse, func(p Primitive)) { + return func(event *tcell.EventMouse, setFocus func(p Primitive)) { + if b.mouseCapture != nil { + event = b.mouseCapture(event) + } + if event != nil && mouseHandler != nil { + mouseHandler(event, setFocus) + } + } +} + +// MouseHandler returns nil. +func (b *Box) MouseHandler() func(event *tcell.EventMouse, setFocus func(p Primitive)) { + return b.WrapMouseHandler(nil) +} + +// SetMouseCapture installs a function which captures mouse events before they are +// forwarded to the primitive's default mouse event handler. This function can +// then choose to forward that mouse event (or a different one) to the default +// handler by returning it. If nil is returned, the default handler will not +// be called. +// +// Providing a nil handler will remove a previously existing handler. +func (b *Box) SetMouseCapture(capture func(event *tcell.EventMouse) *tcell.EventMouse) *Box { + b.mouseCapture = capture + return b +} + +// GetMouseCapture returns the function installed with SetMouseCapture() or nil +// if no such function has been installed. +func (b *Box) GetMouseCapture() func(event *tcell.EventMouse) *tcell.EventMouse { + return b.mouseCapture +} + +// SetBackgroundColor sets the box's background color. +func (b *Box) SetBackgroundColor(color tcell.Color) *Box { + b.backgroundColor = color + return b +} + +// SetBorder sets the flag indicating whether or not the box should have a +// border. +func (b *Box) SetBorder(show bool) *Box { + b.border = show + return b +} + +func (b *Box) HasBorder() bool { + return b.border +} + +// SetBorderColor sets the box's border color. +func (b *Box) SetBorderColor(color tcell.Color) *Box { + b.borderColor = color + return b +} + +// SetTitle sets the box's title. +func (b *Box) SetTitle(title string) *Box { + b.title = title + return b +} + +// SetTitleColor sets the box's title color. +func (b *Box) SetTitleColor(color tcell.Color) *Box { + b.titleColor = color + return b +} + +// SetTitleAlign sets the alignment of the title, one of AlignLeft, AlignCenter, +// or AlignRight. +func (b *Box) SetTitleAlign(align int) *Box { + b.titleAlign = align + return b +} + +func (b *Box) GetBackgroundColor() tcell.Color { + return b.backgroundColor +} + +func (b *Box) GetBorderColor() tcell.Color { + return b.borderColor +} + +// Draw draws this primitive onto the screen. +func (b *Box) Draw(screen tcell.Screen) { + // Don't draw anything if there is no space. + if b.width <= 0 || b.height <= 0 { + return + } + + def := tcell.StyleDefault + + // Fill background. + background := def.Background(b.backgroundColor) + for y := b.y; y < b.y+b.height; y++ { + for x := b.x; x < b.x+b.width; x++ { + screen.SetContent(x, y, ' ', nil, background) + } + } + + // Draw border. + if b.border && b.width >= 2 && b.height >= 2 { + border := background.Foreground(b.borderColor) + var vertical, horizontal, topLeft, topRight, bottomLeft, bottomRight rune + if b.focus.HasFocus() { + vertical = GraphicsDbVertBar + horizontal = GraphicsDbHorBar + topLeft = GraphicsDbTopLeftCorner + topRight = GraphicsDbTopRightCorner + bottomLeft = GraphicsDbBottomLeftCorner + bottomRight = GraphicsDbBottomRightCorner + } else { + vertical = GraphicsHoriBar + horizontal = GraphicsVertBar + topLeft = GraphicsTopLeftCorner + topRight = GraphicsTopRightCorner + bottomLeft = GraphicsBottomLeftCorner + bottomRight = GraphicsBottomRightCorner + } + for x := b.x + 1; x < b.x+b.width-1; x++ { + screen.SetContent(x, b.y, vertical, nil, border) + screen.SetContent(x, b.y+b.height-1, vertical, nil, border) + } + for y := b.y + 1; y < b.y+b.height-1; y++ { + screen.SetContent(b.x, y, horizontal, nil, border) + screen.SetContent(b.x+b.width-1, y, horizontal, nil, border) + } + screen.SetContent(b.x, b.y, topLeft, nil, border) + screen.SetContent(b.x+b.width-1, b.y, topRight, nil, border) + screen.SetContent(b.x, b.y+b.height-1, bottomLeft, nil, border) + screen.SetContent(b.x+b.width-1, b.y+b.height-1, bottomRight, nil, border) + + // Draw title. + if b.title != "" && b.width >= 4 { + _, printed := Print(screen, b.title, b.x+1, b.y, b.width-2, b.titleAlign, b.titleColor) + if StringWidth(b.title)-printed > 0 && printed > 0 { + _, _, style, _ := screen.GetContent(b.x+b.width-2, b.y) + fg, _, _ := style.Decompose() + Print(screen, string(GraphicsEllipsis), b.x+b.width-2, b.y, 1, AlignLeft, fg) + } + } + } + + // Call custom draw function. + if b.draw != nil { + b.innerX, b.innerY, b.innerWidth, b.innerHeight = b.draw(screen, b.x, b.y, b.width, b.height) + } else { + // Remember the inner rect. + b.innerX = -1 + b.innerX, b.innerY, b.innerWidth, b.innerHeight = b.GetInnerRect() + } + + // Clamp inner rect to screen. + if b.clampToScreen { + width, height := screen.Size() + if b.innerX < 0 { + b.innerWidth += b.innerX + b.innerX = 0 + } + if b.innerX+b.innerWidth >= width { + b.innerWidth = width - b.innerX + } + if b.innerY+b.innerHeight >= height { + b.innerHeight = height - b.innerY + } + if b.innerY < 0 { + b.innerHeight += b.innerY + b.innerY = 0 + } + } +} + +// Focus is called when this primitive receives focus. +func (b *Box) Focus(delegate func(p Primitive)) { + b.hasFocus = true +} + +// Blur is called when this primitive loses focus. +func (b *Box) Blur() { + b.hasFocus = false +} + +// HasFocus returns whether or not this primitive has focus. +func (b *Box) HasFocus() bool { + return b.hasFocus +} + +// GetFocusable returns the item's Focusable. +func (b *Box) GetFocusable() Focusable { + return b.focus +} |