Commit 244fdab0 authored by Adam Barth's avatar Adam Barth

Merge pull request #1279 from Hixie/fn3

More dartdocs for fn3, refactor buildDirtyElements
parents 963fb413 de62f1b6
...@@ -179,6 +179,7 @@ abstract class Element<T extends Widget> { ...@@ -179,6 +179,7 @@ abstract class Element<T extends Widget> {
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.
/// The element at the root of the tree must have a depth greater than 0.
int _depth; int _depth;
/// The configuration for this element. /// The configuration for this element.
...@@ -258,7 +259,7 @@ abstract class Element<T extends Widget> { ...@@ -258,7 +259,7 @@ abstract class Element<T extends Widget> {
assert(_depth == null); assert(_depth == null);
_parent = parent; _parent = parent;
_slot = slot; _slot = slot;
_depth = _parent != null ? _parent._depth + 1 : 0; _depth = _parent != null ? _parent._depth + 1 : 1;
assert(() { _debugLifecycleState = _ElementLifecycle.mounted; return true; }); assert(() { _debugLifecycleState = _ElementLifecycle.mounted; return true; });
} }
...@@ -328,64 +329,20 @@ abstract class Element<T extends Widget> { ...@@ -328,64 +329,20 @@ abstract class Element<T extends Widget> {
void unmount() { void unmount() {
assert(_debugLifecycleState == _ElementLifecycle.mounted); assert(_debugLifecycleState == _ElementLifecycle.mounted);
assert(_widget != null); assert(_widget != null);
assert(_slot == null);
assert(_depth != null); assert(_depth != null);
assert(() { _debugLifecycleState = _ElementLifecycle.defunct; return true; }); assert(() { _debugLifecycleState = _ElementLifecycle.defunct; return true; });
} }
// TODO(ianh): Merge this into the binding, expose for tests
static void flushBuild() { static void flushBuild() {
_buildScheduler.buildDirtyElements(); _buildScheduler.buildDirtyElements();
} }
} }
class _BuildScheduler {
final Set<BuildableElement> _dirtyElements = new Set<BuildableElement>();
bool _inBuildDirtyElements = false;
void schedule(BuildableElement element) {
if (_dirtyElements.isEmpty)
scheduler.ensureVisualUpdate();
_dirtyElements.add(element);
}
void _absorbDirtyElements(List<BuildableElement> list) {
list.addAll(_dirtyElements);
_dirtyElements.clear();
list.sort((BuildableElement a, BuildableElement b) => a._depth - b._depth);
}
void buildDirtyElements() {
if (_dirtyElements.isEmpty)
return;
_inBuildDirtyElements = true;
try {
while (!_dirtyElements.isEmpty) {
List<BuildableElement> sortedDirtyElements = new List<BuildableElement>();
_absorbDirtyElements(sortedDirtyElements);
int index = 0;
while (index < sortedDirtyElements.length) {
sortedDirtyElements[index]._rebuildIfNeeded();
if (!_dirtyElements.isEmpty) {
assert(_dirtyElements.every((Element element) => !sortedDirtyElements.contains(element)));
_absorbDirtyElements(sortedDirtyElements);
index = 0;
} else {
index += 1;
}
}
}
} finally {
_inBuildDirtyElements = false;
}
assert(_dirtyElements.isEmpty);
}
}
final _BuildScheduler _buildScheduler = new _BuildScheduler();
typedef Widget WidgetBuilder(); typedef Widget WidgetBuilder();
/// Base class for the instantiation of StatelessComponent and StatefulComponent
/// widgets.
abstract class BuildableElement<T extends Widget> extends Element<T> { abstract class BuildableElement<T extends Widget> extends Element<T> {
BuildableElement(T widget) : super(widget); BuildableElement(T widget) : super(widget);
...@@ -393,6 +350,14 @@ abstract class BuildableElement<T extends Widget> extends Element<T> { ...@@ -393,6 +350,14 @@ abstract class BuildableElement<T extends Widget> extends Element<T> {
Element _child; Element _child;
bool _dirty = true; bool _dirty = true;
void mount(Element parent, dynamic slot) {
super.mount(parent, slot);
assert(_child == null);
_rebuild();
assert(_child != null);
}
// This is also called for the first build
void _rebuild() { void _rebuild() {
assert(_debugLifecycleState == _ElementLifecycle.mounted); assert(_debugLifecycleState == _ElementLifecycle.mounted);
_dirty = false; _dirty = false;
...@@ -406,18 +371,25 @@ abstract class BuildableElement<T extends Widget> extends Element<T> { ...@@ -406,18 +371,25 @@ abstract class BuildableElement<T extends Widget> extends Element<T> {
_child = _updateChild(_child, built, _slot); _child = _updateChild(_child, built, _slot);
} }
/// Called by the binding when scheduleBuild() has been called to mark this element dirty.
void _rebuildIfNeeded() { void _rebuildIfNeeded() {
if (_dirty) if (_dirty)
_rebuild(); _rebuild();
} }
/// Marks the element as dirty and adds it to the global list of widgets to
/// rebuild in the next frame.
///
/// Since it is inefficient to build an element twice in one frame,
/// applications and components should be structured so as to only mark
/// components dirty during event handlers before the frame begins, not during
/// the build itself.
void scheduleBuild() { void scheduleBuild() {
assert(_debugLifecycleState == _ElementLifecycle.mounted); assert(_debugLifecycleState == _ElementLifecycle.mounted);
if (_dirty) if (_dirty)
return; return;
_dirty = true; _dirty = true;
_buildScheduler.schedule(this); _buildScheduler.schedule(this);
// TODO(abarth): Implement rebuilding.
} }
void visitChildren(ElementVisitor visitor) { void visitChildren(ElementVisitor visitor) {
...@@ -425,22 +397,16 @@ abstract class BuildableElement<T extends Widget> extends Element<T> { ...@@ -425,22 +397,16 @@ abstract class BuildableElement<T extends Widget> extends Element<T> {
visitor(_child); visitor(_child);
} }
void mount(Element parent, dynamic slot) {
super.mount(parent, slot);
assert(_child == null);
_rebuild();
assert(_child != null);
}
void unmount() { void unmount() {
super.unmount(); super.unmount();
_dirty = false; _dirty = false; // so that we don't get rebuilt even if we're already marked dirty
} }
} }
/// Instantiation of StatelessComponent widgets.
class StatelessComponentElement extends BuildableElement<StatelessComponent> { class StatelessComponentElement extends BuildableElement<StatelessComponent> {
StatelessComponentElement(StatelessComponent component) : super(component) { StatelessComponentElement(StatelessComponent widget) : super(widget) {
_builder = component.build; _builder = _widget.build;
} }
void update(StatelessComponent newWidget) { void update(StatelessComponent newWidget) {
...@@ -451,10 +417,11 @@ class StatelessComponentElement extends BuildableElement<StatelessComponent> { ...@@ -451,10 +417,11 @@ class StatelessComponentElement extends BuildableElement<StatelessComponent> {
} }
} }
/// Instantiation of StatefulComponent widgets.
class StatefulComponentElement extends BuildableElement<StatefulComponent> { class StatefulComponentElement extends BuildableElement<StatefulComponent> {
StatefulComponentElement(StatefulComponent configuration) StatefulComponentElement(StatefulComponent widget)
: _state = configuration.createState(), super(configuration) { : _state = widget.createState(), super(widget) {
assert(_state._config == configuration); assert(_state._config == widget);
_state._element = this; _state._element = this;
_builder = _state.build; _builder = _state.build;
} }
...@@ -478,25 +445,27 @@ class StatefulComponentElement extends BuildableElement<StatefulComponent> { ...@@ -478,25 +445,27 @@ class StatefulComponentElement extends BuildableElement<StatefulComponent> {
} }
} }
RenderObjectElement _findAncestorRenderObjectElement(Element ancestor) { /// Base class for instantiations of RenderObjectWidget subclasses
while (ancestor != null && ancestor is! RenderObjectElement) abstract class RenderObjectElement<T extends RenderObjectWidget> extends Element<T> {
ancestor = ancestor._parent;
return ancestor;
}
class RenderObjectElement<T extends RenderObjectWidget> extends Element<T> {
RenderObjectElement(T widget) RenderObjectElement(T widget)
: renderObject = widget.createRenderObject(), super(widget); : renderObject = widget.createRenderObject(), super(widget);
/// The underlying [RenderObject] for this element
final RenderObject renderObject; final RenderObject renderObject;
RenderObjectElement _ancestorRenderObjectElement; RenderObjectElement _ancestorRenderObjectElement;
RenderObjectElement _findAncestorRenderObjectElement() {
Element ancestor = _parent;
while (ancestor != null && ancestor is! RenderObjectElement)
ancestor = ancestor._parent;
return ancestor;
}
void mount(Element parent, dynamic slot) { void mount(Element parent, dynamic slot) {
super.mount(parent, slot); super.mount(parent, slot);
assert(_ancestorRenderObjectElement == null); assert(_ancestorRenderObjectElement == null);
_ancestorRenderObjectElement = _findAncestorRenderObjectElement(_parent); _ancestorRenderObjectElement = _findAncestorRenderObjectElement();
if (_ancestorRenderObjectElement != null) _ancestorRenderObjectElement?.insertChildRenderObject(renderObject, slot);
_ancestorRenderObjectElement.insertChildRenderObject(renderObject, slot);
} }
void update(T newWidget) { void update(T newWidget) {
...@@ -512,6 +481,15 @@ class RenderObjectElement<T extends RenderObjectWidget> extends Element<T> { ...@@ -512,6 +481,15 @@ class RenderObjectElement<T extends RenderObjectWidget> extends Element<T> {
} }
} }
void insertChildRenderObject(RenderObject child, dynamic slot);
void removeChildRenderObject(RenderObject child);
}
/// Instantiation of RenderObjectWidgets that have no children
class LeafRenderObjectElement<T extends RenderObjectWidget> extends RenderObjectElement<T> {
LeafRenderObjectElement(T widget): super(widget);
void insertChildRenderObject(RenderObject child, dynamic slot) { void insertChildRenderObject(RenderObject child, dynamic slot) {
assert(false); assert(false);
} }
...@@ -521,10 +499,7 @@ class RenderObjectElement<T extends RenderObjectWidget> extends Element<T> { ...@@ -521,10 +499,7 @@ class RenderObjectElement<T extends RenderObjectWidget> extends Element<T> {
} }
} }
class LeafRenderObjectElement<T extends RenderObjectWidget> extends RenderObjectElement<T> { /// Instantiation of RenderObjectWidgets that have up to one child
LeafRenderObjectElement(T widget): super(widget);
}
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);
...@@ -537,19 +512,19 @@ class OneChildRenderObjectElement<T extends OneChildRenderObjectWidget> extends ...@@ -537,19 +512,19 @@ class OneChildRenderObjectElement<T extends OneChildRenderObjectWidget> extends
void mount(Element parent, dynamic slot) { void mount(Element parent, dynamic slot) {
super.mount(parent, slot); super.mount(parent, slot);
_child = _updateChild(_child, _widget.child, uniqueChild); _child = _updateChild(_child, _widget.child, _uniqueChild);
} }
void update(T newWidget) { void update(T newWidget) {
super.update(newWidget); super.update(newWidget);
assert(_widget == newWidget); 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
} }
...@@ -563,10 +538,61 @@ class OneChildRenderObjectElement<T extends OneChildRenderObjectWidget> extends ...@@ -563,10 +538,61 @@ class OneChildRenderObjectElement<T extends OneChildRenderObjectWidget> extends
} }
} }
/// Instantiation of RenderObjectWidgets that can have a list of children
class MultiChildRenderObjectElement<T extends MultiChildRenderObjectWidget> extends RenderObjectElement<T> { class MultiChildRenderObjectElement<T extends MultiChildRenderObjectWidget> extends RenderObjectElement<T> {
MultiChildRenderObjectElement(T widget) : super(widget); MultiChildRenderObjectElement(T widget) : super(widget);
// TODO(ianh): implement
}
class _BuildScheduler {
final List<BuildableElement> _dirtyElements = new List<BuildableElement>();
int _debugBuildingAtDepth;
void schedule(BuildableElement element) {
assert(_debugBuildingAtDepth == null || element._depth > _debugBuildingAtDepth);
assert(!_dirtyElements.contains(element));
if (_dirtyElements.isEmpty)
scheduler.ensureVisualUpdate();
_dirtyElements.add(element);
}
void _absorbDirtyElements(List<BuildableElement> list) {
assert(_debugBuildingAtDepth != null);
assert(!_dirtyElements.any((element) => element._depth <= _debugBuildingAtDepth));
_dirtyElements.sort((BuildableElement a, BuildableElement b) => a._depth - b._depth);
list.addAll(_dirtyElements);
_dirtyElements.clear();
}
/// Builds all the elements that were marked as dirty using schedule(), in depth order.
/// If elements are marked as dirty while this runs, they must be deeper than the algorithm
/// has yet reached.
void buildDirtyElements() {
assert(_debugBuildingAtDepth == null);
if (_dirtyElements.isEmpty)
return;
assert(() { _debugBuildingAtDepth = 0; return true; });
List<BuildableElement> sortedDirtyElements = new List<BuildableElement>();
int index = 0;
do {
_absorbDirtyElements(sortedDirtyElements);
for (; index < sortedDirtyElements.length; index += 1) {
BuildableElement element = sortedDirtyElements[index];
assert(() {
if (element._depth > _debugBuildingAtDepth)
_debugBuildingAtDepth = element._depth;
return element._depth == _debugBuildingAtDepth;
});
element._rebuildIfNeeded();
}
} while (_dirtyElements.isNotEmpty);
assert(() { _debugBuildingAtDepth = null; return true; });
}
} }
final _BuildScheduler _buildScheduler = new _BuildScheduler();
typedef void WidgetsExceptionHandler(String context, dynamic exception, StackTrace stack); typedef void WidgetsExceptionHandler(String context, dynamic exception, StackTrace stack);
......
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