| 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 |