| Index: sky/sdk/lib/framework/fn2.dart
|
| diff --git a/sky/sdk/lib/framework/fn2.dart b/sky/sdk/lib/framework/fn2.dart
|
| deleted file mode 100644
|
| index 227d04cb3db9fad33f1765917c845acee8358cb9..0000000000000000000000000000000000000000
|
| --- a/sky/sdk/lib/framework/fn2.dart
|
| +++ /dev/null
|
| @@ -1,1207 +0,0 @@
|
| -// Copyright 2015 The Chromium Authors. All rights reserved.
|
| -// 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;
|
| -
|
| -// final sky.Tracing _tracing = sky.window.tracing;
|
| -
|
| -final bool _shouldLogRenderDuration = false;
|
| -
|
| -/*
|
| - * All Effen nodes derive from UINode. All nodes have a _parent, a _key and
|
| - * can be sync'd.
|
| - */
|
| -abstract class UINode {
|
| -
|
| - UINode({ Object key }) {
|
| - _key = key == null ? "$runtimeType" : "$runtimeType-$key";
|
| - assert(this is App || _inRenderDirtyComponents); // you should not build the UI tree ahead of time, build it only during build()
|
| - }
|
| -
|
| - String _key;
|
| - String get key => _key;
|
| -
|
| - UINode _parent;
|
| - UINode get parent => _parent;
|
| -
|
| - bool _mounted = false;
|
| - bool _wasMounted = false;
|
| - bool get mounted => _mounted;
|
| - static bool _notifyingMountStatus = false;
|
| - static Set<UINode> _mountedChanged = new HashSet<UINode>();
|
| -
|
| - void setParent(UINode newParent) {
|
| - assert(!_notifyingMountStatus);
|
| - _parent = newParent;
|
| - if (newParent == null) {
|
| - if (_mounted) {
|
| - _mounted = false;
|
| - _mountedChanged.add(this);
|
| - }
|
| - } else {
|
| - assert(newParent._mounted);
|
| - if (_parent._mounted != _mounted) {
|
| - _mounted = _parent._mounted;
|
| - _mountedChanged.add(this);
|
| - }
|
| - }
|
| - }
|
| -
|
| - static void _notifyMountStatusChanged() {
|
| - try {
|
| - _notifyingMountStatus = true;
|
| - for (UINode node in _mountedChanged) {
|
| - if (node._wasMounted != node._mounted) {
|
| - if (node._mounted)
|
| - node.didMount();
|
| - else
|
| - node.didUnmount();
|
| - node._wasMounted = node._mounted;
|
| - }
|
| - }
|
| - _mountedChanged.clear();
|
| - } finally {
|
| - _notifyingMountStatus = false;
|
| - }
|
| - }
|
| - void didMount() { }
|
| - void didUnmount() { }
|
| -
|
| - RenderObject _root;
|
| - RenderObject get root => _root;
|
| -
|
| - // Subclasses which implements Nodes that become stateful may return true
|
| - // if the |old| node has become stateful and should be retained.
|
| - // This is called immediately before _sync().
|
| - // Component._retainStatefulNodeIfPossible() calls syncFields().
|
| - bool _retainStatefulNodeIfPossible(UINode old) => false;
|
| -
|
| - bool get interchangeable => false; // if true, then keys can be duplicated
|
| -
|
| - void _sync(UINode old, dynamic slot);
|
| - // 'slot' is the identifier that the parent RenderObjectWrapper uses to know
|
| - // where to put this descendant
|
| -
|
| - void remove() {
|
| - _root = null;
|
| - setParent(null);
|
| - }
|
| -
|
| - UINode findAncestor(Type targetType) {
|
| - var ancestor = _parent;
|
| - while (ancestor != null && !reflectClass(ancestor.runtimeType).isSubtypeOf(reflectClass(targetType)))
|
| - ancestor = ancestor._parent;
|
| - return ancestor;
|
| - }
|
| -
|
| - void removeChild(UINode node) {
|
| - node.remove();
|
| - }
|
| -
|
| - // Returns the child which should be retained as the child of this node.
|
| - UINode syncChild(UINode node, UINode oldNode, dynamic slot) {
|
| -
|
| - assert(oldNode is! Component || !oldNode._disqualifiedFromEverAppearingAgain);
|
| -
|
| - if (node == oldNode) {
|
| - assert(node == null || node.mounted);
|
| - return node; // Nothing to do. Subtrees must be identical.
|
| - }
|
| -
|
| - if (node == null) {
|
| - // the child in this slot has gone away
|
| - assert(oldNode.mounted);
|
| - removeChild(oldNode);
|
| - assert(!oldNode.mounted);
|
| - return null;
|
| - }
|
| -
|
| - if (oldNode != null && node._key == oldNode._key && node._retainStatefulNodeIfPossible(oldNode)) {
|
| - assert(oldNode.mounted);
|
| - assert(!node.mounted);
|
| - oldNode._sync(node, slot);
|
| - assert(oldNode.root is RenderObject);
|
| - return oldNode;
|
| - }
|
| -
|
| - if (oldNode != null && node._key != oldNode._key) {
|
| - assert(oldNode.mounted);
|
| - removeChild(oldNode);
|
| - oldNode = null;
|
| - }
|
| -
|
| - assert(!node.mounted);
|
| - node.setParent(this);
|
| - node._sync(oldNode, slot);
|
| - assert(node.root is RenderObject);
|
| - return node;
|
| - }
|
| -}
|
| -
|
| -// Descendants of TagNode provide a way to tag RenderObjectWrapper and
|
| -// Component nodes with annotations, such as event listeners,
|
| -// stylistic information, etc.
|
| -abstract class TagNode extends UINode {
|
| -
|
| - TagNode(UINode content, { Object key }) : this.content = content, super(key: key);
|
| -
|
| - UINode content;
|
| -
|
| - void _sync(UINode old, dynamic slot) {
|
| - UINode oldContent = old == null ? null : (old as TagNode).content;
|
| - content = syncChild(content, oldContent, slot);
|
| - assert(content.root != null);
|
| - _root = content.root;
|
| - assert(_root == root); // in case a subclass reintroduces it
|
| - }
|
| -
|
| - void remove() {
|
| - if (content != null)
|
| - removeChild(content);
|
| - super.remove();
|
| - }
|
| -
|
| -}
|
| -
|
| -class ParentDataNode extends TagNode {
|
| - ParentDataNode(UINode content, this.parentData, { Object key }): super(content, key: key);
|
| - final ParentData parentData;
|
| -}
|
| -
|
| -typedef void GestureEventListener(sky.GestureEvent e);
|
| -typedef void PointerEventListener(sky.PointerEvent e);
|
| -typedef void EventListener(sky.Event e);
|
| -
|
| -class EventListenerNode extends TagNode {
|
| -
|
| - EventListenerNode(UINode content, {
|
| - EventListener onWheel,
|
| - GestureEventListener onGestureFlingCancel,
|
| - GestureEventListener onGestureFlingStart,
|
| - GestureEventListener onGestureScrollStart,
|
| - GestureEventListener onGestureScrollUpdate,
|
| - GestureEventListener onGestureTap,
|
| - GestureEventListener onGestureTapDown,
|
| - PointerEventListener onPointerCancel,
|
| - PointerEventListener onPointerDown,
|
| - PointerEventListener onPointerMove,
|
| - PointerEventListener onPointerUp,
|
| - Map<String, sky.EventListener> custom
|
| - }) : listeners = _createListeners(
|
| - onWheel: onWheel,
|
| - onGestureFlingCancel: onGestureFlingCancel,
|
| - onGestureFlingStart: onGestureFlingStart,
|
| - onGestureScrollUpdate: onGestureScrollUpdate,
|
| - onGestureScrollStart: onGestureScrollStart,
|
| - onGestureTap: onGestureTap,
|
| - onGestureTapDown: onGestureTapDown,
|
| - onPointerCancel: onPointerCancel,
|
| - onPointerDown: onPointerDown,
|
| - onPointerMove: onPointerMove,
|
| - onPointerUp: onPointerUp,
|
| - custom: custom
|
| - ),
|
| - super(content);
|
| -
|
| - final Map<String, sky.EventListener> listeners;
|
| -
|
| - static Map<String, sky.EventListener> _createListeners({
|
| - EventListener onWheel,
|
| - GestureEventListener onGestureFlingCancel,
|
| - GestureEventListener onGestureFlingStart,
|
| - GestureEventListener onGestureScrollStart,
|
| - GestureEventListener onGestureScrollUpdate,
|
| - GestureEventListener onGestureTap,
|
| - GestureEventListener onGestureTapDown,
|
| - PointerEventListener onPointerCancel,
|
| - PointerEventListener onPointerDown,
|
| - PointerEventListener onPointerMove,
|
| - PointerEventListener onPointerUp,
|
| - Map<String, sky.EventListener> custom
|
| - }) {
|
| - var listeners = custom != null ?
|
| - new HashMap<String, sky.EventListener>.from(custom) :
|
| - new HashMap<String, sky.EventListener>();
|
| -
|
| - if (onWheel != null)
|
| - listeners['wheel'] = onWheel;
|
| - if (onGestureFlingCancel != null)
|
| - listeners['gestureflingcancel'] = onGestureFlingCancel;
|
| - if (onGestureFlingStart != null)
|
| - listeners['gestureflingstart'] = onGestureFlingStart;
|
| - if (onGestureScrollStart != null)
|
| - listeners['gesturescrollstart'] = onGestureScrollStart;
|
| - if (onGestureScrollUpdate != null)
|
| - listeners['gesturescrollupdate'] = onGestureScrollUpdate;
|
| - if (onGestureTap != null)
|
| - listeners['gesturetap'] = onGestureTap;
|
| - if (onGestureTapDown != null)
|
| - listeners['gesturetapdown'] = onGestureTapDown;
|
| - if (onPointerCancel != null)
|
| - listeners['pointercancel'] = onPointerCancel;
|
| - if (onPointerDown != null)
|
| - listeners['pointerdown'] = onPointerDown;
|
| - if (onPointerMove != null)
|
| - listeners['pointermove'] = onPointerMove;
|
| - if (onPointerUp != null)
|
| - listeners['pointerup'] = onPointerUp;
|
| -
|
| - return listeners;
|
| - }
|
| -
|
| - void _handleEvent(sky.Event e) {
|
| - sky.EventListener listener = listeners[e.type];
|
| - if (listener != null) {
|
| - listener(e);
|
| - }
|
| - }
|
| -
|
| -}
|
| -
|
| -/*
|
| - * RenderObjectWrappers correspond to a desired state of a RenderObject.
|
| - * They are fully immutable, with one exception: A UINode which is a
|
| - * Component which lives within an MultiChildRenderObjectWrapper's
|
| - * children list, may be replaced with the "old" instance if it has
|
| - * become stateful.
|
| - */
|
| -abstract class RenderObjectWrapper extends UINode {
|
| -
|
| - RenderObjectWrapper({
|
| - Object key
|
| - }) : super(key: key);
|
| -
|
| - RenderObject createNode();
|
| -
|
| - void insert(RenderObjectWrapper child, dynamic slot);
|
| -
|
| - static final Map<RenderObject, RenderObjectWrapper> _nodeMap =
|
| - new HashMap<RenderObject, RenderObjectWrapper>();
|
| -
|
| - static RenderObjectWrapper _getMounted(RenderObject node) => _nodeMap[node];
|
| -
|
| - void _sync(UINode old, dynamic slot) {
|
| - assert(parent != null);
|
| - if (old == null) {
|
| - _root = createNode();
|
| - var ancestor = findAncestor(RenderObjectWrapper);
|
| - if (ancestor is RenderObjectWrapper)
|
| - ancestor.insert(this, slot);
|
| - } else {
|
| - _root = old.root;
|
| - }
|
| - assert(_root == root); // in case a subclass reintroduces it
|
| - assert(root != null);
|
| - assert(mounted);
|
| - _nodeMap[root] = this;
|
| - syncRenderObject(old);
|
| - }
|
| -
|
| - void syncRenderObject(RenderObjectWrapper old) {
|
| - ParentData parentData = null;
|
| - UINode ancestor = parent;
|
| - while (ancestor != null && ancestor is! RenderObjectWrapper) {
|
| - if (ancestor is ParentDataNode && ancestor.parentData != null) {
|
| - if (parentData != null)
|
| - parentData.merge(ancestor.parentData); // this will throw if the types aren't the same
|
| - else
|
| - parentData = ancestor.parentData;
|
| - }
|
| - ancestor = ancestor.parent;
|
| - }
|
| - if (parentData != null) {
|
| - assert(root.parentData != null);
|
| - root.parentData.merge(parentData); // this will throw if the types aren't appropriate
|
| - if (parent.root != null)
|
| - parent.root.markNeedsLayout();
|
| - }
|
| - }
|
| -
|
| - void remove() {
|
| - assert(root != null);
|
| - _nodeMap.remove(root);
|
| - super.remove();
|
| - }
|
| -}
|
| -
|
| -abstract class OneChildRenderObjectWrapper extends RenderObjectWrapper {
|
| -
|
| - OneChildRenderObjectWrapper({ UINode child, Object key }) : _child = child, super(key: key);
|
| -
|
| - UINode _child;
|
| - UINode get child => _child;
|
| -
|
| - void syncRenderObject(RenderObjectWrapper old) {
|
| - super.syncRenderObject(old);
|
| - UINode oldChild = old == null ? null : (old as OneChildRenderObjectWrapper).child;
|
| - _child = syncChild(child, oldChild, null);
|
| - }
|
| -
|
| - void insert(RenderObjectWrapper child, dynamic slot) {
|
| - final root = this.root; // TODO(ianh): Remove this once the analyzer is cleverer
|
| - assert(slot == null);
|
| - assert(root is RenderObjectWithChildMixin);
|
| - root.child = child.root;
|
| - 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 RenderObjectWithChildMixin);
|
| - root.child = null;
|
| - super.removeChild(node);
|
| - assert(root == this.root); // TODO(ianh): Remove this once the analyzer is cleverer
|
| - }
|
| -
|
| - void remove() {
|
| - if (child != null)
|
| - removeChild(child);
|
| - super.remove();
|
| - }
|
| -
|
| -}
|
| -
|
| -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;
|
| -
|
| - RenderOpacity createNode() => new RenderOpacity(opacity: opacity);
|
| -
|
| - void syncRenderObject(Opacity old) {
|
| - super.syncRenderObject(old);
|
| - root.opacity = opacity;
|
| - }
|
| -}
|
| -
|
| -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);
|
| -
|
| - RenderClipOval get root { RenderClipOval result = super.root; return result; }
|
| - RenderClipOval createNode() => new RenderClipOval();
|
| -}
|
| -
|
| -class Padding extends OneChildRenderObjectWrapper {
|
| -
|
| - Padding({ this.padding, UINode child, Object key })
|
| - : super(child: child, key: key);
|
| -
|
| - RenderPadding get root { RenderPadding result = super.root; return result; }
|
| - final EdgeDims padding;
|
| -
|
| - RenderPadding createNode() => new RenderPadding(padding: padding);
|
| -
|
| - void syncRenderObject(Padding old) {
|
| - super.syncRenderObject(old);
|
| - root.padding = padding;
|
| - }
|
| -
|
| -}
|
| -
|
| -class DecoratedBox extends OneChildRenderObjectWrapper {
|
| -
|
| - 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;
|
| -
|
| - RenderObject nextSibling = null;
|
| - UINode currentNode = null;
|
| - UINode oldNode = null;
|
| -
|
| - void sync(int atIndex) {
|
| - children[atIndex] = syncChild(currentNode, oldNode, nextSibling);
|
| - assert(children[atIndex] != null);
|
| - }
|
| -
|
| - // Scan backwards from end of list while nodes can be directly synced
|
| - // without reordering.
|
| - while (endIndex > startIndex && oldEndIndex > oldStartIndex) {
|
| - currentNode = children[endIndex - 1];
|
| - oldNode = oldChildren[oldEndIndex - 1];
|
| -
|
| - if (currentNode._key != oldNode._key) {
|
| - break;
|
| - }
|
| -
|
| - endIndex--;
|
| - oldEndIndex--;
|
| - sync(endIndex);
|
| - }
|
| -
|
| - HashMap<String, UINode> oldNodeIdMap = null;
|
| -
|
| - bool oldNodeReordered(String key) {
|
| - return oldNodeIdMap != null &&
|
| - oldNodeIdMap.containsKey(key) &&
|
| - oldNodeIdMap[key] == null;
|
| - }
|
| -
|
| - void advanceOldStartIndex() {
|
| - oldStartIndex++;
|
| - while (oldStartIndex < oldEndIndex &&
|
| - oldNodeReordered(oldChildren[oldStartIndex]._key)) {
|
| - oldStartIndex++;
|
| - }
|
| - }
|
| -
|
| - void ensureOldIdMap() {
|
| - if (oldNodeIdMap != null)
|
| - return;
|
| -
|
| - oldNodeIdMap = new HashMap<String, UINode>();
|
| - for (int i = oldStartIndex; i < oldEndIndex; i++) {
|
| - var node = oldChildren[i];
|
| - if (!node.interchangeable)
|
| - oldNodeIdMap.putIfAbsent(node._key, () => node);
|
| - }
|
| - }
|
| -
|
| - bool searchForOldNode() {
|
| - if (currentNode.interchangeable)
|
| - return false; // never re-order these nodes
|
| -
|
| - ensureOldIdMap();
|
| - oldNode = oldNodeIdMap[currentNode._key];
|
| - if (oldNode == null)
|
| - return false;
|
| -
|
| - oldNodeIdMap[currentNode._key] = null; // mark it reordered
|
| - assert(root is ContainerRenderObjectMixin);
|
| - assert(old.root is ContainerRenderObjectMixin);
|
| - assert(oldNode.root != null);
|
| -
|
| - (old.root as ContainerRenderObjectMixin).remove(oldNode.root); // TODO(ianh): Remove cast once the analyzer is cleverer
|
| - root.add(oldNode.root, before: nextSibling);
|
| -
|
| - return true;
|
| - }
|
| -
|
| - // Scan forwards, this time we may re-order;
|
| - nextSibling = root.firstChild;
|
| - while (startIndex < endIndex && oldStartIndex < oldEndIndex) {
|
| - currentNode = children[startIndex];
|
| - oldNode = oldChildren[oldStartIndex];
|
| -
|
| - if (currentNode._key == oldNode._key) {
|
| - assert(currentNode.runtimeType == oldNode.runtimeType);
|
| - nextSibling = root.childAfter(nextSibling);
|
| - sync(startIndex);
|
| - startIndex++;
|
| - advanceOldStartIndex();
|
| - continue;
|
| - }
|
| -
|
| - oldNode = null;
|
| - searchForOldNode();
|
| - sync(startIndex);
|
| - startIndex++;
|
| - }
|
| -
|
| - // New insertions
|
| - oldNode = null;
|
| - while (startIndex < endIndex) {
|
| - currentNode = children[startIndex];
|
| - sync(startIndex);
|
| - startIndex++;
|
| - }
|
| -
|
| - // Removals
|
| - currentNode = null;
|
| - while (oldStartIndex < oldEndIndex) {
|
| - oldNode = oldChildren[oldStartIndex];
|
| - removeChild(oldNode);
|
| - advanceOldStartIndex();
|
| - }
|
| -
|
| - assert(root == this.root); // TODO(ianh): Remove this once the analyzer is cleverer
|
| - }
|
| -
|
| -}
|
| -
|
| -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 {
|
| -
|
| - UINodeAppView() {
|
| - assert(_appView == null);
|
| - }
|
| -
|
| - static UINodeAppView _appView;
|
| - static void initUINodeAppView() {
|
| - if (_appView == null)
|
| - _appView = new UINodeAppView();
|
| - }
|
| -
|
| - void dispatchEvent(sky.Event event, HitTestResult result) {
|
| - assert(_appView == this);
|
| - super.dispatchEvent(event, result);
|
| - for (HitTestEntry entry in result.path.reversed) {
|
| - UINode target = RenderObjectWrapper._getMounted(entry.target);
|
| - if (target == null)
|
| - continue;
|
| - RenderObject targetRoot = target.root;
|
| - while (target != null && target.root == targetRoot) {
|
| - if (target is EventListenerNode)
|
| - target._handleEvent(event);
|
| - target = target._parent;
|
| - }
|
| - }
|
| - }
|
| -
|
| -}
|
| -
|
| -abstract class AbstractUINodeRoot extends Component {
|
| -
|
| - AbstractUINodeRoot() : super(stateful: true) {
|
| - UINodeAppView.initUINodeAppView();
|
| - _mounted = true;
|
| - _scheduleComponentForRender(this);
|
| - }
|
| -
|
| - void syncFields(AbstractUINodeRoot source) {
|
| - assert(false);
|
| - // if we get here, it implies that we have a parent
|
| - }
|
| -
|
| - void _buildIfDirty() {
|
| - assert(_dirty);
|
| - assert(_mounted);
|
| - assert(parent == null);
|
| - _sync(null, null);
|
| - }
|
| -
|
| -}
|
| -
|
| -abstract class App extends AbstractUINodeRoot {
|
| -
|
| - App();
|
| -
|
| - AppView get appView => UINodeAppView._appView;
|
| -
|
| - void _buildIfDirty() {
|
| - super._buildIfDirty();
|
| -
|
| - if (root.parent == null) {
|
| - // we haven't attached it yet
|
| - UINodeAppView._appView.root = root;
|
| - }
|
| - assert(root.parent is RenderView);
|
| - }
|
| -
|
| -}
|
| -
|
| -typedef UINode Builder();
|
| -
|
| -class RenderNodeToUINodeAdapter extends AbstractUINodeRoot {
|
| -
|
| - RenderNodeToUINodeAdapter(
|
| - RenderObjectWithChildMixin<RenderBox> container,
|
| - this.builder
|
| - ) : _container = container {
|
| - assert(builder != null);
|
| - }
|
| -
|
| - RenderObjectWithChildMixin<RenderBox> _container;
|
| - RenderObjectWithChildMixin<RenderBox> get container => _container;
|
| - void set container(RenderObjectWithChildMixin<RenderBox> value) {
|
| - if (_container != value) {
|
| - assert(value.child == null);
|
| - if (root != null) {
|
| - assert(_container.child == root);
|
| - _container.child = null;
|
| - }
|
| - _container = value;
|
| - if (root != null) {
|
| - _container.child = root;
|
| - assert(_container.child == root);
|
| - }
|
| - }
|
| - }
|
| -
|
| - final Builder builder;
|
| -
|
| - void _buildIfDirty() {
|
| - super._buildIfDirty();
|
| - if (root.parent == null) {
|
| - // we haven't attached it yet
|
| - assert(_container.child == null);
|
| - _container.child = root;
|
| - }
|
| - assert(root.parent == _container);
|
| - }
|
| -
|
| - UINode build() => builder();
|
| -
|
| -}
|
|
|