| 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 import 'dart:async'; | 5 import 'dart:async'; |
| 6 import 'dart:collection'; | 6 import 'dart:collection'; |
| 7 import 'dart:mirrors'; | 7 import 'dart:mirrors'; |
| 8 import 'dart:sky' as sky; | 8 import 'dart:sky' as sky; |
| 9 | 9 |
| 10 import '../base/hit_test.dart'; | 10 import '../base/hit_test.dart'; |
| (...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 130 // belonged to the old node, continue to live on in the replacement node. | 130 // belonged to the old node, continue to live on in the replacement node. |
| 131 node.remove(); | 131 node.remove(); |
| 132 assert(node.parent == null); | 132 assert(node.parent == null); |
| 133 } | 133 } |
| 134 | 134 |
| 135 void detachRoot(); | 135 void detachRoot(); |
| 136 | 136 |
| 137 // Returns the child which should be retained as the child of this node. | 137 // Returns the child which should be retained as the child of this node. |
| 138 Widget syncChild(Widget newNode, Widget oldNode, dynamic slot) { | 138 Widget syncChild(Widget newNode, Widget oldNode, dynamic slot) { |
| 139 | 139 |
| 140 assert(oldNode is! Component || | |
| 141 (oldNode is Component && !oldNode._disqualifiedFromEverAppearingAgain
)); // TODO(ianh): Simplify this once the analyzer is cleverer | |
| 142 | |
| 143 if (newNode == oldNode) { | 140 if (newNode == oldNode) { |
| 144 assert(newNode == null || newNode.mounted); | 141 assert(newNode == null || newNode.mounted); |
| 145 assert(newNode is! RenderObjectWrapper || | 142 assert(newNode is! RenderObjectWrapper || |
| 146 (newNode is RenderObjectWrapper && newNode._ancestor != null)); //
TODO(ianh): Simplify this once the analyzer is cleverer | 143 (newNode is RenderObjectWrapper && newNode._ancestor != null)); //
TODO(ianh): Simplify this once the analyzer is cleverer |
| 147 if (newNode != null) | 144 if (newNode != null) |
| 148 newNode.setParent(this); | 145 newNode.setParent(this); |
| 149 return newNode; // Nothing to do. Subtrees must be identical. | 146 return newNode; // Nothing to do. Subtrees must be identical. |
| 150 } | 147 } |
| 151 | 148 |
| 152 if (newNode == null) { | 149 if (newNode == null) { |
| (...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 352 sky.EventListener listener = listeners[e.type]; | 349 sky.EventListener listener = listeners[e.type]; |
| 353 if (listener != null) { | 350 if (listener != null) { |
| 354 listener(e); | 351 listener(e); |
| 355 } | 352 } |
| 356 } | 353 } |
| 357 | 354 |
| 358 } | 355 } |
| 359 | 356 |
| 360 abstract class Component extends Widget { | 357 abstract class Component extends Widget { |
| 361 | 358 |
| 362 Component({ String key, bool stateful }) | 359 Component({ String key }) |
| 363 : _stateful = stateful != null ? stateful : false, | 360 : _order = _currentOrder + 1, |
| 364 _order = _currentOrder + 1, | |
| 365 super._withKey(key); | 361 super._withKey(key); |
| 366 | 362 |
| 367 static Component _currentlyBuilding; | 363 static Component _currentlyBuilding; |
| 368 bool get _isBuilding => _currentlyBuilding == this; | 364 bool get _isBuilding => _currentlyBuilding == this; |
| 369 | 365 |
| 370 bool _stateful; | |
| 371 bool _dirty = true; | 366 bool _dirty = true; |
| 372 bool _disqualifiedFromEverAppearingAgain = false; | |
| 373 | 367 |
| 374 Widget _built; | 368 Widget _built; |
| 375 dynamic _slot; // cached slot from the last time we were synced | 369 dynamic _slot; // cached slot from the last time we were synced |
| 376 | 370 |
| 377 void didMount() { | |
| 378 assert(!_disqualifiedFromEverAppearingAgain); | |
| 379 super.didMount(); | |
| 380 } | |
| 381 | |
| 382 void remove() { | 371 void remove() { |
| 383 assert(_built != null); | 372 assert(_built != null); |
| 384 assert(root != null); | 373 assert(root != null); |
| 385 removeChild(_built); | 374 removeChild(_built); |
| 386 _built = null; | 375 _built = null; |
| 387 super.remove(); | 376 super.remove(); |
| 388 } | 377 } |
| 389 | 378 |
| 390 void detachRoot() { | 379 void detachRoot() { |
| 391 assert(_built != null); | 380 assert(_built != null); |
| 392 assert(root != null); | 381 assert(root != null); |
| 393 _built.detachRoot(); | 382 _built.detachRoot(); |
| 394 } | 383 } |
| 395 | 384 |
| 396 Set<Type> _dependencies; | 385 Set<Type> _dependencies; |
| 397 Inherited inheritedOfType(Type targetType) { | 386 Inherited inheritedOfType(Type targetType) { |
| 398 if (_dependencies == null) | 387 if (_dependencies == null) |
| 399 _dependencies = new Set<Type>(); | 388 _dependencies = new Set<Type>(); |
| 400 _dependencies.add(targetType); | 389 _dependencies.add(targetType); |
| 401 Widget ancestor = parent; | 390 Widget ancestor = parent; |
| 402 while (ancestor != null && ancestor.runtimeType != targetType) | 391 while (ancestor != null && ancestor.runtimeType != targetType) |
| 403 ancestor = ancestor.parent; | 392 ancestor = ancestor.parent; |
| 404 return ancestor; | 393 return ancestor; |
| 405 } | 394 } |
| 406 void _dependenciesChanged() { | 395 void _dependenciesChanged() { |
| 407 // called by Inherited.sync() | 396 // called by Inherited.sync() |
| 408 scheduleBuild(); | 397 scheduleBuild(); |
| 409 } | 398 } |
| 410 | 399 |
| 411 bool _retainStatefulNodeIfPossible(Component old) { | |
| 412 assert(!_disqualifiedFromEverAppearingAgain); | |
| 413 | |
| 414 if (old == null || !old._stateful) | |
| 415 return false; | |
| 416 | |
| 417 assert(runtimeType == old.runtimeType); | |
| 418 assert(key == old.key); | |
| 419 | |
| 420 // Make |this|, the newly-created object, into the "old" Component, and kill
it | |
| 421 _stateful = false; | |
| 422 _built = old._built; | |
| 423 assert(_built != null); | |
| 424 _disqualifiedFromEverAppearingAgain = true; | |
| 425 | |
| 426 // Make |old| the "new" component | |
| 427 old._built = null; | |
| 428 old._dirty = true; | |
| 429 old.syncFields(this); | |
| 430 return true; | |
| 431 } | |
| 432 | |
| 433 // This is called by _retainStatefulNodeIfPossible(), during | |
| 434 // syncChild(), just before _sync() is called. | |
| 435 // This must be implemented on any subclass that can become stateful | |
| 436 // (but don't call super.syncFields() if you inherit directly from | |
| 437 // Component, since that'll fire an assert). | |
| 438 // If you don't ever become stateful, then don't override this. | |
| 439 void syncFields(Component source) { | |
| 440 assert(false); | |
| 441 } | |
| 442 | |
| 443 // order corresponds to _build_ order, not depth in the tree. | 400 // order corresponds to _build_ order, not depth in the tree. |
| 444 // All the Components built by a particular other Component will have the | 401 // All the Components built by a particular other Component will have the |
| 445 // same order, regardless of whether one is subsequently inserted | 402 // same order, regardless of whether one is subsequently inserted |
| 446 // into another. The order is used to not tell a Component to | 403 // into another. The order is used to not tell a Component to |
| 447 // rebuild if the Component that it built has itself been rebuilt. | 404 // rebuild if the Component that it built has itself been rebuilt. |
| 448 final int _order; | 405 final int _order; |
| 449 static int _currentOrder = 0; | 406 static int _currentOrder = 0; |
| 450 | 407 |
| 451 // There are three cases here: | 408 // There are three cases here: |
| 452 // 1) Building for the first time: | 409 // 1) Building for the first time: |
| 453 // assert(_built == null && old == null) | 410 // assert(_built == null && old == null) |
| 454 // 2) Re-building (because a dirty flag got set): | 411 // 2) Re-building (because a dirty flag got set): |
| 455 // assert(_built != null && old == null) | 412 // assert(_built != null && old == null) |
| 456 // 3) Syncing against an old version | 413 // 3) Syncing against an old version |
| 457 // assert(_built == null && old != null) | 414 // assert(_built == null && old != null) |
| 458 void _sync(Component old, dynamic slot) { | 415 void _sync(Component old, dynamic slot) { |
| 459 assert(_built == null || old == null); | 416 assert(_built == null || old == null); |
| 460 assert(!_disqualifiedFromEverAppearingAgain); | |
| 461 | 417 |
| 462 _slot = slot; | 418 _slot = slot; |
| 463 | 419 |
| 464 var oldBuilt; | 420 var oldBuilt; |
| 465 if (old == null) { | 421 if (old == null) { |
| 466 oldBuilt = _built; | 422 oldBuilt = _built; |
| 467 } else { | 423 } else { |
| 468 assert(_built == null); | 424 assert(_built == null); |
| 469 oldBuilt = old._built; | 425 oldBuilt = old._built; |
| 470 } | 426 } |
| 471 | 427 |
| 472 int lastOrder = _currentOrder; | 428 int lastOrder = _currentOrder; |
| 473 _currentOrder = _order; | 429 _currentOrder = _order; |
| 474 _currentlyBuilding = this; | 430 _currentlyBuilding = this; |
| 475 _built = build(); | 431 _built = build(); |
| 476 assert(_built != null); | 432 assert(_built != null); |
| 477 _currentlyBuilding = null; | 433 _currentlyBuilding = null; |
| 478 _currentOrder = lastOrder; | 434 _currentOrder = lastOrder; |
| 479 | 435 |
| 480 _built = syncChild(_built, oldBuilt, slot); | 436 _built = syncChild(_built, oldBuilt, slot); |
| 481 assert(_built != null); | 437 assert(_built != null); |
| 482 assert(_built.parent == this); | 438 assert(_built.parent == this); |
| 483 _dirty = false; | 439 _dirty = false; |
| 484 _root = _built.root; | 440 _root = _built.root; |
| 485 assert(_root == root); // in case a subclass reintroduces it | 441 assert(_root == root); // in case a subclass reintroduces it |
| 486 assert(root != null); | 442 assert(root != null); |
| 487 } | 443 } |
| 488 | 444 |
| 489 void _buildIfDirty() { | 445 void _buildIfDirty() { |
| 490 assert(!_disqualifiedFromEverAppearingAgain); | |
| 491 if (!_dirty || !_mounted) | 446 if (!_dirty || !_mounted) |
| 492 return; | 447 return; |
| 493 | 448 |
| 494 assert(root != null); | 449 assert(root != null); |
| 495 _sync(null, _slot); | 450 _sync(null, _slot); |
| 496 } | 451 } |
| 497 | 452 |
| 498 void scheduleBuild() { | 453 void scheduleBuild() { |
| 499 assert(!_disqualifiedFromEverAppearingAgain); | |
| 500 if (_isBuilding || _dirty || !_mounted) | 454 if (_isBuilding || _dirty || !_mounted) |
| 501 return; | 455 return; |
| 502 _dirty = true; | 456 _dirty = true; |
| 503 _scheduleComponentForRender(this); | 457 _scheduleComponentForRender(this); |
| 504 } | 458 } |
| 505 | 459 |
| 506 void setState(Function fn()) { | |
| 507 assert(_stateful); | |
| 508 fn(); | |
| 509 scheduleBuild(); | |
| 510 } | |
| 511 | |
| 512 Widget build(); | 460 Widget build(); |
| 513 | 461 |
| 514 } | 462 } |
| 515 | 463 |
| 464 abstract class StatefulComponent extends Component { |
| 465 |
| 466 StatefulComponent({ String key }) : super(key: key); |
| 467 |
| 468 bool _disqualifiedFromEverAppearingAgain = false; |
| 469 |
| 470 void didMount() { |
| 471 assert(!_disqualifiedFromEverAppearingAgain); |
| 472 super.didMount(); |
| 473 } |
| 474 |
| 475 void _buildIfDirty() { |
| 476 assert(!_disqualifiedFromEverAppearingAgain); |
| 477 super._buildIfDirty(); |
| 478 } |
| 479 |
| 480 void _sync(Widget old, dynamic slot) { |
| 481 assert(!_disqualifiedFromEverAppearingAgain); |
| 482 super._sync(old, slot); |
| 483 } |
| 484 |
| 485 Widget syncChild(Widget node, Widget oldNode, dynamic slot) { |
| 486 assert(!_disqualifiedFromEverAppearingAgain); |
| 487 return super.syncChild(node, oldNode, slot); |
| 488 } |
| 489 |
| 490 bool _retainStatefulNodeIfPossible(StatefulComponent old) { |
| 491 assert(!_disqualifiedFromEverAppearingAgain); |
| 492 |
| 493 if (old == null) |
| 494 return false; |
| 495 |
| 496 assert(runtimeType == old.runtimeType); |
| 497 assert(key == old.key); |
| 498 |
| 499 // Make |this|, the newly-created object, into the "old" Component, and kill
it |
| 500 _built = old._built; |
| 501 assert(_built != null); |
| 502 _disqualifiedFromEverAppearingAgain = true; |
| 503 |
| 504 // Make |old| the "new" component |
| 505 old._built = null; |
| 506 old._dirty = true; |
| 507 old.syncFields(this); |
| 508 return true; |
| 509 } |
| 510 |
| 511 // This is called by _retainStatefulNodeIfPossible(), during |
| 512 // syncChild(), just before _sync() is called. Derived |
| 513 // classes should override this method to update `this` to |
| 514 // account for the new values the parent passed to `source`. |
| 515 // Make sure to call super.syncFields(source) unless you are |
| 516 // extending StatefulComponent directly. |
| 517 void syncFields(Component source); |
| 518 |
| 519 void setState(Function fn()) { |
| 520 assert(!_disqualifiedFromEverAppearingAgain); |
| 521 fn(); |
| 522 scheduleBuild(); |
| 523 } |
| 524 } |
| 525 |
| 516 Set<Component> _dirtyComponents = new Set<Component>(); | 526 Set<Component> _dirtyComponents = new Set<Component>(); |
| 517 bool _buildScheduled = false; | 527 bool _buildScheduled = false; |
| 518 bool _inRenderDirtyComponents = false; | 528 bool _inRenderDirtyComponents = false; |
| 519 | 529 |
| 520 List<int> _debugFrameTimes = <int>[]; | 530 List<int> _debugFrameTimes = <int>[]; |
| 521 | 531 |
| 522 void _absorbDirtyComponents(List<Component> list) { | 532 void _absorbDirtyComponents(List<Component> list) { |
| 523 list.addAll(_dirtyComponents); | 533 list.addAll(_dirtyComponents); |
| 524 _dirtyComponents.clear(); | 534 _dirtyComponents.clear(); |
| 525 list.sort((Component a, Component b) => a._order - b._order); | 535 list.sort((Component a, Component b) => a._order - b._order); |
| (...skipping 397 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 923 while (target != null && target.root == targetRoot) { | 933 while (target != null && target.root == targetRoot) { |
| 924 if (target is Listener) | 934 if (target is Listener) |
| 925 target._handleEvent(event); | 935 target._handleEvent(event); |
| 926 target = target._parent; | 936 target = target._parent; |
| 927 } | 937 } |
| 928 } | 938 } |
| 929 } | 939 } |
| 930 | 940 |
| 931 } | 941 } |
| 932 | 942 |
| 933 abstract class App extends Component { | 943 abstract class App extends StatefulComponent { |
| 934 | 944 |
| 935 // Apps are assumed to be stateful | 945 App({ String key }) : super(key: key); |
| 936 App({ String key }) : super(key: key, stateful: true); | |
| 937 | 946 |
| 938 void _handleEvent(sky.Event event) { | 947 void _handleEvent(sky.Event event) { |
| 939 if (event.type == 'back') | 948 if (event.type == 'back') |
| 940 onBack(); | 949 onBack(); |
| 941 } | 950 } |
| 942 | 951 |
| 943 void didMount() { | 952 void didMount() { |
| 944 super.didMount(); | 953 super.didMount(); |
| 945 SkyBinding.instance.addEventListener(_handleEvent); | 954 SkyBinding.instance.addEventListener(_handleEvent); |
| 946 } | 955 } |
| 947 | 956 |
| 948 void didUnmount() { | 957 void didUnmount() { |
| 949 super.didUnmount(); | 958 super.didUnmount(); |
| 950 SkyBinding.instance.removeEventListener(_handleEvent); | 959 SkyBinding.instance.removeEventListener(_handleEvent); |
| 951 } | 960 } |
| 952 | 961 |
| 962 void syncFields(Component source) { } |
| 963 |
| 953 // Override this to handle back button behavior in your app | 964 // Override this to handle back button behavior in your app |
| 954 void onBack() { } | 965 void onBack() { } |
| 955 } | 966 } |
| 956 | 967 |
| 957 abstract class AbstractWidgetRoot extends Component { | 968 abstract class AbstractWidgetRoot extends StatefulComponent { |
| 958 | 969 |
| 959 AbstractWidgetRoot() : super(stateful: true) { | 970 AbstractWidgetRoot() { |
| 960 _mounted = true; | 971 _mounted = true; |
| 961 _scheduleComponentForRender(this); | 972 _scheduleComponentForRender(this); |
| 962 } | 973 } |
| 963 | 974 |
| 964 void syncFields(AbstractWidgetRoot source) { | 975 void syncFields(AbstractWidgetRoot source) { |
| 965 assert(false); | 976 assert(false); |
| 966 // if we get here, it implies that we have a parent | 977 // if we get here, it implies that we have a parent |
| 967 } | 978 } |
| 968 | 979 |
| 969 void _buildIfDirty() { | 980 void _buildIfDirty() { |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1034 if (root.parent == null) { | 1045 if (root.parent == null) { |
| 1035 // we haven't attached it yet | 1046 // we haven't attached it yet |
| 1036 assert(_container.child == null); | 1047 assert(_container.child == null); |
| 1037 _container.child = root; | 1048 _container.child = root; |
| 1038 } | 1049 } |
| 1039 assert(root.parent == _container); | 1050 assert(root.parent == _container); |
| 1040 } | 1051 } |
| 1041 | 1052 |
| 1042 Widget build() => builder(); | 1053 Widget build() => builder(); |
| 1043 } | 1054 } |
| OLD | NEW |