chore(viewport): make horizontal scroll API better match vertical scroll API

This commit is contained in:
Christian Rocha
2025-01-06 16:45:46 -05:00
parent c9d33f9296
commit 4acc392e42
2 changed files with 54 additions and 50 deletions
+24 -22
View File
@@ -39,6 +39,13 @@ type Model struct {
// YOffset is the vertical scroll position.
YOffset int
// xOffset is the horizontal scroll position.
xOffset int
// horizontalStep is the number of columns we move left or right during a
// default horizontal scroll.
horizontalStep int
// YPosition is the position of the viewport in relation to the terminal
// window. It's used in high performance rendering only.
YPosition int
@@ -59,10 +66,6 @@ type Model struct {
// Deprecated: high performance rendering is now deprecated in Bubble Tea.
HighPerformanceRendering bool
// horizontal step represents the step of indent we add with one move left or right.
horizontalStep int
indent int
initialized bool
lines []string
longestLineWidth int
@@ -138,8 +141,8 @@ func (m Model) visibleLines() (lines []string) {
cutLines := make([]string, len(lines))
for i, line := range lines {
if m.indent > 0 {
line = ansi.TruncateLeft(line, m.indent, "")
if m.xOffset > 0 {
line = ansi.TruncateLeft(line, m.xOffset, "")
}
line = ansi.Truncate(line, m.Width, "")
cutLines[i] = line
@@ -311,9 +314,9 @@ func ViewUp(m Model, lines []string) tea.Cmd {
return tea.ScrollUp(lines, top, bottom)
}
// SetHorizontalStep is a setter for `horizontalStep`.
// Must be set before `MoveLeft` or `MoveRight` is used.
// If 0 or negative, left/right movement doesn't work.
// SetHorizontalStep sets the amount of cells that the viewport moves in the
// default viewport keymapping. If set to 0 or less, horizontal scrolling is
// disabled.
func (m *Model) SetHorizontalStep(n int) {
if n < 0 {
n = 0
@@ -322,27 +325,26 @@ func (m *Model) SetHorizontalStep(n int) {
m.horizontalStep = n
}
// MoveLeft moves all lines to set runes left.
// If current indent is 0, it doesn't work.
func (m *Model) MoveLeft() {
m.indent -= m.horizontalStep
if m.indent < 0 {
m.indent = 0
// MoveLeft moves the viewport to the left by the given number of columns.
func (m *Model) MoveLeft(cols int) {
m.xOffset -= cols
if m.xOffset < 0 {
m.xOffset = 0
}
}
// MoveRight moves all lines to set runes right.
func (m *Model) MoveRight() {
// MoveRight moves viewport to the right by the given number of columns.
func (m *Model) MoveRight(cols int) {
// prevents over scrolling to the right
if m.indent >= m.longestLineWidth-m.Width {
if m.xOffset >= m.longestLineWidth-m.Width {
return
}
m.indent += m.horizontalStep
m.xOffset += cols
}
// Resets lines indent to zero.
func (m *Model) ResetIndent() {
m.indent = 0
m.xOffset = 0
}
// Update handles standard message-based viewport updates.
@@ -401,10 +403,10 @@ func (m Model) updateAsModel(msg tea.Msg) (Model, tea.Cmd) {
}
case key.Matches(msg, m.KeyMap.Left):
m.MoveLeft()
m.MoveLeft(m.horizontalStep)
case key.Matches(msg, m.KeyMap.Right):
m.MoveRight()
m.MoveRight(m.horizontalStep)
}
case tea.MouseMsg:
+30 -28
View File
@@ -91,28 +91,28 @@ func TestMoveLeft(t *testing.T) {
t.Parallel()
m := New(10, 10)
if m.indent != zeroPosition {
t.Errorf("default indent should be %d, got %d", zeroPosition, m.indent)
if m.xOffset != zeroPosition {
t.Errorf("default indent should be %d, got %d", zeroPosition, m.xOffset)
}
m.MoveLeft()
if m.indent != zeroPosition {
t.Errorf("indent should be %d, got %d", zeroPosition, m.indent)
m.MoveLeft(m.horizontalStep)
if m.xOffset != zeroPosition {
t.Errorf("indent should be %d, got %d", zeroPosition, m.xOffset)
}
})
t.Run("move", func(t *testing.T) {
t.Parallel()
m := New(10, 10)
if m.indent != zeroPosition {
t.Errorf("default indent should be %d, got %d", zeroPosition, m.indent)
if m.xOffset != zeroPosition {
t.Errorf("default indent should be %d, got %d", zeroPosition, m.xOffset)
}
m.indent = defaultHorizontalStep * 2
m.MoveLeft()
m.xOffset = defaultHorizontalStep * 2
m.MoveLeft(m.horizontalStep)
newIndent := defaultHorizontalStep
if m.indent != newIndent {
t.Errorf("indent should be %d, got %d", newIndent, m.indent)
if m.xOffset != newIndent {
t.Errorf("indent should be %d, got %d", newIndent, m.xOffset)
}
})
}
@@ -127,14 +127,14 @@ func TestMoveRight(t *testing.T) {
m := New(10, 10)
m.SetContent("Some line that is longer than width")
if m.indent != zeroPosition {
t.Errorf("default indent should be %d, got %d", zeroPosition, m.indent)
if m.xOffset != zeroPosition {
t.Errorf("default indent should be %d, got %d", zeroPosition, m.xOffset)
}
m.MoveRight()
m.MoveRight(m.horizontalStep)
newIndent := defaultHorizontalStep
if m.indent != newIndent {
t.Errorf("indent should be %d, got %d", newIndent, m.indent)
if m.xOffset != newIndent {
t.Errorf("indent should be %d, got %d", newIndent, m.xOffset)
}
})
}
@@ -148,11 +148,11 @@ func TestResetIndent(t *testing.T) {
zeroPosition := 0
m := New(10, 10)
m.indent = 500
m.xOffset = 500
m.ResetIndent()
if m.indent != zeroPosition {
t.Errorf("indent should be %d, got %d", zeroPosition, m.indent)
if m.xOffset != zeroPosition {
t.Errorf("indent should be %d, got %d", zeroPosition, m.xOffset)
}
})
}
@@ -196,7 +196,7 @@ func TestVisibleLines(t *testing.T) {
m := New(10, 10)
list := m.visibleLines()
m.indent = 5
m.xOffset = 5
if len(list) != 0 {
t.Errorf("list should be empty, got %d", len(list))
@@ -274,10 +274,10 @@ func TestVisibleLines(t *testing.T) {
}
// move right
m.MoveRight()
m.MoveRight(m.horizontalStep)
list = m.visibleLines()
newPrefix := perceptPrefix[m.indent:]
newPrefix := perceptPrefix[m.xOffset:]
if !strings.HasPrefix(list[0], newPrefix) {
t.Errorf("first list item has to have prefix %s, get %s", newPrefix, list[0])
}
@@ -287,7 +287,7 @@ func TestVisibleLines(t *testing.T) {
}
// move left
m.MoveLeft()
m.MoveLeft(m.horizontalStep)
list = m.visibleLines()
if !strings.HasPrefix(list[0], perceptPrefix) {
t.Errorf("first list item has to have prefix %s", perceptPrefix)
@@ -301,6 +301,8 @@ func TestVisibleLines(t *testing.T) {
t.Run("list: with 2 cells symbols: horizontal scroll", func(t *testing.T) {
t.Parallel()
const horizontalStep = 5
initList := []string{
"あいうえお",
"Aあいうえお",
@@ -327,7 +329,7 @@ func TestVisibleLines(t *testing.T) {
}
// move right
m.MoveRight()
m.MoveRight(horizontalStep)
list = m.visibleLines()
for i := range list {
@@ -338,7 +340,7 @@ func TestVisibleLines(t *testing.T) {
}
// move left
m.MoveLeft()
m.MoveLeft(horizontalStep)
list = m.visibleLines()
for i := range list {
if list[i] != initList[i] {
@@ -347,8 +349,8 @@ func TestVisibleLines(t *testing.T) {
}
// move left second times do not change lites if indent == 0
m.indent = 0
m.MoveLeft()
m.xOffset = 0
m.MoveLeft(horizontalStep)
list = m.visibleLines()
for i := range list {
if list[i] != initList[i] {
@@ -368,7 +370,7 @@ func TestRightOverscroll(t *testing.T) {
m.SetContent(content)
for i := 0; i < 10; i++ {
m.MoveRight()
m.MoveRight(m.horizontalStep)
}
visibleLines := m.visibleLines()