Use baseline value to get position in next line (#93129)
This commit is contained in:
parent
52ae102f18
commit
0374542cc5
@ -161,16 +161,6 @@ class VerticalCaretMovementRun extends BidirectionalIterator<TextPosition> {
|
|||||||
return _isValid;
|
return _isValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Computes the vertical distance from the `from` line's bottom to the `to`
|
|
||||||
// lines top.
|
|
||||||
double _lineDistance(int from, int to) {
|
|
||||||
double lineHeight = 0;
|
|
||||||
for (int index = from + 1; index < to; index += 1) {
|
|
||||||
lineHeight += _lineMetrics[index].height;
|
|
||||||
}
|
|
||||||
return lineHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Map<int, MapEntry<Offset, TextPosition>> _positionCache = <int, MapEntry<Offset, TextPosition>>{};
|
final Map<int, MapEntry<Offset, TextPosition>> _positionCache = <int, MapEntry<Offset, TextPosition>>{};
|
||||||
|
|
||||||
MapEntry<Offset, TextPosition> _getTextPositionForLine(int lineNumber) {
|
MapEntry<Offset, TextPosition> _getTextPositionForLine(int lineNumber) {
|
||||||
@ -181,11 +171,8 @@ class VerticalCaretMovementRun extends BidirectionalIterator<TextPosition> {
|
|||||||
return cachedPosition;
|
return cachedPosition;
|
||||||
}
|
}
|
||||||
assert(lineNumber != _currentLine);
|
assert(lineNumber != _currentLine);
|
||||||
final double distanceY = lineNumber > _currentLine
|
|
||||||
? _lineMetrics[_currentLine].descent + _lineMetrics[lineNumber].ascent + _lineDistance(_currentLine, lineNumber)
|
|
||||||
: - _lineMetrics[_currentLine].ascent - _lineMetrics[lineNumber].descent - _lineDistance(lineNumber, _currentLine);
|
|
||||||
|
|
||||||
final Offset newOffset = _currentOffset.translate(0, distanceY);
|
final Offset newOffset = Offset(_currentOffset.dx, _lineMetrics[lineNumber].baseline);
|
||||||
final TextPosition closestPosition = _editable._textPainter.getPositionForOffset(newOffset);
|
final TextPosition closestPosition = _editable._textPainter.getPositionForOffset(newOffset);
|
||||||
final MapEntry<Offset, TextPosition> position = MapEntry<Offset, TextPosition>(newOffset, closestPosition);
|
final MapEntry<Offset, TextPosition> position = MapEntry<Offset, TextPosition>(newOffset, closestPosition);
|
||||||
_positionCache[lineNumber] = position;
|
_positionCache[lineNumber] = position;
|
||||||
@ -2418,17 +2405,16 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
|
|||||||
// TODO(LongCatIsLooong): include line boundaries information in
|
// TODO(LongCatIsLooong): include line boundaries information in
|
||||||
// ui.LineMetrics, then we can get rid of this.
|
// ui.LineMetrics, then we can get rid of this.
|
||||||
final Offset offset = _textPainter.getOffsetForCaret(startPosition, Rect.zero);
|
final Offset offset = _textPainter.getOffsetForCaret(startPosition, Rect.zero);
|
||||||
int line = 0;
|
|
||||||
double accumulatedHeight = 0;
|
|
||||||
for (final ui.LineMetrics lineMetrics in metrics) {
|
for (final ui.LineMetrics lineMetrics in metrics) {
|
||||||
if (accumulatedHeight + lineMetrics.height > offset.dy) {
|
if (lineMetrics.baseline + lineMetrics.descent > offset.dy) {
|
||||||
return MapEntry<int, Offset>(line, Offset(offset.dx, lineMetrics.baseline));
|
return MapEntry<int, Offset>(lineMetrics.lineNumber, Offset(offset.dx, lineMetrics.baseline));
|
||||||
}
|
}
|
||||||
line += 1;
|
|
||||||
accumulatedHeight += lineMetrics.height;
|
|
||||||
}
|
}
|
||||||
assert(false, 'unable to find the line for $startPosition');
|
assert(false, 'unable to find the line for $startPosition');
|
||||||
return MapEntry<int, Offset>(math.max(0, metrics.length - 1), Offset(offset.dx, accumulatedHeight));
|
return MapEntry<int, Offset>(
|
||||||
|
math.max(0, metrics.length - 1),
|
||||||
|
Offset(offset.dx, metrics.isNotEmpty ? metrics.last.baseline + metrics.last.descent : 0.0),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Starts a [VerticalCaretMovementRun] at the given location in the text, for
|
/// Starts a [VerticalCaretMovementRun] at the given location in the text, for
|
||||||
|
@ -57,7 +57,12 @@ void main() {
|
|||||||
final TextEditingController controller = TextEditingController(text: testText);
|
final TextEditingController controller = TextEditingController(text: testText);
|
||||||
|
|
||||||
final FocusNode focusNode = FocusNode();
|
final FocusNode focusNode = FocusNode();
|
||||||
Widget buildEditableText({ TextAlign textAlign = TextAlign.left, bool readOnly = false, bool obscured = false }) {
|
Widget buildEditableText({
|
||||||
|
TextAlign textAlign = TextAlign.left,
|
||||||
|
bool readOnly = false,
|
||||||
|
bool obscured = false,
|
||||||
|
TextStyle style = const TextStyle(fontSize: 10.0),
|
||||||
|
}) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
home: Align(
|
home: Align(
|
||||||
alignment: Alignment.topLeft,
|
alignment: Alignment.topLeft,
|
||||||
@ -69,7 +74,7 @@ void main() {
|
|||||||
showSelectionHandles: true,
|
showSelectionHandles: true,
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
focusNode: focusNode,
|
focusNode: focusNode,
|
||||||
style: const TextStyle(fontSize: 10),
|
style: style,
|
||||||
textScaleFactor: 1,
|
textScaleFactor: 1,
|
||||||
// Avoid the cursor from taking up width.
|
// Avoid the cursor from taking up width.
|
||||||
cursorWidth: 0,
|
cursorWidth: 0,
|
||||||
@ -1593,6 +1598,32 @@ void main() {
|
|||||||
offset: 3, // Would have been 4 if the run wasn't interrupted.
|
offset: 3, // Would have been 4 if the run wasn't interrupted.
|
||||||
));
|
));
|
||||||
}, variant: TargetPlatformVariant.all());
|
}, variant: TargetPlatformVariant.all());
|
||||||
|
|
||||||
|
testWidgets('long run with fractional text height', (WidgetTester tester) async {
|
||||||
|
controller.text = "${'źdźbło\n' * 49}źdźbło";
|
||||||
|
controller.selection = const TextSelection.collapsed(offset: 2);
|
||||||
|
await tester.pumpWidget(buildEditableText(style: const TextStyle(fontSize: 13.0, height: 1.17)));
|
||||||
|
|
||||||
|
for (int i = 1; i <= 49; i++) {
|
||||||
|
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.arrowDown));
|
||||||
|
await tester.pump();
|
||||||
|
expect(
|
||||||
|
controller.selection,
|
||||||
|
TextSelection.collapsed(offset: 2 + i * 7),
|
||||||
|
reason: 'line $i',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 49; i >= 1; i--) {
|
||||||
|
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.arrowUp));
|
||||||
|
await tester.pump();
|
||||||
|
expect(
|
||||||
|
controller.selection,
|
||||||
|
TextSelection.collapsed(offset: 2 + (i - 1) * 7),
|
||||||
|
reason: 'line $i',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, variant: TargetPlatformVariant.all());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user