forked from Mirrors/bubbletea
Text input value getter/setter + cursor movement methods
This commit is contained in:
parent
826420a00e
commit
6ceafa032c
|
@ -22,7 +22,6 @@ type ErrMsg error
|
||||||
type Model struct {
|
type Model struct {
|
||||||
Err error
|
Err error
|
||||||
Prompt string
|
Prompt string
|
||||||
Value string
|
|
||||||
Cursor string
|
Cursor string
|
||||||
BlinkSpeed time.Duration
|
BlinkSpeed time.Duration
|
||||||
Placeholder string
|
Placeholder string
|
||||||
|
@ -40,6 +39,9 @@ type Model struct {
|
||||||
// viewport. If 0 or less this setting is ignored.
|
// viewport. If 0 or less this setting is ignored.
|
||||||
Width int
|
Width int
|
||||||
|
|
||||||
|
// Underlying text value
|
||||||
|
value string
|
||||||
|
|
||||||
// Focus indicates whether user input focus should be on this input
|
// Focus indicates whether user input focus should be on this input
|
||||||
// component. When false, don't blink and ignore keyboard input.
|
// component. When false, don't blink and ignore keyboard input.
|
||||||
focus bool
|
focus bool
|
||||||
|
@ -55,6 +57,43 @@ type Model struct {
|
||||||
offset int
|
offset int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetValue sets the value of the text input
|
||||||
|
func (m *Model) SetValue(s string) {
|
||||||
|
if m.CharLimit > 0 && len(s) > m.CharLimit {
|
||||||
|
m.value = s[:m.CharLimit]
|
||||||
|
} else {
|
||||||
|
m.value = s
|
||||||
|
}
|
||||||
|
if m.pos > len(m.value) {
|
||||||
|
m.pos = len(m.value)
|
||||||
|
}
|
||||||
|
m.handleOverflow()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value returns the value of the text input
|
||||||
|
func (m Model) Value() string {
|
||||||
|
return m.value
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cursor start moves the cursor to the given position. If the position is out
|
||||||
|
// of bounds the cursor will be moved to the start or end accordingly.
|
||||||
|
func (m *Model) SetCursor(pos int) {
|
||||||
|
m.pos = max(0, min(len(m.value), pos))
|
||||||
|
m.handleOverflow()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CursorStart moves the cursor to the start of the field
|
||||||
|
func (m *Model) CursorStart() {
|
||||||
|
m.pos = 0
|
||||||
|
m.handleOverflow()
|
||||||
|
}
|
||||||
|
|
||||||
|
// CursorEnd moves the cursor to the end of the field
|
||||||
|
func (m *Model) CursorEnd() {
|
||||||
|
m.pos = len(m.value)
|
||||||
|
m.handleOverflow()
|
||||||
|
}
|
||||||
|
|
||||||
// Focused returns the focus state on the model
|
// Focused returns the focus state on the model
|
||||||
func (m Model) Focused() bool {
|
func (m Model) Focused() bool {
|
||||||
return m.focus
|
return m.focus
|
||||||
|
@ -74,11 +113,25 @@ func (m *Model) Blur() {
|
||||||
|
|
||||||
// Reset sets the input to its default state with no input.
|
// Reset sets the input to its default state with no input.
|
||||||
func (m *Model) Reset() {
|
func (m *Model) Reset() {
|
||||||
m.Value = ""
|
m.value = ""
|
||||||
m.offset = 0
|
m.offset = 0
|
||||||
m.pos = 0
|
m.pos = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If a max width is defined, perform some logic to treat the visible area
|
||||||
|
// as a horizontally scrolling viewport.
|
||||||
|
func (m *Model) handleOverflow() {
|
||||||
|
if m.Width > 0 {
|
||||||
|
overflow := max(0, len(m.value)-m.Width)
|
||||||
|
|
||||||
|
if overflow > 0 && m.pos < m.offset {
|
||||||
|
m.offset = max(0, min(len(m.value), m.pos))
|
||||||
|
} else if overflow > 0 && m.pos >= m.offset+m.Width {
|
||||||
|
m.offset = max(0, m.pos-m.Width)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// colorText colorizes a given string according to the TextColor value of the
|
// colorText colorizes a given string according to the TextColor value of the
|
||||||
// model
|
// model
|
||||||
func (m *Model) colorText(s string) string {
|
func (m *Model) colorText(s string) string {
|
||||||
|
@ -106,7 +159,6 @@ type BlinkMsg struct{}
|
||||||
func NewModel() Model {
|
func NewModel() Model {
|
||||||
return Model{
|
return Model{
|
||||||
Prompt: "> ",
|
Prompt: "> ",
|
||||||
Value: "",
|
|
||||||
BlinkSpeed: time.Millisecond * 600,
|
BlinkSpeed: time.Millisecond * 600,
|
||||||
Placeholder: "",
|
Placeholder: "",
|
||||||
TextColor: "",
|
TextColor: "",
|
||||||
|
@ -114,6 +166,7 @@ func NewModel() Model {
|
||||||
CursorColor: "",
|
CursorColor: "",
|
||||||
CharLimit: 0,
|
CharLimit: 0,
|
||||||
|
|
||||||
|
value: "",
|
||||||
focus: false,
|
focus: false,
|
||||||
blink: true,
|
blink: true,
|
||||||
pos: 0,
|
pos: 0,
|
||||||
|
@ -134,8 +187,8 @@ func Update(msg boba.Msg, m Model) (Model, boba.Cmd) {
|
||||||
case boba.KeyBackspace:
|
case boba.KeyBackspace:
|
||||||
fallthrough
|
fallthrough
|
||||||
case boba.KeyDelete:
|
case boba.KeyDelete:
|
||||||
if len(m.Value) > 0 {
|
if len(m.value) > 0 {
|
||||||
m.Value = m.Value[:m.pos-1] + m.Value[m.pos:]
|
m.value = m.value[:m.pos-1] + m.value[m.pos:]
|
||||||
m.pos--
|
m.pos--
|
||||||
}
|
}
|
||||||
case boba.KeyLeft:
|
case boba.KeyLeft:
|
||||||
|
@ -143,7 +196,7 @@ func Update(msg boba.Msg, m Model) (Model, boba.Cmd) {
|
||||||
m.pos--
|
m.pos--
|
||||||
}
|
}
|
||||||
case boba.KeyRight:
|
case boba.KeyRight:
|
||||||
if m.pos < len(m.Value) {
|
if m.pos < len(m.value) {
|
||||||
m.pos++
|
m.pos++
|
||||||
}
|
}
|
||||||
case boba.KeyCtrlF: // ^F, forward one character
|
case boba.KeyCtrlF: // ^F, forward one character
|
||||||
|
@ -151,23 +204,23 @@ func Update(msg boba.Msg, m Model) (Model, boba.Cmd) {
|
||||||
case boba.KeyCtrlB: // ^B, back one charcter
|
case boba.KeyCtrlB: // ^B, back one charcter
|
||||||
fallthrough
|
fallthrough
|
||||||
case boba.KeyCtrlA: // ^A, go to beginning
|
case boba.KeyCtrlA: // ^A, go to beginning
|
||||||
m.pos = 0
|
m.CursorStart()
|
||||||
case boba.KeyCtrlD: // ^D, delete char under cursor
|
case boba.KeyCtrlD: // ^D, delete char under cursor
|
||||||
if len(m.Value) > 0 && m.pos < len(m.Value) {
|
if len(m.value) > 0 && m.pos < len(m.value) {
|
||||||
m.Value = m.Value[:m.pos] + m.Value[m.pos+1:]
|
m.value = m.value[:m.pos] + m.value[m.pos+1:]
|
||||||
}
|
}
|
||||||
case boba.KeyCtrlE: // ^E, go to end
|
case boba.KeyCtrlE: // ^E, go to end
|
||||||
m.pos = len(m.Value)
|
m.CursorEnd()
|
||||||
case boba.KeyCtrlK: // ^K, kill text after cursor
|
case boba.KeyCtrlK: // ^K, kill text after cursor
|
||||||
m.Value = m.Value[:m.pos]
|
m.value = m.value[:m.pos]
|
||||||
m.pos = len(m.Value)
|
m.pos = len(m.value)
|
||||||
case boba.KeyCtrlU: // ^U, kill text before cursor
|
case boba.KeyCtrlU: // ^U, kill text before cursor
|
||||||
m.Value = m.Value[m.pos:]
|
m.value = m.value[m.pos:]
|
||||||
m.pos = 0
|
m.pos = 0
|
||||||
m.offset = 0
|
m.offset = 0
|
||||||
case boba.KeyRune: // input a regular character
|
case boba.KeyRune: // input a regular character
|
||||||
if m.CharLimit <= 0 || len(m.Value) < m.CharLimit {
|
if m.CharLimit <= 0 || len(m.value) < m.CharLimit {
|
||||||
m.Value = m.Value[:m.pos] + string(msg.Rune) + m.Value[m.pos:]
|
m.value = m.value[:m.pos] + string(msg.Rune) + m.value[m.pos:]
|
||||||
m.pos++
|
m.pos++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -180,16 +233,7 @@ func Update(msg boba.Msg, m Model) (Model, boba.Cmd) {
|
||||||
return m, Blink(m)
|
return m, Blink(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a max width is defined, perform some logic to treat the visible area
|
m.handleOverflow()
|
||||||
// as a horizontally scrolling mini viewport.
|
|
||||||
if m.Width > 0 {
|
|
||||||
overflow := max(0, len(m.Value)-m.Width)
|
|
||||||
if overflow > 0 && m.pos < m.offset {
|
|
||||||
m.offset = max(0, min(len(m.Value), m.pos))
|
|
||||||
} else if overflow > 0 && m.pos >= m.offset+m.Width {
|
|
||||||
m.offset = max(0, m.pos-m.Width)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
@ -202,18 +246,18 @@ func View(model boba.Model) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Placeholder text
|
// Placeholder text
|
||||||
if m.Value == "" && m.Placeholder != "" {
|
if m.value == "" && m.Placeholder != "" {
|
||||||
return placeholderView(m)
|
return placeholderView(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
left := m.offset
|
left := m.offset
|
||||||
right := 0
|
right := 0
|
||||||
if m.Width > 0 {
|
if m.Width > 0 {
|
||||||
right = min(len(m.Value), m.offset+m.Width+1)
|
right = min(len(m.value), m.offset+m.Width+1)
|
||||||
} else {
|
} else {
|
||||||
right = len(m.Value)
|
right = len(m.value)
|
||||||
}
|
}
|
||||||
value := m.Value[left:right]
|
value := m.value[left:right]
|
||||||
pos := m.pos - m.offset
|
pos := m.pos - m.offset
|
||||||
|
|
||||||
v := m.colorText(value[:pos])
|
v := m.colorText(value[:pos])
|
||||||
|
|
Loading…
Reference in New Issue