Fix cursor Y positioning logic in editor

This commit is contained in:
Greg Pomerantz 2026-06-03 22:47:06 -04:00
parent 6939550527
commit 979656c2d0

View File

@ -457,47 +457,72 @@ func SetCursorFromPoint(x, y float64) {
return return
} }
// 1. Find the best line based on y lineHeight := float64(EditorLineHeight())
bestLineY := layout.Y[0]
minYDist := 1e9 // 1. Identify the intended line index based on y
for _, yVal := range layout.Y { // layout.Y values are relative to the text region origin.
yDist := float64(yVal) - y // We need to account for scroll offset.
if yDist < 0 { visualLine := int((y + float64(TheState.ScrollOffset)) / lineHeight)
yDist = -yDist
// Group glyphs by their Y-baseline
type lineGroup struct {
y float64
indices []int
} }
if yDist < minYDist { groups := []lineGroup{}
minYDist = yDist seenY := make(map[float64]int) // maps Y to group index
bestLineY = yVal
for i, yVal := range layout.Y {
yFloat := float64(yVal)
idx, ok := seenY[yFloat]
if !ok {
idx = len(groups)
groups = append(groups, lineGroup{y: yFloat, indices: []int{}})
seenY[yFloat] = idx
} }
groups[idx].indices = append(groups[idx].indices, i)
}
// Sort groups by Y
sort.Slice(groups, func(i, j int) bool { return groups[i].y < groups[j].y })
// If visualLine is out of bounds, clamp
if visualLine < 0 {
visualLine = 0
}
if visualLine >= len(groups) {
visualLine = len(groups) - 1
} }
// 2. Identify glyphs on this line and find the rightmost extent targetGroup := groups[visualLine]
// 3. Identify rightmost extent on this line
rightmostX := 0.0 rightmostX := 0.0
rightmostIdx := -1 rightmostIdx := -1
for i, yVal := range layout.Y { for _, i := range targetGroup.indices {
if yVal == bestLineY {
xEnd := float64(layout.X[i] + layout.Advance[i]) xEnd := float64(layout.X[i] + layout.Advance[i])
if xEnd > rightmostX { if xEnd > rightmostX {
rightmostX = xEnd rightmostX = xEnd
rightmostIdx = i rightmostIdx = i
} }
} }
}
// 3. Check if tap is to the right of the last character on this line // 4. Check if tap is to the right of the last character
if rightmostIdx != -1 && x > rightmostX { if rightmostIdx != -1 && x > rightmostX {
// Position after the last character on this line // Position at the end of the line content, before any trailing newline.
start := layout.ByteOffsets[rightmostIdx] start := layout.ByteOffsets[rightmostIdx]
_, size := utf8.DecodeRuneInString(TheState.Editor.Buffer[start:]) r, size := utf8.DecodeRuneInString(TheState.Editor.Buffer[start:])
if r == '\n' {
TheState.Editor.CursorPosition = start
} else {
TheState.Editor.CursorPosition = start + size TheState.Editor.CursorPosition = start + size
}
return return
} }
// 4. Otherwise, find the closest glyph on this line. // 5. Otherwise, find the closest glyph on this line.
bestIdx := -1 bestIdx := -1
minDist := float64(1e9) minDist := float64(1e9)
for i, yVal := range layout.Y { for _, i := range targetGroup.indices {
if yVal == bestLineY {
if bestIdx == -1 { if bestIdx == -1 {
bestIdx = i bestIdx = i
} }
@ -512,7 +537,6 @@ func SetCursorFromPoint(x, y float64) {
bestIdx = i bestIdx = i
} }
} }
}
if bestIdx != -1 { if bestIdx != -1 {
TheState.Editor.CursorPosition = layout.ByteOffsets[bestIdx] TheState.Editor.CursorPosition = layout.ByteOffsets[bestIdx]
} }