Commit ba41fd35 authored by Hixie's avatar Hixie

More dartdoc for fn3, and sundry changes.

- move _uniqueChild earlier since a comment now mentions it earlier.
- reorder methods in Element to more closely reflect call order.
- change mount to be the place that sets the parent pointer, for consistency.
- make the lifecycleState a purely debug-time thing for consistency.
- make BuildableElement.unmount set dirty to false so that we won't
  build unmounted objects.
- rename "updated" to "newWidget" for clarity.
- change how we unmount things so that the slot is reset up to a
  RenderObjectElement, but not further, and don't reset the depth.
parent 12097bdd
...@@ -158,6 +158,12 @@ enum _ElementLifecycle { ...@@ -158,6 +158,12 @@ enum _ElementLifecycle {
typedef void ElementVisitor(Element element); typedef void ElementVisitor(Element element);
const Object _uniqueChild = const Object();
/// Elements are the instantiations of Widget configurations.
///
/// Elements can, in principle, have children. Only subclasses of
/// RenderObjectElement are allowed to have more than one child.
abstract class Element<T extends Widget> { abstract class Element<T extends Widget> {
Element(T widget) : _widget = widget { Element(T widget) : _widget = widget {
assert(_widget != null); assert(_widget != null);
...@@ -165,17 +171,21 @@ abstract class Element<T extends Widget> { ...@@ -165,17 +171,21 @@ abstract class Element<T extends Widget> {
Element _parent; Element _parent;
/// information set by parent to define where this child fits in its parent's /// Information set by parent to define where this child fits in its parent's
/// child list /// child list.
///
/// Subclasses of Element that only have one child should use _uniqueChild for
/// the slot for that child.
dynamic _slot; dynamic _slot;
/// an integer that is guaranteed to be greater than the parent's, if any /// An integer that is guaranteed to be greater than the parent's, if any.
int _depth; int _depth;
/// the configuration for this element /// The configuration for this element.
T _widget; T _widget;
_ElementLifecycle _lifecycleState = _ElementLifecycle.initial; /// This is used to verify that Element objects move through life in an orderly fashion.
_ElementLifecycle _debugLifecycleState = _ElementLifecycle.initial;
/// Calls the argument for each child. Must be overridden by subclasses that support having children. /// Calls the argument for each child. Must be overridden by subclasses that support having children.
void visitChildren(ElementVisitor visitor) { } void visitChildren(ElementVisitor visitor) { }
...@@ -189,52 +199,90 @@ abstract class Element<T extends Widget> { ...@@ -189,52 +199,90 @@ abstract class Element<T extends Widget> {
visitChildren(walk); visitChildren(walk);
} }
void mount(dynamic slot) { Element _updateChild(Element child, Widget newWidget, dynamic slot) {
assert(_lifecycleState == _ElementLifecycle.initial); // This method is the core of the system.
assert(_parent == null || _parent._lifecycleState == _ElementLifecycle.mounted); //
// It is called each time we are to add, update, or remove a child based on
// an updated configuration.
//
// If the child is null, and the newWidget is not null, then we have a new
// child for which we need to create an Element, configured with newWidget.
//
// If the newWidget is null, and the child is not null, then we need to
// remove it because it no longer has a configuration.
//
// If neither are null, then we need to update the child's configuration to
// be the new configuration given by newWidget. If newWidget can be given to
// the existing child, then it is so given. Otherwise, the old child needs
// to be disposed and a new child created for the new configuration.
//
// If both are null, then we don't have a child and won't have a child, so
// we do nothing.
//
// The _updateChild() method returns the new child, if it had to create one,
// or the child that was passed in, if it just had to update the child, or
// null, if it removed the child and did not replace it.
assert(slot != null); assert(slot != null);
assert(_widget != null); if (newWidget == null) {
assert(_depth == null); if (child != null)
_lifecycleState = _ElementLifecycle.mounted; _detachChild(child);
_slot = slot; return null;
_depth = _parent == null ? 0 : _parent._depth + 1; }
if (child != null) {
assert(child._slot == slot);
if (child._widget == newWidget)
return child;
if (_canUpdate(child._widget, newWidget)) {
child.update(newWidget);
assert(child._widget == newWidget);
return child;
}
_detachChild(child);
assert(child._parent == null);
}
child = newWidget.createElement();
child.mount(this, slot);
assert(child._debugLifecycleState == _ElementLifecycle.mounted);
return child;
} }
void updateSlot(dynamic slot) { /// Called when an Element is given a new parent shortly after having been
assert(slot != null); /// created.
assert(_lifecycleState == _ElementLifecycle.mounted); void mount(Element parent, dynamic slot) {
assert(_parent != null); assert(_debugLifecycleState == _ElementLifecycle.initial);
assert(_parent._lifecycleState == _ElementLifecycle.mounted);
assert(_widget != null); assert(_widget != null);
assert(_parent == null);
assert(parent == null || parent._debugLifecycleState == _ElementLifecycle.mounted);
assert(_slot == null);
assert(slot != null);
assert(_depth == null); assert(_depth == null);
_parent = parent;
_slot = slot; _slot = slot;
_depth = _parent != null ? _parent._depth + 1 : 0;
assert(() { _debugLifecycleState = _ElementLifecycle.mounted; return true; });
} }
/// Called when an Element receives a new configuration widget.
void update(T newWidget) { void update(T newWidget) {
assert(newWidget != null); assert(_debugLifecycleState == _ElementLifecycle.mounted);
assert(_lifecycleState == _ElementLifecycle.mounted);
assert(_widget != null); assert(_widget != null);
assert(newWidget != null);
assert(_slot != null);
assert(_depth != null); assert(_depth != null);
assert(_canUpdate(_widget, newWidget)); assert(_canUpdate(_widget, newWidget));
_widget = newWidget; _widget = newWidget;
} }
void unmount() { /// Called by MultiChildRenderObjectElement, and other RenderObjectElement
assert(_lifecycleState == _ElementLifecycle.mounted); /// subclasses that have multiple children, to update the slot of a particular
assert(_widget != null); /// child when the child is moved in its child list.
assert(_depth != null); void updateSlotForChild(Element child, dynamic slot) {
_slot = null; assert(_debugLifecycleState == _ElementLifecycle.mounted);
_depth = null; assert(child != null);
_lifecycleState = _ElementLifecycle.defunct;
}
void _updateSlotForChild(Element child, dynamic slot) {
if (child == null)
return;
assert(child._parent == this); assert(child._parent == this);
void move(Element element) { void move(Element element) {
child.updateSlot(slot); child._updateSlot(slot);
if (child is! RenderObjectElement) if (child is! RenderObjectElement)
child.visitChildren(move); child.visitChildren(move);
} }
...@@ -242,47 +290,47 @@ abstract class Element<T extends Widget> { ...@@ -242,47 +290,47 @@ abstract class Element<T extends Widget> {
move(child); move(child);
} }
void _updateSlot(dynamic slot) {
assert(_debugLifecycleState == _ElementLifecycle.mounted);
assert(_widget != null);
assert(_parent != null);
assert(_parent._debugLifecycleState == _ElementLifecycle.mounted);
assert(_slot != null);
assert(slot != null);
assert(_depth == null);
_slot = slot;
}
void _detachChild(Element child) { void _detachChild(Element child) {
if (child == null) assert(child != null);
return;
assert(child._parent == this); assert(child._parent == this);
child._parent = null; child._parent = null;
bool haveDetachedRenderObject = false; bool haveDetachedRenderObject = false;
void detach(Element descendant) { void detach(Element descendant) {
if (!haveDetachedRenderObject && descendant is RenderObjectElement) { if (!haveDetachedRenderObject) {
descendant.detachRenderObject(); descendant._slot = null;
haveDetachedRenderObject = true; if (descendant is RenderObjectElement) {
descendant.detachRenderObject();
haveDetachedRenderObject = true;
}
} }
descendant.unmount(); descendant.unmount();
assert(descendant._debugLifecycleState == _ElementLifecycle.defunct);
} }
detach(child); detach(child);
child.visitDescendants(detach); child.visitDescendants(detach);
} }
Element _updateChild(Element child, Widget updated, dynamic slot) { /// Called when an Element is removed from the tree.
if (updated == null) { /// Currently, an Element removed from the tree never returns.
_detachChild(child); void unmount() {
return null; assert(_debugLifecycleState == _ElementLifecycle.mounted);
} assert(_widget != null);
assert(_slot == null);
if (child != null) { assert(_depth != null);
assert(child._slot == slot); assert(() { _debugLifecycleState = _ElementLifecycle.defunct; return true; });
if (child._widget == updated)
return child;
if (_canUpdate(child._widget, updated)) {
child.update(updated);
return child;
}
_detachChild(child);
assert(child._parent == null);
}
Element newChild = updated.createElement();
newChild._parent = this;
newChild.mount(slot);
return newChild;
} }
static void flushBuild() { static void flushBuild() {
...@@ -300,7 +348,7 @@ class _BuildScheduler { ...@@ -300,7 +348,7 @@ class _BuildScheduler {
_dirtyElements.add(element); _dirtyElements.add(element);
} }
void _absorbDirtyElement(List<BuildableElement> list) { void _absorbDirtyElements(List<BuildableElement> list) {
list.addAll(_dirtyElements); list.addAll(_dirtyElements);
_dirtyElements.clear(); _dirtyElements.clear();
list.sort((BuildableElement a, BuildableElement b) => a._depth - b._depth); list.sort((BuildableElement a, BuildableElement b) => a._depth - b._depth);
...@@ -314,7 +362,7 @@ class _BuildScheduler { ...@@ -314,7 +362,7 @@ class _BuildScheduler {
try { try {
while (!_dirtyElements.isEmpty) { while (!_dirtyElements.isEmpty) {
List<BuildableElement> sortedDirtyElements = new List<BuildableElement>(); List<BuildableElement> sortedDirtyElements = new List<BuildableElement>();
_absorbDirtyElement(sortedDirtyElements); _absorbDirtyElements(sortedDirtyElements);
int index = 0; int index = 0;
while (index < sortedDirtyElements.length) { while (index < sortedDirtyElements.length) {
sortedDirtyElements[index]._rebuildIfNeeded(); sortedDirtyElements[index]._rebuildIfNeeded();
...@@ -346,6 +394,7 @@ abstract class BuildableElement<T extends Widget> extends Element<T> { ...@@ -346,6 +394,7 @@ abstract class BuildableElement<T extends Widget> extends Element<T> {
bool _dirty = true; bool _dirty = true;
void _rebuild() { void _rebuild() {
assert(_debugLifecycleState == _ElementLifecycle.mounted);
_dirty = false; _dirty = false;
Widget built; Widget built;
try { try {
...@@ -358,12 +407,13 @@ abstract class BuildableElement<T extends Widget> extends Element<T> { ...@@ -358,12 +407,13 @@ abstract class BuildableElement<T extends Widget> extends Element<T> {
} }
void _rebuildIfNeeded() { void _rebuildIfNeeded() {
if (_dirty && _lifecycleState == _ElementLifecycle.mounted) if (_dirty)
_rebuild(); _rebuild();
} }
void scheduleBuild() { void scheduleBuild() {
if (_dirty || _lifecycleState != _ElementLifecycle.mounted) assert(_debugLifecycleState == _ElementLifecycle.mounted);
if (_dirty)
return; return;
_dirty = true; _dirty = true;
_buildScheduler.schedule(this); _buildScheduler.schedule(this);
...@@ -375,12 +425,17 @@ abstract class BuildableElement<T extends Widget> extends Element<T> { ...@@ -375,12 +425,17 @@ abstract class BuildableElement<T extends Widget> extends Element<T> {
visitor(_child); visitor(_child);
} }
void mount(dynamic slot) { void mount(Element parent, dynamic slot) {
super.mount(slot); super.mount(parent, slot);
assert(_child == null); assert(_child == null);
_rebuild(); _rebuild();
assert(_child != null); assert(_child != null);
} }
void unmount() {
super.unmount();
_dirty = false;
}
} }
class StatelessComponentElement extends BuildableElement<StatelessComponent> { class StatelessComponentElement extends BuildableElement<StatelessComponent> {
...@@ -436,17 +491,17 @@ class RenderObjectElement<T extends RenderObjectWidget> extends Element<T> { ...@@ -436,17 +491,17 @@ class RenderObjectElement<T extends RenderObjectWidget> extends Element<T> {
final RenderObject renderObject; final RenderObject renderObject;
RenderObjectElement _ancestorRenderObjectElement; RenderObjectElement _ancestorRenderObjectElement;
void mount(dynamic slot) { void mount(Element parent, dynamic slot) {
super.mount(slot); super.mount(parent, slot);
assert(_ancestorRenderObjectElement == null); assert(_ancestorRenderObjectElement == null);
_ancestorRenderObjectElement = _findAncestorRenderObjectElement(_parent); _ancestorRenderObjectElement = _findAncestorRenderObjectElement(_parent);
if (_ancestorRenderObjectElement != null) if (_ancestorRenderObjectElement != null)
_ancestorRenderObjectElement.insertChildRenderObject(renderObject, slot); _ancestorRenderObjectElement.insertChildRenderObject(renderObject, slot);
} }
void update(T updated) { void update(T newWidget) {
super.update(updated); super.update(newWidget);
assert(_widget == updated); assert(_widget == newWidget);
_widget.updateRenderObject(renderObject); _widget.updateRenderObject(renderObject);
} }
...@@ -470,8 +525,6 @@ class LeafRenderObjectElement<T extends RenderObjectWidget> extends RenderObject ...@@ -470,8 +525,6 @@ class LeafRenderObjectElement<T extends RenderObjectWidget> extends RenderObject
LeafRenderObjectElement(T widget): super(widget); LeafRenderObjectElement(T widget): super(widget);
} }
final Object _uniqueChild = new Object();
class OneChildRenderObjectElement<T extends OneChildRenderObjectWidget> extends RenderObjectElement<T> { class OneChildRenderObjectElement<T extends OneChildRenderObjectWidget> extends RenderObjectElement<T> {
OneChildRenderObjectElement(T widget) : super(widget); OneChildRenderObjectElement(T widget) : super(widget);
...@@ -482,21 +535,21 @@ class OneChildRenderObjectElement<T extends OneChildRenderObjectWidget> extends ...@@ -482,21 +535,21 @@ class OneChildRenderObjectElement<T extends OneChildRenderObjectWidget> extends
visitor(_child); visitor(_child);
} }
void mount(dynamic slot) { void mount(Element parent, dynamic slot) {
super.mount(slot); super.mount(parent, slot);
_child = _updateChild(_child, _widget.child, _uniqueChild); _child = _updateChild(_child, _widget.child, uniqueChild);
} }
void update(T updated) { void update(T newWidget) {
super.update(updated); super.update(newWidget);
assert(_widget == updated); assert(_widget == newWidget);
_child = _updateChild(_child, _widget.child, _uniqueChild); _child = _updateChild(_child, _widget.child, uniqueChild);
} }
void insertChildRenderObject(RenderObject child, dynamic slot) { void insertChildRenderObject(RenderObject child, dynamic slot) {
final renderObject = this.renderObject; // TODO(ianh): Remove this once the analyzer is cleverer final renderObject = this.renderObject; // TODO(ianh): Remove this once the analyzer is cleverer
assert(renderObject is RenderObjectWithChildMixin); assert(renderObject is RenderObjectWithChildMixin);
assert(slot == _uniqueChild); assert(slot == uniqueChild);
renderObject.child = child; renderObject.child = child;
assert(renderObject == this.renderObject); // TODO(ianh): Remove this once the analyzer is cleverer assert(renderObject == this.renderObject); // TODO(ianh): Remove this once the analyzer is cleverer
} }
......
...@@ -37,7 +37,7 @@ class WidgetTester { ...@@ -37,7 +37,7 @@ class WidgetTester {
void pumpFrame(Widget widget) { void pumpFrame(Widget widget) {
if (_rootElement == null) { if (_rootElement == null) {
_rootElement = new StatelessComponentElement(new TestComponent(child: widget)); _rootElement = new StatelessComponentElement(new TestComponent(child: widget));
_rootElement.mount(_rootSlot); _rootElement.mount(null, _rootSlot);
} else { } else {
_rootElement.update(new TestComponent(child: widget)); _rootElement.update(new TestComponent(child: widget));
} }
......
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