Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions gui.go
Original file line number Diff line number Diff line change
Expand Up @@ -327,8 +327,9 @@ func (g *Gui) SetView(name string, x0, y0, x1, y1 int, overlaps byte) (*View, er

if v.Editable {
cursorX, cursorY := v.TextArea.GetCursorXY()
newViewCursorX, newOriginX := updatedCursorAndOrigin(0, v.InnerWidth(), cursorX)
newViewCursorY, newOriginY := updatedCursorAndOrigin(0, v.InnerHeight(), cursorY)
contentX, contentY := v.TextArea.GetContentDimensions()
newViewCursorX, newOriginX := updatedCursorAndOrigin(0, v.InnerWidth(), cursorX, contentX)
newViewCursorY, newOriginY := updatedCursorAndOrigin(0, v.InnerHeight(), cursorY, contentY)

v.SetCursor(newViewCursorX, newViewCursorY)
v.SetOrigin(newOriginX, newOriginY)
Expand Down
25 changes: 25 additions & 0 deletions text_area.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ type TextArea struct {
clipboard string
AutoWrap bool
AutoWrapWidth int
maxContentX int // cached max x coordinate of content (rightmost position)
maxContentY int // cached max y coordinate of content (line count - 1)
}

func stringToTextAreaCells(str string) []TextAreaCell {
Expand Down Expand Up @@ -248,6 +250,29 @@ func (self *TextArea) updateCells() {
}

self.cells, _ = contentToCells(self.content, width)
self.computeMaxDimensions()
}

func (self *TextArea) computeMaxDimensions() {
self.maxContentX = 0
self.maxContentY = 0
for _, cell := range self.cells {
if cell.y > self.maxContentY {
self.maxContentY = cell.y
}
rightEdge := cell.x + cell.width
if rightEdge > self.maxContentX {
self.maxContentX = rightEdge
}
}
// If content ends with a newline, there's a blank line after it
if len(self.cells) > 0 && self.cells[len(self.cells)-1].char == "\n" {
self.maxContentY++
}
}

func (self *TextArea) GetContentDimensions() (int, int) {
return self.maxContentX, self.maxContentY
}

func (self *TextArea) typeCharacter(ch string) {
Expand Down
16 changes: 13 additions & 3 deletions view.go
Original file line number Diff line number Diff line change
Expand Up @@ -1730,15 +1730,16 @@ func (v *View) RenderTextArea() {
cursorX, cursorY := v.TextArea.GetCursorXY()
prevOriginX, prevOriginY := v.Origin()
width, height := v.InnerWidth(), v.InnerHeight()
contentX, contentY := v.TextArea.GetContentDimensions()

newViewCursorX, newOriginX := updatedCursorAndOrigin(prevOriginX, width, cursorX)
newViewCursorY, newOriginY := updatedCursorAndOrigin(prevOriginY, height, cursorY)
newViewCursorX, newOriginX := updatedCursorAndOrigin(prevOriginX, width, cursorX, contentX)
newViewCursorY, newOriginY := updatedCursorAndOrigin(prevOriginY, height, cursorY, contentY)

v.SetCursor(newViewCursorX, newViewCursorY)
v.SetOrigin(newOriginX, newOriginY)
}

func updatedCursorAndOrigin(prevOrigin int, size int, cursor int) (int, int) {
func updatedCursorAndOrigin(prevOrigin int, size int, cursor int, contentLen int) (int, int) {
var newViewCursor int
newOrigin := prevOrigin
usableSize := size - 1
Expand All @@ -1753,6 +1754,15 @@ func updatedCursorAndOrigin(prevOrigin int, size int, cursor int) (int, int) {
newViewCursor = cursor - prevOrigin
}

// Prevent scrolling past the end of content
maxOrigin := contentLen - usableSize
if maxOrigin < 0 {
maxOrigin = 0
}
if newOrigin > maxOrigin {
newOrigin = maxOrigin
}

return newViewCursor, newOrigin
}

Expand Down
36 changes: 27 additions & 9 deletions view_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,22 +101,40 @@ func TestUpdatedCursorAndOrigin(t *testing.T) {
prevOrigin int
size int
cursor int
contentLen int
expectedCursor int
expectedOrigin int
}{
{0, 10, 0, 0, 0},
{0, 10, 9, 9, 0},
{0, 10, 10, 9, 1},
{0, 10, 19, 9, 10},
{0, 10, 20, 9, 11},
{20, 10, 19, 0, 19},
{20, 10, 25, 5, 20},
// Original test cases with very long content (100 chars)
{0, 10, 0, 100, 0, 0},
{0, 10, 9, 100, 9, 0},
{0, 10, 10, 100, 9, 1},
{0, 10, 19, 100, 9, 10},
{0, 10, 20, 100, 9, 11},
{20, 10, 19, 100, 0, 19},
{20, 10, 25, 100, 5, 20},
// content shorter than view should clamp origin to 0
{5, 10, 5, 8, 0, 0},
// content equal to usable size should allow origin at most 0
{0, 10, 8, 9, 8, 0},
// content just longer than usable size (10 content, size 10, usableSize 9)
{0, 10, 9, 10, 9, 0}, // cursor at last char, should be visible at origin 0
{0, 10, 10, 10, 9, 1}, // cursor past end gets clamped, but so does origin to max 1
{1, 10, 8, 10, 7, 1}, // already scrolled, cursor moves left
{1, 10, 9, 10, 8, 1}, // cursor at last position of content, origin stays at 1
// content exactly fits after some scrolling
{5, 10, 5, 14, 0, 5}, // contentLen=14, usableSize=9, maxOrigin=5, cursor at origin
{5, 10, 9, 14, 4, 5}, // contentLen=14, cursor at 9, can show 5-13, origin stays at 5
// cursor at end of just-longer content should show last chars
{0, 10, 20, 20, 9, 11}, // contentLen=20, cursor at 20, maxOrigin=11, shows 11-19
// scrolled past middle, content shrinks, should clamp origin
{7, 10, 5, 15, 0, 5}, // was scrolled to 7, but contentLen=15 allows maxOrigin=6, cursor at 5 means origin=5
}

for _, test := range tests {
cursor, origin := updatedCursorAndOrigin(test.prevOrigin, test.size, test.cursor)
cursor, origin := updatedCursorAndOrigin(test.prevOrigin, test.size, test.cursor, test.contentLen)
assert.EqualValues(t, test.expectedCursor, cursor, "Cursor is wrong")
assert.EqualValues(t, test.expectedOrigin, origin, "Origin in wrong")
assert.EqualValues(t, test.expectedOrigin, origin, "Origin is wrong")
}
}

Expand Down