Added list, improved existing primitives, and fixed a bunch of bugs.
This commit is contained in:
parent
b83a7766a6
commit
d5bf1a4ef0
10 changed files with 383 additions and 80 deletions
94
box.go
94
box.go
|
@ -1,16 +1,24 @@
|
|||
package tview
|
||||
|
||||
import "github.com/gdamore/tcell"
|
||||
import (
|
||||
"github.com/gdamore/tcell"
|
||||
)
|
||||
|
||||
// Characters to draw the box border.
|
||||
const (
|
||||
BoxVertBar = '\u2500'
|
||||
BoxHorBar = '\u2502'
|
||||
BoxTopLeftCorner = '\u250c'
|
||||
BoxTopRightCorner = '\u2510'
|
||||
BoxBottomRightCorner = '\u2518'
|
||||
BoxBottomLeftCorner = '\u2514'
|
||||
BoxEllipsis = '\u2026'
|
||||
BoxVertBar = '\u2500'
|
||||
BoxHorBar = '\u2502'
|
||||
BoxTopLeftCorner = '\u250c'
|
||||
BoxTopRightCorner = '\u2510'
|
||||
BoxBottomRightCorner = '\u2518'
|
||||
BoxBottomLeftCorner = '\u2514'
|
||||
BoxDbVertBar = '\u2550'
|
||||
BoxDbHorBar = '\u2551'
|
||||
BoxDbTopLeftCorner = '\u2554'
|
||||
BoxDbTopRightCorner = '\u2557'
|
||||
BoxDbBottomRightCorner = '\u255d'
|
||||
BoxDbBottomLeftCorner = '\u255a'
|
||||
BoxEllipsis = '\u2026'
|
||||
)
|
||||
|
||||
// Box implements Rect with a background and optional elements such as a border
|
||||
|
@ -19,9 +27,6 @@ type Box struct {
|
|||
// The position of the rect.
|
||||
x, y, width, height int
|
||||
|
||||
// Whether or not the box has focus.
|
||||
hasFocus bool
|
||||
|
||||
// The box's background color.
|
||||
backgroundColor tcell.Color
|
||||
|
||||
|
@ -32,25 +37,30 @@ type Box struct {
|
|||
// The color of the border.
|
||||
borderColor tcell.Color
|
||||
|
||||
// The color of the border when the box has focus.
|
||||
focusedBorderColor tcell.Color
|
||||
|
||||
// The title. Only visible if there is a border, too.
|
||||
title string
|
||||
|
||||
// The color of the title.
|
||||
titleColor tcell.Color
|
||||
|
||||
// Provides a way to find out if this box has focus. We always go through
|
||||
// this interface because it may be overriden by implementing classes.
|
||||
focus Focusable
|
||||
|
||||
// Whether or not this box has focus.
|
||||
hasFocus bool
|
||||
}
|
||||
|
||||
// NewBox returns a Box without a border.
|
||||
func NewBox() *Box {
|
||||
return &Box{
|
||||
width: 15,
|
||||
height: 10,
|
||||
borderColor: tcell.ColorWhite,
|
||||
focusedBorderColor: tcell.ColorYellow,
|
||||
titleColor: tcell.ColorWhite,
|
||||
b := &Box{
|
||||
width: 15,
|
||||
height: 10,
|
||||
borderColor: tcell.ColorWhite,
|
||||
titleColor: tcell.ColorWhite,
|
||||
}
|
||||
b.focus = b
|
||||
return b
|
||||
}
|
||||
|
||||
// Draw draws this primitive onto the screen.
|
||||
|
@ -73,21 +83,34 @@ func (b *Box) Draw(screen tcell.Screen) {
|
|||
// Draw border.
|
||||
if b.border && b.width >= 2 && b.height >= 2 {
|
||||
border := background.Foreground(b.borderColor)
|
||||
if b.hasFocus {
|
||||
border = background.Foreground(b.focusedBorderColor)
|
||||
var vertical, horizontal, topLeft, topRight, bottomLeft, bottomRight rune
|
||||
if b.focus.HasFocus() {
|
||||
vertical = BoxDbVertBar
|
||||
horizontal = BoxDbHorBar
|
||||
topLeft = BoxDbTopLeftCorner
|
||||
topRight = BoxDbTopRightCorner
|
||||
bottomLeft = BoxDbBottomLeftCorner
|
||||
bottomRight = BoxDbBottomRightCorner
|
||||
} else {
|
||||
vertical = BoxVertBar
|
||||
horizontal = BoxHorBar
|
||||
topLeft = BoxTopLeftCorner
|
||||
topRight = BoxTopRightCorner
|
||||
bottomLeft = BoxBottomLeftCorner
|
||||
bottomRight = BoxBottomRightCorner
|
||||
}
|
||||
for x := b.x + 1; x < b.x+b.width-1; x++ {
|
||||
screen.SetContent(x, b.y, BoxVertBar, nil, border)
|
||||
screen.SetContent(x, b.y+b.height-1, BoxVertBar, nil, border)
|
||||
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, BoxHorBar, nil, border)
|
||||
screen.SetContent(b.x+b.width-1, y, BoxHorBar, nil, border)
|
||||
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, BoxTopLeftCorner, nil, border)
|
||||
screen.SetContent(b.x+b.width-1, b.y, BoxTopRightCorner, nil, border)
|
||||
screen.SetContent(b.x, b.y+b.height-1, BoxBottomLeftCorner, nil, border)
|
||||
screen.SetContent(b.x+b.width-1, b.y+b.height-1, BoxBottomRightCorner, 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 {
|
||||
|
@ -145,12 +168,6 @@ func (b *Box) SetBorderColor(color tcell.Color) *Box {
|
|||
return b
|
||||
}
|
||||
|
||||
// SetFocusedBorderColor sets the box's border color for when the box has focus.
|
||||
func (b *Box) SetFocusedBorderColor(color tcell.Color) *Box {
|
||||
b.focusedBorderColor = color
|
||||
return b
|
||||
}
|
||||
|
||||
// SetTitle sets the box's title.
|
||||
func (b *Box) SetTitle(title string) *Box {
|
||||
b.title = title
|
||||
|
@ -172,3 +189,8 @@ func (b *Box) Focus(app *Application) {
|
|||
func (b *Box) Blur() {
|
||||
b.hasFocus = false
|
||||
}
|
||||
|
||||
// HasFocus returns whether or not this primitive has focus.
|
||||
func (b *Box) HasFocus() bool {
|
||||
return b.hasFocus
|
||||
}
|
||||
|
|
10
button.go
10
button.go
|
@ -6,7 +6,7 @@ import (
|
|||
|
||||
// Button is labeled box that triggers an action when selected.
|
||||
type Button struct {
|
||||
Box
|
||||
*Box
|
||||
|
||||
// The text to be displayed before the input area.
|
||||
label string
|
||||
|
@ -32,7 +32,7 @@ type Button struct {
|
|||
func NewButton(label string) *Button {
|
||||
box := NewBox().SetBackgroundColor(tcell.ColorBlue)
|
||||
return &Button{
|
||||
Box: *box,
|
||||
Box: box,
|
||||
label: label,
|
||||
labelColor: tcell.ColorWhite,
|
||||
labelColorActivated: tcell.ColorBlue,
|
||||
|
@ -93,7 +93,7 @@ func (b *Button) SetBlurFunc(handler func(key tcell.Key)) *Button {
|
|||
func (b *Button) Draw(screen tcell.Screen) {
|
||||
// Draw the box.
|
||||
backgroundColor := b.backgroundColor
|
||||
if b.hasFocus {
|
||||
if b.focus.HasFocus() {
|
||||
b.backgroundColor = b.backgroundColorActivated
|
||||
}
|
||||
b.Box.Draw(screen)
|
||||
|
@ -107,12 +107,12 @@ func (b *Button) Draw(screen tcell.Screen) {
|
|||
width -= 2
|
||||
}
|
||||
labelColor := b.labelColor
|
||||
if b.hasFocus {
|
||||
if b.focus.HasFocus() {
|
||||
labelColor = b.labelColorActivated
|
||||
}
|
||||
Print(screen, b.label, x, y, width, AlignCenter, labelColor)
|
||||
|
||||
if b.hasFocus {
|
||||
if b.focus.HasFocus() {
|
||||
screen.HideCursor()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,25 +7,38 @@ import (
|
|||
|
||||
func main() {
|
||||
app := tview.NewApplication()
|
||||
var list *tview.List
|
||||
|
||||
form := tview.NewFrame(tview.NewForm().
|
||||
frame := tview.NewFrame(tview.NewForm().
|
||||
AddItem("First name", "", 20, nil).
|
||||
AddItem("Last name", "", 20, nil).
|
||||
AddItem("Age", "", 4, nil).
|
||||
AddButton("Save", func() { app.Stop() }).
|
||||
AddButton("Cancel", nil)).
|
||||
AddText("Customer details", true, tview.AlignLeft, tcell.ColorRed)
|
||||
form.SetBorder(true)
|
||||
AddButton("Cancel", nil).
|
||||
AddButton("Go to list", func() { app.SetFocus(list) })).
|
||||
AddText("Customer details", true, tview.AlignLeft, tcell.ColorRed).
|
||||
AddText("Customer details", false, tview.AlignCenter, tcell.ColorRed)
|
||||
frame.SetBorder(true)
|
||||
|
||||
box := tview.NewFlex(tview.FlexColumn, []tview.Primitive{
|
||||
form,
|
||||
list = tview.NewList().
|
||||
AddItem("Edit a form", "You can do whatever you want", 'e').
|
||||
AddItem("Quit the program", "Do it!", 0).
|
||||
SetSelectedFunc(func(index int, mainText, secondaryText string, shortcut rune) {
|
||||
if shortcut == 'e' {
|
||||
app.SetFocus(frame)
|
||||
}
|
||||
})
|
||||
list.SetBorder(true)
|
||||
|
||||
flex := tview.NewFlex(tview.FlexColumn, []tview.Primitive{
|
||||
frame,
|
||||
tview.NewFlex(tview.FlexRow, []tview.Primitive{
|
||||
tview.NewBox().SetBorder(true).SetTitle("Second"),
|
||||
list,
|
||||
tview.NewBox().SetBorder(true).SetTitle("Third"),
|
||||
}),
|
||||
tview.NewBox().SetBorder(true).SetTitle("Fourth"),
|
||||
})
|
||||
box.AddItem(tview.NewBox().SetBorder(true).SetTitle("Fifth"), 20)
|
||||
flex.AddItem(tview.NewBox().SetBorder(true).SetTitle("Fifth"), 20)
|
||||
|
||||
inputField := tview.NewInputField().
|
||||
SetLabel("Type something: ").
|
||||
|
@ -33,10 +46,10 @@ func main() {
|
|||
SetAcceptanceFunc(tview.InputFieldFloat)
|
||||
inputField.SetBorder(true).SetTitle("Type!")
|
||||
|
||||
final := tview.NewFlex(tview.FlexRow, []tview.Primitive{box})
|
||||
final := tview.NewFlex(tview.FlexRow, []tview.Primitive{flex})
|
||||
final.AddItem(inputField, 3)
|
||||
|
||||
app.SetRoot(final, true).SetFocus(form)
|
||||
app.SetRoot(final, true).SetFocus(list)
|
||||
|
||||
if err := app.Run(); err != nil {
|
||||
panic(err)
|
||||
|
|
27
flex.go
27
flex.go
|
@ -8,8 +8,8 @@ const (
|
|||
FlexColumn
|
||||
)
|
||||
|
||||
// FlexItem holds layout options for one item.
|
||||
type FlexItem struct {
|
||||
// flexItem holds layout options for one item.
|
||||
type flexItem struct {
|
||||
Item Primitive // The item to be positioned.
|
||||
FixedSize int // The item's fixed size which may not be changed, 0 if it has no fixed size.
|
||||
}
|
||||
|
@ -17,8 +17,8 @@ type FlexItem struct {
|
|||
// Flex is a basic implementation of a flexbox layout.
|
||||
type Flex struct {
|
||||
x, y, width, height int // The size and position of this primitive.
|
||||
Items []FlexItem // The items to be positioned.
|
||||
Direction int // FlexRow or FlexColumn.
|
||||
items []flexItem // The items to be positioned.
|
||||
direction int // FlexRow or FlexColumn.
|
||||
}
|
||||
|
||||
// NewFlex returns a new flexbox layout container with the given primitives.
|
||||
|
@ -28,10 +28,10 @@ func NewFlex(direction int, items []Primitive) *Flex {
|
|||
box := &Flex{
|
||||
width: 15,
|
||||
height: 10,
|
||||
Direction: direction,
|
||||
direction: direction,
|
||||
}
|
||||
for _, item := range items {
|
||||
box.Items = append(box.Items, FlexItem{Item: item})
|
||||
box.items = append(box.items, flexItem{Item: item})
|
||||
}
|
||||
return box
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ func NewFlex(direction int, items []Primitive) *Flex {
|
|||
// AddItem adds a new item to the container. fixedSize is a size that may not be
|
||||
// changed. A value of 0 means that its size may be changed.
|
||||
func (f *Flex) AddItem(item Primitive, fixedSize int) *Flex {
|
||||
f.Items = append(f.Items, FlexItem{Item: item, FixedSize: fixedSize})
|
||||
f.items = append(f.items, flexItem{Item: item, FixedSize: fixedSize})
|
||||
return f
|
||||
}
|
||||
|
||||
|
@ -50,10 +50,10 @@ func (f *Flex) Draw(screen tcell.Screen) {
|
|||
// How much space can we distribute?
|
||||
var variables int
|
||||
distSize := f.width
|
||||
if f.Direction == FlexRow {
|
||||
if f.direction == FlexRow {
|
||||
distSize = f.height
|
||||
}
|
||||
for _, item := range f.Items {
|
||||
for _, item := range f.items {
|
||||
if item.FixedSize > 0 {
|
||||
distSize -= item.FixedSize
|
||||
} else {
|
||||
|
@ -63,17 +63,17 @@ func (f *Flex) Draw(screen tcell.Screen) {
|
|||
|
||||
// Calculate positions and draw items.
|
||||
pos := f.x
|
||||
if f.Direction == FlexRow {
|
||||
if f.direction == FlexRow {
|
||||
pos = f.y
|
||||
}
|
||||
for _, item := range f.Items {
|
||||
for _, item := range f.items {
|
||||
size := item.FixedSize
|
||||
if size <= 0 {
|
||||
size = distSize / variables
|
||||
distSize -= size
|
||||
variables--
|
||||
}
|
||||
if f.Direction == FlexColumn {
|
||||
if f.direction == FlexColumn {
|
||||
item.Item.SetRect(pos, f.y, size, f.height)
|
||||
} else {
|
||||
item.Item.SetRect(f.x, pos, f.width, size)
|
||||
|
@ -105,6 +105,9 @@ func (f *Flex) InputHandler() func(event *tcell.EventKey) {
|
|||
|
||||
// Focus is called when this primitive receives focus.
|
||||
func (f *Flex) Focus(app *Application) {
|
||||
if len(f.items) > 0 {
|
||||
app.SetFocus(f.items[0].Item)
|
||||
}
|
||||
}
|
||||
|
||||
// Blur is called when this primitive loses focus.
|
||||
|
|
8
focusable.go
Normal file
8
focusable.go
Normal file
|
@ -0,0 +1,8 @@
|
|||
package tview
|
||||
|
||||
// Focusable provides a method which determines if a primitive has focus.
|
||||
// Composed primitives may be focused based on the focused state of their
|
||||
// contained primitives.
|
||||
type Focusable interface {
|
||||
HasFocus() bool
|
||||
}
|
32
form.go
32
form.go
|
@ -8,7 +8,7 @@ import (
|
|||
|
||||
// Form is a Box which contains multiple input fields, one per row.
|
||||
type Form struct {
|
||||
Box
|
||||
*Box
|
||||
|
||||
// The items of the form (one row per item).
|
||||
items []*InputField
|
||||
|
@ -35,13 +35,19 @@ type Form struct {
|
|||
|
||||
// NewForm returns a new form.
|
||||
func NewForm() *Form {
|
||||
return &Form{
|
||||
Box: *NewBox(),
|
||||
box := NewBox()
|
||||
|
||||
f := &Form{
|
||||
Box: box,
|
||||
itemPadding: 1,
|
||||
labelColor: tcell.ColorYellow,
|
||||
fieldBackgroundColor: tcell.ColorBlue,
|
||||
fieldTextColor: tcell.ColorWhite,
|
||||
}
|
||||
|
||||
f.focus = f
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
// SetItemPadding sets the number of empty rows between form items.
|
||||
|
@ -156,8 +162,6 @@ func (f *Form) Draw(screen tcell.Screen) {
|
|||
|
||||
// Focus is called by the application when the primitive receives focus.
|
||||
func (f *Form) Focus(app *Application) {
|
||||
f.Box.Focus(app)
|
||||
|
||||
if len(f.items)+len(f.buttons) == 0 {
|
||||
return
|
||||
}
|
||||
|
@ -166,10 +170,9 @@ func (f *Form) Focus(app *Application) {
|
|||
if f.focusedElement < 0 || f.focusedElement >= len(f.items)+len(f.buttons) {
|
||||
f.focusedElement = 0
|
||||
}
|
||||
f.hasFocus = false
|
||||
handler := func(key tcell.Key) {
|
||||
switch key {
|
||||
case tcell.KeyTab:
|
||||
case tcell.KeyTab, tcell.KeyEnter:
|
||||
f.focusedElement++
|
||||
case tcell.KeyBacktab:
|
||||
f.focusedElement--
|
||||
|
@ -198,3 +201,18 @@ func (f *Form) Focus(app *Application) {
|
|||
func (f *Form) InputHandler() func(event *tcell.EventKey) {
|
||||
return func(event *tcell.EventKey) {}
|
||||
}
|
||||
|
||||
// HasFocus returns whether or not this primitive has focus.
|
||||
func (f *Form) HasFocus() bool {
|
||||
for _, item := range f.items {
|
||||
if item.focus.HasFocus() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for _, button := range f.buttons {
|
||||
if button.focus.HasFocus() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
27
frame.go
27
frame.go
|
@ -12,10 +12,10 @@ type frameText struct {
|
|||
Color tcell.Color // The text color.
|
||||
}
|
||||
|
||||
// Frame is a wrapper which adds a border around another primitive. The top and
|
||||
// the bottom border may also contain text.
|
||||
// Frame is a wrapper which adds a border around another box. The top area
|
||||
// (header) and the bottom area (footer) may also contain text.
|
||||
type Frame struct {
|
||||
Box
|
||||
*Box
|
||||
|
||||
// The contained primitive.
|
||||
primitive Primitive
|
||||
|
@ -30,8 +30,10 @@ type Frame struct {
|
|||
// NewFrame returns a new frame around the given primitive. The primitive's
|
||||
// size will be changed to fit within this frame.
|
||||
func NewFrame(primitive Primitive) *Frame {
|
||||
return &Frame{
|
||||
Box: *NewBox(),
|
||||
box := NewBox()
|
||||
|
||||
f := &Frame{
|
||||
Box: box,
|
||||
primitive: primitive,
|
||||
top: 1,
|
||||
bottom: 1,
|
||||
|
@ -40,6 +42,10 @@ func NewFrame(primitive Primitive) *Frame {
|
|||
left: 1,
|
||||
right: 1,
|
||||
}
|
||||
|
||||
f.focus = f
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
// AddText adds text to the frame. Set "header" to true if the text is to appear
|
||||
|
@ -129,7 +135,7 @@ func (f *Frame) Draw(screen tcell.Screen) {
|
|||
|
||||
// Set the size of the contained primitive.
|
||||
if topMax > top {
|
||||
top = topMax + 1 + f.header
|
||||
top = topMax + f.header
|
||||
}
|
||||
if bottomMin < bottom {
|
||||
bottom = bottomMin - f.footer
|
||||
|
@ -153,3 +159,12 @@ func (f *Frame) InputHandler() func(event *tcell.EventKey) {
|
|||
return func(event *tcell.EventKey) {
|
||||
}
|
||||
}
|
||||
|
||||
// HasFocus returns whether or not this primitive has focus.
|
||||
func (f *Frame) HasFocus() bool {
|
||||
focusable, ok := f.primitive.(Focusable)
|
||||
if ok {
|
||||
return focusable.HasFocus()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ func init() {
|
|||
// InputField is a one-line box (three lines if there is a title) where the
|
||||
// user can enter text.
|
||||
type InputField struct {
|
||||
Box
|
||||
*Box
|
||||
|
||||
// The text that was entered.
|
||||
text string
|
||||
|
@ -85,7 +85,7 @@ type InputField struct {
|
|||
// NewInputField returns a new input field.
|
||||
func NewInputField() *InputField {
|
||||
return &InputField{
|
||||
Box: *NewBox(),
|
||||
Box: NewBox(),
|
||||
labelColor: tcell.ColorYellow,
|
||||
fieldBackgroundColor: tcell.ColorBlue,
|
||||
fieldTextColor: tcell.ColorWhite,
|
||||
|
@ -206,7 +206,7 @@ func (i *InputField) Draw(screen tcell.Screen) {
|
|||
}
|
||||
|
||||
// Set cursor.
|
||||
if i.hasFocus {
|
||||
if i.focus.HasFocus() {
|
||||
i.setCursor(screen)
|
||||
}
|
||||
}
|
||||
|
|
215
list.go
Normal file
215
list.go
Normal file
|
@ -0,0 +1,215 @@
|
|||
package tview
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
)
|
||||
|
||||
// listItem represents one item in a List.
|
||||
type listItem struct {
|
||||
MainText string // The main text of the list item.
|
||||
SecondaryText string // A secondary text to be shown underneath the main text.
|
||||
Shortcut rune // The key to select the list item directly, 0 if there is no shortcut.
|
||||
}
|
||||
|
||||
// List displays rows of items, each of which can be selected.
|
||||
type List struct {
|
||||
*Box
|
||||
|
||||
// The items of the list.
|
||||
items []*listItem
|
||||
|
||||
// The index of the currently selected item.
|
||||
currentItem int
|
||||
|
||||
// Whether or not to show the secondary item texts.
|
||||
showSecondaryText bool
|
||||
|
||||
// The item main text color. Selected items have their background and text
|
||||
// color switched.
|
||||
mainTextColor tcell.Color
|
||||
|
||||
// The item secondary text color.
|
||||
secondaryTextColor tcell.Color
|
||||
|
||||
// The item shortcut text color.
|
||||
shortcutColor tcell.Color
|
||||
|
||||
// An optional function which is called when a list item was selected.
|
||||
selected func(index int, mainText, secondaryText string, shortcut rune)
|
||||
}
|
||||
|
||||
// NewList returns a new form.
|
||||
func NewList() *List {
|
||||
return &List{
|
||||
Box: NewBox(),
|
||||
showSecondaryText: true,
|
||||
mainTextColor: tcell.ColorWhite,
|
||||
secondaryTextColor: tcell.ColorGreen,
|
||||
shortcutColor: tcell.ColorYellow,
|
||||
}
|
||||
}
|
||||
|
||||
// SetMainTextColorColor sets the color of the items' main text.
|
||||
func (l *List) SetMainTextColorColor(color tcell.Color) *List {
|
||||
l.mainTextColor = color
|
||||
return l
|
||||
}
|
||||
|
||||
// SetSecondaryTextColorColor sets the color of the items' secondary text.
|
||||
func (l *List) SetSecondaryTextColorColor(color tcell.Color) *List {
|
||||
l.secondaryTextColor = color
|
||||
return l
|
||||
}
|
||||
|
||||
// SetShortcutColor sets the color of the items' shortcut.
|
||||
func (l *List) SetShortcutColor(color tcell.Color) *List {
|
||||
l.shortcutColor = color
|
||||
return l
|
||||
}
|
||||
|
||||
// ShowSecondaryText determines whether or not to show secondary item texts.
|
||||
func (l *List) ShowSecondaryText(show bool) *List {
|
||||
l.showSecondaryText = show
|
||||
return l
|
||||
}
|
||||
|
||||
// SetSelectedFunc sets the function which is called when the user selects a
|
||||
// list item. The function receives the item's index in the list of items
|
||||
// (starting with 0), its main text, secondary text, and its shortcut rune.
|
||||
func (l *List) SetSelectedFunc(handler func(int, string, string, rune)) *List {
|
||||
l.selected = handler
|
||||
return l
|
||||
}
|
||||
|
||||
// AddItem adds a new item to the list. An item has a main text which will be
|
||||
// highlighted when selected. It also has a secondary text which is shown
|
||||
// underneath the main text (if it is set to visible) but which may remain
|
||||
// empty.
|
||||
//
|
||||
// The shortcut is a key binding. If the specified rune is entered, the item
|
||||
// is selected immediately. Set to 0 for no binding.
|
||||
func (l *List) AddItem(mainText, secondaryText string, shortcut rune) *List {
|
||||
l.items = append(l.items, &listItem{
|
||||
MainText: mainText,
|
||||
SecondaryText: secondaryText,
|
||||
Shortcut: shortcut,
|
||||
})
|
||||
return l
|
||||
}
|
||||
|
||||
// Draw draws this primitive onto the screen.
|
||||
func (l *List) Draw(screen tcell.Screen) {
|
||||
l.Box.Draw(screen)
|
||||
|
||||
// Determine the dimensions.
|
||||
x := l.x
|
||||
y := l.y
|
||||
width := l.width
|
||||
bottomLimit := l.y + l.height
|
||||
if l.border {
|
||||
x++
|
||||
y++
|
||||
width -= 2
|
||||
bottomLimit -= 2
|
||||
}
|
||||
|
||||
// Do we show any shortcuts?
|
||||
var showShortcuts bool
|
||||
for _, item := range l.items {
|
||||
if item.Shortcut != 0 {
|
||||
showShortcuts = true
|
||||
x += 4
|
||||
width -= 4
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the list items.
|
||||
for index, item := range l.items {
|
||||
if y >= bottomLimit {
|
||||
break
|
||||
}
|
||||
|
||||
// Shortcuts.
|
||||
if showShortcuts && item.Shortcut != 0 {
|
||||
Print(screen, fmt.Sprintf("(%s)", string(item.Shortcut)), x-2, y, width+4, AlignRight, l.shortcutColor)
|
||||
}
|
||||
|
||||
// Main text.
|
||||
color := l.mainTextColor
|
||||
if l.focus.HasFocus() && index == l.currentItem {
|
||||
textLength := len([]rune(item.MainText))
|
||||
style := tcell.StyleDefault.Background(l.mainTextColor)
|
||||
for bx := 0; bx < textLength && bx < width; bx++ {
|
||||
screen.SetContent(x+bx, y, ' ', nil, style)
|
||||
}
|
||||
color = l.backgroundColor
|
||||
}
|
||||
Print(screen, item.MainText, x, y, width, AlignLeft, color)
|
||||
y++
|
||||
|
||||
if y >= bottomLimit {
|
||||
break
|
||||
}
|
||||
|
||||
// Secondary text.
|
||||
if l.showSecondaryText {
|
||||
Print(screen, item.SecondaryText, x, y, width, AlignLeft, l.secondaryTextColor)
|
||||
y++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// InputHandler returns the handler for this primitive.
|
||||
func (l *List) InputHandler() func(event *tcell.EventKey) {
|
||||
return func(event *tcell.EventKey) {
|
||||
switch key := event.Key(); key {
|
||||
case tcell.KeyTab, tcell.KeyDown, tcell.KeyRight:
|
||||
l.currentItem++
|
||||
case tcell.KeyBacktab, tcell.KeyUp, tcell.KeyLeft:
|
||||
l.currentItem--
|
||||
case tcell.KeyHome:
|
||||
l.currentItem = 0
|
||||
case tcell.KeyEnd:
|
||||
l.currentItem = len(l.items) - 1
|
||||
case tcell.KeyPgDn:
|
||||
l.currentItem += 5
|
||||
case tcell.KeyPgUp:
|
||||
l.currentItem -= 5
|
||||
case tcell.KeyEnter:
|
||||
if l.selected != nil {
|
||||
item := l.items[l.currentItem]
|
||||
l.selected(l.currentItem, item.MainText, item.SecondaryText, item.Shortcut)
|
||||
}
|
||||
case tcell.KeyRune:
|
||||
ch := event.Rune()
|
||||
if ch != ' ' {
|
||||
// It's not a space bar. Is it a shortcut?
|
||||
var found bool
|
||||
for index, item := range l.items {
|
||||
if item.Shortcut == ch {
|
||||
// We have a shortcut.
|
||||
found = true
|
||||
l.currentItem = index
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
break
|
||||
}
|
||||
}
|
||||
if l.selected != nil {
|
||||
item := l.items[l.currentItem]
|
||||
l.selected(l.currentItem, item.MainText, item.SecondaryText, item.Shortcut)
|
||||
}
|
||||
}
|
||||
|
||||
if l.currentItem < 0 {
|
||||
l.currentItem = len(l.items) - 1
|
||||
} else if l.currentItem >= len(l.items) {
|
||||
l.currentItem = 0
|
||||
}
|
||||
}
|
||||
}
|
11
util.go
11
util.go
|
@ -1,6 +1,10 @@
|
|||
package tview
|
||||
|
||||
import "github.com/gdamore/tcell"
|
||||
import (
|
||||
"math"
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
)
|
||||
|
||||
// Text alignment within a box.
|
||||
const (
|
||||
|
@ -52,3 +56,8 @@ func Print(screen tcell.Screen, text string, x, y, maxWidth, align int, color tc
|
|||
|
||||
return len(runes)
|
||||
}
|
||||
|
||||
// PrintSimple prints white text to the screen at the given position.
|
||||
func PrintSimple(screen tcell.Screen, text string, x, y int) {
|
||||
Print(screen, text, x, y, math.MaxInt64, AlignLeft, tcell.ColorWhite)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue