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

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

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

Powered by Google App Engine
This is Rietveld 408576698