From 498cfc67c39a8ad0742d48ccf9f7c9cb025c681b Mon Sep 17 00:00:00 2001 From: Michael Goderbauer Date: Thu, 1 Jun 2017 12:52:08 -0700 Subject: [PATCH] (Update and) Integrate Rendering README into code (#10389) * (Update and) Integrate Rendering README into code * Add mising whitespace * Review comments and other touch-ups --- packages/flutter/lib/src/rendering/README.md | 112 ------------------ .../flutter/lib/src/rendering/object.dart | 61 ++++++++-- 2 files changed, 53 insertions(+), 120 deletions(-) delete mode 100644 packages/flutter/lib/src/rendering/README.md diff --git a/packages/flutter/lib/src/rendering/README.md b/packages/flutter/lib/src/rendering/README.md deleted file mode 100644 index 540ac8ef7f..0000000000 --- a/packages/flutter/lib/src/rendering/README.md +++ /dev/null @@ -1,112 +0,0 @@ -Flutter Rendering Layer -======================= - -This document is intended to describe some of the core designs of the -Flutter rendering layer. - -Layout ------- - -Paint ------ - -Compositing ------------ - -Semantics ---------- - -The last phase of a frame is the Semantics phase. This only occurs if -a semantics server has been installed, for example if the user is -using an accessibility tool. - -Each frame, the semantics phase starts with a call to the -`PipelineOwner.flushSemantics()` method from the `Renderer` binding's -`beginFrame()` method. - -Each node marked as needing semantics (which initially is just the -root node, as scheduled by `scheduleInitialSemantics()`), in depth -order, has its semantics updated by calling `_updateSemantics()`. - -The `_updateSemantics()` method calls `_getSemantics()` to obtain an -`_InterestingSemanticsFragment`, and then calls `compile()` on that -fragment to obtain a `SemanticsNode` which becomes the value of the -`RenderObject`'s `_semantics` field. **This is essentially a two-pass -walk of the render tree. The first pass determines the shape of the -output tree, and the second creates the nodes of this tree and hooks -them together.** The second walk is a sparse walk; it only walks the -nodes that are interesting for the purpose of semantics. - -`_getSemantics()` is the core function that walks the render tree to -obtain the semantics. It collects semantic annotators for this -`RenderObject`, then walks its children collecting -`_SemanticsFragment`s for them, and then returns an appropriate -`_SemanticsFragment` object that describes the `RenderObject`'s -semantics. - -Semantic annotators are functions that, given a `SemanticsNode`, set -some flags or strings on the object. They are obtained from -`getSemanticsAnnotators()`. For example, here is how `RenderParagraph` -annotates the `SemanticsNode` with its text: - -```dart - Iterable getSemanticsAnnotators() sync* { - yield (SemanticsNode node) { - node.label = text.toPlainText(); - }; - } -``` - -A `_SemanticsFragment` object is a node in a short-lived tree which is -used to create the final `SemanticsNode` tree that is sent to the -semantics server. These objects have a list of semantic annotators, -and a list of `_SemanticsFragment` children. - -There are several `_SemanticsFragment` classes. The `_getSemantics()` -method picks its return value as follows: - -* `_CleanSemanticsFragment` is used to represent a `RenderObject` that - has a `SemanticsNode` and which is in no way dirty. This class has - no children and no annotators, and when compiled, it returns the - `SemanticsNode` that the `RenderObject` already has. - -* `_RootSemanticsFragment`* is used to represent the `RenderObject` - found at the top of the render tree. This class always compiles to a - `SemanticsNode` with ID 0. - -* `_ConcreteSemanticsFragment`* is used to represent a `RenderObject` - that has `hasSemantics` set to true. It returns the `SemanticsNode` - for that `RenderObject`. - -* `_ImplicitSemanticsFragment`* is used to represent a `RenderObject` - that does not have `hasSemantics` set to true, but which does have - some semantic annotators. When it is compiled, if the nearest - ancestor `_SemanticsFragment` that isn't also an - `_ImplicitSemanticsFragment` is a `_RootSemanticsFragment` or a - `_ConcreteSemanticsFragment`, then the `SemanticsNode` from that - object is reused. Otherwise, a new one is created. - -* `_ForkingSemanticsFragment` is used to represent a `RenderObject` - that introduces no semantics of its own, but which has two or more - descendants that do introduce semantics (and which are not ancestors - or descendants of each other). - -* For `RenderObject` nodes that introduce no semantics but which have - a (single) child that does, the `_SemanticsFragment` of the child is - returned. - -* For `RenderObject` nodes that introduce no semantics and have no - descendants that introduce semantics, `null` is returned. - -The classes marked with an asterisk * above are the -`_InterestingSemanticsFragment` classes. - -When the `_SemanticsFragment` tree is then compiled, the -`SemanticsNode` objects are created (if necessary), the semantic -annotators are run on each `SemanticsNode`, the geometry (matrix, -size, and clip) is applied, and the children are updated. - -As part of this, the code clears out the `_semantics` field of any -`RenderObject` that previously had a `SemanticsNode` but no longer -does. This is done as part of the first walk where possible, and as -part of the second otherwise. diff --git a/packages/flutter/lib/src/rendering/object.dart b/packages/flutter/lib/src/rendering/object.dart index 9b7dd0b26b..3880119661 100644 --- a/packages/flutter/lib/src/rendering/object.dart +++ b/packages/flutter/lib/src/rendering/object.dart @@ -652,6 +652,12 @@ class _SemanticsGeometry { } } +/// Describes the shape of the semantic tree. +/// +/// A [_SemanticsFragment] object is a node in a short-lived tree which is used +/// to create the final [SemanticsNode] tree that is sent to the semantics +/// server. These objects have a [SemanticsAnnotator], and a list of +/// [_SemanticsFragment] children. abstract class _SemanticsFragment { _SemanticsFragment({ @required RenderObject renderObjectOwner, @@ -689,9 +695,11 @@ abstract class _SemanticsFragment { String toString() => '$runtimeType#$hashCode'; } -/// Represents a subtree that doesn't need updating, it already has a -/// SemanticsNode and isn't dirty. (We still update the matrix, since -/// that comes from the (dirty) ancestors.) +/// Represents a [RenderObject] which is in no way dirty. +/// +/// This class has no children and no annotators, and when compiled, it returns +/// the [SemanticsNode] that the [RenderObject] already has. (We still update +/// the matrix, since that comes from the (dirty) ancestors.) class _CleanSemanticsFragment extends _SemanticsFragment { _CleanSemanticsFragment({ @required RenderObject renderObjectOwner @@ -750,6 +758,9 @@ abstract class _InterestingSemanticsFragment extends _SemanticsFragment { _SemanticsGeometry createSemanticsGeometryForChild(_SemanticsGeometry geometry); } +/// Represents the [RenderObject] found at the top of the render tree. +/// +/// This class always compiles to a [SemanticsNode] with ID 0. class _RootSemanticsFragment extends _InterestingSemanticsFragment { _RootSemanticsFragment({ RenderObject renderObjectOwner, @@ -780,6 +791,9 @@ class _RootSemanticsFragment extends _InterestingSemanticsFragment { } } +/// Represents a RenderObject that has [isSemanticBoundary] set to `true`. +/// +/// It returns the SemanticsNode for that [RenderObject]. class _ConcreteSemanticsFragment extends _InterestingSemanticsFragment { _ConcreteSemanticsFragment({ RenderObject renderObjectOwner, @@ -808,6 +822,13 @@ class _ConcreteSemanticsFragment extends _InterestingSemanticsFragment { } } +/// Represents a RenderObject that does not have [isSemanticBoundary] set to +/// `true`, but which does have some semantic annotators. +/// +/// When it is compiled, if the nearest ancestor [_SemanticsFragment] that isn't +/// also an [_ImplicitSemanticsFragment] is a [_RootSemanticsFragment] or a +/// [_ConcreteSemanticsFragment], then the [SemanticsNode] from that object is +/// reused. Otherwise, a new one is created. class _ImplicitSemanticsFragment extends _InterestingSemanticsFragment { _ImplicitSemanticsFragment({ RenderObject renderObjectOwner, @@ -851,6 +872,9 @@ class _ImplicitSemanticsFragment extends _InterestingSemanticsFragment { } } +/// Represents a [RenderObject] that introduces no semantics of its own, but +/// which has two or more descendants that do introduce semantics +/// (and which are not ancestors or descendants of each other). class _ForkingSemanticsFragment extends _SemanticsFragment { _ForkingSemanticsFragment({ RenderObject renderObjectOwner, @@ -1180,7 +1204,11 @@ class PipelineOwner { bool _debugDoingSemantics = false; final List _nodesNeedingSemantics = []; - /// Update the semantics for all render objects. + /// Update the semantics for render objects marked as needing a semantics + /// update. + /// + /// Initially, only the root node, as scheduled by [scheduleInitialSemantics], + /// needs a semantics update. /// /// This function is one of the core stages of the rendering pipeline. The /// semantics are compiled after painting and only after @@ -2486,6 +2514,13 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { } } + /// Updates the semantic information of the render object. + /// + /// This is essentially a two-pass walk of the render tree. The first pass + /// determines the shape of the output tree (captured in + /// [_SemanticsFragment]s), and the second creates the nodes of this tree and + /// hooks them together. The second walk is a sparse walk; it only walks the + /// nodes that are interesting for the purpose of semantics. void _updateSemantics() { try { assert(_needsSemanticsUpdate); @@ -2500,6 +2535,12 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { } } + /// Core function that walks the render tree to obtain the semantics. + /// + /// It collects semantic annotators for this RenderObject, then walks its + /// children collecting [_SemanticsFragments] for them, and then returns an + /// appropriate [_SemanticsFragment] object that describes the RenderObject's + /// semantics. _SemanticsFragment _getSemanticsFragment() { // early-exit if we're not dirty and have our own semantics if (!_needsSemanticsUpdate && isSemanticBoundary) { @@ -2533,17 +2574,21 @@ abstract class RenderObject extends AbstractNode implements HitTestTarget { if (annotator != null) return new _ImplicitSemanticsFragment(renderObjectOwner: this, annotator: annotator, children: children); _semantics = null; - if (children == null) + if (children == null) { + // Introduces no semantics and has no descendants that introduce semantics. return null; + } if (children.length > 1) return new _ForkingSemanticsFragment(renderObjectOwner: this, children: children); assert(children.length == 1); return children.single; } - /// Called when collecting the semantics of this node. Subclasses - /// that have children that are not semantically relevant (e.g. - /// because they are invisible) should skip those children here. + /// Called when collecting the semantics of this node. + /// + /// The implementation has to return the children in paint order skipping all + /// children that are not semantically relevant (e.g. because they are + /// invisible). /// /// The default implementation mirrors the behavior of /// [visitChildren()] (which is supposed to walk all the children).