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:mirrors'; |
10 import 'dart:sky' as sky; | 10 import 'dart:sky' as sky; |
11 import 'reflect.dart' as reflect; | 11 import 'reflect.dart' as reflect; |
12 import 'layout2.dart'; | 12 import 'layout2.dart'; |
| 13 import 'app.dart'; |
13 | 14 |
14 final sky.Tracing _tracing = sky.window.tracing; | 15 // final sky.Tracing _tracing = sky.window.tracing; |
15 | 16 |
16 final bool _shouldLogRenderDuration = false; | 17 final bool _shouldLogRenderDuration = false; |
17 final bool _shouldTrace = false; | 18 final bool _shouldTrace = false; |
18 | 19 |
19 enum _SyncOperation { IDENTICAL, INSERTION, STATEFUL, STATELESS, REMOVAL } | 20 enum _SyncOperation { IDENTICAL, INSERTION, STATEFUL, STATELESS, REMOVAL } |
20 | 21 |
21 /* | 22 /* |
22 * All Effen nodes derive from UINode. All nodes have a _parent, a _key and | 23 * All Effen nodes derive from UINode. All nodes have a _parent, a _key and |
23 * can be sync'd. | 24 * can be sync'd. |
24 */ | 25 */ |
25 abstract class UINode { | 26 abstract class UINode { |
26 String _key; | 27 String _key; |
27 UINode _parent; | 28 UINode _parent; |
28 UINode get parent => _parent; | 29 UINode get parent => _parent; |
29 RenderCSS root; | 30 RenderNode root; |
30 bool _defunct = false; | 31 bool _defunct = false; |
31 | 32 |
32 UINode({ Object key }) { | 33 UINode({ Object key }) { |
33 _key = key == null ? "$runtimeType" : "$runtimeType-$key"; | 34 _key = key == null ? "$runtimeType" : "$runtimeType-$key"; |
34 assert(this is App || _inRenderDirtyComponents); // you should not build the
UI tree ahead of time, build it only during build() | 35 assert(this is App || _inRenderDirtyComponents); // you should not build the
UI tree ahead of time, build it only during build() |
35 } | 36 } |
36 | 37 |
37 // Subclasses which implements Nodes that become stateful may return true | 38 // Subclasses which implements Nodes that become stateful may return true |
38 // if the |old| node has become stateful and should be retained. | 39 // if the |old| node has become stateful and should be retained. |
39 bool _willSync(UINode old) => false; | 40 bool _willSync(UINode old) => false; |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
87 _trace('_sync($outString) $key'); | 88 _trace('_sync($outString) $key'); |
88 } | 89 } |
89 | 90 |
90 void removeChild(UINode node) { | 91 void removeChild(UINode node) { |
91 _traceSync(_SyncOperation.REMOVAL, node._key); | 92 _traceSync(_SyncOperation.REMOVAL, node._key); |
92 node._remove(); | 93 node._remove(); |
93 } | 94 } |
94 | 95 |
95 // Returns the child which should be retained as the child of this node. | 96 // Returns the child which should be retained as the child of this node. |
96 UINode syncChild(UINode node, UINode oldNode, dynamic slot) { | 97 UINode syncChild(UINode node, UINode oldNode, dynamic slot) { |
97 | |
98 if (node == oldNode) { | 98 if (node == oldNode) { |
99 _traceSync(_SyncOperation.IDENTICAL, node == null ? '*null*' : node._key); | 99 _traceSync(_SyncOperation.IDENTICAL, node == null ? '*null*' : node._key); |
100 return node; // Nothing to do. Subtrees must be identical. | 100 return node; // Nothing to do. Subtrees must be identical. |
101 } | 101 } |
102 | 102 |
103 if (node == null) { | 103 if (node == null) { |
104 // the child in this slot has gone away | 104 // the child in this slot has gone away |
105 removeChild(oldNode); | 105 removeChild(oldNode); |
106 return null; | 106 return null; |
107 } | 107 } |
108 assert(oldNode == null || node._key == oldNode._key); | 108 assert(oldNode == null || node._key == oldNode._key); |
109 | 109 |
110 // TODO(rafaelw): This eagerly removes the old DOM. It may be that a | 110 // TODO(rafaelw): This eagerly removes the old DOM. It may be that a |
111 // new component was built that could re-use some of it. Consider | 111 // new component was built that could re-use some of it. Consider |
112 // syncing the new VDOM against the old one. | 112 // syncing the new VDOM against the old one. |
113 if (oldNode != null && node._key != oldNode._key) { | 113 if (oldNode != null && node._key != oldNode._key) { |
114 removeChild(oldNode); | 114 removeChild(oldNode); |
115 } | 115 } |
116 | 116 |
117 if (node._willSync(oldNode)) { | 117 if (node._willSync(oldNode)) { |
118 _traceSync(_SyncOperation.STATEFUL, node._key); | 118 _traceSync(_SyncOperation.STATEFUL, node._key); |
119 oldNode._sync(node, slot); | 119 oldNode._sync(node, slot); |
120 node._defunct = true; | 120 node._defunct = true; |
121 assert(oldNode.root is RenderCSS); | 121 assert(oldNode.root is RenderNode); |
122 return oldNode; | 122 return oldNode; |
123 } | 123 } |
124 | 124 |
125 assert(!node._defunct); | 125 assert(!node._defunct); |
126 node._parent = this; | 126 node._parent = this; |
127 | 127 |
128 if (oldNode == null) { | 128 if (oldNode == null) { |
129 _traceSync(_SyncOperation.INSERTION, node._key); | 129 _traceSync(_SyncOperation.INSERTION, node._key); |
130 } else { | 130 } else { |
131 _traceSync(_SyncOperation.STATELESS, node._key); | 131 _traceSync(_SyncOperation.STATELESS, node._key); |
132 } | 132 } |
133 node._sync(oldNode, slot); | 133 node._sync(oldNode, slot); |
134 if (oldNode != null) | 134 if (oldNode != null) |
135 oldNode._defunct = true; | 135 oldNode._defunct = true; |
136 | 136 |
137 assert(node.root is RenderCSS); | 137 assert(node.root is RenderNode); |
138 return node; | 138 return node; |
139 } | 139 } |
140 } | 140 } |
141 | 141 |
142 abstract class ContentNode extends UINode { | 142 abstract class ContentNode extends UINode { |
143 UINode content; | 143 UINode content; |
144 | 144 |
145 ContentNode(UINode content) : this.content = content, super(key: content._key)
; | 145 ContentNode(UINode content) : this.content = content, super(key: content._key)
; |
146 | 146 |
147 void _sync(UINode old, dynamic slot) { | 147 void _sync(UINode old, dynamic slot) { |
148 UINode oldContent = old == null ? null : (old as ContentNode).content; | 148 UINode oldContent = old == null ? null : (old as ContentNode).content; |
149 content = syncChild(content, oldContent, slot); | 149 content = syncChild(content, oldContent, slot); |
150 assert(content.root != null); | 150 assert(content.root != null); |
151 root = content.root; | 151 root = content.root; |
152 } | 152 } |
153 | 153 |
154 void _remove() { | 154 void _remove() { |
155 if (content != null) | 155 if (content != null) |
156 removeChild(content); | 156 removeChild(content); |
157 super._remove(); | 157 super._remove(); |
158 } | 158 } |
159 } | 159 } |
160 | 160 |
161 class StyleNode extends ContentNode { | |
162 final Style style; | |
163 | |
164 StyleNode(UINode content, this.style): super(content); | |
165 } | |
166 | |
167 class ParentDataNode extends ContentNode { | 161 class ParentDataNode extends ContentNode { |
168 final ParentData parentData; | 162 final ParentData parentData; |
169 | 163 |
170 ParentDataNode(UINode content, this.parentData): super(content); | 164 ParentDataNode(UINode content, this.parentData): super(content); |
171 } | 165 } |
172 | 166 |
173 typedef GestureEventListener(sky.GestureEvent e); | 167 typedef GestureEventListener(sky.GestureEvent e); |
174 typedef PointerEventListener(sky.PointerEvent e); | 168 typedef PointerEventListener(sky.PointerEvent e); |
175 typedef EventListener(sky.Event e); | 169 typedef EventListener(sky.Event e); |
176 | 170 |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
280 | 274 |
281 void _sync(UINode old, dynamic slot) { | 275 void _sync(UINode old, dynamic slot) { |
282 for (var type in listeners.keys) { | 276 for (var type in listeners.keys) { |
283 _ensureDocumentListener(type); | 277 _ensureDocumentListener(type); |
284 } | 278 } |
285 super._sync(old, slot); | 279 super._sync(old, slot); |
286 } | 280 } |
287 } | 281 } |
288 | 282 |
289 /* | 283 /* |
290 * RenderNodeWrappers correspond to a desired state of a RenderCSS. | 284 * RenderNodeWrappers correspond to a desired state of a RenderNode. |
291 * They are fully immutable, with one exception: A UINode which is a | 285 * They are fully immutable, with one exception: A UINode which is a |
292 * Component which lives within an OneChildListRenderNodeWrapper's | 286 * Component which lives within an OneChildListRenderNodeWrapper's |
293 * children list, may be replaced with the "old" instance if it has | 287 * children list, may be replaced with the "old" instance if it has |
294 * become stateful. | 288 * become stateful. |
295 */ | 289 */ |
296 abstract class RenderNodeWrapper extends UINode { | 290 abstract class RenderNodeWrapper extends UINode { |
297 | 291 |
298 static final Map<RenderCSS, RenderNodeWrapper> _nodeMap = | 292 static final Map<RenderNode, RenderNodeWrapper> _nodeMap = |
299 new HashMap<RenderCSS, RenderNodeWrapper>(); | 293 new HashMap<RenderNode, RenderNodeWrapper>(); |
300 | 294 |
301 static RenderNodeWrapper _getMounted(RenderCSS node) => _nodeMap[node]; | 295 static RenderNodeWrapper _getMounted(RenderNode node) => _nodeMap[node]; |
302 | 296 |
303 RenderNodeWrapper({ | 297 RenderNodeWrapper({ |
304 Object key, | 298 Object key |
305 this.style, | |
306 this.inlineStyle | |
307 }) : super(key: key); | 299 }) : super(key: key); |
308 | 300 |
309 final Style style; | 301 RenderNode createNode(); |
310 final String inlineStyle; | |
311 | |
312 RenderCSS createNode(); | |
313 RenderNodeWrapper get emptyNode; | 302 RenderNodeWrapper get emptyNode; |
314 | 303 |
315 void insert(RenderNodeWrapper child, dynamic slot); | 304 void insert(RenderNodeWrapper child, dynamic slot); |
316 | 305 |
317 void _sync(UINode old, dynamic slot) { | 306 void _sync(UINode old, dynamic slot) { |
318 if (old == null) { | 307 if (old == null) { |
319 root = createNode(); | 308 root = createNode(); |
320 assert(root != null); | 309 assert(root != null); |
321 var ancestor = findAncestor(RenderNodeWrapper); | 310 var ancestor = findAncestor(RenderNodeWrapper); |
322 if (ancestor is RenderNodeWrapper) | 311 if (ancestor is RenderNodeWrapper) |
323 ancestor.insert(this, slot); | 312 ancestor.insert(this, slot); |
324 old = emptyNode; | 313 old = emptyNode; |
325 } else { | 314 } else { |
326 root = old.root; | 315 root = old.root; |
327 assert(root != null); | 316 assert(root != null); |
328 } | 317 } |
329 | 318 |
330 _nodeMap[root] = this; | 319 _nodeMap[root] = this; |
331 syncRenderNode(old); | 320 syncRenderNode(old); |
332 } | 321 } |
333 | 322 |
334 void syncRenderNode(RenderNodeWrapper old) { | 323 void syncRenderNode(RenderNodeWrapper old) { |
335 RenderNodeWrapper oldRenderNodeWrapper = old as RenderNodeWrapper; | |
336 | |
337 List<Style> styles = new List<Style>(); | |
338 if (style != null) | |
339 styles.add(style); | |
340 ParentData parentData = null; | 324 ParentData parentData = null; |
341 UINode parent = _parent; | 325 UINode parent = _parent; |
342 while (parent != null && parent is! RenderNodeWrapper) { | 326 while (parent != null && parent is! RenderNodeWrapper) { |
343 if (parent is StyleNode && parent.style != null) | |
344 styles.add(parent.style); | |
345 else | |
346 if (parent is ParentDataNode && parent.parentData != null) { | 327 if (parent is ParentDataNode && parent.parentData != null) { |
347 if (parentData != null) | 328 if (parentData != null) |
348 parentData.merge(parent.parentData); // this will throw if the types a
ren't the same | 329 parentData.merge(parent.parentData); // this will throw if the types a
ren't the same |
349 else | 330 else |
350 parentData = parent.parentData; | 331 parentData = parent.parentData; |
351 } | 332 } |
352 parent = parent._parent; | 333 parent = parent._parent; |
353 } | 334 } |
354 root.updateStyles(styles); | |
355 if (parentData != null) { | 335 if (parentData != null) { |
356 assert(root.parentData != null); | 336 assert(root.parentData != null); |
357 root.parentData.merge(parentData); // this will throw if the types aren't
approriate | 337 root.parentData.merge(parentData); // this will throw if the types aren't
approriate |
358 assert(parent != null); | 338 assert(parent != null); |
359 assert(parent.root != null); | 339 assert(parent.root != null); |
360 parent.root.markNeedsLayout(); | 340 parent.root.markNeedsLayout(); |
361 } | 341 } |
362 root.updateInlineStyle(inlineStyle); | |
363 } | 342 } |
364 | 343 |
365 void removeChild(UINode node) { | 344 void removeChild(UINode node) { |
366 assert(root is RenderCSSContainer); | |
367 root.remove(node.root); | 345 root.remove(node.root); |
368 super.removeChild(node); | 346 super.removeChild(node); |
369 } | 347 } |
370 | 348 |
371 void _remove() { | 349 void _remove() { |
372 assert(root != null); | 350 assert(root != null); |
373 _nodeMap.remove(root); | 351 _nodeMap.remove(root); |
374 super._remove(); | 352 super._remove(); |
375 } | 353 } |
376 } | 354 } |
377 | 355 |
378 final List<UINode> _emptyList = new List<UINode>(); | 356 final List<UINode> _emptyList = new List<UINode>(); |
379 | 357 |
380 abstract class OneChildListRenderNodeWrapper extends RenderNodeWrapper { | 358 abstract class OneChildListRenderNodeWrapper extends RenderNodeWrapper { |
381 | 359 |
382 // In OneChildListRenderNodeWrapper subclasses, slots are RenderCSS nodes | 360 // In OneChildListRenderNodeWrapper subclasses, slots are RenderNode nodes |
383 // to use as the "insert before" sibling in RenderCSSContainer.add() calls | 361 // to use as the "insert before" sibling in ContainerRenderNodeMixin.add() cal
ls |
384 | 362 |
385 final List<UINode> children; | 363 final List<UINode> children; |
386 | 364 |
387 OneChildListRenderNodeWrapper({ | 365 OneChildListRenderNodeWrapper({ |
388 Object key, | 366 Object key, |
389 List<UINode> children, | 367 List<UINode> children |
390 Style style, | |
391 String inlineStyle | |
392 }) : this.children = children == null ? _emptyList : children, | 368 }) : this.children = children == null ? _emptyList : children, |
393 super( | 369 super( |
394 key: key, | 370 key: key |
395 style: style, | |
396 inlineStyle: inlineStyle | |
397 ) { | 371 ) { |
398 assert(!_debugHasDuplicateIds()); | 372 assert(!_debugHasDuplicateIds()); |
399 } | 373 } |
400 | 374 |
401 void insert(RenderNodeWrapper child, dynamic slot) { | 375 void insert(RenderNodeWrapper child, dynamic slot) { |
402 assert(slot == null || slot is RenderCSS); | 376 assert(slot == null || slot is RenderNode); |
403 root.add(child.root, before: slot); | 377 root.add(child.root, before: slot); |
404 } | 378 } |
405 | 379 |
406 void _remove() { | 380 void _remove() { |
407 assert(children != null); | 381 assert(children != null); |
408 for (var child in children) { | 382 for (var child in children) { |
409 assert(child != null); | 383 assert(child != null); |
410 removeChild(child); | 384 removeChild(child); |
411 } | 385 } |
412 super._remove(); | 386 super._remove(); |
(...skipping 11 matching lines...) Expand all Loading... |
424 of another node, they must have unique keys. | 398 of another node, they must have unique keys. |
425 Duplicate: "${child._key}"'''; | 399 Duplicate: "${child._key}"'''; |
426 } | 400 } |
427 } | 401 } |
428 return false; | 402 return false; |
429 } | 403 } |
430 | 404 |
431 void syncRenderNode(OneChildListRenderNodeWrapper old) { | 405 void syncRenderNode(OneChildListRenderNodeWrapper old) { |
432 super.syncRenderNode(old); | 406 super.syncRenderNode(old); |
433 | 407 |
434 if (root is! RenderCSSContainer) | 408 if (root is! ContainerRenderNodeMixin) |
435 return; | 409 return; |
436 | 410 |
437 var startIndex = 0; | 411 var startIndex = 0; |
438 var endIndex = children.length; | 412 var endIndex = children.length; |
439 | 413 |
440 var oldChildren = old.children; | 414 var oldChildren = old.children; |
441 var oldStartIndex = 0; | 415 var oldStartIndex = 0; |
442 var oldEndIndex = oldChildren.length; | 416 var oldEndIndex = oldChildren.length; |
443 | 417 |
444 RenderCSS nextSibling = null; | 418 RenderNode nextSibling = null; |
445 UINode currentNode = null; | 419 UINode currentNode = null; |
446 UINode oldNode = null; | 420 UINode oldNode = null; |
447 | 421 |
448 void sync(int atIndex) { | 422 void sync(int atIndex) { |
449 children[atIndex] = syncChild(currentNode, oldNode, nextSibling); | 423 children[atIndex] = syncChild(currentNode, oldNode, nextSibling); |
450 assert(children[atIndex] != null); | 424 assert(children[atIndex] != null); |
451 } | 425 } |
452 | 426 |
453 // Scan backwards from end of list while nodes can be directly synced | 427 // Scan backwards from end of list while nodes can be directly synced |
454 // without reordering. | 428 // without reordering. |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
496 bool searchForOldNode() { | 470 bool searchForOldNode() { |
497 if (currentNode.interchangeable) | 471 if (currentNode.interchangeable) |
498 return false; // never re-order these nodes | 472 return false; // never re-order these nodes |
499 | 473 |
500 ensureOldIdMap(); | 474 ensureOldIdMap(); |
501 oldNode = oldNodeIdMap[currentNode._key]; | 475 oldNode = oldNodeIdMap[currentNode._key]; |
502 if (oldNode == null) | 476 if (oldNode == null) |
503 return false; | 477 return false; |
504 | 478 |
505 oldNodeIdMap[currentNode._key] = null; // mark it reordered | 479 oldNodeIdMap[currentNode._key] = null; // mark it reordered |
506 assert(root is RenderCSSContainer); | 480 assert(root is ContainerRenderNodeMixin); |
507 assert(oldNode.root is RenderCSSContainer); | 481 assert(oldNode.root is ContainerRenderNodeMixin); |
508 | 482 |
509 old.root.remove(oldNode.root); | 483 old.root.remove(oldNode.root); |
510 root.add(oldNode.root, before: nextSibling); | 484 root.add(oldNode.root, before: nextSibling); |
511 | 485 |
512 return true; | 486 return true; |
513 } | 487 } |
514 | 488 |
515 // Scan forwards, this time we may re-order; | 489 // Scan forwards, this time we may re-order; |
516 nextSibling = root.firstChild; | 490 nextSibling = root.firstChild; |
517 while (startIndex < endIndex && oldStartIndex < oldEndIndex) { | 491 while (startIndex < endIndex && oldStartIndex < oldEndIndex) { |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
555 | 529 |
556 RenderCSSContainer root; | 530 RenderCSSContainer root; |
557 RenderCSSContainer createNode() => new RenderCSSContainer(this); | 531 RenderCSSContainer createNode() => new RenderCSSContainer(this); |
558 | 532 |
559 static final Container _emptyContainer = new Container(); | 533 static final Container _emptyContainer = new Container(); |
560 | 534 |
561 RenderNodeWrapper get emptyNode => _emptyContainer; | 535 RenderNodeWrapper get emptyNode => _emptyContainer; |
562 | 536 |
563 Container({ | 537 Container({ |
564 Object key, | 538 Object key, |
565 List<UINode> children, | 539 List<UINode> children |
566 Style style, | |
567 String inlineStyle | |
568 }) : super( | 540 }) : super( |
569 key: key, | 541 key: key, |
570 children: children, | 542 children: children |
571 style: style, | |
572 inlineStyle: inlineStyle | |
573 ); | 543 ); |
574 } | 544 } |
575 | 545 |
576 class Paragraph extends OneChildListRenderNodeWrapper { | 546 class Paragraph extends OneChildListRenderNodeWrapper { |
577 | 547 |
578 RenderCSSParagraph root; | 548 RenderCSSParagraph root; |
579 RenderCSSParagraph createNode() => new RenderCSSParagraph(this); | 549 RenderCSSParagraph createNode() => new RenderCSSParagraph(this); |
580 | 550 |
581 static final Paragraph _emptyContainer = new Paragraph(); | 551 static final Paragraph _emptyContainer = new Paragraph(); |
582 | 552 |
583 RenderNodeWrapper get emptyNode => _emptyContainer; | 553 RenderNodeWrapper get emptyNode => _emptyContainer; |
584 | 554 |
585 Paragraph({ | 555 Paragraph({ |
586 Object key, | 556 Object key, |
587 List<UINode> children, | 557 List<UINode> children |
588 Style style, | |
589 String inlineStyle | |
590 }) : super( | 558 }) : super( |
591 key: key, | 559 key: key, |
592 children: children, | 560 children: children |
593 style: style, | |
594 inlineStyle: inlineStyle | |
595 ); | 561 ); |
596 } | 562 } |
597 | 563 |
598 class FlexContainer extends OneChildListRenderNodeWrapper { | 564 class FlexContainer extends OneChildListRenderNodeWrapper { |
599 | 565 |
600 RenderCSSFlex root; | 566 RenderFlex root; |
601 RenderCSSFlex createNode() => new RenderCSSFlex(this, this.direction); | 567 RenderFlex createNode() => new RenderFlex(this, this.direction); |
602 | 568 |
603 static final FlexContainer _emptyContainer = new FlexContainer(); | 569 static final FlexContainer _emptyContainer = new FlexContainer(); |
604 // direction doesn't matter if it's empty | 570 // direction doesn't matter if it's empty |
605 | 571 |
606 RenderNodeWrapper get emptyNode => _emptyContainer; | 572 RenderNodeWrapper get emptyNode => _emptyContainer; |
607 | 573 |
608 final FlexDirection direction; | 574 final FlexDirection direction; |
609 | 575 |
610 FlexContainer({ | 576 FlexContainer({ |
611 Object key, | 577 Object key, |
612 List<UINode> children, | 578 List<UINode> children, |
613 Style style, | 579 this.direction: FlexDirection.Horizontal |
614 String inlineStyle, | |
615 this.direction: FlexDirection.Row | |
616 }) : super( | 580 }) : super( |
617 key: key, | 581 key: key, |
618 children: children, | 582 children: children |
619 style: style, | |
620 inlineStyle: inlineStyle | |
621 ); | 583 ); |
622 | 584 |
623 void syncRenderNode(UINode old) { | 585 void syncRenderNode(UINode old) { |
624 super.syncRenderNode(old); | 586 super.syncRenderNode(old); |
625 root.direction = direction; | 587 root.direction = direction; |
626 } | 588 } |
627 } | 589 } |
628 | 590 |
| 591 class FlexExpandingChild extends ParentDataNode { |
| 592 FlexExpandingChild(UINode content, [int flex = 1]): super(content, new FlexBox
ParentData()..flex = flex); |
| 593 } |
| 594 |
629 class FillStackContainer extends OneChildListRenderNodeWrapper { | 595 class FillStackContainer extends OneChildListRenderNodeWrapper { |
630 | 596 |
631 RenderCSSStack root; | 597 RenderCSSStack root; |
632 RenderCSSStack createNode() => new RenderCSSStack(this); | 598 RenderCSSStack createNode() => new RenderCSSStack(this); |
633 | 599 |
634 static final FillStackContainer _emptyContainer = new FillStackContainer(); | 600 static final FillStackContainer _emptyContainer = new FillStackContainer(); |
635 | 601 |
636 RenderNodeWrapper get emptyNode => _emptyContainer; | 602 RenderNodeWrapper get emptyNode => _emptyContainer; |
637 | 603 |
638 FillStackContainer({ | 604 FillStackContainer({ |
639 Object key, | 605 Object key, |
640 List<UINode> children, | 606 List<UINode> children |
641 Style style, | |
642 String inlineStyle | |
643 }) : super( | 607 }) : super( |
644 key: key, | 608 key: key, |
645 children: _positionNodesToFill(children), | 609 children: _positionNodesToFill(children) |
646 style: style, | |
647 inlineStyle: inlineStyle | |
648 ); | 610 ); |
649 | 611 |
650 static StackParentData _fillParentData = new StackParentData() | 612 static StackParentData _fillParentData = new StackParentData() |
651 ..top = 0.0 | 613 ..top = 0.0 |
652 ..left = 0.0 | 614 ..left = 0.0 |
653 ..right = 0.0 | 615 ..right = 0.0 |
654 ..bottom = 0.0; | 616 ..bottom = 0.0; |
655 | 617 |
656 static List<UINode> _positionNodesToFill(List<UINode> input) { | 618 static List<UINode> _positionNodesToFill(List<UINode> input) { |
657 if (input == null) | 619 if (input == null) |
658 return null; | 620 return null; |
659 return input.map((node) { | 621 return input.map((node) { |
660 return new ParentDataNode(node, _fillParentData); | 622 return new ParentDataNode(node, _fillParentData); |
661 }).toList(); | 623 }).toList(); |
662 } | 624 } |
663 } | 625 } |
664 | 626 |
665 class TextFragment extends RenderNodeWrapper { | 627 class TextFragment extends RenderNodeWrapper { |
666 | 628 |
667 RenderCSSInline root; | 629 RenderCSSInline root; |
668 RenderCSSInline createNode() => new RenderCSSInline(this, this.data); | 630 RenderCSSInline createNode() => new RenderCSSInline(this, this.data); |
669 | 631 |
670 static final TextFragment _emptyText = new TextFragment(''); | 632 static final TextFragment _emptyText = new TextFragment(''); |
671 | 633 |
672 RenderNodeWrapper get emptyNode => _emptyText; | 634 RenderNodeWrapper get emptyNode => _emptyText; |
673 | 635 |
674 final String data; | 636 final String data; |
675 | 637 |
676 TextFragment(this.data, { | 638 TextFragment(this.data, { |
677 Object key, | 639 Object key |
678 Style style, | |
679 String inlineStyle | |
680 }) : super( | 640 }) : super( |
681 key: key, | 641 key: key |
682 style: style, | |
683 inlineStyle: inlineStyle | |
684 ); | 642 ); |
685 | 643 |
686 void syncRenderNode(UINode old) { | 644 void syncRenderNode(UINode old) { |
687 super.syncRenderNode(old); | 645 super.syncRenderNode(old); |
688 root.data = data; | 646 root.data = data; |
689 } | 647 } |
690 } | 648 } |
691 | 649 |
692 class Image extends RenderNodeWrapper { | 650 class Image extends RenderNodeWrapper { |
693 | 651 |
694 RenderCSSImage root; | 652 RenderCSSImage root; |
695 RenderCSSImage createNode() => new RenderCSSImage(this, this.src, this.width,
this.height); | 653 RenderCSSImage createNode() => new RenderCSSImage(this, this.src, this.width,
this.height); |
696 | 654 |
697 static final Image _emptyImage = new Image(); | 655 static final Image _emptyImage = new Image(); |
698 | 656 |
699 RenderNodeWrapper get emptyNode => _emptyImage; | 657 RenderNodeWrapper get emptyNode => _emptyImage; |
700 | 658 |
701 final String src; | 659 final String src; |
702 final int width; | 660 final int width; |
703 final int height; | 661 final int height; |
704 | 662 |
705 Image({ | 663 Image({ |
706 Object key, | 664 Object key, |
707 Style style, | |
708 String inlineStyle, | |
709 this.width, | 665 this.width, |
710 this.height, | 666 this.height, |
711 this.src | 667 this.src |
712 }) : super( | 668 }) : super( |
713 key: key, | 669 key: key |
714 style: style, | |
715 inlineStyle: inlineStyle | |
716 ); | 670 ); |
717 | 671 |
718 void syncRenderNode(UINode old) { | 672 void syncRenderNode(UINode old) { |
719 super.syncRenderNode(old); | 673 super.syncRenderNode(old); |
720 root.configure(this.src, this.width, this.height); | 674 root.configure(this.src, this.width, this.height); |
721 } | 675 } |
722 } | 676 } |
723 | 677 |
724 | 678 |
725 Set<Component> _mountedComponents = new HashSet<Component>(); | 679 Set<Component> _mountedComponents = new HashSet<Component>(); |
(...skipping 21 matching lines...) Expand all Loading... |
747 } finally { | 701 } finally { |
748 _notifingMountStatus = false; | 702 _notifingMountStatus = false; |
749 } | 703 } |
750 } | 704 } |
751 | 705 |
752 List<Component> _dirtyComponents = new List<Component>(); | 706 List<Component> _dirtyComponents = new List<Component>(); |
753 bool _buildScheduled = false; | 707 bool _buildScheduled = false; |
754 bool _inRenderDirtyComponents = false; | 708 bool _inRenderDirtyComponents = false; |
755 | 709 |
756 void _buildDirtyComponents() { | 710 void _buildDirtyComponents() { |
757 _tracing.begin('fn::_buildDirtyComponents'); | 711 //_tracing.begin('fn::_buildDirtyComponents'); |
758 | 712 |
759 Stopwatch sw; | 713 Stopwatch sw; |
760 if (_shouldLogRenderDuration) | 714 if (_shouldLogRenderDuration) |
761 sw = new Stopwatch()..start(); | 715 sw = new Stopwatch()..start(); |
762 | 716 |
763 try { | 717 try { |
764 _inRenderDirtyComponents = true; | 718 _inRenderDirtyComponents = true; |
765 | 719 |
766 _dirtyComponents.sort((a, b) => a._order - b._order); | 720 _dirtyComponents.sort((a, b) => a._order - b._order); |
767 for (var comp in _dirtyComponents) { | 721 for (var comp in _dirtyComponents) { |
768 comp._buildIfDirty(); | 722 comp._buildIfDirty(); |
769 } | 723 } |
770 | 724 |
771 _dirtyComponents.clear(); | 725 _dirtyComponents.clear(); |
772 _buildScheduled = false; | 726 _buildScheduled = false; |
773 } finally { | 727 } finally { |
774 _inRenderDirtyComponents = false; | 728 _inRenderDirtyComponents = false; |
775 } | 729 } |
776 | 730 |
777 _notifyMountStatusChanged(); | 731 _notifyMountStatusChanged(); |
778 | 732 |
779 if (_shouldLogRenderDuration) { | 733 if (_shouldLogRenderDuration) { |
780 sw.stop(); | 734 sw.stop(); |
781 print('Render took ${sw.elapsedMicroseconds} microseconds'); | 735 print('Render took ${sw.elapsedMicroseconds} microseconds'); |
782 } | 736 } |
783 | 737 |
784 _tracing.end('fn::_buildDirtyComponents'); | 738 //_tracing.end('fn::_buildDirtyComponents'); |
785 } | 739 } |
786 | 740 |
787 void _scheduleComponentForRender(Component c) { | 741 void _scheduleComponentForRender(Component c) { |
788 assert(!_inRenderDirtyComponents); | 742 assert(!_inRenderDirtyComponents); |
789 _dirtyComponents.add(c); | 743 _dirtyComponents.add(c); |
790 | 744 |
791 if (!_buildScheduled) { | 745 if (!_buildScheduled) { |
792 _buildScheduled = true; | 746 _buildScheduled = true; |
793 new Future.microtask(_buildDirtyComponents); | 747 new Future.microtask(_buildDirtyComponents); |
794 } | 748 } |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
835 _mountCallbacks.forEach((fn) => fn()); | 789 _mountCallbacks.forEach((fn) => fn()); |
836 } | 790 } |
837 | 791 |
838 void _didUnmount() { | 792 void _didUnmount() { |
839 if (_unmountCallbacks != null) | 793 if (_unmountCallbacks != null) |
840 _unmountCallbacks.forEach((fn) => fn()); | 794 _unmountCallbacks.forEach((fn) => fn()); |
841 } | 795 } |
842 | 796 |
843 // TODO(rafaelw): It seems wrong to expose DOM at all. This is presently | 797 // TODO(rafaelw): It seems wrong to expose DOM at all. This is presently |
844 // needed to get sizing info. | 798 // needed to get sizing info. |
845 RenderCSS getRoot() => root; | 799 RenderNode getRoot() => root; |
846 | 800 |
847 void _remove() { | 801 void _remove() { |
848 assert(_built != null); | 802 assert(_built != null); |
849 assert(root != null); | 803 assert(root != null); |
850 removeChild(_built); | 804 removeChild(_built); |
851 _built = null; | 805 _built = null; |
852 _enqueueDidUnmount(this); | 806 _enqueueDidUnmount(this); |
853 super._remove(); | 807 super._remove(); |
854 } | 808 } |
855 | 809 |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
930 return; | 884 return; |
931 | 885 |
932 _dirty = true; | 886 _dirty = true; |
933 _scheduleComponentForRender(this); | 887 _scheduleComponentForRender(this); |
934 } | 888 } |
935 | 889 |
936 UINode build(); | 890 UINode build(); |
937 } | 891 } |
938 | 892 |
939 abstract class App extends Component { | 893 abstract class App extends Component { |
940 RenderCSS _host; | |
941 | 894 |
942 App() : super(stateful: true) { | 895 App() : super(stateful: true) { |
943 _host = new RenderCSSRoot(this); | 896 _appView = new AppView(null); |
944 _scheduleComponentForRender(this); | 897 _scheduleComponentForRender(this); |
945 } | 898 } |
946 | 899 |
| 900 AppView _appView; |
| 901 |
947 void _buildIfDirty() { | 902 void _buildIfDirty() { |
948 if (!_dirty || _defunct) | 903 assert(_dirty); |
949 return; | 904 assert(!_defunct); |
950 | 905 _trace('$_key rebuilding app...'); |
951 _trace('$_key rebuilding...'); | |
952 _sync(null, null); | 906 _sync(null, null); |
953 if (root.parent == null) | 907 if (root.parent == null) |
954 _host.add(root); | 908 _appView.root = root; |
955 assert(root.parent == _host); | 909 assert(root.parent is RenderView); |
956 } | 910 } |
957 } | 911 } |
958 | 912 |
959 class Text extends Component { | 913 class Text extends Component { |
960 Text(this.data) : super(key: '*text*'); | 914 Text(this.data) : super(key: '*text*'); |
961 final String data; | 915 final String data; |
962 bool get interchangeable => true; | 916 bool get interchangeable => true; |
963 UINode build() => new Paragraph(children: [new TextFragment(data)]); | 917 UINode build() => new Paragraph(children: [new TextFragment(data)]); |
964 } | 918 } |
| 919 |
| 920 |
| 921 // for now, but only for now: |
| 922 |
| 923 class RenderSolidColor extends RenderDecoratedBox { |
| 924 final double desiredHeight; |
| 925 final double desiredWidth; |
| 926 final int backgroundColor; |
| 927 |
| 928 RenderSolidColor(int backgroundColor, { this.desiredHeight: double.INFINITY, |
| 929 this.desiredWidth: double.INFINITY }) |
| 930 : backgroundColor = backgroundColor, |
| 931 super(new BoxDecoration(backgroundColor: backgroundColor)); |
| 932 |
| 933 BoxDimensions getIntrinsicDimensions(BoxConstraints constraints) { |
| 934 return new BoxDimensions.withConstraints(constraints, |
| 935 height: desiredHeight, |
| 936 width: desiredWidth); |
| 937 } |
| 938 |
| 939 void layout(BoxConstraints constraints, { RenderNode relayoutSubtreeRoot }) { |
| 940 width = constraints.constrainWidth(desiredWidth); |
| 941 height = constraints.constrainHeight(desiredHeight); |
| 942 layoutDone(); |
| 943 } |
| 944 |
| 945 void handlePointer(sky.PointerEvent event) { |
| 946 if (event.type == 'pointerdown') |
| 947 decoration = new BoxDecoration(backgroundColor: 0xFFFF0000); |
| 948 else if (event.type == 'pointerup') |
| 949 decoration = new BoxDecoration(backgroundColor: backgroundColor); |
| 950 } |
| 951 } |
| 952 |
| 953 class Rectangle extends RenderNodeWrapper { |
| 954 |
| 955 Rectangle(this.color, { |
| 956 Object key |
| 957 }) : super( |
| 958 key: key |
| 959 ); |
| 960 |
| 961 final int color; |
| 962 |
| 963 RenderSolidColor root; |
| 964 RenderSolidColor createNode() => new RenderSolidColor(color, desiredWidth: 40.
0, desiredHeight: 130.0); |
| 965 |
| 966 static final Rectangle _emptyRectangle = new Rectangle(0); |
| 967 RenderNodeWrapper get emptyNode => _emptyRectangle; |
| 968 |
| 969 } |
OLD | NEW |