Route all view origin writes through SetOriginX and SetOriginY

Several methods assigned v.ox and v.oy directly: SetOrigin, CopyContent,
the wrap/autoscroll branches in draw, FocusPoint, and
Scroll{Up,Down,Left,Right}. Funnelling them all through SetOriginX and
SetOriginY gives a single place to observe (or set a breakpoint on)
every change to a view's scroll position, which makes debugging scroll
behaviour much easier.

This means those call sites now also get the setters' `< 0` clamps, but
that is behaviour-preserving in every case: each assigned value is
already >= 0. calculateNewOrigin never returns a negative number;
CopyContent copies origins that are themselves always >= 0; and the draw
and scroll writes are all guarded (or fed only non-negative amounts) so
the result can't go below zero.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Stefan Haller 2026-08-08 18:57:12 +02:00
parent 01600042cd
commit 94018de3e8

View file

@ -402,7 +402,7 @@ func (v *View) FocusPoint(cx int, cy int, scrollIntoView bool) {
if scrollIntoView {
height := v.InnerHeight()
v.oy = calculateNewOrigin(cy, v.oy, lineCount, height)
v.SetOriginY(calculateNewOrigin(cy, v.oy, lineCount, height))
}
v.cx = cx
@ -707,15 +707,8 @@ func (v *View) CursorY() int {
// implement Horizontal and Vertical scrolling with just incrementing
// or decrementing ox and oy.
func (v *View) SetOrigin(x, y int) {
if x < 0 {
x = 0
}
if y < 0 {
y = 0
}
v.ox = x
v.oy = y
v.SetOriginX(x)
v.SetOriginY(y)
}
func (v *View) SetOriginX(x int) {
@ -1166,8 +1159,8 @@ func (v *View) CopyContent(from *View) {
// their contents.
v.lines = slices.Clone(from.lines)
v.viewLines = slices.Clone(from.viewLines)
v.ox = from.ox
v.oy = from.oy
v.SetOriginX(from.ox)
v.SetOriginY(from.oy)
v.cx = from.cx
v.cy = from.cy
}
@ -1335,14 +1328,14 @@ func (v *View) draw(isWindowFocused bool) {
if maxX == 0 {
return
}
v.ox = 0
v.SetOriginX(0)
}
v.refreshViewLinesIfNeeded()
visibleViewLinesHeight := v.viewLineLengthIgnoringTrailingBlankLines()
if v.Autoscroll && visibleViewLinesHeight > maxY {
v.oy = visibleViewLinesHeight - maxY
v.SetOriginY(visibleViewLinesHeight - maxY)
}
if len(v.viewLines) == 0 {
@ -1989,7 +1982,7 @@ func (v *View) ScrollUp(amount int) {
}
if amount != 0 {
v.oy -= amount
v.SetOriginY(v.oy - amount)
v.cy += amount
v.clearHover()
@ -2001,7 +1994,7 @@ func (v *View) ScrollUp(amount int) {
func (v *View) ScrollDown(amount int) {
adjustedAmount := v.adjustDownwardScrollAmount(amount)
if adjustedAmount > 0 {
v.oy += adjustedAmount
v.SetOriginY(v.oy + adjustedAmount)
v.cy -= adjustedAmount
v.clearHover()
@ -2015,7 +2008,7 @@ func (v *View) ScrollLeft(amount int) {
newOx = 0
}
if newOx != v.ox {
v.ox = newOx
v.SetOriginX(newOx)
v.clearHover()
}
@ -2023,7 +2016,7 @@ func (v *View) ScrollLeft(amount int) {
// not applying any limits to this
func (v *View) ScrollRight(amount int) {
v.ox += amount
v.SetOriginX(v.ox + amount)
v.clearHover()
}