moveUp function
NavigationResult
moveUp(
- Root root,
- CaretStop current,
- double preferredX,
- CaretXResolver resolveX,
- CaretYResolver resolveY, {
- List<
CaretStop> ? stops,
Moves the cursor up by one LogicalLine.
preferredX is the x coordinate in pixels to maintain.
If it's -1.0, it's calculated from the current position via resolveX.
Implementation
NavigationResult moveUp(
Root root,
CaretStop current,
double preferredX,
CaretXResolver resolveX,
CaretYResolver resolveY, {
List<CaretStop>? stops,
}) {
final stops_ = stops ?? buildAllStops(root);
if (stops_.isEmpty) return NavigationResult.none;
// resolveY/resolveX are expensive (O(visible) each). Cache their
// results for the duration of this call to avoid redundant lookups.
final yCache = <CaretStop, double>{};
double cachedY(CaretStop s) => yCache[s] ??= resolveY(s);
final xCache = <CaretStop, double>{};
double cachedX(CaretStop s) => xCache[s] ??= resolveX(s);
final x = preferredX >= 0.0 ? preferredX : cachedX(current);
final currentY = cachedY(current);
print('[MOVE_UP] current=${current.fragmentId}:${current.offset} x=$x currentY=$currentY');
// Find the highest y that is strictly above the current line
double? targetY;
for (final stop in stops_) {
final y = cachedY(stop);
if (y < currentY - _kLineYTolerance) {
if (targetY == null || y > targetY) targetY = y;
}
}
print('[MOVE_UP] targetY=$targetY');
// No line above: go to the first stop of the document
if (targetY == null) {
final first = stops_.first;
if (first == current) return NavigationResult.none;
print('[MOVE_UP] no line above → first=${first.fragmentId}:${first.offset}');
return NavigationResult(position: first, preferredX: x);
}
// Among the stops on the target line, take the one with closest x
final lineStops = stops_
.where((s) => (cachedY(s) - targetY!).abs() <= _kLineYTolerance)
.toList();
print('[MOVE_UP] lineStops=${lineStops.map((s) => '${s.fragmentId}:${s.offset}').join(', ')}');
final best = _stopNearestX(lineStops, x, cachedX);
print('[MOVE_UP] best=${best.fragmentId}:${best.offset}');
return NavigationResult(position: best, preferredX: x);
}