From 2d58d49d8af0d7f7cca84c9b47000e27d6506257 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Sun, 18 Aug 2024 14:36:11 +0200 Subject: [PATCH 1/5] Extract function moveLeftFromSoftLineBreak --- text_area.go | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/text_area.go b/text_area.go index ebd6a6bf..133caaa1 100644 --- a/text_area.go +++ b/text_area.go @@ -282,13 +282,7 @@ func (self *TextArea) GoToEndOfLine() { self.cursor = self.closestNewlineOnRight() - // If the end of line is a soft line break, we need to move left by one so - // that we end up at the last whitespace before the line break. Otherwise - // we'd be at the start of the next line, since the newline character - // doesn't really exist in the real content. - if self.cursor < len(self.content) && self.content[self.cursor] != '\n' { - self.cursor-- - } + self.moveLeftFromSoftLineBreak() } func (self *TextArea) closestNewlineOnRight() int { @@ -303,6 +297,16 @@ func (self *TextArea) closestNewlineOnRight() int { return len(self.content) } +func (self *TextArea) moveLeftFromSoftLineBreak() { + // If the end of line is a soft line break, we need to move left by one so + // that we end up at the last whitespace before the line break. Otherwise + // we'd be at the start of the next line, since the newline character + // doesn't really exist in the real content. + if self.cursor < len(self.content) && self.content[self.cursor] != '\n' { + self.cursor-- + } +} + func (self *TextArea) atLineStart() bool { return self.cursor == 0 || (len(self.content) > self.cursor-1 && self.content[self.cursor-1] == '\n') From 903d1f33b2dd55142fe5cf548cb08dc00df8b7f1 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Sun, 18 Aug 2024 13:24:16 +0200 Subject: [PATCH 2/5] Fix SetCursor2D at soft line break Similar to the code in GoToEndOfLine, we need to move left by one character when setting the cursor position to a soft line break, so that we end up at the last whitespace before the line break. This fixes pressing up-arrow or down-arrow at the end of a line when jumping to an auto-wrapped line that is shorter. --- text_area.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/text_area.go b/text_area.go index 133caaa1..7291b870 100644 --- a/text_area.go +++ b/text_area.go @@ -424,12 +424,16 @@ func (self *TextArea) SetCursor2D(x int, y int) { for _, r := range self.wrappedContent { if x <= 0 && y == 0 { self.cursor = self.wrappedCursorToOrigCursor(newCursor) + if self.wrappedContent[newCursor] == '\n' { + self.moveLeftFromSoftLineBreak() + } return } if r == '\n' { if y == 0 { self.cursor = self.wrappedCursorToOrigCursor(newCursor) + self.moveLeftFromSoftLineBreak() return } y-- From 2511d5d82f1d08097dad51aab5e343524efa5cec Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Wed, 14 Aug 2024 12:35:35 +0200 Subject: [PATCH 3/5] Fix logic of clicking past last character The logic didn't take scrolling into account; also, not only do we need to constrain the x position to the line length, but also the y position to the number of lines. --- gui.go | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/gui.go b/gui.go index 7ede647a..0af9f18c 100644 --- a/gui.go +++ b/gui.go @@ -1327,13 +1327,26 @@ func (g *Gui) onKey(ev *GocuiEvent) error { } } + // newCx and newCy are relative to the view port, i.e. to the visible area of the view newCx := mx - v.x0 - 1 newCy := my - v.y0 - 1 - // if view is editable don't go further than the furthest character for that line - if v.Editable && newCy >= 0 && newCy <= len(v.lines)-1 { - lastCharForLine := len(v.lines[newCy]) - if lastCharForLine < newCx { - newCx = lastCharForLine + // newX and newY are relative to the view's content, independent of its scroll position + newX := newCx + v.ox + newY := newCy + v.oy + // if view is editable don't go further than the furthest character for that line + if v.Editable { + if newY < 0 { + newY = 0 + newCy = -v.oy + } else if newY >= len(v.lines) { + newY = len(v.lines) - 1 + newCy = newY - v.oy + } + + lastCharForLine := len(v.lines[newY]) + if lastCharForLine < newX { + newX = lastCharForLine + newCx = lastCharForLine - v.ox } } if !IsMouseScrollKey(ev.Key) { @@ -1343,7 +1356,7 @@ func (g *Gui) onKey(ev *GocuiEvent) error { } if IsMouseKey(ev.Key) { - opts := ViewMouseBindingOpts{X: newCx + v.ox, Y: newCy + v.oy} + opts := ViewMouseBindingOpts{X: newX, Y: newY} matched, err := g.execMouseKeybindings(v, ev, opts) if err != nil { return err From 0c4bf775e6810730db9bb0e53a411944f015054b Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Sun, 18 Aug 2024 12:20:11 +0200 Subject: [PATCH 4/5] Skip over \0 characters at end of line We insert \0 cells at the end of lines, presumably to properly support turning off styles at line ends. When clicking into an editable view after the end of a line, we need to skip past these. --- gui.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gui.go b/gui.go index 0af9f18c..e5d7e603 100644 --- a/gui.go +++ b/gui.go @@ -1344,6 +1344,9 @@ func (g *Gui) onKey(ev *GocuiEvent) error { } lastCharForLine := len(v.lines[newY]) + for lastCharForLine > 0 && v.lines[newY][lastCharForLine-1].chr == 0 { + lastCharForLine-- + } if lastCharForLine < newX { newX = lastCharForLine newCx = lastCharForLine - v.ox From 2845e65d96dea785a77c5fd13db5728f15ee5c46 Mon Sep 17 00:00:00 2001 From: Stefan Haller Date: Wed, 14 Aug 2024 12:38:50 +0200 Subject: [PATCH 5/5] When clicking in an editable view, set the cursor v.SetCursor doesn't do this, we need to call SetCursor2D on the TextArea in addition. --- gui.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/gui.go b/gui.go index e5d7e603..c1ee93ce 100644 --- a/gui.go +++ b/gui.go @@ -1356,6 +1356,15 @@ func (g *Gui) onKey(ev *GocuiEvent) error { if err := v.SetCursor(newCx, newCy); err != nil { return err } + if v.Editable { + v.TextArea.SetCursor2D(newX, newY) + + // SetCursor2D might have adjusted the text area's cursor to the + // left to move left from a soft line break, so we need to + // update the view's cursor to match the text area's cursor. + cX, _ := v.TextArea.GetCursorXY() + v.SetCursorX(cX) + } } if IsMouseKey(ev.Key) {