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

Side by Side Diff: sky/framework/fn.dart

Issue 1132063007: Rationalize Dart mojo and sky package structure (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: Created 5 years, 7 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
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 library fn;
6
7 import 'dart:async';
8 import 'dart:collection';
9 import 'dart:sky' as sky;
10 import 'reflect.dart' as reflect;
11 import 'layout.dart';
12
13 export 'layout.dart' show Style;
14
15 final sky.Tracing _tracing = sky.window.tracing;
16
17 final bool _shouldLogRenderDuration = false;
18 final bool _shouldTrace = false;
19
20 enum _SyncOperation { IDENTICAL, INSERTION, STATEFUL, STATELESS, REMOVAL }
21
22 /*
23 * All Effen nodes derive from UINode. All nodes have a _parent, a _key and
24 * can be sync'd.
25 */
26 abstract class UINode {
27 String _key;
28 UINode _parent;
29 UINode get parent => _parent;
30 RenderCSS _root;
31 bool _defunct = false;
32
33 UINode({ Object key }) {
34 _key = key == null ? "$runtimeType" : "$runtimeType-$key";
35 }
36
37 // Subclasses which implements Nodes that become stateful may return true
38 // if the |old| node has become stateful and should be retained.
39 bool _willSync(UINode old) => false;
40
41 bool get interchangeable => false; // if true, then keys can be duplicated
42
43 void _sync(UINode old, RenderCSSContainer host, RenderCSS insertBefore);
44
45 void _remove() {
46 _defunct = true;
47 _root = null;
48 handleRemoved();
49 }
50 void handleRemoved() { }
51
52 int _nodeDepth;
53 void _ensureDepth() {
54 if (_nodeDepth == null) {
55 if (_parent != null) {
56 _parent._ensureDepth();
57 _nodeDepth = _parent._nodeDepth + 1;
58 } else {
59 _nodeDepth = 0;
60 }
61 }
62 }
63
64 void _trace(String message) {
65 if (!_shouldTrace)
66 return;
67
68 _ensureDepth();
69 print((' ' * _nodeDepth) + message);
70 }
71
72 void _traceSync(_SyncOperation op, String key) {
73 if (!_shouldTrace)
74 return;
75
76 String opString = op.toString().toLowerCase();
77 String outString = opString.substring(opString.indexOf('.') + 1);
78 _trace('_sync($outString) $key');
79 }
80
81 void _removeChild(UINode node) {
82 _traceSync(_SyncOperation.REMOVAL, node._key);
83 node._remove();
84 }
85
86 // Returns the child which should be retained as the child of this node.
87 UINode _syncChild(UINode node, UINode oldNode, RenderCSSContainer host,
88 RenderCSS insertBefore) {
89
90 assert(oldNode == null || node._key == oldNode._key);
91
92 if (node == oldNode) {
93 _traceSync(_SyncOperation.IDENTICAL, node._key);
94 return node; // Nothing to do. Subtrees must be identical.
95 }
96
97 // TODO(rafaelw): This eagerly removes the old DOM. It may be that a
98 // new component was built that could re-use some of it. Consider
99 // syncing the new VDOM against the old one.
100 if (oldNode != null && node._key != oldNode._key) {
101 _removeChild(oldNode);
102 }
103
104 if (node._willSync(oldNode)) {
105 _traceSync(_SyncOperation.STATEFUL, node._key);
106 oldNode._sync(node, host, insertBefore);
107 node._defunct = true;
108 assert(oldNode._root is RenderCSS);
109 return oldNode;
110 }
111
112 node._parent = this;
113
114 if (oldNode == null) {
115 _traceSync(_SyncOperation.INSERTION, node._key);
116 } else {
117 _traceSync(_SyncOperation.STATELESS, node._key);
118 }
119 node._sync(oldNode, host, insertBefore);
120 if (oldNode != null)
121 oldNode._defunct = true;
122
123 assert(node._root is RenderCSS);
124 return node;
125 }
126 }
127
128 abstract class ContentNode extends UINode {
129 UINode content;
130
131 ContentNode(UINode content) : this.content = content, super(key: content._key) ;
132
133 void _sync(UINode old, RenderCSSContainer host, RenderCSS insertBefore) {
134 UINode oldContent = old == null ? null : (old as ContentNode).content;
135 content = _syncChild(content, oldContent, host, insertBefore);
136 assert(content._root != null);
137 _root = content._root;
138 }
139
140 void _remove() {
141 if (content != null)
142 _removeChild(content);
143 super._remove();
144 }
145 }
146
147 class StyleNode extends ContentNode {
148 final Style style;
149
150 StyleNode(UINode content, this.style): super(content);
151 }
152
153 class ParentDataNode extends ContentNode {
154 final ParentData parentData;
155
156 ParentDataNode(UINode content, this.parentData): super(content);
157 }
158
159 /*
160 * SkyNodeWrappers correspond to a desired state of a RenderCSS. They are fully
161 * immutable, with one exception: A UINode which is a Component which lives with in
162 * an SkyElementWrapper's children list, may be replaced with the "old" instance if it
163 * has become stateful.
164 */
165 abstract class SkyNodeWrapper extends UINode {
166
167 static final Map<RenderCSS, SkyNodeWrapper> _nodeMap =
168 new HashMap<RenderCSS, SkyNodeWrapper>();
169
170 static SkyNodeWrapper _getMounted(RenderCSS node) => _nodeMap[node];
171
172 SkyNodeWrapper({ Object key }) : super(key: key);
173
174 SkyNodeWrapper get _emptyNode;
175
176 RenderCSS _createNode();
177
178 void _sync(UINode old, RenderCSSContainer host, RenderCSS insertBefore) {
179 if (old == null) {
180 _root = _createNode();
181 assert(_root != null);
182 host.add(_root, before: insertBefore);
183 old = _emptyNode;
184 } else {
185 _root = old._root;
186 assert(_root != null);
187 }
188
189 _nodeMap[_root] = this;
190 _syncNode(old);
191 }
192
193 void _syncNode(SkyNodeWrapper old);
194
195 void _removeChild(UINode node) {
196 assert(_root is RenderCSSContainer);
197 _root.remove(node._root);
198 super._removeChild(node);
199 }
200
201 void _remove() {
202 assert(_root != null);
203 _nodeMap.remove(_root);
204 super._remove();
205 }
206 }
207
208 typedef GestureEventListener(sky.GestureEvent e);
209 typedef PointerEventListener(sky.PointerEvent e);
210 typedef EventListener(sky.Event e);
211
212 class EventListenerNode extends ContentNode {
213 final Map<String, sky.EventListener> listeners;
214
215 static final Set<String> _registeredEvents = new HashSet<String>();
216
217 static Map<String, sky.EventListener> _createListeners({
218 EventListener onWheel,
219 GestureEventListener onGestureFlingCancel,
220 GestureEventListener onGestureFlingStart,
221 GestureEventListener onGestureScrollStart,
222 GestureEventListener onGestureScrollUpdate,
223 GestureEventListener onGestureTap,
224 GestureEventListener onGestureTapDown,
225 PointerEventListener onPointerCancel,
226 PointerEventListener onPointerDown,
227 PointerEventListener onPointerMove,
228 PointerEventListener onPointerUp,
229 Map<String, sky.EventListener> custom
230 }) {
231 var listeners = custom != null ?
232 new HashMap<String, sky.EventListener>.from(custom) :
233 new HashMap<String, sky.EventListener>();
234
235 if (onWheel != null)
236 listeners['wheel'] = onWheel;
237 if (onGestureFlingCancel != null)
238 listeners['gestureflingcancel'] = onGestureFlingCancel;
239 if (onGestureFlingStart != null)
240 listeners['gestureflingstart'] = onGestureFlingStart;
241 if (onGestureScrollStart != null)
242 listeners['gesturescrollstart'] = onGestureScrollStart;
243 if (onGestureScrollUpdate != null)
244 listeners['gesturescrollupdate'] = onGestureScrollUpdate;
245 if (onGestureTap != null)
246 listeners['gesturetap'] = onGestureTap;
247 if (onGestureTapDown != null)
248 listeners['gesturetapdown'] = onGestureTapDown;
249 if (onPointerCancel != null)
250 listeners['pointercancel'] = onPointerCancel;
251 if (onPointerDown != null)
252 listeners['pointerdown'] = onPointerDown;
253 if (onPointerMove != null)
254 listeners['pointermove'] = onPointerMove;
255 if (onPointerUp != null)
256 listeners['pointerup'] = onPointerUp;
257
258 return listeners;
259 }
260
261 EventListenerNode(UINode content, {
262 EventListener onWheel,
263 GestureEventListener onGestureFlingCancel,
264 GestureEventListener onGestureFlingStart,
265 GestureEventListener onGestureScrollStart,
266 GestureEventListener onGestureScrollUpdate,
267 GestureEventListener onGestureTap,
268 GestureEventListener onGestureTapDown,
269 PointerEventListener onPointerCancel,
270 PointerEventListener onPointerDown,
271 PointerEventListener onPointerMove,
272 PointerEventListener onPointerUp,
273 Map<String, sky.EventListener> custom
274 }) : listeners = _createListeners(
275 onWheel: onWheel,
276 onGestureFlingCancel: onGestureFlingCancel,
277 onGestureFlingStart: onGestureFlingStart,
278 onGestureScrollUpdate: onGestureScrollUpdate,
279 onGestureScrollStart: onGestureScrollStart,
280 onGestureTap: onGestureTap,
281 onGestureTapDown: onGestureTapDown,
282 onPointerCancel: onPointerCancel,
283 onPointerDown: onPointerDown,
284 onPointerMove: onPointerMove,
285 onPointerUp: onPointerUp,
286 custom: custom
287 ),
288 super(content);
289
290 void _handleEvent(sky.Event e) {
291 sky.EventListener listener = listeners[e.type];
292 if (listener != null) {
293 listener(e);
294 }
295 }
296
297 static void _dispatchEvent(sky.Event e) {
298 UINode target = SkyNodeWrapper._getMounted(bridgeEventTargetToRenderNode(e.t arget));
299
300 // TODO(rafaelw): StopPropagation?
301 while (target != null) {
302 if (target is EventListenerNode) {
303 target._handleEvent(e);
304 }
305
306 target = target._parent;
307 }
308 }
309
310 static void _ensureDocumentListener(String eventType) {
311 if (_registeredEvents.add(eventType)) {
312 sky.document.addEventListener(eventType, _dispatchEvent);
313 }
314 }
315
316 void _sync(UINode old, RenderCSSContainer host, RenderCSS insertBefore) {
317 for (var type in listeners.keys) {
318 _ensureDocumentListener(type);
319 }
320
321 super._sync(old, host, insertBefore);
322 }
323 }
324
325 final List<UINode> _emptyList = new List<UINode>();
326
327 abstract class SkyElementWrapper extends SkyNodeWrapper {
328
329 final List<UINode> children;
330 final Style style;
331 final String inlineStyle;
332
333 SkyElementWrapper({
334 Object key,
335 List<UINode> children,
336 this.style,
337 this.inlineStyle
338 }) : this.children = children == null ? _emptyList : children,
339 super(key: key) {
340
341 assert(!_debugHasDuplicateIds());
342 }
343
344 void _remove() {
345 assert(children != null);
346 for (var child in children) {
347 assert(child != null);
348 _removeChild(child);
349 }
350 super._remove();
351 }
352
353 bool _debugHasDuplicateIds() {
354 var idSet = new HashSet<String>();
355 for (var child in children) {
356 assert(child != null);
357 if (child.interchangeable)
358 continue; // when these nodes are reordered, we just reassign the data
359
360 if (!idSet.add(child._key)) {
361 throw '''If multiple non-interchangeable nodes of the same type exist as children
362 of another node, they must have unique keys.
363 Duplicate: "${child._key}"''';
364 }
365 }
366 return false;
367 }
368
369 void _syncNode(SkyNodeWrapper old) {
370 SkyElementWrapper oldSkyElementWrapper = old as SkyElementWrapper;
371
372 List<Style> styles = new List<Style>();
373 if (style != null)
374 styles.add(style);
375 ParentData parentData = null;
376 UINode parent = _parent;
377 while (parent != null && parent is! SkyNodeWrapper) {
378 if (parent is StyleNode && parent.style != null)
379 styles.add(parent.style);
380 else
381 if (parent is ParentDataNode && parent.parentData != null) {
382 if (parentData != null)
383 parentData.merge(parent.parentData); // this will throw if the types a ren't the same
384 else
385 parentData = parent.parentData;
386 }
387 parent = parent._parent;
388 }
389 _root.updateStyles(styles);
390 if (parentData != null) {
391 assert(_root.parentData != null);
392 _root.parentData.merge(parentData); // this will throw if the types aren't approriate
393 assert(parent != null);
394 assert(parent._root != null);
395 parent._root.markNeedsLayout();
396 }
397 _root.updateInlineStyle(inlineStyle);
398
399 _syncChildren(oldSkyElementWrapper);
400 }
401
402 void _syncChildren(SkyElementWrapper oldSkyElementWrapper) {
403 if (_root is! RenderCSSContainer)
404 return;
405
406 var startIndex = 0;
407 var endIndex = children.length;
408
409 var oldChildren = oldSkyElementWrapper.children;
410 var oldStartIndex = 0;
411 var oldEndIndex = oldChildren.length;
412
413 RenderCSS nextSibling = null;
414 UINode currentNode = null;
415 UINode oldNode = null;
416
417 void sync(int atIndex) {
418 children[atIndex] = _syncChild(currentNode, oldNode, _root, nextSibling);
419 assert(children[atIndex] != null);
420 }
421
422 // Scan backwards from end of list while nodes can be directly synced
423 // without reordering.
424 while (endIndex > startIndex && oldEndIndex > oldStartIndex) {
425 currentNode = children[endIndex - 1];
426 oldNode = oldChildren[oldEndIndex - 1];
427
428 if (currentNode._key != oldNode._key) {
429 break;
430 }
431
432 endIndex--;
433 oldEndIndex--;
434 sync(endIndex);
435 }
436
437 HashMap<String, UINode> oldNodeIdMap = null;
438
439 bool oldNodeReordered(String key) {
440 return oldNodeIdMap != null &&
441 oldNodeIdMap.containsKey(key) &&
442 oldNodeIdMap[key] == null;
443 }
444
445 void advanceOldStartIndex() {
446 oldStartIndex++;
447 while (oldStartIndex < oldEndIndex &&
448 oldNodeReordered(oldChildren[oldStartIndex]._key)) {
449 oldStartIndex++;
450 }
451 }
452
453 void ensureOldIdMap() {
454 if (oldNodeIdMap != null)
455 return;
456
457 oldNodeIdMap = new HashMap<String, UINode>();
458 for (int i = oldStartIndex; i < oldEndIndex; i++) {
459 var node = oldChildren[i];
460 if (!node.interchangeable)
461 oldNodeIdMap.putIfAbsent(node._key, () => node);
462 }
463 }
464
465 bool searchForOldNode() {
466 if (currentNode.interchangeable)
467 return false; // never re-order these nodes
468
469 ensureOldIdMap();
470 oldNode = oldNodeIdMap[currentNode._key];
471 if (oldNode == null)
472 return false;
473
474 oldNodeIdMap[currentNode._key] = null; // mark it reordered
475 assert(_root is RenderCSSContainer);
476 assert(oldNode._root is RenderCSSContainer);
477 oldSkyElementWrapper._root.remove(oldNode._root);
478 _root.add(oldNode._root, before: nextSibling);
479 return true;
480 }
481
482 // Scan forwards, this time we may re-order;
483 nextSibling = _root.firstChild;
484 while (startIndex < endIndex && oldStartIndex < oldEndIndex) {
485 currentNode = children[startIndex];
486 oldNode = oldChildren[oldStartIndex];
487
488 if (currentNode._key == oldNode._key) {
489 assert(currentNode.runtimeType == oldNode.runtimeType);
490 nextSibling = _root.childAfter(nextSibling);
491 sync(startIndex);
492 startIndex++;
493 advanceOldStartIndex();
494 continue;
495 }
496
497 oldNode = null;
498 searchForOldNode();
499 sync(startIndex);
500 startIndex++;
501 }
502
503 // New insertions
504 oldNode = null;
505 while (startIndex < endIndex) {
506 currentNode = children[startIndex];
507 sync(startIndex);
508 startIndex++;
509 }
510
511 // Removals
512 currentNode = null;
513 while (oldStartIndex < oldEndIndex) {
514 oldNode = oldChildren[oldStartIndex];
515 _removeChild(oldNode);
516 advanceOldStartIndex();
517 }
518 }
519 }
520
521 class Container extends SkyElementWrapper {
522
523 RenderCSSContainer _root;
524 RenderCSSContainer _createNode() => new RenderCSSContainer(this);
525
526 static final Container _emptyContainer = new Container();
527
528 SkyNodeWrapper get _emptyNode => _emptyContainer;
529
530 Container({
531 Object key,
532 List<UINode> children,
533 Style style,
534 String inlineStyle
535 }) : super(
536 key: key,
537 children: children,
538 style: style,
539 inlineStyle: inlineStyle
540 );
541 }
542
543 class Paragraph extends SkyElementWrapper {
544
545 RenderCSSParagraph _root;
546 RenderCSSParagraph _createNode() => new RenderCSSParagraph(this);
547
548 static final Paragraph _emptyContainer = new Paragraph();
549
550 SkyNodeWrapper get _emptyNode => _emptyContainer;
551
552 Paragraph({
553 Object key,
554 List<UINode> children,
555 Style style,
556 String inlineStyle
557 }) : super(
558 key: key,
559 children: children,
560 style: style,
561 inlineStyle: inlineStyle
562 );
563 }
564
565 class FlexContainer extends SkyElementWrapper {
566
567 RenderCSSFlex _root;
568 RenderCSSFlex _createNode() => new RenderCSSFlex(this, this.direction);
569
570 static final FlexContainer _emptyContainer = new FlexContainer();
571 // direction doesn't matter if it's empty
572
573 SkyNodeWrapper get _emptyNode => _emptyContainer;
574
575 final FlexDirection direction;
576
577 FlexContainer({
578 Object key,
579 List<UINode> children,
580 Style style,
581 String inlineStyle,
582 this.direction: FlexDirection.Row
583 }) : super(
584 key: key,
585 children: children,
586 style: style,
587 inlineStyle: inlineStyle
588 );
589
590 void _syncNode(UINode old) {
591 super._syncNode(old);
592 _root.direction = direction;
593 }
594 }
595
596 class TextFragment extends SkyElementWrapper {
597
598 RenderCSSInline _root;
599 RenderCSSInline _createNode() => new RenderCSSInline(this, this.data);
600
601 static final TextFragment _emptyText = new TextFragment('');
602
603 SkyNodeWrapper get _emptyNode => _emptyText;
604
605 final String data;
606
607 TextFragment(this.data, {
608 Object key,
609 Style style,
610 String inlineStyle
611 }) : super(
612 key: key,
613 style: style,
614 inlineStyle: inlineStyle
615 );
616
617 void _syncNode(UINode old) {
618 super._syncNode(old);
619 _root.data = data;
620 }
621 }
622
623 class Image extends SkyElementWrapper {
624
625 RenderCSSImage _root;
626 RenderCSSImage _createNode() => new RenderCSSImage(this, this.src, this.width, this.height);
627
628 static final Image _emptyImage = new Image();
629
630 SkyNodeWrapper get _emptyNode => _emptyImage;
631
632 final String src;
633 final int width;
634 final int height;
635
636 Image({
637 Object key,
638 Style style,
639 String inlineStyle,
640 this.width,
641 this.height,
642 this.src
643 }) : super(
644 key: key,
645 style: style,
646 inlineStyle: inlineStyle
647 );
648
649 void _syncNode(UINode old) {
650 super._syncNode(old);
651 _root.configure(this.src, this.width, this.height);
652 }
653 }
654
655
656 Set<Component> _mountedComponents = new HashSet<Component>();
657 Set<Component> _unmountedComponents = new HashSet<Component>();
658
659 void _enqueueDidMount(Component c) {
660 assert(!_notifingMountStatus);
661 _mountedComponents.add(c);
662 }
663
664 void _enqueueDidUnmount(Component c) {
665 assert(!_notifingMountStatus);
666 _unmountedComponents.add(c);
667 }
668
669 bool _notifingMountStatus = false;
670
671 void _notifyMountStatusChanged() {
672 try {
673 _notifingMountStatus = true;
674 _unmountedComponents.forEach((c) => c._didUnmount());
675 _mountedComponents.forEach((c) => c._didMount());
676 _mountedComponents.clear();
677 _unmountedComponents.clear();
678 } finally {
679 _notifingMountStatus = false;
680 }
681 }
682
683 List<Component> _dirtyComponents = new List<Component>();
684 bool _buildScheduled = false;
685 bool _inRenderDirtyComponents = false;
686
687 void _buildDirtyComponents() {
688 _tracing.begin('fn::_buildDirtyComponents');
689
690 Stopwatch sw;
691 if (_shouldLogRenderDuration)
692 sw = new Stopwatch()..start();
693
694 try {
695 _inRenderDirtyComponents = true;
696
697 _dirtyComponents.sort((a, b) => a._order - b._order);
698 for (var comp in _dirtyComponents) {
699 comp._buildIfDirty();
700 }
701
702 _dirtyComponents.clear();
703 _buildScheduled = false;
704 } finally {
705 _inRenderDirtyComponents = false;
706 }
707
708 _notifyMountStatusChanged();
709
710 if (_shouldLogRenderDuration) {
711 sw.stop();
712 print('Render took ${sw.elapsedMicroseconds} microseconds');
713 }
714
715 _tracing.end('fn::_buildDirtyComponents');
716 }
717
718 void _scheduleComponentForRender(Component c) {
719 assert(!_inRenderDirtyComponents);
720 _dirtyComponents.add(c);
721
722 if (!_buildScheduled) {
723 _buildScheduled = true;
724 new Future.microtask(_buildDirtyComponents);
725 }
726 }
727
728 abstract class Component extends UINode {
729 bool get _isBuilding => _currentlyBuilding == this;
730 bool _dirty = true;
731
732 UINode _built;
733 final int _order;
734 static int _currentOrder = 0;
735 bool _stateful;
736 static Component _currentlyBuilding;
737 List<Function> _mountCallbacks;
738 List<Function> _unmountCallbacks;
739
740 void onDidMount(Function fn) {
741 if (_mountCallbacks == null)
742 _mountCallbacks = new List<Function>();
743
744 _mountCallbacks.add(fn);
745 }
746
747 void onDidUnmount(Function fn) {
748 if (_unmountCallbacks == null)
749 _unmountCallbacks = new List<Function>();
750
751 _unmountCallbacks.add(fn);
752 }
753
754
755 Component({ Object key, bool stateful })
756 : _stateful = stateful != null ? stateful : false,
757 _order = _currentOrder + 1,
758 super(key: key);
759
760 Component.fromArgs(Object key, bool stateful)
761 : this(key: key, stateful: stateful);
762
763 void _didMount() {
764 if (_mountCallbacks != null)
765 _mountCallbacks.forEach((fn) => fn());
766 }
767
768 void _didUnmount() {
769 if (_unmountCallbacks != null)
770 _unmountCallbacks.forEach((fn) => fn());
771 }
772
773 // TODO(rafaelw): It seems wrong to expose DOM at all. This is presently
774 // needed to get sizing info.
775 RenderCSS getRoot() => _root;
776
777 void _remove() {
778 assert(_built != null);
779 assert(_root != null);
780 _removeChild(_built);
781 _built = null;
782 _enqueueDidUnmount(this);
783 super._remove();
784 }
785
786 bool _willSync(UINode old) {
787 Component oldComponent = old as Component;
788 if (oldComponent == null || !oldComponent._stateful)
789 return false;
790
791 // Make |this| the "old" Component
792 _stateful = false;
793 _built = oldComponent._built;
794 assert(_built != null);
795
796 // Make |oldComponent| the "new" component
797 reflect.copyPublicFields(this, oldComponent);
798 oldComponent._built = null;
799 oldComponent._dirty = true;
800 return true;
801 }
802
803 /* There are three cases here:
804 * 1) Building for the first time:
805 * assert(_built == null && old == null)
806 * 2) Re-building (because a dirty flag got set):
807 * assert(_built != null && old == null)
808 * 3) Syncing against an old version
809 * assert(_built == null && old != null)
810 */
811 void _sync(UINode old, RenderCSSContainer host, RenderCSS insertBefore) {
812 assert(!_defunct);
813 assert(_built == null || old == null);
814
815 Component oldComponent = old as Component;
816
817 var oldBuilt;
818 if (oldComponent == null) {
819 oldBuilt = _built;
820 } else {
821 assert(_built == null);
822 oldBuilt = oldComponent._built;
823 }
824
825 if (oldBuilt == null)
826 _enqueueDidMount(this);
827
828 int lastOrder = _currentOrder;
829 _currentOrder = _order;
830 _currentlyBuilding = this;
831 _built = build();
832 _currentlyBuilding = null;
833 _currentOrder = lastOrder;
834
835 _built = _syncChild(_built, oldBuilt, host, insertBefore);
836 _dirty = false;
837 _root = _built._root;
838 assert(_root != null);
839 }
840
841 void _buildIfDirty() {
842 if (!_dirty || _defunct)
843 return;
844
845 _trace('$_key rebuilding...');
846 _sync(null, null, null); // TODO(ianh): figure out how passing "null, null, null" here is ok
847 }
848
849 void scheduleBuild() {
850 setState(() {});
851 }
852
853 void setState(Function fn()) {
854 _stateful = true;
855 fn();
856 if (_isBuilding || _dirty || _defunct)
857 return;
858
859 _dirty = true;
860 _scheduleComponentForRender(this);
861 }
862
863 UINode build();
864 }
865
866 abstract class App extends Component {
867 RenderCSS _host;
868
869 App() : super(stateful: true) {
870 _host = new RenderCSSRoot(this);
871 _scheduleComponentForRender(this);
872 }
873
874 void _buildIfDirty() {
875 if (!_dirty || _defunct)
876 return;
877
878 _trace('$_key rebuilding...');
879 _sync(null, _host, _root);
880 }
881 }
882
883 class Text extends Component {
884 Text(this.data) : super(key: '*text*');
885 final String data;
886 bool get interchangeable => true;
887 UINode build() => new Paragraph(children: [new TextFragment(data)]);
888 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698