widget: simplify Editor.offsetToScreenPos

It used to return an iterator function, now it just takes the result
of a previous call.

Signed-off-by: Elias Naur <mail@eliasnaur.com>
This commit is contained in:
Elias Naur
2022-02-04 16:54:14 +01:00
parent d0869ef457
commit 3614782e0d
2 changed files with 37 additions and 47 deletions
+33 -43
View File
@@ -773,62 +773,52 @@ func (e *Editor) CaretCoords() f32.Point {
// offsetToScreenPos2 is a utility function to shortcut the common case of // offsetToScreenPos2 is a utility function to shortcut the common case of
// wanting the positions of exactly two offsets. // wanting the positions of exactly two offsets.
func (e *Editor) offsetToScreenPos2(o1, o2 int) (combinedPos, combinedPos) { func (e *Editor) offsetToScreenPos2(o1, o2 int) (combinedPos, combinedPos) {
cp1, iter := e.offsetToScreenPos(o1) cp1 := e.offsetToScreenPos(combinedPos{}, o1)
return cp1, iter(o2) cp2 := e.offsetToScreenPos(cp1, o2)
return cp1, cp2
} }
// offsetToScreenPos takes an offset into the editor text (e.g. // offsetToScreenPos takes an offset into the editor text (e.g.
// e.caret.end.ofs) and returns a combinedPos that corresponds to its current // e.caret.end.ofs) and returns a combinedPos that corresponds to its current
// screen position, as well as an iterator that lets you get the combinedPos // screen position. Hint is either the zero-value position or the result of
// of a later offset. The offsets given to offsetToScreenPos and to the // a previous call with a lower offset.
// returned iterator must be sorted, lowest first, and they must be valid (0
// <= offset <= e.Len()).
// //
// This function is written this way to take advantage of previous work done // This function is written this way to take advantage of previous work done
// for offsets after the first. Otherwise you have to start from the top each // for offsets after the first. Otherwise you have to start from the top each
// time. // time.
func (e *Editor) offsetToScreenPos(offset int) (combinedPos, func(int) combinedPos) { func (e *Editor) offsetToScreenPos(hint combinedPos, offset int) combinedPos {
var col, line, idx int if hint == (combinedPos{}) {
var x fixed.Int26_6 l := e.lines[0]
runes := 0 hint = combinedPos{
x: align(e.Alignment, l.Width, e.viewSize.X),
l := e.lines[line] y: l.Ascent.Ceil(),
y := l.Ascent.Ceil() }
prevDesc := l.Descent }
LOOP:
iter := func(offset int) combinedPos {
LOOP:
for { for {
for ; col < len(l.Layout.Advances); col++ { l := e.lines[hint.lineCol.Y]
if idx >= offset { for ; hint.lineCol.X < len(l.Layout.Advances); hint.lineCol.X++ {
if hint.ofs >= offset {
break LOOP break LOOP
} }
x += l.Layout.Advances[col] hint.x += l.Layout.Advances[hint.lineCol.X]
_, s := e.rr.runeAt(idx) _, s := e.rr.runeAt(hint.ofs)
idx += s hint.ofs += s
runes++ hint.runes++
} }
if lastLine := line == len(e.lines)-1; lastLine || idx > offset { if lastLine := hint.lineCol.Y == len(e.lines)-1; lastLine || hint.ofs > offset {
break LOOP break LOOP
} }
line++ prevDesc := l.Descent
x = 0 hint.lineCol.Y++
col = 0 hint.lineCol.X = 0
l = e.lines[line] l = e.lines[hint.lineCol.Y]
y += (prevDesc + l.Ascent).Ceil() hint.x = align(e.Alignment, l.Width, e.viewSize.X)
prevDesc = l.Descent hint.y += (prevDesc + l.Ascent).Ceil()
} }
return combinedPos{ return hint
lineCol: screenPos{Y: line, X: col},
x: x + align(e.Alignment, e.lines[line].Width, e.viewSize.X),
y: y,
ofs: offset,
runes: runes,
}
}
return iter(offset), iter
} }
func (e *Editor) invalidate() { func (e *Editor) invalidate() {
@@ -1271,10 +1261,10 @@ func (e *Editor) makeValidCaret(positions ...*combinedPos) {
sort.Slice(positions, func(i, j int) bool { sort.Slice(positions, func(i, j int) bool {
return positions[i].ofs < positions[j].ofs return positions[i].ofs < positions[j].ofs
}) })
var iter func(offset int) combinedPos var hint combinedPos
*positions[0], iter = e.offsetToScreenPos(positions[0].ofs) for _, cp := range positions {
for _, cp := range positions[1:] { hint = e.offsetToScreenPos(hint, cp.ofs)
*cp = iter(cp.ofs) *cp = hint
} }
} }
+1 -1
View File
@@ -151,7 +151,7 @@ func TestEditorCaretConsistency(t *testing.T) {
t.Helper() t.Helper()
gotLine, gotCol := e.CaretPos() gotLine, gotCol := e.CaretPos()
gotCoords := e.CaretCoords() gotCoords := e.CaretCoords()
want, _ := e.offsetToScreenPos(e.caret.start.ofs) want := e.offsetToScreenPos(combinedPos{}, e.caret.start.ofs)
wantCoords := f32.Pt(float32(want.x)/64, float32(want.y)) wantCoords := f32.Pt(float32(want.x)/64, float32(want.y))
if want.lineCol.Y != gotLine || want.lineCol.X != gotCol || gotCoords != wantCoords { if want.lineCol.Y != gotLine || want.lineCol.X != gotCol || gotCoords != wantCoords {
return fmt.Errorf("caret (%d,%d) pos %s, want (%d,%d) pos %s", return fmt.Errorf("caret (%d,%d) pos %s, want (%d,%d) pos %s",