diff --git a/gui.go b/gui.go index 7ede647a..c1ee93ce 100644 --- a/gui.go +++ b/gui.go @@ -1327,23 +1327,48 @@ 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]) + for lastCharForLine > 0 && v.lines[newY][lastCharForLine-1].chr == 0 { + lastCharForLine-- + } + if lastCharForLine < newX { + newX = lastCharForLine + newCx = lastCharForLine - v.ox } } if !IsMouseScrollKey(ev.Key) { 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) { - 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 diff --git a/text_area.go b/text_area.go index ebd6a6bf..7291b870 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') @@ -420,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--