Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 library fn; | 5 library fn; |
| 6 | 6 |
| 7 import 'app.dart'; | 7 import 'app.dart'; |
| 8 import 'dart:async'; | 8 import 'dart:async'; |
| 9 import 'dart:collection'; | 9 import 'dart:collection'; |
| 10 import 'dart:mirrors'; | 10 import 'dart:mirrors'; |
| 11 import 'dart:sky' as sky; | 11 import 'dart:sky' as sky; |
| 12 import 'package:vector_math/vector_math.dart'; | 12 import 'package:vector_math/vector_math.dart'; |
| 13 import 'reflect.dart' as reflect; | 13 import 'reflect.dart' as reflect; |
| 14 import 'rendering/block.dart'; | 14 import 'rendering/block.dart'; |
| 15 import 'rendering/box.dart'; | 15 import 'rendering/box.dart'; |
| 16 import 'rendering/flex.dart'; | 16 import 'rendering/flex.dart'; |
| 17 import 'rendering/object.dart'; | 17 import 'rendering/object.dart'; |
| 18 import 'rendering/paragraph.dart'; | 18 import 'rendering/paragraph.dart'; |
| 19 import 'rendering/stack.dart'; | 19 import 'rendering/stack.dart'; |
| 20 export 'rendering/object.dart' show Point, Size, Rect, Color, Paint, Path; | 20 export 'rendering/object.dart' show Point, Size, Rect, Color, Paint, Path; |
| 21 export 'rendering/box.dart' show BoxDecoration, Border, BorderSide, EdgeDims; | 21 export 'rendering/box.dart' show BoxDecoration, Border, BorderSide, EdgeDims; |
| 22 export 'rendering/flex.dart' show FlexDirection; | 22 export 'rendering/flex.dart' show FlexDirection; |
| 23 | 23 |
| 24 // final sky.Tracing _tracing = sky.window.tracing; | 24 // final sky.Tracing _tracing = sky.window.tracing; |
| 25 | 25 |
| 26 final bool _shouldLogRenderDuration = false; | 26 final bool _shouldLogRenderDuration = false; |
| 27 final bool _shouldTrace = false; | |
| 28 | |
| 29 enum _SyncOperation { identical, insertion, stateful, stateless, removal } | |
| 30 | 27 |
| 31 /* | 28 /* |
| 32 * All Effen nodes derive from UINode. All nodes have a _parent, a _key and | 29 * All Effen nodes derive from UINode. All nodes have a _parent, a _key and |
| 33 * can be sync'd. | 30 * can be sync'd. |
| 34 */ | 31 */ |
| 35 abstract class UINode { | 32 abstract class UINode { |
| 36 String _key; | |
| 37 UINode _parent; | |
| 38 UINode get parent => _parent; | |
| 39 RenderObject root; | |
| 40 bool _defunct = false; | |
| 41 | 33 |
| 42 UINode({ Object key }) { | 34 UINode({ Object key }) { |
| 43 _key = key == null ? "$runtimeType" : "$runtimeType-$key"; | 35 _key = key == null ? "$runtimeType" : "$runtimeType-$key"; |
| 44 assert(this is App || _inRenderDirtyComponents); // you should not build the UI tree ahead of time, build it only during build() | 36 assert(this is App || _inRenderDirtyComponents); // you should not build the UI tree ahead of time, build it only during build() |
| 45 } | 37 } |
| 46 | 38 |
| 39 String _key; | |
| 40 String get key => _key; | |
| 41 | |
| 42 UINode _parent; | |
| 43 UINode get parent => _parent; | |
| 44 | |
| 45 bool _mounted = false; | |
| 46 bool _wasMounted = false; | |
| 47 bool get mounted => _mounted; | |
| 48 static bool _notifyingMountStatus = false; | |
| 49 static Set<UINode> _mountedChanged = new HashSet<UINode>(); | |
| 50 | |
| 51 void setParent(UINode newParent) { | |
| 52 assert(!_notifyingMountStatus); | |
| 53 _parent = newParent; | |
| 54 if (newParent == null) { | |
| 55 if (_mounted) { | |
| 56 _mounted = false; | |
| 57 _mountedChanged.add(this); | |
| 58 } | |
| 59 } else { | |
| 60 assert(newParent._mounted); | |
| 61 if (_parent._mounted != _mounted) { | |
| 62 _mounted = _parent._mounted; | |
| 63 _mountedChanged.add(this); | |
| 64 } | |
| 65 } | |
| 66 } | |
| 67 | |
| 68 static void _notifyMountStatusChanged() { | |
| 69 try { | |
| 70 _notifyingMountStatus = true; | |
| 71 _mountedChanged.forEach((UINode node) { | |
|
abarth-chromium
2015/06/08 16:52:51
for (UINode node in _mountedChanged) {
}
| |
| 72 if (node._wasMounted != node._mounted) { | |
| 73 if (node._mounted) | |
| 74 node._didMount(); | |
| 75 else | |
| 76 node._didUnmount(); | |
| 77 node._wasMounted = node._mounted; | |
| 78 } | |
| 79 }); | |
| 80 _mountedChanged.clear(); | |
| 81 } finally { | |
| 82 _notifyingMountStatus = false; | |
| 83 } | |
| 84 } | |
| 85 void _didMount() { } | |
| 86 void _didUnmount() { } | |
| 87 | |
| 88 RenderObject root; | |
| 89 | |
| 47 // Subclasses which implements Nodes that become stateful may return true | 90 // Subclasses which implements Nodes that become stateful may return true |
| 48 // if the |old| node has become stateful and should be retained. | 91 // if the |old| node has become stateful and should be retained. |
| 49 bool _willSync(UINode old) => false; | 92 bool _willSync(UINode old) => false; |
| 50 | 93 |
| 51 bool get interchangeable => false; // if true, then keys can be duplicated | 94 bool get interchangeable => false; // if true, then keys can be duplicated |
| 52 | 95 |
| 53 void _sync(UINode old, dynamic slot); | 96 void _sync(UINode old, dynamic slot); |
| 54 // 'slot' is the identifier that the parent RenderObjectWrapper uses to know | 97 // 'slot' is the identifier that the parent RenderObjectWrapper uses to know |
| 55 // where to put this descendant | 98 // where to put this descendant |
| 56 | 99 |
| 57 void remove() { | 100 void remove() { |
| 58 _defunct = true; | |
| 59 root = null; | 101 root = null; |
| 60 handleRemoved(); | 102 setParent(null); |
| 61 } | 103 } |
| 62 void handleRemoved() { } | |
| 63 | 104 |
| 64 UINode findAncestor(Type targetType) { | 105 UINode findAncestor(Type targetType) { |
| 65 var ancestor = _parent; | 106 var ancestor = _parent; |
| 66 while (ancestor != null && !reflectClass(ancestor.runtimeType).isSubtypeOf(r eflectClass(targetType))) | 107 while (ancestor != null && !reflectClass(ancestor.runtimeType).isSubtypeOf(r eflectClass(targetType))) |
| 67 ancestor = ancestor._parent; | 108 ancestor = ancestor._parent; |
| 68 return ancestor; | 109 return ancestor; |
| 69 } | 110 } |
| 70 | 111 |
| 71 int _nodeDepth; | |
| 72 void _ensureDepth() { | |
| 73 if (_nodeDepth == null) { | |
| 74 if (_parent != null) { | |
| 75 _parent._ensureDepth(); | |
| 76 _nodeDepth = _parent._nodeDepth + 1; | |
| 77 } else { | |
| 78 _nodeDepth = 0; | |
| 79 } | |
| 80 } | |
| 81 } | |
| 82 | |
| 83 void _trace(String message) { | |
| 84 if (!_shouldTrace) | |
| 85 return; | |
| 86 | |
| 87 _ensureDepth(); | |
| 88 print((' ' * _nodeDepth) + message); | |
| 89 } | |
| 90 | |
| 91 void _traceSync(_SyncOperation op, String key) { | |
| 92 if (!_shouldTrace) | |
| 93 return; | |
| 94 | |
| 95 String opString = op.toString().toLowerCase(); | |
| 96 String outString = opString.substring(opString.indexOf('.') + 1); | |
| 97 _trace('_sync($outString) $key'); | |
| 98 } | |
| 99 | |
| 100 void removeChild(UINode node) { | 112 void removeChild(UINode node) { |
| 101 _traceSync(_SyncOperation.removal, node._key); | |
| 102 node.remove(); | 113 node.remove(); |
| 103 } | 114 } |
| 104 | 115 |
| 105 // Returns the child which should be retained as the child of this node. | 116 // Returns the child which should be retained as the child of this node. |
| 106 UINode syncChild(UINode node, UINode oldNode, dynamic slot) { | 117 UINode syncChild(UINode node, UINode oldNode, dynamic slot) { |
| 118 | |
| 107 if (node == oldNode) { | 119 if (node == oldNode) { |
| 108 _traceSync(_SyncOperation.identical, node == null ? '*null*' : node._key); | 120 assert(node == null || node.mounted); |
| 109 return node; // Nothing to do. Subtrees must be identical. | 121 return node; // Nothing to do. Subtrees must be identical. |
| 110 } | 122 } |
| 111 | 123 |
| 112 if (node == null) { | 124 if (node == null) { |
| 113 // the child in this slot has gone away | 125 // the child in this slot has gone away |
| 126 assert(oldNode.mounted); | |
| 114 removeChild(oldNode); | 127 removeChild(oldNode); |
| 128 assert(!oldNode.mounted); | |
| 115 return null; | 129 return null; |
| 116 } | 130 } |
| 117 assert(oldNode == null || node._key == oldNode._key); | |
| 118 | 131 |
| 119 // TODO(rafaelw): This eagerly removes the old DOM. It may be that a | 132 if (oldNode != null && node._key == oldNode._key && node._willSync(oldNode)) { |
| 120 // new component was built that could re-use some of it. Consider | 133 assert(oldNode.mounted); |
| 121 // syncing the new VDOM against the old one. | 134 assert(!node.mounted); |
| 122 if (oldNode != null && node._key != oldNode._key) { | |
| 123 removeChild(oldNode); | |
| 124 } | |
| 125 | |
| 126 if (node._willSync(oldNode)) { | |
| 127 _traceSync(_SyncOperation.stateful, node._key); | |
| 128 oldNode._sync(node, slot); | 135 oldNode._sync(node, slot); |
| 129 node._defunct = true; | |
| 130 assert(oldNode.root is RenderObject); | 136 assert(oldNode.root is RenderObject); |
| 131 return oldNode; | 137 return oldNode; |
| 132 } | 138 } |
| 133 | 139 |
| 134 assert(!node._defunct); | 140 if (oldNode != null && node._key != oldNode._key) { |
| 135 node._parent = this; | 141 assert(oldNode.mounted); |
| 142 removeChild(oldNode); | |
| 143 oldNode = null; | |
| 144 } | |
| 136 | 145 |
| 137 if (oldNode == null) { | 146 assert(!node.mounted); |
| 138 _traceSync(_SyncOperation.insertion, node._key); | 147 node.setParent(this); |
| 139 } else { | |
| 140 _traceSync(_SyncOperation.stateless, node._key); | |
| 141 } | |
| 142 node._sync(oldNode, slot); | 148 node._sync(oldNode, slot); |
| 143 if (oldNode != null) | |
| 144 oldNode._defunct = true; | |
| 145 | |
| 146 assert(node.root is RenderObject); | 149 assert(node.root is RenderObject); |
| 147 return node; | 150 return node; |
| 148 } | 151 } |
| 149 } | 152 } |
| 150 | 153 |
| 151 abstract class ContentNode extends UINode { | 154 abstract class ContentNode extends UINode { |
| 152 UINode content; | 155 UINode content; |
| 153 | 156 |
| 154 ContentNode(UINode content) : this.content = content, super(key: content._key) ; | 157 ContentNode(UINode content) : this.content = content, super(key: content._key) ; |
| 155 | 158 |
| (...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 277 | 280 |
| 278 RenderObjectWrapper({ | 281 RenderObjectWrapper({ |
| 279 Object key | 282 Object key |
| 280 }) : super(key: key); | 283 }) : super(key: key); |
| 281 | 284 |
| 282 RenderObject createNode(); | 285 RenderObject createNode(); |
| 283 | 286 |
| 284 void insert(RenderObjectWrapper child, dynamic slot); | 287 void insert(RenderObjectWrapper child, dynamic slot); |
| 285 | 288 |
| 286 void _sync(UINode old, dynamic slot) { | 289 void _sync(UINode old, dynamic slot) { |
| 290 assert(parent != null); | |
| 287 if (old == null) { | 291 if (old == null) { |
| 288 root = createNode(); | 292 root = createNode(); |
| 289 assert(root != null); | 293 assert(root != null); |
| 290 var ancestor = findAncestor(RenderObjectWrapper); | 294 var ancestor = findAncestor(RenderObjectWrapper); |
| 291 if (ancestor is RenderObjectWrapper) | 295 if (ancestor is RenderObjectWrapper) |
| 292 ancestor.insert(this, slot); | 296 ancestor.insert(this, slot); |
| 293 } else { | 297 } else { |
| 294 root = old.root; | 298 root = old.root; |
| 295 assert(root != null); | |
| 296 } | 299 } |
| 297 | 300 assert(mounted); |
| 301 assert(root != null); | |
| 298 _nodeMap[root] = this; | 302 _nodeMap[root] = this; |
| 299 syncRenderObject(old); | 303 syncRenderObject(old); |
| 300 } | 304 } |
| 301 | 305 |
| 302 void syncRenderObject(RenderObjectWrapper old) { | 306 void syncRenderObject(RenderObjectWrapper old) { |
| 303 ParentData parentData = null; | 307 ParentData parentData = null; |
| 304 UINode parent = _parent; | 308 UINode ancestor = parent; |
| 305 while (parent != null && parent is! RenderObjectWrapper) { | 309 while (ancestor != null && ancestor is! RenderObjectWrapper) { |
| 306 if (parent is ParentDataNode && parent.parentData != null) { | 310 if (ancestor is ParentDataNode && ancestor.parentData != null) { |
| 307 if (parentData != null) | 311 if (parentData != null) |
| 308 parentData.merge(parent.parentData); // this will throw if the types a ren't the same | 312 parentData.merge(ancestor.parentData); // this will throw if the types aren't the same |
| 309 else | 313 else |
| 310 parentData = parent.parentData; | 314 parentData = ancestor.parentData; |
| 311 } | 315 } |
| 312 parent = parent._parent; | 316 ancestor = ancestor.parent; |
| 313 } | 317 } |
| 314 if (parentData != null) { | 318 if (parentData != null) { |
| 315 assert(root.parentData != null); | 319 assert(root.parentData != null); |
| 316 root.parentData.merge(parentData); // this will throw if the types aren't approriate | 320 root.parentData.merge(parentData); // this will throw if the types aren't appropriate |
| 317 assert(parent != null); | 321 if (parent.root != null) |
| 318 assert(parent.root != null); | 322 parent.root.markNeedsLayout(); |
| 319 parent.root.markNeedsLayout(); | |
| 320 } | 323 } |
| 321 } | 324 } |
| 322 | 325 |
| 323 void remove() { | 326 void remove() { |
| 324 assert(root != null); | 327 assert(root != null); |
| 325 _nodeMap.remove(root); | 328 _nodeMap.remove(root); |
| 326 super.remove(); | 329 super.remove(); |
| 327 } | 330 } |
| 328 } | 331 } |
| 329 | 332 |
| (...skipping 404 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 734 root.src = src; | 737 root.src = src; |
| 735 root.requestedSize = size; | 738 root.requestedSize = size; |
| 736 } | 739 } |
| 737 | 740 |
| 738 void insert(RenderObjectWrapper child, dynamic slot) { | 741 void insert(RenderObjectWrapper child, dynamic slot) { |
| 739 assert(false); | 742 assert(false); |
| 740 // Image does not support having children currently | 743 // Image does not support having children currently |
| 741 } | 744 } |
| 742 } | 745 } |
| 743 | 746 |
| 744 | |
| 745 Set<Component> _mountedComponents = new HashSet<Component>(); | |
| 746 Set<Component> _unmountedComponents = new HashSet<Component>(); | |
| 747 | |
| 748 void _enqueueDidMount(Component c) { | |
| 749 assert(!_notifingMountStatus); | |
| 750 _mountedComponents.add(c); | |
| 751 } | |
| 752 | |
| 753 void _enqueueDidUnmount(Component c) { | |
| 754 assert(!_notifingMountStatus); | |
| 755 _unmountedComponents.add(c); | |
| 756 } | |
| 757 | |
| 758 bool _notifingMountStatus = false; | |
| 759 | |
| 760 void _notifyMountStatusChanged() { | |
| 761 try { | |
| 762 _notifingMountStatus = true; | |
| 763 _unmountedComponents.forEach((c) => c._didUnmount()); | |
| 764 _mountedComponents.forEach((c) => c._didMount()); | |
| 765 _mountedComponents.clear(); | |
| 766 _unmountedComponents.clear(); | |
| 767 } finally { | |
| 768 _notifingMountStatus = false; | |
| 769 } | |
| 770 } | |
| 771 | |
| 772 List<Component> _dirtyComponents = new List<Component>(); | 747 List<Component> _dirtyComponents = new List<Component>(); |
| 773 bool _buildScheduled = false; | 748 bool _buildScheduled = false; |
| 774 bool _inRenderDirtyComponents = false; | 749 bool _inRenderDirtyComponents = false; |
| 775 | 750 |
| 776 void _buildDirtyComponents() { | 751 void _buildDirtyComponents() { |
| 777 //_tracing.begin('fn::_buildDirtyComponents'); | 752 //_tracing.begin('fn::_buildDirtyComponents'); |
| 778 | 753 |
| 779 Stopwatch sw; | 754 Stopwatch sw; |
| 780 if (_shouldLogRenderDuration) | 755 if (_shouldLogRenderDuration) |
| 781 sw = new Stopwatch()..start(); | 756 sw = new Stopwatch()..start(); |
| 782 | 757 |
| 783 try { | 758 try { |
| 784 _inRenderDirtyComponents = true; | 759 _inRenderDirtyComponents = true; |
| 785 | 760 |
| 786 _dirtyComponents.sort((a, b) => a._order - b._order); | 761 _dirtyComponents.sort((a, b) => a._order - b._order); |
| 787 for (var comp in _dirtyComponents) { | 762 for (var comp in _dirtyComponents) { |
| 788 comp._buildIfDirty(); | 763 comp._buildIfDirty(); |
| 789 } | 764 } |
| 790 | 765 |
| 791 _dirtyComponents.clear(); | 766 _dirtyComponents.clear(); |
| 792 _buildScheduled = false; | 767 _buildScheduled = false; |
| 793 } finally { | 768 } finally { |
| 794 _inRenderDirtyComponents = false; | 769 _inRenderDirtyComponents = false; |
| 795 } | 770 } |
| 796 | 771 |
| 797 _notifyMountStatusChanged(); | 772 UINode._notifyMountStatusChanged(); |
| 798 | 773 |
| 799 if (_shouldLogRenderDuration) { | 774 if (_shouldLogRenderDuration) { |
| 800 sw.stop(); | 775 sw.stop(); |
| 801 print('Render took ${sw.elapsedMicroseconds} microseconds'); | 776 print('Render took ${sw.elapsedMicroseconds} microseconds'); |
| 802 } | 777 } |
| 803 | 778 |
| 804 //_tracing.end('fn::_buildDirtyComponents'); | 779 //_tracing.end('fn::_buildDirtyComponents'); |
| 805 } | 780 } |
| 806 | 781 |
| 807 void _scheduleComponentForRender(Component c) { | 782 void _scheduleComponentForRender(Component c) { |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 834 _mountCallbacks.add(fn); | 809 _mountCallbacks.add(fn); |
| 835 } | 810 } |
| 836 | 811 |
| 837 void onDidUnmount(Function fn) { | 812 void onDidUnmount(Function fn) { |
| 838 if (_unmountCallbacks == null) | 813 if (_unmountCallbacks == null) |
| 839 _unmountCallbacks = new List<Function>(); | 814 _unmountCallbacks = new List<Function>(); |
| 840 | 815 |
| 841 _unmountCallbacks.add(fn); | 816 _unmountCallbacks.add(fn); |
| 842 } | 817 } |
| 843 | 818 |
| 844 | |
| 845 Component({ Object key, bool stateful }) | 819 Component({ Object key, bool stateful }) |
| 846 : _stateful = stateful != null ? stateful : false, | 820 : _stateful = stateful != null ? stateful : false, |
| 847 _order = _currentOrder + 1, | 821 _order = _currentOrder + 1, |
| 848 super(key: key); | 822 super(key: key); |
| 849 | 823 |
| 850 Component.fromArgs(Object key, bool stateful) | 824 Component.fromArgs(Object key, bool stateful) |
| 851 : this(key: key, stateful: stateful); | 825 : this(key: key, stateful: stateful); |
| 852 | 826 |
| 853 void _didMount() { | 827 void _didMount() { |
| 828 super._didMount(); | |
| 854 if (_mountCallbacks != null) | 829 if (_mountCallbacks != null) |
| 855 _mountCallbacks.forEach((fn) => fn()); | 830 _mountCallbacks.forEach((fn) => fn()); |
| 856 } | 831 } |
| 857 | 832 |
| 858 void _didUnmount() { | 833 void _didUnmount() { |
| 834 super._didUnmount(); | |
| 859 if (_unmountCallbacks != null) | 835 if (_unmountCallbacks != null) |
| 860 _unmountCallbacks.forEach((fn) => fn()); | 836 _unmountCallbacks.forEach((fn) => fn()); |
| 861 } | 837 } |
| 862 | 838 |
| 863 // TODO(rafaelw): It seems wrong to expose DOM at all. This is presently | 839 // TODO(rafaelw): It seems wrong to expose DOM at all. This is presently |
| 864 // needed to get sizing info. | 840 // needed to get sizing info. |
| 865 RenderObject getRoot() => root; | 841 RenderObject getRoot() => root; |
| 866 | 842 |
| 867 void remove() { | 843 void remove() { |
| 868 assert(_built != null); | 844 assert(_built != null); |
| 869 assert(root != null); | 845 assert(root != null); |
| 870 removeChild(_built); | 846 removeChild(_built); |
| 871 _built = null; | 847 _built = null; |
| 872 _enqueueDidUnmount(this); | |
| 873 super.remove(); | 848 super.remove(); |
| 874 } | 849 } |
| 875 | 850 |
| 876 bool _willSync(UINode old) { | 851 bool _willSync(UINode old) { |
| 877 Component oldComponent = old as Component; | 852 Component oldComponent = old as Component; |
| 878 if (oldComponent == null || !oldComponent._stateful) | 853 if (oldComponent == null || !oldComponent._stateful) |
| 879 return false; | 854 return false; |
| 880 | 855 |
| 881 // Make |this| the "old" Component | 856 // Make |this| the "old" Component |
| 882 _stateful = false; | 857 _stateful = false; |
| 883 _built = oldComponent._built; | 858 _built = oldComponent._built; |
| 884 assert(_built != null); | 859 assert(_built != null); |
| 885 | 860 |
| 886 // Make |oldComponent| the "new" component | 861 // Make |oldComponent| the "new" component |
| 887 reflect.copyPublicFields(this, oldComponent); | 862 reflect.copyPublicFields(this, oldComponent); |
| 888 oldComponent._built = null; | 863 oldComponent._built = null; |
| 889 oldComponent._dirty = true; | 864 oldComponent._dirty = true; |
| 890 return true; | 865 return true; |
| 891 } | 866 } |
| 892 | 867 |
| 893 /* There are three cases here: | 868 /* There are three cases here: |
| 894 * 1) Building for the first time: | 869 * 1) Building for the first time: |
| 895 * assert(_built == null && old == null) | 870 * assert(_built == null && old == null) |
| 896 * 2) Re-building (because a dirty flag got set): | 871 * 2) Re-building (because a dirty flag got set): |
| 897 * assert(_built != null && old == null) | 872 * assert(_built != null && old == null) |
| 898 * 3) Syncing against an old version | 873 * 3) Syncing against an old version |
| 899 * assert(_built == null && old != null) | 874 * assert(_built == null && old != null) |
| 900 */ | 875 */ |
| 901 void _sync(UINode old, dynamic slot) { | 876 void _sync(UINode old, dynamic slot) { |
| 902 assert(!_defunct); | |
| 903 assert(_built == null || old == null); | 877 assert(_built == null || old == null); |
| 904 | 878 |
| 905 Component oldComponent = old as Component; | 879 Component oldComponent = old as Component; |
| 906 | 880 |
| 907 _slot = slot; | 881 _slot = slot; |
| 908 | 882 |
| 909 var oldBuilt; | 883 var oldBuilt; |
| 910 if (oldComponent == null) { | 884 if (oldComponent == null) { |
| 911 oldBuilt = _built; | 885 oldBuilt = _built; |
| 912 } else { | 886 } else { |
| 913 assert(_built == null); | 887 assert(_built == null); |
| 914 oldBuilt = oldComponent._built; | 888 oldBuilt = oldComponent._built; |
| 915 } | 889 } |
| 916 | 890 |
| 917 if (oldBuilt == null) | |
| 918 _enqueueDidMount(this); | |
| 919 | |
| 920 int lastOrder = _currentOrder; | 891 int lastOrder = _currentOrder; |
| 921 _currentOrder = _order; | 892 _currentOrder = _order; |
| 922 _currentlyBuilding = this; | 893 _currentlyBuilding = this; |
| 923 _built = build(); | 894 _built = build(); |
| 895 assert(_built != null); | |
| 924 _currentlyBuilding = null; | 896 _currentlyBuilding = null; |
| 925 _currentOrder = lastOrder; | 897 _currentOrder = lastOrder; |
| 926 | 898 |
| 927 _built = syncChild(_built, oldBuilt, slot); | 899 _built = syncChild(_built, oldBuilt, slot); |
| 900 assert(_built != null); | |
| 928 _dirty = false; | 901 _dirty = false; |
| 929 root = _built.root; | 902 root = _built.root; |
| 930 assert(root != null); | 903 assert(root != null); |
| 931 } | 904 } |
| 932 | 905 |
| 933 void _buildIfDirty() { | 906 void _buildIfDirty() { |
| 934 if (!_dirty || _defunct) | 907 if (!_dirty || !_mounted) |
| 935 return; | 908 return; |
| 936 | 909 |
| 937 _trace('$_key rebuilding...'); | |
| 938 assert(root != null); | 910 assert(root != null); |
| 939 _sync(null, _slot); | 911 _sync(null, _slot); |
| 940 } | 912 } |
| 941 | 913 |
| 942 void scheduleBuild() { | 914 void scheduleBuild() { |
| 943 setState(() {}); | 915 setState(() {}); |
| 944 } | 916 } |
| 945 | 917 |
| 946 void setState(Function fn()) { | 918 void setState(Function fn()) { |
| 947 _stateful = true; | 919 _stateful = true; |
| 948 fn(); | 920 fn(); |
| 949 if (_isBuilding || _dirty || _defunct) | 921 if (_isBuilding || _dirty || !_mounted) |
| 950 return; | 922 return; |
| 951 | 923 |
| 952 _dirty = true; | 924 _dirty = true; |
| 953 _scheduleComponentForRender(this); | 925 _scheduleComponentForRender(this); |
| 954 } | 926 } |
| 955 | 927 |
| 956 UINode build(); | 928 UINode build(); |
| 957 } | 929 } |
| 958 | 930 |
| 959 class Container extends Component { | 931 class Container extends Component { |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 971 this.margin, | 943 this.margin, |
| 972 this.decoration, | 944 this.decoration, |
| 973 this.desiredSize, | 945 this.desiredSize, |
| 974 this.padding | 946 this.padding |
| 975 }) : super(key: key); | 947 }) : super(key: key); |
| 976 | 948 |
| 977 UINode build() { | 949 UINode build() { |
| 978 UINode current = child; | 950 UINode current = child; |
| 979 | 951 |
| 980 if (padding != null) | 952 if (padding != null) |
| 981 current = new Padding(padding: padding, child: current); | 953 current = new Padding(padding: padding, child: current, key: '$key-padding '); |
| 982 | 954 |
| 983 if (decoration != null) | 955 if (decoration != null) |
| 984 current = new DecoratedBox(decoration: decoration, child: current); | 956 current = new DecoratedBox(decoration: decoration, child: current, key: '$ key-decoration'); |
| 985 | 957 |
| 986 if (desiredSize != null) | 958 if (desiredSize != null) |
| 987 current = new SizedBox(desiredSize: desiredSize, child: current); | 959 current = new SizedBox(desiredSize: desiredSize, child: current, key: '$ke y-size'); |
| 988 | 960 |
| 989 if (margin != null) | 961 if (margin != null) |
| 990 current = new Padding(padding: margin, child: current); | 962 current = new Padding(padding: margin, child: current, key: '$key-margin') ; |
| 991 | 963 |
| 992 if (transform != null) | 964 if (transform != null) |
| 993 current = new Transform(transform: transform, child: current); | 965 current = new Transform(transform: transform, child: current, key: '$key-t ransform'); |
| 994 | 966 |
| 995 return current; | 967 return current; |
| 996 } | 968 } |
| 997 } | 969 } |
| 998 | 970 |
| 999 class _AppView extends AppView { | 971 class _AppView extends AppView { |
| 1000 _AppView() : super(null); | 972 _AppView() : super(null); |
| 1001 | 973 |
| 1002 void dispatchEvent(sky.Event event, HitTestResult result) { | 974 void dispatchEvent(sky.Event event, HitTestResult result) { |
| 1003 super.dispatchEvent(event, result); | 975 super.dispatchEvent(event, result); |
| 1004 | 976 |
| 1005 UINode target = RenderObjectWrapper._getMounted(result.path.first); | 977 UINode target = RenderObjectWrapper._getMounted(result.path.first); |
| 1006 | 978 |
| 1007 // TODO(rafaelw): StopPropagation? | 979 // TODO(rafaelw): StopPropagation? |
| 1008 while (target != null) { | 980 while (target != null) { |
| 1009 if (target is EventListenerNode) | 981 if (target is EventListenerNode) |
| 1010 target._handleEvent(event); | 982 target._handleEvent(event); |
| 1011 target = target._parent; | 983 target = target._parent; |
| 1012 } | 984 } |
| 1013 } | 985 } |
| 1014 } | 986 } |
| 1015 | 987 |
| 1016 abstract class App extends Component { | 988 abstract class App extends Component { |
| 1017 | 989 |
| 1018 App() : super(stateful: true) { | 990 App() : super(stateful: true) { |
| 1019 _appView = new _AppView(); | 991 _appView = new _AppView(); |
| 1020 _scheduleComponentForRender(this); | 992 _scheduleComponentForRender(this); |
| 993 _mounted = true; | |
| 1021 } | 994 } |
| 1022 | 995 |
| 1023 AppView _appView; | 996 AppView _appView; |
| 1024 AppView get appView => _appView; | 997 AppView get appView => _appView; |
| 1025 | 998 |
| 1026 void _buildIfDirty() { | 999 void _buildIfDirty() { |
| 1027 assert(_dirty); | 1000 assert(_dirty); |
| 1028 assert(!_defunct); | 1001 assert(_mounted); |
| 1029 _trace('$_key rebuilding app...'); | |
| 1030 _sync(null, null); | 1002 _sync(null, null); |
| 1031 if (root.parent == null) | 1003 if (root.parent == null) |
| 1032 _appView.root = root; | 1004 _appView.root = root; |
| 1033 assert(root.parent is RenderView); | 1005 assert(root.parent is RenderView); |
| 1034 } | 1006 } |
| 1035 } | 1007 } |
| 1036 | 1008 |
| 1037 class Text extends Component { | 1009 class Text extends Component { |
| 1038 Text(this.data) : super(key: '*text*'); | 1010 Text(this.data) : super(key: '*text*'); |
| 1039 final String data; | 1011 final String data; |
| 1040 bool get interchangeable => true; | 1012 bool get interchangeable => true; |
| 1041 UINode build() => new Paragraph(text: data); | 1013 UINode build() => new Paragraph(text: data); |
| 1042 } | 1014 } |
| OLD | NEW |