mirror of https://github.com/rivo/tview.git
Introduced horizontal item alignment in forms. Resolves #33
This commit is contained in:
parent
c105638ec3
commit
aa25839cfa
|
@ -96,6 +96,11 @@ func (c *Checkbox) SetFormAttributes(label string, labelColor, bgColor, fieldTex
|
|||
return c
|
||||
}
|
||||
|
||||
// GetFieldLength returns this primitive's field length.
|
||||
func (c *Checkbox) GetFieldLength() int {
|
||||
return 1
|
||||
}
|
||||
|
||||
// SetChangedFunc sets a handler which is called when the checked state of this
|
||||
// checkbox was changed by the user. The handler function receives the new
|
||||
// state.
|
||||
|
|
15
dropdown.go
15
dropdown.go
|
@ -138,6 +138,21 @@ func (d *DropDown) SetFieldLength(length int) *DropDown {
|
|||
return d
|
||||
}
|
||||
|
||||
// GetFieldLength returns this primitive's field length.
|
||||
func (d *DropDown) GetFieldLength() int {
|
||||
if d.fieldLength > 0 {
|
||||
return d.fieldLength
|
||||
}
|
||||
fieldLength := 0
|
||||
for _, option := range d.options {
|
||||
length := runewidth.StringWidth(option.Text)
|
||||
if length > fieldLength {
|
||||
fieldLength = length
|
||||
}
|
||||
}
|
||||
return fieldLength
|
||||
}
|
||||
|
||||
// AddOption adds a new selectable option to this drop-down. The "selected"
|
||||
// callback is called when this option was selected. It may be nil.
|
||||
func (d *DropDown) AddOption(text string, selected func()) *DropDown {
|
||||
|
|
120
form.go
120
form.go
|
@ -7,6 +7,11 @@ import (
|
|||
runewidth "github.com/mattn/go-runewidth"
|
||||
)
|
||||
|
||||
// DefaultFormFieldLength is the default field length of form elements whose
|
||||
// field length is flexible (0). This is used in the Form class for horizontal
|
||||
// layouts.
|
||||
var DefaultFormFieldLength = 10
|
||||
|
||||
// FormItem is the interface all form items must implement to be able to be
|
||||
// included in a form.
|
||||
type FormItem interface {
|
||||
|
@ -18,6 +23,11 @@ type FormItem interface {
|
|||
// SetFormAttributes sets a number of item attributes at once.
|
||||
SetFormAttributes(label string, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) FormItem
|
||||
|
||||
// GetFieldLength returns the length of the form item's field (the area which
|
||||
// is manipulated by the user). A value of 0 indicates the the field length
|
||||
// is flexible and may use as much space as required.
|
||||
GetFieldLength() int
|
||||
|
||||
// SetEnteredFunc sets the handler function for when the user finished
|
||||
// entering data into the item. The handler may receive events for the
|
||||
// Enter key (we're done), the Escape key (cancel input), the Tab key (move to
|
||||
|
@ -37,6 +47,10 @@ type Form struct {
|
|||
// The buttons of the form.
|
||||
buttons []*Button
|
||||
|
||||
// If set to true, instead of position items and buttons from top to bottom,
|
||||
// they are positioned from left to right.
|
||||
horizontal bool
|
||||
|
||||
// The alignment of the buttons.
|
||||
buttonsAlign int
|
||||
|
||||
|
@ -85,12 +99,23 @@ func NewForm() *Form {
|
|||
return f
|
||||
}
|
||||
|
||||
// SetItemPadding sets the number of empty rows between form items.
|
||||
// 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 (f *Form) SetItemPadding(padding int) *Form {
|
||||
f.itemPadding = padding
|
||||
return f
|
||||
}
|
||||
|
||||
// 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 (f *Form) SetHorizontal(horizontal bool) *Form {
|
||||
f.horizontal = horizontal
|
||||
return f
|
||||
}
|
||||
|
||||
// SetLabelColor sets the color of the labels.
|
||||
func (f *Form) SetLabelColor(color tcell.Color) *Form {
|
||||
f.labelColor = color
|
||||
|
@ -110,7 +135,7 @@ func (f *Form) SetFieldTextColor(color tcell.Color) *Form {
|
|||
}
|
||||
|
||||
// SetButtonsAlign sets how the buttons align horizontally, one of AlignLeft
|
||||
// (the default), AlignCenter, and AlignRight.
|
||||
// (the default), AlignCenter, and AlignRight. This is only
|
||||
func (f *Form) SetButtonsAlign(align int) *Form {
|
||||
f.buttonsAlign = align
|
||||
return f
|
||||
|
@ -201,6 +226,14 @@ func (f *Form) Clear(includeButtons bool) *Form {
|
|||
return f
|
||||
}
|
||||
|
||||
// AddFormItem adds a new item to the form. This can be used to add your own
|
||||
// objects to the form. Note, however, that the Form class will override some
|
||||
// of its attributes to make it work in the form context.
|
||||
func (f *Form) AddFormItem(item FormItem) *Form {
|
||||
f.items = append(f.items, item)
|
||||
return f
|
||||
}
|
||||
|
||||
// GetFormItem returns the form element at the given position, starting with
|
||||
// index 0. Elements are referenced in the order they were added. Buttons are
|
||||
// not included.
|
||||
|
@ -223,6 +256,7 @@ func (f *Form) Draw(screen tcell.Screen) {
|
|||
x, y, width, height := f.GetInnerRect()
|
||||
bottomLimit := y + height
|
||||
rightLimit := x + width
|
||||
startX := x
|
||||
|
||||
// Find the longest label.
|
||||
var labelLength int
|
||||
|
@ -237,23 +271,60 @@ func (f *Form) Draw(screen tcell.Screen) {
|
|||
|
||||
// Set up and draw the input fields.
|
||||
for _, item := range f.items {
|
||||
// Stop if there is no more space.
|
||||
if y >= bottomLimit {
|
||||
return // Stop here.
|
||||
return
|
||||
}
|
||||
|
||||
// Calculate the space needed.
|
||||
label := strings.TrimSpace(item.GetLabel())
|
||||
labelWidth := runewidth.StringWidth(label)
|
||||
var itemWidth int
|
||||
if f.horizontal {
|
||||
fieldLength := item.GetFieldLength()
|
||||
if fieldLength == 0 {
|
||||
fieldLength = DefaultFormFieldLength
|
||||
}
|
||||
label += " "
|
||||
labelWidth++
|
||||
itemWidth = labelWidth + fieldLength
|
||||
} else {
|
||||
// We want all fields to align vertically.
|
||||
label += strings.Repeat(" ", labelLength-labelWidth)
|
||||
itemWidth = width
|
||||
}
|
||||
|
||||
// Advance to next line if there is no space.
|
||||
if f.horizontal && x+labelWidth+1 >= rightLimit {
|
||||
x = startX
|
||||
y += 2
|
||||
}
|
||||
|
||||
// Adjust the item's attributes.
|
||||
if x+itemWidth >= rightLimit {
|
||||
itemWidth = rightLimit - x
|
||||
}
|
||||
item.SetFormAttributes(
|
||||
label+strings.Repeat(" ", labelLength-runewidth.StringWidth(label)),
|
||||
label,
|
||||
f.labelColor,
|
||||
f.backgroundColor,
|
||||
f.fieldTextColor,
|
||||
f.fieldBackgroundColor,
|
||||
).SetRect(x, y, width, 1)
|
||||
).SetRect(x, y, itemWidth, 1)
|
||||
|
||||
// Draw items with focus last (in case of overlaps).
|
||||
if item.GetFocusable().HasFocus() {
|
||||
defer item.Draw(screen)
|
||||
} else {
|
||||
item.Draw(screen)
|
||||
}
|
||||
y += 1 + f.itemPadding
|
||||
|
||||
// Advance to next item.
|
||||
if f.horizontal {
|
||||
x += itemWidth + f.itemPadding
|
||||
} else {
|
||||
y += 1 + f.itemPadding
|
||||
}
|
||||
}
|
||||
|
||||
// How wide are the buttons?
|
||||
|
@ -262,32 +333,43 @@ func (f *Form) Draw(screen tcell.Screen) {
|
|||
for index, button := range f.buttons {
|
||||
width := runewidth.StringWidth(button.GetLabel()) + 4
|
||||
buttonWidths[index] = width
|
||||
buttonsWidth += width + 2
|
||||
buttonsWidth += width + 1
|
||||
}
|
||||
buttonsWidth -= 2
|
||||
buttonsWidth--
|
||||
|
||||
// Where do we place them?
|
||||
if x+buttonsWidth < rightLimit {
|
||||
if !f.horizontal && x+buttonsWidth < rightLimit {
|
||||
if f.buttonsAlign == AlignRight {
|
||||
x = rightLimit - buttonsWidth
|
||||
} else if f.buttonsAlign == AlignCenter {
|
||||
x = (x + rightLimit - buttonsWidth) / 2
|
||||
}
|
||||
|
||||
// In vertical layouts, buttons always appear after an empty line.
|
||||
if f.itemPadding == 0 {
|
||||
y++
|
||||
}
|
||||
}
|
||||
|
||||
// Draw them.
|
||||
if f.itemPadding == 0 {
|
||||
y++
|
||||
}
|
||||
if y >= bottomLimit {
|
||||
return // Stop here.
|
||||
}
|
||||
for index, button := range f.buttons {
|
||||
space := rightLimit - x
|
||||
if space < 1 {
|
||||
break // No space for this button anymore.
|
||||
if y >= bottomLimit {
|
||||
return // Stop here.
|
||||
}
|
||||
|
||||
space := rightLimit - x
|
||||
buttonWidth := buttonWidths[index]
|
||||
if f.horizontal {
|
||||
if space < buttonWidth-4 {
|
||||
x = startX
|
||||
y += 2
|
||||
space = width
|
||||
}
|
||||
} else {
|
||||
if space < 1 {
|
||||
break // No space for this button anymore.
|
||||
}
|
||||
}
|
||||
if buttonWidth > space {
|
||||
buttonWidth = space
|
||||
}
|
||||
|
@ -298,7 +380,7 @@ func (f *Form) Draw(screen tcell.Screen) {
|
|||
SetRect(x, y, buttonWidth, 1)
|
||||
button.Draw(screen)
|
||||
|
||||
x += buttonWidth + 2
|
||||
x += buttonWidth + 1
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -125,6 +125,11 @@ func (i *InputField) SetFieldLength(length int) *InputField {
|
|||
return i
|
||||
}
|
||||
|
||||
// GetFieldLength returns this primitive's field length.
|
||||
func (i *InputField) GetFieldLength() int {
|
||||
return i.fieldLength
|
||||
}
|
||||
|
||||
// SetMaskCharacter sets a character that masks user input on a screen. A value
|
||||
// of 0 disables masking.
|
||||
func (i *InputField) SetMaskCharacter(mask rune) *InputField {
|
||||
|
|
Loading…
Reference in New Issue