Unverified Commit 6493c8b4 authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

Adapt markNeedsSemanticsUpdate algorithm to new semantics tree compiler (#13274)

* ensures that only semantics boundaries will be added to owner._nodesNeedingSemantics as expected by compiler.
* no longer throws assert if markNeedsSemanticsUpdate is called on non-semantic-boundary render object with a non-semantic-boundary parent.
* Fixes #13109.
* removes onlyLocalUpdates from markNeedsSemanticsUpdate as its no longer needed.
parent a78c9f70
...@@ -214,7 +214,7 @@ class _RenderCupertinoSwitch extends RenderConstrainedBox { ...@@ -214,7 +214,7 @@ class _RenderCupertinoSwitch extends RenderConstrainedBox {
if (value == _value) if (value == _value)
return; return;
_value = value; _value = value;
markNeedsSemanticsUpdate(onlyLocalUpdates: true); markNeedsSemanticsUpdate();
_position _position
..curve = Curves.ease ..curve = Curves.ease
..reverseCurve = Curves.ease.flipped; ..reverseCurve = Curves.ease.flipped;
......
...@@ -122,7 +122,7 @@ abstract class RenderToggleable extends RenderConstrainedBox { ...@@ -122,7 +122,7 @@ abstract class RenderToggleable extends RenderConstrainedBox {
if (value == _value) if (value == _value)
return; return;
_value = value; _value = value;
markNeedsSemanticsUpdate(onlyLocalUpdates: true); markNeedsSemanticsUpdate();
_position _position
..curve = Curves.easeIn ..curve = Curves.easeIn
..reverseCurve = Curves.easeOut; ..reverseCurve = Curves.easeOut;
......
...@@ -2201,81 +2201,53 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im ...@@ -2201,81 +2201,53 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
/// Mark this node as needing an update to its semantics description. /// Mark this node as needing an update to its semantics description.
/// ///
/// `onlyLocalUpdates` should be set to true to reduce cost if the semantics /// This must be called whenever the semantics configuration of this
/// update does not in any way change the shape of the semantics tree (e.g. /// [RenderObject] as annotated by [describeSemanticsConfiguration] changes in
/// [SemanticsNode]s will neither be added/removed from the tree nor be moved /// any way to update the semantics tree.
/// within the tree). In other words, with `onlyLocalUpdates` the void markNeedsSemanticsUpdate() {
/// [RenderObject] can indicate that it only wants to perform updates on the
/// local [SemanticsNode] (e.g. changing a label or flag) without affecting
/// other nodes in the tree.
///
/// `onlyLocalUpdates` has to be set to false in the following cases as they
/// will change the shape of the tree:
///
/// 1. [isSemanticBoundary] changed its value.
/// 2. [semanticsAnnotator] changed from or to returning null and
/// [isSemanticBoundary] isn't true.
///
/// If `onlyLocalUpdates` is incorrectly set to true, asserts
/// might throw or the computed semantics tree might be out-of-date without
/// warning.
void markNeedsSemanticsUpdate({ bool onlyLocalUpdates: false }) {
assert(!attached || !owner._debugDoingSemantics); assert(!attached || !owner._debugDoingSemantics);
_cachedSemanticsConfiguration = null;
if ((attached && owner._semanticsOwner == null)) if ((attached && owner._semanticsOwner == null))
return; return;
if (onlyLocalUpdates) {
// The shape of the tree didn't change, but the details did. // Dirty the semantics tree starting at `this` until we have reached a
// If we have our own SemanticsNode (our _semantics isn't null) // RenderObject that is a semantics boundary. All semantics past this
// then mark ourselves dirty. If we don't then we are using an // RenderObject are still up-to date. Therefore, we will later only rebuild
// ancestor's; mark all the nodes up to that one dirty. // the semantics subtree starting at th identified semantics boundary.
RenderObject node = this;
while (node._semantics == null && node.parent is RenderObject) { final bool wasSemanticsBoundary = _cachedSemanticsConfiguration?.isSemanticBoundary == true;
if (node._needsSemanticsUpdate) _cachedSemanticsConfiguration = null;
return; bool isEffectiveSemanticsBoundary = _semanticsConfiguration.isSemanticBoundary && wasSemanticsBoundary;
node._cachedSemanticsConfiguration = null; RenderObject node = this;
node._needsSemanticsUpdate = true;
node = node.parent; while (!isEffectiveSemanticsBoundary && node.parent is RenderObject) {
} if (node != this && node._needsSemanticsUpdate)
if (!node._needsSemanticsUpdate) { break;
node._needsSemanticsUpdate = true; node._needsSemanticsUpdate = true;
node._cachedSemanticsConfiguration = null;
if (owner != null) { node = node.parent;
owner._nodesNeedingSemantics.add(node); node._cachedSemanticsConfiguration = null;
owner.requestVisualUpdate(); isEffectiveSemanticsBoundary = node._semanticsConfiguration.isSemanticBoundary;
} }
} if (node != this && _semantics != null && _needsSemanticsUpdate) {
} else { // If `this` node has already been added to [owner._nodesNeedingSemantics]
// The shape of the semantics tree around us may have changed. // remove it as it is no longer guaranteed that its semantics
// The worst case is that we may have removed a branch of the // node will continue to be in the tree. If it still is in the tree, the
// semantics tree, because when that happens we have to go up // ancestor `node` added to [owner._nodesNeedingSemantics] at the end of
// and dirty the nearest _semantics-laden ancestor of the // this block will ensure that the semantics of `this` node actually get
// affected node to rebuild the tree. // updated.
RenderObject node = this; // (See semantics_10_test.dart for an example why this is required).
do { owner._nodesNeedingSemantics.remove(this);
if (node.parent is! RenderObject) }
break; if (!node._needsSemanticsUpdate) {
node._needsSemanticsUpdate = true; if (node != this) {
// Reset for `this` happened above already.
node._cachedSemanticsConfiguration = null; node._cachedSemanticsConfiguration = null;
node = node.parent;
} while (node._semantics == null);
if (node != this && _semantics != null && _needsSemanticsUpdate) {
// If [this] node has already been added to [owner._nodesNeedingSemantics]
// remove it as it is no longer guaranteed that its semantics
// node will continue to be in the tree. If it still is in the tree, the
// ancestor [node] added to [owner._nodesNeedingSemantics] at the end of
// this block will ensure that the semantics of [this] node actually get
// updated.
// (See semantics_10_test.dart for an example why this is required).
owner._nodesNeedingSemantics.remove(this);
} }
if (!node._needsSemanticsUpdate) { node._needsSemanticsUpdate = true;
node._cachedSemanticsConfiguration = null; if (owner != null) {
node._needsSemanticsUpdate = true; assert(node._semanticsConfiguration.isSemanticBoundary || node.parent is! RenderObject);
if (owner != null) { owner._nodesNeedingSemantics.add(node);
owner._nodesNeedingSemantics.add(node); owner.requestVisualUpdate();
owner.requestVisualUpdate();
}
} }
} }
} }
......
...@@ -992,7 +992,7 @@ abstract class _RenderCustomClip<T> extends RenderProxyBox { ...@@ -992,7 +992,7 @@ abstract class _RenderCustomClip<T> extends RenderProxyBox {
void _markNeedsClip() { void _markNeedsClip() {
_clip = null; _clip = null;
markNeedsPaint(); markNeedsPaint();
markNeedsSemanticsUpdate(onlyLocalUpdates: true); markNeedsSemanticsUpdate();
} }
T get _defaultClip; T get _defaultClip;
...@@ -3003,7 +3003,7 @@ class RenderSemanticsGestureHandler extends RenderProxyBox { ...@@ -3003,7 +3003,7 @@ class RenderSemanticsGestureHandler extends RenderProxyBox {
if (setEquals<SemanticsAction>(value, _validActions)) if (setEquals<SemanticsAction>(value, _validActions))
return; return;
_validActions = value; _validActions = value;
markNeedsSemanticsUpdate(onlyLocalUpdates: true); markNeedsSemanticsUpdate();
} }
/// Called when the user taps on the render object. /// Called when the user taps on the render object.
...@@ -3012,11 +3012,10 @@ class RenderSemanticsGestureHandler extends RenderProxyBox { ...@@ -3012,11 +3012,10 @@ class RenderSemanticsGestureHandler extends RenderProxyBox {
set onTap(GestureTapCallback value) { set onTap(GestureTapCallback value) {
if (_onTap == value) if (_onTap == value)
return; return;
final bool hadHandlers = _hasHandlers;
final bool hadHandler = _onTap != null; final bool hadHandler = _onTap != null;
_onTap = value; _onTap = value;
if ((value != null) != hadHandler) if ((value != null) != hadHandler)
markNeedsSemanticsUpdate(onlyLocalUpdates: _hasHandlers == hadHandlers); markNeedsSemanticsUpdate();
} }
/// Called when the user presses on the render object for a long period of time. /// Called when the user presses on the render object for a long period of time.
...@@ -3025,11 +3024,10 @@ class RenderSemanticsGestureHandler extends RenderProxyBox { ...@@ -3025,11 +3024,10 @@ class RenderSemanticsGestureHandler extends RenderProxyBox {
set onLongPress(GestureLongPressCallback value) { set onLongPress(GestureLongPressCallback value) {
if (_onLongPress == value) if (_onLongPress == value)
return; return;
final bool hadHandlers = _hasHandlers;
final bool hadHandler = _onLongPress != null; final bool hadHandler = _onLongPress != null;
_onLongPress = value; _onLongPress = value;
if ((value != null) != hadHandler) if ((value != null) != hadHandler)
markNeedsSemanticsUpdate(onlyLocalUpdates: _hasHandlers == hadHandlers); markNeedsSemanticsUpdate();
} }
/// Called when the user scrolls to the left or to the right. /// Called when the user scrolls to the left or to the right.
...@@ -3038,11 +3036,10 @@ class RenderSemanticsGestureHandler extends RenderProxyBox { ...@@ -3038,11 +3036,10 @@ class RenderSemanticsGestureHandler extends RenderProxyBox {
set onHorizontalDragUpdate(GestureDragUpdateCallback value) { set onHorizontalDragUpdate(GestureDragUpdateCallback value) {
if (_onHorizontalDragUpdate == value) if (_onHorizontalDragUpdate == value)
return; return;
final bool hadHandlers = _hasHandlers;
final bool hadHandler = _onHorizontalDragUpdate != null; final bool hadHandler = _onHorizontalDragUpdate != null;
_onHorizontalDragUpdate = value; _onHorizontalDragUpdate = value;
if ((value != null) != hadHandler) if ((value != null) != hadHandler)
markNeedsSemanticsUpdate(onlyLocalUpdates: _hasHandlers == hadHandlers); markNeedsSemanticsUpdate();
} }
/// Called when the user scrolls up or down. /// Called when the user scrolls up or down.
...@@ -3051,11 +3048,10 @@ class RenderSemanticsGestureHandler extends RenderProxyBox { ...@@ -3051,11 +3048,10 @@ class RenderSemanticsGestureHandler extends RenderProxyBox {
set onVerticalDragUpdate(GestureDragUpdateCallback value) { set onVerticalDragUpdate(GestureDragUpdateCallback value) {
if (_onVerticalDragUpdate == value) if (_onVerticalDragUpdate == value)
return; return;
final bool hadHandlers = _hasHandlers;
final bool hadHandler = _onVerticalDragUpdate != null; final bool hadHandler = _onVerticalDragUpdate != null;
_onVerticalDragUpdate = value; _onVerticalDragUpdate = value;
if ((value != null) != hadHandler) if ((value != null) != hadHandler)
markNeedsSemanticsUpdate(onlyLocalUpdates: _hasHandlers == hadHandlers); markNeedsSemanticsUpdate();
} }
/// The fraction of the dimension of this render box to use when /// The fraction of the dimension of this render box to use when
...@@ -3296,9 +3292,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -3296,9 +3292,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
set checked(bool value) { set checked(bool value) {
if (checked == value) if (checked == value)
return; return;
final bool hadValue = checked != null;
_checked = value; _checked = value;
markNeedsSemanticsUpdate(onlyLocalUpdates: (value != null) == hadValue); markNeedsSemanticsUpdate();
} }
/// If non-null, sets the [SemanticsNode.isSelected] semantic to the given /// If non-null, sets the [SemanticsNode.isSelected] semantic to the given
...@@ -3308,9 +3303,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -3308,9 +3303,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
set selected(bool value) { set selected(bool value) {
if (selected == value) if (selected == value)
return; return;
final bool hadValue = selected != null;
_selected = value; _selected = value;
markNeedsSemanticsUpdate(onlyLocalUpdates: (value != null) == hadValue); markNeedsSemanticsUpdate();
} }
/// If non-null, sets the [SemanticsNode.isButton] semantic to the given value. /// If non-null, sets the [SemanticsNode.isButton] semantic to the given value.
...@@ -3319,9 +3313,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -3319,9 +3313,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
set button(bool value) { set button(bool value) {
if (button == value) if (button == value)
return; return;
final bool hadValue = button != null;
_button = value; _button = value;
markNeedsSemanticsUpdate(onlyLocalUpdates: (value != null) == hadValue); markNeedsSemanticsUpdate();
} }
/// If non-null, sets the [SemanticsNode.label] semantic to the given value. /// If non-null, sets the [SemanticsNode.label] semantic to the given value.
...@@ -3332,9 +3325,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -3332,9 +3325,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
set label(String value) { set label(String value) {
if (_label == value) if (_label == value)
return; return;
final bool hadValue = _label != null;
_label = value; _label = value;
markNeedsSemanticsUpdate(onlyLocalUpdates: (value != null) == hadValue); markNeedsSemanticsUpdate();
} }
/// If non-null, sets the [SemanticsNode.value] semantic to the given value. /// If non-null, sets the [SemanticsNode.value] semantic to the given value.
...@@ -3345,9 +3337,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -3345,9 +3337,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
set value(String value) { set value(String value) {
if (_value == value) if (_value == value)
return; return;
final bool hadValue = _value != null;
_value = value; _value = value;
markNeedsSemanticsUpdate(onlyLocalUpdates: (value != null) == hadValue); markNeedsSemanticsUpdate();
} }
/// If non-null, sets the [SemanticsNode.increasedValue] semantic to the given /// If non-null, sets the [SemanticsNode.increasedValue] semantic to the given
...@@ -3359,9 +3350,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -3359,9 +3350,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
set increasedValue(String value) { set increasedValue(String value) {
if (_increasedValue == value) if (_increasedValue == value)
return; return;
final bool hadValue = _increasedValue != null;
_increasedValue = value; _increasedValue = value;
markNeedsSemanticsUpdate(onlyLocalUpdates: (value != null) == hadValue); markNeedsSemanticsUpdate();
} }
/// If non-null, sets the [SemanticsNode.decreasedValue] semantic to the given /// If non-null, sets the [SemanticsNode.decreasedValue] semantic to the given
...@@ -3373,9 +3363,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -3373,9 +3363,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
set decreasedValue(String value) { set decreasedValue(String value) {
if (_decreasedValue == value) if (_decreasedValue == value)
return; return;
final bool hadValue = _decreasedValue != null;
_decreasedValue = value; _decreasedValue = value;
markNeedsSemanticsUpdate(onlyLocalUpdates: (value != null) == hadValue); markNeedsSemanticsUpdate();
} }
/// If non-null, sets the [SemanticsNode.hint] semantic to the given value. /// If non-null, sets the [SemanticsNode.hint] semantic to the given value.
...@@ -3386,9 +3375,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -3386,9 +3375,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
set hint(String value) { set hint(String value) {
if (_hint == value) if (_hint == value)
return; return;
final bool hadValue = _hint != null;
_hint = value; _hint = value;
markNeedsSemanticsUpdate(onlyLocalUpdates: (value != null) == hadValue); markNeedsSemanticsUpdate();
} }
/// If non-null, sets the [SemanticsNode.textDirection] semantic to the given value. /// If non-null, sets the [SemanticsNode.textDirection] semantic to the given value.
...@@ -3400,9 +3388,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -3400,9 +3388,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
set textDirection(TextDirection value) { set textDirection(TextDirection value) {
if (textDirection == value) if (textDirection == value)
return; return;
final bool hadValue = textDirection != null;
_textDirection = value; _textDirection = value;
markNeedsSemanticsUpdate(onlyLocalUpdates: (value != null) == hadValue); markNeedsSemanticsUpdate();
} }
/// The handler for [SemanticsAction.tap]. /// The handler for [SemanticsAction.tap].
...@@ -3421,7 +3408,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -3421,7 +3408,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
final bool hadValue = _onTap != null; final bool hadValue = _onTap != null;
_onTap = handler; _onTap = handler;
if ((handler != null) == hadValue) if ((handler != null) == hadValue)
markNeedsSemanticsUpdate(onlyLocalUpdates: true); markNeedsSemanticsUpdate();
} }
/// The handler for [SemanticsAction.longPress]. /// The handler for [SemanticsAction.longPress].
...@@ -3440,7 +3427,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -3440,7 +3427,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
final bool hadValue = _onLongPress != null; final bool hadValue = _onLongPress != null;
_onLongPress = handler; _onLongPress = handler;
if ((handler != null) != hadValue) if ((handler != null) != hadValue)
markNeedsSemanticsUpdate(onlyLocalUpdates: true); markNeedsSemanticsUpdate();
} }
/// The handler for [SemanticsAction.scrollLeft]. /// The handler for [SemanticsAction.scrollLeft].
...@@ -3462,7 +3449,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -3462,7 +3449,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
final bool hadValue = _onScrollLeft != null; final bool hadValue = _onScrollLeft != null;
_onScrollLeft = handler; _onScrollLeft = handler;
if ((handler != null) != hadValue) if ((handler != null) != hadValue)
markNeedsSemanticsUpdate(onlyLocalUpdates: true); markNeedsSemanticsUpdate();
} }
/// The handler for [SemanticsAction.scrollRight]. /// The handler for [SemanticsAction.scrollRight].
...@@ -3484,7 +3471,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -3484,7 +3471,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
final bool hadValue = _onScrollRight != null; final bool hadValue = _onScrollRight != null;
_onScrollRight = handler; _onScrollRight = handler;
if ((handler != null) != hadValue) if ((handler != null) != hadValue)
markNeedsSemanticsUpdate(onlyLocalUpdates: true); markNeedsSemanticsUpdate();
} }
/// The handler for [SemanticsAction.scrollUp]. /// The handler for [SemanticsAction.scrollUp].
...@@ -3506,7 +3493,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -3506,7 +3493,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
final bool hadValue = _onScrollUp != null; final bool hadValue = _onScrollUp != null;
_onScrollUp = handler; _onScrollUp = handler;
if ((handler != null) != hadValue) if ((handler != null) != hadValue)
markNeedsSemanticsUpdate(onlyLocalUpdates: true); markNeedsSemanticsUpdate();
} }
/// The handler for [SemanticsAction.scrollDown]. /// The handler for [SemanticsAction.scrollDown].
...@@ -3528,7 +3515,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -3528,7 +3515,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
final bool hadValue = _onScrollDown != null; final bool hadValue = _onScrollDown != null;
_onScrollDown = handler; _onScrollDown = handler;
if ((handler != null) != hadValue) if ((handler != null) != hadValue)
markNeedsSemanticsUpdate(onlyLocalUpdates: true); markNeedsSemanticsUpdate();
} }
/// The handler for [SemanticsAction.increase]. /// The handler for [SemanticsAction.increase].
...@@ -3547,7 +3534,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -3547,7 +3534,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
final bool hadValue = _onIncrease != null; final bool hadValue = _onIncrease != null;
_onIncrease = handler; _onIncrease = handler;
if ((handler != null) != hadValue) if ((handler != null) != hadValue)
markNeedsSemanticsUpdate(onlyLocalUpdates: true); markNeedsSemanticsUpdate();
} }
/// The handler for [SemanticsAction.decrease]. /// The handler for [SemanticsAction.decrease].
...@@ -3566,20 +3553,11 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -3566,20 +3553,11 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
final bool hadValue = _onDecrease != null; final bool hadValue = _onDecrease != null;
_onDecrease = handler; _onDecrease = handler;
if ((handler != null) != hadValue) if ((handler != null) != hadValue)
markNeedsSemanticsUpdate(onlyLocalUpdates: true); markNeedsSemanticsUpdate();
} }
@override @override
void describeSemanticsConfiguration(SemanticsConfiguration config) { void describeSemanticsConfiguration(SemanticsConfiguration config) {
assert(
onIncrease == null || (value == null) == (increasedValue == null),
'If "onIncrease" is set either both "value" and "increasedValue" or neither have to be set.',
);
assert(
onDecrease == null || (value == null) == (decreasedValue == null),
'If "onDecrease" is set either both "value" and "decreasedValue" or neither have to be set.',
);
config.isSemanticBoundary = container; config.isSemanticBoundary = container;
config.explicitChildNodes = explicitChildNodes; config.explicitChildNodes = explicitChildNodes;
......
...@@ -21,21 +21,6 @@ void main() { ...@@ -21,21 +21,6 @@ void main() {
renderObject.markNeedsSemanticsUpdate(); renderObject.markNeedsSemanticsUpdate();
expect(onNeedVisualUpdateCallCount, 2); expect(onNeedVisualUpdateCallCount, 2);
}); });
test('ensure frame is scheduled for markNeedsSemanticsUpdate with onlyChanges: true', () {
final TestRenderObject renderObject = new TestRenderObject();
int onNeedVisualUpdateCallCount = 0;
final PipelineOwner owner = new PipelineOwner(onNeedVisualUpdate: () {
onNeedVisualUpdateCallCount +=1;
});
owner.ensureSemantics();
renderObject.attach(owner);
owner.flushSemantics();
expect(onNeedVisualUpdateCallCount, 1);
renderObject.markNeedsSemanticsUpdate(onlyLocalUpdates: true);
expect(onNeedVisualUpdateCallCount, 2);
});
} }
class TestRenderObject extends RenderObject { class TestRenderObject extends RenderObject {
......
...@@ -30,7 +30,7 @@ void main() { ...@@ -30,7 +30,7 @@ void main() {
// Initial render does semantics. // Initial render does semantics.
expect(semanticsUpdateCount, 1); expect(semanticsUpdateCount, 1);
expect(testRender.describeSemanticsConfigurationCallCount, isNot(0)); expect(testRender.describeSemanticsConfigurationCallCount, isPositive);
testRender.describeSemanticsConfigurationCallCount = 0; testRender.describeSemanticsConfigurationCallCount = 0;
semanticsUpdateCount = 0; semanticsUpdateCount = 0;
...@@ -41,7 +41,7 @@ void main() { ...@@ -41,7 +41,7 @@ void main() {
// Object is asked for semantics, but no update is sent. // Object is asked for semantics, but no update is sent.
expect(semanticsUpdateCount, 0); expect(semanticsUpdateCount, 0);
expect(testRender.describeSemanticsConfigurationCallCount, 1); expect(testRender.describeSemanticsConfigurationCallCount, isPositive);
testRender.describeSemanticsConfigurationCallCount = 0; testRender.describeSemanticsConfigurationCallCount = 0;
semanticsUpdateCount = 0; semanticsUpdateCount = 0;
...@@ -53,7 +53,7 @@ void main() { ...@@ -53,7 +53,7 @@ void main() {
// Object is asked for semantics, and update is sent. // Object is asked for semantics, and update is sent.
expect(semanticsUpdateCount, 1); expect(semanticsUpdateCount, 1);
expect(testRender.describeSemanticsConfigurationCallCount, 1); expect(testRender.describeSemanticsConfigurationCallCount, isPositive);
semanticsHandle.dispose(); semanticsHandle.dispose();
}); });
......
...@@ -60,7 +60,7 @@ void main() { ...@@ -60,7 +60,7 @@ void main() {
expect(node.getSemanticsData().tags, tags); expect(node.getSemanticsData().tags, tags);
}); });
test('after markNeedsSemanticsUpdate(onlyLocalUpdates: true) all render objects between two semantic boundaries are asked for annotations', () { test('after markNeedsSemanticsUpdate() all render objects between two semantic boundaries are asked for annotations', () {
renderer.pipelineOwner.ensureSemantics(); renderer.pipelineOwner.ensureSemantics();
TestRender middle; TestRender middle;
...@@ -92,7 +92,7 @@ void main() { ...@@ -92,7 +92,7 @@ void main() {
expect(root.debugSemantics.getSemanticsData().actions, expectedActions); expect(root.debugSemantics.getSemanticsData().actions, expectedActions);
middle.action = SemanticsAction.scrollDown; middle.action = SemanticsAction.scrollDown;
middle.markNeedsSemanticsUpdate(onlyLocalUpdates: true); middle.markNeedsSemanticsUpdate();
pumpFrame(phase: EnginePhase.flushSemantics); pumpFrame(phase: EnginePhase.flushSemantics);
......
...@@ -223,7 +223,6 @@ void main() { ...@@ -223,7 +223,6 @@ void main() {
' └─child: RenderSemanticsGestureHandler#00000\n' ' └─child: RenderSemanticsGestureHandler#00000\n'
' │ parentData: <none> (can use size)\n' ' │ parentData: <none> (can use size)\n'
' │ constraints: BoxConstraints(w=800.0, h=600.0)\n' ' │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
' │ semantic boundary\n'
' │ size: Size(800.0, 600.0)\n' ' │ size: Size(800.0, 600.0)\n'
' │ gestures: vertical scroll\n' ' │ gestures: vertical scroll\n'
' │\n' ' │\n'
...@@ -328,7 +327,6 @@ void main() { ...@@ -328,7 +327,6 @@ void main() {
' └─child: RenderSemanticsGestureHandler#00000\n' ' └─child: RenderSemanticsGestureHandler#00000\n'
' │ parentData: <none> (can use size)\n' ' │ parentData: <none> (can use size)\n'
' │ constraints: BoxConstraints(w=800.0, h=600.0)\n' ' │ constraints: BoxConstraints(w=800.0, h=600.0)\n'
' │ semantic boundary\n'
' │ size: Size(800.0, 600.0)\n' ' │ size: Size(800.0, 600.0)\n'
' │ gestures: vertical scroll\n' ' │ gestures: vertical scroll\n'
' │\n' ' │\n'
......
...@@ -104,11 +104,11 @@ void main() { ...@@ -104,11 +104,11 @@ void main() {
final TestSemantics expectedSemantics = new TestSemantics.root( final TestSemantics expectedSemantics = new TestSemantics.root(
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 1, id: 3,
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics( new TestSemantics(
id: 2, id: 4,
rect: TestSemantics.fullScreen, rect: TestSemantics.fullScreen,
actions: SemanticsAction.tap.index, actions: SemanticsAction.tap.index,
), ),
......
...@@ -9,10 +9,8 @@ import 'package:flutter_test/flutter_test.dart'; ...@@ -9,10 +9,8 @@ import 'package:flutter_test/flutter_test.dart';
import 'semantics_tester.dart'; import 'semantics_tester.dart';
List<String> callLog = <String>[];
void main() { void main() {
testWidgets('can call markNeedsSemanticsUpdate(onlyChanges: true) followed by markNeedsSemanticsUpdate(onlyChanges: false)', (WidgetTester tester) async { testWidgets('can cease to be semantics boundary after markNeedsSemanticsUpdate() has already been called once', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester); final SemanticsTester semantics = new SemanticsTester(tester);
await tester.pumpWidget( await tester.pumpWidget(
...@@ -23,8 +21,6 @@ void main() { ...@@ -23,8 +21,6 @@ void main() {
), ),
); );
callLog.clear();
// The following should not trigger an assert. // The following should not trigger an assert.
await tester.pumpWidget( await tester.pumpWidget(
buildTestWidgets( buildTestWidgets(
...@@ -34,8 +30,6 @@ void main() { ...@@ -34,8 +30,6 @@ void main() {
), ),
); );
expect(callLog, <String>['markNeedsSemanticsUpdate(onlyChanges: true)', 'markNeedsSemanticsUpdate(onlyChanges: false)']);
semantics.dispose(); semantics.dispose();
}); });
} }
...@@ -109,22 +103,20 @@ class RenderTest extends RenderProxyBox { ...@@ -109,22 +103,20 @@ class RenderTest extends RenderProxyBox {
} }
String _label; String _label = '<>';
set label(String value) { set label(String value) {
if (value == _label) if (value == _label)
return; return;
_label = value; _label = value;
markNeedsSemanticsUpdate(onlyLocalUpdates: true); markNeedsSemanticsUpdate();
callLog.add('markNeedsSemanticsUpdate(onlyChanges: true)');
} }
bool _isSemanticBoundary; bool _isSemanticBoundary = false;
set isSemanticBoundary(bool value) { set isSemanticBoundary(bool value) {
if (_isSemanticBoundary == value) if (_isSemanticBoundary == value)
return; return;
_isSemanticBoundary = value; _isSemanticBoundary = value;
markNeedsSemanticsUpdate(onlyLocalUpdates: false); markNeedsSemanticsUpdate();
callLog.add('markNeedsSemanticsUpdate(onlyChanges: false)');
} }
} }
// 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/widgets.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
import 'semantics_tester.dart';
void main() {
testWidgets('markNeedsSemanticsUpdate() called on non-boundary with non-boundary parent', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
await tester.pumpWidget(
const Semantics(
container: true,
onTap: dummyTapHandler,
child: const Semantics(
onTap: dummyTapHandler,
child: const Semantics(
onTap: dummyTapHandler,
textDirection: TextDirection.ltr,
label: 'foo',
),
),
),
);
expect(semantics, hasSemantics(new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
id: 1,
actions: SemanticsAction.tap.index,
children: <TestSemantics>[
new TestSemantics(
id: 2,
actions: SemanticsAction.tap.index,
children: <TestSemantics>[
new TestSemantics(
id: 3,
actions: SemanticsAction.tap.index,
label: 'foo',
)
],
),
],
)
],
), ignoreRect: true, ignoreTransform: true));
// make a change causing call to markNeedsSemanticsUpdate()
// This should not throw an assert.
await tester.pumpWidget(
const Semantics(
container: true,
onTap: dummyTapHandler,
child: const Semantics(
onTap: dummyTapHandler,
child: const Semantics(
onTap: dummyTapHandler,
textDirection: TextDirection.ltr,
label: 'bar', // <-- only change
),
),
),
);
expect(semantics, hasSemantics(new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
id: 1,
actions: SemanticsAction.tap.index,
children: <TestSemantics>[
new TestSemantics(
id: 2,
actions: SemanticsAction.tap.index,
children: <TestSemantics>[
new TestSemantics(
id: 3,
actions: SemanticsAction.tap.index,
label: 'bar',
)
],
),
],
)
],
), ignoreRect: true, ignoreTransform: true));
semantics.dispose();
});
}
void dummyTapHandler() { }
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