| 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 |