Unverified Commit a9e27811 authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

Do not do semantics for detached objects (#15320)

parent 35c43ecc
...@@ -2222,8 +2222,10 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im ...@@ -2222,8 +2222,10 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
/// any way to update the semantics tree. /// any way to update the semantics tree.
void markNeedsSemanticsUpdate() { void markNeedsSemanticsUpdate() {
assert(!attached || !owner._debugDoingSemantics); assert(!attached || !owner._debugDoingSemantics);
if (attached && owner._semanticsOwner == null) if (!attached || owner._semanticsOwner == null) {
_cachedSemanticsConfiguration = null;
return; return;
}
// Dirty the semantics tree starting at `this` until we have reached a // Dirty the semantics tree starting at `this` until we have reached a
// RenderObject that is a semantics boundary. All semantics past this // RenderObject that is a semantics boundary. All semantics past this
......
...@@ -723,24 +723,42 @@ class _GestureSemantics extends SingleChildRenderObjectWidget { ...@@ -723,24 +723,42 @@ class _GestureSemantics extends SingleChildRenderObjectWidget {
@override @override
RenderSemanticsGestureHandler createRenderObject(BuildContext context) { RenderSemanticsGestureHandler createRenderObject(BuildContext context) {
final RenderSemanticsGestureHandler renderObject = new RenderSemanticsGestureHandler(); return new RenderSemanticsGestureHandler(
_updateHandlers(renderObject); onTap: _onTapHandler,
return renderObject; onLongPress: _onLongPressHandler,
onHorizontalDragUpdate: _onHorizontalDragUpdateHandler,
onVerticalDragUpdate: _onVerticalDragUpdateHandler,
);
} }
void _updateHandlers(RenderSemanticsGestureHandler renderObject) { void _updateHandlers(RenderSemanticsGestureHandler renderObject) {
final Map<Type, GestureRecognizer> recognizers = owner._recognizers;
renderObject renderObject
..onTap = recognizers.containsKey(TapGestureRecognizer) ? owner._handleSemanticsTap : null ..onTap = _onTapHandler
..onLongPress = recognizers.containsKey(LongPressGestureRecognizer) ? owner._handleSemanticsLongPress : null ..onLongPress = _onLongPressHandler
..onHorizontalDragUpdate = recognizers.containsKey(HorizontalDragGestureRecognizer) || ..onHorizontalDragUpdate = _onHorizontalDragUpdateHandler
recognizers.containsKey(PanGestureRecognizer) ? owner._handleSemanticsHorizontalDragUpdate : null ..onVerticalDragUpdate = _onVerticalDragUpdateHandler;
..onVerticalDragUpdate = recognizers.containsKey(VerticalDragGestureRecognizer) ||
recognizers.containsKey(PanGestureRecognizer) ? owner._handleSemanticsVerticalDragUpdate : null;
} }
@override @override
void updateRenderObject(BuildContext context, RenderSemanticsGestureHandler renderObject) { void updateRenderObject(BuildContext context, RenderSemanticsGestureHandler renderObject) {
_updateHandlers(renderObject); _updateHandlers(renderObject);
} }
GestureTapCallback get _onTapHandler {
return owner._recognizers.containsKey(TapGestureRecognizer) ? owner._handleSemanticsTap : null;
}
GestureTapCallback get _onLongPressHandler {
return owner._recognizers.containsKey(LongPressGestureRecognizer) ? owner._handleSemanticsLongPress : null;
}
GestureDragUpdateCallback get _onHorizontalDragUpdateHandler {
return owner._recognizers.containsKey(HorizontalDragGestureRecognizer) ||
owner._recognizers.containsKey(PanGestureRecognizer) ? owner._handleSemanticsHorizontalDragUpdate : null;
}
GestureDragUpdateCallback get _onVerticalDragUpdateHandler {
return owner._recognizers.containsKey(VerticalDragGestureRecognizer) ||
owner._recognizers.containsKey(PanGestureRecognizer) ? owner._handleSemanticsVerticalDragUpdate : null;
}
} }
...@@ -21,6 +21,15 @@ void main() { ...@@ -21,6 +21,15 @@ void main() {
renderObject.markNeedsSemanticsUpdate(); renderObject.markNeedsSemanticsUpdate();
expect(onNeedVisualUpdateCallCount, 2); expect(onNeedVisualUpdateCallCount, 2);
}); });
test('detached RenderObject does not do semantics', () {
final TestRenderObject renderObject = new TestRenderObject();
expect(renderObject.attached, isFalse);
expect(renderObject.describeSemanticsConfigurationCallCount, 0);
renderObject.markNeedsSemanticsUpdate();
expect(renderObject.describeSemanticsConfigurationCallCount, 0);
});
} }
class TestRenderObject extends RenderObject { class TestRenderObject extends RenderObject {
...@@ -39,10 +48,13 @@ class TestRenderObject extends RenderObject { ...@@ -39,10 +48,13 @@ class TestRenderObject extends RenderObject {
@override @override
Rect get semanticBounds => new Rect.fromLTWH(0.0, 0.0, 10.0, 20.0); Rect get semanticBounds => new Rect.fromLTWH(0.0, 0.0, 10.0, 20.0);
int describeSemanticsConfigurationCallCount = 0;
@override @override
void describeSemanticsConfiguration(SemanticsConfiguration config) { void describeSemanticsConfiguration(SemanticsConfiguration config) {
super.describeSemanticsConfiguration(config); super.describeSemanticsConfiguration(config);
config.isSemanticBoundary = true; config.isSemanticBoundary = true;
describeSemanticsConfigurationCallCount++;
} }
} }
...@@ -12,6 +12,10 @@ import 'semantics_tester.dart'; ...@@ -12,6 +12,10 @@ import 'semantics_tester.dart';
void main() { void main() {
SemanticsTester semantics; SemanticsTester semantics;
setUp(() {
debugResetSemanticsIdCounter();
});
tearDown(() { tearDown(() {
semantics?.dispose(); semantics?.dispose();
semantics = null; semantics = null;
...@@ -327,13 +331,35 @@ void main() { ...@@ -327,13 +331,35 @@ void main() {
children: new List<Widget>.generate(40, (int i) { children: new List<Widget>.generate(40, (int i) {
return new Container( return new Container(
child: new Text('item $i'), child: new Text('item $i'),
height: 40.0, height: 400.0,
); );
}), }),
), ),
), ),
); );
final TestSemantics expectedSemantics = new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
children: <TestSemantics>[
new TestSemantics(
actions: <SemanticsAction>[SemanticsAction.scrollUp],
children: <TestSemantics>[
new TestSemantics(
label: r'item 0',
textDirection: TextDirection.ltr,
),
new TestSemantics(
label: r'item 1',
textDirection: TextDirection.ltr,
),
],
),
],
),
],
);
// Start with semantics off. // Start with semantics off.
expect(tester.binding.pipelineOwner.semanticsOwner, isNull); expect(tester.binding.pipelineOwner.semanticsOwner, isNull);
...@@ -341,6 +367,7 @@ void main() { ...@@ -341,6 +367,7 @@ void main() {
semantics = new SemanticsTester(tester); semantics = new SemanticsTester(tester);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(tester.binding.pipelineOwner.semanticsOwner, isNotNull); expect(tester.binding.pipelineOwner.semanticsOwner, isNotNull);
expect(semantics, hasSemantics(expectedSemantics, ignoreId: true, ignoreRect: true, ignoreTransform: true));
// Semantics off // Semantics off
semantics.dispose(); semantics.dispose();
...@@ -351,6 +378,7 @@ void main() { ...@@ -351,6 +378,7 @@ void main() {
semantics = new SemanticsTester(tester); semantics = new SemanticsTester(tester);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(tester.binding.pipelineOwner.semanticsOwner, isNotNull); expect(tester.binding.pipelineOwner.semanticsOwner, isNotNull);
expect(semantics, hasSemantics(expectedSemantics, ignoreId: true, ignoreRect: true, ignoreTransform: true));
}); });
} }
......
...@@ -265,9 +265,9 @@ class TestSemantics { ...@@ -265,9 +265,9 @@ 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 (nextNodeId != null && nextNodeId != nodeData.nextNodeId) if (!ignoreId && nextNodeId != null && nextNodeId != nodeData.nextNodeId)
return fail('expected node id $id to have nextNodeId "$nextNodeId" but found "${nodeData.nextNodeId}".'); return fail('expected node id $id to have nextNodeId "$nextNodeId" but found "${nodeData.nextNodeId}".');
if (previousNodeId != null && previousNodeId != nodeData.previousNodeId) if (!ignoreId && previousNodeId != null && previousNodeId != nodeData.previousNodeId)
return fail('expected node id $id to have previousNodeId "$previousNodeId" but found "${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.');
......
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