Skip to content

Commit 8a65872

Browse files
authored
Re-apply "Send nextId in addition to previousId for traversal order" (flutter#14694)
Re-apply "Send nextId in addition to previousId for traversal order (flutter#14676)"
1 parent e9d7bf8 commit 8a65872

File tree

6 files changed

+97
-13
lines changed

6 files changed

+97
-13
lines changed

bin/internal/engine.version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
f5a4a9378740c3d5996583a9ed1f7e28ff08ee85
1+
05c5f817eb731569ddaad6397ee9ce000d37f2f1

packages/flutter/lib/src/semantics/semantics.dart

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ class SemanticsData extends Diagnosticable {
9292
@required this.decreasedValue,
9393
@required this.hint,
9494
@required this.textDirection,
95+
@required this.nextNodeId,
9596
@required this.previousNodeId,
9697
@required this.rect,
9798
@required this.textSelection,
@@ -151,6 +152,10 @@ class SemanticsData extends Diagnosticable {
151152
/// [increasedValue], and [decreasedValue].
152153
final TextDirection textDirection;
153154

155+
/// The index indicating the ID of the next node in the traversal order after
156+
/// this node for the platform's accessibility services.
157+
final int nextNodeId;
158+
154159
/// The index indicating the ID of the previous node in the traversal order before
155160
/// this node for the platform's accessibility services.
156161
final int previousNodeId;
@@ -237,6 +242,7 @@ class SemanticsData extends Diagnosticable {
237242
properties.add(new StringProperty('decreasedValue', decreasedValue, defaultValue: ''));
238243
properties.add(new StringProperty('hint', hint, defaultValue: ''));
239244
properties.add(new EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
245+
properties.add(new IntProperty('nextNodeId', nextNodeId, defaultValue: null));
240246
properties.add(new IntProperty('previousNodeId', previousNodeId, defaultValue: null));
241247
if (textSelection?.isValid == true)
242248
properties.add(new MessageProperty('textSelection', '[${textSelection.start}, ${textSelection.end}]'));
@@ -258,6 +264,7 @@ class SemanticsData extends Diagnosticable {
258264
&& typedOther.decreasedValue == decreasedValue
259265
&& typedOther.hint == hint
260266
&& typedOther.textDirection == textDirection
267+
&& typedOther.nextNodeId == nextNodeId
261268
&& typedOther.previousNodeId == previousNodeId
262269
&& typedOther.rect == rect
263270
&& setEquals(typedOther.tags, tags)
@@ -269,7 +276,7 @@ class SemanticsData extends Diagnosticable {
269276
}
270277

271278
@override
272-
int get hashCode => ui.hashValues(flags, actions, label, value, increasedValue, decreasedValue, hint, textDirection, previousNodeId, rect, tags, textSelection, scrollPosition, scrollExtentMax, scrollExtentMin, transform);
279+
int get hashCode => ui.hashValues(flags, actions, label, value, increasedValue, decreasedValue, hint, textDirection, nextNodeId, previousNodeId, rect, tags, textSelection, scrollPosition, scrollExtentMax, scrollExtentMin, transform);
273280
}
274281

275282
class _SemanticsDiagnosticableNode extends DiagnosticableNode<SemanticsNode> {
@@ -1067,10 +1074,30 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
10671074

10681075
/// The sort order for ordering the traversal of [SemanticsNode]s by the
10691076
/// platform's accessibility services (e.g. VoiceOver on iOS and TalkBack on
1070-
/// Android). This is used to determine the [previousNodeId] during a semantics update.
1077+
/// Android). This is used to determine the [nextNodeId] and [previousNodeId]
1078+
/// during a semantics update.
10711079
SemanticsSortOrder _sortOrder;
10721080
SemanticsSortOrder get sortOrder => _sortOrder;
10731081

1082+
/// The ID of the next node in the traversal order after this node.
1083+
///
1084+
/// Only valid after at least one semantics update has been built.
1085+
///
1086+
/// This is the value passed to the engine to tell it what the order
1087+
/// should be for traversing semantics nodes.
1088+
///
1089+
/// If this is set to -1, it will indicate that there is no next node to
1090+
/// the engine (i.e. this is the last node in the sort order). When it is
1091+
/// null, it means that no semantics update has been built yet.
1092+
int _nextNodeId;
1093+
void _updateNextNodeId(int value) {
1094+
if (value == _nextNodeId)
1095+
return;
1096+
_nextNodeId = value;
1097+
_markDirty();
1098+
}
1099+
int get nextNodeId => _nextNodeId;
1100+
10741101
/// The ID of the previous node in the traversal order before this node.
10751102
///
10761103
/// Only valid after at least one semantics update has been built.
@@ -1194,6 +1221,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
11941221
String increasedValue = _increasedValue;
11951222
String decreasedValue = _decreasedValue;
11961223
TextDirection textDirection = _textDirection;
1224+
int nextNodeId = _nextNodeId;
11971225
int previousNodeId = _previousNodeId;
11981226
Set<SemanticsTag> mergedTags = tags == null ? null : new Set<SemanticsTag>.from(tags);
11991227
TextSelection textSelection = _textSelection;
@@ -1207,6 +1235,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
12071235
flags |= node._flags;
12081236
actions |= node._actionsAsBits;
12091237
textDirection ??= node._textDirection;
1238+
nextNodeId ??= node._nextNodeId;
12101239
previousNodeId ??= node._previousNodeId;
12111240
textSelection ??= node._textSelection;
12121241
scrollPosition ??= node._scrollPosition;
@@ -1247,6 +1276,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
12471276
decreasedValue: decreasedValue,
12481277
hint: hint,
12491278
textDirection: textDirection,
1279+
nextNodeId: nextNodeId,
12501280
previousNodeId: previousNodeId,
12511281
rect: rect,
12521282
transform: transform,
@@ -1289,6 +1319,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
12891319
increasedValue: data.increasedValue,
12901320
hint: data.hint,
12911321
textDirection: data.textDirection,
1322+
nextNodeId: data.nextNodeId,
12921323
previousNodeId: data.previousNodeId,
12931324
textSelectionBase: data.textSelection != null ? data.textSelection.baseOffset : -1,
12941325
textSelectionExtent: data.textSelection != null ? data.textSelection.extentOffset : -1,
@@ -1363,6 +1394,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
13631394
properties.add(new StringProperty('decreasedValue', _decreasedValue, defaultValue: ''));
13641395
properties.add(new StringProperty('hint', _hint, defaultValue: ''));
13651396
properties.add(new EnumProperty<TextDirection>('textDirection', _textDirection, defaultValue: null));
1397+
properties.add(new IntProperty('nextNodeId', _nextNodeId, defaultValue: null));
13661398
properties.add(new IntProperty('previousNodeId', _previousNodeId, defaultValue: null));
13671399
properties.add(new DiagnosticsProperty<SemanticsSortOrder>('sortOrder', sortOrder, defaultValue: null));
13681400
if (_textSelection?.isValid == true)
@@ -1539,10 +1571,10 @@ class SemanticsOwner extends ChangeNotifier {
15391571
super.dispose();
15401572
}
15411573

1542-
// Updates the previousNodeId IDs on the semantics nodes. These IDs are used
1543-
// on the platform side to order the nodes for traversal by the accessibility
1544-
// services. If the previousNodeId for a node changes, the node will be marked as
1545-
// dirty.
1574+
// Updates the nextNodeId and previousNodeId IDs on the semantics nodes. These
1575+
// IDs are used on the platform side to order the nodes for traversal by the
1576+
// accessibility services. If the nextNodeId or previousNodeId for a node
1577+
// changes, the node will be marked as dirty.
15461578
void _updateTraversalOrder() {
15471579
final List<_TraversalSortNode> nodesInSemanticsTraversalOrder = <_TraversalSortNode>[];
15481580
SemanticsSortOrder currentSortOrder = new SemanticsSortOrder(keys: <SemanticsSortKey>[]);
@@ -1577,12 +1609,20 @@ class SemanticsOwner extends ChangeNotifier {
15771609
return true;
15781610
}
15791611
rootSemanticsNode.visitChildren(visitor);
1612+
1613+
if (nodesInSemanticsTraversalOrder.isEmpty)
1614+
return;
1615+
15801616
nodesInSemanticsTraversalOrder.sort();
1581-
int previousNodeId = -1;
1582-
for (_TraversalSortNode node in nodesInSemanticsTraversalOrder) {
1583-
node.node._updatePreviousNodeId(previousNodeId);
1584-
previousNodeId = node.node.id;
1617+
_TraversalSortNode node = nodesInSemanticsTraversalOrder.removeLast();
1618+
node.node._updateNextNodeId(-1);
1619+
while (nodesInSemanticsTraversalOrder.isNotEmpty) {
1620+
final _TraversalSortNode previousNode = nodesInSemanticsTraversalOrder.removeLast();
1621+
node.node._updatePreviousNodeId(previousNode.node.id);
1622+
previousNode.node._updateNextNodeId(node.node.id);
1623+
node = previousNode;
15851624
}
1625+
node.node._updatePreviousNodeId(-1);
15861626
}
15871627

15881628
/// Update the semantics using [Window.updateSemantics].

packages/flutter/test/semantics/semantics_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ void main() {
348348

349349
expect(
350350
minimalProperties.toStringDeep(minLevel: DiagnosticLevel.hidden),
351-
'SemanticsNode#1(owner: null, isMergedIntoParent: false, mergeAllDescendantsIntoThisNode: false, Rect.fromLTRB(0.0, 0.0, 0.0, 0.0), actions: [], isInMutuallyExcusiveGroup: false, isSelected: false, isFocused: false, isButton: false, isTextField: false, invisible, label: "", value: "", increasedValue: "", decreasedValue: "", hint: "", textDirection: null, previousNodeId: null, sortOrder: null, scrollExtentMin: null, scrollPosition: null, scrollExtentMax: null)\n'
351+
'SemanticsNode#1(owner: null, isMergedIntoParent: false, mergeAllDescendantsIntoThisNode: false, Rect.fromLTRB(0.0, 0.0, 0.0, 0.0), actions: [], isInMutuallyExcusiveGroup: false, isSelected: false, isFocused: false, isButton: false, isTextField: false, invisible, label: "", value: "", increasedValue: "", decreasedValue: "", hint: "", textDirection: null, nextNodeId: null, previousNodeId: null, sortOrder: null, scrollExtentMin: null, scrollPosition: null, scrollExtentMax: null)\n'
352352
);
353353

354354
final SemanticsConfiguration config = new SemanticsConfiguration()

packages/flutter/test/widgets/semantics_test.dart

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,7 @@ void main() {
417417
rect: TestSemantics.fullScreen,
418418
actions: allActions.fold(0, (int previous, SemanticsAction action) => previous | action.index),
419419
previousNodeId: -1,
420+
nextNodeId: -1,
420421
),
421422
],
422423
);
@@ -613,44 +614,59 @@ void main() {
613614
expect(semanticsUpdateCount, 1);
614615
expect(semantics, hasSemantics(
615616
new TestSemantics(
617+
id: 0,
616618
children: <TestSemantics>[
617619
new TestSemantics(
620+
id: 2,
621+
nextNodeId: 5,
618622
previousNodeId: -1,
619623
children: <TestSemantics>[
620624
new TestSemantics(
625+
id: 3,
621626
label: r'Label 1',
622627
textDirection: TextDirection.ltr,
628+
nextNodeId: -1,
623629
previousNodeId: 4,
624630
),
625631
new TestSemantics(
632+
id: 4,
626633
label: r'Label 2',
627634
textDirection: TextDirection.ltr,
635+
nextNodeId: 3,
628636
previousNodeId: 6,
629637
),
630638
new TestSemantics(
639+
id: 5,
640+
nextNodeId: 8,
631641
previousNodeId: 2,
632642
children: <TestSemantics>[
633643
new TestSemantics(
644+
id: 6,
634645
label: r'Label 3',
635646
textDirection: TextDirection.ltr,
647+
nextNodeId: 4,
636648
previousNodeId: 7,
637649
),
638650
new TestSemantics(
651+
id: 7,
639652
label: r'Label 4',
640653
textDirection: TextDirection.ltr,
654+
nextNodeId: 6,
641655
previousNodeId: 8,
642656
),
643657
new TestSemantics(
658+
id: 8,
644659
label: r'Label 5',
645660
textDirection: TextDirection.ltr,
661+
nextNodeId: 7,
646662
previousNodeId: 5,
647663
),
648664
],
649665
),
650666
],
651667
),
652668
],
653-
), ignoreTransform: true, ignoreRect: true, ignoreId: true),
669+
), ignoreTransform: true, ignoreRect: true),
654670
);
655671
semantics.dispose();
656672
});
@@ -713,26 +729,31 @@ void main() {
713729
new TestSemantics(
714730
label: r'Label 1',
715731
textDirection: TextDirection.ltr,
732+
nextNodeId: -1,
716733
previousNodeId: 3,
717734
),
718735
new TestSemantics(
719736
label: r'Label 2',
720737
textDirection: TextDirection.ltr,
738+
nextNodeId: 2,
721739
previousNodeId: 4,
722740
),
723741
new TestSemantics(
724742
label: r'Label 3',
725743
textDirection: TextDirection.ltr,
744+
nextNodeId: 3,
726745
previousNodeId: 5,
727746
),
728747
new TestSemantics(
729748
label: r'Label 4',
730749
textDirection: TextDirection.ltr,
750+
nextNodeId: 4,
731751
previousNodeId: 6,
732752
),
733753
new TestSemantics(
734754
label: r'Label 5',
735755
textDirection: TextDirection.ltr,
756+
nextNodeId: 5,
736757
previousNodeId: -1,
737758
),
738759
],
@@ -802,31 +823,37 @@ void main() {
802823
new TestSemantics(
803824
children: <TestSemantics>[
804825
new TestSemantics(
826+
nextNodeId: 5,
805827
previousNodeId: -1,
806828
children: <TestSemantics>[
807829
new TestSemantics(
808830
label: r'Label 1',
809831
textDirection: TextDirection.ltr,
832+
nextNodeId: 7,
810833
previousNodeId: 5,
811834
),
812835
new TestSemantics(
813836
label: r'Label 2',
814837
textDirection: TextDirection.ltr,
838+
nextNodeId: -1,
815839
previousNodeId: 6,
816840
),
817841
new TestSemantics(
818842
label: r'Label 3',
819843
textDirection: TextDirection.ltr,
844+
nextNodeId: 3,
820845
previousNodeId: 2,
821846
),
822847
new TestSemantics(
823848
label: r'Label 4',
824849
textDirection: TextDirection.ltr,
850+
nextNodeId: 4,
825851
previousNodeId: 7,
826852
),
827853
new TestSemantics(
828854
label: r'Label 5',
829855
textDirection: TextDirection.ltr,
856+
nextNodeId: 6,
830857
previousNodeId: 3,
831858
),
832859
],

packages/flutter/test/widgets/semantics_tester.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ class TestSemantics {
4343
this.decreasedValue: '',
4444
this.hint: '',
4545
this.textDirection,
46+
this.nextNodeId,
4647
this.previousNodeId,
4748
this.rect,
4849
this.transform,
@@ -71,6 +72,7 @@ class TestSemantics {
7172
this.hint: '',
7273
this.textDirection,
7374
this.previousNodeId,
75+
this.nextNodeId,
7476
this.transform,
7577
this.textSelection,
7678
this.children: const <TestSemantics>[],
@@ -106,6 +108,7 @@ class TestSemantics {
106108
this.increasedValue: '',
107109
this.decreasedValue: '',
108110
this.textDirection,
111+
this.nextNodeId,
109112
this.previousNodeId,
110113
this.rect,
111114
Matrix4 transform,
@@ -173,6 +176,10 @@ class TestSemantics {
173176
/// is also set.
174177
final TextDirection textDirection;
175178

179+
/// The ID of the node that is next in the semantics traversal order after
180+
/// this node.
181+
final int nextNodeId;
182+
176183
/// The ID of the node that is previous in the semantics traversal order before
177184
/// this node.
178185
final int previousNodeId;
@@ -258,6 +265,8 @@ class TestSemantics {
258265
return fail('expected node id $id to have hint "$hint" but found hint "${nodeData.hint}".');
259266
if (textDirection != null && textDirection != nodeData.textDirection)
260267
return fail('expected node id $id to have textDirection "$textDirection" but found "${nodeData.textDirection}".');
268+
if (nextNodeId != null && nextNodeId != nodeData.nextNodeId)
269+
return fail('expected node id $id to have nextNodeId "$nextNodeId" but found "${nodeData.nextNodeId}".');
261270
if (previousNodeId != null && previousNodeId != nodeData.previousNodeId)
262271
return fail('expected node id $id to have previousNodeId "$previousNodeId" but found "${nodeData.previousNodeId}".');
263272
if ((nodeData.label != '' || nodeData.value != '' || nodeData.hint != '' || node.increasedValue != '' || node.decreasedValue != '') && nodeData.textDirection == null)
@@ -311,6 +320,8 @@ class TestSemantics {
311320
buf.writeln('$indent hint: \'$hint\',');
312321
if (textDirection != null)
313322
buf.writeln('$indent textDirection: $textDirection,');
323+
if (nextNodeId != null)
324+
buf.writeln('$indent nextNodeId: $nextNodeId,');
314325
if (previousNodeId != null)
315326
buf.writeln('$indent previousNodeId: $previousNodeId,');
316327
if (textSelection?.isValid == true)
@@ -522,6 +533,8 @@ class SemanticsTester {
522533
buf.writeln(' hint: r\'${node.hint}\',');
523534
if (node.textDirection != null)
524535
buf.writeln(' textDirection: ${node.textDirection},');
536+
if (node.nextNodeId != null)
537+
buf.writeln(' nextNodeId: ${node.nextNodeId},');
525538
if (node.previousNodeId != null)
526539
buf.writeln(' previousNodeId: ${node.previousNodeId},');
527540

packages/flutter/test/widgets/semantics_tester_generateTestSemanticsExpressionForCurrentSemanticsTree_test.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,14 +105,17 @@ void _tests() {
105105
new TestSemantics(
106106
children: <TestSemantics>[
107107
new TestSemantics(
108+
nextNodeId: 4,
108109
previousNodeId: -1,
109110
children: <TestSemantics>[
110111
new TestSemantics(
112+
nextNodeId: 2,
111113
previousNodeId: 1,
112114
children: <TestSemantics>[
113115
new TestSemantics(
114116
label: r'Plain text',
115117
textDirection: TextDirection.ltr,
118+
nextNodeId: 3,
116119
previousNodeId: 4,
117120
),
118121
new TestSemantics(
@@ -124,6 +127,7 @@ void _tests() {
124127
decreasedValue: r'test-decreasedValue',
125128
hint: r'test-hint',
126129
textDirection: TextDirection.rtl,
130+
nextNodeId: -1,
127131
previousNodeId: 2,
128132
),
129133
],

0 commit comments

Comments
 (0)