Index: sky/framework/fn.dart |
diff --git a/sky/framework/fn.dart b/sky/framework/fn.dart |
deleted file mode 100644 |
index 427e4da8fd7f9cc7691af7965176ad4e42867970..0000000000000000000000000000000000000000 |
--- a/sky/framework/fn.dart |
+++ /dev/null |
@@ -1,924 +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 'dart:async'; |
-import 'dart:collection'; |
-import 'dart:sky' as sky; |
-import 'reflect.dart' as reflect; |
-import 'layout.dart'; |
- |
-export 'layout.dart' show Style; |
- |
-final sky.Tracing _tracing = sky.window.tracing; |
- |
-final bool _shouldLogRenderDuration = false; |
-final bool _shouldTrace = false; |
- |
-enum _SyncOperation { IDENTICAL, INSERTION, STATEFUL, STATELESS, REMOVAL } |
- |
-/* |
- * All Effen nodes derive from UINode. All nodes have a _parent, a _key and |
- * can be sync'd. |
- */ |
-abstract class UINode { |
- String _key; |
- UINode _parent; |
- UINode get parent => _parent; |
- RenderCSS _root; |
- bool _defunct = false; |
- |
- UINode({ Object key }) { |
- _key = key == null ? "$runtimeType" : "$runtimeType-$key"; |
- } |
- |
- // Subclasses which implements Nodes that become stateful may return true |
- // if the |old| node has become stateful and should be retained. |
- bool _willSync(UINode old) => false; |
- |
- bool get interchangeable => false; // if true, then keys can be duplicated |
- |
- void _sync(UINode old, RenderCSSContainer host, RenderCSS insertBefore); |
- |
- void _remove() { |
- _defunct = true; |
- _root = null; |
- handleRemoved(); |
- } |
- void handleRemoved() { } |
- |
- int _nodeDepth; |
- void _ensureDepth() { |
- if (_nodeDepth == null) { |
- if (_parent != null) { |
- _parent._ensureDepth(); |
- _nodeDepth = _parent._nodeDepth + 1; |
- } else { |
- _nodeDepth = 0; |
- } |
- } |
- } |
- |
- void _trace(String message) { |
- if (!_shouldTrace) |
- return; |
- |
- _ensureDepth(); |
- print((' ' * _nodeDepth) + message); |
- } |
- |
- void _traceSync(_SyncOperation op, String key) { |
- if (!_shouldTrace) |
- return; |
- |
- String opString = op.toString().toLowerCase(); |
- String outString = opString.substring(opString.indexOf('.') + 1); |
- _trace('_sync($outString) $key'); |
- } |
- |
- void _removeChild(UINode node) { |
- _traceSync(_SyncOperation.REMOVAL, node._key); |
- node._remove(); |
- } |
- |
- // Returns the child which should be retained as the child of this node. |
- UINode _syncChild(UINode node, UINode oldNode, RenderCSSContainer host, |
- RenderCSS insertBefore) { |
- |
- assert(oldNode == null || node._key == oldNode._key); |
- |
- if (node == oldNode) { |
- _traceSync(_SyncOperation.IDENTICAL, node._key); |
- return node; // Nothing to do. Subtrees must be identical. |
- } |
- |
- // TODO(rafaelw): This eagerly removes the old DOM. It may be that a |
- // new component was built that could re-use some of it. Consider |
- // syncing the new VDOM against the old one. |
- if (oldNode != null && node._key != oldNode._key) { |
- _removeChild(oldNode); |
- } |
- |
- if (node._willSync(oldNode)) { |
- _traceSync(_SyncOperation.STATEFUL, node._key); |
- oldNode._sync(node, host, insertBefore); |
- node._defunct = true; |
- assert(oldNode._root is RenderCSS); |
- return oldNode; |
- } |
- |
- node._parent = this; |
- |
- if (oldNode == null) { |
- _traceSync(_SyncOperation.INSERTION, node._key); |
- } else { |
- _traceSync(_SyncOperation.STATELESS, node._key); |
- } |
- node._sync(oldNode, host, insertBefore); |
- if (oldNode != null) |
- oldNode._defunct = true; |
- |
- assert(node._root is RenderCSS); |
- return node; |
- } |
-} |
- |
-abstract class ContentNode extends UINode { |
- UINode content; |
- |
- ContentNode(UINode content) : this.content = content, super(key: content._key); |
- |
- void _sync(UINode old, RenderCSSContainer host, RenderCSS insertBefore) { |
- UINode oldContent = old == null ? null : (old as ContentNode).content; |
- content = _syncChild(content, oldContent, host, insertBefore); |
- assert(content._root != null); |
- _root = content._root; |
- } |
- |
- void _remove() { |
- if (content != null) |
- _removeChild(content); |
- super._remove(); |
- } |
-} |
- |
-class StyleNode extends ContentNode { |
- final Style style; |
- |
- StyleNode(UINode content, this.style): super(content); |
-} |
- |
-class ParentDataNode extends ContentNode { |
- final ParentData parentData; |
- |
- ParentDataNode(UINode content, this.parentData): super(content); |
-} |
- |
-/* |
- * SkyNodeWrappers correspond to a desired state of a RenderCSS. They are fully |
- * immutable, with one exception: A UINode which is a Component which lives within |
- * an SkyElementWrapper's children list, may be replaced with the "old" instance if it |
- * has become stateful. |
- */ |
-abstract class SkyNodeWrapper extends UINode { |
- |
- static final Map<RenderCSS, SkyNodeWrapper> _nodeMap = |
- new HashMap<RenderCSS, SkyNodeWrapper>(); |
- |
- static SkyNodeWrapper _getMounted(RenderCSS node) => _nodeMap[node]; |
- |
- SkyNodeWrapper({ Object key }) : super(key: key); |
- |
- SkyNodeWrapper get _emptyNode; |
- |
- RenderCSS _createNode(); |
- |
- void _sync(UINode old, RenderCSSContainer host, RenderCSS insertBefore) { |
- if (old == null) { |
- _root = _createNode(); |
- assert(_root != null); |
- host.add(_root, before: insertBefore); |
- old = _emptyNode; |
- } else { |
- _root = old._root; |
- assert(_root != null); |
- } |
- |
- _nodeMap[_root] = this; |
- _syncNode(old); |
- } |
- |
- void _syncNode(SkyNodeWrapper old); |
- |
- void _removeChild(UINode node) { |
- assert(_root is RenderCSSContainer); |
- _root.remove(node._root); |
- super._removeChild(node); |
- } |
- |
- void _remove() { |
- assert(_root != null); |
- _nodeMap.remove(_root); |
- super._remove(); |
- } |
-} |
- |
-typedef GestureEventListener(sky.GestureEvent e); |
-typedef PointerEventListener(sky.PointerEvent e); |
-typedef EventListener(sky.Event e); |
- |
-class EventListenerNode extends ContentNode { |
- final Map<String, sky.EventListener> listeners; |
- |
- static final Set<String> _registeredEvents = new HashSet<String>(); |
- |
- 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; |
- } |
- |
- 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); |
- |
- void _handleEvent(sky.Event e) { |
- sky.EventListener listener = listeners[e.type]; |
- if (listener != null) { |
- listener(e); |
- } |
- } |
- |
- static void _dispatchEvent(sky.Event e) { |
- UINode target = SkyNodeWrapper._getMounted(bridgeEventTargetToRenderNode(e.target)); |
- |
- // TODO(rafaelw): StopPropagation? |
- while (target != null) { |
- if (target is EventListenerNode) { |
- target._handleEvent(e); |
- } |
- |
- target = target._parent; |
- } |
- } |
- |
- static void _ensureDocumentListener(String eventType) { |
- if (_registeredEvents.add(eventType)) { |
- sky.document.addEventListener(eventType, _dispatchEvent); |
- } |
- } |
- |
- void _sync(UINode old, RenderCSSContainer host, RenderCSS insertBefore) { |
- for (var type in listeners.keys) { |
- _ensureDocumentListener(type); |
- } |
- |
- super._sync(old, host, insertBefore); |
- } |
-} |
- |
-final List<UINode> _emptyList = new List<UINode>(); |
- |
-abstract class SkyElementWrapper extends SkyNodeWrapper { |
- |
- final List<UINode> children; |
- final Style style; |
- final String inlineStyle; |
- |
- SkyElementWrapper({ |
- Object key, |
- List<UINode> children, |
- this.style, |
- this.inlineStyle |
- }) : this.children = children == null ? _emptyList : children, |
- super(key: key) { |
- |
- assert(!_debugHasDuplicateIds()); |
- } |
- |
- 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 _syncNode(SkyNodeWrapper old) { |
- SkyElementWrapper oldSkyElementWrapper = old as SkyElementWrapper; |
- |
- List<Style> styles = new List<Style>(); |
- if (style != null) |
- styles.add(style); |
- ParentData parentData = null; |
- UINode parent = _parent; |
- while (parent != null && parent is! SkyNodeWrapper) { |
- if (parent is StyleNode && parent.style != null) |
- styles.add(parent.style); |
- else |
- if (parent is ParentDataNode && parent.parentData != null) { |
- if (parentData != null) |
- parentData.merge(parent.parentData); // this will throw if the types aren't the same |
- else |
- parentData = parent.parentData; |
- } |
- parent = parent._parent; |
- } |
- _root.updateStyles(styles); |
- if (parentData != null) { |
- assert(_root.parentData != null); |
- _root.parentData.merge(parentData); // this will throw if the types aren't approriate |
- assert(parent != null); |
- assert(parent._root != null); |
- parent._root.markNeedsLayout(); |
- } |
- _root.updateInlineStyle(inlineStyle); |
- |
- _syncChildren(oldSkyElementWrapper); |
- } |
- |
- void _syncChildren(SkyElementWrapper oldSkyElementWrapper) { |
- if (_root is! RenderCSSContainer) |
- return; |
- |
- var startIndex = 0; |
- var endIndex = children.length; |
- |
- var oldChildren = oldSkyElementWrapper.children; |
- var oldStartIndex = 0; |
- var oldEndIndex = oldChildren.length; |
- |
- RenderCSS nextSibling = null; |
- UINode currentNode = null; |
- UINode oldNode = null; |
- |
- void sync(int atIndex) { |
- children[atIndex] = _syncChild(currentNode, oldNode, _root, 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 RenderCSSContainer); |
- assert(oldNode._root is RenderCSSContainer); |
- oldSkyElementWrapper._root.remove(oldNode._root); |
- _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(); |
- } |
- } |
-} |
- |
-class Container extends SkyElementWrapper { |
- |
- RenderCSSContainer _root; |
- RenderCSSContainer _createNode() => new RenderCSSContainer(this); |
- |
- static final Container _emptyContainer = new Container(); |
- |
- SkyNodeWrapper get _emptyNode => _emptyContainer; |
- |
- Container({ |
- Object key, |
- List<UINode> children, |
- Style style, |
- String inlineStyle |
- }) : super( |
- key: key, |
- children: children, |
- style: style, |
- inlineStyle: inlineStyle |
- ); |
-} |
- |
-class Paragraph extends SkyElementWrapper { |
- |
- RenderCSSParagraph _root; |
- RenderCSSParagraph _createNode() => new RenderCSSParagraph(this); |
- |
- static final Paragraph _emptyContainer = new Paragraph(); |
- |
- SkyNodeWrapper get _emptyNode => _emptyContainer; |
- |
- Paragraph({ |
- Object key, |
- List<UINode> children, |
- Style style, |
- String inlineStyle |
- }) : super( |
- key: key, |
- children: children, |
- style: style, |
- inlineStyle: inlineStyle |
- ); |
-} |
- |
-class FlexContainer extends SkyElementWrapper { |
- |
- RenderCSSFlex _root; |
- RenderCSSFlex _createNode() => new RenderCSSFlex(this, this.direction); |
- |
- static final FlexContainer _emptyContainer = new FlexContainer(); |
- // direction doesn't matter if it's empty |
- |
- SkyNodeWrapper get _emptyNode => _emptyContainer; |
- |
- final FlexDirection direction; |
- |
- FlexContainer({ |
- Object key, |
- List<UINode> children, |
- Style style, |
- String inlineStyle, |
- this.direction: FlexDirection.Row |
- }) : super( |
- key: key, |
- children: children, |
- style: style, |
- inlineStyle: inlineStyle |
- ); |
- |
- void _syncNode(UINode old) { |
- super._syncNode(old); |
- _root.direction = direction; |
- } |
-} |
- |
-class FillStackContainer extends SkyElementWrapper { |
- |
- RenderCSSStack _root; |
- RenderCSSStack _createNode() => new RenderCSSStack(this); |
- |
- static final FillStackContainer _emptyContainer = new FillStackContainer(); |
- |
- SkyNodeWrapper get _emptyNode => _emptyContainer; |
- |
- FillStackContainer({ |
- Object key, |
- List<UINode> children, |
- Style style, |
- String inlineStyle |
- }) : super( |
- key: key, |
- children: _positionNodesToFill(children), |
- style: style, |
- inlineStyle: inlineStyle |
- ); |
- |
- static StackParentData _fillParentData = new StackParentData() |
- ..top = 0.0 |
- ..left = 0.0 |
- ..right = 0.0 |
- ..bottom = 0.0; |
- |
- static List<UINode> _positionNodesToFill(List<UINode> input) { |
- if (input == null) |
- return null; |
- return input.map((node) { |
- return new ParentDataNode(node, _fillParentData); |
- }).toList(); |
- } |
-} |
- |
-class TextFragment extends SkyElementWrapper { |
- |
- RenderCSSInline _root; |
- RenderCSSInline _createNode() => new RenderCSSInline(this, this.data); |
- |
- static final TextFragment _emptyText = new TextFragment(''); |
- |
- SkyNodeWrapper get _emptyNode => _emptyText; |
- |
- final String data; |
- |
- TextFragment(this.data, { |
- Object key, |
- Style style, |
- String inlineStyle |
- }) : super( |
- key: key, |
- style: style, |
- inlineStyle: inlineStyle |
- ); |
- |
- void _syncNode(UINode old) { |
- super._syncNode(old); |
- _root.data = data; |
- } |
-} |
- |
-class Image extends SkyElementWrapper { |
- |
- RenderCSSImage _root; |
- RenderCSSImage _createNode() => new RenderCSSImage(this, this.src, this.width, this.height); |
- |
- static final Image _emptyImage = new Image(); |
- |
- SkyNodeWrapper get _emptyNode => _emptyImage; |
- |
- final String src; |
- final int width; |
- final int height; |
- |
- Image({ |
- Object key, |
- Style style, |
- String inlineStyle, |
- this.width, |
- this.height, |
- this.src |
- }) : super( |
- key: key, |
- style: style, |
- inlineStyle: inlineStyle |
- ); |
- |
- void _syncNode(UINode old) { |
- super._syncNode(old); |
- _root.configure(this.src, this.width, this.height); |
- } |
-} |
- |
- |
-Set<Component> _mountedComponents = new HashSet<Component>(); |
-Set<Component> _unmountedComponents = new HashSet<Component>(); |
- |
-void _enqueueDidMount(Component c) { |
- assert(!_notifingMountStatus); |
- _mountedComponents.add(c); |
-} |
- |
-void _enqueueDidUnmount(Component c) { |
- assert(!_notifingMountStatus); |
- _unmountedComponents.add(c); |
-} |
- |
-bool _notifingMountStatus = false; |
- |
-void _notifyMountStatusChanged() { |
- try { |
- _notifingMountStatus = true; |
- _unmountedComponents.forEach((c) => c._didUnmount()); |
- _mountedComponents.forEach((c) => c._didMount()); |
- _mountedComponents.clear(); |
- _unmountedComponents.clear(); |
- } finally { |
- _notifingMountStatus = false; |
- } |
-} |
- |
-List<Component> _dirtyComponents = new List<Component>(); |
-bool _buildScheduled = false; |
-bool _inRenderDirtyComponents = false; |
- |
-void _buildDirtyComponents() { |
- _tracing.begin('fn::_buildDirtyComponents'); |
- |
- Stopwatch sw; |
- if (_shouldLogRenderDuration) |
- sw = new Stopwatch()..start(); |
- |
- try { |
- _inRenderDirtyComponents = true; |
- |
- _dirtyComponents.sort((a, b) => a._order - b._order); |
- for (var comp in _dirtyComponents) { |
- comp._buildIfDirty(); |
- } |
- |
- _dirtyComponents.clear(); |
- _buildScheduled = false; |
- } finally { |
- _inRenderDirtyComponents = false; |
- } |
- |
- _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 { |
- bool get _isBuilding => _currentlyBuilding == this; |
- bool _dirty = true; |
- |
- UINode _built; |
- final int _order; |
- static int _currentOrder = 0; |
- bool _stateful; |
- static Component _currentlyBuilding; |
- List<Function> _mountCallbacks; |
- List<Function> _unmountCallbacks; |
- |
- void onDidMount(Function fn) { |
- if (_mountCallbacks == null) |
- _mountCallbacks = new List<Function>(); |
- |
- _mountCallbacks.add(fn); |
- } |
- |
- void onDidUnmount(Function fn) { |
- if (_unmountCallbacks == null) |
- _unmountCallbacks = new List<Function>(); |
- |
- _unmountCallbacks.add(fn); |
- } |
- |
- |
- 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); |
- |
- void _didMount() { |
- if (_mountCallbacks != null) |
- _mountCallbacks.forEach((fn) => fn()); |
- } |
- |
- void _didUnmount() { |
- if (_unmountCallbacks != null) |
- _unmountCallbacks.forEach((fn) => fn()); |
- } |
- |
- // TODO(rafaelw): It seems wrong to expose DOM at all. This is presently |
- // needed to get sizing info. |
- RenderCSS getRoot() => _root; |
- |
- void _remove() { |
- assert(_built != null); |
- assert(_root != null); |
- _removeChild(_built); |
- _built = null; |
- _enqueueDidUnmount(this); |
- super._remove(); |
- } |
- |
- bool _willSync(UINode old) { |
- Component oldComponent = old as Component; |
- if (oldComponent == null || !oldComponent._stateful) |
- return false; |
- |
- // Make |this| the "old" Component |
- _stateful = false; |
- _built = oldComponent._built; |
- assert(_built != null); |
- |
- // Make |oldComponent| the "new" component |
- reflect.copyPublicFields(this, oldComponent); |
- oldComponent._built = null; |
- oldComponent._dirty = true; |
- return true; |
- } |
- |
- /* 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, RenderCSSContainer host, RenderCSS insertBefore) { |
- assert(!_defunct); |
- assert(_built == null || old == null); |
- |
- Component oldComponent = old as Component; |
- |
- var oldBuilt; |
- if (oldComponent == null) { |
- oldBuilt = _built; |
- } else { |
- assert(_built == null); |
- oldBuilt = oldComponent._built; |
- } |
- |
- if (oldBuilt == null) |
- _enqueueDidMount(this); |
- |
- int lastOrder = _currentOrder; |
- _currentOrder = _order; |
- _currentlyBuilding = this; |
- _built = build(); |
- _currentlyBuilding = null; |
- _currentOrder = lastOrder; |
- |
- _built = _syncChild(_built, oldBuilt, host, insertBefore); |
- _dirty = false; |
- _root = _built._root; |
- assert(_root != null); |
- } |
- |
- void _buildIfDirty() { |
- if (!_dirty || _defunct) |
- return; |
- |
- _trace('$_key rebuilding...'); |
- _sync(null, null, null); // TODO(ianh): figure out how passing "null, null, null" here is ok |
- } |
- |
- void scheduleBuild() { |
- setState(() {}); |
- } |
- |
- void setState(Function fn()) { |
- _stateful = true; |
- fn(); |
- if (_isBuilding || _dirty || _defunct) |
- return; |
- |
- _dirty = true; |
- _scheduleComponentForRender(this); |
- } |
- |
- UINode build(); |
-} |
- |
-abstract class App extends Component { |
- RenderCSS _host; |
- |
- App() : super(stateful: true) { |
- _host = new RenderCSSRoot(this); |
- _scheduleComponentForRender(this); |
- } |
- |
- void _buildIfDirty() { |
- if (!_dirty || _defunct) |
- return; |
- |
- _trace('$_key rebuilding...'); |
- _sync(null, _host, _root); |
- } |
-} |
- |
-class Text extends Component { |
- Text(this.data) : super(key: '*text*'); |
- final String data; |
- bool get interchangeable => true; |
- UINode build() => new Paragraph(children: [new TextFragment(data)]); |
-} |