Merge pull request #1279 from Hixie/fn3
More dartdocs for fn3, refactor buildDirtyElements
This commit is contained in:
commit
244fdab0b1
@ -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> {
|
|||||||
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> {
|
|||||||
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> {
|
|||||||
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> {
|
|||||||
_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> {
|
|||||||
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> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
|||||||
|
|
||||||
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
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user