Unverified Commit c53501d8 authored by Callum Moffat's avatar Callum Moffat Committed by GitHub

Send text direction in selection rects (#117436)

parent e599e5c9
......@@ -1316,11 +1316,16 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
/// Returns a list of rects that bound the given selection.
///
/// See [TextPainter.getBoxesForSelection] for more details.
List<Rect> getBoxesForSelection(TextSelection selection) {
List<TextBox> getBoxesForSelection(TextSelection selection) {
_computeTextMetricsIfNeeded();
return _textPainter.getBoxesForSelection(selection)
.map((TextBox textBox) => textBox.toRect().shift(_paintOffset))
.toList();
.map((TextBox textBox) => TextBox.fromLTRBD(
textBox.left + _paintOffset.dx,
textBox.top + _paintOffset.dy,
textBox.right + _paintOffset.dx,
textBox.bottom + _paintOffset.dy,
textBox.direction
)).toList();
}
@override
......
......@@ -1211,7 +1211,11 @@ abstract class ScribbleClient {
class SelectionRect {
/// Constructor for creating a [SelectionRect] from a text [position] and
/// [bounds].
const SelectionRect({required this.position, required this.bounds});
const SelectionRect({
required this.position,
required this.bounds,
this.direction = TextDirection.ltr,
});
/// The position of this selection rect within the text String.
final int position;
......@@ -1220,6 +1224,9 @@ class SelectionRect {
/// currently focused [RenderEditable]'s coordinate space.
final Rect bounds;
/// The direction text flows within this selection rect.
final TextDirection direction;
@override
bool operator ==(Object other) {
if (identical(this, other)) {
......@@ -1230,7 +1237,8 @@ class SelectionRect {
}
return other is SelectionRect
&& other.position == position
&& other.bounds == bounds;
&& other.bounds == bounds
&& other.direction == direction;
}
@override
......@@ -2321,7 +2329,14 @@ class _PlatformTextInputControl with TextInputControl {
_channel.invokeMethod<void>(
'TextInput.setSelectionRects',
selectionRects.map((SelectionRect rect) {
return <num>[rect.bounds.left, rect.bounds.top, rect.bounds.width, rect.bounds.height, rect.position];
return <num>[
rect.bounds.left,
rect.bounds.top,
rect.bounds.width,
rect.bounds.height,
rect.position,
rect.direction.index,
];
}).toList(),
);
}
......
......@@ -3279,7 +3279,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
if (selection.isCollapsed) {
rectToReveal = targetOffset.rect;
} else {
final List<Rect> selectionBoxes = renderEditable.getBoxesForSelection(selection);
final List<TextBox> selectionBoxes = renderEditable.getBoxesForSelection(selection);
// selectionBoxes may be empty if, for example, the selection does not
// encompass a full character, like if it only contained part of an
// extended grapheme cluster.
......@@ -3287,7 +3287,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
rectToReveal = targetOffset.rect;
} else {
rectToReveal = selection.baseOffset < selection.extentOffset ?
selectionBoxes.last : selectionBoxes.first;
selectionBoxes.last.toRect() : selectionBoxes.first.toRect();
}
}
......@@ -3590,11 +3590,11 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
final CharacterRange characterRange = CharacterRange(plainText);
while (characterRange.moveNext()) {
final int graphemeEnd = graphemeStart + characterRange.current.length;
final List<Rect> boxes = renderEditable.getBoxesForSelection(
final List<TextBox> boxes = renderEditable.getBoxesForSelection(
TextSelection(baseOffset: graphemeStart, extentOffset: graphemeEnd),
);
final Rect? box = boxes.isEmpty ? null : boxes.first;
final TextBox? box = boxes.isEmpty ? null : boxes.first;
if (box != null) {
final Rect paintBounds = renderEditable.paintBounds;
// Stop early when characters are already below the bottom edge of the
......@@ -3602,8 +3602,8 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
if (paintBounds.bottom <= box.top) {
break;
}
if (paintBounds.contains(box.topLeft) || paintBounds.contains(box.bottomRight)) {
rects.add(SelectionRect(position: graphemeStart, bounds: box));
if (paintBounds.contains(Offset(box.left, box.top)) || paintBounds.contains(Offset(box.right, box.bottom))) {
rects.add(SelectionRect(position: graphemeStart, bounds: box.toRect(), direction: box.direction));
}
}
graphemeStart = graphemeEnd;
......
......@@ -906,10 +906,20 @@ void main() {
expect(fakeTextChannel.outgoingCalls.length, 6);
expect(fakeTextChannel.outgoingCalls.last.method, 'TextInput.setEditableSizeAndTransform');
connection.setSelectionRects(const <SelectionRect>[SelectionRect(position: 0, bounds: Rect.zero)]);
connection.setSelectionRects(const <SelectionRect>[SelectionRect(position: 1, bounds: Rect.fromLTWH(2, 3, 4, 5), direction: TextDirection.rtl)]);
expectedMethodCalls.add('setSelectionRects');
expect(control.methodCalls, expectedMethodCalls);
expect(fakeTextChannel.outgoingCalls.length, 7);
expect(fakeTextChannel.outgoingCalls.last.arguments, const TypeMatcher<List<List<num>>>());
final List<List<num>> sentList = fakeTextChannel.outgoingCalls.last.arguments as List<List<num>>;
expect(sentList.length, 1);
expect(sentList[0].length, 6);
expect(sentList[0][0], 2); // left
expect(sentList[0][1], 3); // top
expect(sentList[0][2], 4); // width
expect(sentList[0][3], 5); // height
expect(sentList[0][4], 1); // position
expect(sentList[0][5], TextDirection.rtl.index); // direction
expect(fakeTextChannel.outgoingCalls.last.method, 'TextInput.setSelectionRects');
connection.setStyle(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment