fix!(textarea): virtual cursor blink

This commit is contained in:
Christian Rocha
2025-05-26 09:15:38 -04:00
parent 8e84f33a49
commit b531de8f1c
+44 -22
View File
@@ -260,17 +260,9 @@ type Model struct {
// KeyMap encodes the keybindings recognized by the widget. // KeyMap encodes the keybindings recognized by the widget.
KeyMap KeyMap KeyMap KeyMap
// Styling. FocusedStyle and BlurredStyle are used to style the textarea in
// focused and blurred states.
Styles Styles
// virtualCursor manages the virtual cursor. // virtualCursor manages the virtual cursor.
virtualCursor cursor.Model virtualCursor cursor.Model
// VirtualCursor determines whether or not to use the virtual cursor. If
// set to false, use [Model.Cursor] to return a real cursor for rendering.
VirtualCursor bool
// CharLimit is the maximum number of characters this input element will // CharLimit is the maximum number of characters this input element will
// accept. If 0 or less, there's no limit. // accept. If 0 or less, there's no limit.
CharLimit int CharLimit int
@@ -283,6 +275,15 @@ type Model struct {
// there's no limit. // there's no limit.
MaxWidth int MaxWidth int
// Styling. Styles are defined in [Styles]. Use [SetStyles] and [GetStyles]
// to work with this value publicly.
styles Styles
// useVirtualCursor determines whether or not to use the virtual cursor.
// Use [SetVirtualCursor] and [VirtualCursor] to work with this this
// value publicly.
useVirtualCursor bool
// If promptFunc is set, it replaces Prompt as a generator for // If promptFunc is set, it replaces Prompt as a generator for
// prompt strings at the beginning of each line. // prompt strings at the beginning of each line.
promptFunc func(line int) string promptFunc func(line int) string
@@ -337,11 +338,11 @@ func New() Model {
MaxHeight: defaultMaxHeight, MaxHeight: defaultMaxHeight,
MaxWidth: defaultMaxWidth, MaxWidth: defaultMaxWidth,
Prompt: lipgloss.ThickBorder().Left + " ", Prompt: lipgloss.ThickBorder().Left + " ",
Styles: styles, styles: styles,
cache: memoization.NewMemoCache[line, [][]rune](maxLines), cache: memoization.NewMemoCache[line, [][]rune](maxLines),
EndOfBufferCharacter: ' ', EndOfBufferCharacter: ' ',
ShowLineNumbers: true, ShowLineNumbers: true,
VirtualCursor: true, useVirtualCursor: true,
virtualCursor: cur, virtualCursor: cur,
KeyMap: DefaultKeyMap(), KeyMap: DefaultKeyMap(),
@@ -403,21 +404,43 @@ func DefaultDarkStyles() Styles {
return DefaultStyles(true) return DefaultStyles(true)
} }
// Styles returns the current styles for the textarea.
func (m Model) Styles() Styles {
return m.styles
}
// SetStyles updates styling for the textarea.
func (m *Model) SetStyles(s Styles) {
m.styles = s
m.updateVirtualCursorStyle()
}
// VirtualCursor returns whether or not the virtual cursor is enabled.
func (m Model) VirtualCursor() bool {
return m.useVirtualCursor
}
// SetVirtualCursor sets whether or not to use the virtual cursor.
func (m *Model) SetVirtualCursor(v bool) {
m.useVirtualCursor = v
m.updateVirtualCursorStyle()
}
// updateVirtualCursorStyle sets styling on the virtual cursor based on the // updateVirtualCursorStyle sets styling on the virtual cursor based on the
// textarea's style settings. // textarea's style settings.
func (m *Model) updateVirtualCursorStyle() { func (m *Model) updateVirtualCursorStyle() {
if !m.VirtualCursor { if !m.useVirtualCursor {
m.virtualCursor.SetMode(cursor.CursorHide) m.virtualCursor.SetMode(cursor.CursorHide)
return return
} }
m.virtualCursor.Style = lipgloss.NewStyle().Foreground(m.Styles.Cursor.Color) m.virtualCursor.Style = lipgloss.NewStyle().Foreground(m.styles.Cursor.Color)
// By default, the blink speed of the cursor is set to a default // By default, the blink speed of the cursor is set to a default
// internally. // internally.
if m.Styles.Cursor.Blink { if m.styles.Cursor.Blink {
if m.Styles.Cursor.BlinkSpeed > 0 { if m.styles.Cursor.BlinkSpeed > 0 {
m.virtualCursor.BlinkSpeed = m.Styles.Cursor.BlinkSpeed m.virtualCursor.BlinkSpeed = m.styles.Cursor.BlinkSpeed
} }
m.virtualCursor.SetMode(cursor.CursorBlink) m.virtualCursor.SetMode(cursor.CursorBlink)
return return
@@ -663,9 +686,9 @@ func (m Model) Focused() bool {
// whether the textarea is focused or blurred. // whether the textarea is focused or blurred.
func (m Model) activeStyle() *StyleState { func (m Model) activeStyle() *StyleState {
if m.focus { if m.focus {
return &m.Styles.Focused return &m.styles.Focused
} }
return &m.Styles.Blurred return &m.styles.Blurred
} }
// Focus sets the focus state on the model. When the model is in focus it can // Focus sets the focus state on the model. When the model is in focus it can
@@ -1188,7 +1211,6 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
// View renders the text area in its current state. // View renders the text area in its current state.
func (m Model) View() string { func (m Model) View() string {
m.updateVirtualCursorStyle()
if m.Value() == "" && m.row == 0 && m.col == 0 && m.Placeholder != "" { if m.Value() == "" && m.row == 0 && m.col == 0 && m.Placeholder != "" {
return m.placeholderView() return m.placeholderView()
} }
@@ -1431,7 +1453,7 @@ func Blink() tea.Msg {
// f.Cursor.Position.X += offsetX // f.Cursor.Position.X += offsetX
// f.Cursor.Position.Y += offsetY // f.Cursor.Position.Y += offsetY
func (m Model) Cursor() *tea.Cursor { func (m Model) Cursor() *tea.Cursor {
if m.VirtualCursor { if m.useVirtualCursor {
return nil return nil
} }
@@ -1453,9 +1475,9 @@ func (m Model) Cursor() *tea.Cursor {
baseStyle.GetBorderTopSize() baseStyle.GetBorderTopSize()
c := tea.NewCursor(xOffset, yOffset) c := tea.NewCursor(xOffset, yOffset)
c.Blink = m.Styles.Cursor.Blink c.Blink = m.styles.Cursor.Blink
c.Color = m.Styles.Cursor.Color c.Color = m.styles.Cursor.Color
c.Shape = m.Styles.Cursor.Shape c.Shape = m.styles.Cursor.Shape
return c return c
} }