Unverified Commit 16e6be8b authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

Inline AbstractNode into SemanticsNode and Layer (#128467)

parent 40bb615b
......@@ -136,7 +136,7 @@ const String _flutterRenderingLibrary = 'package:flutter/rendering.dart';
///
/// * [RenderView.compositeFrame], which implements this recomposition protocol
/// for painting [RenderObject] trees on the display.
abstract class Layer extends AbstractNode with DiagnosticableTreeMixin {
abstract class Layer with DiagnosticableTreeMixin {
/// Creates an instance of Layer.
Layer() {
if (kFlutterMemoryAllocationsEnabled) {
......@@ -344,8 +344,8 @@ abstract class Layer extends AbstractNode with DiagnosticableTreeMixin {
///
/// Only subclasses of [ContainerLayer] can have children in the layer tree.
/// All other layer classes are used for leaves in the layer tree.
@override
ContainerLayer? get parent => super.parent as ContainerLayer?;
ContainerLayer? get parent => _parent;
ContainerLayer? _parent;
// Whether this layer has any changes since its last call to [addToScene].
//
......@@ -495,6 +495,71 @@ abstract class Layer extends AbstractNode with DiagnosticableTreeMixin {
_needsAddToScene = _needsAddToScene || alwaysNeedsAddToScene;
}
/// The owner for this node (null if unattached).
///
/// The entire subtree that this node belongs to will have the same owner.
Object? get owner => _owner;
Object? _owner;
/// Whether this node is in a tree whose root is attached to something.
///
/// This becomes true during the call to [attach].
///
/// This becomes false during the call to [detach].
bool get attached => _owner != null;
/// Mark this node as attached to the given owner.
///
/// Typically called only from the [parent]'s [attach] method, and by the
/// [owner] to mark the root of a tree as attached.
///
/// Subclasses with children should override this method to first call their
/// inherited [attach] method, and then [attach] all their children to the
/// same [owner].
///
/// Implementations of this method should start with a call to the inherited
/// method, as in `super.attach(owner)`.
@mustCallSuper
void attach(covariant Object owner) {
assert(_owner == null);
_owner = owner;
}
/// Mark this node as detached.
///
/// Typically called only from the [parent]'s [detach], and by the [owner] to
/// mark the root of a tree as detached.
///
/// Subclasses with children should override this method to first call their
/// inherited [detach] method, and then [detach] all their children.
///
/// Implementations of this method should end with a call to the inherited
/// method, as in `super.detach()`.
@mustCallSuper
void detach() {
assert(_owner != null);
_owner = null;
assert(parent == null || attached == parent!.attached);
}
/// The depth of this node in the tree.
///
/// The depth of nodes in a tree monotonically increases as you traverse down
/// the tree.
int get depth => _depth;
int _depth = 0;
/// Adjust the [depth] of this node's children, if any.
///
/// Override this method in subclasses with child nodes to call
/// [ContainerLayer.redepthChild] for each child. Do not call this method
/// directly.
@protected
void redepthChildren() {
// ContainerLayer provides an implementation since its the only one that
// can actually have children.
}
/// This layer's next sibling in the parent layer's child list.
Layer? get nextSibling => _nextSibling;
Layer? _nextSibling;
......@@ -503,30 +568,6 @@ abstract class Layer extends AbstractNode with DiagnosticableTreeMixin {
Layer? get previousSibling => _previousSibling;
Layer? _previousSibling;
@override
void dropChild(Layer child) {
assert(!_debugMutationsLocked);
if (!alwaysNeedsAddToScene) {
markNeedsAddToScene();
}
if (child._compositionCallbackCount != 0) {
_updateSubtreeCompositionObserverCount(-child._compositionCallbackCount);
}
super.dropChild(child);
}
@override
void adoptChild(Layer child) {
assert(!_debugMutationsLocked);
if (!alwaysNeedsAddToScene) {
markNeedsAddToScene();
}
if (child._compositionCallbackCount != 0) {
_updateSubtreeCompositionObserverCount(child._compositionCallbackCount);
}
super.adoptChild(child);
}
/// Removes this layer from its parent layer's child list.
///
/// This has no effect if the layer's parent is already null.
......@@ -1198,7 +1239,7 @@ class ContainerLayer extends Layer {
assert(node != child); // indicates we are about to create a cycle
return true;
}());
adoptChild(child);
_adoptChild(child);
child._previousSibling = lastChild;
if (lastChild != null) {
lastChild!._nextSibling = child;
......@@ -1209,6 +1250,52 @@ class ContainerLayer extends Layer {
assert(child.attached == attached);
}
void _adoptChild(Layer child) {
assert(!_debugMutationsLocked);
if (!alwaysNeedsAddToScene) {
markNeedsAddToScene();
}
if (child._compositionCallbackCount != 0) {
_updateSubtreeCompositionObserverCount(child._compositionCallbackCount);
}
assert(child._parent == null);
assert(() {
Layer node = this;
while (node.parent != null) {
node = node.parent!;
}
assert(node != child); // indicates we are about to create a cycle
return true;
}());
child._parent = this;
if (attached) {
child.attach(_owner!);
}
redepthChild(child);
}
@override
void redepthChildren() {
Layer? child = firstChild;
while (child != null) {
redepthChild(child);
child = child.nextSibling;
}
}
/// Adjust the [depth] of the given [child] to be greater than this node's own
/// [depth].
///
/// Only call this method from overrides of [redepthChildren].
@protected
void redepthChild(Layer child) {
assert(child.owner == owner);
if (child._depth <= _depth) {
child._depth = _depth + 1;
child.redepthChildren();
}
}
// Implementation of [Layer.remove].
void _removeChild(Layer child) {
assert(child.parent == this);
......@@ -1235,11 +1322,27 @@ class ContainerLayer extends Layer {
assert(lastChild == null || _debugUltimatePreviousSiblingOf(lastChild!, equals: firstChild));
child._previousSibling = null;
child._nextSibling = null;
dropChild(child);
_dropChild(child);
child._parentHandle.layer = null;
assert(!child.attached);
}
void _dropChild(Layer child) {
assert(!_debugMutationsLocked);
if (!alwaysNeedsAddToScene) {
markNeedsAddToScene();
}
if (child._compositionCallbackCount != 0) {
_updateSubtreeCompositionObserverCount(-child._compositionCallbackCount);
}
assert(child._parent == this);
assert(child.attached == attached);
child._parent = null;
if (attached) {
child.detach();
}
}
/// Removes all of this layer's children from its child list.
void removeAllChildren() {
assert(!_debugMutationsLocked);
......@@ -1249,7 +1352,7 @@ class ContainerLayer extends Layer {
child._previousSibling = null;
child._nextSibling = null;
assert(child.attached == attached);
dropChild(child);
_dropChild(child);
child._parentHandle.layer = null;
child = next;
}
......
......@@ -1646,7 +1646,7 @@ void debugResetSemanticsIdCounter() {
/// (i.e., during [PipelineOwner.flushSemantics]), which happens after
/// compositing. The semantics tree is then uploaded into the engine for use
/// by assistive technology.
class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
class SemanticsNode with DiagnosticableTreeMixin {
/// Creates a semantic node.
///
/// Each semantic node has a unique identifier that is assigned when the node
......@@ -1923,7 +1923,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
if (child.parent == this) {
// we might have already had our child stolen from us by
// another node that is deeper in the tree.
dropChild(child);
_dropChild(child);
}
sawChange = true;
}
......@@ -1937,10 +1937,10 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
// ancestors. In that case, we drop the child eagerly here.
// TODO(ianh): Find a way to assert that the same node didn't
// actually appear in the tree in two places.
child.parent?.dropChild(child);
child.parent?._dropChild(child);
}
assert(!child.attached);
adoptChild(child);
_adoptChild(child);
sawChange = true;
}
}
......@@ -1998,22 +1998,73 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
return true;
}
// AbstractNode OVERRIDES
/// The owner for this node (null if unattached).
///
/// The entire subtree that this node belongs to will have the same owner.
SemanticsOwner? get owner => _owner;
SemanticsOwner? _owner;
@override
SemanticsOwner? get owner => super.owner as SemanticsOwner?;
/// Whether this node is in a tree whose root is attached to something.
///
/// This becomes true during the call to [attach].
///
/// This becomes false during the call to [detach].
bool get attached => _owner != null;
@override
SemanticsNode? get parent => super.parent as SemanticsNode?;
/// The parent of this node in the tree.
SemanticsNode? get parent => _parent;
SemanticsNode? _parent;
@override
void redepthChildren() {
_children?.forEach(redepthChild);
/// The depth of this node in the tree.
///
/// The depth of nodes in a tree monotonically increases as you traverse down
/// the tree.
int get depth => _depth;
int _depth = 0;
void _redepthChild(SemanticsNode child) {
assert(child.owner == owner);
if (child._depth <= _depth) {
child._depth = _depth + 1;
child._redepthChildren();
}
}
@override
void _redepthChildren() {
_children?.forEach(_redepthChild);
}
void _adoptChild(SemanticsNode child) {
assert(child._parent == null);
assert(() {
SemanticsNode node = this;
while (node.parent != null) {
node = node.parent!;
}
assert(node != child); // indicates we are about to create a cycle
return true;
}());
child._parent = this;
if (attached) {
child.attach(_owner!);
}
_redepthChild(child);
}
void _dropChild(SemanticsNode child) {
assert(child._parent == this);
assert(child.attached == attached);
child._parent = null;
if (attached) {
child.detach();
}
}
/// Mark this node as attached to the given owner.
@visibleForTesting
void attach(SemanticsOwner owner) {
super.attach(owner);
assert(_owner == null);
_owner = owner;
while (owner._nodes.containsKey(id)) {
// Ids may repeat if the Flutter has generated > 2^16 ids. We need to keep
// regenerating the id until we found an id that is not used.
......@@ -2032,14 +2083,16 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
}
}
@override
/// Mark this node as detached.
@visibleForTesting
void detach() {
assert(_owner != null);
assert(owner!._nodes.containsKey(id));
assert(!owner!._detachedNodes.contains(this));
owner!._nodes.remove(id);
owner!._detachedNodes.add(this);
super.detach();
assert(owner == null);
_owner = null;
assert(parent == null || attached == parent!.attached);
if (_children != null) {
for (final SemanticsNode child in _children!) {
// The list of children may be stale and may contain nodes that have
......
......@@ -899,8 +899,7 @@ void main() {
expect(() => layer.markNeedsAddToScene(), throwsAssertionError);
expect(() => layer.debugMarkClean(), throwsAssertionError);
expect(() => layer.updateSubtreeNeedsAddToScene(), throwsAssertionError);
expect(() => layer.dropChild(ContainerLayer()), throwsAssertionError);
expect(() => layer.adoptChild(ContainerLayer()), throwsAssertionError);
expect(() => layer.remove(), throwsAssertionError);
expect(() => (layer as ContainerLayer).append(ContainerLayer()), throwsAssertionError);
expect(() => layer.engineLayer = null, throwsAssertionError);
compositedB1 = true;
......
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