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

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
« no previous file with comments | « sky/framework/embedder.dart ('k') | sky/framework/layout.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 FillStackContainer extends SkyElementWrapper {
597
598 RenderCSSStack _root;
599 RenderCSSStack _createNode() => new RenderCSSStack(this);
600
601 static final FillStackContainer _emptyContainer = new FillStackContainer();
602
603 SkyNodeWrapper get _emptyNode => _emptyContainer;
604
605 FillStackContainer({
606 Object key,
607 List<UINode> children,
608 Style style,
609 String inlineStyle
610 }) : super(
611 key: key,
612 children: _positionNodesToFill(children),
613 style: style,
614 inlineStyle: inlineStyle
615 );
616
617 static StackParentData _fillParentData = new StackParentData()
618 ..top = 0.0
619 ..left = 0.0
620 ..right = 0.0
621 ..bottom = 0.0;
622
623 static List<UINode> _positionNodesToFill(List<UINode> input) {
624 if (input == null)
625 return null;
626 return input.map((node) {
627 return new ParentDataNode(node, _fillParentData);
628 }).toList();
629 }
630 }
631
632 class TextFragment extends SkyElementWrapper {
633
634 RenderCSSInline _root;
635 RenderCSSInline _createNode() => new RenderCSSInline(this, this.data);
636
637 static final TextFragment _emptyText = new TextFragment('');
638
639 SkyNodeWrapper get _emptyNode => _emptyText;
640
641 final String data;
642
643 TextFragment(this.data, {
644 Object key,
645 Style style,
646 String inlineStyle
647 }) : super(
648 key: key,
649 style: style,
650 inlineStyle: inlineStyle
651 );
652
653 void _syncNode(UINode old) {
654 super._syncNode(old);
655 _root.data = data;
656 }
657 }
658
659 class Image extends SkyElementWrapper {
660
661 RenderCSSImage _root;
662 RenderCSSImage _createNode() => new RenderCSSImage(this, this.src, this.width, this.height);
663
664 static final Image _emptyImage = new Image();
665
666 SkyNodeWrapper get _emptyNode => _emptyImage;
667
668 final String src;
669 final int width;
670 final int height;
671
672 Image({
673 Object key,
674 Style style,
675 String inlineStyle,
676 this.width,
677 this.height,
678 this.src
679 }) : super(
680 key: key,
681 style: style,
682 inlineStyle: inlineStyle
683 );
684
685 void _syncNode(UINode old) {
686 super._syncNode(old);
687 _root.configure(this.src, this.width, this.height);
688 }
689 }
690
691
692 Set<Component> _mountedComponents = new HashSet<Component>();
693 Set<Component> _unmountedComponents = new HashSet<Component>();
694
695 void _enqueueDidMount(Component c) {
696 assert(!_notifingMountStatus);
697 _mountedComponents.add(c);
698 }
699
700 void _enqueueDidUnmount(Component c) {
701 assert(!_notifingMountStatus);
702 _unmountedComponents.add(c);
703 }
704
705 bool _notifingMountStatus = false;
706
707 void _notifyMountStatusChanged() {
708 try {
709 _notifingMountStatus = true;
710 _unmountedComponents.forEach((c) => c._didUnmount());
711 _mountedComponents.forEach((c) => c._didMount());
712 _mountedComponents.clear();
713 _unmountedComponents.clear();
714 } finally {
715 _notifingMountStatus = false;
716 }
717 }
718
719 List<Component> _dirtyComponents = new List<Component>();
720 bool _buildScheduled = false;
721 bool _inRenderDirtyComponents = false;
722
723 void _buildDirtyComponents() {
724 _tracing.begin('fn::_buildDirtyComponents');
725
726 Stopwatch sw;
727 if (_shouldLogRenderDuration)
728 sw = new Stopwatch()..start();
729
730 try {
731 _inRenderDirtyComponents = true;
732
733 _dirtyComponents.sort((a, b) => a._order - b._order);
734 for (var comp in _dirtyComponents) {
735 comp._buildIfDirty();
736 }
737
738 _dirtyComponents.clear();
739 _buildScheduled = false;
740 } finally {
741 _inRenderDirtyComponents = false;
742 }
743
744 _notifyMountStatusChanged();
745
746 if (_shouldLogRenderDuration) {
747 sw.stop();
748 print('Render took ${sw.elapsedMicroseconds} microseconds');
749 }
750
751 _tracing.end('fn::_buildDirtyComponents');
752 }
753
754 void _scheduleComponentForRender(Component c) {
755 assert(!_inRenderDirtyComponents);
756 _dirtyComponents.add(c);
757
758 if (!_buildScheduled) {
759 _buildScheduled = true;
760 new Future.microtask(_buildDirtyComponents);
761 }
762 }
763
764 abstract class Component extends UINode {
765 bool get _isBuilding => _currentlyBuilding == this;
766 bool _dirty = true;
767
768 UINode _built;
769 final int _order;
770 static int _currentOrder = 0;
771 bool _stateful;
772 static Component _currentlyBuilding;
773 List<Function> _mountCallbacks;
774 List<Function> _unmountCallbacks;
775
776 void onDidMount(Function fn) {
777 if (_mountCallbacks == null)
778 _mountCallbacks = new List<Function>();
779
780 _mountCallbacks.add(fn);
781 }
782
783 void onDidUnmount(Function fn) {
784 if (_unmountCallbacks == null)
785 _unmountCallbacks = new List<Function>();
786
787 _unmountCallbacks.add(fn);
788 }
789
790
791 Component({ Object key, bool stateful })
792 : _stateful = stateful != null ? stateful : false,
793 _order = _currentOrder + 1,
794 super(key: key);
795
796 Component.fromArgs(Object key, bool stateful)
797 : this(key: key, stateful: stateful);
798
799 void _didMount() {
800 if (_mountCallbacks != null)
801 _mountCallbacks.forEach((fn) => fn());
802 }
803
804 void _didUnmount() {
805 if (_unmountCallbacks != null)
806 _unmountCallbacks.forEach((fn) => fn());
807 }
808
809 // TODO(rafaelw): It seems wrong to expose DOM at all. This is presently
810 // needed to get sizing info.
811 RenderCSS getRoot() => _root;
812
813 void _remove() {
814 assert(_built != null);
815 assert(_root != null);
816 _removeChild(_built);
817 _built = null;
818 _enqueueDidUnmount(this);
819 super._remove();
820 }
821
822 bool _willSync(UINode old) {
823 Component oldComponent = old as Component;
824 if (oldComponent == null || !oldComponent._stateful)
825 return false;
826
827 // Make |this| the "old" Component
828 _stateful = false;
829 _built = oldComponent._built;
830 assert(_built != null);
831
832 // Make |oldComponent| the "new" component
833 reflect.copyPublicFields(this, oldComponent);
834 oldComponent._built = null;
835 oldComponent._dirty = true;
836 return true;
837 }
838
839 /* There are three cases here:
840 * 1) Building for the first time:
841 * assert(_built == null && old == null)
842 * 2) Re-building (because a dirty flag got set):
843 * assert(_built != null && old == null)
844 * 3) Syncing against an old version
845 * assert(_built == null && old != null)
846 */
847 void _sync(UINode old, RenderCSSContainer host, RenderCSS insertBefore) {
848 assert(!_defunct);
849 assert(_built == null || old == null);
850
851 Component oldComponent = old as Component;
852
853 var oldBuilt;
854 if (oldComponent == null) {
855 oldBuilt = _built;
856 } else {
857 assert(_built == null);
858 oldBuilt = oldComponent._built;
859 }
860
861 if (oldBuilt == null)
862 _enqueueDidMount(this);
863
864 int lastOrder = _currentOrder;
865 _currentOrder = _order;
866 _currentlyBuilding = this;
867 _built = build();
868 _currentlyBuilding = null;
869 _currentOrder = lastOrder;
870
871 _built = _syncChild(_built, oldBuilt, host, insertBefore);
872 _dirty = false;
873 _root = _built._root;
874 assert(_root != null);
875 }
876
877 void _buildIfDirty() {
878 if (!_dirty || _defunct)
879 return;
880
881 _trace('$_key rebuilding...');
882 _sync(null, null, null); // TODO(ianh): figure out how passing "null, null, null" here is ok
883 }
884
885 void scheduleBuild() {
886 setState(() {});
887 }
888
889 void setState(Function fn()) {
890 _stateful = true;
891 fn();
892 if (_isBuilding || _dirty || _defunct)
893 return;
894
895 _dirty = true;
896 _scheduleComponentForRender(this);
897 }
898
899 UINode build();
900 }
901
902 abstract class App extends Component {
903 RenderCSS _host;
904
905 App() : super(stateful: true) {
906 _host = new RenderCSSRoot(this);
907 _scheduleComponentForRender(this);
908 }
909
910 void _buildIfDirty() {
911 if (!_dirty || _defunct)
912 return;
913
914 _trace('$_key rebuilding...');
915 _sync(null, _host, _root);
916 }
917 }
918
919 class Text extends Component {
920 Text(this.data) : super(key: '*text*');
921 final String data;
922 bool get interchangeable => true;
923 UINode build() => new Paragraph(children: [new TextFragment(data)]);
924 }
OLDNEW
« no previous file with comments | « sky/framework/embedder.dart ('k') | sky/framework/layout.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698