Skip to content

Commit c53501d

Browse files
authored
Send text direction in selection rects (#117436)
1 parent e599e5c commit c53501d

File tree

4 files changed

+43
-13
lines changed

4 files changed

+43
-13
lines changed

packages/flutter/lib/src/rendering/editable.dart

+8-3
Original file line numberDiff line numberDiff line change
@@ -1316,11 +1316,16 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin,
13161316
/// Returns a list of rects that bound the given selection.
13171317
///
13181318
/// See [TextPainter.getBoxesForSelection] for more details.
1319-
List<Rect> getBoxesForSelection(TextSelection selection) {
1319+
List<TextBox> getBoxesForSelection(TextSelection selection) {
13201320
_computeTextMetricsIfNeeded();
13211321
return _textPainter.getBoxesForSelection(selection)
1322-
.map((TextBox textBox) => textBox.toRect().shift(_paintOffset))
1323-
.toList();
1322+
.map((TextBox textBox) => TextBox.fromLTRBD(
1323+
textBox.left + _paintOffset.dx,
1324+
textBox.top + _paintOffset.dy,
1325+
textBox.right + _paintOffset.dx,
1326+
textBox.bottom + _paintOffset.dy,
1327+
textBox.direction
1328+
)).toList();
13241329
}
13251330

13261331
@override

packages/flutter/lib/src/services/text_input.dart

+18-3
Original file line numberDiff line numberDiff line change
@@ -1211,7 +1211,11 @@ abstract class ScribbleClient {
12111211
class SelectionRect {
12121212
/// Constructor for creating a [SelectionRect] from a text [position] and
12131213
/// [bounds].
1214-
const SelectionRect({required this.position, required this.bounds});
1214+
const SelectionRect({
1215+
required this.position,
1216+
required this.bounds,
1217+
this.direction = TextDirection.ltr,
1218+
});
12151219

12161220
/// The position of this selection rect within the text String.
12171221
final int position;
@@ -1220,6 +1224,9 @@ class SelectionRect {
12201224
/// currently focused [RenderEditable]'s coordinate space.
12211225
final Rect bounds;
12221226

1227+
/// The direction text flows within this selection rect.
1228+
final TextDirection direction;
1229+
12231230
@override
12241231
bool operator ==(Object other) {
12251232
if (identical(this, other)) {
@@ -1230,7 +1237,8 @@ class SelectionRect {
12301237
}
12311238
return other is SelectionRect
12321239
&& other.position == position
1233-
&& other.bounds == bounds;
1240+
&& other.bounds == bounds
1241+
&& other.direction == direction;
12341242
}
12351243

12361244
@override
@@ -2321,7 +2329,14 @@ class _PlatformTextInputControl with TextInputControl {
23212329
_channel.invokeMethod<void>(
23222330
'TextInput.setSelectionRects',
23232331
selectionRects.map((SelectionRect rect) {
2324-
return <num>[rect.bounds.left, rect.bounds.top, rect.bounds.width, rect.bounds.height, rect.position];
2332+
return <num>[
2333+
rect.bounds.left,
2334+
rect.bounds.top,
2335+
rect.bounds.width,
2336+
rect.bounds.height,
2337+
rect.position,
2338+
rect.direction.index,
2339+
];
23252340
}).toList(),
23262341
);
23272342
}

packages/flutter/lib/src/widgets/editable_text.dart

+6-6
Original file line numberDiff line numberDiff line change
@@ -3279,15 +3279,15 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
32793279
if (selection.isCollapsed) {
32803280
rectToReveal = targetOffset.rect;
32813281
} else {
3282-
final List<Rect> selectionBoxes = renderEditable.getBoxesForSelection(selection);
3282+
final List<TextBox> selectionBoxes = renderEditable.getBoxesForSelection(selection);
32833283
// selectionBoxes may be empty if, for example, the selection does not
32843284
// encompass a full character, like if it only contained part of an
32853285
// extended grapheme cluster.
32863286
if (selectionBoxes.isEmpty) {
32873287
rectToReveal = targetOffset.rect;
32883288
} else {
32893289
rectToReveal = selection.baseOffset < selection.extentOffset ?
3290-
selectionBoxes.last : selectionBoxes.first;
3290+
selectionBoxes.last.toRect() : selectionBoxes.first.toRect();
32913291
}
32923292
}
32933293

@@ -3590,20 +3590,20 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
35903590
final CharacterRange characterRange = CharacterRange(plainText);
35913591
while (characterRange.moveNext()) {
35923592
final int graphemeEnd = graphemeStart + characterRange.current.length;
3593-
final List<Rect> boxes = renderEditable.getBoxesForSelection(
3593+
final List<TextBox> boxes = renderEditable.getBoxesForSelection(
35943594
TextSelection(baseOffset: graphemeStart, extentOffset: graphemeEnd),
35953595
);
35963596

3597-
final Rect? box = boxes.isEmpty ? null : boxes.first;
3597+
final TextBox? box = boxes.isEmpty ? null : boxes.first;
35983598
if (box != null) {
35993599
final Rect paintBounds = renderEditable.paintBounds;
36003600
// Stop early when characters are already below the bottom edge of the
36013601
// RenderEditable, regardless of its clipBehavior.
36023602
if (paintBounds.bottom <= box.top) {
36033603
break;
36043604
}
3605-
if (paintBounds.contains(box.topLeft) || paintBounds.contains(box.bottomRight)) {
3606-
rects.add(SelectionRect(position: graphemeStart, bounds: box));
3605+
if (paintBounds.contains(Offset(box.left, box.top)) || paintBounds.contains(Offset(box.right, box.bottom))) {
3606+
rects.add(SelectionRect(position: graphemeStart, bounds: box.toRect(), direction: box.direction));
36073607
}
36083608
}
36093609
graphemeStart = graphemeEnd;

packages/flutter/test/services/text_input_test.dart

+11-1
Original file line numberDiff line numberDiff line change
@@ -906,10 +906,20 @@ void main() {
906906
expect(fakeTextChannel.outgoingCalls.length, 6);
907907
expect(fakeTextChannel.outgoingCalls.last.method, 'TextInput.setEditableSizeAndTransform');
908908

909-
connection.setSelectionRects(const <SelectionRect>[SelectionRect(position: 0, bounds: Rect.zero)]);
909+
connection.setSelectionRects(const <SelectionRect>[SelectionRect(position: 1, bounds: Rect.fromLTWH(2, 3, 4, 5), direction: TextDirection.rtl)]);
910910
expectedMethodCalls.add('setSelectionRects');
911911
expect(control.methodCalls, expectedMethodCalls);
912912
expect(fakeTextChannel.outgoingCalls.length, 7);
913+
expect(fakeTextChannel.outgoingCalls.last.arguments, const TypeMatcher<List<List<num>>>());
914+
final List<List<num>> sentList = fakeTextChannel.outgoingCalls.last.arguments as List<List<num>>;
915+
expect(sentList.length, 1);
916+
expect(sentList[0].length, 6);
917+
expect(sentList[0][0], 2); // left
918+
expect(sentList[0][1], 3); // top
919+
expect(sentList[0][2], 4); // width
920+
expect(sentList[0][3], 5); // height
921+
expect(sentList[0][4], 1); // position
922+
expect(sentList[0][5], TextDirection.rtl.index); // direction
913923
expect(fakeTextChannel.outgoingCalls.last.method, 'TextInput.setSelectionRects');
914924

915925
connection.setStyle(

0 commit comments

Comments
 (0)