chore(textarea): improve cursor styling API

This commit is contained in:
Christian Rocha
2025-01-31 16:21:17 -05:00
parent 0e5ff3c5c3
commit eb985a2f81
+27 -34
View File
@@ -154,9 +154,9 @@ type CursorStyle struct {
// CursorBlink determines whether or not the cursor should blink. // CursorBlink determines whether or not the cursor should blink.
Blink bool Blink bool
// BlinkSpeed is the speed at which the virtualcursor blinks. This has no // BlinkSpeed is the speed at which the virtual cursor blinks. This has no
// effect on real cursors as well as no effect if the cursor is set not // effect on real cursors as well as no effect if the cursor is set not to
// to [CursorBlink]. // [CursorBlink].
// //
// By default, the blink speed is set to about 500ms. // By default, the blink speed is set to about 500ms.
BlinkSpeed time.Duration BlinkSpeed time.Duration
@@ -168,6 +168,7 @@ type CursorStyle struct {
type Styles struct { type Styles struct {
Focused StyleState Focused StyleState
Blurred StyleState Blurred StyleState
Cursor CursorStyle
} }
// StyleState that will be applied to the text area. // StyleState that will be applied to the text area.
@@ -186,7 +187,6 @@ type StyleState struct {
Placeholder lipgloss.Style Placeholder lipgloss.Style
Prompt lipgloss.Style Prompt lipgloss.Style
Text lipgloss.Style Text lipgloss.Style
Cursor CursorStyle
} }
func (s StyleState) computedCursorLine() lipgloss.Style { func (s StyleState) computedCursorLine() lipgloss.Style {
@@ -264,7 +264,7 @@ type Model struct {
// Styling. FocusedStyle and BlurredStyle are used to style the textarea in // Styling. FocusedStyle and BlurredStyle are used to style the textarea in
// focused and blurred states. // focused and blurred states.
styles Styles Styles Styles
// activeStyle is the current styling to use. // activeStyle is the current styling to use.
// It is used to abstract the differences in focus state when styling the // It is used to abstract the differences in focus state when styling the
@@ -345,7 +345,7 @@ func New() Model {
MaxHeight: defaultMaxHeight, MaxHeight: defaultMaxHeight,
MaxWidth: defaultMaxWidth, MaxWidth: defaultMaxWidth,
Prompt: lipgloss.ThickBorder().Left + " ", Prompt: lipgloss.ThickBorder().Left + " ",
styles: styles, Styles: styles,
activeStyle: &styles.Blurred, activeStyle: &styles.Blurred,
cache: memoization.NewMemoCache[line, [][]rune](maxLines), cache: memoization.NewMemoCache[line, [][]rune](maxLines),
EndOfBufferCharacter: ' ', EndOfBufferCharacter: ' ',
@@ -383,9 +383,6 @@ func DefaultStyles(isDark bool) Styles {
Placeholder: lipgloss.NewStyle().Foreground(lipgloss.Color("240")), Placeholder: lipgloss.NewStyle().Foreground(lipgloss.Color("240")),
Prompt: lipgloss.NewStyle().Foreground(lipgloss.Color("7")), Prompt: lipgloss.NewStyle().Foreground(lipgloss.Color("7")),
Text: lipgloss.NewStyle(), Text: lipgloss.NewStyle(),
Cursor: CursorStyle{
Style: lipgloss.NewStyle().Foreground(lipgloss.Color("7")),
},
} }
s.Blurred = StyleState{ s.Blurred = StyleState{
Base: lipgloss.NewStyle(), Base: lipgloss.NewStyle(),
@@ -397,6 +394,10 @@ func DefaultStyles(isDark bool) Styles {
Prompt: lipgloss.NewStyle().Foreground(lipgloss.Color("7")), Prompt: lipgloss.NewStyle().Foreground(lipgloss.Color("7")),
Text: lipgloss.NewStyle().Foreground(lightDark(lipgloss.Color("245"), lipgloss.Color("7"))), Text: lipgloss.NewStyle().Foreground(lightDark(lipgloss.Color("245"), lipgloss.Color("7"))),
} }
s.Cursor = CursorStyle{
Style: lipgloss.NewStyle().Foreground(lipgloss.Color("7")),
Blink: true,
}
return s return s
} }
@@ -410,40 +411,34 @@ func DefaultDarkStyles() Styles {
return DefaultStyles(true) return DefaultStyles(true)
} }
// SetStyles sets the styles for the textarea. // updateVirtualCursorStyle sets styling on the virtual cursor based on the
func (m *Model) SetStyles(s Styles) { // textarea's style settings.
m.styles = s func (m *Model) updateVirtualCursorStyle() {
if m.focus { if !m.VirtualCursor {
m.activeStyle = &m.styles.Focused m.virtualCursor.SetMode(cursor.CursorHide)
} else { return
m.activeStyle = &m.styles.Blurred
} }
m.virtualCursor.Style = s.Focused.Cursor.Style m.virtualCursor.Style = m.Styles.Cursor.Style
m.virtualCursor.BlinkedStyle = s.Focused.Cursor.BlinkedStyle m.virtualCursor.BlinkedStyle = m.Styles.Cursor.BlinkedStyle
// 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 s.Focused.Cursor.BlinkSpeed > 0 { if m.Styles.Cursor.BlinkSpeed > 0 {
m.virtualCursor.BlinkSpeed = m.activeStyle.Cursor.BlinkSpeed m.virtualCursor.BlinkSpeed = m.Styles.Cursor.BlinkSpeed
} }
if !m.VirtualCursor { if !m.VirtualCursor {
m.virtualCursor.SetMode(cursor.CursorHide) m.virtualCursor.SetMode(cursor.CursorHide)
return return
} }
if s.Focused.Cursor.Blink { if m.Styles.Cursor.Blink {
m.virtualCursor.SetMode(cursor.CursorBlink) m.virtualCursor.SetMode(cursor.CursorBlink)
return return
} }
m.virtualCursor.SetMode(cursor.CursorStatic) m.virtualCursor.SetMode(cursor.CursorStatic)
} }
// Styles returns the styles for the textarea.
func (m Model) Styles() Styles {
return m.styles
}
// SetValue sets the value of the text input. // SetValue sets the value of the text input.
func (m *Model) SetValue(s string) { func (m *Model) SetValue(s string) {
m.Reset() m.Reset()
@@ -682,7 +677,7 @@ func (m Model) Focused() bool {
// receive keyboard input and the cursor will be hidden. // receive keyboard input and the cursor will be hidden.
func (m *Model) Focus() tea.Cmd { func (m *Model) Focus() tea.Cmd {
m.focus = true m.focus = true
m.activeStyle = &m.styles.Focused m.activeStyle = &m.Styles.Focused
return m.virtualCursor.Focus() return m.virtualCursor.Focus()
} }
@@ -690,7 +685,7 @@ func (m *Model) Focus() tea.Cmd {
// not receive keyboard input and the cursor will be hidden. // not receive keyboard input and the cursor will be hidden.
func (m *Model) Blur() { func (m *Model) Blur() {
m.focus = false m.focus = false
m.activeStyle = &m.styles.Blurred m.activeStyle = &m.Styles.Blurred
m.virtualCursor.Blur() m.virtualCursor.Blur()
} }
@@ -1195,6 +1190,7 @@ 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()
} }
@@ -1422,16 +1418,13 @@ func Blink() tea.Msg {
// If you're using a real cursor, you should also set [Model.VirtualCursor] to // If you're using a real cursor, you should also set [Model.VirtualCursor] to
// false. // false.
func (m Model) Cursor() *tea.Cursor { func (m Model) Cursor() *tea.Cursor {
if m.VirtualCursor || !m.Focused() {
return nil
}
lineInfo := m.LineInfo() lineInfo := m.LineInfo()
x, y := lineInfo.CharOffset, m.cursorLineNumber()-m.viewport.YOffset x, y := lineInfo.CharOffset, m.cursorLineNumber()-m.viewport.YOffset
c := tea.NewCursor(x, y) c := tea.NewCursor(x, y)
c.Blink = m.styles.Focused.Cursor.Blink c.Blink = m.Styles.Cursor.Blink
c.Color = m.styles.Focused.Cursor.Style.GetForeground() c.Color = m.Styles.Cursor.Style.GetForeground()
c.Shape = m.Styles.Cursor.Shape
return c return c
} }