Commit 437e4c08 authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

Make "mergeIntoParent" information available during compile of semantics tree (#12332)

* ++

* Allow unmerging of SemanticsNodes

* test passing

* ++

* remove prints

* doc comments

* rectify comment

* review comments
parent 0044ea2d
...@@ -710,7 +710,12 @@ abstract class _InterestingSemanticsFragment extends _SemanticsFragment { ...@@ -710,7 +710,12 @@ abstract class _InterestingSemanticsFragment extends _SemanticsFragment {
SemanticsAnnotator annotator, SemanticsAnnotator annotator,
Iterable<_SemanticsFragment> children, Iterable<_SemanticsFragment> children,
bool dropSemanticsOfPreviousSiblings, bool dropSemanticsOfPreviousSiblings,
}) : super(renderObjectOwner: renderObjectOwner, annotator: annotator, children: children, dropSemanticsOfPreviousSiblings: dropSemanticsOfPreviousSiblings); }) : super(
renderObjectOwner: renderObjectOwner,
annotator: annotator,
children: children,
dropSemanticsOfPreviousSiblings: dropSemanticsOfPreviousSiblings,
);
@override @override
Iterable<SemanticsNode> compile({ _SemanticsGeometry geometry, SemanticsNode currentSemantics, SemanticsNode parentSemantics }) sync* { Iterable<SemanticsNode> compile({ _SemanticsGeometry geometry, SemanticsNode currentSemantics, SemanticsNode parentSemantics }) sync* {
...@@ -743,7 +748,12 @@ class _RootSemanticsFragment extends _InterestingSemanticsFragment { ...@@ -743,7 +748,12 @@ class _RootSemanticsFragment extends _InterestingSemanticsFragment {
SemanticsAnnotator annotator, SemanticsAnnotator annotator,
Iterable<_SemanticsFragment> children, Iterable<_SemanticsFragment> children,
bool dropSemanticsOfPreviousSiblings, bool dropSemanticsOfPreviousSiblings,
}) : super(renderObjectOwner: renderObjectOwner, annotator: annotator, children: children, dropSemanticsOfPreviousSiblings: dropSemanticsOfPreviousSiblings); }) : super(
renderObjectOwner: renderObjectOwner,
annotator: annotator,
children: children,
dropSemanticsOfPreviousSiblings: dropSemanticsOfPreviousSiblings,
);
@override @override
SemanticsNode establishSemanticsNode(_SemanticsGeometry geometry, SemanticsNode currentSemantics, SemanticsNode parentSemantics) { SemanticsNode establishSemanticsNode(_SemanticsGeometry geometry, SemanticsNode currentSemantics, SemanticsNode parentSemantics) {
...@@ -787,7 +797,19 @@ class _ConcreteSemanticsFragment extends _InterestingSemanticsFragment { ...@@ -787,7 +797,19 @@ class _ConcreteSemanticsFragment extends _InterestingSemanticsFragment {
SemanticsAnnotator annotator, SemanticsAnnotator annotator,
Iterable<_SemanticsFragment> children, Iterable<_SemanticsFragment> children,
bool dropSemanticsOfPreviousSiblings, bool dropSemanticsOfPreviousSiblings,
}) : super(renderObjectOwner: renderObjectOwner, annotator: annotator, children: children, dropSemanticsOfPreviousSiblings: dropSemanticsOfPreviousSiblings); bool mergeIntoParent,
bool mergesAllDescendants,
}) : _mergeIntoParent = mergeIntoParent,
_mergesAllDescendants = mergesAllDescendants,
super(
renderObjectOwner: renderObjectOwner,
annotator: annotator,
children: children,
dropSemanticsOfPreviousSiblings: dropSemanticsOfPreviousSiblings,
);
final bool _mergeIntoParent;
final bool _mergesAllDescendants;
@override @override
SemanticsNode establishSemanticsNode(_SemanticsGeometry geometry, SemanticsNode currentSemantics, SemanticsNode parentSemantics) { SemanticsNode establishSemanticsNode(_SemanticsGeometry geometry, SemanticsNode currentSemantics, SemanticsNode parentSemantics) {
...@@ -796,6 +818,8 @@ class _ConcreteSemanticsFragment extends _InterestingSemanticsFragment { ...@@ -796,6 +818,8 @@ class _ConcreteSemanticsFragment extends _InterestingSemanticsFragment {
showOnScreen: renderObjectOwner.showOnScreen, showOnScreen: renderObjectOwner.showOnScreen,
); );
final SemanticsNode node = renderObjectOwner._semantics; final SemanticsNode node = renderObjectOwner._semantics;
node.isMergedIntoParent = _mergeIntoParent;
node.mergeAllDescendantsIntoThisNode = _mergesAllDescendants;
if (geometry != null) { if (geometry != null) {
geometry.applyAncestorChain(_ancestorChain); geometry.applyAncestorChain(_ancestorChain);
geometry.updateSemanticsNode(rendering: renderObjectOwner, semantics: node, parentSemantics: parentSemantics); geometry.updateSemanticsNode(rendering: renderObjectOwner, semantics: node, parentSemantics: parentSemantics);
...@@ -830,27 +854,43 @@ class _ImplicitSemanticsFragment extends _InterestingSemanticsFragment { ...@@ -830,27 +854,43 @@ class _ImplicitSemanticsFragment extends _InterestingSemanticsFragment {
SemanticsAnnotator annotator, SemanticsAnnotator annotator,
Iterable<_SemanticsFragment> children, Iterable<_SemanticsFragment> children,
bool dropSemanticsOfPreviousSiblings, bool dropSemanticsOfPreviousSiblings,
}) : super(renderObjectOwner: renderObjectOwner, annotator: annotator, children: children, dropSemanticsOfPreviousSiblings: dropSemanticsOfPreviousSiblings); bool mergeIntoParent,
bool mergesAllDescendants,
}) : _mergeIntoParent = mergeIntoParent,
_mergesAllDescendants = mergesAllDescendants,
super(
renderObjectOwner: renderObjectOwner,
annotator: annotator,
children: children,
dropSemanticsOfPreviousSiblings: dropSemanticsOfPreviousSiblings,
);
// If true, this fragment will introduce its own node into the Semantics Tree. // If true, this fragment will introduce its own node into the Semantics Tree.
// If false, a borrowed semantics node from an ancestor is used. // If false, a borrowed semantics node from an ancestor is used.
bool _introducesOwnNode; bool _introducesOwnNode;
final bool _mergeIntoParent;
final bool _mergesAllDescendants;
@override @override
SemanticsNode establishSemanticsNode(_SemanticsGeometry geometry, SemanticsNode currentSemantics, SemanticsNode parentSemantics) { SemanticsNode establishSemanticsNode(_SemanticsGeometry geometry, SemanticsNode currentSemantics, SemanticsNode parentSemantics) {
SemanticsNode node; SemanticsNode node;
assert(_introducesOwnNode == null); assert(_introducesOwnNode == null);
_introducesOwnNode = currentSemantics == null && annotator != null; assert(annotator != null || _mergesAllDescendants);
_introducesOwnNode = currentSemantics == null;
if (_introducesOwnNode) { if (_introducesOwnNode) {
renderObjectOwner._semantics ??= new SemanticsNode( renderObjectOwner._semantics ??= new SemanticsNode(
handler: renderObjectOwner is SemanticsActionHandler ? renderObjectOwner as dynamic : null, handler: renderObjectOwner is SemanticsActionHandler ? renderObjectOwner as dynamic : null,
showOnScreen: renderObjectOwner.showOnScreen, showOnScreen: renderObjectOwner.showOnScreen,
); );
node = renderObjectOwner._semantics; node = renderObjectOwner._semantics;
node.isMergedIntoParent = _mergeIntoParent;
} else { } else {
renderObjectOwner._semantics = null; renderObjectOwner._semantics = null;
node = currentSemantics; node = currentSemantics;
} }
if (!node.mergeAllDescendantsIntoThisNode)
node.mergeAllDescendantsIntoThisNode = _mergesAllDescendants;
if (geometry != null) { if (geometry != null) {
geometry.applyAncestorChain(_ancestorChain); geometry.applyAncestorChain(_ancestorChain);
if (_introducesOwnNode) if (_introducesOwnNode)
...@@ -2513,6 +2553,14 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im ...@@ -2513,6 +2553,14 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
/// determine if a node is previous to this one. /// determine if a node is previous to this one.
bool get isBlockingSemanticsOfPreviouslyPaintedNodes => false; bool get isBlockingSemanticsOfPreviouslyPaintedNodes => false;
/// Whether the semantic information provided by this [RenderObject] and all
/// of its descendants should be treated as one logical entity.
///
/// If true is returned, the descendants of this [RenderObject]'s
/// [SemanticsNode] will merge their semantic information into the
/// [SemanticsNode] representing this [RenderObject].
bool get isMergingSemanticsOfDescendants => false;
/// The bounding box, in the local coordinate system, of this /// The bounding box, in the local coordinate system, of this
/// object, for accessibility purposes. /// object, for accessibility purposes.
Rect get semanticBounds; Rect get semanticBounds;
...@@ -2662,7 +2710,7 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im ...@@ -2662,7 +2710,7 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
try { try {
assert(_needsSemanticsUpdate); assert(_needsSemanticsUpdate);
assert(_semantics != null || parent is! RenderObject); assert(_semantics != null || parent is! RenderObject);
final _SemanticsFragment fragment = _getSemanticsFragment(); final _SemanticsFragment fragment = _getSemanticsFragment(mergeIntoParent: _semantics?.parent?.isPartOfNodeMerging ?? false);
assert(fragment is _InterestingSemanticsFragment); assert(fragment is _InterestingSemanticsFragment);
final SemanticsNode node = fragment.compile(parentSemantics: _semantics?.parent).single; final SemanticsNode node = fragment.compile(parentSemantics: _semantics?.parent).single;
assert(node != null); assert(node != null);
...@@ -2678,14 +2726,20 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im ...@@ -2678,14 +2726,20 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
/// children collecting [_SemanticsFragments] for them, and then returns an /// children collecting [_SemanticsFragments] for them, and then returns an
/// appropriate [_SemanticsFragment] object that describes the RenderObject's /// appropriate [_SemanticsFragment] object that describes the RenderObject's
/// semantics. /// semantics.
_SemanticsFragment _getSemanticsFragment() { _SemanticsFragment _getSemanticsFragment({ bool mergeIntoParent: false }) {
// early-exit if we're not dirty and have our own semantics // early-exit if we're not dirty and have our own semantics
if (!_needsSemanticsUpdate && isSemanticBoundary) { if (!_needsSemanticsUpdate && isSemanticBoundary) {
assert(_semantics != null); assert(_semantics != null);
return new _CleanSemanticsFragment(renderObjectOwner: this, dropSemanticsOfPreviousSiblings: isBlockingSemanticsOfPreviouslyPaintedNodes); if (mergeIntoParent == _semantics.isMergedIntoParent) {
return new _CleanSemanticsFragment(
renderObjectOwner: this,
dropSemanticsOfPreviousSiblings: isBlockingSemanticsOfPreviouslyPaintedNodes,
);
}
} }
List<_SemanticsFragment> children; List<_SemanticsFragment> children;
bool dropSemanticsOfPreviousSiblings = isBlockingSemanticsOfPreviouslyPaintedNodes; bool dropSemanticsOfPreviousSiblings = isBlockingSemanticsOfPreviouslyPaintedNodes;
final bool childrenMergeIntoParent = mergeIntoParent || isMergingSemanticsOfDescendants;
visitChildrenForSemantics((RenderObject child) { visitChildrenForSemantics((RenderObject child) {
if (_needsSemanticsGeometryUpdate) { if (_needsSemanticsGeometryUpdate) {
// If our geometry changed, make sure the child also does a // If our geometry changed, make sure the child also does a
...@@ -2694,7 +2748,7 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im ...@@ -2694,7 +2748,7 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
child._needsSemanticsUpdate = true; child._needsSemanticsUpdate = true;
child._needsSemanticsGeometryUpdate = true; child._needsSemanticsGeometryUpdate = true;
} }
final _SemanticsFragment fragment = child._getSemanticsFragment(); final _SemanticsFragment fragment = child._getSemanticsFragment(mergeIntoParent: childrenMergeIntoParent);
assert(fragment != null); assert(fragment != null);
if (fragment.dropSemanticsOfPreviousSiblings) { if (fragment.dropSemanticsOfPreviousSiblings) {
children = null; // throw away all left siblings of [child]. children = null; // throw away all left siblings of [child].
...@@ -2714,19 +2768,51 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im ...@@ -2714,19 +2768,51 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
_needsSemanticsUpdate = false; _needsSemanticsUpdate = false;
_needsSemanticsGeometryUpdate = false; _needsSemanticsGeometryUpdate = false;
final SemanticsAnnotator annotator = semanticsAnnotator; final SemanticsAnnotator annotator = semanticsAnnotator;
if (parent is! RenderObject) if (parent is! RenderObject) {
return new _RootSemanticsFragment(renderObjectOwner: this, annotator: annotator, children: children, dropSemanticsOfPreviousSiblings: dropSemanticsOfPreviousSiblings); assert(!mergeIntoParent);
if (isSemanticBoundary) assert(!isMergingSemanticsOfDescendants);
return new _ConcreteSemanticsFragment(renderObjectOwner: this, annotator: annotator, children: children, dropSemanticsOfPreviousSiblings: dropSemanticsOfPreviousSiblings); return new _RootSemanticsFragment(
if (annotator != null) renderObjectOwner: this,
return new _ImplicitSemanticsFragment(renderObjectOwner: this, annotator: annotator, children: children, dropSemanticsOfPreviousSiblings: dropSemanticsOfPreviousSiblings); annotator: annotator,
children: children,
dropSemanticsOfPreviousSiblings: dropSemanticsOfPreviousSiblings,
);
}
if (isSemanticBoundary) {
return new _ConcreteSemanticsFragment(
renderObjectOwner: this,
annotator: annotator,
children: children,
dropSemanticsOfPreviousSiblings: dropSemanticsOfPreviousSiblings,
mergeIntoParent: mergeIntoParent,
mergesAllDescendants: isMergingSemanticsOfDescendants,
);
}
if (annotator != null || isMergingSemanticsOfDescendants) {
return new _ImplicitSemanticsFragment(
renderObjectOwner: this,
annotator: annotator,
children: children,
dropSemanticsOfPreviousSiblings: dropSemanticsOfPreviousSiblings,
mergeIntoParent: mergeIntoParent,
mergesAllDescendants: isMergingSemanticsOfDescendants,
);
}
_semantics = null; _semantics = null;
if (children == null) { if (children == null) {
// Introduces no semantics and has no descendants that introduce semantics. // Introduces no semantics and has no descendants that introduce semantics.
return new _EmptySemanticsFragment(renderObjectOwner: this, dropSemanticsOfPreviousSiblings: dropSemanticsOfPreviousSiblings); return new _EmptySemanticsFragment(
renderObjectOwner: this,
dropSemanticsOfPreviousSiblings: dropSemanticsOfPreviousSiblings,
);
}
if (children.length > 1) {
return new _ForkingSemanticsFragment(
renderObjectOwner: this,
children: children,
dropSemanticsOfPreviousSiblings: dropSemanticsOfPreviousSiblings,
);
} }
if (children.length > 1)
return new _ForkingSemanticsFragment(renderObjectOwner: this, children: children, dropSemanticsOfPreviousSiblings: dropSemanticsOfPreviousSiblings);
assert(children.length == 1); assert(children.length == 1);
return children.single..dropSemanticsOfPreviousSiblings = dropSemanticsOfPreviousSiblings; return children.single..dropSemanticsOfPreviousSiblings = dropSemanticsOfPreviousSiblings;
} }
......
...@@ -3014,6 +3014,7 @@ class RenderSemanticsGestureHandler extends RenderProxyBox implements SemanticsA ...@@ -3014,6 +3014,7 @@ class RenderSemanticsGestureHandler extends RenderProxyBox implements SemanticsA
_innerNode ??= new SemanticsNode(handler: this, showOnScreen: showOnScreen); _innerNode ??= new SemanticsNode(handler: this, showOnScreen: showOnScreen);
_innerNode _innerNode
..wasAffectedByClip = node.wasAffectedByClip ..wasAffectedByClip = node.wasAffectedByClip
..isMergedIntoParent = node.isPartOfNodeMerging
..rect = Offset.zero & node.rect.size; ..rect = Offset.zero & node.rect.size;
semanticsAnnotator(_innerNode); semanticsAnnotator(_innerNode);
...@@ -3270,11 +3271,8 @@ class RenderMergeSemantics extends RenderProxyBox { ...@@ -3270,11 +3271,8 @@ class RenderMergeSemantics extends RenderProxyBox {
RenderMergeSemantics({ RenderBox child }) : super(child); RenderMergeSemantics({ RenderBox child }) : super(child);
@override @override
SemanticsAnnotator get semanticsAnnotator => _annotate; bool get isMergingSemanticsOfDescendants => true;
void _annotate(SemanticsNode node) {
node.mergeAllDescendantsIntoThisNode = true;
}
} }
/// Excludes this subtree from the semantic tree. /// Excludes this subtree from the semantic tree.
......
...@@ -330,7 +330,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -330,7 +330,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
return _actionHandler != null && (_actions & action.index) != 0; return _actionHandler != null && (_actions & action.index) != 0;
} }
/// Whether all this node and all of its descendants should be treated as one logical entity. /// Whether this node and all of its descendants should be treated as one logical entity.
bool get mergeAllDescendantsIntoThisNode => _mergeAllDescendantsIntoThisNode; bool get mergeAllDescendantsIntoThisNode => _mergeAllDescendantsIntoThisNode;
bool _mergeAllDescendantsIntoThisNode = false; bool _mergeAllDescendantsIntoThisNode = false;
set mergeAllDescendantsIntoThisNode(bool value) { set mergeAllDescendantsIntoThisNode(bool value) {
...@@ -341,17 +341,26 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -341,17 +341,26 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
_markDirty(); _markDirty();
} }
bool get _inheritedMergeAllDescendantsIntoThisNode => _inheritedMergeAllDescendantsIntoThisNodeValue; /// Whether this node merges its semantic information into an ancestor node.
bool _inheritedMergeAllDescendantsIntoThisNodeValue = false; bool get isMergedIntoParent => _isMergedIntoParent;
set _inheritedMergeAllDescendantsIntoThisNode(bool value) { bool _isMergedIntoParent = false;
set isMergedIntoParent(bool value) {
assert(value != null); assert(value != null);
if (_inheritedMergeAllDescendantsIntoThisNodeValue == value) if (_isMergedIntoParent == value)
return; return;
_inheritedMergeAllDescendantsIntoThisNodeValue = value; _isMergedIntoParent = value;
_markDirty(); _markDirty();
} }
bool get _shouldMergeAllDescendantsIntoThisNode => mergeAllDescendantsIntoThisNode || _inheritedMergeAllDescendantsIntoThisNode; /// Whether this node is taking part in a merge of semantic information.
///
/// This returns true if the node is either merged into an ancestor node or if
/// decedent nodes are merged into this node.
///
/// See also:
/// * [isMergedIntoParent]
/// * [mergeAllDescendantsIntoThisNode]
bool get isPartOfNodeMerging => mergeAllDescendantsIntoThisNode || isMergedIntoParent;
int _flags = 0; int _flags = 0;
void _setFlag(SemanticsFlags flag, bool value) { void _setFlag(SemanticsFlags flag, bool value) {
...@@ -433,13 +442,11 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -433,13 +442,11 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
/// Restore this node to its default state. /// Restore this node to its default state.
void reset() { void reset() {
final bool hadInheritedMergeAllDescendantsIntoThisNode = _inheritedMergeAllDescendantsIntoThisNode;
_actions = 0; _actions = 0;
_flags = 0; _flags = 0;
if (hadInheritedMergeAllDescendantsIntoThisNode)
_inheritedMergeAllDescendantsIntoThisNodeValue = true;
_label = ''; _label = '';
_textDirection = null; _textDirection = null;
_mergeAllDescendantsIntoThisNode = false;
_tags.clear(); _tags.clear();
_markDirty(); _markDirty();
} }
...@@ -603,8 +610,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -603,8 +610,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
_dirty = false; _dirty = false;
_markDirty(); _markDirty();
} }
if (parent != null) assert(isMergedIntoParent == (parent?.isPartOfNodeMerging ?? false));
_inheritedMergeAllDescendantsIntoThisNode = parent._shouldMergeAllDescendantsIntoThisNode;
if (_children != null) { if (_children != null) {
for (SemanticsNode child in _children) for (SemanticsNode child in _children)
child.attach(owner); child.attach(owner);
...@@ -759,7 +765,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -759,7 +765,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
hideOwner = inDirtyNodes; hideOwner = inDirtyNodes;
} }
properties.add(new DiagnosticsProperty<SemanticsOwner>('owner', owner, level: hideOwner ? DiagnosticLevel.hidden : DiagnosticLevel.info)); properties.add(new DiagnosticsProperty<SemanticsOwner>('owner', owner, level: hideOwner ? DiagnosticLevel.hidden : DiagnosticLevel.info));
properties.add(new FlagProperty('shouldMergeAllDescendantsIntoThisNode', value: _shouldMergeAllDescendantsIntoThisNode, ifTrue: 'leaf merge')); properties.add(new FlagProperty('isPartOfNodeMerging', value: isPartOfNodeMerging, ifTrue: 'leaf merge'));
final Offset offset = transform != null ? MatrixUtils.getAsTranslation(transform) : null; final Offset offset = transform != null ? MatrixUtils.getAsTranslation(transform) : null;
if (offset != null) { if (offset != null) {
properties.add(new DiagnosticsProperty<Rect>('rect', rect.shift(offset), showName: false)); properties.add(new DiagnosticsProperty<Rect>('rect', rect.shift(offset), showName: false));
...@@ -885,31 +891,12 @@ class SemanticsOwner extends ChangeNotifier { ...@@ -885,31 +891,12 @@ class SemanticsOwner extends ChangeNotifier {
visitedNodes.addAll(localDirtyNodes); visitedNodes.addAll(localDirtyNodes);
for (SemanticsNode node in localDirtyNodes) { for (SemanticsNode node in localDirtyNodes) {
assert(node._dirty); assert(node._dirty);
assert(node.parent == null || !node.parent._shouldMergeAllDescendantsIntoThisNode || node._inheritedMergeAllDescendantsIntoThisNode); assert(node.parent == null || !node.parent.isPartOfNodeMerging || node.isMergedIntoParent);
if (node._shouldMergeAllDescendantsIntoThisNode) { if (node.isPartOfNodeMerging) {
assert(node.mergeAllDescendantsIntoThisNode || node.parent != null); assert(node.mergeAllDescendantsIntoThisNode || node.parent != null);
if (node.mergeAllDescendantsIntoThisNode || // if we're merged into our parent, make sure our parent is added to the dirty list
node.parent != null && node.parent._shouldMergeAllDescendantsIntoThisNode) { if (node.parent != null && node.parent.isPartOfNodeMerging)
// if we're merged into our parent, make sure our parent is added to the list node.parent._markDirty(); // this can add the node to the dirty list
if (node.parent != null && node.parent._shouldMergeAllDescendantsIntoThisNode)
node.parent._markDirty(); // this can add the node to the dirty list
// make sure all the descendants are also marked, so that if one gets marked dirty later we know to walk up then too
if (node._children != null) {
for (SemanticsNode child in node._children)
child._inheritedMergeAllDescendantsIntoThisNode = true; // this can add the node to the dirty list
}
} else {
// we previously were being merged but aren't any more
// update our bits and all our descendants'
assert(node._inheritedMergeAllDescendantsIntoThisNode);
assert(!node.mergeAllDescendantsIntoThisNode);
assert(node.parent == null || !node.parent._shouldMergeAllDescendantsIntoThisNode);
node._inheritedMergeAllDescendantsIntoThisNode = false;
if (node._children != null) {
for (SemanticsNode child in node._children)
child._inheritedMergeAllDescendantsIntoThisNode = false; // this can add the node to the dirty list
}
}
} }
} }
} }
...@@ -937,7 +924,7 @@ class SemanticsOwner extends ChangeNotifier { ...@@ -937,7 +924,7 @@ class SemanticsOwner extends ChangeNotifier {
SemanticsActionHandler _getSemanticsActionHandlerForId(int id, SemanticsAction action) { SemanticsActionHandler _getSemanticsActionHandlerForId(int id, SemanticsAction action) {
SemanticsNode result = _nodes[id]; SemanticsNode result = _nodes[id];
if (result != null && result._shouldMergeAllDescendantsIntoThisNode && !result._canPerformAction(action)) { if (result != null && result.isPartOfNodeMerging && !result._canPerformAction(action)) {
result._visitDescendants((SemanticsNode node) { result._visitDescendants((SemanticsNode node) {
if (node._canPerformAction(action)) { if (node._canPerformAction(action)) {
result = node; result = node;
......
...@@ -177,7 +177,7 @@ void main() { ...@@ -177,7 +177,7 @@ void main() {
expect( expect(
minimalProperties.toStringDeep(minLevel: DiagnosticLevel.hidden), minimalProperties.toStringDeep(minLevel: DiagnosticLevel.hidden),
'SemanticsNode#16(owner: null, shouldMergeAllDescendantsIntoThisNode: false, Rect.fromLTRB(0.0, 0.0, 0.0, 0.0), wasAffectedByClip: false, actions: [], tags: [], isSelected: false, label: "", textDirection: null)\n', 'SemanticsNode#16(owner: null, isPartOfNodeMerging: false, Rect.fromLTRB(0.0, 0.0, 0.0, 0.0), wasAffectedByClip: false, actions: [], tags: [], isSelected: false, label: "", textDirection: null)\n',
); );
final SemanticsNode allProperties = new SemanticsNode() final SemanticsNode allProperties = new SemanticsNode()
......
// Copyright 2017 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 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'semantics_tester.dart';
void main() {
testWidgets('MergeSemantics', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
// not merged
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.ltr,
child: new Row(
children: <Widget>[
new Semantics(
label: 'test1',
textDirection: TextDirection.ltr,
child: new Container()
),
new Semantics(
label: 'test2',
textDirection: TextDirection.ltr,
child: new Container()
)
],
),
),
);
expect(semantics, hasSemantics(
new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(id: 1, label: 'test1'),
new TestSemantics.rootChild(id: 2, label: 'test2'),
],
),
ignoreRect: true,
ignoreTransform: true,
));
// merged
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.ltr,
child: new MergeSemantics(
child: new Row(
children: <Widget>[
new Semantics(
label: 'test1',
textDirection: TextDirection.ltr,
child: new Container()
),
new Semantics(
label: 'test2',
textDirection: TextDirection.ltr,
child: new Container()
)
],
),
),
),
);
expect(semantics, hasSemantics(
new TestSemantics.root(label: 'test1\ntest2'),
ignoreRect: true,
ignoreTransform: true,
));
// not merged
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.ltr,
child: new Row(
children: <Widget>[
new Semantics(
label: 'test1',
textDirection: TextDirection.ltr,
child: new Container()
),
new Semantics(
label: 'test2',
textDirection: TextDirection.ltr,
child: new Container()
)
],
),
),
);
expect(semantics, hasSemantics(
new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(id: 5, label: 'test1'),
new TestSemantics.rootChild(id: 6, label: 'test2'),
],
),
ignoreRect: true,
ignoreTransform: true,
));
semantics.dispose();
});
}
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