mirror of
https://github.com/rivo/tview.git
synced 2025-04-26 13:49:06 +08:00
Added frames (with headers/footers) and buttons. Extended form.
This commit is contained in:
parent
f9f139caaf
commit
b83a7766a6
135
button.go
Normal file
135
button.go
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
package tview
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gdamore/tcell"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Button is labeled box that triggers an action when selected.
|
||||||
|
type Button struct {
|
||||||
|
Box
|
||||||
|
|
||||||
|
// The text to be displayed before the input area.
|
||||||
|
label string
|
||||||
|
|
||||||
|
// The label color.
|
||||||
|
labelColor tcell.Color
|
||||||
|
|
||||||
|
// The label color when the button is in focus.
|
||||||
|
labelColorActivated tcell.Color
|
||||||
|
|
||||||
|
// The background color when the button is in focus.
|
||||||
|
backgroundColorActivated tcell.Color
|
||||||
|
|
||||||
|
// An optional function which is called when the button was selected.
|
||||||
|
selected func()
|
||||||
|
|
||||||
|
// An optional function which is called when the user leaves the button. A
|
||||||
|
// key is provided indicating which key was pressed to leave (tab or backtab).
|
||||||
|
blur func(tcell.Key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewButton returns a new input field.
|
||||||
|
func NewButton(label string) *Button {
|
||||||
|
box := NewBox().SetBackgroundColor(tcell.ColorBlue)
|
||||||
|
return &Button{
|
||||||
|
Box: *box,
|
||||||
|
label: label,
|
||||||
|
labelColor: tcell.ColorWhite,
|
||||||
|
labelColorActivated: tcell.ColorBlue,
|
||||||
|
backgroundColorActivated: tcell.ColorWhite,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLabel sets the button text.
|
||||||
|
func (b *Button) SetLabel(label string) *Button {
|
||||||
|
b.label = label
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLabel returns the button text.
|
||||||
|
func (b *Button) GetLabel() string {
|
||||||
|
return b.label
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLabelColor sets the color of the button text.
|
||||||
|
func (b *Button) SetLabelColor(color tcell.Color) *Button {
|
||||||
|
b.labelColor = color
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLabelColorActivated sets the color of the button text when the button is
|
||||||
|
// in focus.
|
||||||
|
func (b *Button) SetLabelColorActivated(color tcell.Color) *Button {
|
||||||
|
b.labelColorActivated = color
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBackgroundColorActivated sets the background color of the button text when
|
||||||
|
// the button is in focus.
|
||||||
|
func (b *Button) SetBackgroundColorActivated(color tcell.Color) *Button {
|
||||||
|
b.backgroundColorActivated = color
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSelectedFunc sets a handler which is called when the button was selected.
|
||||||
|
func (b *Button) SetSelectedFunc(handler func()) *Button {
|
||||||
|
b.selected = handler
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBlurFunc sets a handler which is called when the user leaves the button.
|
||||||
|
// The callback function is provided with the key that was pressed, which is one
|
||||||
|
// of the following:
|
||||||
|
//
|
||||||
|
// - KeyEscape: Leaving the button with no specific direction.
|
||||||
|
// - KeyTab: Move to the next field.
|
||||||
|
// - KeyBacktab: Move to the previous field.
|
||||||
|
func (b *Button) SetBlurFunc(handler func(key tcell.Key)) *Button {
|
||||||
|
b.blur = handler
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw draws this primitive onto the screen.
|
||||||
|
func (b *Button) Draw(screen tcell.Screen) {
|
||||||
|
// Draw the box.
|
||||||
|
backgroundColor := b.backgroundColor
|
||||||
|
if b.hasFocus {
|
||||||
|
b.backgroundColor = b.backgroundColorActivated
|
||||||
|
}
|
||||||
|
b.Box.Draw(screen)
|
||||||
|
b.backgroundColor = backgroundColor
|
||||||
|
|
||||||
|
// Draw label.
|
||||||
|
x := b.x + b.width/2
|
||||||
|
y := b.y + b.height/2
|
||||||
|
width := b.width
|
||||||
|
if b.border {
|
||||||
|
width -= 2
|
||||||
|
}
|
||||||
|
labelColor := b.labelColor
|
||||||
|
if b.hasFocus {
|
||||||
|
labelColor = b.labelColorActivated
|
||||||
|
}
|
||||||
|
Print(screen, b.label, x, y, width, AlignCenter, labelColor)
|
||||||
|
|
||||||
|
if b.hasFocus {
|
||||||
|
screen.HideCursor()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// InputHandler returns the handler for this primitive.
|
||||||
|
func (b *Button) InputHandler() func(event *tcell.EventKey) {
|
||||||
|
return func(event *tcell.EventKey) {
|
||||||
|
// Process key event.
|
||||||
|
switch key := event.Key(); key {
|
||||||
|
case tcell.KeyEnter: // Selected.
|
||||||
|
if b.selected != nil {
|
||||||
|
b.selected()
|
||||||
|
}
|
||||||
|
case tcell.KeyBacktab, tcell.KeyTab, tcell.KeyEscape: // Leave. No action.
|
||||||
|
if b.blur != nil {
|
||||||
|
b.blur(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,20 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import "github.com/rivo/tview"
|
import (
|
||||||
|
"github.com/gdamore/tcell"
|
||||||
|
"github.com/rivo/tview"
|
||||||
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
form := tview.NewForm().AddItem("First name", "", 20, nil).AddItem("Last name", "", 20, nil).AddItem("Age", "", 4, nil)
|
app := tview.NewApplication()
|
||||||
|
|
||||||
|
form := 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)
|
form.SetBorder(true)
|
||||||
|
|
||||||
box := tview.NewFlex(tview.FlexColumn, []tview.Primitive{
|
box := tview.NewFlex(tview.FlexColumn, []tview.Primitive{
|
||||||
@ -25,7 +36,6 @@ func main() {
|
|||||||
final := tview.NewFlex(tview.FlexRow, []tview.Primitive{box})
|
final := tview.NewFlex(tview.FlexRow, []tview.Primitive{box})
|
||||||
final.AddItem(inputField, 3)
|
final.AddItem(inputField, 3)
|
||||||
|
|
||||||
app := tview.NewApplication()
|
|
||||||
app.SetRoot(final, true).SetFocus(form)
|
app.SetRoot(final, true).SetFocus(form)
|
||||||
|
|
||||||
if err := app.Run(); err != nil {
|
if err := app.Run(); err != nil {
|
||||||
|
75
form.go
75
form.go
@ -13,11 +13,15 @@ type Form struct {
|
|||||||
// The items of the form (one row per item).
|
// The items of the form (one row per item).
|
||||||
items []*InputField
|
items []*InputField
|
||||||
|
|
||||||
|
// The buttons of the form.
|
||||||
|
buttons []*Button
|
||||||
|
|
||||||
// The number of empty rows between items.
|
// The number of empty rows between items.
|
||||||
itemPadding int
|
itemPadding int
|
||||||
|
|
||||||
// The index of the item which has focus.
|
// The index of the item or button which has focus. (Items are counted first,
|
||||||
focusedItem int
|
// buttons are counted last.)
|
||||||
|
focusedElement int
|
||||||
|
|
||||||
// The label color.
|
// The label color.
|
||||||
labelColor tcell.Color
|
labelColor tcell.Color
|
||||||
@ -77,6 +81,13 @@ func (f *Form) AddItem(label, value string, fieldLength int, accept func(textToC
|
|||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddButton adds a new button to the form. The "selected" function is called
|
||||||
|
// when the user selects this button. It may be nil.
|
||||||
|
func (f *Form) AddButton(label string, selected func()) *Form {
|
||||||
|
f.buttons = append(f.buttons, NewButton(label).SetSelectedFunc(selected))
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
// Draw draws this primitive onto the screen.
|
// Draw draws this primitive onto the screen.
|
||||||
func (f *Form) Draw(screen tcell.Screen) {
|
func (f *Form) Draw(screen tcell.Screen) {
|
||||||
f.Box.Draw(screen)
|
f.Box.Draw(screen)
|
||||||
@ -92,6 +103,7 @@ func (f *Form) Draw(screen tcell.Screen) {
|
|||||||
width -= 2
|
width -= 2
|
||||||
bottomLimit -= 2
|
bottomLimit -= 2
|
||||||
}
|
}
|
||||||
|
rightLimit := x + width
|
||||||
|
|
||||||
// Find the longest label.
|
// Find the longest label.
|
||||||
var labelLength int
|
var labelLength int
|
||||||
@ -106,7 +118,7 @@ func (f *Form) Draw(screen tcell.Screen) {
|
|||||||
// Set up and draw the input fields.
|
// Set up and draw the input fields.
|
||||||
for _, inputField := range f.items {
|
for _, inputField := range f.items {
|
||||||
if y >= bottomLimit {
|
if y >= bottomLimit {
|
||||||
break
|
return // Stop here.
|
||||||
}
|
}
|
||||||
label := strings.TrimSpace(inputField.GetLabel())
|
label := strings.TrimSpace(inputField.GetLabel())
|
||||||
inputField.SetLabelColor(f.labelColor).
|
inputField.SetLabelColor(f.labelColor).
|
||||||
@ -118,34 +130,71 @@ func (f *Form) Draw(screen tcell.Screen) {
|
|||||||
inputField.Draw(screen)
|
inputField.Draw(screen)
|
||||||
y += 1 + f.itemPadding
|
y += 1 + f.itemPadding
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Draw the buttons.
|
||||||
|
if f.itemPadding == 0 {
|
||||||
|
y++
|
||||||
|
}
|
||||||
|
if y >= bottomLimit {
|
||||||
|
return // Stop here.
|
||||||
|
}
|
||||||
|
for _, button := range f.buttons {
|
||||||
|
space := rightLimit - x
|
||||||
|
if space < 1 {
|
||||||
|
return // No space for this button anymore.
|
||||||
|
}
|
||||||
|
buttonWidth := len([]rune(button.GetLabel())) + 4
|
||||||
|
if buttonWidth > space {
|
||||||
|
buttonWidth = space
|
||||||
|
}
|
||||||
|
button.SetRect(x, y, buttonWidth, 1)
|
||||||
|
button.Draw(screen)
|
||||||
|
|
||||||
|
x += buttonWidth + 2
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Focus is called by the application when the primitive receives focus.
|
// Focus is called by the application when the primitive receives focus.
|
||||||
func (f *Form) Focus(app *Application) {
|
func (f *Form) Focus(app *Application) {
|
||||||
f.Box.Focus(app)
|
f.Box.Focus(app)
|
||||||
|
|
||||||
if len(f.items) == 0 {
|
if len(f.items)+len(f.buttons) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hand on the focus to one of our items.
|
// Hand on the focus to one of our child elements.
|
||||||
if f.focusedItem < 0 || f.focusedItem >= len(f.items) {
|
if f.focusedElement < 0 || f.focusedElement >= len(f.items)+len(f.buttons) {
|
||||||
f.focusedItem = 0
|
f.focusedElement = 0
|
||||||
}
|
}
|
||||||
f.hasFocus = false
|
f.hasFocus = false
|
||||||
inputField := f.items[f.focusedItem]
|
handler := func(key tcell.Key) {
|
||||||
inputField.SetDoneFunc(func(key tcell.Key) {
|
|
||||||
switch key {
|
switch key {
|
||||||
case tcell.KeyTab:
|
case tcell.KeyTab:
|
||||||
f.focusedItem++
|
f.focusedElement++
|
||||||
|
case tcell.KeyBacktab:
|
||||||
|
f.focusedElement--
|
||||||
|
if f.focusedElement < 0 {
|
||||||
|
f.focusedElement = len(f.items) + len(f.buttons) - 1
|
||||||
|
}
|
||||||
|
case tcell.KeyEscape:
|
||||||
|
f.focusedElement = 0
|
||||||
|
}
|
||||||
f.Focus(app)
|
f.Focus(app)
|
||||||
}
|
}
|
||||||
})
|
if f.focusedElement < len(f.items) {
|
||||||
|
// We're selecting an item.
|
||||||
|
inputField := f.items[f.focusedElement]
|
||||||
|
inputField.SetDoneFunc(handler)
|
||||||
app.SetFocus(inputField)
|
app.SetFocus(inputField)
|
||||||
|
} else {
|
||||||
|
// We're selecting a button.
|
||||||
|
button := f.buttons[f.focusedElement-len(f.items)]
|
||||||
|
button.SetBlurFunc(handler)
|
||||||
|
app.SetFocus(button)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// InputHandler returns the handler for this primitive.
|
// InputHandler returns the handler for this primitive.
|
||||||
func (f *Form) InputHandler() func(event *tcell.EventKey) {
|
func (f *Form) InputHandler() func(event *tcell.EventKey) {
|
||||||
return func(event *tcell.EventKey) {
|
return func(event *tcell.EventKey) {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
155
frame.go
Normal file
155
frame.go
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
package tview
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gdamore/tcell"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Frame is a wrapper which adds a border around another primitive. The top and
|
||||||
|
// the bottom border may also contain text.
|
||||||
|
type Frame struct {
|
||||||
|
Box
|
||||||
|
|
||||||
|
// The contained primitive.
|
||||||
|
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
|
||||||
|
// size will be changed to fit within this frame.
|
||||||
|
func NewFrame(primitive Primitive) *Frame {
|
||||||
|
return &Frame{
|
||||||
|
Box: *NewBox(),
|
||||||
|
primitive: primitive,
|
||||||
|
top: 1,
|
||||||
|
bottom: 1,
|
||||||
|
header: 1,
|
||||||
|
footer: 1,
|
||||||
|
left: 1,
|
||||||
|
right: 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
f.Box.Draw(screen)
|
||||||
|
|
||||||
|
// Calculate start positions.
|
||||||
|
left := f.x
|
||||||
|
right := f.x + f.width - 1
|
||||||
|
top := f.y
|
||||||
|
bottom := f.y + f.height - 1
|
||||||
|
if f.border {
|
||||||
|
left++
|
||||||
|
right--
|
||||||
|
top++
|
||||||
|
bottom--
|
||||||
|
}
|
||||||
|
left += f.left
|
||||||
|
right -= f.right
|
||||||
|
top += f.top
|
||||||
|
bottom -= f.bottom
|
||||||
|
center := (left + right) / 2
|
||||||
|
if left >= right || top >= bottom {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
x := left
|
||||||
|
if text.Align == AlignCenter {
|
||||||
|
x = center
|
||||||
|
} else if text.Align == AlignRight {
|
||||||
|
x = right
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw text.
|
||||||
|
Print(screen, text.Text, x, y, right-left+1, text.Align, text.Color)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the size of the contained primitive.
|
||||||
|
if topMax > top {
|
||||||
|
top = topMax + 1 + f.header
|
||||||
|
}
|
||||||
|
if bottomMin < bottom {
|
||||||
|
bottom = bottomMin - f.footer
|
||||||
|
}
|
||||||
|
if top >= bottom {
|
||||||
|
return // No space for the primitive.
|
||||||
|
}
|
||||||
|
f.primitive.SetRect(left, top, right+1-left, bottom-top)
|
||||||
|
|
||||||
|
// Finally, draw the contained primitive.
|
||||||
|
f.primitive.Draw(screen)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Focus is called when this primitive receives focus.
|
||||||
|
func (f *Frame) Focus(app *Application) {
|
||||||
|
app.SetFocus(f.primitive)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InputHandler returns the handler for this primitive.
|
||||||
|
func (f *Frame) InputHandler() func(event *tcell.EventKey) {
|
||||||
|
return func(event *tcell.EventKey) {
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@ package tview
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/gdamore/tcell"
|
"github.com/gdamore/tcell"
|
||||||
@ -76,7 +77,8 @@ type InputField struct {
|
|||||||
accept func(text string, ch rune) bool
|
accept func(text string, ch rune) bool
|
||||||
|
|
||||||
// An optional function which is called when the user indicated that they
|
// An optional function which is called when the user indicated that they
|
||||||
// are done entering text. The key which was pressed is provided.
|
// are done entering text. The key which was pressed is provided (tab,
|
||||||
|
// shift-tab, enter, or escape).
|
||||||
done func(tcell.Key)
|
done func(tcell.Key)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,6 +156,7 @@ func (i *InputField) SetAcceptanceFunc(handler func(textToCheck string, lastChar
|
|||||||
// - KeyEnter: Done entering text.
|
// - KeyEnter: Done entering text.
|
||||||
// - KeyEscape: Abort text input.
|
// - KeyEscape: Abort text input.
|
||||||
// - KeyTab: Move to the next field.
|
// - KeyTab: Move to the next field.
|
||||||
|
// - KeyBacktab: Move to the previous field.
|
||||||
func (i *InputField) SetDoneFunc(handler func(key tcell.Key)) *InputField {
|
func (i *InputField) SetDoneFunc(handler func(key tcell.Key)) *InputField {
|
||||||
i.done = handler
|
i.done = handler
|
||||||
return i
|
return i
|
||||||
@ -179,17 +182,9 @@ func (i *InputField) Draw(screen tcell.Screen) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Draw label.
|
// Draw label.
|
||||||
labelStyle := tcell.StyleDefault.Background(i.backgroundColor).Foreground(i.labelColor)
|
x += Print(screen, i.label, x, y, rightLimit-x, AlignLeft, i.labelColor)
|
||||||
for _, ch := range i.label {
|
|
||||||
if x >= rightLimit {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
screen.SetContent(x, y, ch, nil, labelStyle)
|
|
||||||
x++
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw input area.
|
// Draw input area.
|
||||||
inputStyle := tcell.StyleDefault.Background(i.fieldBackgroundColor).Foreground(i.fieldTextColor)
|
|
||||||
fieldLength := i.fieldLength
|
fieldLength := i.fieldLength
|
||||||
if fieldLength == 0 {
|
if fieldLength == 0 {
|
||||||
fieldLength = math.MaxInt64
|
fieldLength = math.MaxInt64
|
||||||
@ -197,20 +192,17 @@ func (i *InputField) Draw(screen tcell.Screen) {
|
|||||||
if rightLimit-x < fieldLength {
|
if rightLimit-x < fieldLength {
|
||||||
fieldLength = rightLimit - x
|
fieldLength = rightLimit - x
|
||||||
}
|
}
|
||||||
text := []rune(i.text)
|
fieldStyle := tcell.StyleDefault.Background(i.fieldBackgroundColor)
|
||||||
index := 0
|
for index := 0; index < fieldLength; index++ {
|
||||||
if fieldLength-1 < len(text) {
|
screen.SetContent(x+index, y, ' ', nil, fieldStyle)
|
||||||
index = len(text) - fieldLength + 1
|
|
||||||
}
|
}
|
||||||
for fieldLength > 0 {
|
|
||||||
ch := ' '
|
// Draw entered text.
|
||||||
if index < len(text) {
|
fieldLength-- // We need one cell for the cursor.
|
||||||
ch = text[index]
|
if fieldLength < len([]rune(i.text)) {
|
||||||
}
|
Print(screen, i.text, x+fieldLength-1, y, fieldLength, AlignRight, i.fieldTextColor)
|
||||||
screen.SetContent(x, y, ch, nil, inputStyle)
|
} else {
|
||||||
x++
|
Print(screen, i.text, x, y, fieldLength, AlignLeft, i.fieldTextColor)
|
||||||
index++
|
|
||||||
fieldLength--
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set cursor.
|
// Set cursor.
|
||||||
@ -255,12 +247,15 @@ func (i *InputField) InputHandler() func(event *tcell.EventKey) {
|
|||||||
i.text = newText
|
i.text = newText
|
||||||
case tcell.KeyCtrlU: // Delete all.
|
case tcell.KeyCtrlU: // Delete all.
|
||||||
i.text = ""
|
i.text = ""
|
||||||
|
case tcell.KeyCtrlW: // Delete last word.
|
||||||
|
lastWord := regexp.MustCompile(`\s*\S+\s*$`)
|
||||||
|
i.text = lastWord.ReplaceAllString(i.text, "")
|
||||||
case tcell.KeyBackspace, tcell.KeyBackspace2: // Delete last character.
|
case tcell.KeyBackspace, tcell.KeyBackspace2: // Delete last character.
|
||||||
if len([]rune(i.text)) == 0 {
|
if len([]rune(i.text)) == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
i.text = i.text[:len([]rune(i.text))-1]
|
i.text = i.text[:len([]rune(i.text))-1]
|
||||||
case tcell.KeyEnter, tcell.KeyTab, tcell.KeyEscape: // We're done.
|
case tcell.KeyEnter, tcell.KeyTab, tcell.KeyBacktab, tcell.KeyEscape: // We're done.
|
||||||
if i.done != nil {
|
if i.done != nil {
|
||||||
i.done(key)
|
i.done(key)
|
||||||
}
|
}
|
||||||
|
54
util.go
Normal file
54
util.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package tview
|
||||||
|
|
||||||
|
import "github.com/gdamore/tcell"
|
||||||
|
|
||||||
|
// Text alignment within a box.
|
||||||
|
const (
|
||||||
|
AlignLeft = iota
|
||||||
|
AlignCenter
|
||||||
|
AlignRight
|
||||||
|
)
|
||||||
|
|
||||||
|
// Print prints text onto the screen at position (x,y). "align" is one of the
|
||||||
|
// Align constants and will affect the direction starting at (x,y) into which
|
||||||
|
// the text is printed. The screen's background color will be maintained. The
|
||||||
|
// number of runes printed will not exceed "maxWidth".
|
||||||
|
//
|
||||||
|
// Returns the number of runes printed.
|
||||||
|
func Print(screen tcell.Screen, text string, x, y, maxWidth, align int, color tcell.Color) int {
|
||||||
|
// We deal with runes, not with bytes.
|
||||||
|
runes := []rune(text)
|
||||||
|
if maxWidth < 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shorten text if it's too long.
|
||||||
|
if len(runes) > maxWidth {
|
||||||
|
switch align {
|
||||||
|
case AlignCenter:
|
||||||
|
trim := (len(runes) - maxWidth) / 2
|
||||||
|
runes = runes[trim : maxWidth+trim]
|
||||||
|
case AlignRight:
|
||||||
|
runes = runes[len(runes)-maxWidth:]
|
||||||
|
default: // AlignLeft.
|
||||||
|
runes = runes[:maxWidth]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust x-position.
|
||||||
|
if align == AlignCenter {
|
||||||
|
x -= len(runes) / 2
|
||||||
|
} else if align == AlignRight {
|
||||||
|
x -= len(runes) - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw text.
|
||||||
|
for _, ch := range runes {
|
||||||
|
_, _, style, _ := screen.GetContent(x, y)
|
||||||
|
style = style.Foreground(color)
|
||||||
|
screen.SetContent(x, y, ch, nil, style)
|
||||||
|
x++
|
||||||
|
}
|
||||||
|
|
||||||
|
return len(runes)
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user