| 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:math' as math; | 5 import 'dart:math' as math; |
| 6 import 'dart:sky' as sky; | 6 import 'dart:sky' as sky; |
| 7 import 'dart:sky' show Point, Offset, Size, Rect, Color, Paint, Path; | 7 import 'dart:sky' show Point, Offset, Size, Rect, Color, Paint, Path; |
| 8 | 8 |
| 9 import '../base/hit_test.dart'; | 9 import '../base/hit_test.dart'; |
| 10 import '../base/node.dart'; | 10 import '../base/node.dart'; |
| (...skipping 21 matching lines...) Expand all Loading... |
| 32 void paintChild(RenderObject child, Point point) { | 32 void paintChild(RenderObject child, Point point) { |
| 33 child.paint(this, point.toOffset()); | 33 child.paint(this, point.toOffset()); |
| 34 } | 34 } |
| 35 } | 35 } |
| 36 | 36 |
| 37 abstract class Constraints { | 37 abstract class Constraints { |
| 38 const Constraints(); | 38 const Constraints(); |
| 39 bool get isTight; | 39 bool get isTight; |
| 40 } | 40 } |
| 41 | 41 |
| 42 typedef void LayoutCallback(Constraints constraints); |
| 43 |
| 42 abstract class RenderObject extends AbstractNode implements HitTestTarget { | 44 abstract class RenderObject extends AbstractNode implements HitTestTarget { |
| 43 | 45 |
| 44 // LAYOUT | 46 // LAYOUT |
| 45 | 47 |
| 46 // parentData is only for use by the RenderObject that actually lays this | 48 // parentData is only for use by the RenderObject that actually lays this |
| 47 // node out, and any other nodes who happen to know exactly what | 49 // node out, and any other nodes who happen to know exactly what |
| 48 // kind of node that is. | 50 // kind of node that is. |
| 49 dynamic parentData; // TODO(ianh): change the type of this back to ParentData
once the analyzer is cleverer | 51 dynamic parentData; // TODO(ianh): change the type of this back to ParentData
once the analyzer is cleverer |
| 50 void setupParentData(RenderObject child) { | 52 void setupParentData(RenderObject child) { |
| 51 // override this to setup .parentData correctly for your class | 53 // override this to setup .parentData correctly for your class |
| 52 assert(!debugDoingLayout); | 54 assert(debugCanPerformMutations); |
| 53 assert(!debugDoingPaint); | |
| 54 if (child.parentData is! ParentData) | 55 if (child.parentData is! ParentData) |
| 55 child.parentData = new ParentData(); | 56 child.parentData = new ParentData(); |
| 56 } | 57 } |
| 57 | 58 |
| 58 void adoptChild(RenderObject child) { // only for use by subclasses | 59 void adoptChild(RenderObject child) { // only for use by subclasses |
| 59 // call this whenever you decide a node is a child | 60 // call this whenever you decide a node is a child |
| 60 assert(!debugDoingLayout); | 61 assert(debugCanPerformMutations); |
| 61 assert(!debugDoingPaint); | |
| 62 assert(child != null); | 62 assert(child != null); |
| 63 setupParentData(child); | 63 setupParentData(child); |
| 64 super.adoptChild(child); | 64 super.adoptChild(child); |
| 65 markNeedsLayout(); | 65 markNeedsLayout(); |
| 66 } | 66 } |
| 67 void dropChild(RenderObject child) { // only for use by subclasses | 67 void dropChild(RenderObject child) { // only for use by subclasses |
| 68 assert(!debugDoingLayout); | 68 assert(debugCanPerformMutations); |
| 69 assert(!debugDoingPaint); | |
| 70 assert(child != null); | 69 assert(child != null); |
| 71 assert(child.parentData != null); | 70 assert(child.parentData != null); |
| 72 child._cleanRelayoutSubtreeRoot(); | 71 child._cleanRelayoutSubtreeRoot(); |
| 73 child.parentData.detach(); | 72 child.parentData.detach(); |
| 74 super.dropChild(child); | 73 super.dropChild(child); |
| 75 markNeedsLayout(); | 74 markNeedsLayout(); |
| 76 } | 75 } |
| 77 | 76 |
| 78 static List<RenderObject> _nodesNeedingLayout = new List<RenderObject>(); | |
| 79 static bool _debugDoingLayout = false; | 77 static bool _debugDoingLayout = false; |
| 80 static bool get debugDoingLayout => _debugDoingLayout; | 78 static bool get debugDoingLayout => _debugDoingLayout; |
| 81 bool _debugDoingThisResize = false; | 79 bool _debugDoingThisResize = false; |
| 82 bool get debugDoingThisResize => _debugDoingThisResize; | 80 bool get debugDoingThisResize => _debugDoingThisResize; |
| 83 bool _debugDoingThisLayout = false; | 81 bool _debugDoingThisLayout = false; |
| 84 bool get debugDoingThisLayout => _debugDoingThisLayout; | 82 bool get debugDoingThisLayout => _debugDoingThisLayout; |
| 85 static RenderObject _debugActiveLayout = null; | 83 static RenderObject _debugActiveLayout = null; |
| 86 static RenderObject get debugActiveLayout => _debugActiveLayout; | 84 static RenderObject get debugActiveLayout => _debugActiveLayout; |
| 85 bool _debugDoingThisLayoutWithCallback = false; |
| 86 bool _debugMutationsLocked = false; |
| 87 bool _debugCanParentUseSize; | 87 bool _debugCanParentUseSize; |
| 88 bool get debugCanParentUseSize => _debugCanParentUseSize; | 88 bool get debugCanParentUseSize => _debugCanParentUseSize; |
| 89 bool get debugCanPerformMutations { |
| 90 RenderObject node = this; |
| 91 while (true) { |
| 92 if (node._debugDoingThisLayoutWithCallback) |
| 93 return true; |
| 94 if (node._debugMutationsLocked) |
| 95 return false; |
| 96 if (node.parent is! RenderObject) |
| 97 return true; |
| 98 node = node.parent; |
| 99 } |
| 100 } |
| 101 |
| 102 static List<RenderObject> _nodesNeedingLayout = new List<RenderObject>(); |
| 89 bool _needsLayout = true; | 103 bool _needsLayout = true; |
| 90 bool get needsLayout => _needsLayout; | 104 bool get needsLayout => _needsLayout; |
| 91 RenderObject _relayoutSubtreeRoot; | 105 RenderObject _relayoutSubtreeRoot; |
| 92 Constraints _constraints; | 106 Constraints _constraints; |
| 93 Constraints get constraints => _constraints; | 107 Constraints get constraints => _constraints; |
| 94 bool debugDoesMeetConstraints(); // override this in a subclass to verify that
your state matches the constraints object | 108 bool debugDoesMeetConstraints(); // override this in a subclass to verify that
your state matches the constraints object |
| 95 bool debugAncestorsAlreadyMarkedNeedsLayout() { | 109 bool debugAncestorsAlreadyMarkedNeedsLayout() { |
| 96 if (_relayoutSubtreeRoot == null) | 110 if (_relayoutSubtreeRoot == null) |
| 97 return true; // we haven't yet done layout even once, so there's nothing f
or us to do | 111 return true; // we haven't yet done layout even once, so there's nothing f
or us to do |
| 98 RenderObject node = this; | 112 RenderObject node = this; |
| 99 while (node != _relayoutSubtreeRoot) { | 113 while (node != _relayoutSubtreeRoot) { |
| 100 assert(node._relayoutSubtreeRoot == _relayoutSubtreeRoot); | 114 assert(node._relayoutSubtreeRoot == _relayoutSubtreeRoot); |
| 101 assert(node.parent != null); | 115 assert(node.parent != null); |
| 102 node = node.parent as RenderObject; | 116 node = node.parent as RenderObject; |
| 103 if (!node._needsLayout) | 117 if (!node._needsLayout) |
| 104 return false; | 118 return false; |
| 105 } | 119 } |
| 106 assert(node._relayoutSubtreeRoot == node); | 120 assert(node._relayoutSubtreeRoot == node); |
| 107 return true; | 121 return true; |
| 108 } | 122 } |
| 109 void markNeedsLayout() { | 123 void markNeedsLayout() { |
| 110 assert(!debugDoingLayout); | 124 assert(debugCanPerformMutations); |
| 111 assert(!debugDoingPaint); | |
| 112 if (_needsLayout) { | 125 if (_needsLayout) { |
| 113 assert(debugAncestorsAlreadyMarkedNeedsLayout()); | 126 assert(debugAncestorsAlreadyMarkedNeedsLayout()); |
| 114 return; | 127 return; |
| 115 } | 128 } |
| 116 _needsLayout = true; | 129 _needsLayout = true; |
| 117 assert(_relayoutSubtreeRoot != null); | 130 assert(_relayoutSubtreeRoot != null); |
| 118 if (_relayoutSubtreeRoot != this) { | 131 if (_relayoutSubtreeRoot != this) { |
| 119 final parent = this.parent; // TODO(ianh): Remove this once the analyzer i
s cleverer | 132 final parent = this.parent; // TODO(ianh): Remove this once the analyzer i
s cleverer |
| 120 assert(parent is RenderObject); | 133 assert(parent is RenderObject); |
| 121 parent.markNeedsLayout(); | 134 parent.markNeedsLayout(); |
| (...skipping 30 matching lines...) Expand all Loading... |
| 152 node.layoutWithoutResize(); | 165 node.layoutWithoutResize(); |
| 153 }); | 166 }); |
| 154 } finally { | 167 } finally { |
| 155 _debugDoingLayout = false; | 168 _debugDoingLayout = false; |
| 156 sky.tracing.end('RenderObject.flushLayout'); | 169 sky.tracing.end('RenderObject.flushLayout'); |
| 157 } | 170 } |
| 158 } | 171 } |
| 159 void layoutWithoutResize() { | 172 void layoutWithoutResize() { |
| 160 try { | 173 try { |
| 161 assert(_relayoutSubtreeRoot == this); | 174 assert(_relayoutSubtreeRoot == this); |
| 162 _debugCanParentUseSize = false; | 175 RenderObject debugPreviousActiveLayout; |
| 163 _debugDoingThisLayout = true; | 176 assert(!_debugMutationsLocked); |
| 164 RenderObject debugPreviousActiveLayout = _debugActiveLayout; | 177 assert(!_debugDoingThisLayoutWithCallback); |
| 165 _debugActiveLayout = this; | 178 assert(() { |
| 179 _debugMutationsLocked = true; |
| 180 _debugCanParentUseSize = false; |
| 181 _debugDoingThisLayout = true; |
| 182 debugPreviousActiveLayout = _debugActiveLayout; |
| 183 _debugActiveLayout = this; |
| 184 return true; |
| 185 }); |
| 166 performLayout(); | 186 performLayout(); |
| 167 _debugActiveLayout = debugPreviousActiveLayout; | 187 assert(() { |
| 168 _debugDoingThisLayout = false; | 188 _debugActiveLayout = debugPreviousActiveLayout; |
| 169 _debugCanParentUseSize = null; | 189 _debugDoingThisLayout = false; |
| 190 _debugCanParentUseSize = null; |
| 191 _debugMutationsLocked = false; |
| 192 return true; |
| 193 }); |
| 170 } catch (e, stack) { | 194 } catch (e, stack) { |
| 171 print('Exception raised during layout:\n${e}\nContext:\n${this}'); | 195 print('Exception raised during layout:\n${e}\nContext:\n${this}'); |
| 172 print(stack); | 196 print(stack); |
| 173 return; | 197 return; |
| 174 } | 198 } |
| 175 _needsLayout = false; | 199 _needsLayout = false; |
| 176 markNeedsPaint(); | 200 markNeedsPaint(); |
| 177 } | 201 } |
| 178 void layout(Constraints constraints, { bool parentUsesSize: false }) { | 202 void layout(Constraints constraints, { bool parentUsesSize: false }) { |
| 179 final parent = this.parent; // TODO(ianh): Remove this once the analyzer is
cleverer | 203 final parent = this.parent; // TODO(ianh): Remove this once the analyzer is
cleverer |
| 180 RenderObject relayoutSubtreeRoot; | 204 RenderObject relayoutSubtreeRoot; |
| 181 if (!parentUsesSize || sizedByParent || constraints.isTight || parent is! Re
nderObject) | 205 if (!parentUsesSize || sizedByParent || constraints.isTight || parent is! Re
nderObject) |
| 182 relayoutSubtreeRoot = this; | 206 relayoutSubtreeRoot = this; |
| 183 else | 207 else |
| 184 relayoutSubtreeRoot = parent._relayoutSubtreeRoot; | 208 relayoutSubtreeRoot = parent._relayoutSubtreeRoot; |
| 185 assert(parent == this.parent); // TODO(ianh): Remove this once the analyzer
is cleverer | 209 assert(parent == this.parent); // TODO(ianh): Remove this once the analyzer
is cleverer |
| 186 if (!needsLayout && constraints == _constraints && relayoutSubtreeRoot == _r
elayoutSubtreeRoot) | 210 if (!needsLayout && constraints == _constraints && relayoutSubtreeRoot == _r
elayoutSubtreeRoot) |
| 187 return; | 211 return; |
| 188 _constraints = constraints; | 212 _constraints = constraints; |
| 189 _relayoutSubtreeRoot = relayoutSubtreeRoot; | 213 _relayoutSubtreeRoot = relayoutSubtreeRoot; |
| 190 _debugCanParentUseSize = parentUsesSize; | 214 assert(!_debugMutationsLocked); |
| 215 assert(!_debugDoingThisLayoutWithCallback); |
| 216 assert(() { |
| 217 _debugMutationsLocked = true; |
| 218 _debugCanParentUseSize = parentUsesSize; |
| 219 return true; |
| 220 }); |
| 191 if (sizedByParent) { | 221 if (sizedByParent) { |
| 192 _debugDoingThisResize = true; | 222 assert(() { _debugDoingThisResize = true; return true; }); |
| 193 performResize(); | 223 performResize(); |
| 194 _debugDoingThisResize = false; | 224 assert(() { _debugDoingThisResize = false; return true; }); |
| 195 } | 225 } |
| 196 _debugDoingThisLayout = true; | 226 RenderObject debugPreviousActiveLayout; |
| 197 RenderObject debugPreviousActiveLayout = _debugActiveLayout; | 227 assert(() { |
| 198 _debugActiveLayout = this; | 228 _debugDoingThisLayout = true; |
| 229 debugPreviousActiveLayout = _debugActiveLayout; |
| 230 _debugActiveLayout = this; |
| 231 return true; |
| 232 }); |
| 199 performLayout(); | 233 performLayout(); |
| 200 _debugActiveLayout = debugPreviousActiveLayout; | 234 assert(() { |
| 201 _debugDoingThisLayout = false; | 235 _debugActiveLayout = debugPreviousActiveLayout; |
| 202 _debugCanParentUseSize = null; | 236 _debugDoingThisLayout = false; |
| 237 _debugCanParentUseSize = null; |
| 238 _debugMutationsLocked = false; |
| 239 return true; |
| 240 }); |
| 203 assert(debugDoesMeetConstraints()); | 241 assert(debugDoesMeetConstraints()); |
| 204 _needsLayout = false; | 242 _needsLayout = false; |
| 205 markNeedsPaint(); | 243 markNeedsPaint(); |
| 206 assert(parent == this.parent); // TODO(ianh): Remove this once the analyzer
is cleverer | 244 assert(parent == this.parent); // TODO(ianh): Remove this once the analyzer
is cleverer |
| 207 } | 245 } |
| 208 bool get sizedByParent => false; // return true if the constraints are the onl
y input to the sizing algorithm (in particular, child nodes have no impact) | 246 bool get sizedByParent => false; // return true if the constraints are the onl
y input to the sizing algorithm (in particular, child nodes have no impact) |
| 209 void performResize(); // set the local dimensions, using only the constraints
(only called if sizedByParent is true) | 247 void performResize(); // set the local dimensions, using only the constraints
(only called if sizedByParent is true) |
| 210 void performLayout(); | 248 void performLayout(); |
| 211 // Override this to perform relayout without your parent's | 249 // Override this to perform relayout without your parent's |
| 212 // involvement. | 250 // involvement. |
| 213 // | 251 // |
| 214 // This is called during layout. If sizedByParent is true, then | 252 // This is called during layout. If sizedByParent is true, then |
| 215 // performLayout() should not change your dimensions, only do that | 253 // performLayout() should not change your dimensions, only do that |
| 216 // in performResize(). If sizedByParent is false, then set both | 254 // in performResize(). If sizedByParent is false, then set both |
| 217 // your dimensions and do your children's layout here. | 255 // your dimensions and do your children's layout here. |
| 218 // | 256 // |
| 219 // When calling layout() on your children, pass in | 257 // When calling layout() on your children, pass in |
| 220 // "parentUsesSize: true" if your size or layout is dependent on | 258 // "parentUsesSize: true" if your size or layout is dependent on |
| 221 // your child's size or intrinsic dimensions. | 259 // your child's size or intrinsic dimensions. |
| 260 void invokeLayoutCallback(LayoutCallback callback) { |
| 261 assert(_debugMutationsLocked); |
| 262 assert(_debugDoingThisLayout); |
| 263 assert(!_debugDoingThisLayoutWithCallback); |
| 264 assert(() { |
| 265 _debugDoingThisLayoutWithCallback = true; |
| 266 return true; |
| 267 }); |
| 268 callback(constraints); |
| 269 assert(() { |
| 270 _debugDoingThisLayoutWithCallback = false; |
| 271 return true; |
| 272 }); |
| 273 } |
| 222 | 274 |
| 223 // when the parent has rotated (e.g. when the screen has been turned | 275 // when the parent has rotated (e.g. when the screen has been turned |
| 224 // 90 degrees), immediately prior to layout() being called for the | 276 // 90 degrees), immediately prior to layout() being called for the |
| 225 // new dimensions, rotate() is called with the old and new angles. | 277 // new dimensions, rotate() is called with the old and new angles. |
| 226 // The next time paint() is called, the coordinate space will have | 278 // The next time paint() is called, the coordinate space will have |
| 227 // been rotated N quarter-turns clockwise, where: | 279 // been rotated N quarter-turns clockwise, where: |
| 228 // N = newAngle-oldAngle | 280 // N = newAngle-oldAngle |
| 229 // ...but the rendering is expected to remain the same, pixel for | 281 // ...but the rendering is expected to remain the same, pixel for |
| 230 // pixel, on the output device. Then, the layout() method or | 282 // pixel, on the output device. Then, the layout() method or |
| 231 // equivalent will be invoked. | 283 // equivalent will be invoked. |
| (...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 444 } | 496 } |
| 445 void addAll(List<ChildType> children) { | 497 void addAll(List<ChildType> children) { |
| 446 if (children != null) | 498 if (children != null) |
| 447 for (ChildType child in children) | 499 for (ChildType child in children) |
| 448 add(child); | 500 add(child); |
| 449 } | 501 } |
| 450 void _removeFromChildList(ChildType child) { | 502 void _removeFromChildList(ChildType child) { |
| 451 assert(child.parentData is ParentDataType); | 503 assert(child.parentData is ParentDataType); |
| 452 assert(_debugUltimatePreviousSiblingOf(child, equals: _firstChild)); | 504 assert(_debugUltimatePreviousSiblingOf(child, equals: _firstChild)); |
| 453 assert(_debugUltimateNextSiblingOf(child, equals: _lastChild)); | 505 assert(_debugUltimateNextSiblingOf(child, equals: _lastChild)); |
| 454 _childCount -= 1; | |
| 455 assert(_childCount >= 0); | 506 assert(_childCount >= 0); |
| 456 if (child.parentData.previousSibling == null) { | 507 if (child.parentData.previousSibling == null) { |
| 457 assert(_firstChild == child); | 508 assert(_firstChild == child); |
| 458 _firstChild = child.parentData.nextSibling; | 509 _firstChild = child.parentData.nextSibling; |
| 459 } else { | 510 } else { |
| 460 assert(child.parentData.previousSibling.parentData is ParentDataType); | 511 assert(child.parentData.previousSibling.parentData is ParentDataType); |
| 461 child.parentData.previousSibling.parentData.nextSibling = child.parentData
.nextSibling; | 512 child.parentData.previousSibling.parentData.nextSibling = child.parentData
.nextSibling; |
| 462 } | 513 } |
| 463 if (child.parentData.nextSibling == null) { | 514 if (child.parentData.nextSibling == null) { |
| 464 assert(_lastChild == child); | 515 assert(_lastChild == child); |
| 465 _lastChild = child.parentData.previousSibling; | 516 _lastChild = child.parentData.previousSibling; |
| 466 } else { | 517 } else { |
| 467 assert(child.parentData.nextSibling.parentData is ParentDataType); | 518 assert(child.parentData.nextSibling.parentData is ParentDataType); |
| 468 child.parentData.nextSibling.parentData.previousSibling = child.parentData
.previousSibling; | 519 child.parentData.nextSibling.parentData.previousSibling = child.parentData
.previousSibling; |
| 469 } | 520 } |
| 470 child.parentData.previousSibling = null; | 521 child.parentData.previousSibling = null; |
| 471 child.parentData.nextSibling = null; | 522 child.parentData.nextSibling = null; |
| 523 _childCount -= 1; |
| 472 } | 524 } |
| 473 void remove(ChildType child) { | 525 void remove(ChildType child) { |
| 474 _removeFromChildList(child); | 526 _removeFromChildList(child); |
| 475 dropChild(child); | 527 dropChild(child); |
| 476 } | 528 } |
| 529 void removeAll() { |
| 530 ChildType child = _firstChild; |
| 531 while (child != null) { |
| 532 assert(child.parentData is ParentDataType); |
| 533 ChildType next = child.parentData.nextSibling; |
| 534 child.parentData.previousSibling = null; |
| 535 child.parentData.nextSibling = null; |
| 536 dropChild(child); |
| 537 child = next; |
| 538 } |
| 539 _firstChild = null; |
| 540 _lastChild = null; |
| 541 _childCount = 0; |
| 542 } |
| 477 void move(ChildType child, { ChildType before }) { | 543 void move(ChildType child, { ChildType before }) { |
| 478 assert(child != this); | 544 assert(child != this); |
| 479 assert(before != this); | 545 assert(before != this); |
| 480 assert(child != before); | 546 assert(child != before); |
| 481 assert(child.parent == this); | 547 assert(child.parent == this); |
| 482 assert(child.parentData is ParentDataType); | 548 assert(child.parentData is ParentDataType); |
| 483 if (child.parentData.nextSibling == before) | 549 if (child.parentData.nextSibling == before) |
| 484 return; | 550 return; |
| 485 _removeFromChildList(child); | 551 _removeFromChildList(child); |
| 486 _addToChildList(child, before: before); | 552 _addToChildList(child, before: before); |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 530 int count = 1; | 596 int count = 1; |
| 531 ChildType child = _firstChild; | 597 ChildType child = _firstChild; |
| 532 while (child != null) { | 598 while (child != null) { |
| 533 result += '${prefix}child ${count}: ${child.toString(prefix)}'; | 599 result += '${prefix}child ${count}: ${child.toString(prefix)}'; |
| 534 count += 1; | 600 count += 1; |
| 535 child = child.parentData.nextSibling; | 601 child = child.parentData.nextSibling; |
| 536 } | 602 } |
| 537 return result; | 603 return result; |
| 538 } | 604 } |
| 539 } | 605 } |
| OLD | NEW |