mirror of https://github.com/rivo/tview
Many improvements
parent
f855bee020
commit
d9b056bafb
@ -0,0 +1,459 @@
|
||||
//
|
||||
// Copyright (c) 2018 Litmus Automation Inc.
|
||||
// Author: Levko Burburas <levko.burburas.external@litmus.cloud>
|
||||
//
|
||||
|
||||
package tview
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
)
|
||||
|
||||
// listBoxItem represents one item in a ListBox.
|
||||
type listBoxItem 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.
|
||||
Selected func() // The optional function which is called when the item is selected.
|
||||
}
|
||||
|
||||
// ListBox displays rows of items, each of which can be selected.
|
||||
//
|
||||
// See https://github.com/rivo/tview/wiki/ListBox for an example.
|
||||
type ListBox struct {
|
||||
*Box
|
||||
|
||||
align int
|
||||
|
||||
labelFiller string
|
||||
|
||||
// The items of the list.
|
||||
items []*listBoxItem
|
||||
|
||||
// The text to be displayed before the input area.
|
||||
label string
|
||||
|
||||
// The screen width of the label area. A value of 0 means use the width of
|
||||
// the label text.
|
||||
labelWidth int
|
||||
|
||||
// The screen width of the input area. A value of 0 means extend as much as
|
||||
// possible.
|
||||
fieldWidth int
|
||||
|
||||
// The label color.
|
||||
labelColor tcell.Color
|
||||
|
||||
// The background color of the input area.
|
||||
fieldBackgroundColor tcell.Color
|
||||
|
||||
// The text color of the input area.
|
||||
fieldTextColor tcell.Color
|
||||
|
||||
// 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.
|
||||
mainTextColor tcell.Color
|
||||
|
||||
// The item secondary text color.
|
||||
secondaryTextColor tcell.Color
|
||||
|
||||
// The item shortcut text color.
|
||||
shortcutColor tcell.Color
|
||||
|
||||
// The text color for selected items.
|
||||
selectedTextColor tcell.Color
|
||||
|
||||
// The background color for selected items.
|
||||
selectedBackgroundColor tcell.Color
|
||||
|
||||
// An optional function which is called when the user has navigated to a list
|
||||
// item.
|
||||
changed func(index int, mainText, secondaryText string, shortcut rune)
|
||||
|
||||
// An optional function which is called when a list item was selected. This
|
||||
// function will be called even if the list item defines its own callback.
|
||||
selected func(index int, mainText, secondaryText string, shortcut rune)
|
||||
|
||||
// An optional function which is called when the user presses the Escape key.
|
||||
done func(tcell.Key)
|
||||
}
|
||||
|
||||
// NewListBox returns a new form.
|
||||
func NewListBox() *ListBox {
|
||||
l := &ListBox{
|
||||
Box: NewBox(),
|
||||
mainTextColor: Styles.PrimaryTextColor,
|
||||
secondaryTextColor: Styles.TertiaryTextColor,
|
||||
shortcutColor: Styles.SecondaryTextColor,
|
||||
selectedTextColor: Styles.PrimitiveBackgroundColor,
|
||||
selectedBackgroundColor: Styles.PrimaryTextColor,
|
||||
labelColor: Styles.SecondaryTextColor,
|
||||
fieldBackgroundColor: Styles.ContrastBackgroundColor,
|
||||
fieldTextColor: Styles.PrimaryTextColor,
|
||||
align: AlignLeft,
|
||||
labelFiller: " ",
|
||||
}
|
||||
|
||||
l.focus = l
|
||||
return l
|
||||
}
|
||||
|
||||
// SetCurrentItem sets the currently selected item by its index. This triggers
|
||||
// a "changed" event.
|
||||
func (l *ListBox) SetCurrentItem(index int) *ListBox {
|
||||
l.currentItem = index
|
||||
if l.currentItem < len(l.items) && l.changed != nil {
|
||||
item := l.items[l.currentItem]
|
||||
l.changed(l.currentItem, item.MainText, item.SecondaryText, item.Shortcut)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// SetCurrentItemByText sets the currently selected item by its text. This triggers
|
||||
// a "changed" event.
|
||||
func (l *ListBox) SetCurrentItemByText(text string) *ListBox {
|
||||
for i := 0; i < len(l.items); i++ {
|
||||
if l.items[i].MainText == text {
|
||||
l.SetCurrentItem(i)
|
||||
break
|
||||
}
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// GetCurrentItem returns the index of the currently selected list item.
|
||||
func (l *ListBox) GetCurrentItem() int {
|
||||
return l.currentItem
|
||||
}
|
||||
|
||||
// SetMainTextColor sets the color of the items' main text.
|
||||
func (l *ListBox) SetMainTextColor(color tcell.Color) *ListBox {
|
||||
l.mainTextColor = color
|
||||
return l
|
||||
}
|
||||
|
||||
// SetSecondaryTextColor sets the color of the items' secondary text.
|
||||
func (l *ListBox) SetSecondaryTextColor(color tcell.Color) *ListBox {
|
||||
l.secondaryTextColor = color
|
||||
return l
|
||||
}
|
||||
|
||||
// SetShortcutColor sets the color of the items' shortcut.
|
||||
func (l *ListBox) SetShortcutColor(color tcell.Color) *ListBox {
|
||||
l.shortcutColor = color
|
||||
return l
|
||||
}
|
||||
|
||||
// SetSelectedTextColor sets the text color of selected items.
|
||||
func (l *ListBox) SetSelectedTextColor(color tcell.Color) *ListBox {
|
||||
l.selectedTextColor = color
|
||||
return l
|
||||
}
|
||||
|
||||
// SetSelectedBackgroundColor sets the background color of selected items.
|
||||
func (l *ListBox) SetSelectedBackgroundColor(color tcell.Color) *ListBox {
|
||||
l.selectedBackgroundColor = color
|
||||
return l
|
||||
}
|
||||
|
||||
// ShowSecondaryText determines whether or not to show secondary item texts.
|
||||
func (l *ListBox) ShowSecondaryText(show bool) *ListBox {
|
||||
l.showSecondaryText = show
|
||||
return l
|
||||
}
|
||||
|
||||
// SetChangedFunc sets the function which is called when the user navigates to
|
||||
// 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.
|
||||
//
|
||||
// This function is also called when the first item is added or when
|
||||
// SetCurrentItem() is called.
|
||||
func (l *ListBox) SetChangedFunc(handler func(int, string, string, rune)) *ListBox {
|
||||
l.changed = handler
|
||||
return l
|
||||
}
|
||||
|
||||
// SetSelectedFunc sets the function which is called when the user selects a
|
||||
// list item by pressing Enter on the current selection. 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 *ListBox) SetSelectedFunc(handler func(int, string, string, rune)) *ListBox {
|
||||
l.selected = handler
|
||||
return l
|
||||
}
|
||||
|
||||
// SetDoneFunc sets a function which is called when the user presses the Escape
|
||||
// key.
|
||||
func (l *ListBox) SetDoneFunc(handler func(tcell.Key)) *ListBox {
|
||||
l.done = 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.
|
||||
//
|
||||
// The "selected" callback will be invoked when the user selects the item. You
|
||||
// may provide nil if no such item is needed or if all events are handled
|
||||
// through the selected callback set with SetSelectedFunc().
|
||||
func (l *ListBox) AddItem(mainText, secondaryText string, shortcut rune, selected func()) *ListBox {
|
||||
l.items = append(l.items, &listBoxItem{
|
||||
MainText: mainText,
|
||||
SecondaryText: secondaryText,
|
||||
Shortcut: shortcut,
|
||||
Selected: selected,
|
||||
})
|
||||
if len(l.items) == 1 && l.changed != nil {
|
||||
item := l.items[0]
|
||||
l.changed(0, item.MainText, item.SecondaryText, item.Shortcut)
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
// Clear removes all items from the list.
|
||||
func (l *ListBox) Clear() *ListBox {
|
||||
l.items = nil
|
||||
l.currentItem = 0
|
||||
return l
|
||||
}
|
||||
|
||||
// Draw draws this primitive onto the screen.
|
||||
func (l *ListBox) Draw(screen tcell.Screen) {
|
||||
l.Box.Draw(screen)
|
||||
|
||||
// Determine the dimensions.
|
||||
x, y, width, height := l.GetInnerRect()
|
||||
bottomLimit := y + height
|
||||
|
||||
// Do we show any shortcuts?
|
||||
var showShortcuts bool
|
||||
for _, item := range l.items {
|
||||
if item.Shortcut != 0 {
|
||||
showShortcuts = true
|
||||
x += 4
|
||||
width -= 4
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// We want to keep the current selection in view. What is our offset?
|
||||
var offset int
|
||||
if l.showSecondaryText {
|
||||
if l.currentItem >= height/2 {
|
||||
offset = l.currentItem + 1 - (height / 2)
|
||||
}
|
||||
} else {
|
||||
if l.currentItem >= height {
|
||||
offset = l.currentItem + 1 - height
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the list items.
|
||||
for index, item := range l.items {
|
||||
if index < offset {
|
||||
continue
|
||||
}
|
||||
|
||||
if y >= bottomLimit {
|
||||
break
|
||||
}
|
||||
|
||||
// Shortcuts.
|
||||
if showShortcuts && item.Shortcut != 0 {
|
||||
Print(screen, fmt.Sprintf("(%s)", string(item.Shortcut)), x-5, y, 4, AlignRight, l.shortcutColor)
|
||||
}
|
||||
|
||||
// Main text.
|
||||
Print(screen, item.MainText, x, y, width, AlignLeft, l.mainTextColor)
|
||||
|
||||
// Background color of selected text.
|
||||
if index == l.currentItem {
|
||||
textWidth := StringWidth(item.MainText)
|
||||
for bx := 0; bx < textWidth && bx < width; bx++ {
|
||||
m, c, style, _ := screen.GetContent(x+bx, y)
|
||||
fg, _, _ := style.Decompose()
|
||||
if fg == l.mainTextColor {
|
||||
fg = l.selectedTextColor
|
||||
}
|
||||
style = style.Background(l.selectedBackgroundColor).Foreground(fg)
|
||||
screen.SetContent(x+bx, y, m, c, style)
|
||||
}
|
||||
}
|
||||
|
||||
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 *ListBox) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
|
||||
_, _, _, height := l.GetInnerRect()
|
||||
return l.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
|
||||
previousItem := l.currentItem
|
||||
|
||||
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 += height
|
||||
case tcell.KeyPgUp:
|
||||
l.currentItem -= height
|
||||
case tcell.KeyEnter:
|
||||
item := l.items[l.currentItem]
|
||||
if item.Selected != nil {
|
||||
item.Selected()
|
||||
}
|
||||
if l.selected != nil {
|
||||
l.selected(l.currentItem, item.MainText, item.SecondaryText, item.Shortcut)
|
||||
}
|
||||
case tcell.KeyEscape:
|
||||
if l.done != nil {
|
||||
l.done(key)
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
item := l.items[l.currentItem]
|
||||
if item.Selected != nil {
|
||||
item.Selected()
|
||||
}
|
||||
if l.selected != nil {
|
||||
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
|
||||
}
|
||||
|
||||
if l.currentItem != previousItem && l.currentItem < len(l.items) && l.changed != nil {
|
||||
item := l.items[l.currentItem]
|
||||
l.changed(l.currentItem, item.MainText, item.SecondaryText, item.Shortcut)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// SetFieldAlign sets the input alignment within the inputfield. This must be
|
||||
// either AlignLeft, AlignCenter, or AlignRight.
|
||||
func (l *ListBox) SetFieldAlign(align int) FormItem {
|
||||
l.align = align
|
||||
return l
|
||||
}
|
||||
|
||||
// GetFieldAlign returns the input alignment within the inputfield.
|
||||
func (l *ListBox) GetFieldAlign() (align int) {
|
||||
return l.align
|
||||
}
|
||||
|
||||
// SetLabelFiller sets a sign which will be fill the label when this one need to stretch
|
||||
func (l *ListBox) SetLabelFiller(labelFiller string) FormItem {
|
||||
l.labelFiller = labelFiller
|
||||
return l
|
||||
}
|
||||
|
||||
// GetLabelWidth returns label width.
|
||||
func (l *ListBox) GetLabelWidth() int {
|
||||
return StringWidth(strings.Replace(l.label, "%s", "", -1))
|
||||
}
|
||||
|
||||
// SetLabelWidth sets the screen width of the label. A value of 0 will cause the
|
||||
// primitive to use the width of the label string.
|
||||
func (l *ListBox) SetLabelWidth(width int) *ListBox {
|
||||
l.labelWidth = width
|
||||
return l
|
||||
}
|
||||
|
||||
// GetLabelFiller gets a sign which uses for stretching
|
||||
func (l *ListBox) GetLabelFiller() (labelFiller string) {
|
||||
return l.labelFiller
|
||||
}
|
||||
|
||||
// SetFieldWidth sets the screen width of the input area. A value of 0 means
|
||||
// extend as much as possible.
|
||||
func (l *ListBox) SetFieldWidth(width int) FormItem {
|
||||
l.fieldWidth = width
|
||||
return l
|
||||
}
|
||||
|
||||
// GetFieldWidth returns this primitive's field width.
|
||||
func (l *ListBox) GetFieldWidth() int {
|
||||
if l.fieldWidth == 0 {
|
||||
_, _, l.fieldWidth, _ = l.GetInnerRect()
|
||||
}
|
||||
return l.fieldWidth
|
||||
}
|
||||
|
||||
// SetLabel sets the text to be displayed before the input area.
|
||||
func (l *ListBox) SetLabel(label string) *ListBox {
|
||||
l.label = label
|
||||
return l
|
||||
}
|
||||
|
||||
// GetLabel returns the text to be displayed before the input area.
|
||||
func (l *ListBox) GetLabel() string {
|
||||
return l.label
|
||||
}
|
||||
|
||||
// SetFinishedFunc calls SetDoneFunc().
|
||||
func (l *ListBox) SetFinishedFunc(handler func(key tcell.Key)) FormItem {
|
||||
return l.SetDoneFunc(handler)
|
||||
}
|
||||
|
||||
// SetFormAttributes sets attributes shared by all form items.
|
||||
func (l *ListBox) SetFormAttributes(labelWidth, fieldWidth int, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) FormItem {
|
||||
if l.fieldWidth == 0 {
|
||||
l.fieldWidth = fieldWidth
|
||||
}
|
||||
if l.labelWidth == 0 {
|
||||
l.labelWidth = labelWidth
|
||||
}
|
||||
|
||||
l.labelColor = labelColor
|
||||
l.SetBackgroundColor(bgColor)
|
||||
l.fieldTextColor = fieldTextColor
|
||||
l.fieldBackgroundColor = fieldBgColor
|
||||
return l
|
||||
}
|
@ -0,0 +1,462 @@
|
||||
//
|
||||
// Copyright (c) 2018 Litmus Automation Inc.
|
||||
// Author: Levko Burburas <levko.burburas.external@litmus.cloud>
|
||||
//
|
||||
|
||||
package tview
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"github.com/gdamore/tcell"
|
||||
)
|
||||
|
||||
// RadioOption holds key and title for radio option
|
||||
type RadioOption struct {
|
||||
Name string
|
||||
Title string
|
||||
}
|
||||
|
||||
// NewRadioOption returns a new option for RadioOption
|
||||
func NewRadioOption(name, text string) *RadioOption {
|
||||
return &RadioOption{
|
||||
Name: name,
|
||||
Title: text,
|
||||
}
|
||||
}
|
||||
|
||||
// RadioButtons implements a simple primitive for radio button selections.
|
||||
type RadioButtons struct {
|
||||
*Box
|
||||
options []*RadioOption
|
||||
currentOption int
|
||||
selectedOption int
|
||||
itemPadding int
|
||||
align int
|
||||
|
||||
lockColors bool
|
||||
|
||||
// The text to be displayed before the input area.
|
||||
subLabel string
|
||||
|
||||
// The item sub label color.
|
||||
subLabelColor tcell.Color
|
||||
|
||||
// The text to be displayed before the input area.
|
||||
label string
|
||||
|
||||
labelFiller string
|
||||
|
||||
// The label color.
|
||||
labelColor tcell.Color
|
||||
|
||||
// The background color of the input area.
|
||||
fieldBackgroundColor tcell.Color
|
||||
|
||||
// The text color of the input area.
|
||||
fieldTextColor tcell.Color
|
||||
|
||||
// The item main text color.
|
||||
mainTextColor tcell.Color
|
||||
|
||||
// The item secondary text color.
|
||||
secondaryTextColor tcell.Color
|
||||
|
||||
// The text color for selected items.
|
||||
selectedTextColor tcell.Color
|
||||
|
||||
// The background color for selected items.
|
||||
selectedBackgroundColor tcell.Color
|
||||
|
||||
// The screen width of the input area. A value of 0 means extend as much as
|
||||
// possible.
|
||||
fieldWidth int
|
||||
|
||||
labelWidth int
|
||||
|
||||
// An optional function which is called when the user indicated that they
|
||||
// are done selecting options. The key which was pressed is provided (tab,
|
||||
// shift-tab, or escape).
|
||||
done func(tcell.Key)
|
||||
|
||||
// If set to true, instead of position items and buttons from top to bottom,
|
||||
// they are positioned from left to right.
|
||||
horizontal bool
|
||||
|
||||
horizontalSeparator string
|
||||
|
||||
// An optional function which is called when the user has navigated to a list
|
||||
// item.
|
||||
changed func(*RadioOption)
|
||||
}
|
||||
|
||||
// NewRadioButtons returns a new radio button primitive.
|
||||
func NewRadioButtons() *RadioButtons {
|
||||
r := &RadioButtons{
|
||||
Box: NewBox(),
|
||||
labelColor: tcell.ColorWhite,
|
||||
fieldBackgroundColor: tcell.ColorBlack,
|
||||
fieldTextColor: tcell.ColorWhite,
|
||||
mainTextColor: Styles.PrimaryTextColor,
|
||||
secondaryTextColor: Styles.TertiaryTextColor,
|
||||
selectedTextColor: Styles.PrimitiveBackgroundColor,
|
||||
selectedBackgroundColor: Styles.PrimaryTextColor,
|
||||
align: AlignLeft,
|
||||
labelFiller: " ",
|
||||
}
|
||||
|
||||
r.focus = r
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// SetOptions replaces all current options with the ones provided
|
||||
func (r *RadioButtons) SetOptions(options []*RadioOption) *RadioButtons {
|
||||
r.options = options
|
||||
return r
|
||||
}
|
||||
|
||||
// SetItemPadding sets the number of empty rows between form items for vertical
|
||||
// layouts and the number of empty cells between form items for horizontal
|
||||
// layouts.
|
||||
func (r *RadioButtons) SetItemPadding(padding int) *RadioButtons {
|
||||
r.itemPadding = padding
|
||||
return r
|
||||
}
|
||||
|
||||
// SetAlign sets the radiobox alignment within the box. This must be
|
||||
// either AlignLeft, AlignCenter, or AlignRight.
|
||||
func (r *RadioButtons) SetAlign(align int) *RadioButtons {
|
||||
r.align = align
|
||||
return r
|
||||
}
|
||||
|
||||
// InputHandler returns the handler for this primitive.
|
||||
func (r *RadioButtons) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
|
||||
return r.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
|
||||
switch key := event.Key(); key {
|
||||
case tcell.KeyUp, tcell.KeyLeft:
|
||||
r.currentOption--
|
||||
if r.currentOption < 0 {
|
||||
r.currentOption = len(r.options) - 1 - int(math.Mod(float64(len(r.options)), float64(r.currentOption)))
|
||||
}
|
||||
case tcell.KeyDown, tcell.KeyRight:
|
||||
r.currentOption++
|
||||
if r.currentOption >= len(r.options) {
|
||||
r.currentOption = int(math.Mod(float64(len(r.options)), float64(r.currentOption)))
|
||||
}
|
||||
case tcell.KeyEnter, tcell.KeyRune: // We're done.
|
||||
r.selectedOption = r.currentOption
|
||||
if r.changed != nil {
|
||||
r.changed(r.options[r.currentOption])
|
||||
}
|
||||
case tcell.KeyTab, tcell.KeyBacktab: // We're done.
|
||||
if r.done != nil {
|
||||
r.done(key)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// GetRect returns the current position of the rectangle, x, y, width, and
|
||||
// height.
|
||||
func (r *RadioButtons) GetRect() (int, int, int, int) {
|
||||
x, y, width, _ := r.Box.GetRect()
|
||||
optionsCount := len(r.options)
|
||||
if optionsCount == 0 {
|
||||
optionsCount = 1
|
||||
}
|
||||
height := 1
|
||||
if !r.horizontal {
|
||||
height = (optionsCount * (r.itemPadding + 1)) - r.itemPadding
|
||||
}
|
||||
if height > 0 && r.Box.GetBorder() {
|
||||
height++
|
||||
}
|
||||
return x, y, width, height
|
||||
}
|
||||
|
||||
// GetLabelWidth returns label width.
|
||||
func (r *RadioButtons) GetLabelWidth() int {
|
||||
return StringWidth(strings.Replace(r.subLabel+r.label, "%s", "", -1))
|
||||
}
|
||||
|
||||
// GetFieldWidth returns field width.
|
||||
func (r *RadioButtons) GetFieldWidth() int {
|
||||
if r.fieldWidth > 0 {
|
||||
return r.fieldWidth
|
||||
}
|
||||
|
||||
var maxWidth int
|
||||
for i := 0; i < len(r.options); i++ {
|
||||
line := fmt.Sprintf(`%s[white] %s`, Styles.GraphicsRadioUnchecked, r.options[i].Title)
|
||||
if r.horizontal {
|
||||
maxWidth += StringWidth(line)
|
||||
if i < len(r.options)-1 {
|
||||
maxWidth += r.itemPadding + 1
|
||||
}
|
||||
} else if maxWidth < StringWidth(line) {
|
||||
maxWidth = StringWidth(line)
|
||||
}
|
||||
}
|
||||
|
||||
return maxWidth
|
||||
}
|
||||
|
||||
// SetFieldWidth sets the screen width of the options area. A value of 0 means
|
||||
// extend to as long as the longest option text.
|
||||
func (r *RadioButtons) SetFieldWidth(width int) FormItem {
|
||||
r.fieldWidth = width
|
||||
return r
|
||||
}
|
||||
|
||||
// SetFormAttributes sets attributes shared by all form items.
|
||||
func (r *RadioButtons) SetFormAttributes(labelWidth, fieldWidth int, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) FormItem {
|
||||
if r.fieldWidth == 0 {
|
||||
r.fieldWidth = fieldWidth
|
||||
}
|
||||
if r.labelWidth == 0 {
|
||||
r.labelWidth = labelWidth
|
||||
}
|
||||
if !r.lockColors {
|
||||
r.labelColor = labelColor
|
||||
r.backgroundColor = bgColor
|
||||
r.fieldTextColor = fieldTextColor
|
||||
r.fieldBackgroundColor = fieldBgColor
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// SetFieldAlign sets the input alignment within the radiobutton box. This must be
|
||||
// either AlignLeft, AlignCenter, or AlignRight.
|
||||
func (r *RadioButtons) SetFieldAlign(align int) FormItem {
|
||||
r.align = align
|
||||
return r
|
||||
}
|
||||
|
||||
// SetLabelFiller sets a sign which will be fill the label when this one need to stretch
|
||||
func (r *RadioButtons) SetLabelFiller(filler string) FormItem {
|
||||
r.labelFiller = filler
|
||||
return r
|
||||
}
|
||||
|
||||
// GetFieldAlign returns the input alignment within the radiobutton box.
|
||||
func (r *RadioButtons) GetFieldAlign() (align int) {
|
||||
return r.align
|
||||
}
|
||||
|
||||
// SetDoneFunc sets a handler which is called when the user is done selecting
|
||||
// options. The callback function is provided with the key that was pressed,
|
||||
// which is one of the following:
|
||||
//
|
||||
// - KeyEscape: Abort selection.
|
||||
// - KeyTab: Move to the next field.
|
||||
// - KeyBacktab: Move to the previous field.
|
||||
func (r *RadioButtons) SetDoneFunc(handler func(key tcell.Key)) *RadioButtons {
|
||||
r.done = handler
|
||||
return r
|
||||
}
|
||||
|
||||
// SetFinishedFunc calls SetDoneFunc().
|
||||
func (r *RadioButtons) SetFinishedFunc(handler func(key tcell.Key)) FormItem {
|
||||
return r.SetDoneFunc(handler)
|
||||
}
|
||||
|
||||
// SetLabel sets the text to be displayed before the input area.
|
||||
func (r *RadioButtons) SetLabel(label string) *RadioButtons {
|
||||
if !strings.Contains(label, "%s") {
|
||||
label += "%s"
|
||||
}
|
||||
r.label = label
|
||||
return r
|
||||
}
|
||||
|
||||
// GetLabel returns the text to be displayed before the input area.
|
||||
func (r *RadioButtons) GetLabel() string {
|
||||
return r.label
|
||||
}
|
||||
|
||||
// SetLockColors locks the change of colors by form
|
||||
func (r *RadioButtons) SetLockColors(lock bool) *RadioButtons {
|
||||
r.lockColors = lock
|
||||
return r
|
||||
}
|
||||
|
||||
// SetLabelColor sets the color of the label.
|
||||
func (r *RadioButtons) SetLabelColor(color tcell.Color) *RadioButtons {
|
||||
r.labelColor = color
|
||||
return r
|
||||
}
|
||||
|
||||
// SetSubLabel sets the text to be displayed before the input area.
|
||||
func (r *RadioButtons) SetSubLabel(label string) *RadioButtons {
|
||||
r.subLabel = label
|
||||
return r
|
||||
}
|
||||
|
||||
// SetSubLabelColor sets the color of the subLabel.
|
||||
func (r *RadioButtons) SetSubLabelColor(color tcell.Color) *RadioButtons {
|
||||
r.subLabelColor = color
|
||||
return r
|
||||
}
|
||||
|
||||
// SetFieldBackgroundColor sets the background color of the options area.
|
||||
func (r *RadioButtons) SetFieldBackgroundColor(color tcell.Color) *RadioButtons {
|
||||
r.fieldBackgroundColor = color
|
||||
return r
|
||||
}
|
||||
|
||||
// SetFieldTextColor sets the text color of the options area.
|
||||
func (r *RadioButtons) SetFieldTextColor(color tcell.Color) *RadioButtons {
|
||||
r.fieldTextColor = color
|
||||
return r
|
||||
}
|
||||
|
||||
// SetHorizontal sets the direction the form elements are laid out. If set to
|
||||
// true, instead of positioning them from top to bottom (the default), they are
|
||||
// positioned from left to right, moving into the next row if there is not
|
||||
// enough space.
|
||||
func (r *RadioButtons) SetHorizontal(horizontal bool) *RadioButtons {
|
||||
r.horizontal = horizontal
|
||||
return r
|
||||
}
|
||||
|
||||
// SetCurrentOptionByName sets the index of the currently selected option. This may
|
||||
// be a negative value to indicate that no option is currently selected.
|
||||
func (r *RadioButtons) SetCurrentOptionByName(name string) *RadioButtons {
|
||||
for i := 0; i < len(r.options); i++ {
|
||||
if r.options[i].Name == name {
|
||||
r.selectedOption = i
|
||||
if r.changed != nil {
|
||||
r.changed(r.options[r.selectedOption])
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// SetCurrentOption sets the index of the currently selected option. This may
|
||||
// be a negative value to indicate that no option is currently selected.
|
||||
func (r *RadioButtons) SetCurrentOption(index int) *RadioButtons {
|
||||
r.selectedOption = index
|
||||
if r.changed != nil {
|
||||
r.changed(r.options[r.selectedOption])
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// GetCurrentOption returns the index of the currently selected option as well
|
||||
// as its text. If no option was selected, -1 and an empty string is returned.
|
||||
func (r *RadioButtons) GetCurrentOption() *RadioOption {
|
||||
return r.options[r.selectedOption]
|
||||
}
|
||||
|
||||
// GetCurrentOptionName returns the name of the currently selected option.
|
||||
func (r *RadioButtons) GetCurrentOptionName() string {
|
||||
return r.options[r.selectedOption].Name
|
||||
}
|
||||
|
||||
// SetChangedFunc sets the function which is called when the user navigates to
|
||||
// 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.
|
||||
//
|
||||
// This function is also called when the first item is added or when
|
||||
// SetCurrentItem() is called.
|
||||
func (r *RadioButtons) SetChangedFunc(handler func(*RadioOption)) *RadioButtons {
|
||||
r.changed = handler
|
||||
return r
|
||||
}
|
||||
|
||||
// Draw draws this primitive onto the screen.
|
||||
func (r *RadioButtons) Draw(screen tcell.Screen) {
|
||||
r.Box.Draw(screen)
|
||||
x, y, width, height := r.GetInnerRect()
|
||||
|
||||
rightLimit := x + width
|
||||
if height < 1 || rightLimit <= x {
|
||||
return
|
||||
}
|
||||
|
||||
// Draw label.
|
||||
var labels = []struct {
|
||||
text string
|
||||
color tcell.Color
|
||||
}{{
|
||||
text: r.subLabel,
|
||||
color: r.subLabelColor,
|
||||
}, {
|
||||
text: r.label,
|
||||
color: r.labelColor,
|
||||
}}
|
||||
|
||||
if len(labels) > 0 {
|
||||
labelWidth := r.labelWidth
|
||||
if labelWidth > rightLimit-x {
|
||||
labelWidth = rightLimit - x
|
||||
}
|
||||
|
||||
addCount := labelWidth - r.GetLabelWidth()
|
||||
|
||||
for _, label := range labels {
|
||||
if addCount > 0 && strings.Contains(label.text, "%s") {
|
||||
label.text = fmt.Sprintf(label.text, strings.Repeat(r.labelFiller, addCount))
|
||||
addCount = 0
|
||||
} else {
|
||||
label.text = strings.Replace(label.text, "%s", "", -1)
|
||||
}
|
||||
|
||||
labelWidth = StringWidth(label.text)
|
||||
Print(screen, label.text, x, y, labelWidth, AlignLeft, label.color)
|
||||
x += labelWidth
|
||||
}
|
||||
}
|
||||
|
||||
var lineWidth int
|
||||
for index, option := range r.options {
|
||||
if index >= height && !r.horizontal {
|
||||
break
|
||||
}
|
||||
radioButton := Styles.GraphicsRadioUnchecked // Unchecked.
|
||||
if index == r.selectedOption {
|
||||
radioButton = Styles.GraphicsRadioChecked // Checked.
|
||||
}
|
||||
|
||||
line := fmt.Sprintf(`%s[white] %s`, radioButton, option.Title)
|
||||
if r.horizontal {
|
||||
Print(screen, line, x+lineWidth, y, width, AlignLeft, tcell.ColorWhite)
|
||||
} else {
|
||||
Print(screen, line, x, y+(index*(r.itemPadding+1)), width, AlignLeft, tcell.ColorWhite)
|
||||
}
|
||||
|
||||
// Background color of selected text.
|
||||
if r.HasFocus() && index == r.currentOption {
|
||||
textWidth := StringWidth(line)
|
||||
for bx := 0; bx < textWidth && bx < width; bx++ {
|
||||
if r.horizontal {
|
||||
m, c, style, _ := screen.GetContent(x+lineWidth+bx, y)
|
||||
fg, _, _ := style.Decompose()
|
||||
if fg == r.mainTextColor {
|
||||
fg = r.selectedTextColor
|
||||
}
|
||||
style = style.Background(r.selectedBackgroundColor).Foreground(fg)
|
||||
screen.SetContent(x+lineWidth+bx, y, m, c, style)
|
||||
} else {
|
||||
m, c, style, _ := screen.GetContent(x+bx, y+(index*(r.itemPadding+1)))
|
||||
fg, _, _ := style.Decompose()
|
||||
if fg == r.mainTextColor {
|
||||
fg = r.selectedTextColor
|
||||
}
|
||||
style = style.Background(r.selectedBackgroundColor).Foreground(fg)
|
||||
screen.SetContent(x+bx, y+(index*(r.itemPadding+1)), m, c, style)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if r.horizontal {
|
||||
lineWidth += StringWidth(line) + (r.itemPadding + 1)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue