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,60 +457,84 @@ 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
if yDist < minYDist { type lineGroup struct {
minYDist = yDist y float64
bestLineY = yVal indices []int
}
groups := []lineGroup{}
seenY := make(map[float64]int) // maps Y to group index
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:])
TheState.Editor.CursorPosition = start + size if r == '\n' {
TheState.Editor.CursorPosition = start
} else {
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 }
} // Calculate distance to the glyph center
// Calculate distance to the glyph center glyphCenterX := float64(layout.X[i] + layout.Advance[i]/2)
glyphCenterX := float64(layout.X[i] + layout.Advance[i]/2) dist := glyphCenterX - x
dist := glyphCenterX - x if dist < 0 {
if dist < 0 { dist = -dist
dist = -dist }
} if dist < minDist {
if dist < minDist { minDist = dist
minDist = dist bestIdx = i
bestIdx = i
}
} }
} }
if bestIdx != -1 { if bestIdx != -1 {