|
|
|
@ -192,11 +192,24 @@ type textAreaUndoItem struct {
|
|
|
|
|
type TextArea struct {
|
|
|
|
|
*Box
|
|
|
|
|
|
|
|
|
|
// The size of the text area. If set to 0, the text area will use the entire
|
|
|
|
|
// available space.
|
|
|
|
|
width, height int
|
|
|
|
|
|
|
|
|
|
// The text to be shown in the text area when it is empty.
|
|
|
|
|
placeholder string
|
|
|
|
|
|
|
|
|
|
// The label text shown, usually when part of a form.
|
|
|
|
|
label string
|
|
|
|
|
|
|
|
|
|
// The width of the text area's label.
|
|
|
|
|
labelWidth int
|
|
|
|
|
|
|
|
|
|
// Styles:
|
|
|
|
|
|
|
|
|
|
// The label style.
|
|
|
|
|
labelStyle tcell.Style
|
|
|
|
|
|
|
|
|
|
// The style of the text. Background colors different from the Box's
|
|
|
|
|
// background color may lead to unwanted artefacts.
|
|
|
|
|
textStyle tcell.Style
|
|
|
|
@ -314,6 +327,10 @@ type TextArea struct {
|
|
|
|
|
// An optional function which is called when the position of the cursor or
|
|
|
|
|
// the selection has changed.
|
|
|
|
|
moved func()
|
|
|
|
|
|
|
|
|
|
// A callback function set by the Form class and called when the user leaves
|
|
|
|
|
// this form item.
|
|
|
|
|
finished func(tcell.Key)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewTextArea returns a new text area. Use [TextArea.SetText] to set the
|
|
|
|
@ -324,6 +341,7 @@ func NewTextArea() *TextArea {
|
|
|
|
|
wrap: true,
|
|
|
|
|
wordWrap: true,
|
|
|
|
|
placeholderStyle: tcell.StyleDefault.Background(Styles.PrimitiveBackgroundColor).Foreground(Styles.TertiaryTextColor),
|
|
|
|
|
labelStyle: tcell.StyleDefault.Foreground(Styles.SecondaryTextColor),
|
|
|
|
|
textStyle: tcell.StyleDefault.Background(Styles.PrimitiveBackgroundColor).Foreground(Styles.PrimaryTextColor),
|
|
|
|
|
selectedStyle: tcell.StyleDefault.Background(Styles.PrimaryTextColor).Foreground(Styles.PrimitiveBackgroundColor),
|
|
|
|
|
spans: make([]textAreaSpan, 2, pieceChainMinCap), // We reserve some space to avoid reallocations right when editing starts.
|
|
|
|
@ -693,6 +711,44 @@ func (t *TextArea) SetPlaceholder(placeholder string) *TextArea {
|
|
|
|
|
return t
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetLabel sets the text to be displayed before the text area.
|
|
|
|
|
func (t *TextArea) SetLabel(label string) *TextArea {
|
|
|
|
|
t.label = label
|
|
|
|
|
return t
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetLabel returns the text to be displayed before the text area.
|
|
|
|
|
func (t *TextArea) GetLabel() string {
|
|
|
|
|
return t.label
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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 (t *TextArea) SetLabelWidth(width int) *TextArea {
|
|
|
|
|
t.labelWidth = width
|
|
|
|
|
return t
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetSize sets the screen size of the input element of the text area. The input
|
|
|
|
|
// element is always located next to the label which is always located in the
|
|
|
|
|
// top left corner. If any of the values are 0 or larger than the available
|
|
|
|
|
// space, the available space will be used.
|
|
|
|
|
func (t *TextArea) SetSize(rows, columns int) *TextArea {
|
|
|
|
|
t.width = columns
|
|
|
|
|
t.height = rows
|
|
|
|
|
return t
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetFieldWidth returns this primitive's field width.
|
|
|
|
|
func (t *TextArea) GetFieldWidth() int {
|
|
|
|
|
return t.width
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetFieldHeight returns this primitive's field height.
|
|
|
|
|
func (t *TextArea) GetFieldHeight() int {
|
|
|
|
|
return t.height
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetMaxLength sets the maximum number of bytes allowed in the text area. A
|
|
|
|
|
// value of 0 means there is no limit. If the text area currently contains more
|
|
|
|
|
// bytes than this, it may violate this constraint.
|
|
|
|
@ -701,6 +757,17 @@ func (t *TextArea) SetMaxLength(maxLength int) *TextArea {
|
|
|
|
|
return t
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetLabelStyle sets the style of the label.
|
|
|
|
|
func (t *TextArea) SetLabelStyle(style tcell.Style) *TextArea {
|
|
|
|
|
t.labelStyle = style
|
|
|
|
|
return t
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// GetLabelStyle returns the style of the label.
|
|
|
|
|
func (t *TextArea) GetLabelStyle() tcell.Style {
|
|
|
|
|
return t.labelStyle
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetTextStyle sets the style of the text. Background colors different from the
|
|
|
|
|
// Box's background color may lead to unwanted artefacts.
|
|
|
|
|
func (t *TextArea) SetTextStyle(style tcell.Style) *TextArea {
|
|
|
|
@ -775,6 +842,21 @@ func (t *TextArea) SetMovedFunc(handler func()) *TextArea {
|
|
|
|
|
return t
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetFinishedFunc sets a callback invoked when the user leaves this form item.
|
|
|
|
|
func (t *TextArea) SetFinishedFunc(handler func(key tcell.Key)) FormItem {
|
|
|
|
|
t.finished = handler
|
|
|
|
|
return t
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetFormAttributes sets attributes shared by all form items.
|
|
|
|
|
func (t *TextArea) SetFormAttributes(labelWidth int, labelColor, bgColor, fieldTextColor, fieldBgColor tcell.Color) FormItem {
|
|
|
|
|
t.labelWidth = labelWidth
|
|
|
|
|
t.backgroundColor = bgColor
|
|
|
|
|
t.labelStyle = t.labelStyle.Foreground(labelColor)
|
|
|
|
|
t.textStyle = tcell.StyleDefault.Foreground(fieldTextColor).Background(fieldBgColor)
|
|
|
|
|
return t
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// replace deletes a range of text and inserts the given text at that position.
|
|
|
|
|
// If the resulting text would exceed the maximum length, the function does not
|
|
|
|
|
// do anything. The function returns the end position of the deleted/inserted
|
|
|
|
@ -947,7 +1029,7 @@ func (t *TextArea) Draw(screen tcell.Screen) {
|
|
|
|
|
|
|
|
|
|
// Prepare
|
|
|
|
|
x, y, width, height := t.GetInnerRect()
|
|
|
|
|
if width == 0 || height == 0 {
|
|
|
|
|
if width <= 0 || height <= 0 {
|
|
|
|
|
return // We have no space for anything.
|
|
|
|
|
}
|
|
|
|
|
columnOffset := t.columnOffset
|
|
|
|
@ -955,6 +1037,43 @@ func (t *TextArea) Draw(screen tcell.Screen) {
|
|
|
|
|
columnOffset = 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Draw label.
|
|
|
|
|
_, labelBg, _ := t.labelStyle.Decompose()
|
|
|
|
|
if t.labelWidth > 0 {
|
|
|
|
|
labelWidth := t.labelWidth
|
|
|
|
|
if labelWidth > width {
|
|
|
|
|
labelWidth = width
|
|
|
|
|
}
|
|
|
|
|
printWithStyle(screen, t.label, x, y, 0, labelWidth, AlignLeft, t.labelStyle, labelBg == tcell.ColorDefault)
|
|
|
|
|
x += labelWidth
|
|
|
|
|
width -= labelWidth
|
|
|
|
|
} else {
|
|
|
|
|
_, drawnWidth, _, _ := printWithStyle(screen, t.label, x, y, 0, width, AlignLeft, t.labelStyle, labelBg == tcell.ColorDefault)
|
|
|
|
|
x += drawnWidth
|
|
|
|
|
width -= drawnWidth
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// What's the space for the input element?
|
|
|
|
|
if t.width > 0 && t.width < width {
|
|
|
|
|
width = t.width
|
|
|
|
|
}
|
|
|
|
|
if t.height > 0 && t.height < height {
|
|
|
|
|
height = t.height
|
|
|
|
|
}
|
|
|
|
|
if width <= 0 {
|
|
|
|
|
return // No space left for the text area.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Draw the input element if necessary.
|
|
|
|
|
_, bg, _ := t.textStyle.Decompose()
|
|
|
|
|
if bg != t.GetBackgroundColor() {
|
|
|
|
|
for row := 0; row < height; row++ {
|
|
|
|
|
for column := 0; column < width; column++ {
|
|
|
|
|
screen.SetContent(x+column, y+row, ' ', nil, t.textStyle)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Show/hide the cursor at the end.
|
|
|
|
|
defer func() {
|
|
|
|
|
if t.HasFocus() {
|
|
|
|
@ -1856,6 +1975,12 @@ func (t *TextArea) InputHandler() func(event *tcell.EventKey, setFocus func(p Pr
|
|
|
|
|
t.selectionStart = t.cursor
|
|
|
|
|
newLastAction = taActionTypeSpace
|
|
|
|
|
case tcell.KeyTab: // Insert a tab character. It will be rendered as TabSize spaces.
|
|
|
|
|
// But forwarding takes precedence.
|
|
|
|
|
if t.finished != nil {
|
|
|
|
|
t.finished(key)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
from, to, row := t.getSelection()
|
|
|
|
|
t.cursor.pos = t.replace(from, to, "\t", t.lastAction == taActionTypeSpace)
|
|
|
|
|
t.cursor.row = -1
|
|
|
|
@ -1863,6 +1988,11 @@ func (t *TextArea) InputHandler() func(event *tcell.EventKey, setFocus func(p Pr
|
|
|
|
|
t.findCursor(true, row)
|
|
|
|
|
t.selectionStart = t.cursor
|
|
|
|
|
newLastAction = taActionTypeSpace
|
|
|
|
|
case tcell.KeyBacktab, tcell.KeyEscape: // Only used in forms.
|
|
|
|
|
if t.finished != nil {
|
|
|
|
|
t.finished(key)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
case tcell.KeyRune:
|
|
|
|
|
if event.Modifiers()&tcell.ModAlt > 0 {
|
|
|
|
|
// We accept some Alt- key combinations.
|
|
|
|
@ -2084,7 +2214,11 @@ func (t *TextArea) MouseHandler() func(action MouseAction, event *tcell.EventMou
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Turn mouse coordinates into text coordinates.
|
|
|
|
|
column := x - rectX
|
|
|
|
|
labelWidth := t.labelWidth
|
|
|
|
|
if labelWidth == 0 && t.label != "" {
|
|
|
|
|
labelWidth = TaggedStringWidth(t.label)
|
|
|
|
|
}
|
|
|
|
|
column := x - rectX - labelWidth
|
|
|
|
|
row := y - rectY
|
|
|
|
|
if !t.wrap {
|
|
|
|
|
column += t.columnOffset
|
|
|
|
|