Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(54)

Side by Side Diff: sky/sdk/lib/widgets/widget.dart

Issue 1182463006: Clean up how we remove nodes from the render tree, and make sure we reinsert them in the right plac… (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « sky/sdk/lib/widgets/scaffold.dart ('k') | sky/tests/widgets/syncs1.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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
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
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 }
OLDNEW
« no previous file with comments | « sky/sdk/lib/widgets/scaffold.dart ('k') | sky/tests/widgets/syncs1.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698