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'; |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
83 // This is called immediately before _sync(). | 83 // This is called immediately before _sync(). |
84 // Component._retainStatefulNodeIfPossible() calls syncFields(). | 84 // Component._retainStatefulNodeIfPossible() calls syncFields(). |
85 bool _retainStatefulNodeIfPossible(Widget old) => false; | 85 bool _retainStatefulNodeIfPossible(Widget old) => false; |
86 | 86 |
87 bool get interchangeable => false; // if true, then keys can be duplicated | 87 bool get interchangeable => false; // if true, then keys can be duplicated |
88 | 88 |
89 void _sync(Widget old, dynamic slot); | 89 void _sync(Widget old, dynamic slot); |
90 // 'slot' is the identifier that the parent RenderObjectWrapper uses to know | 90 // 'slot' is the identifier that the parent RenderObjectWrapper uses to know |
91 // where to put this descendant | 91 // where to put this descendant |
92 | 92 |
93 void remove() { | |
94 _root = null; | |
95 setParent(null); | |
96 } | |
97 | |
98 Widget findAncestor(Type targetType) { | 93 Widget findAncestor(Type targetType) { |
99 var ancestor = _parent; | 94 var ancestor = _parent; |
100 while (ancestor != null && !reflectClass(ancestor.runtimeType).isSubtypeOf(r
eflectClass(targetType))) | 95 while (ancestor != null && !reflectClass(ancestor.runtimeType).isSubtypeOf(r
eflectClass(targetType))) |
101 ancestor = ancestor._parent; | 96 ancestor = ancestor._parent; |
102 return ancestor; | 97 return ancestor; |
103 } | 98 } |
104 | 99 |
| 100 void remove() { |
| 101 _root = null; |
| 102 setParent(null); |
| 103 } |
| 104 |
105 void removeChild(Widget node) { | 105 void removeChild(Widget node) { |
106 node.remove(); | 106 node.remove(); |
107 } | 107 } |
108 | 108 |
| 109 void detachRoot(); |
| 110 |
109 // Returns the child which should be retained as the child of this node. | 111 // Returns the child which should be retained as the child of this node. |
110 Widget syncChild(Widget node, Widget oldNode, dynamic slot) { | 112 Widget syncChild(Widget node, Widget oldNode, dynamic slot) { |
111 | 113 |
112 assert(oldNode is! Component || !oldNode._disqualifiedFromEverAppearingAgain
); | 114 assert(oldNode is! Component || !oldNode._disqualifiedFromEverAppearingAgain
); |
113 | 115 |
114 if (node == oldNode) { | 116 if (node == oldNode) { |
115 assert(node == null || node.mounted); | 117 assert(node == null || node.mounted); |
| 118 assert(node is! RenderObjectWrapper || node._ancestor != null); |
116 return node; // Nothing to do. Subtrees must be identical. | 119 return node; // Nothing to do. Subtrees must be identical. |
117 } | 120 } |
118 | 121 |
119 if (node == null) { | 122 if (node == null) { |
120 // the child in this slot has gone away | 123 // the child in this slot has gone away |
121 assert(oldNode.mounted); | 124 assert(oldNode.mounted); |
| 125 oldNode.detachRoot(); |
122 removeChild(oldNode); | 126 removeChild(oldNode); |
123 assert(!oldNode.mounted); | 127 assert(!oldNode.mounted); |
124 return null; | 128 return null; |
125 } | 129 } |
126 | 130 |
127 if (oldNode != null && | 131 if (oldNode != null && |
128 oldNode.runtimeType == node.runtimeType && | 132 oldNode.runtimeType == node.runtimeType && |
129 oldNode.key == node.key && | 133 oldNode.key == node.key && |
130 node._retainStatefulNodeIfPossible(oldNode)) { | 134 node._retainStatefulNodeIfPossible(oldNode)) { |
131 assert(oldNode.mounted); | 135 assert(oldNode.mounted); |
132 assert(!node.mounted); | 136 assert(!node.mounted); |
133 oldNode._sync(node, slot); | 137 oldNode._sync(node, slot); |
134 assert(oldNode.root is RenderObject); | 138 assert(oldNode.root is RenderObject); |
135 return oldNode; | 139 return oldNode; |
136 } | 140 } |
137 | 141 |
138 if (oldNode != null && | 142 if (oldNode != null && |
139 (oldNode.runtimeType != node.runtimeType || oldNode.key != node.key)) { | 143 (oldNode.runtimeType != node.runtimeType || oldNode.key != node.key)) { |
140 assert(oldNode.mounted); | 144 assert(oldNode.mounted); |
| 145 oldNode.detachRoot(); |
141 removeChild(oldNode); | 146 removeChild(oldNode); |
142 oldNode = null; | 147 oldNode = null; |
143 } | 148 } |
144 | 149 |
145 assert(!node.mounted); | 150 assert(!node.mounted); |
146 node.setParent(this); | 151 node.setParent(this); |
147 node._sync(oldNode, slot); | 152 node._sync(oldNode, slot); |
148 assert(node.root is RenderObject); | 153 assert(node.root is RenderObject); |
149 return node; | 154 return node; |
150 } | 155 } |
| 156 |
| 157 String toString() => '$runtimeType($key)'; |
| 158 |
151 } | 159 } |
152 | 160 |
153 | 161 |
154 // Descendants of TagNode provide a way to tag RenderObjectWrapper and | 162 // Descendants of TagNode provide a way to tag RenderObjectWrapper and |
155 // Component nodes with annotations, such as event listeners, | 163 // Component nodes with annotations, such as event listeners, |
156 // stylistic information, etc. | 164 // stylistic information, etc. |
157 abstract class TagNode extends Widget { | 165 abstract class TagNode extends Widget { |
158 | 166 |
159 TagNode(Widget content, { String key }) | 167 TagNode(Widget content, { String key }) |
160 : this.content = content, super(key: key); | 168 : this.content = content, super(key: key); |
161 | 169 |
162 Widget content; | 170 Widget content; |
163 | 171 |
164 void _sync(Widget old, dynamic slot) { | 172 void _sync(Widget old, dynamic slot) { |
165 Widget oldContent = old == null ? null : (old as TagNode).content; | 173 Widget oldContent = old == null ? null : (old as TagNode).content; |
166 content = syncChild(content, oldContent, slot); | 174 content = syncChild(content, oldContent, slot); |
167 assert(content.root != null); | 175 assert(content.root != null); |
168 _root = content.root; | 176 _root = content.root; |
169 assert(_root == root); // in case a subclass reintroduces it | 177 assert(_root == root); // in case a subclass reintroduces it |
170 } | 178 } |
171 | 179 |
172 void remove() { | 180 void remove() { |
173 if (content != null) | 181 if (content != null) |
174 removeChild(content); | 182 removeChild(content); |
175 super.remove(); | 183 super.remove(); |
176 } | 184 } |
177 | 185 |
| 186 void detachRoot() { |
| 187 if (content != null) |
| 188 content.detachRoot(); |
| 189 } |
| 190 |
178 } | 191 } |
179 | 192 |
180 class ParentDataNode extends TagNode { | 193 class ParentDataNode extends TagNode { |
181 ParentDataNode(Widget content, this.parentData, { String key }) | 194 ParentDataNode(Widget content, this.parentData, { String key }) |
182 : super(content, key: key); | 195 : super(content, key: key); |
183 final ParentData parentData; | 196 final ParentData parentData; |
184 } | 197 } |
185 | 198 |
186 typedef void GestureEventListener(sky.GestureEvent e); | 199 typedef void GestureEventListener(sky.GestureEvent e); |
187 typedef void PointerEventListener(sky.PointerEvent e); | 200 typedef void PointerEventListener(sky.PointerEvent e); |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
298 } | 311 } |
299 | 312 |
300 void remove() { | 313 void remove() { |
301 assert(_built != null); | 314 assert(_built != null); |
302 assert(root != null); | 315 assert(root != null); |
303 removeChild(_built); | 316 removeChild(_built); |
304 _built = null; | 317 _built = null; |
305 super.remove(); | 318 super.remove(); |
306 } | 319 } |
307 | 320 |
| 321 void detachRoot() { |
| 322 assert(_built != null); |
| 323 assert(root != null); |
| 324 _built.detachRoot(); |
| 325 } |
| 326 |
308 bool _retainStatefulNodeIfPossible(Widget old) { | 327 bool _retainStatefulNodeIfPossible(Widget old) { |
309 assert(!_disqualifiedFromEverAppearingAgain); | 328 assert(!_disqualifiedFromEverAppearingAgain); |
310 | 329 |
311 Component oldComponent = old as Component; | 330 Component oldComponent = old as Component; |
312 if (oldComponent == null || !oldComponent._stateful) | 331 if (oldComponent == null || !oldComponent._stateful) |
313 return false; | 332 return false; |
314 | 333 |
315 assert(runtimeType == oldComponent.runtimeType); | 334 assert(runtimeType == oldComponent.runtimeType); |
316 assert(key == oldComponent.key); | 335 assert(key == oldComponent.key); |
317 | 336 |
(...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
459 // They are fully immutable, with one exception: A Widget which is a | 478 // They are fully immutable, with one exception: A Widget which is a |
460 // Component which lives within an MultiChildRenderObjectWrapper's | 479 // Component which lives within an MultiChildRenderObjectWrapper's |
461 // children list, may be replaced with the "old" instance if it has | 480 // children list, may be replaced with the "old" instance if it has |
462 // become stateful. | 481 // become stateful. |
463 abstract class RenderObjectWrapper extends Widget { | 482 abstract class RenderObjectWrapper extends Widget { |
464 | 483 |
465 RenderObjectWrapper({ String key }) : super(key: key); | 484 RenderObjectWrapper({ String key }) : super(key: key); |
466 | 485 |
467 RenderObject createNode(); | 486 RenderObject createNode(); |
468 | 487 |
469 void insert(RenderObjectWrapper child, dynamic slot); | |
470 | |
471 static final Map<RenderObject, RenderObjectWrapper> _nodeMap = | 488 static final Map<RenderObject, RenderObjectWrapper> _nodeMap = |
472 new HashMap<RenderObject, RenderObjectWrapper>(); | 489 new HashMap<RenderObject, RenderObjectWrapper>(); |
| 490 static RenderObjectWrapper _getMounted(RenderObject node) => _nodeMap[node]; |
473 | 491 |
474 static RenderObjectWrapper _getMounted(RenderObject node) => _nodeMap[node]; | 492 RenderObjectWrapper _ancestor; |
| 493 void insert(RenderObjectWrapper child, dynamic slot); |
| 494 void detachChildRoot(RenderObjectWrapper child); |
475 | 495 |
476 void _sync(Widget old, dynamic slot) { | 496 void _sync(Widget old, dynamic slot) { |
477 // TODO(abarth): We should split RenderObjectWrapper into two pieces so that | 497 // TODO(abarth): We should split RenderObjectWrapper into two pieces so that |
478 // RenderViewObject doesn't need to inherit all this code it | 498 // RenderViewObject doesn't need to inherit all this code it |
479 // doesn't need. | 499 // doesn't need. |
480 assert(parent != null || this is RenderViewWrapper); | 500 assert(parent != null || this is RenderViewWrapper); |
481 if (old == null) { | 501 if (old == null) { |
482 _root = createNode(); | 502 _root = createNode(); |
483 var ancestor = findAncestor(RenderObjectWrapper); | 503 _ancestor = findAncestor(RenderObjectWrapper); |
484 if (ancestor is RenderObjectWrapper) | 504 if (_ancestor is RenderObjectWrapper) |
485 ancestor.insert(this, slot); | 505 _ancestor.insert(this, slot); |
| 506 else |
| 507 print("$this has no ancestor"); |
486 } else { | 508 } else { |
487 _root = old.root; | 509 _root = old.root; |
| 510 _ancestor = old._ancestor; |
488 } | 511 } |
489 assert(_root == root); // in case a subclass reintroduces it | 512 assert(_root == root); // in case a subclass reintroduces it |
490 assert(root != null); | 513 assert(root != null); |
491 assert(mounted); | 514 assert(mounted); |
492 _nodeMap[root] = this; | 515 _nodeMap[root] = this; |
493 syncRenderObject(old); | 516 syncRenderObject(old); |
494 } | 517 } |
495 | 518 |
496 void syncRenderObject(RenderObjectWrapper old) { | 519 void syncRenderObject(RenderObjectWrapper old) { |
497 ParentData parentData = null; | 520 ParentData parentData = null; |
(...skipping 13 matching lines...) Expand all Loading... |
511 if (parent.root != null) | 534 if (parent.root != null) |
512 parent.root.markNeedsLayout(); | 535 parent.root.markNeedsLayout(); |
513 } | 536 } |
514 } | 537 } |
515 | 538 |
516 void remove() { | 539 void remove() { |
517 assert(root != null); | 540 assert(root != null); |
518 _nodeMap.remove(root); | 541 _nodeMap.remove(root); |
519 super.remove(); | 542 super.remove(); |
520 } | 543 } |
| 544 |
| 545 void detachRoot() { |
| 546 assert(_ancestor != null); |
| 547 assert(root != null); |
| 548 _ancestor.detachChildRoot(this); |
| 549 } |
| 550 |
521 } | 551 } |
522 | 552 |
523 abstract class OneChildRenderObjectWrapper extends RenderObjectWrapper { | 553 abstract class OneChildRenderObjectWrapper extends RenderObjectWrapper { |
524 | 554 |
525 OneChildRenderObjectWrapper({ String key, Widget child }) | 555 OneChildRenderObjectWrapper({ String key, Widget child }) |
526 : _child = child, super(key: key); | 556 : _child = child, super(key: key); |
527 | 557 |
528 Widget _child; | 558 Widget _child; |
529 Widget get child => _child; | 559 Widget get child => _child; |
530 | 560 |
531 void syncRenderObject(RenderObjectWrapper old) { | 561 void syncRenderObject(RenderObjectWrapper old) { |
532 super.syncRenderObject(old); | 562 super.syncRenderObject(old); |
533 Widget oldChild = old == null ? null : (old as OneChildRenderObjectWrapper).
child; | 563 Widget oldChild = old == null ? null : (old as OneChildRenderObjectWrapper).
child; |
534 _child = syncChild(child, oldChild, null); | 564 _child = syncChild(child, oldChild, null); |
535 } | 565 } |
536 | 566 |
537 void insert(RenderObjectWrapper child, dynamic slot) { | 567 void insert(RenderObjectWrapper child, dynamic slot) { |
538 final root = this.root; // TODO(ianh): Remove this once the analyzer is clev
erer | 568 final root = this.root; // TODO(ianh): Remove this once the analyzer is clev
erer |
| 569 assert(root is RenderObjectWithChildMixin); |
539 assert(slot == null); | 570 assert(slot == null); |
540 assert(root is RenderObjectWithChildMixin); | |
541 root.child = child.root; | 571 root.child = child.root; |
542 assert(root == this.root); // TODO(ianh): Remove this once the analyzer is c
leverer | 572 assert(root == this.root); // TODO(ianh): Remove this once the analyzer is c
leverer |
543 } | 573 } |
544 | 574 |
545 void removeChild(Widget node) { | 575 void detachChildRoot(RenderObjectWrapper child) { |
546 final root = this.root; // TODO(ianh): Remove this once the analyzer is clev
erer | 576 final root = this.root; // TODO(ianh): Remove this once the analyzer is clev
erer |
547 assert(root is RenderObjectWithChildMixin); | 577 assert(root is RenderObjectWithChildMixin); |
| 578 assert(root.child == child.root); |
548 root.child = null; | 579 root.child = null; |
549 super.removeChild(node); | |
550 assert(root == this.root); // TODO(ianh): Remove this once the analyzer is c
leverer | 580 assert(root == this.root); // TODO(ianh): Remove this once the analyzer is c
leverer |
551 } | 581 } |
552 | 582 |
553 void remove() { | 583 void remove() { |
554 if (child != null) | 584 if (child != null) |
555 removeChild(child); | 585 removeChild(child); |
556 super.remove(); | 586 super.remove(); |
557 } | 587 } |
558 | 588 |
559 } | 589 } |
(...skipping 12 matching lines...) Expand all Loading... |
572 final List<Widget> children; | 602 final List<Widget> children; |
573 | 603 |
574 void insert(RenderObjectWrapper child, dynamic slot) { | 604 void insert(RenderObjectWrapper child, dynamic slot) { |
575 final root = this.root; // TODO(ianh): Remove this once the analyzer is clev
erer | 605 final root = this.root; // TODO(ianh): Remove this once the analyzer is clev
erer |
576 assert(slot == null || slot is RenderObject); | 606 assert(slot == null || slot is RenderObject); |
577 assert(root is ContainerRenderObjectMixin); | 607 assert(root is ContainerRenderObjectMixin); |
578 root.add(child.root, before: slot); | 608 root.add(child.root, before: slot); |
579 assert(root == this.root); // TODO(ianh): Remove this once the analyzer is c
leverer | 609 assert(root == this.root); // TODO(ianh): Remove this once the analyzer is c
leverer |
580 } | 610 } |
581 | 611 |
582 void removeChild(Widget node) { | 612 void detachChildRoot(RenderObjectWrapper child) { |
583 final root = this.root; // TODO(ianh): Remove this once the analyzer is clev
erer | 613 final root = this.root; // TODO(ianh): Remove this once the analyzer is clev
erer |
584 assert(root is ContainerRenderObjectMixin); | 614 assert(root is ContainerRenderObjectMixin); |
585 assert(node.root.parent == root); | 615 assert(child.root.parent == root); |
586 root.remove(node.root); | 616 root.remove(child.root); |
587 super.removeChild(node); | |
588 assert(root == this.root); // TODO(ianh): Remove this once the analyzer is c
leverer | 617 assert(root == this.root); // TODO(ianh): Remove this once the analyzer is c
leverer |
589 } | 618 } |
590 | 619 |
591 void remove() { | 620 void remove() { |
592 assert(children != null); | 621 assert(children != null); |
593 for (var child in children) { | 622 for (var child in children) { |
594 assert(child != null); | 623 assert(child != null); |
595 removeChild(child); | 624 removeChild(child); |
596 } | 625 } |
597 super.remove(); | 626 super.remove(); |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
640 currentNode = children[endIndex - 1]; | 669 currentNode = children[endIndex - 1]; |
641 oldNode = oldChildren[oldEndIndex - 1]; | 670 oldNode = oldChildren[oldEndIndex - 1]; |
642 | 671 |
643 if (currentNode.runtimeType != oldNode.runtimeType || currentNode.key != o
ldNode.key) { | 672 if (currentNode.runtimeType != oldNode.runtimeType || currentNode.key != o
ldNode.key) { |
644 break; | 673 break; |
645 } | 674 } |
646 | 675 |
647 endIndex--; | 676 endIndex--; |
648 oldEndIndex--; | 677 oldEndIndex--; |
649 sync(endIndex); | 678 sync(endIndex); |
| 679 nextSibling = children[endIndex].root; |
650 } | 680 } |
651 | 681 |
652 HashMap<String, Widget> oldNodeIdMap = null; | 682 HashMap<String, Widget> oldNodeIdMap = null; |
653 | 683 |
654 bool oldNodeReordered(String key) { | 684 bool oldNodeReordered(String key) { |
655 return oldNodeIdMap != null && | 685 return oldNodeIdMap != null && |
656 oldNodeIdMap.containsKey(key) && | 686 oldNodeIdMap.containsKey(key) && |
657 oldNodeIdMap[key] == null; | 687 oldNodeIdMap[key] == null; |
658 } | 688 } |
659 | 689 |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
722 while (startIndex < endIndex) { | 752 while (startIndex < endIndex) { |
723 currentNode = children[startIndex]; | 753 currentNode = children[startIndex]; |
724 sync(startIndex); | 754 sync(startIndex); |
725 startIndex++; | 755 startIndex++; |
726 } | 756 } |
727 | 757 |
728 // Removals | 758 // Removals |
729 currentNode = null; | 759 currentNode = null; |
730 while (oldStartIndex < oldEndIndex) { | 760 while (oldStartIndex < oldEndIndex) { |
731 oldNode = oldChildren[oldStartIndex]; | 761 oldNode = oldChildren[oldStartIndex]; |
732 removeChild(oldNode); | 762 syncChild(null, oldNode, null); |
733 advanceOldStartIndex(); | 763 advanceOldStartIndex(); |
734 } | 764 } |
735 | 765 |
736 assert(root == this.root); // TODO(ianh): Remove this once the analyzer is c
leverer | 766 assert(root == this.root); // TODO(ianh): Remove this once the analyzer is c
leverer |
737 } | 767 } |
738 | 768 |
739 } | 769 } |
740 | 770 |
741 class WidgetAppView extends AppView { | 771 class WidgetAppView extends AppView { |
742 | 772 |
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
851 if (root.parent == null) { | 881 if (root.parent == null) { |
852 // we haven't attached it yet | 882 // we haven't attached it yet |
853 assert(_container.child == null); | 883 assert(_container.child == null); |
854 _container.child = root; | 884 _container.child = root; |
855 } | 885 } |
856 assert(root.parent == _container); | 886 assert(root.parent == _container); |
857 } | 887 } |
858 | 888 |
859 Widget build() => builder(); | 889 Widget build() => builder(); |
860 } | 890 } |
OLD | NEW |