Unverified Commit d3540962 authored by Yegor's avatar Yegor Committed by GitHub

a11y traversal: sort locally; use new sorting algorithm (#16253)

New a11y traversal:

- sort direct sibling SemanticsNodes only
- use new sorting algorithm
- implement RTL
- test semantics in traversal order by default
- add AppBar traversal test
- breaking: remove nextNodeId/previousNodeId from the framework
- breaking: remove DebugSemanticsDumpOrder.geometricOrder
parent d3d4976f
...@@ -130,7 +130,7 @@ class StockHomeState extends State<StockHome> { ...@@ -130,7 +130,7 @@ class StockHomeState extends State<StockHome> {
debugDumpApp(); debugDumpApp();
debugDumpRenderTree(); debugDumpRenderTree();
debugDumpLayerTree(); debugDumpLayerTree();
debugDumpSemanticsTree(DebugSemanticsDumpOrder.geometricOrder); debugDumpSemanticsTree(DebugSemanticsDumpOrder.traversalOrder);
} catch (e, stack) { } catch (e, stack) {
debugPrint('Exception while dumping app:\n$e\n$stack'); debugPrint('Exception while dumping app:\n$e\n$stack');
} }
......
...@@ -482,10 +482,14 @@ class _AppBarState extends State<AppBar> { ...@@ -482,10 +482,14 @@ class _AppBarState extends State<AppBar> {
); );
} }
return new Material( return new Semantics(
color: widget.backgroundColor ?? themeData.primaryColor, container: true,
elevation: widget.elevation, explicitChildNodes: true,
child: appBar, child: new Material(
color: widget.backgroundColor ?? themeData.primaryColor,
elevation: widget.elevation,
child: appBar,
),
); );
} }
} }
......
...@@ -485,41 +485,45 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr ...@@ -485,41 +485,45 @@ class _BottomNavigationBarState extends State<BottomNavigationBar> with TickerPr
backgroundColor = _backgroundColor; backgroundColor = _backgroundColor;
break; break;
} }
return new Stack( return new Semantics(
children: <Widget>[ container: true,
new Positioned.fill( explicitChildNodes: true,
child: new Material( // Casts shadow. child: new Stack(
elevation: 8.0, children: <Widget>[
color: backgroundColor, new Positioned.fill(
child: new Material( // Casts shadow.
elevation: 8.0,
color: backgroundColor,
),
), ),
), new ConstrainedBox(
new ConstrainedBox( constraints: new BoxConstraints(minHeight: kBottomNavigationBarHeight + additionalBottomPadding),
constraints: new BoxConstraints(minHeight: kBottomNavigationBarHeight + additionalBottomPadding), child: new Stack(
child: new Stack( children: <Widget>[
children: <Widget>[ new Positioned.fill(
new Positioned.fill( child: new CustomPaint(
child: new CustomPaint( painter: new _RadialPainter(
painter: new _RadialPainter( circles: _circles.toList(),
circles: _circles.toList(), textDirection: Directionality.of(context),
textDirection: Directionality.of(context), ),
), ),
), ),
), new Material( // Splashes.
new Material( // Splashes. type: MaterialType.transparency,
type: MaterialType.transparency, child: new Padding(
child: new Padding( padding: new EdgeInsets.only(bottom: additionalBottomPadding),
padding: new EdgeInsets.only(bottom: additionalBottomPadding), child: new MediaQuery.removePadding(
child: new MediaQuery.removePadding( context: context,
context: context, removeBottom: true,
removeBottom: true, child: _createContainer(_createTiles()),
child: _createContainer(_createTiles()), ),
), ),
), ),
), ],
], ),
), ),
), ],
], ),
); );
} }
} }
......
...@@ -641,30 +641,39 @@ class _MonthPickerState extends State<MonthPicker> { ...@@ -641,30 +641,39 @@ class _MonthPickerState extends State<MonthPicker> {
height: _kMaxDayPickerHeight, height: _kMaxDayPickerHeight,
child: new Stack( child: new Stack(
children: <Widget>[ children: <Widget>[
new PageView.builder( new Semantics(
key: new ValueKey<DateTime>(widget.selectedDate), sortKey: _MonthPickerSortKey.calendar,
controller: _dayPickerController, child: new PageView.builder(
scrollDirection: Axis.horizontal, key: new ValueKey<DateTime>(widget.selectedDate),
itemCount: _monthDelta(widget.firstDate, widget.lastDate) + 1, controller: _dayPickerController,
itemBuilder: _buildItems, scrollDirection: Axis.horizontal,
onPageChanged: _handleMonthPageChanged, itemCount: _monthDelta(widget.firstDate, widget.lastDate) + 1,
itemBuilder: _buildItems,
onPageChanged: _handleMonthPageChanged,
),
), ),
new PositionedDirectional( new PositionedDirectional(
top: 0.0, top: 0.0,
start: 8.0, start: 8.0,
child: new IconButton( child: new Semantics(
icon: const Icon(Icons.chevron_left), sortKey: _MonthPickerSortKey.previousMonth,
tooltip: _isDisplayingFirstMonth ? null : '${localizations.previousMonthTooltip} ${localizations.formatMonthYear(_previousMonthDate)}', child: new IconButton(
onPressed: _isDisplayingFirstMonth ? null : _handlePreviousMonth, icon: const Icon(Icons.chevron_left),
tooltip: _isDisplayingFirstMonth ? null : '${localizations.previousMonthTooltip} ${localizations.formatMonthYear(_previousMonthDate)}',
onPressed: _isDisplayingFirstMonth ? null : _handlePreviousMonth,
),
), ),
), ),
new PositionedDirectional( new PositionedDirectional(
top: 0.0, top: 0.0,
end: 8.0, end: 8.0,
child: new IconButton( child: new Semantics(
icon: const Icon(Icons.chevron_right), sortKey: _MonthPickerSortKey.nextMonth,
tooltip: _isDisplayingLastMonth ? null : '${localizations.nextMonthTooltip} ${localizations.formatMonthYear(_nextMonthDate)}', child: new IconButton(
onPressed: _isDisplayingLastMonth ? null : _handleNextMonth, icon: const Icon(Icons.chevron_right),
tooltip: _isDisplayingLastMonth ? null : '${localizations.nextMonthTooltip} ${localizations.formatMonthYear(_nextMonthDate)}',
onPressed: _isDisplayingLastMonth ? null : _handleNextMonth,
),
), ),
), ),
], ],
...@@ -680,6 +689,16 @@ class _MonthPickerState extends State<MonthPicker> { ...@@ -680,6 +689,16 @@ class _MonthPickerState extends State<MonthPicker> {
} }
} }
// Defines semantic traversal order of the top-level widgets inside the month
// picker.
class _MonthPickerSortKey extends OrdinalSortKey {
static const _MonthPickerSortKey previousMonth = const _MonthPickerSortKey(1.0);
static const _MonthPickerSortKey nextMonth = const _MonthPickerSortKey(2.0);
static const _MonthPickerSortKey calendar = const _MonthPickerSortKey(3.0);
const _MonthPickerSortKey(double order) : super(order);
}
/// A scrollable list of years to allow picking a year. /// A scrollable list of years to allow picking a year.
/// ///
/// The year picker widget is rarely used directly. Instead, consider using /// The year picker widget is rarely used directly. Instead, consider using
......
...@@ -101,8 +101,8 @@ abstract class RendererBinding extends BindingBase with ServicesBinding, Schedul ...@@ -101,8 +101,8 @@ abstract class RendererBinding extends BindingBase with ServicesBinding, Schedul
); );
registerSignalServiceExtension( registerSignalServiceExtension(
name: 'debugDumpSemanticsTreeInGeometricOrder', name: 'debugDumpSemanticsTreeInTraversalOrder',
callback: () { debugDumpSemanticsTree(DebugSemanticsDumpOrder.geometricOrder); return debugPrintDone; } callback: () { debugDumpSemanticsTree(DebugSemanticsDumpOrder.traversalOrder); return debugPrintDone; }
); );
registerSignalServiceExtension( registerSignalServiceExtension(
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:math' as math;
import 'dart:typed_data'; import 'dart:typed_data';
import 'dart:ui' as ui; import 'dart:ui' as ui;
import 'dart:ui' show Offset, Rect, SemanticsAction, SemanticsFlag, import 'dart:ui' show Offset, Rect, SemanticsAction, SemanticsFlag,
...@@ -92,8 +93,6 @@ class SemanticsData extends Diagnosticable { ...@@ -92,8 +93,6 @@ class SemanticsData extends Diagnosticable {
@required this.decreasedValue, @required this.decreasedValue,
@required this.hint, @required this.hint,
@required this.textDirection, @required this.textDirection,
@required this.nextNodeId,
@required this.previousNodeId,
@required this.rect, @required this.rect,
@required this.textSelection, @required this.textSelection,
@required this.scrollPosition, @required this.scrollPosition,
...@@ -152,14 +151,6 @@ class SemanticsData extends Diagnosticable { ...@@ -152,14 +151,6 @@ class SemanticsData extends Diagnosticable {
/// [increasedValue], and [decreasedValue]. /// [increasedValue], and [decreasedValue].
final TextDirection textDirection; final TextDirection textDirection;
/// The index indicating the ID of the next node in the traversal order after
/// this node for the platform's accessibility services.
final int nextNodeId;
/// The index indicating the ID of the previous node in the traversal order before
/// this node for the platform's accessibility services.
final int previousNodeId;
/// The currently selected text (or the position of the cursor) within [value] /// The currently selected text (or the position of the cursor) within [value]
/// if this node represents a text field. /// if this node represents a text field.
final TextSelection textSelection; final TextSelection textSelection;
...@@ -242,8 +233,6 @@ class SemanticsData extends Diagnosticable { ...@@ -242,8 +233,6 @@ class SemanticsData extends Diagnosticable {
properties.add(new StringProperty('decreasedValue', decreasedValue, defaultValue: '')); properties.add(new StringProperty('decreasedValue', decreasedValue, defaultValue: ''));
properties.add(new StringProperty('hint', hint, defaultValue: '')); properties.add(new StringProperty('hint', hint, defaultValue: ''));
properties.add(new EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null)); properties.add(new EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
properties.add(new IntProperty('nextNodeId', nextNodeId, defaultValue: null));
properties.add(new IntProperty('previousNodeId', previousNodeId, defaultValue: null));
if (textSelection?.isValid == true) if (textSelection?.isValid == true)
properties.add(new MessageProperty('textSelection', '[${textSelection.start}, ${textSelection.end}]')); properties.add(new MessageProperty('textSelection', '[${textSelection.start}, ${textSelection.end}]'));
properties.add(new DoubleProperty('scrollExtentMin', scrollExtentMin, defaultValue: null)); properties.add(new DoubleProperty('scrollExtentMin', scrollExtentMin, defaultValue: null));
...@@ -264,8 +253,6 @@ class SemanticsData extends Diagnosticable { ...@@ -264,8 +253,6 @@ class SemanticsData extends Diagnosticable {
&& typedOther.decreasedValue == decreasedValue && typedOther.decreasedValue == decreasedValue
&& typedOther.hint == hint && typedOther.hint == hint
&& typedOther.textDirection == textDirection && typedOther.textDirection == textDirection
&& typedOther.nextNodeId == nextNodeId
&& typedOther.previousNodeId == previousNodeId
&& typedOther.rect == rect && typedOther.rect == rect
&& setEquals(typedOther.tags, tags) && setEquals(typedOther.tags, tags)
&& typedOther.textSelection == textSelection && typedOther.textSelection == textSelection
...@@ -276,7 +263,7 @@ class SemanticsData extends Diagnosticable { ...@@ -276,7 +263,7 @@ class SemanticsData extends Diagnosticable {
} }
@override @override
int get hashCode => ui.hashValues(flags, actions, label, value, increasedValue, decreasedValue, hint, textDirection, nextNodeId, previousNodeId, rect, tags, textSelection, scrollPosition, scrollExtentMax, scrollExtentMin, transform); int get hashCode => ui.hashValues(flags, actions, label, value, increasedValue, decreasedValue, hint, textDirection, rect, tags, textSelection, scrollPosition, scrollExtentMax, scrollExtentMin, transform);
} }
class _SemanticsDiagnosticableNode extends DiagnosticableNode<SemanticsNode> { class _SemanticsDiagnosticableNode extends DiagnosticableNode<SemanticsNode> {
...@@ -977,9 +964,9 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -977,9 +964,9 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
/// Visits the immediate children of this node. /// Visits the immediate children of this node.
/// ///
/// This function calls visitor for each child in a pre-order traversal /// This function calls visitor for each immediate child until visitor returns
/// until visitor returns false. Returns true if all the visitor calls /// false. Returns true if all the visitor calls returned true, otherwise
/// returned true, otherwise returns false. /// returns false.
void visitChildren(SemanticsNodeVisitor visitor) { void visitChildren(SemanticsNodeVisitor visitor) {
if (_children != null) { if (_children != null) {
for (SemanticsNode child in _children) { for (SemanticsNode child in _children) {
...@@ -1153,50 +1140,9 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -1153,50 +1140,9 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
/// This is used to describe the order in which the semantic node should be /// This is used to describe the order in which the semantic node should be
/// traversed by the accessibility services on the platform (e.g. VoiceOver /// traversed by the accessibility services on the platform (e.g. VoiceOver
/// on iOS and TalkBack on Android). /// on iOS and TalkBack on Android).
///
/// This is used to determine the [nextNodeId] and [previousNodeId] during a
/// semantics update.
SemanticsSortKey get sortKey => _sortKey; SemanticsSortKey get sortKey => _sortKey;
SemanticsSortKey _sortKey; SemanticsSortKey _sortKey;
/// The ID of the next node in the traversal order after this node.
///
/// Only valid after at least one semantics update has been built.
///
/// This is the value passed to the engine to tell it what the order
/// should be for traversing semantics nodes.
///
/// If this is set to -1, it will indicate that there is no next node to
/// the engine (i.e. this is the last node in the sort order). When it is
/// null, it means that no semantics update has been built yet.
int get nextNodeId => _nextNodeId;
int _nextNodeId;
void _updateNextNodeId(int value) {
if (value == _nextNodeId)
return;
_nextNodeId = value;
_markDirty();
}
/// The ID of the previous node in the traversal order before this node.
///
/// Only valid after at least one semantics update has been built.
///
/// This is the value passed to the engine to tell it what the order
/// should be for traversing semantics nodes.
///
/// If this is set to -1, it will indicate that there is no previous node to
/// the engine (i.e. this is the first node in the sort order). When it is
/// null, it means that no semantics update has been built yet.
int get previousNodeId => _previousNodeId;
int _previousNodeId;
void _updatePreviousNodeId(int value) {
if (value == _previousNodeId)
return;
_previousNodeId = value;
_markDirty();
}
/// The currently selected text (or the position of the cursor) within [value] /// The currently selected text (or the position of the cursor) within [value]
/// if this node represents a text field. /// if this node represents a text field.
TextSelection get textSelection => _textSelection; TextSelection get textSelection => _textSelection;
...@@ -1301,8 +1247,6 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -1301,8 +1247,6 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
String increasedValue = _increasedValue; String increasedValue = _increasedValue;
String decreasedValue = _decreasedValue; String decreasedValue = _decreasedValue;
TextDirection textDirection = _textDirection; TextDirection textDirection = _textDirection;
int nextNodeId = _nextNodeId;
int previousNodeId = _previousNodeId;
Set<SemanticsTag> mergedTags = tags == null ? null : new Set<SemanticsTag>.from(tags); Set<SemanticsTag> mergedTags = tags == null ? null : new Set<SemanticsTag>.from(tags);
TextSelection textSelection = _textSelection; TextSelection textSelection = _textSelection;
double scrollPosition = _scrollPosition; double scrollPosition = _scrollPosition;
...@@ -1315,8 +1259,6 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -1315,8 +1259,6 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
flags |= node._flags; flags |= node._flags;
actions |= node._actionsAsBits; actions |= node._actionsAsBits;
textDirection ??= node._textDirection; textDirection ??= node._textDirection;
nextNodeId ??= node._nextNodeId;
previousNodeId ??= node._previousNodeId;
textSelection ??= node._textSelection; textSelection ??= node._textSelection;
scrollPosition ??= node._scrollPosition; scrollPosition ??= node._scrollPosition;
scrollExtentMax ??= node._scrollExtentMax; scrollExtentMax ??= node._scrollExtentMax;
...@@ -1356,8 +1298,6 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -1356,8 +1298,6 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
decreasedValue: decreasedValue, decreasedValue: decreasedValue,
hint: hint, hint: hint,
textDirection: textDirection, textDirection: textDirection,
nextNodeId: nextNodeId,
previousNodeId: previousNodeId,
rect: rect, rect: rect,
transform: transform, transform: transform,
tags: mergedTags, tags: mergedTags,
...@@ -1382,10 +1322,11 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -1382,10 +1322,11 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
if (!hasChildren || mergeAllDescendantsIntoThisNode) { if (!hasChildren || mergeAllDescendantsIntoThisNode) {
children = _kEmptyChildList; children = _kEmptyChildList;
} else { } else {
final int childCount = _children.length; final List<SemanticsNode> sortedChildren = _childrenInTraversalOrder();
final int childCount = sortedChildren.length;
children = new Int32List(childCount); children = new Int32List(childCount);
for (int i = 0; i < childCount; ++i) { for (int i = 0; i < childCount; ++i) {
children[i] = _children[i].id; children[i] = sortedChildren[i].id;
} }
} }
builder.updateNode( builder.updateNode(
...@@ -1399,8 +1340,6 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -1399,8 +1340,6 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
increasedValue: data.increasedValue, increasedValue: data.increasedValue,
hint: data.hint, hint: data.hint,
textDirection: data.textDirection, textDirection: data.textDirection,
nextNodeId: data.nextNodeId,
previousNodeId: data.previousNodeId,
textSelectionBase: data.textSelection != null ? data.textSelection.baseOffset : -1, textSelectionBase: data.textSelection != null ? data.textSelection.baseOffset : -1,
textSelectionExtent: data.textSelection != null ? data.textSelection.extentOffset : -1, textSelectionExtent: data.textSelection != null ? data.textSelection.extentOffset : -1,
scrollPosition: data.scrollPosition != null ? data.scrollPosition : double.nan, scrollPosition: data.scrollPosition != null ? data.scrollPosition : double.nan,
...@@ -1412,6 +1351,68 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -1412,6 +1351,68 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
_dirty = false; _dirty = false;
} }
/// Builds a new list made of [_children] sorted in semantic traversal order.
List<SemanticsNode> _childrenInTraversalOrder() {
TextDirection inheritedTextDirection = textDirection;
SemanticsNode ancestor = parent;
while (inheritedTextDirection == null && ancestor != null) {
inheritedTextDirection = ancestor.textDirection;
ancestor = ancestor.parent;
}
List<SemanticsNode> childrenInDefaultOrder;
if (inheritedTextDirection != null) {
childrenInDefaultOrder = _childrenInDefaultOrder(_children, inheritedTextDirection);
} else {
// In the absence of text direction default to paint order.
childrenInDefaultOrder = _children;
}
// List.sort does not guarantee stable sort order. Therefore, children are
// first partitioned into groups that have compatible sort keys, i.e. keys
// in the same group can be compared to each other. These groups stay in
// the same place. Only children within the same group are sorted.
final List<_TraversalSortNode> everythingSorted = <_TraversalSortNode>[];
final List<_TraversalSortNode> sortNodes = <_TraversalSortNode>[];
SemanticsSortKey lastSortKey;
for (int position = 0; position < childrenInDefaultOrder.length; position += 1) {
final SemanticsNode child = childrenInDefaultOrder[position];
final SemanticsSortKey sortKey = child.sortKey;
lastSortKey = position > 0
? childrenInDefaultOrder[position - 1].sortKey
: null;
final bool isCompatibleWithPreviousSortKey = position == 0 ||
sortKey.runtimeType == lastSortKey.runtimeType &&
(sortKey == null || sortKey.name == lastSortKey.name);
if (!isCompatibleWithPreviousSortKey && sortNodes.isNotEmpty) {
// Do not sort groups with null sort keys. List.sort does not guarantee
// a stable sort order.
if (lastSortKey != null) {
sortNodes.sort();
}
everythingSorted.addAll(sortNodes);
sortNodes.clear();
}
sortNodes.add(new _TraversalSortNode(
node: child,
sortKey: sortKey,
position: position,
));
}
// Do not sort groups with null sort keys. List.sort does not guarantee
// a stable sort order.
if (lastSortKey != null) {
sortNodes.sort();
}
everythingSorted.addAll(sortNodes);
return everythingSorted
.map<SemanticsNode>((_TraversalSortNode sortNode) => sortNode.node)
.toList();
}
/// Sends a [SemanticsEvent] associated with this [SemanticsNode]. /// Sends a [SemanticsEvent] associated with this [SemanticsNode].
/// ///
/// Semantics events should be sent to inform interested parties (like /// Semantics events should be sent to inform interested parties (like
...@@ -1468,8 +1469,6 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -1468,8 +1469,6 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
properties.add(new StringProperty('decreasedValue', _decreasedValue, defaultValue: '')); properties.add(new StringProperty('decreasedValue', _decreasedValue, defaultValue: ''));
properties.add(new StringProperty('hint', _hint, defaultValue: '')); properties.add(new StringProperty('hint', _hint, defaultValue: ''));
properties.add(new EnumProperty<TextDirection>('textDirection', _textDirection, defaultValue: null)); properties.add(new EnumProperty<TextDirection>('textDirection', _textDirection, defaultValue: null));
properties.add(new IntProperty('nextNodeId', _nextNodeId, defaultValue: null));
properties.add(new IntProperty('previousNodeId', _previousNodeId, defaultValue: null));
properties.add(new DiagnosticsProperty<SemanticsSortKey>('sortKey', sortKey, defaultValue: null)); properties.add(new DiagnosticsProperty<SemanticsSortKey>('sortKey', sortKey, defaultValue: null));
if (_textSelection?.isValid == true) if (_textSelection?.isValid == true)
properties.add(new MessageProperty('text selection', '[${_textSelection.start}, ${_textSelection.end}]')); properties.add(new MessageProperty('text selection', '[${_textSelection.start}, ${_textSelection.end}]'));
...@@ -1487,7 +1486,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -1487,7 +1486,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
String prefixLineOne: '', String prefixLineOne: '',
String prefixOtherLines, String prefixOtherLines,
DiagnosticLevel minLevel: DiagnosticLevel.debug, DiagnosticLevel minLevel: DiagnosticLevel.debug,
DebugSemanticsDumpOrder childOrder: DebugSemanticsDumpOrder.geometricOrder, DebugSemanticsDumpOrder childOrder: DebugSemanticsDumpOrder.traversalOrder,
}) { }) {
assert(childOrder != null); assert(childOrder != null);
return toDiagnosticsNode(childOrder: childOrder).toStringDeep(prefixLineOne: prefixLineOne, prefixOtherLines: prefixOtherLines, minLevel: minLevel); return toDiagnosticsNode(childOrder: childOrder).toStringDeep(prefixLineOne: prefixLineOne, prefixOtherLines: prefixOtherLines, minLevel: minLevel);
...@@ -1497,7 +1496,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -1497,7 +1496,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
DiagnosticsNode toDiagnosticsNode({ DiagnosticsNode toDiagnosticsNode({
String name, String name,
DiagnosticsTreeStyle style: DiagnosticsTreeStyle.sparse, DiagnosticsTreeStyle style: DiagnosticsTreeStyle.sparse,
DebugSemanticsDumpOrder childOrder: DebugSemanticsDumpOrder.geometricOrder, DebugSemanticsDumpOrder childOrder: DebugSemanticsDumpOrder.traversalOrder,
}) { }) {
return new _SemanticsDiagnosticableNode( return new _SemanticsDiagnosticableNode(
name: name, name: name,
...@@ -1509,120 +1508,324 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -1509,120 +1508,324 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
@override @override
List<DiagnosticsNode> debugDescribeChildren({ DebugSemanticsDumpOrder childOrder: DebugSemanticsDumpOrder.inverseHitTest }) { List<DiagnosticsNode> debugDescribeChildren({ DebugSemanticsDumpOrder childOrder: DebugSemanticsDumpOrder.inverseHitTest }) {
return _getChildrenInOrder(childOrder) return debugListChildrenInOrder(childOrder)
.map<DiagnosticsNode>((SemanticsNode node) => node.toDiagnosticsNode(childOrder: childOrder)) .map<DiagnosticsNode>((SemanticsNode node) => node.toDiagnosticsNode(childOrder: childOrder))
.toList(); .toList();
} }
Iterable<SemanticsNode> _getChildrenInOrder(DebugSemanticsDumpOrder childOrder) { /// Returns the list of direct children of this node in the specified order.
List<SemanticsNode> debugListChildrenInOrder(DebugSemanticsDumpOrder childOrder) {
assert(childOrder != null); assert(childOrder != null);
if (_children == null) if (_children == null)
return const <SemanticsNode>[]; return const <SemanticsNode>[];
switch (childOrder) { switch (childOrder) {
case DebugSemanticsDumpOrder.geometricOrder:
return new List<SemanticsNode>.from(_children)..sort(_geometryComparator);
case DebugSemanticsDumpOrder.inverseHitTest: case DebugSemanticsDumpOrder.inverseHitTest:
return _children; return _children;
case DebugSemanticsDumpOrder.traversalOrder:
return _childrenInTraversalOrder();
} }
assert(false); assert(false);
return null; return null;
} }
}
static int _geometryComparator(SemanticsNode a, SemanticsNode b) { /// An edge of a box, such as top, bottom, left or right, used to compute
final Rect rectA = a.transform == null ? a.rect : MatrixUtils.transformRect(a.transform, a.rect); /// [SemanticsNode]s that overlap vertically or horizontally.
final Rect rectB = b.transform == null ? b.rect : MatrixUtils.transformRect(b.transform, b.rect); ///
final int top = rectA.top.compareTo(rectB.top); /// For computing horizontal overlap in an LTR setting we create two [_BoxEdge]
return top == 0 ? rectA.left.compareTo(rectB.left) : top; /// objects for each [SemanticsNode]: one representing the left edge (marked
/// with [isLeadingEdge] equal to true) and one for the right edge (with [isLeadingEdge]
/// equal to false). Similarly, for vertical overlap we also create two objects
/// for each [SemanticsNode], one for the top and one for the bottom edge.
class _BoxEdge implements Comparable<_BoxEdge> {
_BoxEdge({
@required this.isLeadingEdge,
@required this.offset,
@required this.node,
}) : assert(isLeadingEdge != null),
assert(offset != null),
assert(node != null);
/// True if the edge comes before the seconds edge along the traversal
/// direction, and false otherwise.
///
/// This field is never null.
///
/// For example, in LTR traversal the left edge's [isLeadingEdge] is set to true,
/// the right edge's [isLeadingEdge] is set to false. When considering vertical
/// ordering of boxes, the top edge is the start edge, and the bottom edge is
/// the end edge.
final bool isLeadingEdge;
/// The offset from the start edge of the parent [SemanticsNode] in the
/// direction of the traversal.
final double offset;
/// The node whom this edge belongs.
final SemanticsNode node;
@override
int compareTo(_BoxEdge other) {
return (offset - other.offset).sign.toInt();
} }
} }
/// A group of [nodes] that are disjoint vertically or horizontally from other
/// nodes that share the same [SemanticsNode] parent.
///
/// The [nodes] are sorted among each other separately from other nodes.
class _SemanticsSortGroup extends Comparable<_SemanticsSortGroup> {
_SemanticsSortGroup({
@required this.startOffset,
@required this.textDirection,
}) : assert(startOffset != null);
/// The offset from the start edge of the parent [SemanticsNode] in the
/// direction of the traversal.
///
/// This value is equal to the [_BoxEdge.offset] of the first node in the
/// [nodes] list being considered.
final double startOffset;
final TextDirection textDirection;
/// The nodes that are sorted among each other.
final List<SemanticsNode> nodes = <SemanticsNode>[];
@override
int compareTo(_SemanticsSortGroup other) {
return (startOffset - other.startOffset).sign.toInt();
}
/// Sorts this group assuming that [nodes] belong to the same vertical group.
///
/// This method breaks up this group into horizontal [_SemanticsSortGroup]s
/// then sorts them using [sortedWithinKnot].
List<SemanticsNode> sortedWithinVerticalGroup() {
final List<_BoxEdge> edges = <_BoxEdge>[];
for (SemanticsNode child in nodes) {
edges.add(new _BoxEdge(
isLeadingEdge: true,
offset: _pointInParentCoordinates(child, child.rect.topLeft).dx,
node: child,
));
edges.add(new _BoxEdge(
isLeadingEdge: false,
offset: _pointInParentCoordinates(child, child.rect.bottomRight).dx,
node: child,
));
}
edges.sort();
List<_SemanticsSortGroup> horizontalGroups = <_SemanticsSortGroup>[];
_SemanticsSortGroup group;
int depth = 0;
for (_BoxEdge edge in edges) {
if (edge.isLeadingEdge) {
depth += 1;
group ??= new _SemanticsSortGroup(
startOffset: edge.offset,
textDirection: textDirection,
);
group.nodes.add(edge.node);
} else {
depth -= 1;
}
if (depth == 0) {
horizontalGroups.add(group);
group = null;
}
}
horizontalGroups.sort();
if (textDirection == TextDirection.rtl) {
horizontalGroups = horizontalGroups.reversed.toList();
}
final List<SemanticsNode> result = <SemanticsNode>[];
for (_SemanticsSortGroup group in horizontalGroups) {
final List<SemanticsNode> sortedKnotNodes = group.sortedWithinKnot();
result.addAll(sortedKnotNodes);
}
return result;
}
/// Sorts [nodes] where nodes intersect both vertically and horizontally.
///
/// In the special case when [nodes] contains one or less nodes, this method
/// returns [nodes] unchanged.
///
/// This method constructs a graph, where vertices are [SemanticsNode]s and
/// edges are "traversed before" relation between pairs of nodes. The sort
/// order is the topological sorting of the graph, with the original order of
/// [nodes] used as the tie breaker.
///
/// Whether a node is traversed before another node is determined by the
/// vector that connects the two nodes' centers. If the vector "points to the
/// right or down", defined as the [Offset.direction] being between `-pi/4`
/// and `3*pi/4`), then the semantics node whose center is at the end of the
/// vector is said to be traversed after.
List<SemanticsNode> sortedWithinKnot() {
if (nodes.length <= 1) {
// Trivial knot. Nothing to do.
return nodes;
}
final Map<int, SemanticsNode> nodeMap = <int, SemanticsNode>{};
final Map<int, int> edges = <int, int>{};
for (SemanticsNode node in nodes) {
nodeMap[node.id] = node;
final Offset center = _pointInParentCoordinates(node, node.rect.center);
for (SemanticsNode nextNode in nodes) {
if (identical(node, nextNode) || edges[nextNode.id] == node.id) {
// Skip self or when we've already established that the next node
// points to current node.
continue;
}
final Offset nextCenter = _pointInParentCoordinates(nextNode, nextNode.rect.center);
final Offset centerDelta = nextCenter - center;
// When centers coincide, direction is 0.0.
final double direction = centerDelta.direction;
final bool isLtrAndForward = textDirection == TextDirection.ltr &&
-math.pi / 4 < direction && direction < 3 * math.pi / 4;
final bool isRtlAndForward = textDirection == TextDirection.rtl &&
(direction < -3 * math.pi / 4 || direction > 3 * math.pi / 4);
if (isLtrAndForward || isRtlAndForward) {
edges[node.id] = nextNode.id;
}
}
}
final List<int> sortedIds = <int>[];
final Set<int> visitedIds = new Set<int>();
final List<SemanticsNode> startNodes = nodes.toList()..sort((SemanticsNode a, SemanticsNode b) {
final Offset aTopLeft = _pointInParentCoordinates(a, a.rect.topLeft);
final Offset bTopLeft = _pointInParentCoordinates(b, b.rect.topLeft);
final int verticalDiff = aTopLeft.dy.compareTo(bTopLeft.dy);
if (verticalDiff != 0) {
return -verticalDiff;
}
return -aTopLeft.dx.compareTo(bTopLeft.dx);
});
void search(int id) {
if (visitedIds.contains(id)) {
return;
}
visitedIds.add(id);
if (edges.containsKey(id)) {
search(edges[id]);
}
sortedIds.add(id);
}
startNodes.map((SemanticsNode node) => node.id).forEach(search);
return sortedIds.map<SemanticsNode>((int id) => nodeMap[id]).toList().reversed.toList();
}
}
/// Converts `point` to the `node`'s parent's coordinate system.
Offset _pointInParentCoordinates(SemanticsNode node, Offset point) {
if (node.transform == null) {
return point;
}
final Vector3 vector = new Vector3(point.dx, point.dy, 0.0);
node.transform.transform3(vector);
return new Offset(vector.x, vector.y);
}
/// Sorts `children` using the default sorting algorithm, and returns them as a
/// new list.
///
/// The algorithm first breaks up children into groups such that no two nodes
/// from different groups overlap vertically. These groups are sorted vertically
/// according to their [_SemanticsSortGroup.startOffset].
///
/// Within each group, the nodes are sorted using
/// [_SemanticsSortGroup.sortedWithinVerticalGroup].
///
/// For an illustration of the algorithm see http://bit.ly/flutter-default-traversal.
List<SemanticsNode> _childrenInDefaultOrder(List<SemanticsNode> children, TextDirection textDirection) {
final List<_BoxEdge> edges = <_BoxEdge>[];
for (SemanticsNode child in children) {
edges.add(new _BoxEdge(
isLeadingEdge: true,
offset: _pointInParentCoordinates(child, child.rect.topLeft).dy,
node: child,
));
edges.add(new _BoxEdge(
isLeadingEdge: false,
offset: _pointInParentCoordinates(child, child.rect.bottomRight).dy,
node: child,
));
}
edges.sort();
final List<_SemanticsSortGroup> verticalGroups = <_SemanticsSortGroup>[];
_SemanticsSortGroup group;
int depth = 0;
for (_BoxEdge edge in edges) {
if (edge.isLeadingEdge) {
depth += 1;
group ??= new _SemanticsSortGroup(
startOffset: edge.offset,
textDirection: textDirection,
);
group.nodes.add(edge.node);
} else {
depth -= 1;
}
if (depth == 0) {
verticalGroups.add(group);
group = null;
}
}
verticalGroups.sort();
final List<SemanticsNode> result = <SemanticsNode>[];
for (_SemanticsSortGroup group in verticalGroups) {
final List<SemanticsNode> sortedGroupNodes = group.sortedWithinVerticalGroup();
result.addAll(sortedGroupNodes);
}
return result;
}
/// The implementation of [Comparable] that implements the ordering of /// The implementation of [Comparable] that implements the ordering of
/// [SemanticsNode]s in the accessibility traversal. /// [SemanticsNode]s in the accessibility traversal.
/// ///
/// [SemanticsNode]s are sorted prior to sending them to the engine side. /// [SemanticsNode]s are sorted prior to sending them to the engine side.
/// ///
/// This implementation considers a [node]'s [sortKey], it's parent's text /// This implementation considers a [node]'s [sortKey] and its position within
/// direction ([containerTextDirection]), and its geometric position relative to /// the list of its siblings. [sortKey] takes precedence over position.
/// its siblings ([globalStartCorner]).
///
/// A null value is allowed for [containerTextDirection], because in that case
/// we want to fall back to ordering by child insertion order for nodes that are
/// equal after sorting from top to bottom.
class _TraversalSortNode implements Comparable<_TraversalSortNode> { class _TraversalSortNode implements Comparable<_TraversalSortNode> {
_TraversalSortNode({@required this.node, this.containerTextDirection, this.sortKey, Matrix4 transform}) _TraversalSortNode({
@required this.node,
this.sortKey,
@required this.position,
})
: assert(node != null), : assert(node != null),
// When containerTextDirection is null, this is set to topLeft, but the x assert(position != null);
// coordinate is also ignored when doing the comparison in that case, so
// this isn't actually expressing a directionality opinion.
globalStartCorner = _transformPoint(
containerTextDirection == TextDirection.rtl ? node.rect.topRight : node.rect.topLeft,
transform,
);
/// The node whose position this sort node determines. /// The node whose position this sort node determines.
final SemanticsNode node; final SemanticsNode node;
/// The effective text direction for this node is the directionality that
/// its container has.
final TextDirection containerTextDirection;
/// Determines the position of this node among its siblings. /// Determines the position of this node among its siblings.
/// ///
/// Sort keys take precedence over other attributes, such as /// Sort keys take precedence over other attributes, such as
/// [globalStartCorner]. /// [position].
final SemanticsSortKey sortKey; final SemanticsSortKey sortKey;
/// The starting corner for the rectangle on this semantics node in /// Position within the list of siblings as determined by the default sort
/// global coordinates. /// order.
/// final int position;
/// When the container has the directionality [TextDirection.ltr], this is the
/// upper left corner. When the container has the directionality
/// [TextDirection.rtl], this is the upper right corner. When the container
/// has no directionality, this is set, but the x coordinate is ignored.
final Offset globalStartCorner;
static Offset _transformPoint(Offset point, Matrix4 matrix) {
final Vector3 result = matrix.transform3(new Vector3(point.dx, point.dy, 0.0));
return new Offset(result.x, result.y);
}
/// Compares the node's start corner with that of `other`.
///
/// Sorts top to bottom, and then start to end.
///
/// This takes into account the container text direction, since the
/// coordinate system has zero on the left, and we need to compare
/// differently for different text directions.
///
/// If no text direction is available (i.e. [containerTextDirection] is
/// null), then we sort by vertical position first, and then by child
/// insertion order.
int _compareGeometry(_TraversalSortNode other) {
final int verticalDiff = globalStartCorner.dy.compareTo(other.globalStartCorner.dy);
if (verticalDiff != 0) {
return verticalDiff;
}
switch (containerTextDirection) {
case TextDirection.rtl:
return other.globalStartCorner.dx.compareTo(globalStartCorner.dx);
case TextDirection.ltr:
return globalStartCorner.dx.compareTo(other.globalStartCorner.dx);
}
// In case containerTextDirection is null we fall back to child insertion order.
return 0;
}
@override @override
int compareTo(_TraversalSortNode other) { int compareTo(_TraversalSortNode other) {
if (sortKey == null || other?.sortKey == null) { if (sortKey == null || other?.sortKey == null) {
return _compareGeometry(other); return position - other.position;
}
final int comparison = sortKey.compareTo(other.sortKey);
if (comparison != 0) {
return comparison;
} }
return _compareGeometry(other); return sortKey.compareTo(other.sortKey);
} }
} }
...@@ -1650,58 +1853,10 @@ class SemanticsOwner extends ChangeNotifier { ...@@ -1650,58 +1853,10 @@ class SemanticsOwner extends ChangeNotifier {
super.dispose(); super.dispose();
} }
// Updates the nextNodeId and previousNodeId IDs on the semantics nodes. These
// IDs are used on the platform side to order the nodes for traversal by the
// accessibility services. If the nextNodeId or previousNodeId for a node
// changes, the node will be marked as dirty.
void _updateTraversalOrder() {
void updateRecursively(SemanticsNode parent, Matrix4 parentGlobalTransform) {
assert(parentGlobalTransform != null);
final List<_TraversalSortNode> children = <_TraversalSortNode>[];
parent.visitChildren((SemanticsNode child) {
final Matrix4 childGlobalTransform = child.transform != null
? parentGlobalTransform.multiplied(child.transform)
: parentGlobalTransform;
children.add(new _TraversalSortNode(
node: child,
containerTextDirection: parent.textDirection,
sortKey: child.sortKey,
transform: childGlobalTransform,
));
updateRecursively(child, childGlobalTransform);
return true;
});
if (children.isEmpty) {
// We need at least one node for the following code to work.
return;
}
children.sort();
_TraversalSortNode node = children.removeLast();
node.node._updateNextNodeId(-1);
while (children.isNotEmpty) {
final _TraversalSortNode previousNode = children.removeLast();
node.node._updatePreviousNodeId(previousNode.node.id);
previousNode.node._updateNextNodeId(node.node.id);
node = previousNode;
}
node.node._updatePreviousNodeId(-1);
}
updateRecursively(rootSemanticsNode, new Matrix4.identity());
}
/// Update the semantics using [Window.updateSemantics]. /// Update the semantics using [Window.updateSemantics].
void sendSemanticsUpdate() { void sendSemanticsUpdate() {
if (_dirtyNodes.isEmpty) if (_dirtyNodes.isEmpty)
return; return;
// Nodes that change their previousNodeId will be marked as dirty.
_updateTraversalOrder();
final List<SemanticsNode> visitedNodes = <SemanticsNode>[]; final List<SemanticsNode> visitedNodes = <SemanticsNode>[];
while (_dirtyNodes.isNotEmpty) { while (_dirtyNodes.isNotEmpty) {
final List<SemanticsNode> localDirtyNodes = _dirtyNodes.where((SemanticsNode node) => !_detachedNodes.contains(node)).toList(); final List<SemanticsNode> localDirtyNodes = _dirtyNodes.where((SemanticsNode node) => !_detachedNodes.contains(node)).toList();
...@@ -2714,17 +2869,11 @@ enum DebugSemanticsDumpOrder { ...@@ -2714,17 +2869,11 @@ enum DebugSemanticsDumpOrder {
/// the second last, etc. until a taker is found. /// the second last, etc. until a taker is found.
inverseHitTest, inverseHitTest,
/// Print nodes in geometric traversal order. /// Print nodes in semantic traversal order.
/// ///
/// Geometric traversal order is the default traversal order for semantics nodes which /// This is the order in which a user would navigate the UI using the "next"
/// don't have [SemanticsNode.sortOrder] set. This traversal order ignores the node /// and "previous" gestures.
/// sort order, since the diagnostics system follows the widget tree and can only sort traversalOrder,
/// a node's children, and the semantics system sorts nodes globally.
geometricOrder,
// TODO(gspencer): Add support to toStringDeep (and others) to print the tree in
// the actual traversal order that the user will experience. This requires sorting
// nodes globally before printing, not just the children.
} }
String _concatStrings({ String _concatStrings({
...@@ -2754,9 +2903,27 @@ String _concatStrings({ ...@@ -2754,9 +2903,27 @@ String _concatStrings({
/// Base class for all sort keys for [Semantics] accessibility traversal order /// Base class for all sort keys for [Semantics] accessibility traversal order
/// sorting. /// sorting.
/// ///
/// If subclasses of this class compare themselves to another subclass of /// Only keys of the same type and having matching [name]s are compared. If a
/// [SemanticsSortKey], they will compare as "equal" so that keys of the same /// list of sibling [SemanticsNode]s contains keys that are not comparable with
/// type are ordered only with respect to one another. /// each other the list is first sorted using the default sorting algorithm.
/// Then the nodes are broken down into groups by moving comparable nodes
/// towards the _earliest_ node in the group. Finally each group is sorted by
/// sort key and the resulting list is made by concatenating the sorted groups
/// back.
///
/// For example, let's take nodes (C, D, B, E, A, F). Let's assign node A key 1,
/// node B key 2, node C key 3. Let's also assume that the default sort order
/// leaves the original list intact. Because nodes A, B, and C, have comparable
/// sort key, they will form a group by pulling all nodes towards the earliest
/// node, which is C. The result is group (C, B, A). The remaining nodes D, E,
/// F, form a second group with sort key being `null`. The first group is sorted
/// using their sort keys becoming (A, B, C). The second group is left as is
/// because it does not specify sort keys. Then we concatenate the two groups -
/// (A, B, C) and (D, E, F) - into the final (A, B, C, D, E, F).
///
/// Because of the complexity introduced by incomparable sort keys among sibling
/// nodes, it is recommended to either use comparable keys for all nodes, or
/// use null for all of them, leaving the sort order to the default algorithm.
/// ///
/// See Also: /// See Also:
/// ///
...@@ -2769,26 +2936,26 @@ abstract class SemanticsSortKey extends Diagnosticable implements Comparable<Sem ...@@ -2769,26 +2936,26 @@ abstract class SemanticsSortKey extends Diagnosticable implements Comparable<Sem
/// An optional name that will make this sort key only order itself /// An optional name that will make this sort key only order itself
/// with respect to other sort keys of the same [name], as long as /// with respect to other sort keys of the same [name], as long as
/// they are of the same [runtimeType]. If compared with a /// they are of the same [runtimeType].
/// [SemanticsSortKey] with a different name or type, they will
/// compare as "equal".
final String name; final String name;
@override @override
int compareTo(SemanticsSortKey other) { int compareTo(SemanticsSortKey other) {
if (other.runtimeType != runtimeType || other.name != name) // The sorting algorithm must not compare incomparable keys.
return 0; assert(runtimeType == other.runtimeType);
assert(name == other.name);
return doCompare(other); return doCompare(other);
} }
/// The implementation of [compareTo]. /// The implementation of [compareTo].
/// ///
/// The argument is guaranteed to be of the same type as this object. /// The argument is guaranteed to be of the same type as this object and have
/// the same [name].
/// ///
/// The method should return a negative number if this object comes earlier in /// The method should return a negative number if this object comes earlier in
/// the sort order than the argument; and a positive number if it comes later /// the sort order than the argument; and a positive number if it comes later
/// in the sort order. Returning zero causes the system to default to /// in the sort order. Returning zero causes the system to use default sort
/// comparing the geometry of the nodes. /// order.
@protected @protected
int doCompare(covariant SemanticsSortKey other); int doCompare(covariant SemanticsSortKey other);
......
...@@ -602,7 +602,7 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv ...@@ -602,7 +602,7 @@ class _WidgetsAppState extends State<WidgetsApp> implements WidgetsBindingObserv
if (widget.onGenerateTitle != null) { if (widget.onGenerateTitle != null) {
title = new Builder( title = new Builder(
// This Builder exists to provide a context below the Localizations widget. // This Builder exists to provide a context below the Localizations widget.
// The onGenerateCallback() can refer to Localizations via its context // The onGenerateTitle callback can refer to Localizations via its context
// parameter. // parameter.
builder: (BuildContext context) { builder: (BuildContext context) {
final String title = widget.onGenerateTitle(context); final String title = widget.onGenerateTitle(context);
......
...@@ -65,11 +65,11 @@ class ModalBarrier extends StatelessWidget { ...@@ -65,11 +65,11 @@ class ModalBarrier extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
assert(!dismissible || semanticsLabel == null || debugCheckHasDirectionality(context)); assert(!dismissible || semanticsLabel == null || debugCheckHasDirectionality(context));
final bool semanticsDismissable = dismissible && defaultTargetPlatform != TargetPlatform.android; final bool semanticsDismissible = dismissible && defaultTargetPlatform != TargetPlatform.android;
return new BlockSemantics( return new BlockSemantics(
child: new ExcludeSemantics( child: new ExcludeSemantics(
// On Android, the back button is used to dismiss a modal. // On Android, the back button is used to dismiss a modal.
excluding: !semanticsDismissable, excluding: !semanticsDismissible,
child: new GestureDetector( child: new GestureDetector(
onTapDown: (TapDownDetails details) { onTapDown: (TapDownDetails details) {
if (dismissible) if (dismissible)
...@@ -77,8 +77,8 @@ class ModalBarrier extends StatelessWidget { ...@@ -77,8 +77,8 @@ class ModalBarrier extends StatelessWidget {
}, },
behavior: HitTestBehavior.opaque, behavior: HitTestBehavior.opaque,
child: new Semantics( child: new Semantics(
label: semanticsDismissable ? semanticsLabel : null, label: semanticsDismissible ? semanticsLabel : null,
textDirection: semanticsDismissable && semanticsLabel != null ? Directionality.of(context) : null, textDirection: semanticsDismissible && semanticsLabel != null ? Directionality.of(context) : null,
child: new ConstrainedBox( child: new ConstrainedBox(
constraints: const BoxConstraints.expand(), constraints: const BoxConstraints.expand(),
child: color == null ? null : new DecoratedBox( child: color == null ? null : new DecoratedBox(
......
...@@ -102,8 +102,10 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics { ...@@ -102,8 +102,10 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics {
/// create scroll positions and initialize this property. /// create scroll positions and initialize this property.
final bool keepScrollOffset; final bool keepScrollOffset;
/// A label that is used in the [toString] output. Intended to aid with /// A label that is used in the [toString] output.
/// identifying animation controller instances in debug output. ///
/// Intended to aid with identifying animation controller instances in debug
/// output.
final String debugLabel; final String debugLabel;
@override @override
...@@ -123,10 +125,9 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics { ...@@ -123,10 +125,9 @@ abstract class ScrollPosition extends ViewportOffset with ScrollMetrics {
double _viewportDimension; double _viewportDimension;
/// Whether [viewportDimension], [minScrollExtent], [maxScrollExtent], /// Whether [viewportDimension], [minScrollExtent], [maxScrollExtent],
/// [outOfRange], and [atEdge] are available yet. /// [outOfRange], and [atEdge] are available.
/// ///
/// Set to true just before the first time that [applyNewDimensions] is /// Set to true just before the first time [applyNewDimensions] is called.
/// called.
bool get haveDimensions => _haveDimensions; bool get haveDimensions => _haveDimensions;
bool _haveDimensions = false; bool _haveDimensions = false;
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:ui' show SemanticsFlag;
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/scheduler.dart'; import 'package:flutter/scheduler.dart';
......
...@@ -193,11 +193,11 @@ void main() { ...@@ -193,11 +193,11 @@ void main() {
console.clear(); console.clear();
}); });
test('Service extensions - debugDumpSemanticsTreeInGeometricOrder', () async { test('Service extensions - debugDumpSemanticsTreeInTraversalOrder', () async {
Map<String, dynamic> result; Map<String, dynamic> result;
await binding.doFrame(); await binding.doFrame();
result = await binding.testExtension('debugDumpSemanticsTreeInGeometricOrder', <String, String>{}); result = await binding.testExtension('debugDumpSemanticsTreeInTraversalOrder', <String, String>{});
expect(result, <String, String>{}); expect(result, <String, String>{});
expect(console, <String>['Semantics not collected.']); expect(console, <String>['Semantics not collected.']);
console.clear(); console.clear();
......
...@@ -7,6 +7,8 @@ import 'package:flutter/material.dart'; ...@@ -7,6 +7,8 @@ import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import '../widgets/semantics_tester.dart';
Widget buildSliverAppBarApp({ bool floating, bool pinned, double expandedHeight, bool snap: false }) { Widget buildSliverAppBarApp({ bool floating, bool pinned, double expandedHeight, bool snap: false }) {
return new Localizations( return new Localizations(
locale: const Locale('en', 'US'), locale: const Locale('en', 'US'),
...@@ -65,6 +67,10 @@ double appBarBottom(WidgetTester tester) => tester.getBottomLeft(find.byType(App ...@@ -65,6 +67,10 @@ double appBarBottom(WidgetTester tester) => tester.getBottomLeft(find.byType(App
double tabBarHeight(WidgetTester tester) => tester.getSize(find.byType(TabBar)).height; double tabBarHeight(WidgetTester tester) => tester.getSize(find.byType(TabBar)).height;
void main() { void main() {
setUp(() {
debugResetSemanticsIdCounter();
});
testWidgets('AppBar centers title on iOS', (WidgetTester tester) async { testWidgets('AppBar centers title on iOS', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
new MaterialApp( new MaterialApp(
...@@ -1182,4 +1188,155 @@ void main() { ...@@ -1182,4 +1188,155 @@ void main() {
expect(tester.getRect(find.byKey(leadingKey)), new Rect.fromLTRB(800.0 - 56.0, 100.0, 800.0, 100.0 + 56.0)); expect(tester.getRect(find.byKey(leadingKey)), new Rect.fromLTRB(800.0 - 56.0, 100.0, 800.0, 100.0 + 56.0));
expect(tester.getRect(find.byKey(trailingKey)), new Rect.fromLTRB(4.0, 100.0, 400.0 + 4.0, 100.0 + 56.0)); expect(tester.getRect(find.byKey(trailingKey)), new Rect.fromLTRB(4.0, 100.0, 400.0 + 4.0, 100.0 + 56.0));
}); });
testWidgets('SliverAppBar provides correct semantics in LTR', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
await tester.pumpWidget(
new MaterialApp(
home: new Center(
child: new AppBar(
leading: const Text('Leading'),
title: const Text('Title'),
actions: const <Widget>[
const Text('Action 1'),
const Text('Action 2'),
const Text('Action 3'),
],
bottom: const PreferredSize(
preferredSize: const Size(0.0, kToolbarHeight),
child: const Text('Bottom'),
),
),
),
),
);
expect(semantics, hasSemantics(
new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.scopesRoute],
children: <TestSemantics>[
new TestSemantics(
children: <TestSemantics>[
new TestSemantics(
label: 'Leading',
textDirection: TextDirection.ltr,
),
new TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.namesRoute],
label: 'Title',
textDirection: TextDirection.ltr,
),
new TestSemantics(
label: 'Action 1',
textDirection: TextDirection.ltr,
),
new TestSemantics(
label: 'Action 2',
textDirection: TextDirection.ltr,
),
new TestSemantics(
label: 'Action 3',
textDirection: TextDirection.ltr,
),
new TestSemantics(
label: 'Bottom',
textDirection: TextDirection.ltr,
),
],
),
],
),
],
),
ignoreRect: true,
ignoreTransform: true,
ignoreId: true,
));
semantics.dispose();
});
testWidgets('SliverAppBar provides correct semantics in RTL', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
await tester.pumpWidget(
new MaterialApp(
home: new Semantics(
textDirection: TextDirection.rtl,
child: new Directionality(
textDirection: TextDirection.rtl,
child: new Center(
child: new AppBar(
leading: const Text('Leading'),
title: const Text('Title'),
actions: const <Widget>[
const Text('Action 1'),
const Text('Action 2'),
const Text('Action 3'),
],
bottom: const PreferredSize(
preferredSize: const Size(0.0, kToolbarHeight),
child: const Text('Bottom'),
),
),
),
),
),
),
);
expect(semantics, hasSemantics(
new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.scopesRoute],
children: <TestSemantics>[
new TestSemantics(
textDirection: TextDirection.rtl,
children: <TestSemantics>[
new TestSemantics(
children: <TestSemantics>[
new TestSemantics(
label: 'Leading',
textDirection: TextDirection.rtl,
),
new TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.namesRoute],
label: 'Title',
textDirection: TextDirection.rtl,
),
new TestSemantics(
label: 'Action 1',
textDirection: TextDirection.rtl,
),
new TestSemantics(
label: 'Action 2',
textDirection: TextDirection.rtl,
),
new TestSemantics(
label: 'Action 3',
textDirection: TextDirection.rtl,
),
new TestSemantics(
label: 'Bottom',
textDirection: TextDirection.rtl,
),
],
),
],
),
],
),
],
),
ignoreRect: true,
ignoreTransform: true,
ignoreId: true,
));
semantics.dispose();
});
} }
...@@ -512,32 +512,31 @@ void main() { ...@@ -512,32 +512,31 @@ void main() {
), ),
); );
// TODO(goderbauer): traversal order is incorrect, https://github.com/flutter/flutter/issues/14375
final TestSemantics expected = new TestSemantics.root( final TestSemantics expected = new TestSemantics.root(
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 1, id: 1,
flags: <SemanticsFlag>[SemanticsFlag.isSelected], children: <TestSemantics>[
actions: <SemanticsAction>[SemanticsAction.tap], new TestSemantics(
label: 'AC\nTab 1 of 3', id: 2,
textDirection: TextDirection.ltr, flags: <SemanticsFlag>[SemanticsFlag.isSelected],
previousNodeId: 3, // Should be 2 actions: <SemanticsAction>[SemanticsAction.tap],
), label: 'AC\nTab 1 of 3',
new TestSemantics( textDirection: TextDirection.ltr,
id: 2, ),
actions: <SemanticsAction>[SemanticsAction.tap], new TestSemantics(
label: 'Alarm\nTab 2 of 3', id: 3,
textDirection: TextDirection.ltr, actions: <SemanticsAction>[SemanticsAction.tap],
nextNodeId: 3, label: 'Alarm\nTab 2 of 3',
previousNodeId: -1, // Should be 1 textDirection: TextDirection.ltr,
), ),
new TestSemantics( new TestSemantics(
id: 3, id: 4,
actions: <SemanticsAction>[SemanticsAction.tap], actions: <SemanticsAction>[SemanticsAction.tap],
label: 'Hot Tub\nTab 3 of 3', label: 'Hot Tub\nTab 3 of 3',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
nextNodeId: 1, // Should be -1 ),
previousNodeId: 2, ],
), ),
], ],
); );
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:ui' show SemanticsFlag;
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:ui' show SemanticsFlag;
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:ui' show SemanticsFlag;
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
...@@ -100,7 +98,6 @@ void main() { ...@@ -100,7 +98,6 @@ void main() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 1, id: 1,
nextNodeId: 3,
rect: new Rect.fromLTWH(0.0, 0.0, 800.0, 56.0), rect: new Rect.fromLTWH(0.0, 0.0, 800.0, 56.0),
transform: null, transform: null,
flags: <SemanticsFlag>[ flags: <SemanticsFlag>[
...@@ -114,8 +111,6 @@ void main() { ...@@ -114,8 +111,6 @@ void main() {
), ),
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 3, id: 3,
previousNodeId: 1,
nextNodeId: 5,
rect: new Rect.fromLTWH(0.0, 0.0, 800.0, 56.0), rect: new Rect.fromLTWH(0.0, 0.0, 800.0, 56.0),
transform: new Matrix4.translationValues(0.0, 56.0, 0.0), transform: new Matrix4.translationValues(0.0, 56.0, 0.0),
flags: <SemanticsFlag>[ flags: <SemanticsFlag>[
...@@ -129,7 +124,6 @@ void main() { ...@@ -129,7 +124,6 @@ void main() {
), ),
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 5, id: 5,
previousNodeId: 3,
rect: new Rect.fromLTWH(0.0, 0.0, 800.0, 56.0), rect: new Rect.fromLTWH(0.0, 0.0, 800.0, 56.0),
transform: new Matrix4.translationValues(0.0, 112.0, 0.0), transform: new Matrix4.translationValues(0.0, 112.0, 0.0),
flags: <SemanticsFlag>[ flags: <SemanticsFlag>[
......
...@@ -4,7 +4,9 @@ ...@@ -4,7 +4,9 @@
import 'dart:ui'; import 'dart:ui';
import 'package:flutter/rendering.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import '../widgets/semantics_tester.dart'; import '../widgets/semantics_tester.dart';
...@@ -393,179 +395,183 @@ void _tests() { ...@@ -393,179 +395,183 @@ void _tests() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap], actions: <SemanticsAction>[SemanticsAction.tap],
label: r'2016', label: '2016',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
), ),
new TestSemantics( new TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.isSelected], flags: <SemanticsFlag>[SemanticsFlag.isSelected],
actions: <SemanticsAction>[SemanticsAction.tap], actions: <SemanticsAction>[SemanticsAction.tap],
label: r'Fri, Jan 15', label: 'Fri, Jan 15',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
), ),
new TestSemantics( new TestSemantics(
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
actions: <SemanticsAction>[SemanticsAction.scrollLeft, SemanticsAction.scrollRight],
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
actions: <SemanticsAction>[SemanticsAction.scrollLeft, SemanticsAction.scrollRight],
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
actions: <SemanticsAction>[SemanticsAction.tap], children: <TestSemantics>[
label: r'1, Friday, January 1, 2016', new TestSemantics(
textDirection: TextDirection.ltr, actions: <SemanticsAction>[SemanticsAction.tap],
), label: '1, Friday, January 1, 2016',
new TestSemantics( textDirection: TextDirection.ltr,
actions: <SemanticsAction>[SemanticsAction.tap], ),
label: r'2, Saturday, January 2, 2016', new TestSemantics(
textDirection: TextDirection.ltr, actions: <SemanticsAction>[SemanticsAction.tap],
), label: '2, Saturday, January 2, 2016',
new TestSemantics( textDirection: TextDirection.ltr,
actions: <SemanticsAction>[SemanticsAction.tap], ),
label: r'3, Sunday, January 3, 2016', new TestSemantics(
textDirection: TextDirection.ltr, actions: <SemanticsAction>[SemanticsAction.tap],
), label: '3, Sunday, January 3, 2016',
new TestSemantics( textDirection: TextDirection.ltr,
actions: <SemanticsAction>[SemanticsAction.tap], ),
label: r'4, Monday, January 4, 2016', new TestSemantics(
textDirection: TextDirection.ltr, actions: <SemanticsAction>[SemanticsAction.tap],
), label: '4, Monday, January 4, 2016',
new TestSemantics( textDirection: TextDirection.ltr,
actions: <SemanticsAction>[SemanticsAction.tap], ),
label: r'5, Tuesday, January 5, 2016', new TestSemantics(
textDirection: TextDirection.ltr, actions: <SemanticsAction>[SemanticsAction.tap],
), label: '5, Tuesday, January 5, 2016',
new TestSemantics( textDirection: TextDirection.ltr,
actions: <SemanticsAction>[SemanticsAction.tap], ),
label: r'6, Wednesday, January 6, 2016', new TestSemantics(
textDirection: TextDirection.ltr, actions: <SemanticsAction>[SemanticsAction.tap],
), label: '6, Wednesday, January 6, 2016',
new TestSemantics( textDirection: TextDirection.ltr,
actions: <SemanticsAction>[SemanticsAction.tap], ),
label: r'7, Thursday, January 7, 2016', new TestSemantics(
textDirection: TextDirection.ltr, actions: <SemanticsAction>[SemanticsAction.tap],
), label: '7, Thursday, January 7, 2016',
new TestSemantics( textDirection: TextDirection.ltr,
actions: <SemanticsAction>[SemanticsAction.tap], ),
label: r'8, Friday, January 8, 2016', new TestSemantics(
textDirection: TextDirection.ltr, actions: <SemanticsAction>[SemanticsAction.tap],
), label: '8, Friday, January 8, 2016',
new TestSemantics( textDirection: TextDirection.ltr,
actions: <SemanticsAction>[SemanticsAction.tap], ),
label: r'9, Saturday, January 9, 2016', new TestSemantics(
textDirection: TextDirection.ltr, actions: <SemanticsAction>[SemanticsAction.tap],
), label: '9, Saturday, January 9, 2016',
new TestSemantics( textDirection: TextDirection.ltr,
actions: <SemanticsAction>[SemanticsAction.tap], ),
label: r'10, Sunday, January 10, 2016', new TestSemantics(
textDirection: TextDirection.ltr, actions: <SemanticsAction>[SemanticsAction.tap],
), label: '10, Sunday, January 10, 2016',
new TestSemantics( textDirection: TextDirection.ltr,
actions: <SemanticsAction>[SemanticsAction.tap], ),
label: r'11, Monday, January 11, 2016', new TestSemantics(
textDirection: TextDirection.ltr, actions: <SemanticsAction>[SemanticsAction.tap],
), label: '11, Monday, January 11, 2016',
new TestSemantics( textDirection: TextDirection.ltr,
actions: <SemanticsAction>[SemanticsAction.tap], ),
label: r'12, Tuesday, January 12, 2016', new TestSemantics(
textDirection: TextDirection.ltr, actions: <SemanticsAction>[SemanticsAction.tap],
), label: '12, Tuesday, January 12, 2016',
new TestSemantics( textDirection: TextDirection.ltr,
actions: <SemanticsAction>[SemanticsAction.tap], ),
label: r'13, Wednesday, January 13, 2016', new TestSemantics(
textDirection: TextDirection.ltr, actions: <SemanticsAction>[SemanticsAction.tap],
), label: '13, Wednesday, January 13, 2016',
new TestSemantics( textDirection: TextDirection.ltr,
actions: <SemanticsAction>[SemanticsAction.tap], ),
label: r'14, Thursday, January 14, 2016', new TestSemantics(
textDirection: TextDirection.ltr, actions: <SemanticsAction>[SemanticsAction.tap],
), label: '14, Thursday, January 14, 2016',
new TestSemantics( textDirection: TextDirection.ltr,
flags: <SemanticsFlag>[SemanticsFlag.isSelected], ),
actions: <SemanticsAction>[SemanticsAction.tap], new TestSemantics(
label: r'15, Friday, January 15, 2016', flags: <SemanticsFlag>[SemanticsFlag.isSelected],
textDirection: TextDirection.ltr, actions: <SemanticsAction>[SemanticsAction.tap],
), label: '15, Friday, January 15, 2016',
new TestSemantics( textDirection: TextDirection.ltr,
actions: <SemanticsAction>[SemanticsAction.tap], ),
label: r'16, Saturday, January 16, 2016', new TestSemantics(
textDirection: TextDirection.ltr, actions: <SemanticsAction>[SemanticsAction.tap],
), label: '16, Saturday, January 16, 2016',
new TestSemantics( textDirection: TextDirection.ltr,
actions: <SemanticsAction>[SemanticsAction.tap], ),
label: r'17, Sunday, January 17, 2016', new TestSemantics(
textDirection: TextDirection.ltr, actions: <SemanticsAction>[SemanticsAction.tap],
), label: '17, Sunday, January 17, 2016',
new TestSemantics( textDirection: TextDirection.ltr,
actions: <SemanticsAction>[SemanticsAction.tap], ),
label: r'18, Monday, January 18, 2016', new TestSemantics(
textDirection: TextDirection.ltr, actions: <SemanticsAction>[SemanticsAction.tap],
), label: '18, Monday, January 18, 2016',
new TestSemantics( textDirection: TextDirection.ltr,
actions: <SemanticsAction>[SemanticsAction.tap], ),
label: r'19, Tuesday, January 19, 2016', new TestSemantics(
textDirection: TextDirection.ltr, actions: <SemanticsAction>[SemanticsAction.tap],
), label: '19, Tuesday, January 19, 2016',
new TestSemantics( textDirection: TextDirection.ltr,
actions: <SemanticsAction>[SemanticsAction.tap], ),
label: r'20, Wednesday, January 20, 2016', new TestSemantics(
textDirection: TextDirection.ltr, actions: <SemanticsAction>[SemanticsAction.tap],
), label: '20, Wednesday, January 20, 2016',
new TestSemantics( textDirection: TextDirection.ltr,
actions: <SemanticsAction>[SemanticsAction.tap], ),
label: r'21, Thursday, January 21, 2016', new TestSemantics(
textDirection: TextDirection.ltr, actions: <SemanticsAction>[SemanticsAction.tap],
), label: '21, Thursday, January 21, 2016',
new TestSemantics( textDirection: TextDirection.ltr,
actions: <SemanticsAction>[SemanticsAction.tap], ),
label: r'22, Friday, January 22, 2016', new TestSemantics(
textDirection: TextDirection.ltr, actions: <SemanticsAction>[SemanticsAction.tap],
), label: '22, Friday, January 22, 2016',
new TestSemantics( textDirection: TextDirection.ltr,
actions: <SemanticsAction>[SemanticsAction.tap], ),
label: r'23, Saturday, January 23, 2016', new TestSemantics(
textDirection: TextDirection.ltr, actions: <SemanticsAction>[SemanticsAction.tap],
), label: '23, Saturday, January 23, 2016',
new TestSemantics( textDirection: TextDirection.ltr,
actions: <SemanticsAction>[SemanticsAction.tap], ),
label: r'24, Sunday, January 24, 2016', new TestSemantics(
textDirection: TextDirection.ltr, actions: <SemanticsAction>[SemanticsAction.tap],
), label: '24, Sunday, January 24, 2016',
new TestSemantics( textDirection: TextDirection.ltr,
actions: <SemanticsAction>[SemanticsAction.tap], ),
label: r'25, Monday, January 25, 2016', new TestSemantics(
textDirection: TextDirection.ltr, actions: <SemanticsAction>[SemanticsAction.tap],
), label: '25, Monday, January 25, 2016',
new TestSemantics( textDirection: TextDirection.ltr,
actions: <SemanticsAction>[SemanticsAction.tap], ),
label: r'26, Tuesday, January 26, 2016', new TestSemantics(
textDirection: TextDirection.ltr, actions: <SemanticsAction>[SemanticsAction.tap],
), label: '26, Tuesday, January 26, 2016',
new TestSemantics( textDirection: TextDirection.ltr,
actions: <SemanticsAction>[SemanticsAction.tap], ),
label: r'27, Wednesday, January 27, 2016', new TestSemantics(
textDirection: TextDirection.ltr, actions: <SemanticsAction>[SemanticsAction.tap],
), label: '27, Wednesday, January 27, 2016',
new TestSemantics( textDirection: TextDirection.ltr,
actions: <SemanticsAction>[SemanticsAction.tap], ),
label: r'28, Thursday, January 28, 2016', new TestSemantics(
textDirection: TextDirection.ltr, actions: <SemanticsAction>[SemanticsAction.tap],
), label: '28, Thursday, January 28, 2016',
new TestSemantics( textDirection: TextDirection.ltr,
actions: <SemanticsAction>[SemanticsAction.tap], ),
label: r'29, Friday, January 29, 2016', new TestSemantics(
textDirection: TextDirection.ltr, actions: <SemanticsAction>[SemanticsAction.tap],
), label: '29, Friday, January 29, 2016',
new TestSemantics( textDirection: TextDirection.ltr,
actions: <SemanticsAction>[SemanticsAction.tap], ),
label: r'30, Saturday, January 30, 2016', new TestSemantics(
textDirection: TextDirection.ltr, actions: <SemanticsAction>[SemanticsAction.tap],
), label: '30, Saturday, January 30, 2016',
new TestSemantics( textDirection: TextDirection.ltr,
actions: <SemanticsAction>[SemanticsAction.tap], ),
label: r'31, Sunday, January 31, 2016', new TestSemantics(
textDirection: TextDirection.ltr, actions: <SemanticsAction>[SemanticsAction.tap],
label: '31, Sunday, January 31, 2016',
textDirection: TextDirection.ltr,
),
],
), ),
], ],
), ),
...@@ -576,43 +582,27 @@ void _tests() { ...@@ -576,43 +582,27 @@ void _tests() {
], ],
), ),
new TestSemantics( new TestSemantics(
flags: <SemanticsFlag>[ flags: <SemanticsFlag>[SemanticsFlag.isButton, SemanticsFlag.hasEnabledState, SemanticsFlag.isEnabled],
SemanticsFlag.isButton,
SemanticsFlag.hasEnabledState,
SemanticsFlag.isEnabled,
],
actions: <SemanticsAction>[SemanticsAction.tap], actions: <SemanticsAction>[SemanticsAction.tap],
label: r'Previous month December 2015', label: 'Previous month December 2015',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
), ),
new TestSemantics( new TestSemantics(
flags: <SemanticsFlag>[ flags: <SemanticsFlag>[SemanticsFlag.isButton, SemanticsFlag.hasEnabledState, SemanticsFlag.isEnabled],
SemanticsFlag.isButton,
SemanticsFlag.hasEnabledState,
SemanticsFlag.isEnabled,
],
actions: <SemanticsAction>[SemanticsAction.tap], actions: <SemanticsAction>[SemanticsAction.tap],
label: r'Next month February 2016', label: 'Next month February 2016',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
), ),
new TestSemantics( new TestSemantics(
flags: <SemanticsFlag>[ flags: <SemanticsFlag>[SemanticsFlag.isButton, SemanticsFlag.hasEnabledState, SemanticsFlag.isEnabled],
SemanticsFlag.isButton,
SemanticsFlag.hasEnabledState,
SemanticsFlag.isEnabled,
],
actions: <SemanticsAction>[SemanticsAction.tap], actions: <SemanticsAction>[SemanticsAction.tap],
label: r'CANCEL', label: 'CANCEL',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
), ),
new TestSemantics( new TestSemantics(
flags: <SemanticsFlag>[ flags: <SemanticsFlag>[SemanticsFlag.isButton, SemanticsFlag.hasEnabledState, SemanticsFlag.isEnabled],
SemanticsFlag.isButton,
SemanticsFlag.hasEnabledState,
SemanticsFlag.isEnabled,
],
actions: <SemanticsAction>[SemanticsAction.tap], actions: <SemanticsAction>[SemanticsAction.tap],
label: r'OK', label: 'OK',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
), ),
], ],
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:ui' show SemanticsFlag;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:ui' show SemanticsFlag;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:ui' show SemanticsFlag, SemanticsAction;
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
...@@ -1363,7 +1361,6 @@ void main() { ...@@ -1363,7 +1361,6 @@ void main() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 3, id: 3,
nextNodeId: 4,
actions: SemanticsAction.tap.index, actions: SemanticsAction.tap.index,
flags: SemanticsFlag.isSelected.index, flags: SemanticsFlag.isSelected.index,
label: 'TAB #0\nTab 1 of 2', label: 'TAB #0\nTab 1 of 2',
...@@ -1372,7 +1369,6 @@ void main() { ...@@ -1372,7 +1369,6 @@ void main() {
), ),
new TestSemantics( new TestSemantics(
id: 4, id: 4,
previousNodeId: 3,
actions: SemanticsAction.tap.index, actions: SemanticsAction.tap.index,
label: 'TAB #1\nTab 2 of 2', label: 'TAB #1\nTab 2 of 2',
rect: new Rect.fromLTRB(0.0, 0.0, 108.0, kTextTabBarHeight), rect: new Rect.fromLTRB(0.0, 0.0, 108.0, kTextTabBarHeight),
...@@ -1622,7 +1618,6 @@ void main() { ...@@ -1622,7 +1618,6 @@ void main() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 3, id: 3,
nextNodeId: 4,
actions: SemanticsAction.tap.index, actions: SemanticsAction.tap.index,
flags: SemanticsFlag.isSelected.index, flags: SemanticsFlag.isSelected.index,
label: 'Semantics override 0\nTab 1 of 2', label: 'Semantics override 0\nTab 1 of 2',
...@@ -1631,7 +1626,6 @@ void main() { ...@@ -1631,7 +1626,6 @@ void main() {
), ),
new TestSemantics( new TestSemantics(
id: 4, id: 4,
previousNodeId: 3,
actions: SemanticsAction.tap.index, actions: SemanticsAction.tap.index,
label: 'Semantics override 1\nTab 2 of 2', label: 'Semantics override 1\nTab 2 of 2',
rect: new Rect.fromLTRB(0.0, 0.0, 108.0, kTextTabBarHeight), rect: new Rect.fromLTRB(0.0, 0.0, 108.0, kTextTabBarHeight),
......
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:io' show Platform; import 'dart:io' show Platform;
import 'dart:ui' show SemanticsFlag;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:ui' show SemanticsFlag;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
......
...@@ -125,7 +125,7 @@ void main() { ...@@ -125,7 +125,7 @@ void main() {
expect(child2.transform, isNull); expect(child2.transform, isNull);
expect( expect(
root.toStringDeep(childOrder: DebugSemanticsDumpOrder.geometricOrder), root.toStringDeep(childOrder: DebugSemanticsDumpOrder.traversalOrder),
'SemanticsNode#3\n' 'SemanticsNode#3\n'
' │ STALE\n' ' │ STALE\n'
' │ owner: null\n' ' │ owner: null\n'
...@@ -143,18 +143,26 @@ void main() { ...@@ -143,18 +143,26 @@ void main() {
); );
}); });
test('Incompatible OrdinalSortKey throw AssertionError when compared', () {
// Different types.
expect(() {
const OrdinalSortKey(0.0).compareTo(const CustomSortKey(0.0));
}, throwsAssertionError);
// Different names.
expect(() {
const OrdinalSortKey(0.0, name: 'a').compareTo(const OrdinalSortKey(0.0, name: 'b'));
}, throwsAssertionError);
});
test('OrdinalSortKey compares correctly', () { test('OrdinalSortKey compares correctly', () {
const List<List<SemanticsSortKey>> tests = const <List<SemanticsSortKey>>[ const List<List<SemanticsSortKey>> tests = const <List<SemanticsSortKey>>[
const <SemanticsSortKey>[const OrdinalSortKey(0.0), const OrdinalSortKey(0.0)], const <SemanticsSortKey>[const OrdinalSortKey(0.0), const OrdinalSortKey(0.0)],
const <SemanticsSortKey>[const OrdinalSortKey(0.0), const OrdinalSortKey(1.0)], const <SemanticsSortKey>[const OrdinalSortKey(0.0), const OrdinalSortKey(1.0)],
const <SemanticsSortKey>[const OrdinalSortKey(1.0), const OrdinalSortKey(0.0)], const <SemanticsSortKey>[const OrdinalSortKey(1.0), const OrdinalSortKey(0.0)],
const <SemanticsSortKey>[const OrdinalSortKey(1.0), const OrdinalSortKey(1.0)], const <SemanticsSortKey>[const OrdinalSortKey(1.0), const OrdinalSortKey(1.0)],
const <SemanticsSortKey>[const OrdinalSortKey(0.0), const CustomSortKey(1.0)],
const <SemanticsSortKey>[const OrdinalSortKey(0.0), const CustomSortKey(0.0)],
const <SemanticsSortKey>[const CustomSortKey(0.0), const OrdinalSortKey(0.0)],
const <SemanticsSortKey>[const CustomSortKey(1.0), const OrdinalSortKey(0.0)],
]; ];
final List<int> expectedResults = <int>[0, -1, 1, 0, 0, 0, 0, 0]; final List<int> expectedResults = <int>[0, -1, 1, 0];
assert(tests.length == expectedResults.length); assert(tests.length == expectedResults.length);
final List<int> results = <int>[]; final List<int> results = <int>[];
for (List<SemanticsSortKey> tuple in tests) { for (List<SemanticsSortKey> tuple in tests) {
...@@ -163,31 +171,14 @@ void main() { ...@@ -163,31 +171,14 @@ void main() {
expect(results, orderedEquals(expectedResults)); expect(results, orderedEquals(expectedResults));
}); });
test('SemanticsSortKey sorts correctly when assigned names', () {
const SemanticsSortKey order1g1 = const CustomSortKey(0.0, name: 'group 1');
const SemanticsSortKey order2g1 = const CustomSortKey(1.0, name: 'group 1');
const SemanticsSortKey order2g2 = const CustomSortKey(1.0, name: 'group 2');
const SemanticsSortKey order3g2 = const OrdinalSortKey(1.0, name: 'group 1');
// Keys in the same group compare.
expect(order1g1.compareTo(order2g1), equals(-1));
// Keys with different names compare equal.
expect(order1g1.compareTo(order2g2), equals(0));
// Keys with same names but different types compare equal.
expect(order1g1.compareTo(order3g2), equals(0));
});
test('OrdinalSortKey compares correctly', () { test('OrdinalSortKey compares correctly', () {
const List<List<SemanticsSortKey>> tests = const <List<SemanticsSortKey>>[ const List<List<SemanticsSortKey>> tests = const <List<SemanticsSortKey>>[
const <SemanticsSortKey>[const OrdinalSortKey(0.0), const OrdinalSortKey(0.0)], const <SemanticsSortKey>[const OrdinalSortKey(0.0), const OrdinalSortKey(0.0)],
const <SemanticsSortKey>[const OrdinalSortKey(0.0), const OrdinalSortKey(1.0)], const <SemanticsSortKey>[const OrdinalSortKey(0.0), const OrdinalSortKey(1.0)],
const <SemanticsSortKey>[const OrdinalSortKey(1.0), const OrdinalSortKey(0.0)], const <SemanticsSortKey>[const OrdinalSortKey(1.0), const OrdinalSortKey(0.0)],
const <SemanticsSortKey>[const OrdinalSortKey(1.0), const OrdinalSortKey(1.0)], const <SemanticsSortKey>[const OrdinalSortKey(1.0), const OrdinalSortKey(1.0)],
const <SemanticsSortKey>[const OrdinalSortKey(0.0), const CustomSortKey(1.0)],
const <SemanticsSortKey>[const OrdinalSortKey(0.0), const CustomSortKey(0.0)],
const <SemanticsSortKey>[const CustomSortKey(0.0), const OrdinalSortKey(0.0)],
const <SemanticsSortKey>[const CustomSortKey(1.0), const OrdinalSortKey(0.0)],
]; ];
final List<int> expectedResults = <int>[0, -1, 1, 0, 0, 0, 0, 0]; final List<int> expectedResults = <int>[0, -1, 1, 0];
assert(tests.length == expectedResults.length); assert(tests.length == expectedResults.length);
final List<int> results = <int>[]; final List<int> results = <int>[];
for (List<SemanticsSortKey> tuple in tests) { for (List<SemanticsSortKey> tuple in tests) {
...@@ -196,19 +187,6 @@ void main() { ...@@ -196,19 +187,6 @@ void main() {
expect(results, orderedEquals(expectedResults)); expect(results, orderedEquals(expectedResults));
}); });
test('SemanticsSortKey sorts correctly when assigned names', () {
const SemanticsSortKey order1g1 = const CustomSortKey(0.0, name: 'group 1');
const SemanticsSortKey order2g1 = const CustomSortKey(1.0, name: 'group 1');
const SemanticsSortKey order2g2 = const CustomSortKey(1.0, name: 'group 2');
const SemanticsSortKey order3g2 = const OrdinalSortKey(1.0, name: 'group 1');
// Keys in the same group compare.
expect(order1g1.compareTo(order2g1), equals(-1));
// Keys with different names compare equal.
expect(order1g1.compareTo(order2g2), equals(0));
// Keys with same names but different types compare equal.
expect(order1g1.compareTo(order3g2), equals(0));
});
test('toStringDeep respects childOrder parameter', () { test('toStringDeep respects childOrder parameter', () {
final SemanticsNode child1 = new SemanticsNode() final SemanticsNode child1 = new SemanticsNode()
..rect = new Rect.fromLTRB(15.0, 0.0, 20.0, 5.0); ..rect = new Rect.fromLTRB(15.0, 0.0, 20.0, 5.0);
...@@ -221,21 +199,21 @@ void main() { ...@@ -221,21 +199,21 @@ void main() {
childrenInInversePaintOrder: <SemanticsNode>[child1, child2], childrenInInversePaintOrder: <SemanticsNode>[child1, child2],
); );
expect( expect(
root.toStringDeep(childOrder: DebugSemanticsDumpOrder.geometricOrder), root.toStringDeep(childOrder: DebugSemanticsDumpOrder.traversalOrder),
'SemanticsNode#3\n' 'SemanticsNode#3\n'
' │ STALE\n' ' │ STALE\n'
' │ owner: null\n' ' │ owner: null\n'
' │ Rect.fromLTRB(0.0, 0.0, 20.0, 5.0)\n' ' │ Rect.fromLTRB(0.0, 0.0, 20.0, 5.0)\n'
' │\n' ' │\n'
' ├─SemanticsNode#2\n' ' ├─SemanticsNode#1\n'
' │ STALE\n' ' │ STALE\n'
' │ owner: null\n' ' │ owner: null\n'
' │ Rect.fromLTRB(10.0, 0.0, 15.0, 5.0)\n' ' │ Rect.fromLTRB(15.0, 0.0, 20.0, 5.0)\n'
' │\n' ' │\n'
' └─SemanticsNode#1\n' ' └─SemanticsNode#2\n'
' STALE\n' ' STALE\n'
' owner: null\n' ' owner: null\n'
' Rect.fromLTRB(15.0, 0.0, 20.0, 5.0)\n' ' Rect.fromLTRB(10.0, 0.0, 15.0, 5.0)\n'
); );
expect( expect(
...@@ -276,36 +254,36 @@ void main() { ...@@ -276,36 +254,36 @@ void main() {
); );
expect( expect(
rootComplex.toStringDeep(childOrder: DebugSemanticsDumpOrder.geometricOrder), rootComplex.toStringDeep(childOrder: DebugSemanticsDumpOrder.traversalOrder),
'SemanticsNode#7\n' 'SemanticsNode#7\n'
' │ STALE\n' ' │ STALE\n'
' │ owner: null\n' ' │ owner: null\n'
' │ Rect.fromLTRB(0.0, 0.0, 25.0, 5.0)\n' ' │ Rect.fromLTRB(0.0, 0.0, 25.0, 5.0)\n'
' │\n' ' │\n'
' ├─SemanticsNode#4\n' ' ├─SemanticsNode#1\n'
' │ │ STALE\n' ' │ STALE\n'
' │ │ owner: null\n' ' │ owner: null\n'
' │ │ Rect.fromLTRB(0.0, 0.0, 10.0, 5.0)\n' ' │ Rect.fromLTRB(15.0, 0.0, 20.0, 5.0)\n'
' │ │\n'
' │ ├─SemanticsNode#6\n'
' │ │ STALE\n'
' │ │ owner: null\n'
' │ │ Rect.fromLTRB(0.0, 0.0, 5.0, 5.0)\n'
' │ │\n'
' │ └─SemanticsNode#5\n'
' │ STALE\n'
' │ owner: null\n'
' │ Rect.fromLTRB(5.0, 0.0, 10.0, 5.0)\n'
' │\n' ' │\n'
' ├─SemanticsNode#2\n' ' ├─SemanticsNode#2\n'
' │ STALE\n' ' │ STALE\n'
' │ owner: null\n' ' │ owner: null\n'
' │ Rect.fromLTRB(10.0, 0.0, 15.0, 5.0)\n' ' │ Rect.fromLTRB(10.0, 0.0, 15.0, 5.0)\n'
' │\n' ' │\n'
' └─SemanticsNode#1\n' ' └─SemanticsNode#4\n'
' STALE\n' ' │ STALE\n'
' owner: null\n' ' │ owner: null\n'
' Rect.fromLTRB(15.0, 0.0, 20.0, 5.0)\n' ' │ Rect.fromLTRB(0.0, 0.0, 10.0, 5.0)\n'
' │\n'
' ├─SemanticsNode#5\n'
' │ STALE\n'
' │ owner: null\n'
' │ Rect.fromLTRB(5.0, 0.0, 10.0, 5.0)\n'
' │\n'
' └─SemanticsNode#6\n'
' STALE\n'
' owner: null\n'
' Rect.fromLTRB(0.0, 0.0, 5.0, 5.0)\n'
); );
expect( expect(
...@@ -368,8 +346,6 @@ void main() { ...@@ -368,8 +346,6 @@ void main() {
' decreasedValue: ""\n' ' decreasedValue: ""\n'
' hint: ""\n' ' hint: ""\n'
' textDirection: null\n' ' textDirection: null\n'
' nextNodeId: null\n'
' previousNodeId: null\n'
' sortKey: null\n' ' sortKey: null\n'
' scrollExtentMin: null\n' ' scrollExtentMin: null\n'
' scrollPosition: null\n' ' scrollPosition: null\n'
......
...@@ -150,20 +150,16 @@ void _defineTests() { ...@@ -150,20 +150,16 @@ void _defineTests() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 3, id: 3,
nextNodeId: 4,
previousNodeId: 2,
label: 'background', label: 'background',
rect: new Rect.fromLTRB(1.0, 1.0, 2.0, 2.0), rect: new Rect.fromLTRB(1.0, 1.0, 2.0, 2.0),
), ),
new TestSemantics( new TestSemantics(
id: 2, id: 2,
nextNodeId: 3,
label: 'Hello', label: 'Hello',
rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 600.0), rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 600.0),
), ),
new TestSemantics( new TestSemantics(
id: 4, id: 4,
previousNodeId: 3,
label: 'foreground', label: 'foreground',
rect: new Rect.fromLTRB(1.0, 1.0, 2.0, 2.0), rect: new Rect.fromLTRB(1.0, 1.0, 2.0, 2.0),
), ),
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:ui' show SemanticsFlag;
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
...@@ -600,8 +598,6 @@ void main() { ...@@ -600,8 +598,6 @@ void main() {
flags: <SemanticsFlag>[SemanticsFlag.isTextField, SemanticsFlag.isObscured], flags: <SemanticsFlag>[SemanticsFlag.isTextField, SemanticsFlag.isObscured],
value: expectedValue, value: expectedValue,
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
nextNodeId: -1,
previousNodeId: -1,
), ),
], ],
), ),
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:ui' show SemanticsFlag;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
...@@ -79,12 +77,10 @@ void main() { ...@@ -79,12 +77,10 @@ void main() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 2, id: 2,
nextNodeId: 3,
label: 'Michael Goderbauer', label: 'Michael Goderbauer',
), ),
new TestSemantics( new TestSemantics(
id: 3, id: 3,
previousNodeId: 2,
label: 'goderbauer@google.com', label: 'goderbauer@google.com',
), ),
], ],
...@@ -235,20 +231,16 @@ void main() { ...@@ -235,20 +231,16 @@ void main() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 6, id: 6,
nextNodeId: 7,
flags: SemanticsFlag.isSelected.index, flags: SemanticsFlag.isSelected.index,
label: 'node 1', label: 'node 1',
), ),
new TestSemantics( new TestSemantics(
id: 7, id: 7,
previousNodeId: 6,
nextNodeId: 8,
flags: SemanticsFlag.isSelected.index, flags: SemanticsFlag.isSelected.index,
label: 'node 2', label: 'node 2',
), ),
new TestSemantics( new TestSemantics(
id: 8, id: 8,
previousNodeId: 7,
flags: SemanticsFlag.isSelected.index, flags: SemanticsFlag.isSelected.index,
label: 'node 3', label: 'node 3',
), ),
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:ui' show SemanticsFlag;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
...@@ -122,14 +120,12 @@ void main() { ...@@ -122,14 +120,12 @@ void main() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 2, id: 2,
nextNodeId: 3,
label: 'child1', label: 'child1',
rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 10.0), rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 10.0),
flags: SemanticsFlag.isSelected.index, flags: SemanticsFlag.isSelected.index,
), ),
new TestSemantics( new TestSemantics(
id: 3, id: 3,
previousNodeId: 2,
label: 'child2', label: 'child2',
rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 10.0), rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 10.0),
flags: SemanticsFlag.isSelected.index, flags: SemanticsFlag.isSelected.index,
...@@ -220,14 +216,12 @@ void main() { ...@@ -220,14 +216,12 @@ void main() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 4, id: 4,
nextNodeId: 3,
label: 'child1', label: 'child1',
rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 10.0), rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 10.0),
flags: SemanticsFlag.isSelected.index, flags: SemanticsFlag.isSelected.index,
), ),
new TestSemantics( new TestSemantics(
id: 3, id: 3,
previousNodeId: 4,
label: 'child2', label: 'child2',
rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 10.0), rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 10.0),
flags: SemanticsFlag.isSelected.index, flags: SemanticsFlag.isSelected.index,
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:ui' show SemanticsFlag;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
...@@ -58,14 +56,12 @@ void main() { ...@@ -58,14 +56,12 @@ void main() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 2, id: 2,
nextNodeId: 3,
label: 'child1', label: 'child1',
rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 10.0), rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 10.0),
flags: SemanticsFlag.isSelected.index, flags: SemanticsFlag.isSelected.index,
), ),
new TestSemantics( new TestSemantics(
id: 3, id: 3,
previousNodeId: 2,
label: 'child2', label: 'child2',
rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 10.0), rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 10.0),
flags: SemanticsFlag.isSelected.index, flags: SemanticsFlag.isSelected.index,
...@@ -156,14 +152,12 @@ void main() { ...@@ -156,14 +152,12 @@ void main() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 4, id: 4,
nextNodeId: 3,
label: 'child1', label: 'child1',
rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 10.0), rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 10.0),
flags: SemanticsFlag.isSelected.index, flags: SemanticsFlag.isSelected.index,
), ),
new TestSemantics( new TestSemantics(
id: 3, id: 3,
previousNodeId: 4,
label: 'child2', label: 'child2',
rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 10.0), rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 10.0),
flags: SemanticsFlag.isSelected.index, flags: SemanticsFlag.isSelected.index,
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:ui' show SemanticsFlag;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:ui' show SemanticsFlag;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
...@@ -54,25 +52,21 @@ void main() { ...@@ -54,25 +52,21 @@ void main() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 1, id: 1,
nextNodeId: 2,
label: 'L1', label: 'L1',
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
), ),
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 2, id: 2,
previousNodeId: 1,
label: 'L2', label: 'L2',
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 3, id: 3,
nextNodeId: 4,
flags: SemanticsFlag.hasCheckedState.index | SemanticsFlag.isChecked.index, flags: SemanticsFlag.hasCheckedState.index | SemanticsFlag.isChecked.index,
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
), ),
new TestSemantics( new TestSemantics(
id: 4, id: 4,
previousNodeId: 3,
flags: SemanticsFlag.hasCheckedState.index, flags: SemanticsFlag.hasCheckedState.index,
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
), ),
...@@ -118,13 +112,11 @@ void main() { ...@@ -118,13 +112,11 @@ void main() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 1, id: 1,
nextNodeId: 2,
label: 'L1', label: 'L1',
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
), ),
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 2, id: 2,
previousNodeId: 1,
label: 'L2', label: 'L2',
flags: SemanticsFlag.hasCheckedState.index | SemanticsFlag.isChecked.index, flags: SemanticsFlag.hasCheckedState.index | SemanticsFlag.isChecked.index,
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
......
...@@ -37,12 +37,10 @@ void main() { ...@@ -37,12 +37,10 @@ void main() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 1, id: 1,
nextNodeId: 2,
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
), ),
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 2, id: 2,
previousNodeId: 1,
label: 'label', label: 'label',
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
), ),
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:ui' show SemanticsFlag;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
...@@ -56,7 +54,6 @@ void main() { ...@@ -56,7 +54,6 @@ void main() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 1, id: 1,
nextNodeId: 4,
flags: SemanticsFlag.hasCheckedState.index | SemanticsFlag.isChecked.index, flags: SemanticsFlag.hasCheckedState.index | SemanticsFlag.isChecked.index,
label: label, label: label,
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
...@@ -64,7 +61,6 @@ void main() { ...@@ -64,7 +61,6 @@ void main() {
// IDs 2 and 3 are used up by the nodes that get merged in // IDs 2 and 3 are used up by the nodes that get merged in
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 4, id: 4,
previousNodeId: 1,
flags: SemanticsFlag.hasCheckedState.index | SemanticsFlag.isChecked.index, flags: SemanticsFlag.hasCheckedState.index | SemanticsFlag.isChecked.index,
label: label, label: label,
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
...@@ -114,7 +110,6 @@ void main() { ...@@ -114,7 +110,6 @@ void main() {
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 1, id: 1,
nextNodeId: 4,
flags: SemanticsFlag.hasCheckedState.index | SemanticsFlag.isChecked.index, flags: SemanticsFlag.hasCheckedState.index | SemanticsFlag.isChecked.index,
label: label, label: label,
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
...@@ -122,7 +117,6 @@ void main() { ...@@ -122,7 +117,6 @@ void main() {
// IDs 2 and 3 are used up by the nodes that get merged in // IDs 2 and 3 are used up by the nodes that get merged in
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 4, id: 4,
previousNodeId: 1,
flags: SemanticsFlag.hasCheckedState.index | SemanticsFlag.isChecked.index, flags: SemanticsFlag.hasCheckedState.index | SemanticsFlag.isChecked.index,
label: label, label: label,
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:ui' show SemanticsFlag;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
......
...@@ -2,8 +2,6 @@ ...@@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:ui' show SemanticsFlag;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
...@@ -44,12 +42,10 @@ void main() { ...@@ -44,12 +42,10 @@ void main() {
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 1, id: 1,
label: 'test1', label: 'test1',
nextNodeId: 2,
), ),
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 2, id: 2,
label: 'test2', label: 'test2',
previousNodeId: 1,
), ),
], ],
), ),
...@@ -113,8 +109,8 @@ void main() { ...@@ -113,8 +109,8 @@ void main() {
expect(semantics, hasSemantics( expect(semantics, hasSemantics(
new TestSemantics.root( new TestSemantics.root(
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild(id: 6, label: 'test1', nextNodeId: 7), new TestSemantics.rootChild(id: 6, label: 'test1'),
new TestSemantics.rootChild(id: 7, label: 'test2', previousNodeId: 6), new TestSemantics.rootChild(id: 7, label: 'test2'),
], ],
), ),
ignoreRect: true, ignoreRect: true,
......
...@@ -674,44 +674,36 @@ void main() { ...@@ -674,44 +674,36 @@ void main() {
new TestSemantics( new TestSemantics(
id: 1, id: 1,
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics(
id: 2,
label: r'Label 1',
textDirection: TextDirection.ltr,
previousNodeId: 3,
),
new TestSemantics(
id: 3,
label: r'Label 2',
textDirection: TextDirection.ltr,
nextNodeId: 2,
previousNodeId: 4,
),
new TestSemantics( new TestSemantics(
id: 4, id: 4,
nextNodeId: 3,
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 5, id: 7,
label: r'Label 3', label: r'Label 5',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
previousNodeId: 6,
), ),
new TestSemantics( new TestSemantics(
id: 6, id: 6,
label: r'Label 4', label: r'Label 4',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
nextNodeId: 5,
previousNodeId: 7,
), ),
new TestSemantics( new TestSemantics(
id: 7, id: 5,
label: r'Label 5', label: r'Label 3',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
nextNodeId: 6,
), ),
], ],
), ),
new TestSemantics(
id: 3,
label: r'Label 2',
textDirection: TextDirection.ltr,
),
new TestSemantics(
id: 2,
label: r'Label 1',
textDirection: TextDirection.ltr,
),
], ],
), ),
], ],
...@@ -755,24 +747,20 @@ void main() { ...@@ -755,24 +747,20 @@ void main() {
expect(semantics, hasSemantics( expect(semantics, hasSemantics(
new TestSemantics.root( new TestSemantics.root(
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics(
id: 1,
label: r'Label 1',
textDirection: TextDirection.ltr,
previousNodeId: 3,
),
new TestSemantics( new TestSemantics(
id: 2, id: 2,
label: r'Label 2', label: r'Label 2',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
nextNodeId: 3,
), ),
new TestSemantics( new TestSemantics(
id: 3, id: 3,
label: r'Label 3', label: r'Label 3',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
nextNodeId: 1, ),
previousNodeId: 2, new TestSemantics(
id: 1,
label: r'Label 1',
textDirection: TextDirection.ltr,
), ),
], ],
), ignoreTransform: true, ignoreRect: true)); ), ignoreTransform: true, ignoreRect: true));
...@@ -818,22 +806,18 @@ void main() { ...@@ -818,22 +806,18 @@ void main() {
new TestSemantics( new TestSemantics(
label: r'Label 2', label: r'Label 2',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
previousNodeId: 1,
), ),
new TestSemantics( new TestSemantics(
label: r'Label 3', label: r'Label 3',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
previousNodeId: 2,
), ),
new TestSemantics( new TestSemantics(
label: r'Label 4', label: r'Label 4',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
previousNodeId: 3,
), ),
new TestSemantics( new TestSemantics(
label: r'Label 5', label: r'Label 5',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
previousNodeId: 4,
), ),
], ],
), ignoreTransform: true, ignoreRect: true, ignoreId: true), ), ignoreTransform: true, ignoreRect: true, ignoreId: true),
...@@ -879,12 +863,10 @@ void main() { ...@@ -879,12 +863,10 @@ void main() {
new TestSemantics( new TestSemantics(
label: r'Label 1', label: r'Label 1',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
previousNodeId: 5,
), ),
new TestSemantics( new TestSemantics(
label: r'Label 2', label: r'Label 2',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
previousNodeId: 1,
), ),
new TestSemantics( new TestSemantics(
label: r'Label 3', label: r'Label 3',
...@@ -893,12 +875,10 @@ void main() { ...@@ -893,12 +875,10 @@ void main() {
new TestSemantics( new TestSemantics(
label: r'Label 4', label: r'Label 4',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
previousNodeId: 3,
), ),
new TestSemantics( new TestSemantics(
label: r'Label 5', label: r'Label 5',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
previousNodeId: 4,
), ),
], ],
), ignoreTransform: true, ignoreRect: true, ignoreId: true), ), ignoreTransform: true, ignoreRect: true, ignoreId: true),
...@@ -986,23 +966,18 @@ void main() { ...@@ -986,23 +966,18 @@ void main() {
), ),
new TestSemantics( new TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.isButton], flags: <SemanticsFlag>[SemanticsFlag.isButton],
previousNodeId: 5,
), ),
new TestSemantics( new TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.isButton], flags: <SemanticsFlag>[SemanticsFlag.isButton],
previousNodeId: 6,
), ),
new TestSemantics( new TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.isButton], flags: <SemanticsFlag>[SemanticsFlag.isButton],
previousNodeId: 3,
), ),
new TestSemantics( new TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.isButton], flags: <SemanticsFlag>[SemanticsFlag.isButton],
previousNodeId: 4,
), ),
new TestSemantics( new TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.isButton], flags: <SemanticsFlag>[SemanticsFlag.isButton],
previousNodeId: 1,
), ),
], ],
), ),
......
...@@ -10,6 +10,7 @@ import 'package:flutter/rendering.dart'; ...@@ -10,6 +10,7 @@ import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
export 'dart:ui' show SemanticsFlag, SemanticsAction;
export 'package:flutter/rendering.dart' show SemanticsData; export 'package:flutter/rendering.dart' show SemanticsData;
const String _matcherHelp = 'Try dumping the semantics with debugDumpSemanticsTree(DebugSemanticsDumpOrder.inverseHitTest) from the package:flutter/rendering.dart library to see what the semantics tree looks like.'; const String _matcherHelp = 'Try dumping the semantics with debugDumpSemanticsTree(DebugSemanticsDumpOrder.inverseHitTest) from the package:flutter/rendering.dart library to see what the semantics tree looks like.';
...@@ -43,8 +44,6 @@ class TestSemantics { ...@@ -43,8 +44,6 @@ class TestSemantics {
this.decreasedValue: '', this.decreasedValue: '',
this.hint: '', this.hint: '',
this.textDirection, this.textDirection,
this.nextNodeId: -1,
this.previousNodeId: -1,
this.rect, this.rect,
this.transform, this.transform,
this.textSelection, this.textSelection,
...@@ -58,8 +57,6 @@ class TestSemantics { ...@@ -58,8 +57,6 @@ class TestSemantics {
assert(decreasedValue != null), assert(decreasedValue != null),
assert(hint != null), assert(hint != null),
assert(children != null), assert(children != null),
assert(nextNodeId != null),
assert(previousNodeId != null),
tags = tags?.toSet() ?? new Set<SemanticsTag>(); tags = tags?.toSet() ?? new Set<SemanticsTag>();
/// Creates an object with some test semantics data, with the [id] and [rect] /// Creates an object with some test semantics data, with the [id] and [rect]
...@@ -87,8 +84,6 @@ class TestSemantics { ...@@ -87,8 +84,6 @@ class TestSemantics {
assert(hint != null), assert(hint != null),
rect = TestSemantics.rootRect, rect = TestSemantics.rootRect,
assert(children != null), assert(children != null),
nextNodeId = null,
previousNodeId = null,
tags = tags?.toSet() ?? new Set<SemanticsTag>(); tags = tags?.toSet() ?? new Set<SemanticsTag>();
/// Creates an object with some test semantics data, with the [id] and [rect] /// Creates an object with some test semantics data, with the [id] and [rect]
...@@ -110,8 +105,6 @@ class TestSemantics { ...@@ -110,8 +105,6 @@ class TestSemantics {
this.increasedValue: '', this.increasedValue: '',
this.decreasedValue: '', this.decreasedValue: '',
this.textDirection, this.textDirection,
this.nextNodeId: -1,
this.previousNodeId: -1,
this.rect, this.rect,
Matrix4 transform, Matrix4 transform,
this.textSelection, this.textSelection,
...@@ -126,8 +119,6 @@ class TestSemantics { ...@@ -126,8 +119,6 @@ class TestSemantics {
assert(hint != null), assert(hint != null),
transform = _applyRootChildScale(transform), transform = _applyRootChildScale(transform),
assert(children != null), assert(children != null),
assert(nextNodeId != null),
assert(previousNodeId != null),
tags = tags?.toSet() ?? new Set<SemanticsTag>(); tags = tags?.toSet() ?? new Set<SemanticsTag>();
/// The unique identifier for this node. /// The unique identifier for this node.
...@@ -180,14 +171,6 @@ class TestSemantics { ...@@ -180,14 +171,6 @@ class TestSemantics {
/// is also set. /// is also set.
final TextDirection textDirection; final TextDirection textDirection;
/// The ID of the node that is next in the semantics traversal order after
/// this node.
final int nextNodeId;
/// The ID of the node that is previous in the semantics traversal order before
/// this node.
final int previousNodeId;
/// The bounding box for this node in its coordinate system. /// The bounding box for this node in its coordinate system.
/// ///
/// Convenient values are available: /// Convenient values are available:
...@@ -232,7 +215,16 @@ class TestSemantics { ...@@ -232,7 +215,16 @@ class TestSemantics {
/// The tags of this node. /// The tags of this node.
final Set<SemanticsTag> tags; final Set<SemanticsTag> tags;
bool _matches(SemanticsNode node, Map<dynamic, dynamic> matchState, { bool ignoreRect: false, bool ignoreTransform: false, bool ignoreId: false }) { bool _matches(
SemanticsNode node,
Map<dynamic, dynamic> matchState,
{
bool ignoreRect: false,
bool ignoreTransform: false,
bool ignoreId: false,
DebugSemanticsDumpOrder childOrder: DebugSemanticsDumpOrder.inverseHitTest,
}
) {
final SemanticsData nodeData = node.getSemanticsData(); final SemanticsData nodeData = node.getSemanticsData();
bool fail(String message) { bool fail(String message) {
...@@ -269,10 +261,6 @@ class TestSemantics { ...@@ -269,10 +261,6 @@ class TestSemantics {
return fail('expected node id $id to have hint "$hint" but found hint "${nodeData.hint}".'); return fail('expected node id $id to have hint "$hint" but found hint "${nodeData.hint}".');
if (textDirection != null && textDirection != nodeData.textDirection) if (textDirection != null && textDirection != nodeData.textDirection)
return fail('expected node id $id to have textDirection "$textDirection" but found "${nodeData.textDirection}".'); return fail('expected node id $id to have textDirection "$textDirection" but found "${nodeData.textDirection}".');
if (!ignoreId && nextNodeId != nodeData.nextNodeId)
return fail('expected node id $id to have nextNodeId "$nextNodeId" but found "${nodeData.nextNodeId}".');
if (!ignoreId && previousNodeId != nodeData.previousNodeId)
return fail('expected node id $id to have previousNodeId "$previousNodeId" but found "${nodeData.previousNodeId}".');
if ((nodeData.label != '' || nodeData.value != '' || nodeData.hint != '' || node.increasedValue != '' || node.decreasedValue != '') && nodeData.textDirection == null) if ((nodeData.label != '' || nodeData.value != '' || nodeData.hint != '' || node.increasedValue != '' || node.decreasedValue != '') && nodeData.textDirection == null)
return fail('expected node id $id, which has a label, value, or hint, to have a textDirection, but it did not.'); return fail('expected node id $id, which has a label, value, or hint, to have a textDirection, but it did not.');
if (!ignoreRect && rect != nodeData.rect) if (!ignoreRect && rect != nodeData.rect)
...@@ -290,14 +278,22 @@ class TestSemantics { ...@@ -290,14 +278,22 @@ class TestSemantics {
return true; return true;
bool result = true; bool result = true;
final Iterator<TestSemantics> it = children.iterator; final Iterator<TestSemantics> it = children.iterator;
node.visitChildren((SemanticsNode node) { for (final SemanticsNode node in node.debugListChildrenInOrder(childOrder)) {
it.moveNext(); it.moveNext();
if (!it.current._matches(node, matchState, ignoreRect: ignoreRect, ignoreTransform: ignoreTransform, ignoreId: ignoreId)) { final bool childMatches = it.current._matches(
node,
matchState,
ignoreRect: ignoreRect,
ignoreTransform: ignoreTransform,
ignoreId: ignoreId,
childOrder: childOrder,
);
if (!childMatches) {
result = false; result = false;
return false; return false;
} }
return true; return true;
}); }
return result; return result;
} }
...@@ -324,10 +320,6 @@ class TestSemantics { ...@@ -324,10 +320,6 @@ class TestSemantics {
buf.writeln('$indent hint: \'$hint\','); buf.writeln('$indent hint: \'$hint\',');
if (textDirection != null) if (textDirection != null)
buf.writeln('$indent textDirection: $textDirection,'); buf.writeln('$indent textDirection: $textDirection,');
if (nextNodeId != null)
buf.writeln('$indent nextNodeId: $nextNodeId,');
if (previousNodeId != null)
buf.writeln('$indent previousNodeId: $previousNodeId,');
if (textSelection?.isValid == true) if (textSelection?.isValid == true)
buf.writeln('$indent textSelection:\n[${textSelection.start}, ${textSelection.end}],'); buf.writeln('$indent textSelection:\n[${textSelection.start}, ${textSelection.end}],');
if (rect != null) if (rect != null)
...@@ -491,9 +483,9 @@ class SemanticsTester { ...@@ -491,9 +483,9 @@ class SemanticsTester {
/// every time and ignore potential regressions. Make sure you do not /// every time and ignore potential regressions. Make sure you do not
/// over-test. Prefer breaking your widgets into smaller widgets and test them /// over-test. Prefer breaking your widgets into smaller widgets and test them
/// individually. /// individually.
String generateTestSemanticsExpressionForCurrentSemanticsTree() { String generateTestSemanticsExpressionForCurrentSemanticsTree(DebugSemanticsDumpOrder childOrder) {
final SemanticsNode node = tester.binding.pipelineOwner.semanticsOwner.rootSemanticsNode; final SemanticsNode node = tester.binding.pipelineOwner.semanticsOwner.rootSemanticsNode;
return _generateSemanticsTestForNode(node, 0); return _generateSemanticsTestForNode(node, 0, childOrder);
} }
static String _flagsToSemanticsFlagExpression(dynamic flags) { static String _flagsToSemanticsFlagExpression(dynamic flags) {
...@@ -524,7 +516,7 @@ class SemanticsTester { ...@@ -524,7 +516,7 @@ class SemanticsTester {
/// Recursively generates [TestSemantics] code for [node] and its children, /// Recursively generates [TestSemantics] code for [node] and its children,
/// indenting the expression by `indentAmount`. /// indenting the expression by `indentAmount`.
static String _generateSemanticsTestForNode(SemanticsNode node, int indentAmount) { static String _generateSemanticsTestForNode(SemanticsNode node, int indentAmount, DebugSemanticsDumpOrder childOrder) {
final String indent = ' ' * indentAmount; final String indent = ' ' * indentAmount;
final StringBuffer buf = new StringBuffer(); final StringBuffer buf = new StringBuffer();
final SemanticsData nodeData = node.getSemanticsData(); final SemanticsData nodeData = node.getSemanticsData();
...@@ -556,19 +548,14 @@ class SemanticsTester { ...@@ -556,19 +548,14 @@ class SemanticsTester {
buf.writeln(' hint: \'${node.hint}\','); buf.writeln(' hint: \'${node.hint}\',');
if (node.textDirection != null) if (node.textDirection != null)
buf.writeln(' textDirection: ${node.textDirection},'); buf.writeln(' textDirection: ${node.textDirection},');
if (node.nextNodeId != null && node.nextNodeId != -1)
buf.writeln(' nextNodeId: ${node.nextNodeId},');
if (node.previousNodeId != null && node.previousNodeId != -1)
buf.writeln(' previousNodeId: ${node.previousNodeId},');
if (node.hasChildren) { if (node.hasChildren) {
buf.writeln(' children: <TestSemantics>['); buf.writeln(' children: <TestSemantics>[');
node.visitChildren((SemanticsNode child) { for (final SemanticsNode child in node.debugListChildrenInOrder(childOrder)) {
buf buf
..write(_generateSemanticsTestForNode(child, 2)) ..write(_generateSemanticsTestForNode(child, 2, childOrder))
..writeln(','); ..writeln(',');
return true; }
});
buf.writeln(' ],'); buf.writeln(' ],');
} }
...@@ -578,18 +565,37 @@ class SemanticsTester { ...@@ -578,18 +565,37 @@ class SemanticsTester {
} }
class _HasSemantics extends Matcher { class _HasSemantics extends Matcher {
const _HasSemantics(this._semantics, { this.ignoreRect: false, this.ignoreTransform: false, this.ignoreId: false }) : assert(_semantics != null), assert(ignoreRect != null), assert(ignoreId != null), assert(ignoreTransform != null); const _HasSemantics(
this._semantics,
{
@required this.ignoreRect,
@required this.ignoreTransform,
@required this.ignoreId,
@required this.childOrder,
}) : assert(_semantics != null),
assert(ignoreRect != null),
assert(ignoreId != null),
assert(ignoreTransform != null),
assert(childOrder != null);
final TestSemantics _semantics; final TestSemantics _semantics;
final bool ignoreRect; final bool ignoreRect;
final bool ignoreTransform; final bool ignoreTransform;
final bool ignoreId; final bool ignoreId;
final DebugSemanticsDumpOrder childOrder;
@override @override
bool matches(covariant SemanticsTester item, Map<dynamic, dynamic> matchState) { bool matches(covariant SemanticsTester item, Map<dynamic, dynamic> matchState) {
final bool doesMatch = _semantics._matches(item.tester.binding.pipelineOwner.semanticsOwner.rootSemanticsNode, matchState, ignoreTransform: ignoreTransform, ignoreRect: ignoreRect, ignoreId: ignoreId); final bool doesMatch = _semantics._matches(
item.tester.binding.pipelineOwner.semanticsOwner.rootSemanticsNode,
matchState,
ignoreTransform: ignoreTransform,
ignoreRect: ignoreRect,
ignoreId: ignoreId,
childOrder: childOrder,
);
if (!doesMatch) { if (!doesMatch) {
matchState['would-match'] = item.generateTestSemanticsExpressionForCurrentSemanticsTree(); matchState['would-match'] = item.generateTestSemanticsExpressionForCurrentSemanticsTree(childOrder);
} }
return doesMatch; return doesMatch;
} }
...@@ -604,7 +610,7 @@ class _HasSemantics extends Matcher { ...@@ -604,7 +610,7 @@ class _HasSemantics extends Matcher {
return mismatchDescription return mismatchDescription
.add('${matchState[TestSemantics]}\n') .add('${matchState[TestSemantics]}\n')
.add('Current SemanticsNode tree:\n') .add('Current SemanticsNode tree:\n')
.add(RendererBinding.instance?.renderView?.debugSemantics?.toStringDeep(childOrder: DebugSemanticsDumpOrder.inverseHitTest)) .add(RendererBinding.instance?.renderView?.debugSemantics?.toStringDeep(childOrder: childOrder))
.add('The semantics tree would have matched the following configuration:\n') .add('The semantics tree would have matched the following configuration:\n')
.add(matchState['would-match']); .add(matchState['would-match']);
} }
...@@ -615,7 +621,16 @@ Matcher hasSemantics(TestSemantics semantics, { ...@@ -615,7 +621,16 @@ Matcher hasSemantics(TestSemantics semantics, {
bool ignoreRect: false, bool ignoreRect: false,
bool ignoreTransform: false, bool ignoreTransform: false,
bool ignoreId: false, bool ignoreId: false,
}) => new _HasSemantics(semantics, ignoreRect: ignoreRect, ignoreTransform: ignoreTransform, ignoreId: ignoreId); DebugSemanticsDumpOrder childOrder: DebugSemanticsDumpOrder.traversalOrder,
}) {
return new _HasSemantics(
semantics,
ignoreRect: ignoreRect,
ignoreTransform: ignoreTransform,
ignoreId: ignoreId,
childOrder: childOrder,
);
}
class _IncludesNodeWith extends Matcher { class _IncludesNodeWith extends Matcher {
const _IncludesNodeWith({ const _IncludesNodeWith({
......
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
import 'dart:async'; import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'dart:ui' show SemanticsFlag;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/semantics.dart'; import 'package:flutter/semantics.dart';
...@@ -55,7 +54,7 @@ void _tests() { ...@@ -55,7 +54,7 @@ void _tests() {
final SemanticsTester semantics = new SemanticsTester(tester); final SemanticsTester semantics = new SemanticsTester(tester);
await pumpTestWidget(tester); await pumpTestWidget(tester);
final String code = semantics final String code = semantics
.generateTestSemanticsExpressionForCurrentSemanticsTree() .generateTestSemanticsExpressionForCurrentSemanticsTree(DebugSemanticsDumpOrder.inverseHitTest)
.split('\n') .split('\n')
.map((String line) => line.trim()) .map((String line) => line.trim())
.join('\n') .join('\n')
...@@ -119,7 +118,6 @@ void _tests() { ...@@ -119,7 +118,6 @@ void _tests() {
tags: <SemanticsTag>[const SemanticsTag('RenderViewport.twoPane')], tags: <SemanticsTag>[const SemanticsTag('RenderViewport.twoPane')],
label: 'Plain text', label: 'Plain text',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
nextNodeId: 4,
), ),
new TestSemantics( new TestSemantics(
id: 4, id: 4,
...@@ -132,7 +130,6 @@ void _tests() { ...@@ -132,7 +130,6 @@ void _tests() {
decreasedValue: 'test-decreasedValue', decreasedValue: 'test-decreasedValue',
hint: 'test-hint', hint: 'test-hint',
textDirection: TextDirection.rtl, textDirection: TextDirection.rtl,
previousNodeId: 3,
), ),
], ],
), ),
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:collection';
import 'dart:math' as math;
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/semantics.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'semantics_tester.dart';
typedef Future<Null> TraversalTestFunction(TraversalTester tester);
const Size tenByTen = const Size(10.0, 10.0);
void main() {
setUp(() {
debugResetSemanticsIdCounter();
});
void testTraversal(String description, TraversalTestFunction testFunction) {
testWidgets(description, (WidgetTester tester) async {
final TraversalTester traversalTester = new TraversalTester(tester);
await testFunction(traversalTester);
traversalTester.dispose();
});
}
// ┌───┐ ┌───┐
// │ A │>│ B │
// └───┘ └───┘
testTraversal('Semantics traverses horizontally left-to-right', (TraversalTester tester) async {
await tester.test(
textDirection: TextDirection.ltr,
children: <String, Rect>{
'A': const Offset(0.0, 0.0) & tenByTen,
'B': const Offset(20.0, 0.0) & tenByTen,
},
expectedTraversal: 'A B',
);
});
// ┌───┐ ┌───┐
// │ A │<│ B │
// └───┘ └───┘
testTraversal('Semantics traverses horizontally right-to-left', (TraversalTester tester) async {
await tester.test(
textDirection: TextDirection.rtl,
children: <String, Rect>{
'A': const Offset(0.0, 0.0) & tenByTen,
'B': const Offset(20.0, 0.0) & tenByTen,
},
expectedTraversal: 'B A',
);
});
// ┌───┐
// │ A │
// └───┘
// V
// ┌───┐
// │ B │
// └───┘
testTraversal('Semantics traverses vertically top-to-bottom', (TraversalTester tester) async {
for (TextDirection textDirection in TextDirection.values) {
await tester.test(
textDirection: textDirection,
children: <String, Rect>{
'A': const Offset(0.0, 0.0) & tenByTen,
'B': const Offset(0.0, 20.0) & tenByTen,
},
expectedTraversal: 'A B',
);
}
});
// ┌───┐ ┌───┐
// │ A │>│ B │
// └───┘ └───┘
// ┌─────┘
// V
// ┌───┐ ┌───┐
// │ C │>│ D │
// └───┘ └───┘
testTraversal('Semantics traverses a grid left-to-right', (TraversalTester tester) async {
await tester.test(
textDirection: TextDirection.ltr,
children: <String, Rect>{
'A': const Offset(0.0, 0.0) & tenByTen,
'B': const Offset(20.0, 0.0) & tenByTen,
'C': const Offset(0.0, 20.0) & tenByTen,
'D': const Offset(20.0, 20.0) & tenByTen,
},
expectedTraversal: 'A B C D',
);
});
// ┌───┐ ┌───┐
// │ A │<│ B │
// └───┘ └───┘
// └─────┐
// V
// ┌───┐ ┌───┐
// │ C │<│ D │
// └───┘ └───┘
testTraversal('Semantics traverses a grid right-to-left', (TraversalTester tester) async {
await tester.test(
textDirection: TextDirection.rtl,
children: <String, Rect>{
'A': const Offset(0.0, 0.0) & tenByTen,
'B': const Offset(20.0, 0.0) & tenByTen,
'C': const Offset(0.0, 20.0) & tenByTen,
'D': const Offset(20.0, 20.0) & tenByTen,
},
expectedTraversal: 'B A D C',
);
});
// ┌───┐ ┌───┐
// │ A │ │ C │
// └───┘<->┌───┐<->└───┘
// │ B │
// └───┘
testTraversal('Semantics traverses vertically overlapping nodes horizontally', (TraversalTester tester) async {
final Map<String, Rect> children = <String, Rect>{
'A': const Offset(0.0, 0.0) & tenByTen,
'B': const Offset(20.0, 5.0) & tenByTen,
'C': const Offset(40.0, 0.0) & tenByTen,
};
await tester.test(
textDirection: TextDirection.ltr,
children: children,
expectedTraversal: 'A B C',
);
await tester.test(
textDirection: TextDirection.rtl,
children: children,
expectedTraversal: 'C B A',
);
});
// LTR:
// ┌───┐ ┌───┐ ┌───┐ ┌───┐
// │ A │>│ B │>│ C │>│ D │
// └───┘ └───┘ └───┘ └───┘
// ┌─────────────────┘
// V
// ┌───┐ ┌─────────┐ ┌───┐
// │ E │ │ │>│ H │
// └───┘ │ G │ └───┘
// V │ │ V
// ┌───┐ │ │ ┌───┐
// │ F │>│ │ │ I │
// └───┘ └─────────┘ └───┘
// ┌─────────────────┘
// V
// ┌───┐ ┌───┐ ┌───┐ ┌───┐
// │ J │>│ K │>│ L │>│ M │
// └───┘ └───┘ └───┘ └───┘
//
// RTL:
// ┌───┐ ┌───┐ ┌───┐ ┌───┐
// │ A │<│ B │<│ C │<│ D │
// └───┘ └───┘ └───┘ └───┘
// └─────────────────┐
// V
// ┌───┐ ┌─────────┐ ┌───┐
// │ E │<│ │ │ H │
// └───┘ │ G │ └───┘
// V │ │ V
// ┌───┐ │ │ ┌───┐
// │ F │ │ │<│ I │
// └───┘ └─────────┘ └───┘
// └─────────────────┐
// V
// ┌───┐ ┌───┐ ┌───┐ ┌───┐
// │ J │<│ K │<│ L │<│ M │
// └───┘ └───┘ └───┘ └───┘
testTraversal('Semantics traverses vertical groups, then horizontal groups, then knots', (TraversalTester tester) async {
final Map<String, Rect> children = <String, Rect>{
'A': const Offset(0.0, 0.0) & tenByTen,
'B': const Offset(20.0, 0.0) & tenByTen,
'C': const Offset(40.0, 0.0) & tenByTen,
'D': const Offset(60.0, 0.0) & tenByTen,
'E': const Offset(0.0, 20.0) & tenByTen,
'F': const Offset(0.0, 40.0) & tenByTen,
'G': const Offset(20.0, 20.0) & (tenByTen * 2.0),
'H': const Offset(60.0, 20.0) & tenByTen,
'I': const Offset(60.0, 40.0) & tenByTen,
'J': const Offset(0.0, 60.0) & tenByTen,
'K': const Offset(20.0, 60.0) & tenByTen,
'L': const Offset(40.0, 60.0) & tenByTen,
'M': const Offset(60.0, 60.0) & tenByTen,
};
await tester.test(
textDirection: TextDirection.ltr,
children: children,
expectedTraversal: 'A B C D E F G H I J K L M',
);
await tester.test(
textDirection: TextDirection.rtl,
children: children,
expectedTraversal: 'D C B A H I G E F M L K J',
);
});
// The following test tests traversal of the simplest "knot", which is two
// nodes overlapping both vertically and horizontally. For example:
//
// ┌─────────┐
// │ │
// │ A │
// │ ┌───┼─────┐
// │ │ │ │
// └─────┼───┘ │
// │ B │
// │ │
// └─────────┘
//
// The outcome depends on the relative positions of the centers of `Rect`s of
// their respective boxes, specifically the direction (i.e. angle) of the
// vector pointing from A to B. We test different angles, one for each octant:
//
// -3π/4 -π/2 -π/4
// ╲ │ ╱
// ╲ 1│2 ╱
// ╲ │ ╱
// i=0 ╲│╱ 3
// π ──────┼────── 0
// 7 ╱│╲ 4
// ╱ │ ╲
// ╱ 6│5 ╲
// ╱ │ ╲
// 3π/4 π/2 π/4
//
// For LTR, angles falling into octants 3, 4, 5, and 6, produce A -> B, all
// others produce B -> A.
//
// For RTL, angles falling into octants 5, 6, 7, and 0, produce A -> B, all
// others produce B -> A.
testTraversal('Semantics sorts knots', (TraversalTester tester) async {
const double start = -math.pi + math.pi / 8.0;
for (int i = 0; i < 8; i += 1) {
final double angle = start + i.toDouble() * math.pi / 4.0;
final double dx = math.cos(angle) * 5.0;
final double dy = math.sin(angle) * 5.0;
final Map<String, Rect> children = <String, Rect>{
'A': const Offset(10.0, 10.0) & tenByTen,
'B': new Offset(10.0 + dx, 10.0 + dy) & tenByTen,
};
try {
await tester.test(
textDirection: TextDirection.ltr,
children: children,
expectedTraversal: 3 <= i && i <= 6 ? 'A B' : 'B A',
);
await tester.test(
textDirection: TextDirection.rtl,
children: children,
expectedTraversal: 1 <= i && i <= 4 ? 'B A' : 'A B',
);
} catch (error) {
fail(
'Test failed with i == $i, angle == ${angle / math.pi}π\n'
'$error'
);
}
}
});
}
class TraversalTester {
TraversalTester(this.tester) : semantics = new SemanticsTester(tester);
final WidgetTester tester;
final SemanticsTester semantics;
Future<Null> test({
TextDirection textDirection,
Map<String, Rect> children,
String expectedTraversal,
}) async {
assert(children is LinkedHashMap);
await tester.pumpWidget(
new Container(
child: new Directionality(
textDirection: textDirection,
child: new Semantics(
textDirection: textDirection,
child: new CustomMultiChildLayout(
delegate: new TestLayoutDelegate(children),
children: children.keys.map<Widget>((String label) {
return new LayoutId(
id: label,
child: new Semantics(
container: true,
explicitChildNodes: true,
label: label,
child: new SizedBox(
width: children[label].width,
height: children[label].height,
),
),
);
}).toList(),
),
),
)
)
);
expect(semantics, hasSemantics(
new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
textDirection: textDirection,
children: expectedTraversal.split(' ').map<TestSemantics>((String label) {
return new TestSemantics(
label: label,
);
}).toList(),
)
],
),
ignoreTransform: true,
ignoreRect: true,
ignoreId: true,
childOrder: DebugSemanticsDumpOrder.traversalOrder,
));
}
void dispose() {
semantics.dispose();
}
}
class TestLayoutDelegate extends MultiChildLayoutDelegate {
TestLayoutDelegate(this.children);
final Map<String, Rect> children;
@override
void performLayout(Size size) {
children.forEach((String label, Rect rect) {
layoutChild(label, new BoxConstraints.loose(size));
positionChild(label, rect.topLeft);
});
}
@override
bool shouldRelayout(MultiChildLayoutDelegate oldDelegate) => oldDelegate == this;
}
...@@ -13,6 +13,16 @@ import 'package:vector_math/vector_math_64.dart'; ...@@ -13,6 +13,16 @@ import 'package:vector_math/vector_math_64.dart';
import 'semantics_tester.dart'; import 'semantics_tester.dart';
void main() { void main() {
group('Sliver Semantics', () {
setUp(() {
debugResetSemanticsIdCounter();
});
_tests();
});
}
void _tests() {
testWidgets('excludeFromScrollable works correctly', (WidgetTester tester) async { testWidgets('excludeFromScrollable works correctly', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester); final SemanticsTester semantics = new SemanticsTester(tester);
...@@ -56,28 +66,24 @@ void main() { ...@@ -56,28 +66,24 @@ void main() {
tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics], tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics],
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 5, id: 6,
actions: <SemanticsAction>[SemanticsAction.scrollUp], actions: <SemanticsAction>[SemanticsAction.scrollUp],
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 2, id: 2,
label: r'Item 0', label: r'Item 0',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
nextNodeId: 3,
previousNodeId: 4,
), ),
new TestSemantics( new TestSemantics(
id: 3, id: 3,
label: r'Item 1', label: r'Item 1',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
previousNodeId: 2,
), ),
new TestSemantics( new TestSemantics(
id: 4, id: 4,
flags: <SemanticsFlag>[SemanticsFlag.namesRoute], flags: <SemanticsFlag>[SemanticsFlag.namesRoute],
label: r'Semantics Test with Slivers', label: r'Semantics Test with Slivers',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
nextNodeId: 2,
), ),
], ],
), ),
...@@ -102,28 +108,23 @@ void main() { ...@@ -102,28 +108,23 @@ void main() {
tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics], tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics],
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 5, id: 6,
actions: <SemanticsAction>[SemanticsAction.scrollUp, SemanticsAction.scrollDown], actions: <SemanticsAction>[SemanticsAction.scrollUp, SemanticsAction.scrollDown],
nextNodeId: 4,
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 2, id: 2,
label: r'Item 0', label: r'Item 0',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
nextNodeId: 3,
), ),
new TestSemantics( new TestSemantics(
id: 3, id: 3,
label: r'Item 1', label: r'Item 1',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
nextNodeId: 6,
previousNodeId: 2,
), ),
new TestSemantics( new TestSemantics(
id: 6, id: 7,
label: r'Item 2', label: r'Item 2',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
previousNodeId: 3,
), ),
], ],
), ),
...@@ -132,7 +133,6 @@ void main() { ...@@ -132,7 +133,6 @@ void main() {
flags: <SemanticsFlag>[SemanticsFlag.namesRoute], flags: <SemanticsFlag>[SemanticsFlag.namesRoute],
label: r'Semantics Test with Slivers', label: r'Semantics Test with Slivers',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
previousNodeId: 5,
), ),
], ],
), ),
...@@ -155,35 +155,29 @@ void main() { ...@@ -155,35 +155,29 @@ void main() {
tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics], tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics],
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 5, id: 6,
actions: <SemanticsAction>[SemanticsAction.scrollUp, SemanticsAction.scrollDown], actions: <SemanticsAction>[SemanticsAction.scrollUp, SemanticsAction.scrollDown],
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 2, id: 2,
label: r'Item 0', label: r'Item 0',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
nextNodeId: 3,
previousNodeId: 4,
), ),
new TestSemantics( new TestSemantics(
id: 3, id: 3,
label: r'Item 1', label: r'Item 1',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
nextNodeId: 6,
previousNodeId: 2,
), ),
new TestSemantics( new TestSemantics(
id: 6, id: 7,
label: r'Item 2', label: r'Item 2',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
previousNodeId: 3,
), ),
new TestSemantics( new TestSemantics(
id: 4, id: 4,
flags: <SemanticsFlag>[SemanticsFlag.namesRoute], flags: <SemanticsFlag>[SemanticsFlag.namesRoute],
label: r'Semantics Test with Slivers', label: r'Semantics Test with Slivers',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
nextNodeId: 2,
), ),
], ],
), ),
...@@ -233,24 +227,22 @@ void main() { ...@@ -233,24 +227,22 @@ void main() {
new TestSemantics.root( new TestSemantics.root(
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 7, id: 1,
tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics], tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics],
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 10, id: 4,
actions: <SemanticsAction>[SemanticsAction.scrollUp, SemanticsAction.scrollDown], actions: <SemanticsAction>[SemanticsAction.scrollUp, SemanticsAction.scrollDown],
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 8, id: 2,
label: 'Item 2', label: 'Item 2',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
previousNodeId: 9,
), ),
new TestSemantics( new TestSemantics(
id: 9, id: 3,
label: 'Item 1', label: 'Item 1',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
nextNodeId: 8,
), ),
], ],
), ),
...@@ -289,44 +281,36 @@ void main() { ...@@ -289,44 +281,36 @@ void main() {
new TestSemantics.root( new TestSemantics.root(
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 11, id: 1,
tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics], tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics],
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 17, id: 7,
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 12, id: 2,
label: 'Item 4', label: 'Item 4',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
previousNodeId: 13,
), ),
new TestSemantics( new TestSemantics(
id: 13, id: 3,
label: 'Item 3', label: 'Item 3',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
nextNodeId: 12,
previousNodeId: 14,
), ),
new TestSemantics( new TestSemantics(
id: 14, id: 4,
label: 'Item 2', label: 'Item 2',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
nextNodeId: 13,
previousNodeId: 15,
), ),
new TestSemantics( new TestSemantics(
id: 15, id: 5,
label: 'Item 1', label: 'Item 1',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
nextNodeId: 14,
previousNodeId: 16,
), ),
new TestSemantics( new TestSemantics(
id: 16, id: 6,
label: 'Item 0', label: 'Item 0',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
nextNodeId: 15,
), ),
], ],
), ),
...@@ -378,49 +362,50 @@ void main() { ...@@ -378,49 +362,50 @@ void main() {
new TestSemantics.root( new TestSemantics.root(
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 18, id: 1,
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics], tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics],
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 23, id: 7,
nextNodeId: 22,
actions: SemanticsAction.scrollUp.index | SemanticsAction.scrollDown.index, actions: SemanticsAction.scrollUp.index | SemanticsAction.scrollDown.index,
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
children: <TestSemantics>[ children: <TestSemantics>[
// Item 0 is missing because its covered by the app bar. // Item 0 is missing because its covered by the app bar.
new TestSemantics( new TestSemantics(
id: 19, id: 2,
nextNodeId: 20,
rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0), rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0),
// Item 1 starts 20.0dp below edge, so there would be room for Item 0. // Item 1 starts 20.0dp below edge, so there would be room for Item 0.
transform: new Matrix4.translation(new Vector3(0.0, 20.0, 0.0)), transform: new Matrix4.translation(new Vector3(0.0, 20.0, 0.0)),
label: 'Item 1', label: 'Item 1',
), ),
new TestSemantics( new TestSemantics(
id: 20, id: 3,
nextNodeId: 21,
previousNodeId: 19,
rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0), rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0),
transform: new Matrix4.translation(new Vector3(0.0, 220.0, 0.0)), transform: new Matrix4.translation(new Vector3(0.0, 220.0, 0.0)),
label: 'Item 2', label: 'Item 2',
), ),
new TestSemantics( new TestSemantics(
id: 21, id: 4,
rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0), rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0),
transform: new Matrix4.translation(new Vector3(0.0, 420.0, 0.0)), transform: new Matrix4.translation(new Vector3(0.0, 420.0, 0.0)),
label: 'Item 3', label: 'Item 3',
previousNodeId: 20,
), ),
], ],
), ),
new TestSemantics( new TestSemantics(
id: 22, id: 5,
rect: new Rect.fromLTRB(0.0, 0.0, 120.0, 20.0), rect: new Rect.fromLTRB(0.0, 0.0, 120.0, 20.0),
flags: <SemanticsFlag>[SemanticsFlag.namesRoute], flags: <SemanticsFlag>[SemanticsFlag.namesRoute],
tags: <SemanticsTag>[RenderViewport.excludeFromScrolling], tags: <SemanticsTag>[RenderViewport.excludeFromScrolling],
label: 'AppBar', children: <TestSemantics>[
previousNodeId: 23, new TestSemantics(
id: 6,
label: 'AppBar',
rect: new Rect.fromLTRB(0.0, 0.0, 120.0, 20.0),
textDirection: TextDirection.ltr,
),
],
), ),
], ],
) )
...@@ -468,34 +453,29 @@ void main() { ...@@ -468,34 +453,29 @@ void main() {
new TestSemantics.root( new TestSemantics.root(
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 24, id: 1,
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics], tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics],
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 29, id: 7,
nextNodeId: 28,
actions: SemanticsAction.scrollUp.index | SemanticsAction.scrollDown.index, actions: SemanticsAction.scrollUp.index | SemanticsAction.scrollDown.index,
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 25, id: 2,
previousNodeId: 26,
rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0), rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0),
transform: new Matrix4.translation(new Vector3(0.0, 420.0, 0.0)), transform: new Matrix4.translation(new Vector3(0.0, 420.0, 0.0)),
label: 'Item 3', label: 'Item 3',
), ),
new TestSemantics( new TestSemantics(
id: 26, id: 3,
nextNodeId: 25,
previousNodeId: 27,
rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0), rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0),
transform: new Matrix4.translation(new Vector3(0.0, 220.0, 0.0)), transform: new Matrix4.translation(new Vector3(0.0, 220.0, 0.0)),
label: 'Item 2', label: 'Item 2',
), ),
new TestSemantics( new TestSemantics(
id: 27, id: 4,
nextNodeId: 26,
rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0), rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0),
// Item 1 starts 20.0dp below edge, so there would be room for Item 0. // Item 1 starts 20.0dp below edge, so there would be room for Item 0.
transform: new Matrix4.translation(new Vector3(0.0, 20.0, 0.0)), transform: new Matrix4.translation(new Vector3(0.0, 20.0, 0.0)),
...@@ -505,12 +485,17 @@ void main() { ...@@ -505,12 +485,17 @@ void main() {
], ],
), ),
new TestSemantics( new TestSemantics(
id: 28, id: 5,
previousNodeId: 29,
rect: new Rect.fromLTRB(0.0, 0.0, 120.0, 20.0), rect: new Rect.fromLTRB(0.0, 0.0, 120.0, 20.0),
flags: <SemanticsFlag>[SemanticsFlag.namesRoute], flags: <SemanticsFlag>[SemanticsFlag.namesRoute],
tags: <SemanticsTag>[RenderViewport.excludeFromScrolling], tags: <SemanticsTag>[RenderViewport.excludeFromScrolling],
label: 'AppBar' children: <TestSemantics>[
new TestSemantics(
id: 6,
rect: new Rect.fromLTRB(0.0, 0.0, 120.0, 20.0),
label: 'AppBar',
),
],
), ),
], ],
) )
...@@ -560,50 +545,48 @@ void main() { ...@@ -560,50 +545,48 @@ void main() {
new TestSemantics.root( new TestSemantics.root(
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 30, id: 1,
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics], tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics],
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 35, id: 7,
nextNodeId: 34,
actions: SemanticsAction.scrollUp.index | SemanticsAction.scrollDown.index, actions: SemanticsAction.scrollUp.index | SemanticsAction.scrollDown.index,
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
children: <TestSemantics>[ children: <TestSemantics>[
// Item 0 is missing because its covered by the app bar. // Item 0 is missing because its covered by the app bar.
new TestSemantics( new TestSemantics(
id: 31, id: 2,
previousNodeId: 32,
// Item 1 ends at 580dp, so there would be 20dp space for Item 0. // Item 1 ends at 580dp, so there would be 20dp space for Item 0.
rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0), rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0),
transform: new Matrix4.translation(new Vector3(0.0, 380.0, 0.0)),
label: 'Item 1', label: 'Item 1',
), ),
new TestSemantics( new TestSemantics(
id: 32, id: 3,
nextNodeId: 31,
previousNodeId: 33,
rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0), rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0),
transform: new Matrix4.translation(new Vector3(0.0, 180.0, 0.0)),
label: 'Item 2', label: 'Item 2',
), ),
new TestSemantics( new TestSemantics(
id: 33, id: 4,
nextNodeId: 32,
rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0), rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0),
transform: new Matrix4.translation(new Vector3(0.0, -20.0, 0.0)),
label: 'Item 3', label: 'Item 3',
), ),
], ],
), ),
new TestSemantics( new TestSemantics(
id: 34, id: 5,
previousNodeId: 35,
rect: new Rect.fromLTRB(0.0, 0.0, 120.0, 20.0), rect: new Rect.fromLTRB(0.0, 0.0, 120.0, 20.0),
transform: new Matrix4.translation(new Vector3(0.0, 544.0, 0.0)), transform: new Matrix4.translation(new Vector3(0.0, 544.0, 0.0)),
flags: <SemanticsFlag>[SemanticsFlag.namesRoute], flags: <SemanticsFlag>[SemanticsFlag.namesRoute],
tags: <SemanticsTag>[RenderViewport.excludeFromScrolling], tags: <SemanticsTag>[RenderViewport.excludeFromScrolling],
label: 'AppBar' children: <TestSemantics>[
new TestSemantics(
id: 6,
rect: new Rect.fromLTRB(0.0, 0.0, 120.0, 20.0),
label: 'AppBar',
textDirection: TextDirection.ltr,
),
],
), ),
], ],
) )
...@@ -652,34 +635,29 @@ void main() { ...@@ -652,34 +635,29 @@ void main() {
new TestSemantics.root( new TestSemantics.root(
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 36, id: 1,
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics], tags: <SemanticsTag>[RenderViewport.useTwoPaneSemantics],
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 41, id: 7,
nextNodeId: 40,
actions: SemanticsAction.scrollUp.index | SemanticsAction.scrollDown.index, actions: SemanticsAction.scrollUp.index | SemanticsAction.scrollDown.index,
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 37, id: 2,
nextNodeId: 38,
rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0), rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0),
transform: new Matrix4.translation(new Vector3(0.0, -20.0, 0.0)), transform: new Matrix4.translation(new Vector3(0.0, -20.0, 0.0)),
label: 'Item 3', label: 'Item 3',
), ),
new TestSemantics( new TestSemantics(
id: 38, id: 3,
nextNodeId: 39,
previousNodeId: 37,
rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0), rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0),
transform: new Matrix4.translation(new Vector3(0.0, 180.0, 0.0)), transform: new Matrix4.translation(new Vector3(0.0, 180.0, 0.0)),
label: 'Item 2', label: 'Item 2',
), ),
new TestSemantics( new TestSemantics(
id: 39, id: 4,
previousNodeId: 38,
rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0), rect: new Rect.fromLTRB(0.0, 0.0, 800.0, 200.0),
// Item 1 ends at 580dp, so there would be 20dp space for Item 0. // Item 1 ends at 580dp, so there would be 20dp space for Item 0.
transform: new Matrix4.translation(new Vector3(0.0, 380.0, 0.0)), transform: new Matrix4.translation(new Vector3(0.0, 380.0, 0.0)),
...@@ -689,13 +667,20 @@ void main() { ...@@ -689,13 +667,20 @@ void main() {
], ],
), ),
new TestSemantics( new TestSemantics(
id: 40, id: 5,
previousNodeId: 41,
rect: new Rect.fromLTRB(0.0, 0.0, 120.0, 20.0), rect: new Rect.fromLTRB(0.0, 0.0, 120.0, 20.0),
transform: new Matrix4.translation(new Vector3(0.0, 544.0, 0.0)), transform: new Matrix4.translation(new Vector3(0.0, 544.0, 0.0)),
flags: <SemanticsFlag>[SemanticsFlag.namesRoute], flags: <SemanticsFlag>[SemanticsFlag.namesRoute],
tags: <SemanticsTag>[RenderViewport.excludeFromScrolling], tags: <SemanticsTag>[RenderViewport.excludeFromScrolling],
label: 'AppBar' children: <TestSemantics>[
new TestSemantics(
id: 6,
rect: new Rect.fromLTRB(0.0, 0.0, 120.0, 20.0),
transform: new Matrix4.translation(new Vector3(0.0, 544.0, 0.0)),
label: 'AppBar',
textDirection: TextDirection.ltr,
),
],
), ),
], ],
) )
......
...@@ -195,9 +195,9 @@ class FlutterDevice { ...@@ -195,9 +195,9 @@ class FlutterDevice {
await view.uiIsolate.flutterDebugDumpLayerTree(); await view.uiIsolate.flutterDebugDumpLayerTree();
} }
Future<Null> debugDumpSemanticsTreeInGeometricOrder() async { Future<Null> debugDumpSemanticsTreeInTraversalOrder() async {
for (FlutterView view in views) for (FlutterView view in views)
await view.uiIsolate.flutterDebugDumpSemanticsTreeInGeometricOrder(); await view.uiIsolate.flutterDebugDumpSemanticsTreeInTraversalOrder();
} }
Future<Null> debugDumpSemanticsTreeInInverseHitTestOrder() async { Future<Null> debugDumpSemanticsTreeInInverseHitTestOrder() async {
...@@ -505,10 +505,10 @@ abstract class ResidentRunner { ...@@ -505,10 +505,10 @@ abstract class ResidentRunner {
await device.debugDumpLayerTree(); await device.debugDumpLayerTree();
} }
Future<Null> _debugDumpSemanticsTreeInGeometricOrder() async { Future<Null> _debugDumpSemanticsTreeInTraversalOrder() async {
await refreshViews(); await refreshViews();
for (FlutterDevice device in flutterDevices) for (FlutterDevice device in flutterDevices)
await device.debugDumpSemanticsTreeInGeometricOrder(); await device.debugDumpSemanticsTreeInTraversalOrder();
} }
Future<Null> _debugDumpSemanticsTreeInInverseHitTestOrder() async { Future<Null> _debugDumpSemanticsTreeInInverseHitTestOrder() async {
...@@ -691,7 +691,7 @@ abstract class ResidentRunner { ...@@ -691,7 +691,7 @@ abstract class ResidentRunner {
} }
} else if (character == 'S') { } else if (character == 'S') {
if (supportsServiceProtocol) { if (supportsServiceProtocol) {
await _debugDumpSemanticsTreeInGeometricOrder(); await _debugDumpSemanticsTreeInTraversalOrder();
return true; return true;
} }
} else if (character == 'U') { } else if (character == 'U') {
...@@ -832,12 +832,12 @@ abstract class ResidentRunner { ...@@ -832,12 +832,12 @@ abstract class ResidentRunner {
printStatus('You can dump the widget hierarchy of the app (debugDumpApp) by pressing "w".'); printStatus('You can dump the widget hierarchy of the app (debugDumpApp) by pressing "w".');
printStatus('To dump the rendering tree of the app (debugDumpRenderTree), press "t".'); printStatus('To dump the rendering tree of the app (debugDumpRenderTree), press "t".');
if (isRunningDebug) { if (isRunningDebug) {
printStatus('For layers (debugDumpLayerTree), use "L"; for accessibility (debugDumpSemantics), use "S" (for geometric order) or "U" (for inverse hit test order).'); printStatus('For layers (debugDumpLayerTree), use "L"; for accessibility (debugDumpSemantics), use "S" (for traversal order) or "U" (for inverse hit test order).');
printStatus('To toggle the widget inspector (WidgetsApp.showWidgetInspectorOverride), press "i".'); printStatus('To toggle the widget inspector (WidgetsApp.showWidgetInspectorOverride), press "i".');
printStatus('To toggle the display of construction lines (debugPaintSizeEnabled), press "p".'); printStatus('To toggle the display of construction lines (debugPaintSizeEnabled), press "p".');
printStatus('To simulate different operating systems, (defaultTargetPlatform), press "o".'); printStatus('To simulate different operating systems, (defaultTargetPlatform), press "o".');
} else { } else {
printStatus('To dump the accessibility tree (debugDumpSemantics), press "S" (for geometric order) or "U" (for inverse hit test order).'); printStatus('To dump the accessibility tree (debugDumpSemantics), press "S" (for traversal order) or "U" (for inverse hit test order).');
} }
printStatus('To display the performance overlay (WidgetsApp.showPerformanceOverlay), press "P".'); printStatus('To display the performance overlay (WidgetsApp.showPerformanceOverlay), press "P".');
} }
......
...@@ -1201,8 +1201,8 @@ class Isolate extends ServiceObjectOwner { ...@@ -1201,8 +1201,8 @@ class Isolate extends ServiceObjectOwner {
return invokeFlutterExtensionRpcRaw('ext.flutter.debugDumpLayerTree', timeout: kLongRequestTimeout); return invokeFlutterExtensionRpcRaw('ext.flutter.debugDumpLayerTree', timeout: kLongRequestTimeout);
} }
Future<Map<String, dynamic>> flutterDebugDumpSemanticsTreeInGeometricOrder() { Future<Map<String, dynamic>> flutterDebugDumpSemanticsTreeInTraversalOrder() {
return invokeFlutterExtensionRpcRaw('ext.flutter.debugDumpSemanticsTreeInGeometricOrder', timeout: kLongRequestTimeout); return invokeFlutterExtensionRpcRaw('ext.flutter.debugDumpSemanticsTreeInTraversalOrder', timeout: kLongRequestTimeout);
} }
Future<Map<String, dynamic>> flutterDebugDumpSemanticsTreeInInverseHitTestOrder() { Future<Map<String, dynamic>> flutterDebugDumpSemanticsTreeInInverseHitTestOrder() {
......
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