| 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 {
|
|
|