Chromium Code Reviews| 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 205 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 216 | 216 |
| 217 } | 217 } |
| 218 | 218 |
| 219 class ParentDataNode extends TagNode { | 219 class ParentDataNode extends TagNode { |
| 220 ParentDataNode(Widget child, this.parentData, { String key }) | 220 ParentDataNode(Widget child, this.parentData, { String key }) |
| 221 : super(child, key: key); | 221 : super(child, key: key); |
| 222 final ParentData parentData; | 222 final ParentData parentData; |
| 223 } | 223 } |
| 224 | 224 |
| 225 abstract class Inherited extends TagNode { | 225 abstract class Inherited extends TagNode { |
| 226 | |
| 226 Inherited({ String key, Widget child }) : super._withKey(child, key); | 227 Inherited({ String key, Widget child }) : super._withKey(child, key); |
| 228 | |
| 229 void _sync(Widget old, dynamic slot) { | |
| 230 if (old != null && syncShouldNotify(old)) { | |
| 231 void notifyChildren(Widget child) { | |
| 232 if (child is Component && | |
| 233 child._dependencies != null && | |
| 234 child._dependencies.contains(runtimeType)) | |
|
abarth-chromium
2015/07/06 20:13:43
Do we want to capture runtimeType in a final varia
| |
| 235 child._dependenciesChanged(); | |
| 236 if (child.runtimeType != this) | |
|
abarth-chromium
2015/07/06 20:13:43
child.runtimeType != runtimeType
?
| |
| 237 child.walkChildren(notifyChildren); | |
| 238 } | |
| 239 walkChildren(notifyChildren); | |
| 240 } | |
| 241 super._sync(old, slot); | |
| 242 } | |
| 243 | |
| 244 bool syncShouldNotify(Inherited old); | |
| 245 | |
| 227 } | 246 } |
| 228 | 247 |
| 229 typedef void GestureEventListener(sky.GestureEvent e); | 248 typedef void GestureEventListener(sky.GestureEvent e); |
| 230 typedef void PointerEventListener(sky.PointerEvent e); | 249 typedef void PointerEventListener(sky.PointerEvent e); |
| 231 typedef void EventListener(sky.Event e); | 250 typedef void EventListener(sky.Event e); |
| 232 | 251 |
| 233 class Listener extends TagNode { | 252 class Listener extends TagNode { |
| 234 | 253 |
| 235 Listener({ | 254 Listener({ |
| 236 String key, | 255 String key, |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 347 _built = null; | 366 _built = null; |
| 348 super.remove(); | 367 super.remove(); |
| 349 } | 368 } |
| 350 | 369 |
| 351 void detachRoot() { | 370 void detachRoot() { |
| 352 assert(_built != null); | 371 assert(_built != null); |
| 353 assert(root != null); | 372 assert(root != null); |
| 354 _built.detachRoot(); | 373 _built.detachRoot(); |
| 355 } | 374 } |
| 356 | 375 |
| 376 Set<Type> _dependencies; | |
| 357 Inherited inheritedOfType(Type targetType) { | 377 Inherited inheritedOfType(Type targetType) { |
| 378 if (_dependencies == null) | |
| 379 _dependencies = new Set<Type>(); | |
| 380 _dependencies.add(targetType); | |
| 358 Widget ancestor = parent; | 381 Widget ancestor = parent; |
| 359 while (ancestor != null && ancestor.runtimeType != targetType) | 382 while (ancestor != null && ancestor.runtimeType != targetType) |
| 360 ancestor = ancestor.parent; | 383 ancestor = ancestor.parent; |
| 361 return ancestor; | 384 return ancestor; |
| 362 } | 385 } |
| 386 void _dependenciesChanged() { | |
| 387 // called by Inherited.sync() | |
| 388 scheduleBuild(); | |
| 389 } | |
| 363 | 390 |
| 364 bool _retainStatefulNodeIfPossible(Widget old) { | 391 bool _retainStatefulNodeIfPossible(Component old) { |
| 365 assert(!_disqualifiedFromEverAppearingAgain); | 392 assert(!_disqualifiedFromEverAppearingAgain); |
| 366 | 393 |
| 367 Component oldComponent = old as Component; | 394 if (old == null || !old._stateful) |
| 368 if (oldComponent == null || !oldComponent._stateful) | |
| 369 return false; | 395 return false; |
| 370 | 396 |
| 371 assert(runtimeType == oldComponent.runtimeType); | 397 assert(runtimeType == old.runtimeType); |
| 372 assert(key == oldComponent.key); | 398 assert(key == old.key); |
| 373 | 399 |
| 374 // Make |this|, the newly-created object, into the "old" Component, and kill it | 400 // Make |this|, the newly-created object, into the "old" Component, and kill it |
| 375 _stateful = false; | 401 _stateful = false; |
| 376 _built = oldComponent._built; | 402 _built = old._built; |
| 377 assert(_built != null); | 403 assert(_built != null); |
| 378 _disqualifiedFromEverAppearingAgain = true; | 404 _disqualifiedFromEverAppearingAgain = true; |
| 379 | 405 |
| 380 // Make |oldComponent| the "new" component | 406 // Make |old| the "new" component |
| 381 oldComponent._built = null; | 407 old._built = null; |
| 382 oldComponent._dirty = true; | 408 old._dirty = true; |
| 383 oldComponent.syncFields(this); | 409 old.syncFields(this); |
| 384 return true; | 410 return true; |
| 385 } | 411 } |
| 386 | 412 |
| 387 // This is called by _retainStatefulNodeIfPossible(), during | 413 // This is called by _retainStatefulNodeIfPossible(), during |
| 388 // syncChild(), just before _sync() is called. | 414 // syncChild(), just before _sync() is called. |
| 389 // This must be implemented on any subclass that can become stateful | 415 // This must be implemented on any subclass that can become stateful |
| 390 // (but don't call super.syncFields() if you inherit directly from | 416 // (but don't call super.syncFields() if you inherit directly from |
| 391 // Component, since that'll fire an assert). | 417 // Component, since that'll fire an assert). |
| 392 // If you don't ever become stateful, then don't override this. | 418 // If you don't ever become stateful, then don't override this. |
| 393 void syncFields(Component source) { | 419 void syncFields(Component source) { |
| 394 assert(false); | 420 assert(false); |
| 395 } | 421 } |
| 396 | 422 |
| 423 // order corresponds to _build_ order, not depth in the tree. | |
| 424 // All the Components built by a particular other Component will have the | |
| 425 // same order, regardless of whether one is subsequently inserted | |
| 426 // into another. The order is used to not tell a Component to | |
| 427 // rebuild if the Component that it built has itself been rebuilt. | |
| 397 final int _order; | 428 final int _order; |
| 398 static int _currentOrder = 0; | 429 static int _currentOrder = 0; |
| 399 | 430 |
| 400 // There are three cases here: | 431 // There are three cases here: |
| 401 // 1) Building for the first time: | 432 // 1) Building for the first time: |
| 402 // assert(_built == null && old == null) | 433 // assert(_built == null && old == null) |
| 403 // 2) Re-building (because a dirty flag got set): | 434 // 2) Re-building (because a dirty flag got set): |
| 404 // assert(_built != null && old == null) | 435 // assert(_built != null && old == null) |
| 405 // 3) Syncing against an old version | 436 // 3) Syncing against an old version |
| 406 // assert(_built == null && old != null) | 437 // assert(_built == null && old != null) |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 439 void _buildIfDirty() { | 470 void _buildIfDirty() { |
| 440 assert(!_disqualifiedFromEverAppearingAgain); | 471 assert(!_disqualifiedFromEverAppearingAgain); |
| 441 if (!_dirty || !_mounted) | 472 if (!_dirty || !_mounted) |
| 442 return; | 473 return; |
| 443 | 474 |
| 444 assert(root != null); | 475 assert(root != null); |
| 445 _sync(null, _slot); | 476 _sync(null, _slot); |
| 446 } | 477 } |
| 447 | 478 |
| 448 void scheduleBuild() { | 479 void scheduleBuild() { |
| 449 setState(() {}); | 480 assert(!_disqualifiedFromEverAppearingAgain); |
| 481 if (_isBuilding || _dirty || !_mounted) | |
| 482 return; | |
| 483 _dirty = true; | |
| 484 _scheduleComponentForRender(this); | |
| 450 } | 485 } |
| 451 | 486 |
| 452 void setState(Function fn()) { | 487 void setState(Function fn()) { |
| 453 assert(!_disqualifiedFromEverAppearingAgain); | |
| 454 assert(_stateful); | 488 assert(_stateful); |
| 455 fn(); | 489 fn(); |
| 456 if (_isBuilding || _dirty || !_mounted) | 490 scheduleBuild(); |
| 457 return; | |
| 458 | |
| 459 _dirty = true; | |
| 460 _scheduleComponentForRender(this); | |
| 461 } | 491 } |
| 462 | 492 |
| 463 Widget build(); | 493 Widget build(); |
| 464 | 494 |
| 465 } | 495 } |
| 466 | 496 |
| 467 Set<Component> _dirtyComponents = new Set<Component>(); | 497 Set<Component> _dirtyComponents = new Set<Component>(); |
| 468 bool _buildScheduled = false; | 498 bool _buildScheduled = false; |
| 469 bool _inRenderDirtyComponents = false; | 499 bool _inRenderDirtyComponents = false; |
| 470 | 500 |
| 471 List<int> _debugFrameTimes = <int>[]; | 501 List<int> _debugFrameTimes = <int>[]; |
| 472 | 502 |
| 503 void _absorbDirtyComponents(List<Component> list) { | |
| 504 list.addAll(_dirtyComponents); | |
| 505 _dirtyComponents.clear(); | |
| 506 list.sort((Component a, Component b) => a._order - b._order); | |
| 507 } | |
| 508 | |
| 473 void _buildDirtyComponents() { | 509 void _buildDirtyComponents() { |
| 474 Stopwatch sw; | 510 Stopwatch sw; |
| 475 if (_shouldLogRenderDuration) | 511 if (_shouldLogRenderDuration) |
| 476 sw = new Stopwatch()..start(); | 512 sw = new Stopwatch()..start(); |
| 477 | 513 |
| 514 _inRenderDirtyComponents = true; | |
| 478 try { | 515 try { |
| 479 sky.tracing.begin('Widgets._buildDirtyComponents'); | 516 sky.tracing.begin('Widgets._buildDirtyComponents'); |
| 480 _inRenderDirtyComponents = true; | 517 List<Component> sortedDirtyComponents = new List<Component>(); |
| 481 | 518 _absorbDirtyComponents(sortedDirtyComponents); |
| 482 List<Component> sortedDirtyComponents = _dirtyComponents.toList(); | 519 int index = 0; |
| 483 sortedDirtyComponents.sort((Component a, Component b) => a._order - b._order ); | 520 while (index < sortedDirtyComponents.length) { |
| 484 for (var comp in sortedDirtyComponents) { | 521 Component component = sortedDirtyComponents[index]; |
| 485 comp._buildIfDirty(); | 522 component._buildIfDirty(); |
| 523 if (_dirtyComponents.length > 0) { | |
| 524 // the following assert verifies that we're not rebuilding anyone twice in one frame | |
| 525 assert(_dirtyComponents.every((Component component) => !sortedDirtyCompo nents.contains(component))); | |
| 526 _absorbDirtyComponents(sortedDirtyComponents); | |
| 527 index = 0; | |
| 528 } else { | |
| 529 index += 1; | |
| 530 } | |
| 486 } | 531 } |
| 487 | 532 } finally { |
| 488 _dirtyComponents.clear(); | |
| 489 _buildScheduled = false; | 533 _buildScheduled = false; |
| 490 } finally { | |
| 491 _inRenderDirtyComponents = false; | 534 _inRenderDirtyComponents = false; |
| 492 sky.tracing.end('Widgets._buildDirtyComponents'); | 535 sky.tracing.end('Widgets._buildDirtyComponents'); |
| 493 } | 536 } |
| 494 | 537 |
| 495 Widget._notifyMountStatusChanged(); | 538 Widget._notifyMountStatusChanged(); |
| 496 | 539 |
| 497 if (_shouldLogRenderDuration) { | 540 if (_shouldLogRenderDuration) { |
| 498 sw.stop(); | 541 sw.stop(); |
| 499 _debugFrameTimes.add(sw.elapsedMicroseconds); | 542 _debugFrameTimes.add(sw.elapsedMicroseconds); |
| 500 if (_debugFrameTimes.length >= 1000) { | 543 if (_debugFrameTimes.length >= 1000) { |
| 501 _debugFrameTimes.sort(); | 544 _debugFrameTimes.sort(); |
| 502 const int i = 99; | 545 const int i = 99; |
| 503 print('_buildDirtyComponents: ${i+1}th fastest frame out of the last ${_de bugFrameTimes.length}: ${_debugFrameTimes[i]} microseconds'); | 546 print('_buildDirtyComponents: ${i+1}th fastest frame out of the last ${_de bugFrameTimes.length}: ${_debugFrameTimes[i]} microseconds'); |
| 504 _debugFrameTimes.clear(); | 547 _debugFrameTimes.clear(); |
| 505 } | 548 } |
| 506 } | 549 } |
| 507 } | 550 } |
| 508 | 551 |
| 509 void _scheduleComponentForRender(Component c) { | 552 void _scheduleComponentForRender(Component c) { |
| 510 assert(!_inRenderDirtyComponents); | |
| 511 _dirtyComponents.add(c); | 553 _dirtyComponents.add(c); |
| 512 | |
| 513 if (!_buildScheduled) { | 554 if (!_buildScheduled) { |
| 514 _buildScheduled = true; | 555 _buildScheduled = true; |
| 515 new Future.microtask(_buildDirtyComponents); | 556 new Future.microtask(_buildDirtyComponents); |
| 516 } | 557 } |
| 517 } | 558 } |
| 518 | 559 |
| 519 | 560 |
| 520 // RenderObjectWrappers correspond to a desired state of a RenderObject. | 561 // RenderObjectWrappers correspond to a desired state of a RenderObject. |
| 521 // They are fully immutable, with one exception: A Widget which is a | 562 // They are fully immutable, with one exception: A Widget which is a |
| 522 // Component which lives within an MultiChildRenderObjectWrapper's | 563 // Component which lives within an MultiChildRenderObjectWrapper's |
| (...skipping 446 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 969 if (root.parent == null) { | 1010 if (root.parent == null) { |
| 970 // we haven't attached it yet | 1011 // we haven't attached it yet |
| 971 assert(_container.child == null); | 1012 assert(_container.child == null); |
| 972 _container.child = root; | 1013 _container.child = root; |
| 973 } | 1014 } |
| 974 assert(root.parent == _container); | 1015 assert(root.parent == _container); |
| 975 } | 1016 } |
| 976 | 1017 |
| 977 Widget build() => builder(); | 1018 Widget build() => builder(); |
| 978 } | 1019 } |
| OLD | NEW |