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 |