Index: sky/sdk/lib/framework/widgets/ui_node.dart |
diff --git a/sky/sdk/lib/framework/fn2.dart b/sky/sdk/lib/framework/widgets/ui_node.dart |
similarity index 70% |
rename from sky/sdk/lib/framework/fn2.dart |
rename to sky/sdk/lib/framework/widgets/ui_node.dart |
index 227d04cb3db9fad33f1765917c845acee8358cb9..2d1cd2497b73444203874a6e78180409b87536df 100644 |
--- a/sky/sdk/lib/framework/fn2.dart |
+++ b/sky/sdk/lib/framework/widgets/ui_node.dart |
@@ -2,23 +2,19 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
-library fn; |
- |
-import 'app.dart'; |
import 'dart:async'; |
import 'dart:collection'; |
import 'dart:mirrors'; |
import 'dart:sky' as sky; |
-import 'package:vector_math/vector_math.dart'; |
-import 'rendering/block.dart'; |
-import 'rendering/box.dart'; |
-import 'rendering/flex.dart'; |
-import 'rendering/object.dart'; |
-import 'rendering/paragraph.dart'; |
-import 'rendering/stack.dart'; |
-export 'rendering/object.dart' show Point, Size, Rect, Color, Paint, Path; |
-export 'rendering/box.dart' show BoxConstraints, BoxDecoration, Border, BorderSide, EdgeDims; |
-export 'rendering/flex.dart' show FlexDirection; |
+ |
+import '../app.dart'; |
+import '../rendering/box.dart'; |
+import '../rendering/object.dart'; |
+ |
+export '../rendering/box.dart' show BoxConstraints, BoxDecoration, Border, BorderSide, EdgeDims; |
+export '../rendering/flex.dart' show FlexDirection; |
+export '../rendering/object.dart' show Point, Size, Rect, Color, Paint, Path; |
+ |
// final sky.Tracing _tracing = sky.window.tracing; |
@@ -155,6 +151,7 @@ abstract class UINode { |
} |
} |
+ |
// Descendants of TagNode provide a way to tag RenderObjectWrapper and |
// Component nodes with annotations, such as event listeners, |
// stylistic information, etc. |
@@ -275,6 +272,190 @@ class EventListenerNode extends TagNode { |
} |
+ |
+abstract class Component extends UINode { |
+ |
+ Component({ Object key, bool stateful }) |
+ : _stateful = stateful != null ? stateful : false, |
+ _order = _currentOrder + 1, |
+ super(key: key); |
+ |
+ Component.fromArgs(Object key, bool stateful) |
+ : this(key: key, stateful: stateful); |
+ |
+ static Component _currentlyBuilding; |
+ bool get _isBuilding => _currentlyBuilding == this; |
+ |
+ bool _stateful; |
+ bool _dirty = true; |
+ bool _disqualifiedFromEverAppearingAgain = false; |
+ |
+ UINode _built; |
+ dynamic _slot; // cached slot from the last time we were synced |
+ |
+ void didMount() { |
+ assert(!_disqualifiedFromEverAppearingAgain); |
+ super.didMount(); |
+ } |
+ |
+ void remove() { |
+ assert(_built != null); |
+ assert(root != null); |
+ removeChild(_built); |
+ _built = null; |
+ super.remove(); |
+ } |
+ |
+ bool _retainStatefulNodeIfPossible(UINode old) { |
+ assert(!_disqualifiedFromEverAppearingAgain); |
+ |
+ Component oldComponent = old as Component; |
+ if (oldComponent == null || !oldComponent._stateful) |
+ return false; |
+ |
+ assert(key == oldComponent.key); |
+ |
+ // Make |this|, the newly-created object, into the "old" Component, and kill it |
+ _stateful = false; |
+ _built = oldComponent._built; |
+ assert(_built != null); |
+ _disqualifiedFromEverAppearingAgain = true; |
+ |
+ // Make |oldComponent| the "new" component |
+ oldComponent._built = null; |
+ oldComponent._dirty = true; |
+ oldComponent.syncFields(this); |
+ return true; |
+ } |
+ |
+ // This is called by _retainStatefulNodeIfPossible(), during |
+ // syncChild(), just before _sync() is called. |
+ // This must be implemented on any subclass that can become stateful |
+ // (but don't call super.syncFields() if you inherit directly from |
+ // Component, since that'll fire an assert). |
+ // If you don't ever become stateful, then don't override this. |
+ void syncFields(Component source) { |
+ assert(false); |
+ } |
+ |
+ final int _order; |
+ static int _currentOrder = 0; |
+ |
+ /* There are three cases here: |
+ * 1) Building for the first time: |
+ * assert(_built == null && old == null) |
+ * 2) Re-building (because a dirty flag got set): |
+ * assert(_built != null && old == null) |
+ * 3) Syncing against an old version |
+ * assert(_built == null && old != null) |
+ */ |
+ void _sync(UINode old, dynamic slot) { |
+ assert(_built == null || old == null); |
+ assert(!_disqualifiedFromEverAppearingAgain); |
+ |
+ Component oldComponent = old as Component; |
+ |
+ _slot = slot; |
+ |
+ var oldBuilt; |
+ if (oldComponent == null) { |
+ oldBuilt = _built; |
+ } else { |
+ assert(_built == null); |
+ oldBuilt = oldComponent._built; |
+ } |
+ |
+ int lastOrder = _currentOrder; |
+ _currentOrder = _order; |
+ _currentlyBuilding = this; |
+ _built = build(); |
+ assert(_built != null); |
+ _currentlyBuilding = null; |
+ _currentOrder = lastOrder; |
+ |
+ _built = syncChild(_built, oldBuilt, slot); |
+ assert(_built != null); |
+ _dirty = false; |
+ _root = _built.root; |
+ assert(_root == root); // in case a subclass reintroduces it |
+ assert(root != null); |
+ } |
+ |
+ void _buildIfDirty() { |
+ assert(!_disqualifiedFromEverAppearingAgain); |
+ if (!_dirty || !_mounted) |
+ return; |
+ |
+ assert(root != null); |
+ _sync(null, _slot); |
+ } |
+ |
+ void scheduleBuild() { |
+ setState(() {}); |
+ } |
+ |
+ void setState(Function fn()) { |
+ assert(!_disqualifiedFromEverAppearingAgain); |
+ _stateful = true; |
+ fn(); |
+ if (_isBuilding || _dirty || !_mounted) |
+ return; |
+ |
+ _dirty = true; |
+ _scheduleComponentForRender(this); |
+ } |
+ |
+ UINode build(); |
+ |
+} |
+ |
+Set<Component> _dirtyComponents = new Set<Component>(); |
+bool _buildScheduled = false; |
+bool _inRenderDirtyComponents = false; |
+ |
+void _buildDirtyComponents() { |
+ //_tracing.begin('fn::_buildDirtyComponents'); |
+ |
+ Stopwatch sw; |
+ if (_shouldLogRenderDuration) |
+ sw = new Stopwatch()..start(); |
+ |
+ try { |
+ _inRenderDirtyComponents = true; |
+ |
+ List<Component> sortedDirtyComponents = _dirtyComponents.toList(); |
+ sortedDirtyComponents.sort((Component a, Component b) => a._order - b._order); |
+ for (var comp in sortedDirtyComponents) { |
+ comp._buildIfDirty(); |
+ } |
+ |
+ _dirtyComponents.clear(); |
+ _buildScheduled = false; |
+ } finally { |
+ _inRenderDirtyComponents = false; |
+ } |
+ |
+ UINode._notifyMountStatusChanged(); |
+ |
+ if (_shouldLogRenderDuration) { |
+ sw.stop(); |
+ print('Render took ${sw.elapsedMicroseconds} microseconds'); |
+ } |
+ |
+ //_tracing.end('fn::_buildDirtyComponents'); |
+} |
+ |
+void _scheduleComponentForRender(Component c) { |
+ assert(!_inRenderDirtyComponents); |
+ _dirtyComponents.add(c); |
+ |
+ if (!_buildScheduled) { |
+ _buildScheduled = true; |
+ new Future.microtask(_buildDirtyComponents); |
+ } |
+} |
+ |
+ |
/* |
* RenderObjectWrappers correspond to a desired state of a RenderObject. |
* They are fully immutable, with one exception: A UINode which is a |
@@ -378,253 +559,76 @@ abstract class OneChildRenderObjectWrapper extends RenderObjectWrapper { |
} |
-class Opacity extends OneChildRenderObjectWrapper { |
- Opacity({ this.opacity, UINode child, Object key }) |
- : super(child: child, key: key); |
- |
- RenderOpacity get root { RenderOpacity result = super.root; return result; } |
- final double opacity; |
+abstract class MultiChildRenderObjectWrapper extends RenderObjectWrapper { |
- RenderOpacity createNode() => new RenderOpacity(opacity: opacity); |
+ // In MultiChildRenderObjectWrapper subclasses, slots are RenderObject nodes |
+ // to use as the "insert before" sibling in ContainerRenderObjectMixin.add() calls |
- void syncRenderObject(Opacity old) { |
- super.syncRenderObject(old); |
- root.opacity = opacity; |
+ MultiChildRenderObjectWrapper({ |
+ Object key, |
+ List<UINode> children |
+ }) : this.children = children == null ? const [] : children, |
+ super(key: key) { |
+ assert(!_debugHasDuplicateIds()); |
} |
-} |
- |
-class ClipRect extends OneChildRenderObjectWrapper { |
- |
- ClipRect({ UINode child, Object key }) |
- : super(child: child, key: key); |
- |
- RenderClipRect get root { RenderClipRect result = super.root; return result; } |
- RenderClipRect createNode() => new RenderClipRect(); |
-} |
- |
-class ClipOval extends OneChildRenderObjectWrapper { |
- ClipOval({ UINode child, Object key }) |
- : super(child: child, key: key); |
+ final List<UINode> children; |
- RenderClipOval get root { RenderClipOval result = super.root; return result; } |
- RenderClipOval createNode() => new RenderClipOval(); |
-} |
+ void insert(RenderObjectWrapper child, dynamic slot) { |
+ final root = this.root; // TODO(ianh): Remove this once the analyzer is cleverer |
+ assert(slot == null || slot is RenderObject); |
+ assert(root is ContainerRenderObjectMixin); |
+ root.add(child.root, before: slot); |
+ assert(root == this.root); // TODO(ianh): Remove this once the analyzer is cleverer |
+ } |
-class Padding extends OneChildRenderObjectWrapper { |
+ void removeChild(UINode node) { |
+ final root = this.root; // TODO(ianh): Remove this once the analyzer is cleverer |
+ assert(root is ContainerRenderObjectMixin); |
+ assert(node.root.parent == root); |
+ root.remove(node.root); |
+ super.removeChild(node); |
+ assert(root == this.root); // TODO(ianh): Remove this once the analyzer is cleverer |
+ } |
- Padding({ this.padding, UINode child, Object key }) |
- : super(child: child, key: key); |
+ void remove() { |
+ assert(children != null); |
+ for (var child in children) { |
+ assert(child != null); |
+ removeChild(child); |
+ } |
+ super.remove(); |
+ } |
- RenderPadding get root { RenderPadding result = super.root; return result; } |
- final EdgeDims padding; |
+ bool _debugHasDuplicateIds() { |
+ var idSet = new HashSet<String>(); |
+ for (var child in children) { |
+ assert(child != null); |
+ if (child.interchangeable) |
+ continue; // when these nodes are reordered, we just reassign the data |
- RenderPadding createNode() => new RenderPadding(padding: padding); |
+ if (!idSet.add(child._key)) { |
+ throw '''If multiple non-interchangeable nodes of the same type exist as children |
+ of another node, they must have unique keys. |
+ Duplicate: "${child._key}"'''; |
+ } |
+ } |
+ return false; |
+ } |
- void syncRenderObject(Padding old) { |
+ void syncRenderObject(MultiChildRenderObjectWrapper old) { |
super.syncRenderObject(old); |
- root.padding = padding; |
- } |
-} |
+ final root = this.root; // TODO(ianh): Remove this once the analyzer is cleverer |
+ if (root is! ContainerRenderObjectMixin) |
+ return; |
-class DecoratedBox extends OneChildRenderObjectWrapper { |
+ var startIndex = 0; |
+ var endIndex = children.length; |
- DecoratedBox({ this.decoration, UINode child, Object key }) |
- : super(child: child, key: key); |
- |
- RenderDecoratedBox get root { RenderDecoratedBox result = super.root; return result; } |
- final BoxDecoration decoration; |
- |
- RenderDecoratedBox createNode() => new RenderDecoratedBox(decoration: decoration); |
- |
- void syncRenderObject(DecoratedBox old) { |
- super.syncRenderObject(old); |
- root.decoration = decoration; |
- } |
- |
-} |
- |
-class SizedBox extends OneChildRenderObjectWrapper { |
- |
- SizedBox({ |
- double width: double.INFINITY, |
- double height: double.INFINITY, |
- UINode child, |
- Object key |
- }) : desiredSize = new Size(width, height), super(child: child, key: key); |
- |
- RenderSizedBox get root { RenderSizedBox result = super.root; return result; } |
- final Size desiredSize; |
- |
- RenderSizedBox createNode() => new RenderSizedBox(desiredSize: desiredSize); |
- |
- void syncRenderObject(SizedBox old) { |
- super.syncRenderObject(old); |
- root.desiredSize = desiredSize; |
- } |
- |
-} |
- |
-class ConstrainedBox extends OneChildRenderObjectWrapper { |
- |
- ConstrainedBox({ this.constraints, UINode child, Object key }) |
- : super(child: child, key: key); |
- |
- RenderConstrainedBox get root { RenderConstrainedBox result = super.root; return result; } |
- final BoxConstraints constraints; |
- |
- RenderConstrainedBox createNode() => new RenderConstrainedBox(additionalConstraints: constraints); |
- |
- void syncRenderObject(ConstrainedBox old) { |
- super.syncRenderObject(old); |
- root.additionalConstraints = constraints; |
- } |
- |
-} |
- |
-class ShrinkWrapWidth extends OneChildRenderObjectWrapper { |
- |
- ShrinkWrapWidth({ UINode child, Object key }) : super(child: child, key: key); |
- |
- RenderShrinkWrapWidth get root { RenderShrinkWrapWidth result = super.root; return result; } |
- |
- RenderShrinkWrapWidth createNode() => new RenderShrinkWrapWidth(); |
- |
-} |
- |
-class Transform extends OneChildRenderObjectWrapper { |
- |
- Transform({ this.transform, UINode child, Object key }) |
- : super(child: child, key: key); |
- |
- RenderTransform get root { RenderTransform result = super.root; return result; } |
- final Matrix4 transform; |
- |
- RenderTransform createNode() => new RenderTransform(transform: transform); |
- |
- void syncRenderObject(Transform old) { |
- super.syncRenderObject(old); |
- root.transform = transform; |
- } |
- |
-} |
- |
-class SizeObserver extends OneChildRenderObjectWrapper { |
- |
- SizeObserver({ this.callback, UINode child, Object key }) |
- : super(child: child, key: key); |
- |
- RenderSizeObserver get root { RenderSizeObserver result = super.root; return result; } |
- final SizeChangedCallback callback; |
- |
- RenderSizeObserver createNode() => new RenderSizeObserver(callback: callback); |
- |
- void syncRenderObject(SizeObserver old) { |
- super.syncRenderObject(old); |
- root.callback = callback; |
- } |
- |
- void remove() { |
- root.callback = null; |
- super.remove(); |
- } |
- |
-} |
- |
-// TODO(jackson) need a mechanism for marking the RenderCustomPaint as needing paint |
-class CustomPaint extends OneChildRenderObjectWrapper { |
- |
- CustomPaint({ this.callback, UINode child, Object key }) |
- : super(child: child, key: key); |
- |
- RenderCustomPaint get root { RenderCustomPaint result = super.root; return result; } |
- final CustomPaintCallback callback; |
- |
- RenderCustomPaint createNode() => new RenderCustomPaint(callback: callback); |
- |
- void syncRenderObject(CustomPaint old) { |
- super.syncRenderObject(old); |
- root.callback = callback; |
- } |
- |
- void remove() { |
- root.callback = null; |
- super.remove(); |
- } |
- |
-} |
- |
-abstract class MultiChildRenderObjectWrapper extends RenderObjectWrapper { |
- |
- // In MultiChildRenderObjectWrapper subclasses, slots are RenderObject nodes |
- // to use as the "insert before" sibling in ContainerRenderObjectMixin.add() calls |
- |
- MultiChildRenderObjectWrapper({ |
- Object key, |
- List<UINode> children |
- }) : this.children = children == null ? const [] : children, |
- super(key: key) { |
- assert(!_debugHasDuplicateIds()); |
- } |
- |
- final List<UINode> children; |
- |
- void insert(RenderObjectWrapper child, dynamic slot) { |
- final root = this.root; // TODO(ianh): Remove this once the analyzer is cleverer |
- assert(slot == null || slot is RenderObject); |
- assert(root is ContainerRenderObjectMixin); |
- root.add(child.root, before: slot); |
- assert(root == this.root); // TODO(ianh): Remove this once the analyzer is cleverer |
- } |
- |
- void removeChild(UINode node) { |
- final root = this.root; // TODO(ianh): Remove this once the analyzer is cleverer |
- assert(root is ContainerRenderObjectMixin); |
- assert(node.root.parent == root); |
- root.remove(node.root); |
- super.removeChild(node); |
- assert(root == this.root); // TODO(ianh): Remove this once the analyzer is cleverer |
- } |
- |
- void remove() { |
- assert(children != null); |
- for (var child in children) { |
- assert(child != null); |
- removeChild(child); |
- } |
- super.remove(); |
- } |
- |
- bool _debugHasDuplicateIds() { |
- var idSet = new HashSet<String>(); |
- for (var child in children) { |
- assert(child != null); |
- if (child.interchangeable) |
- continue; // when these nodes are reordered, we just reassign the data |
- |
- if (!idSet.add(child._key)) { |
- throw '''If multiple non-interchangeable nodes of the same type exist as children |
- of another node, they must have unique keys. |
- Duplicate: "${child._key}"'''; |
- } |
- } |
- return false; |
- } |
- |
- void syncRenderObject(MultiChildRenderObjectWrapper old) { |
- super.syncRenderObject(old); |
- |
- final root = this.root; // TODO(ianh): Remove this once the analyzer is cleverer |
- if (root is! ContainerRenderObjectMixin) |
- return; |
- |
- var startIndex = 0; |
- var endIndex = children.length; |
- |
- var oldChildren = old == null ? [] : old.children; |
- var oldStartIndex = 0; |
- var oldEndIndex = oldChildren.length; |
+ var oldChildren = old == null ? [] : old.children; |
+ var oldStartIndex = 0; |
+ var oldEndIndex = oldChildren.length; |
RenderObject nextSibling = null; |
UINode currentNode = null; |
@@ -740,357 +744,6 @@ abstract class MultiChildRenderObjectWrapper extends RenderObjectWrapper { |
} |
-class Block extends MultiChildRenderObjectWrapper { |
- |
- Block(List<UINode> children, { Object key }) |
- : super(key: key, children: children); |
- |
- RenderBlock get root { RenderBlock result = super.root; return result; } |
- RenderBlock createNode() => new RenderBlock(); |
- |
-} |
- |
-class Stack extends MultiChildRenderObjectWrapper { |
- |
- Stack(List<UINode> children, { Object key }) |
- : super(key: key, children: children); |
- |
- RenderStack get root { RenderStack result = super.root; return result; } |
- RenderStack createNode() => new RenderStack(); |
- |
-} |
- |
-class StackPositionedChild extends ParentDataNode { |
- StackPositionedChild(UINode content, { |
- double top, double right, double bottom, double left |
- }) : super(content, new StackParentData()..top = top |
- ..right = right |
- ..bottom = bottom |
- ..left = left); |
-} |
- |
-class Paragraph extends RenderObjectWrapper { |
- |
- Paragraph({ Object key, this.text }) : super(key: key); |
- |
- RenderParagraph get root { RenderParagraph result = super.root; return result; } |
- RenderParagraph createNode() => new RenderParagraph(text: text); |
- |
- final String text; |
- |
- void syncRenderObject(UINode old) { |
- super.syncRenderObject(old); |
- root.text = text; |
- } |
- |
- void insert(RenderObjectWrapper child, dynamic slot) { |
- assert(false); |
- // Paragraph does not support having children currently |
- } |
- |
-} |
- |
-class Text extends Component { |
- Text(this.data) : super(key: '*text*'); |
- final String data; |
- bool get interchangeable => true; |
- UINode build() => new Paragraph(text: data); |
-} |
- |
-class Flex extends MultiChildRenderObjectWrapper { |
- |
- Flex(List<UINode> children, { |
- Object key, |
- this.direction: FlexDirection.horizontal, |
- this.justifyContent: FlexJustifyContent.flexStart, |
- this.alignItems: FlexAlignItems.center |
- }) : super(key: key, children: children); |
- |
- RenderFlex get root { RenderFlex result = super.root; return result; } |
- RenderFlex createNode() => new RenderFlex(direction: this.direction); |
- |
- final FlexDirection direction; |
- final FlexJustifyContent justifyContent; |
- final FlexAlignItems alignItems; |
- |
- void syncRenderObject(UINode old) { |
- super.syncRenderObject(old); |
- root.direction = direction; |
- root.justifyContent = justifyContent; |
- root.alignItems = alignItems; |
- } |
- |
-} |
- |
-class FlexExpandingChild extends ParentDataNode { |
- FlexExpandingChild(UINode content, { int flex: 1, Object key }) |
- : super(content, new FlexBoxParentData()..flex = flex, key: key); |
-} |
- |
-class Image extends RenderObjectWrapper { |
- |
- Image({ |
- Object key, |
- this.src, |
- this.size |
- }) : super(key: key); |
- |
- RenderImage get root { RenderImage result = super.root; return result; } |
- RenderImage createNode() => new RenderImage(this.src, this.size); |
- |
- final String src; |
- final Size size; |
- |
- void syncRenderObject(UINode old) { |
- super.syncRenderObject(old); |
- root.src = src; |
- root.requestedSize = size; |
- } |
- |
- void insert(RenderObjectWrapper child, dynamic slot) { |
- assert(false); |
- // Image does not support having children currently |
- } |
- |
-} |
- |
-Set<Component> _dirtyComponents = new Set<Component>(); |
-bool _buildScheduled = false; |
-bool _inRenderDirtyComponents = false; |
- |
-void _buildDirtyComponents() { |
- //_tracing.begin('fn::_buildDirtyComponents'); |
- |
- Stopwatch sw; |
- if (_shouldLogRenderDuration) |
- sw = new Stopwatch()..start(); |
- |
- try { |
- _inRenderDirtyComponents = true; |
- |
- List<Component> sortedDirtyComponents = _dirtyComponents.toList(); |
- sortedDirtyComponents.sort((Component a, Component b) => a._order - b._order); |
- for (var comp in sortedDirtyComponents) { |
- comp._buildIfDirty(); |
- } |
- |
- _dirtyComponents.clear(); |
- _buildScheduled = false; |
- } finally { |
- _inRenderDirtyComponents = false; |
- } |
- |
- UINode._notifyMountStatusChanged(); |
- |
- if (_shouldLogRenderDuration) { |
- sw.stop(); |
- print('Render took ${sw.elapsedMicroseconds} microseconds'); |
- } |
- |
- //_tracing.end('fn::_buildDirtyComponents'); |
-} |
- |
-void _scheduleComponentForRender(Component c) { |
- assert(!_inRenderDirtyComponents); |
- _dirtyComponents.add(c); |
- |
- if (!_buildScheduled) { |
- _buildScheduled = true; |
- new Future.microtask(_buildDirtyComponents); |
- } |
-} |
- |
-abstract class Component extends UINode { |
- |
- Component({ Object key, bool stateful }) |
- : _stateful = stateful != null ? stateful : false, |
- _order = _currentOrder + 1, |
- super(key: key); |
- |
- Component.fromArgs(Object key, bool stateful) |
- : this(key: key, stateful: stateful); |
- |
- static Component _currentlyBuilding; |
- bool get _isBuilding => _currentlyBuilding == this; |
- |
- bool _stateful; |
- bool _dirty = true; |
- bool _disqualifiedFromEverAppearingAgain = false; |
- |
- UINode _built; |
- dynamic _slot; // cached slot from the last time we were synced |
- |
- void didMount() { |
- assert(!_disqualifiedFromEverAppearingAgain); |
- super.didMount(); |
- } |
- |
- void remove() { |
- assert(_built != null); |
- assert(root != null); |
- removeChild(_built); |
- _built = null; |
- super.remove(); |
- } |
- |
- bool _retainStatefulNodeIfPossible(UINode old) { |
- assert(!_disqualifiedFromEverAppearingAgain); |
- |
- Component oldComponent = old as Component; |
- if (oldComponent == null || !oldComponent._stateful) |
- return false; |
- |
- assert(key == oldComponent.key); |
- |
- // Make |this|, the newly-created object, into the "old" Component, and kill it |
- _stateful = false; |
- _built = oldComponent._built; |
- assert(_built != null); |
- _disqualifiedFromEverAppearingAgain = true; |
- |
- // Make |oldComponent| the "new" component |
- oldComponent._built = null; |
- oldComponent._dirty = true; |
- oldComponent.syncFields(this); |
- return true; |
- } |
- |
- // This is called by _retainStatefulNodeIfPossible(), during |
- // syncChild(), just before _sync() is called. |
- // This must be implemented on any subclass that can become stateful |
- // (but don't call super.syncFields() if you inherit directly from |
- // Component, since that'll fire an assert). |
- // If you don't ever become stateful, then don't override this. |
- void syncFields(Component source) { |
- assert(false); |
- } |
- |
- final int _order; |
- static int _currentOrder = 0; |
- |
- /* There are three cases here: |
- * 1) Building for the first time: |
- * assert(_built == null && old == null) |
- * 2) Re-building (because a dirty flag got set): |
- * assert(_built != null && old == null) |
- * 3) Syncing against an old version |
- * assert(_built == null && old != null) |
- */ |
- void _sync(UINode old, dynamic slot) { |
- assert(_built == null || old == null); |
- assert(!_disqualifiedFromEverAppearingAgain); |
- |
- Component oldComponent = old as Component; |
- |
- _slot = slot; |
- |
- var oldBuilt; |
- if (oldComponent == null) { |
- oldBuilt = _built; |
- } else { |
- assert(_built == null); |
- oldBuilt = oldComponent._built; |
- } |
- |
- int lastOrder = _currentOrder; |
- _currentOrder = _order; |
- _currentlyBuilding = this; |
- _built = build(); |
- assert(_built != null); |
- _currentlyBuilding = null; |
- _currentOrder = lastOrder; |
- |
- _built = syncChild(_built, oldBuilt, slot); |
- assert(_built != null); |
- _dirty = false; |
- _root = _built.root; |
- assert(_root == root); // in case a subclass reintroduces it |
- assert(root != null); |
- } |
- |
- void _buildIfDirty() { |
- assert(!_disqualifiedFromEverAppearingAgain); |
- if (!_dirty || !_mounted) |
- return; |
- |
- assert(root != null); |
- _sync(null, _slot); |
- } |
- |
- void scheduleBuild() { |
- setState(() {}); |
- } |
- |
- void setState(Function fn()) { |
- assert(!_disqualifiedFromEverAppearingAgain); |
- _stateful = true; |
- fn(); |
- if (_isBuilding || _dirty || !_mounted) |
- return; |
- |
- _dirty = true; |
- _scheduleComponentForRender(this); |
- } |
- |
- UINode build(); |
- |
-} |
- |
-class Container extends Component { |
- |
- Container({ |
- Object key, |
- this.child, |
- this.constraints, |
- this.decoration, |
- this.width, |
- this.height, |
- this.margin, |
- this.padding, |
- this.transform |
- }) : super(key: key); |
- |
- final UINode child; |
- final BoxConstraints constraints; |
- final BoxDecoration decoration; |
- final EdgeDims margin; |
- final EdgeDims padding; |
- final Matrix4 transform; |
- final double width; |
- final double height; |
- |
- UINode build() { |
- UINode current = child; |
- |
- if (child == null && width == null && height == null) |
- current = new SizedBox(); |
- |
- if (padding != null) |
- current = new Padding(padding: padding, child: current); |
- |
- if (decoration != null) |
- current = new DecoratedBox(decoration: decoration, child: current); |
- |
- if (width != null || height != null) |
- current = new SizedBox( |
- width: width == null ? double.INFINITY : width, |
- height: height == null ? double.INFINITY : height, |
- child: current |
- ); |
- |
- if (constraints != null) |
- current = new ConstrainedBox(constraints: constraints, child: current); |
- |
- if (margin != null) |
- current = new Padding(padding: margin, child: current); |
- |
- if (transform != null) |
- current = new Transform(transform: transform, child: current); |
- |
- return current; |
- } |
- |
-} |
class UINodeAppView extends AppView { |
@@ -1164,9 +817,9 @@ abstract class App extends AbstractUINodeRoot { |
typedef UINode Builder(); |
-class RenderNodeToUINodeAdapter extends AbstractUINodeRoot { |
+class RenderObjectToUINodeAdapter extends AbstractUINodeRoot { |
- RenderNodeToUINodeAdapter( |
+ RenderObjectToUINodeAdapter( |
RenderObjectWithChildMixin<RenderBox> container, |
this.builder |
) : _container = container { |