| 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 library fn; | 5 library fn; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:collection'; | 8 import 'dart:collection'; |
| 9 import 'dart:mirrors'; |
| 9 import 'dart:sky' as sky; | 10 import 'dart:sky' as sky; |
| 10 import 'reflect.dart' as reflect; | 11 import 'reflect.dart' as reflect; |
| 11 import 'layout.dart'; | 12 import 'layout.dart'; |
| 12 | 13 |
| 13 export 'layout.dart' show Style; | 14 export 'layout.dart' show Style; |
| 14 | 15 |
| 15 final sky.Tracing _tracing = sky.window.tracing; | 16 final sky.Tracing _tracing = sky.window.tracing; |
| 16 | 17 |
| 17 final bool _shouldLogRenderDuration = false; | 18 final bool _shouldLogRenderDuration = false; |
| 18 final bool _shouldTrace = false; | 19 final bool _shouldTrace = false; |
| (...skipping 15 matching lines...) Expand all Loading... |
| 34 _key = key == null ? "$runtimeType" : "$runtimeType-$key"; | 35 _key = key == null ? "$runtimeType" : "$runtimeType-$key"; |
| 35 assert(this is App || _inRenderDirtyComponents); // you should not build the
UI tree ahead of time, build it only during build() | 36 assert(this is App || _inRenderDirtyComponents); // you should not build the
UI tree ahead of time, build it only during build() |
| 36 } | 37 } |
| 37 | 38 |
| 38 // Subclasses which implements Nodes that become stateful may return true | 39 // Subclasses which implements Nodes that become stateful may return true |
| 39 // if the |old| node has become stateful and should be retained. | 40 // if the |old| node has become stateful and should be retained. |
| 40 bool _willSync(UINode old) => false; | 41 bool _willSync(UINode old) => false; |
| 41 | 42 |
| 42 bool get interchangeable => false; // if true, then keys can be duplicated | 43 bool get interchangeable => false; // if true, then keys can be duplicated |
| 43 | 44 |
| 44 void _sync(UINode old, RenderCSSContainer host, RenderCSS insertBefore); | 45 void _sync(UINode old, dynamic slot); |
| 46 // 'slot' is the identifier that the parent RenderNodeWrapper uses to know |
| 47 // where to put this descendant |
| 45 | 48 |
| 46 void _remove() { | 49 void _remove() { |
| 47 _defunct = true; | 50 _defunct = true; |
| 48 _root = null; | 51 _root = null; |
| 49 handleRemoved(); | 52 handleRemoved(); |
| 50 } | 53 } |
| 51 void handleRemoved() { } | 54 void handleRemoved() { } |
| 52 | 55 |
| 56 UINode findAncestor(Type targetType) { |
| 57 var ancestor = _parent; |
| 58 while (ancestor != null && !reflectClass(ancestor.runtimeType).isSubtypeOf(r
eflectClass(targetType))) |
| 59 ancestor = ancestor._parent; |
| 60 return ancestor; |
| 61 } |
| 62 |
| 53 int _nodeDepth; | 63 int _nodeDepth; |
| 54 void _ensureDepth() { | 64 void _ensureDepth() { |
| 55 if (_nodeDepth == null) { | 65 if (_nodeDepth == null) { |
| 56 if (_parent != null) { | 66 if (_parent != null) { |
| 57 _parent._ensureDepth(); | 67 _parent._ensureDepth(); |
| 58 _nodeDepth = _parent._nodeDepth + 1; | 68 _nodeDepth = _parent._nodeDepth + 1; |
| 59 } else { | 69 } else { |
| 60 _nodeDepth = 0; | 70 _nodeDepth = 0; |
| 61 } | 71 } |
| 62 } | 72 } |
| (...skipping 15 matching lines...) Expand all Loading... |
| 78 String outString = opString.substring(opString.indexOf('.') + 1); | 88 String outString = opString.substring(opString.indexOf('.') + 1); |
| 79 _trace('_sync($outString) $key'); | 89 _trace('_sync($outString) $key'); |
| 80 } | 90 } |
| 81 | 91 |
| 82 void _removeChild(UINode node) { | 92 void _removeChild(UINode node) { |
| 83 _traceSync(_SyncOperation.REMOVAL, node._key); | 93 _traceSync(_SyncOperation.REMOVAL, node._key); |
| 84 node._remove(); | 94 node._remove(); |
| 85 } | 95 } |
| 86 | 96 |
| 87 // Returns the child which should be retained as the child of this node. | 97 // Returns the child which should be retained as the child of this node. |
| 88 UINode _syncChild(UINode node, UINode oldNode, RenderCSSContainer host, | 98 UINode _syncChild(UINode node, UINode oldNode, dynamic slot) { |
| 89 RenderCSS insertBefore) { | 99 assert(node != null); |
| 90 | |
| 91 assert(oldNode == null || node._key == oldNode._key); | 100 assert(oldNode == null || node._key == oldNode._key); |
| 92 | 101 |
| 93 if (node == oldNode) { | 102 if (node == oldNode) { |
| 94 _traceSync(_SyncOperation.IDENTICAL, node._key); | 103 _traceSync(_SyncOperation.IDENTICAL, node._key); |
| 95 return node; // Nothing to do. Subtrees must be identical. | 104 return node; // Nothing to do. Subtrees must be identical. |
| 96 } | 105 } |
| 97 | 106 |
| 98 // TODO(rafaelw): This eagerly removes the old DOM. It may be that a | 107 // TODO(rafaelw): This eagerly removes the old DOM. It may be that a |
| 99 // new component was built that could re-use some of it. Consider | 108 // new component was built that could re-use some of it. Consider |
| 100 // syncing the new VDOM against the old one. | 109 // syncing the new VDOM against the old one. |
| 101 if (oldNode != null && node._key != oldNode._key) { | 110 if (oldNode != null && node._key != oldNode._key) { |
| 102 _removeChild(oldNode); | 111 _removeChild(oldNode); |
| 103 } | 112 } |
| 104 | 113 |
| 105 if (node._willSync(oldNode)) { | 114 if (node._willSync(oldNode)) { |
| 106 _traceSync(_SyncOperation.STATEFUL, node._key); | 115 _traceSync(_SyncOperation.STATEFUL, node._key); |
| 107 oldNode._sync(node, host, insertBefore); | 116 oldNode._sync(node, slot); |
| 108 node._defunct = true; | 117 node._defunct = true; |
| 109 assert(oldNode._root is RenderCSS); | 118 assert(oldNode._root is RenderCSS); |
| 110 return oldNode; | 119 return oldNode; |
| 111 } | 120 } |
| 112 | 121 |
| 113 assert(!node._defunct); | 122 assert(!node._defunct); |
| 114 node._parent = this; | 123 node._parent = this; |
| 115 | 124 |
| 116 if (oldNode == null) { | 125 if (oldNode == null) { |
| 117 _traceSync(_SyncOperation.INSERTION, node._key); | 126 _traceSync(_SyncOperation.INSERTION, node._key); |
| 118 } else { | 127 } else { |
| 119 _traceSync(_SyncOperation.STATELESS, node._key); | 128 _traceSync(_SyncOperation.STATELESS, node._key); |
| 120 } | 129 } |
| 121 node._sync(oldNode, host, insertBefore); | 130 node._sync(oldNode, slot); |
| 122 if (oldNode != null) | 131 if (oldNode != null) |
| 123 oldNode._defunct = true; | 132 oldNode._defunct = true; |
| 124 | 133 |
| 125 assert(node._root is RenderCSS); | 134 assert(node._root is RenderCSS); |
| 126 return node; | 135 return node; |
| 127 } | 136 } |
| 128 } | 137 } |
| 129 | 138 |
| 130 abstract class ContentNode extends UINode { | 139 abstract class ContentNode extends UINode { |
| 131 UINode content; | 140 UINode content; |
| 132 | 141 |
| 133 ContentNode(UINode content) : this.content = content, super(key: content._key)
; | 142 ContentNode(UINode content) : this.content = content, super(key: content._key)
; |
| 134 | 143 |
| 135 void _sync(UINode old, RenderCSSContainer host, RenderCSS insertBefore) { | 144 void _sync(UINode old, dynamic slot) { |
| 136 UINode oldContent = old == null ? null : (old as ContentNode).content; | 145 UINode oldContent = old == null ? null : (old as ContentNode).content; |
| 137 content = _syncChild(content, oldContent, host, insertBefore); | 146 content = _syncChild(content, oldContent, slot); |
| 138 assert(content._root != null); | 147 assert(content._root != null); |
| 139 _root = content._root; | 148 _root = content._root; |
| 140 } | 149 } |
| 141 | 150 |
| 142 void _remove() { | 151 void _remove() { |
| 143 if (content != null) | 152 if (content != null) |
| 144 _removeChild(content); | 153 _removeChild(content); |
| 145 super._remove(); | 154 super._remove(); |
| 146 } | 155 } |
| 147 } | 156 } |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 259 target = target._parent; | 268 target = target._parent; |
| 260 } | 269 } |
| 261 } | 270 } |
| 262 | 271 |
| 263 static void _ensureDocumentListener(String eventType) { | 272 static void _ensureDocumentListener(String eventType) { |
| 264 if (_registeredEvents.add(eventType)) { | 273 if (_registeredEvents.add(eventType)) { |
| 265 sky.document.addEventListener(eventType, _dispatchEvent); | 274 sky.document.addEventListener(eventType, _dispatchEvent); |
| 266 } | 275 } |
| 267 } | 276 } |
| 268 | 277 |
| 269 void _sync(UINode old, RenderCSSContainer host, RenderCSS insertBefore) { | 278 void _sync(UINode old, dynamic slot) { |
| 270 for (var type in listeners.keys) { | 279 for (var type in listeners.keys) { |
| 271 _ensureDocumentListener(type); | 280 _ensureDocumentListener(type); |
| 272 } | 281 } |
| 273 | 282 super._sync(old, slot); |
| 274 super._sync(old, host, insertBefore); | |
| 275 } | 283 } |
| 276 } | 284 } |
| 277 | 285 |
| 278 /* | 286 /* |
| 279 * RenderNodeWrappers correspond to a desired state of a RenderCSS. | 287 * RenderNodeWrappers correspond to a desired state of a RenderCSS. |
| 280 * They are fully immutable, with one exception: A UINode which is a | 288 * They are fully immutable, with one exception: A UINode which is a |
| 281 * Component which lives within an OneChildListRenderNodeWrapper's | 289 * Component which lives within an OneChildListRenderNodeWrapper's |
| 282 * children list, may be replaced with the "old" instance if it has | 290 * children list, may be replaced with the "old" instance if it has |
| 283 * become stateful. | 291 * become stateful. |
| 284 */ | 292 */ |
| 285 abstract class RenderNodeWrapper extends UINode { | 293 abstract class RenderNodeWrapper extends UINode { |
| 286 | 294 |
| 287 static final Map<RenderCSS, RenderNodeWrapper> _nodeMap = | 295 static final Map<RenderCSS, RenderNodeWrapper> _nodeMap = |
| 288 new HashMap<RenderCSS, RenderNodeWrapper>(); | 296 new HashMap<RenderCSS, RenderNodeWrapper>(); |
| 289 | 297 |
| 290 static RenderNodeWrapper _getMounted(RenderCSS node) => _nodeMap[node]; | 298 static RenderNodeWrapper _getMounted(RenderCSS node) => _nodeMap[node]; |
| 291 | 299 |
| 292 RenderNodeWrapper({ | 300 RenderNodeWrapper({ |
| 293 Object key, | 301 Object key, |
| 294 this.style, | 302 this.style, |
| 295 this.inlineStyle | 303 this.inlineStyle |
| 296 }) : super(key: key); | 304 }) : super(key: key); |
| 297 | 305 |
| 298 final Style style; | 306 final Style style; |
| 299 final String inlineStyle; | 307 final String inlineStyle; |
| 300 | 308 |
| 301 RenderCSS _createNode(); | 309 RenderCSS _createNode(); |
| 302 RenderNodeWrapper get _emptyNode; | 310 RenderNodeWrapper get _emptyNode; |
| 303 | 311 |
| 304 void _sync(UINode old, RenderCSSContainer host, RenderCSS insertBefore) { | 312 void insert(RenderNodeWrapper child, dynamic slot); |
| 313 |
| 314 void _sync(UINode old, dynamic slot) { |
| 305 if (old == null) { | 315 if (old == null) { |
| 306 _root = _createNode(); | 316 _root = _createNode(); |
| 307 assert(_root != null); | 317 assert(_root != null); |
| 308 host.add(_root, before: insertBefore); | 318 var ancestor = findAncestor(RenderNodeWrapper); |
| 319 if (ancestor is RenderNodeWrapper) |
| 320 ancestor.insert(this, slot); |
| 309 old = _emptyNode; | 321 old = _emptyNode; |
| 310 } else { | 322 } else { |
| 311 _root = old._root; | 323 _root = old._root; |
| 312 assert(_root != null); | 324 assert(_root != null); |
| 313 } | 325 } |
| 314 | 326 |
| 315 _nodeMap[_root] = this; | 327 _nodeMap[_root] = this; |
| 316 _syncRenderNode(old); | 328 _syncRenderNode(old); |
| 317 } | 329 } |
| 318 | 330 |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 357 assert(_root != null); | 369 assert(_root != null); |
| 358 _nodeMap.remove(_root); | 370 _nodeMap.remove(_root); |
| 359 super._remove(); | 371 super._remove(); |
| 360 } | 372 } |
| 361 } | 373 } |
| 362 | 374 |
| 363 final List<UINode> _emptyList = new List<UINode>(); | 375 final List<UINode> _emptyList = new List<UINode>(); |
| 364 | 376 |
| 365 abstract class OneChildListRenderNodeWrapper extends RenderNodeWrapper { | 377 abstract class OneChildListRenderNodeWrapper extends RenderNodeWrapper { |
| 366 | 378 |
| 379 // In OneChildListRenderNodeWrapper subclasses, slots are RenderCSS nodes |
| 380 // to use as the "insert before" sibling in RenderCSSContainer.add() calls |
| 381 |
| 367 final List<UINode> children; | 382 final List<UINode> children; |
| 368 | 383 |
| 369 OneChildListRenderNodeWrapper({ | 384 OneChildListRenderNodeWrapper({ |
| 370 Object key, | 385 Object key, |
| 371 List<UINode> children, | 386 List<UINode> children, |
| 372 Style style, | 387 Style style, |
| 373 String inlineStyle | 388 String inlineStyle |
| 374 }) : this.children = children == null ? _emptyList : children, | 389 }) : this.children = children == null ? _emptyList : children, |
| 375 super( | 390 super( |
| 376 key: key, | 391 key: key, |
| 377 style: style, | 392 style: style, |
| 378 inlineStyle: inlineStyle | 393 inlineStyle: inlineStyle |
| 379 ) { | 394 ) { |
| 380 assert(!_debugHasDuplicateIds()); | 395 assert(!_debugHasDuplicateIds()); |
| 381 } | 396 } |
| 382 | 397 |
| 398 void insert(RenderNodeWrapper child, dynamic slot) { |
| 399 assert(slot == null || slot is RenderCSS); |
| 400 _root.add(child._root, before: slot); |
| 401 } |
| 402 |
| 383 void _remove() { | 403 void _remove() { |
| 384 assert(children != null); | 404 assert(children != null); |
| 385 for (var child in children) { | 405 for (var child in children) { |
| 386 assert(child != null); | 406 assert(child != null); |
| 387 _removeChild(child); | 407 _removeChild(child); |
| 388 } | 408 } |
| 389 super._remove(); | 409 super._remove(); |
| 390 } | 410 } |
| 391 | 411 |
| 392 bool _debugHasDuplicateIds() { | 412 bool _debugHasDuplicateIds() { |
| (...skipping 23 matching lines...) Expand all Loading... |
| 416 | 436 |
| 417 var oldChildren = old.children; | 437 var oldChildren = old.children; |
| 418 var oldStartIndex = 0; | 438 var oldStartIndex = 0; |
| 419 var oldEndIndex = oldChildren.length; | 439 var oldEndIndex = oldChildren.length; |
| 420 | 440 |
| 421 RenderCSS nextSibling = null; | 441 RenderCSS nextSibling = null; |
| 422 UINode currentNode = null; | 442 UINode currentNode = null; |
| 423 UINode oldNode = null; | 443 UINode oldNode = null; |
| 424 | 444 |
| 425 void sync(int atIndex) { | 445 void sync(int atIndex) { |
| 426 children[atIndex] = _syncChild(currentNode, oldNode, _root, nextSibling); | 446 children[atIndex] = _syncChild(currentNode, oldNode, nextSibling); |
| 427 assert(children[atIndex] != null); | 447 assert(children[atIndex] != null); |
| 428 } | 448 } |
| 429 | 449 |
| 430 // Scan backwards from end of list while nodes can be directly synced | 450 // Scan backwards from end of list while nodes can be directly synced |
| 431 // without reordering. | 451 // without reordering. |
| 432 while (endIndex > startIndex && oldEndIndex > oldStartIndex) { | 452 while (endIndex > startIndex && oldEndIndex > oldStartIndex) { |
| 433 currentNode = children[endIndex - 1]; | 453 currentNode = children[endIndex - 1]; |
| 434 oldNode = oldChildren[oldEndIndex - 1]; | 454 oldNode = oldChildren[oldEndIndex - 1]; |
| 435 | 455 |
| 436 if (currentNode._key != oldNode._key) { | 456 if (currentNode._key != oldNode._key) { |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 475 return false; // never re-order these nodes | 495 return false; // never re-order these nodes |
| 476 | 496 |
| 477 ensureOldIdMap(); | 497 ensureOldIdMap(); |
| 478 oldNode = oldNodeIdMap[currentNode._key]; | 498 oldNode = oldNodeIdMap[currentNode._key]; |
| 479 if (oldNode == null) | 499 if (oldNode == null) |
| 480 return false; | 500 return false; |
| 481 | 501 |
| 482 oldNodeIdMap[currentNode._key] = null; // mark it reordered | 502 oldNodeIdMap[currentNode._key] = null; // mark it reordered |
| 483 assert(_root is RenderCSSContainer); | 503 assert(_root is RenderCSSContainer); |
| 484 assert(oldNode._root is RenderCSSContainer); | 504 assert(oldNode._root is RenderCSSContainer); |
| 505 |
| 485 old._root.remove(oldNode._root); | 506 old._root.remove(oldNode._root); |
| 486 _root.add(oldNode._root, before: nextSibling); | 507 _root.add(oldNode._root, before: nextSibling); |
| 508 |
| 487 return true; | 509 return true; |
| 488 } | 510 } |
| 489 | 511 |
| 490 // Scan forwards, this time we may re-order; | 512 // Scan forwards, this time we may re-order; |
| 491 nextSibling = _root.firstChild; | 513 nextSibling = _root.firstChild; |
| 492 while (startIndex < endIndex && oldStartIndex < oldEndIndex) { | 514 while (startIndex < endIndex && oldStartIndex < oldEndIndex) { |
| 493 currentNode = children[startIndex]; | 515 currentNode = children[startIndex]; |
| 494 oldNode = oldChildren[oldStartIndex]; | 516 oldNode = oldChildren[oldStartIndex]; |
| 495 | 517 |
| 496 if (currentNode._key == oldNode._key) { | 518 if (currentNode._key == oldNode._key) { |
| (...skipping 276 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 773 bool get _isBuilding => _currentlyBuilding == this; | 795 bool get _isBuilding => _currentlyBuilding == this; |
| 774 bool _dirty = true; | 796 bool _dirty = true; |
| 775 | 797 |
| 776 UINode _built; | 798 UINode _built; |
| 777 final int _order; | 799 final int _order; |
| 778 static int _currentOrder = 0; | 800 static int _currentOrder = 0; |
| 779 bool _stateful; | 801 bool _stateful; |
| 780 static Component _currentlyBuilding; | 802 static Component _currentlyBuilding; |
| 781 List<Function> _mountCallbacks; | 803 List<Function> _mountCallbacks; |
| 782 List<Function> _unmountCallbacks; | 804 List<Function> _unmountCallbacks; |
| 805 dynamic _slot; // cached slot from the last time we were synced |
| 783 | 806 |
| 784 void onDidMount(Function fn) { | 807 void onDidMount(Function fn) { |
| 785 if (_mountCallbacks == null) | 808 if (_mountCallbacks == null) |
| 786 _mountCallbacks = new List<Function>(); | 809 _mountCallbacks = new List<Function>(); |
| 787 | 810 |
| 788 _mountCallbacks.add(fn); | 811 _mountCallbacks.add(fn); |
| 789 } | 812 } |
| 790 | 813 |
| 791 void onDidUnmount(Function fn) { | 814 void onDidUnmount(Function fn) { |
| 792 if (_unmountCallbacks == null) | 815 if (_unmountCallbacks == null) |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 845 } | 868 } |
| 846 | 869 |
| 847 /* There are three cases here: | 870 /* There are three cases here: |
| 848 * 1) Building for the first time: | 871 * 1) Building for the first time: |
| 849 * assert(_built == null && old == null) | 872 * assert(_built == null && old == null) |
| 850 * 2) Re-building (because a dirty flag got set): | 873 * 2) Re-building (because a dirty flag got set): |
| 851 * assert(_built != null && old == null) | 874 * assert(_built != null && old == null) |
| 852 * 3) Syncing against an old version | 875 * 3) Syncing against an old version |
| 853 * assert(_built == null && old != null) | 876 * assert(_built == null && old != null) |
| 854 */ | 877 */ |
| 855 void _sync(UINode old, RenderCSSContainer host, RenderCSS insertBefore) { | 878 void _sync(UINode old, dynamic slot) { |
| 856 assert(!_defunct); | 879 assert(!_defunct); |
| 857 assert(_built == null || old == null); | 880 assert(_built == null || old == null); |
| 858 | 881 |
| 859 Component oldComponent = old as Component; | 882 Component oldComponent = old as Component; |
| 860 | 883 |
| 884 _slot = slot; |
| 885 |
| 861 var oldBuilt; | 886 var oldBuilt; |
| 862 if (oldComponent == null) { | 887 if (oldComponent == null) { |
| 863 oldBuilt = _built; | 888 oldBuilt = _built; |
| 864 } else { | 889 } else { |
| 865 assert(_built == null); | 890 assert(_built == null); |
| 866 oldBuilt = oldComponent._built; | 891 oldBuilt = oldComponent._built; |
| 867 } | 892 } |
| 868 | 893 |
| 869 if (oldBuilt == null) | 894 if (oldBuilt == null) |
| 870 _enqueueDidMount(this); | 895 _enqueueDidMount(this); |
| 871 | 896 |
| 872 int lastOrder = _currentOrder; | 897 int lastOrder = _currentOrder; |
| 873 _currentOrder = _order; | 898 _currentOrder = _order; |
| 874 _currentlyBuilding = this; | 899 _currentlyBuilding = this; |
| 875 _built = build(); | 900 _built = build(); |
| 876 _currentlyBuilding = null; | 901 _currentlyBuilding = null; |
| 877 _currentOrder = lastOrder; | 902 _currentOrder = lastOrder; |
| 878 | 903 |
| 879 _built = _syncChild(_built, oldBuilt, host, insertBefore); | 904 _built = _syncChild(_built, oldBuilt, slot); |
| 880 _dirty = false; | 905 _dirty = false; |
| 881 _root = _built._root; | 906 _root = _built._root; |
| 882 assert(_root != null); | 907 assert(_root != null); |
| 883 } | 908 } |
| 884 | 909 |
| 885 void _buildIfDirty() { | 910 void _buildIfDirty() { |
| 886 if (!_dirty || _defunct) | 911 if (!_dirty || _defunct) |
| 887 return; | 912 return; |
| 888 | 913 |
| 889 _trace('$_key rebuilding...'); | 914 _trace('$_key rebuilding...'); |
| 890 _sync(null, _root.parent, _root.parent.childAfter(_root)); | 915 assert(_root != null); |
| 916 _sync(null, _slot); |
| 891 } | 917 } |
| 892 | 918 |
| 893 void scheduleBuild() { | 919 void scheduleBuild() { |
| 894 setState(() {}); | 920 setState(() {}); |
| 895 } | 921 } |
| 896 | 922 |
| 897 void setState(Function fn()) { | 923 void setState(Function fn()) { |
| 898 _stateful = true; | 924 _stateful = true; |
| 899 fn(); | 925 fn(); |
| 900 if (_isBuilding || _dirty || _defunct) | 926 if (_isBuilding || _dirty || _defunct) |
| (...skipping 12 matching lines...) Expand all Loading... |
| 913 App() : super(stateful: true) { | 939 App() : super(stateful: true) { |
| 914 _host = new RenderCSSRoot(this); | 940 _host = new RenderCSSRoot(this); |
| 915 _scheduleComponentForRender(this); | 941 _scheduleComponentForRender(this); |
| 916 } | 942 } |
| 917 | 943 |
| 918 void _buildIfDirty() { | 944 void _buildIfDirty() { |
| 919 if (!_dirty || _defunct) | 945 if (!_dirty || _defunct) |
| 920 return; | 946 return; |
| 921 | 947 |
| 922 _trace('$_key rebuilding...'); | 948 _trace('$_key rebuilding...'); |
| 923 _sync(null, _host, _root); | 949 _sync(null, null); |
| 950 if (_root.parent == null) |
| 951 _host.add(_root); |
| 952 assert(_root.parent == _host); |
| 924 } | 953 } |
| 925 } | 954 } |
| 926 | 955 |
| 927 class Text extends Component { | 956 class Text extends Component { |
| 928 Text(this.data) : super(key: '*text*'); | 957 Text(this.data) : super(key: '*text*'); |
| 929 final String data; | 958 final String data; |
| 930 bool get interchangeable => true; | 959 bool get interchangeable => true; |
| 931 UINode build() => new Paragraph(children: [new TextFragment(data)]); | 960 UINode build() => new Paragraph(children: [new TextFragment(data)]); |
| 932 } | 961 } |
| OLD | NEW |