Skip to content

Commit

Permalink
feat: add method for returning a tea.Cursor
Browse files Browse the repository at this point in the history
  • Loading branch information
meowgorithm committed Jan 23, 2025
1 parent 0c83e6f commit 66bfd4e
Showing 1 changed file with 40 additions and 18 deletions.
58 changes: 40 additions & 18 deletions textarea/textarea.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package textarea
import (
"crypto/sha256"
"fmt"
"image"
"strconv"
"strings"
"unicode"
Expand Down Expand Up @@ -231,8 +232,16 @@ type Model struct {
// when switching focus states.
activeStyle *StyleState

// Cursor is the text area cursor.
Cursor cursor.Model
// UseVirtualCursor indidates whether to render the virtual cursor. If false,
// you should use the [Model.Cursor] method to render a real cursor.
//
// For more info on rendering real cursors see:
// XXX: insert URL of Bubble Tea cursor reference.
UseVirtualCursor bool

// VirtualCursor is the virtual cursor.
// XXX: try and make this internal.
VirtualCursor cursor.Model

// CharLimit is the maximum number of characters this input element will
// accept. If 0 or less, there's no limit.
Expand Down Expand Up @@ -305,7 +314,8 @@ func New() Model {
cache: memoization.NewMemoCache[line, [][]rune](maxLines),
EndOfBufferCharacter: ' ',
ShowLineNumbers: true,
Cursor: cur,
UseVirtualCursor: true,
VirtualCursor: cur,
KeyMap: DefaultKeyMap(),

value: make([][]rune, minHeight, maxLines),
Expand All @@ -316,6 +326,10 @@ func New() Model {
viewport: &vp,
}

if m.UseVirtualCursor {
m.VirtualCursor.SetMode(cursor.CursorHide)
}

m.SetHeight(defaultHeight)
m.SetWidth(defaultWidth)

Expand Down Expand Up @@ -600,15 +614,15 @@ func (m Model) Focused() bool {
func (m *Model) Focus() tea.Cmd {
m.focus = true
m.activeStyle = &m.Styles.Focused
return m.Cursor.Focus()
return m.VirtualCursor.Focus()
}

// Blur removes the focus state on the model. When the model is blurred it can
// not receive keyboard input and the cursor will be hidden.
func (m *Model) Blur() {
m.focus = false
m.activeStyle = &m.Styles.Blurred
m.Cursor.Blur()
m.VirtualCursor.Blur()
}

// Reset sets the input to its default state with no input.
Expand Down Expand Up @@ -976,7 +990,7 @@ func (m *Model) SetHeight(h int) {
// Update is the Bubble Tea update loop.
func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
if !m.focus {
m.Cursor.Blur()
m.VirtualCursor.Blur()
return m, nil
}

Expand Down Expand Up @@ -1098,10 +1112,10 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) {
cmds = append(cmds, cmd)

newRow, newCol := m.cursorLineNumber(), m.col
m.Cursor, cmd = m.Cursor.Update(msg)
if (newRow != oldRow || newCol != oldCol) && m.Cursor.Mode() == cursor.CursorBlink {
m.Cursor.Blink = false
cmd = m.Cursor.BlinkCmd()
m.VirtualCursor, cmd = m.VirtualCursor.Update(msg)
if (newRow != oldRow || newCol != oldCol) && m.VirtualCursor.Mode() == cursor.CursorBlink {
m.VirtualCursor.Blink = false
cmd = m.VirtualCursor.BlinkCmd()
}
cmds = append(cmds, cmd)

Expand All @@ -1115,7 +1129,7 @@ func (m Model) View() string {
if m.Value() == "" && m.row == 0 && m.col == 0 && m.Placeholder != "" {
return m.placeholderView()
}
m.Cursor.TextStyle = m.activeStyle.computedCursorLine()
m.VirtualCursor.TextStyle = m.activeStyle.computedCursorLine()

var (
s strings.Builder
Expand Down Expand Up @@ -1184,11 +1198,11 @@ func (m Model) View() string {
if m.row == l && lineInfo.RowOffset == wl {
s.WriteString(style.Render(string(wrappedLine[:lineInfo.ColumnOffset])))
if m.col >= len(line) && lineInfo.CharOffset >= m.width {
m.Cursor.SetChar(" ")
s.WriteString(m.Cursor.View())
m.VirtualCursor.SetChar(" ")
s.WriteString(m.VirtualCursor.View())
} else {
m.Cursor.SetChar(string(wrappedLine[lineInfo.ColumnOffset]))
s.WriteString(style.Render(m.Cursor.View()))
m.VirtualCursor.SetChar(string(wrappedLine[lineInfo.ColumnOffset]))
s.WriteString(style.Render(m.VirtualCursor.View()))
s.WriteString(style.Render(string(wrappedLine[lineInfo.ColumnOffset+1:])))
}
} else {
Expand Down Expand Up @@ -1220,6 +1234,14 @@ func (m Model) View() string {
return m.activeStyle.Base.Render(m.viewport.View())
}

// Cursor returns cursor properties. It's intended to be used by the
// tea.ViewMetaModel to render a real cursor.
func (m Model) Cursor() tea.Cursor {

Check failure on line 1239 in textarea/textarea.go

View workflow job for this annotation

GitHub Actions / test (~1.18, ubuntu-latest)

undefined: tea.Cursor

Check failure on line 1239 in textarea/textarea.go

View workflow job for this annotation

GitHub Actions / coverage (^1.18, ubuntu-latest)

undefined: tea.Cursor
return tea.Cursor{

Check failure on line 1240 in textarea/textarea.go

View workflow job for this annotation

GitHub Actions / test (~1.18, ubuntu-latest)

undefined: tea.Cursor

Check failure on line 1240 in textarea/textarea.go

View workflow job for this annotation

GitHub Actions / coverage (^1.18, ubuntu-latest)

undefined: tea.Cursor
Position: image.Point{X: m.col, Y: m.row},
}
}

// formatLineNumber formats the line number for display dynamically based on
// the maximum number of lines.
func (m Model) formatLineNumber(x any) string {
Expand Down Expand Up @@ -1291,9 +1313,9 @@ func (m Model) placeholderView() string {
// first line
case i == 0:
// first character of first line as cursor with character
m.Cursor.TextStyle = m.activeStyle.computedPlaceholder()
m.Cursor.SetChar(string(plines[0][0]))
s.WriteString(lineStyle.Render(m.Cursor.View()))
m.VirtualCursor.TextStyle = m.activeStyle.computedPlaceholder()
m.VirtualCursor.SetChar(string(plines[0][0]))
s.WriteString(lineStyle.Render(m.VirtualCursor.View()))

// the rest of the first line
s.WriteString(lineStyle.Render(style.Render(plines[0][1:] + strings.Repeat(" ", max(0, m.width-uniseg.StringWidth(plines[0]))))))
Expand Down

0 comments on commit 66bfd4e

Please sign in to comment.