Unverified Commit a5658485 authored by Jason Simmons's avatar Jason Simmons Committed by GitHub

Ignore text selection boxes when assembling semantics for placeholder runs (#70487)

parent 5f56db0a
...@@ -902,38 +902,11 @@ class RenderParagraph extends RenderBox ...@@ -902,38 +902,11 @@ class RenderParagraph extends RenderBox
RenderBox? child = firstChild; RenderBox? child = firstChild;
final Queue<SemanticsNode> newChildCache = Queue<SemanticsNode>(); final Queue<SemanticsNode> newChildCache = Queue<SemanticsNode>();
for (final InlineSpanSemanticsInformation info in _combineSemanticsInfo()) { for (final InlineSpanSemanticsInformation info in _combineSemanticsInfo()) {
final TextDirection initialDirection = currentDirection;
final TextSelection selection = TextSelection( final TextSelection selection = TextSelection(
baseOffset: start, baseOffset: start,
extentOffset: start + info.text.length, extentOffset: start + info.text.length,
); );
final List<ui.TextBox> rects = getBoxesForSelection(selection);
start += info.text.length; start += info.text.length;
if (rects.isEmpty) {
continue;
}
Rect rect = rects.first.toRect();
currentDirection = rects.first.direction;
for (final ui.TextBox textBox in rects.skip(1)) {
rect = rect.expandToInclude(textBox.toRect());
currentDirection = textBox.direction;
}
// Any of the text boxes may have had infinite dimensions.
// We shouldn't pass infinite dimensions up to the bridges.
rect = Rect.fromLTWH(
math.max(0.0, rect.left),
math.max(0.0, rect.top),
math.min(rect.width, constraints.maxWidth),
math.min(rect.height, constraints.maxHeight),
);
// round the current rectangle to make this API testable and add some
// padding so that the accessibility rects do not overlap with the text.
currentRect = Rect.fromLTRB(
rect.left.floorToDouble() - 4.0,
rect.top.floorToDouble() - 4.0,
rect.right.ceilToDouble() + 4.0,
rect.bottom.ceilToDouble() + 4.0,
);
if (info.isPlaceholder) { if (info.isPlaceholder) {
// A placeholder span may have 0 to multple semantics nodes, we need // A placeholder span may have 0 to multple semantics nodes, we need
...@@ -954,6 +927,33 @@ class RenderParagraph extends RenderBox ...@@ -954,6 +927,33 @@ class RenderParagraph extends RenderBox
child = childAfter(child!); child = childAfter(child!);
placeholderIndex += 1; placeholderIndex += 1;
} else { } else {
final TextDirection initialDirection = currentDirection;
final List<ui.TextBox> rects = getBoxesForSelection(selection);
if (rects.isEmpty) {
continue;
}
Rect rect = rects.first.toRect();
currentDirection = rects.first.direction;
for (final ui.TextBox textBox in rects.skip(1)) {
rect = rect.expandToInclude(textBox.toRect());
currentDirection = textBox.direction;
}
// Any of the text boxes may have had infinite dimensions.
// We shouldn't pass infinite dimensions up to the bridges.
rect = Rect.fromLTWH(
math.max(0.0, rect.left),
math.max(0.0, rect.top),
math.min(rect.width, constraints.maxWidth),
math.min(rect.height, constraints.maxHeight),
);
// round the current rectangle to make this API testable and add some
// padding so that the accessibility rects do not overlap with the text.
currentRect = Rect.fromLTRB(
rect.left.floorToDouble() - 4.0,
rect.top.floorToDouble() - 4.0,
rect.right.ceilToDouble() + 4.0,
rect.bottom.ceilToDouble() + 4.0,
);
final SemanticsConfiguration configuration = SemanticsConfiguration() final SemanticsConfiguration configuration = SemanticsConfiguration()
..sortKey = OrdinalSortKey(ordinal++) ..sortKey = OrdinalSortKey(ordinal++)
..textDirection = initialDirection ..textDirection = initialDirection
......
...@@ -37,6 +37,26 @@ class RenderParagraphWithEmptySelectionBoxList extends RenderParagraph { ...@@ -37,6 +37,26 @@ class RenderParagraphWithEmptySelectionBoxList extends RenderParagraph {
} }
} }
// A subclass of RenderParagraph that returns an empty list in getBoxesForSelection
// for a selection representing a WidgetSpan.
// This is intended to simulate how SkParagraph's implementation of Paragraph.getBoxesForRange
// can return an empty list for a WidgetSpan with empty dimensions.
class RenderParagraphWithEmptyBoxListForWidgetSpan extends RenderParagraph {
RenderParagraphWithEmptyBoxListForWidgetSpan(
InlineSpan text, {
required List<RenderBox> children,
required TextDirection textDirection,
}) : super(text, children: children, textDirection: textDirection);
@override
List<ui.TextBox> getBoxesForSelection(TextSelection selection) {
if (text.getSpanForPosition(selection.base) is WidgetSpan) {
return <ui.TextBox>[];
}
return super.getBoxesForSelection(selection);
}
}
void main() { void main() {
test('getOffsetForCaret control test', () { test('getOffsetForCaret control test', () {
final RenderParagraph paragraph = RenderParagraph( final RenderParagraph paragraph = RenderParagraph(
...@@ -588,4 +608,25 @@ void main() { ...@@ -588,4 +608,25 @@ void main() {
paragraph.assembleSemanticsNode(node, SemanticsConfiguration(), <SemanticsNode>[]); paragraph.assembleSemanticsNode(node, SemanticsConfiguration(), <SemanticsNode>[]);
expect(node.childrenCount, 2); expect(node.childrenCount, 2);
}, skip: isBrowser); // https://github.com/flutter/flutter/issues/61020 }, skip: isBrowser); // https://github.com/flutter/flutter/issues/61020
test('assembleSemanticsNode handles empty WidgetSpans that do not yield selection boxes', () {
final TextSpan text = TextSpan(text: '', children: <InlineSpan>[
TextSpan(text: 'A', recognizer: TapGestureRecognizer()..onTap = () {}),
const WidgetSpan(child: SizedBox(width: 0, height: 0)),
TextSpan(text: 'C', recognizer: TapGestureRecognizer()..onTap = () {}),
]);
final List<RenderBox> renderBoxes = <RenderBox>[
RenderParagraph(const TextSpan(text: 'b'), textDirection: TextDirection.ltr),
];
final RenderParagraph paragraph = RenderParagraphWithEmptyBoxListForWidgetSpan(
text,
children: renderBoxes,
textDirection: TextDirection.ltr,
);
layout(paragraph);
final SemanticsNode node = SemanticsNode();
paragraph.assembleSemanticsNode(node, SemanticsConfiguration(), <SemanticsNode>[]);
expect(node.childrenCount, 2);
}, skip: isBrowser); // https://github.com/flutter/flutter/issues/61020
} }
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