| 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 import 'dart:async'; | 5 import 'dart:async'; |
| 6 import 'dart:collection'; | 6 import 'dart:collection'; |
| 7 import 'dart:mirrors'; | 7 import 'dart:mirrors'; |
| 8 import 'dart:sky' as sky; | 8 import 'dart:sky' as sky; |
| 9 | 9 |
| 10 import '../app/view.dart'; | 10 import '../app/view.dart'; |
| 11 import '../rendering/box.dart'; | 11 import '../rendering/box.dart'; |
| 12 import '../rendering/object.dart'; | 12 import '../rendering/object.dart'; |
| 13 | 13 |
| 14 export '../rendering/box.dart' show BoxConstraints, BoxDecoration, Border, Borde
rSide, EdgeDims; | 14 export '../rendering/box.dart' show BoxConstraints, BoxDecoration, Border, Borde
rSide, EdgeDims; |
| 15 export '../rendering/flex.dart' show FlexDirection; | 15 export '../rendering/flex.dart' show FlexDirection; |
| 16 export '../rendering/object.dart' show Point, Size, Rect, Color, Paint, Path; | 16 export '../rendering/object.dart' show Point, Size, Rect, Color, Paint, Path; |
| 17 | 17 |
| 18 final bool _shouldLogRenderDuration = false; | 18 final bool _shouldLogRenderDuration = false; |
| 19 | 19 |
| 20 // All Effen nodes derive from Widget. All nodes have a _parent, a _key and | 20 // All Effen nodes derive from Widget. All nodes have a _parent, a _key and |
| 21 // can be sync'd. | 21 // can be sync'd. |
| 22 abstract class Widget { | 22 abstract class Widget { |
| 23 | 23 |
| 24 Widget({ String key }) { | 24 Widget({ String key }) : _key = key { |
| 25 _key = key != null ? key : runtimeType.toString(); | |
| 26 assert(this is AbstractWidgetRoot || this is App || _inRenderDirtyComponents
); // you should not build the UI tree ahead of time, build it only during build
() | 25 assert(this is AbstractWidgetRoot || this is App || _inRenderDirtyComponents
); // you should not build the UI tree ahead of time, build it only during build
() |
| 27 } | 26 } |
| 28 | 27 |
| 29 String _key; | 28 String _key; |
| 30 String get key => _key; | 29 String get key => _key; |
| 31 | 30 |
| 32 Widget _parent; | 31 Widget _parent; |
| 33 Widget get parent => _parent; | 32 Widget get parent => _parent; |
| 34 | 33 |
| 35 bool _mounted = false; | 34 bool _mounted = false; |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 77 | 76 |
| 78 RenderObject _root; | 77 RenderObject _root; |
| 79 RenderObject get root => _root; | 78 RenderObject get root => _root; |
| 80 | 79 |
| 81 // Subclasses which implements Nodes that become stateful may return true | 80 // Subclasses which implements Nodes that become stateful may return true |
| 82 // if the |old| node has become stateful and should be retained. | 81 // if the |old| node has become stateful and should be retained. |
| 83 // This is called immediately before _sync(). | 82 // This is called immediately before _sync(). |
| 84 // Component._retainStatefulNodeIfPossible() calls syncFields(). | 83 // Component._retainStatefulNodeIfPossible() calls syncFields(). |
| 85 bool _retainStatefulNodeIfPossible(Widget old) => false; | 84 bool _retainStatefulNodeIfPossible(Widget old) => false; |
| 86 | 85 |
| 87 bool get interchangeable => false; // if true, then keys can be duplicated | |
| 88 | |
| 89 void _sync(Widget old, dynamic slot); | 86 void _sync(Widget old, dynamic slot); |
| 90 // 'slot' is the identifier that the parent RenderObjectWrapper uses to know | 87 // 'slot' is the identifier that the parent RenderObjectWrapper uses to know |
| 91 // where to put this descendant | 88 // where to put this descendant |
| 92 | 89 |
| 93 Widget findAncestor(Type targetType) { | 90 Widget findAncestor(Type targetType) { |
| 94 var ancestor = _parent; | 91 var ancestor = _parent; |
| 95 while (ancestor != null && !reflectClass(ancestor.runtimeType).isSubtypeOf(r
eflectClass(targetType))) | 92 while (ancestor != null && !reflectClass(ancestor.runtimeType).isSubtypeOf(r
eflectClass(targetType))) |
| 96 ancestor = ancestor._parent; | 93 ancestor = ancestor._parent; |
| 97 return ancestor; | 94 return ancestor; |
| 98 } | 95 } |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 147 oldNode = null; | 144 oldNode = null; |
| 148 } | 145 } |
| 149 | 146 |
| 150 assert(!node.mounted); | 147 assert(!node.mounted); |
| 151 node.setParent(this); | 148 node.setParent(this); |
| 152 node._sync(oldNode, slot); | 149 node._sync(oldNode, slot); |
| 153 assert(node.root is RenderObject); | 150 assert(node.root is RenderObject); |
| 154 return node; | 151 return node; |
| 155 } | 152 } |
| 156 | 153 |
| 157 String toString() => '$runtimeType($key)'; | 154 String toString() { |
| 155 if (key == null) |
| 156 return '$runtimeType(unkeyed)'; |
| 157 return '$runtimeType("$key")'; |
| 158 } |
| 158 | 159 |
| 159 } | 160 } |
| 160 | 161 |
| 161 | 162 |
| 162 // Descendants of TagNode provide a way to tag RenderObjectWrapper and | 163 // Descendants of TagNode provide a way to tag RenderObjectWrapper and |
| 163 // Component nodes with annotations, such as event listeners, | 164 // Component nodes with annotations, such as event listeners, |
| 164 // stylistic information, etc. | 165 // stylistic information, etc. |
| 165 abstract class TagNode extends Widget { | 166 abstract class TagNode extends Widget { |
| 166 | 167 |
| 167 TagNode(Widget content, { String key }) | 168 TagNode(Widget content, { String key }) |
| (...skipping 328 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 496 void _sync(Widget old, dynamic slot) { | 497 void _sync(Widget old, dynamic slot) { |
| 497 // TODO(abarth): We should split RenderObjectWrapper into two pieces so that | 498 // TODO(abarth): We should split RenderObjectWrapper into two pieces so that |
| 498 // RenderViewObject doesn't need to inherit all this code it | 499 // RenderViewObject doesn't need to inherit all this code it |
| 499 // doesn't need. | 500 // doesn't need. |
| 500 assert(parent != null || this is RenderViewWrapper); | 501 assert(parent != null || this is RenderViewWrapper); |
| 501 if (old == null) { | 502 if (old == null) { |
| 502 _root = createNode(); | 503 _root = createNode(); |
| 503 _ancestor = findAncestor(RenderObjectWrapper); | 504 _ancestor = findAncestor(RenderObjectWrapper); |
| 504 if (_ancestor is RenderObjectWrapper) | 505 if (_ancestor is RenderObjectWrapper) |
| 505 _ancestor.insert(this, slot); | 506 _ancestor.insert(this, slot); |
| 506 else | |
| 507 print("$this has no ancestor"); | |
| 508 } else { | 507 } else { |
| 509 _root = old.root; | 508 _root = old.root; |
| 510 _ancestor = old._ancestor; | 509 _ancestor = old._ancestor; |
| 511 } | 510 } |
| 512 assert(_root == root); // in case a subclass reintroduces it | 511 assert(_root == root); // in case a subclass reintroduces it |
| 513 assert(root != null); | 512 assert(root != null); |
| 514 assert(mounted); | 513 assert(mounted); |
| 515 _nodeMap[root] = this; | 514 _nodeMap[root] = this; |
| 516 syncRenderObject(old); | 515 syncRenderObject(old); |
| 517 } | 516 } |
| (...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 623 assert(child != null); | 622 assert(child != null); |
| 624 removeChild(child); | 623 removeChild(child); |
| 625 } | 624 } |
| 626 super.remove(); | 625 super.remove(); |
| 627 } | 626 } |
| 628 | 627 |
| 629 bool _debugHasDuplicateIds() { | 628 bool _debugHasDuplicateIds() { |
| 630 var idSet = new HashSet<String>(); | 629 var idSet = new HashSet<String>(); |
| 631 for (var child in children) { | 630 for (var child in children) { |
| 632 assert(child != null); | 631 assert(child != null); |
| 633 if (child.interchangeable) | 632 if (child.key == null) |
| 634 continue; // when these nodes are reordered, we just reassign the data | 633 continue; // when these nodes are reordered, we just reassign the data |
| 635 | 634 |
| 636 if (!idSet.add(child.key)) { | 635 if (!idSet.add(child.key)) { |
| 637 throw '''If multiple non-interchangeable nodes exist as children of anot
her node, they must have unique keys. Duplicate: "${child.key}"'''; | 636 throw '''If multiple keyed nodes exist as children of another node, they
must have unique keys. $this has duplicate child key "${child.key}".'''; |
| 638 } | 637 } |
| 639 } | 638 } |
| 640 return false; | 639 return false; |
| 641 } | 640 } |
| 642 | 641 |
| 643 void syncRenderObject(MultiChildRenderObjectWrapper old) { | 642 void syncRenderObject(MultiChildRenderObjectWrapper old) { |
| 644 super.syncRenderObject(old); | 643 super.syncRenderObject(old); |
| 645 | 644 |
| 646 final root = this.root; // TODO(ianh): Remove this once the analyzer is clev
erer | 645 final root = this.root; // TODO(ianh): Remove this once the analyzer is clev
erer |
| 647 if (root is! ContainerRenderObjectMixin) | 646 if (root is! ContainerRenderObjectMixin) |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 695 } | 694 } |
| 696 } | 695 } |
| 697 | 696 |
| 698 void ensureOldIdMap() { | 697 void ensureOldIdMap() { |
| 699 if (oldNodeIdMap != null) | 698 if (oldNodeIdMap != null) |
| 700 return; | 699 return; |
| 701 | 700 |
| 702 oldNodeIdMap = new HashMap<String, Widget>(); | 701 oldNodeIdMap = new HashMap<String, Widget>(); |
| 703 for (int i = oldStartIndex; i < oldEndIndex; i++) { | 702 for (int i = oldStartIndex; i < oldEndIndex; i++) { |
| 704 var node = oldChildren[i]; | 703 var node = oldChildren[i]; |
| 705 if (!node.interchangeable) | 704 if (node.key != null) |
| 706 oldNodeIdMap.putIfAbsent(node.key, () => node); | 705 oldNodeIdMap.putIfAbsent(node.key, () => node); |
| 707 } | 706 } |
| 708 } | 707 } |
| 709 | 708 |
| 710 bool searchForOldNode() { | 709 bool searchForOldNode() { |
| 711 if (currentNode.interchangeable) | 710 if (currentNode.key == null) |
| 712 return false; // never re-order these nodes | 711 return false; // never re-order these nodes |
| 713 | 712 |
| 714 ensureOldIdMap(); | 713 ensureOldIdMap(); |
| 715 oldNode = oldNodeIdMap[currentNode.key]; | 714 oldNode = oldNodeIdMap[currentNode.key]; |
| 716 if (oldNode == null) | 715 if (oldNode == null) |
| 717 return false; | 716 return false; |
| 718 | 717 |
| 719 oldNodeIdMap[currentNode.key] = null; // mark it reordered | 718 oldNodeIdMap[currentNode.key] = null; // mark it reordered |
| 720 assert(root is ContainerRenderObjectMixin); | 719 assert(root is ContainerRenderObjectMixin); |
| 721 assert(old.root is ContainerRenderObjectMixin); | 720 assert(old.root is ContainerRenderObjectMixin); |
| (...skipping 159 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 881 if (root.parent == null) { | 880 if (root.parent == null) { |
| 882 // we haven't attached it yet | 881 // we haven't attached it yet |
| 883 assert(_container.child == null); | 882 assert(_container.child == null); |
| 884 _container.child = root; | 883 _container.child = root; |
| 885 } | 884 } |
| 886 assert(root.parent == _container); | 885 assert(root.parent == _container); |
| 887 } | 886 } |
| 888 | 887 |
| 889 Widget build() => builder(); | 888 Widget build() => builder(); |
| 890 } | 889 } |
| OLD | NEW |