Index: sky/sdk/lib/framework/layout2.dart |
diff --git a/sky/sdk/lib/framework/layout2.dart b/sky/sdk/lib/framework/layout2.dart |
index e7e0b6d9a17a0523c52429a29db24b62d12629d2..98a929bc1f27bffee425dd4a0493cac81875e199 100644 |
--- a/sky/sdk/lib/framework/layout2.dart |
+++ b/sky/sdk/lib/framework/layout2.dart |
@@ -74,14 +74,11 @@ abstract class RenderNode extends AbstractNode { |
bool _needsLayout = true; |
bool get needsLayout => _needsLayout; |
RenderNode _relayoutSubtreeRoot; |
- void saveRelayoutSubtreeRoot(RenderNode relayoutSubtreeRoot) { |
- _relayoutSubtreeRoot = relayoutSubtreeRoot; |
- assert(_relayoutSubtreeRoot == null || _relayoutSubtreeRoot._relayoutSubtreeRoot == null); |
- assert(_relayoutSubtreeRoot == null || _relayoutSubtreeRoot == parent || (parent is RenderNode && _relayoutSubtreeRoot == parent._relayoutSubtreeRoot)); |
- } |
+ dynamic _constraints; |
+ dynamic get constraints => _constraints; |
bool debugAncestorsAlreadyMarkedNeedsLayout() { |
if (_relayoutSubtreeRoot == null) |
- return true; |
+ return true; // we haven't yet done layout even once, so there's nothing for us to do |
RenderNode node = this; |
while (node != _relayoutSubtreeRoot) { |
assert(node._relayoutSubtreeRoot == _relayoutSubtreeRoot); |
@@ -90,7 +87,7 @@ abstract class RenderNode extends AbstractNode { |
if (!node._needsLayout) |
return false; |
} |
- assert(node._relayoutSubtreeRoot == null); |
+ assert(node._relayoutSubtreeRoot == node); |
return true; |
} |
void markNeedsLayout() { |
@@ -101,7 +98,8 @@ abstract class RenderNode extends AbstractNode { |
return; |
} |
_needsLayout = true; |
- if (_relayoutSubtreeRoot != null) { |
+ assert(_relayoutSubtreeRoot != null); |
+ if (_relayoutSubtreeRoot != this) { |
assert(parent is RenderNode); |
parent.markNeedsLayout(); |
} else { |
@@ -120,8 +118,8 @@ abstract class RenderNode extends AbstractNode { |
} |
void _doLayout() { |
try { |
- assert(_relayoutSubtreeRoot == null); |
- relayout(); |
+ assert(_relayoutSubtreeRoot == this); |
+ performLayout(); |
} catch (e, stack) { |
print('Exception raised during layout of ${this}: ${e}'); |
print(stack); |
@@ -129,75 +127,36 @@ abstract class RenderNode extends AbstractNode { |
} |
assert(!_needsLayout); // check that the relayout() method marked us "not dirty" |
} |
- /* // this method's signature is subclass-specific, but will exist in |
- // some form in all subclasses: |
- void layout({arguments..., RenderNode relayoutSubtreeRoot}) { |
- bool childArgumentsChanged = ...; // true if arguments we're going to pass to the children are different than last time, false otherwise |
- if (this node has an opinion about its size, e.g. because it autosizes based on kids, or has an intrinsic dimension) { |
- if (relayoutSubtreeRoot != null) { |
- saveRelayoutSubtreeRoot(relayoutSubtreeRoot); |
- // for each child, if we are going to size ourselves around them: |
- if (child.needsLayout || childArgumentsChanged) |
- child.layout(... relayoutSubtreeRoot: relayoutSubtreeRoot); |
- width = ...; |
- height = ...; |
- } else { |
- saveRelayoutSubtreeRoot(null); // you can skip this if there's no way you would ever have called saveRelayoutSubtreeRoot() before |
- // we're the root of the relayout subtree |
- // for each child, if we are going to size ourselves around them: |
- if (child.needsLayout || childArgumentsChanged) |
- child.layout(... relayoutSubtreeRoot: this); |
- width = ...; |
- height = ...; |
- } |
- } else { |
- // we're sizing ourselves exclusively on input from the parent (arguments to this function) |
- // ignore relayoutSubtreeRoot |
- saveRelayoutSubtreeRoot(null); // you can skip this if there's no way you would ever have called saveRelayoutSubtreeRoot() before |
- width = ...; // based on input from arguments only |
- height = ...; // based on input from arguments only |
- } |
- // for each child whose size we'll ignore when deciding ours: |
- if (child.needsLayout || childArgumentsChanged) |
- child.layout(... relayoutSubtreeRoot: null); // or just omit relayoutSubtreeRoot |
- layoutDone(); |
- return; |
- } |
- */ |
- void relayout() { |
+ void layout(dynamic constraints, { bool parentUsesSize: false }) { |
+ RenderNode relayoutSubtreeRoot; |
+ if (!parentUsesSize || sizedByParent || parent is! RenderNode) |
+ relayoutSubtreeRoot = this; |
+ else |
+ relayoutSubtreeRoot = parent._relayoutSubtreeRoot; |
+ if (!needsLayout && constraints == _constraints && relayoutSubtreeRoot == _relayoutSubtreeRoot) |
+ return; |
+ _constraints = constraints; |
+ _relayoutSubtreeRoot = relayoutSubtreeRoot; |
+ if (sizedByParent) |
+ performResize(); |
+ performLayout(); |
+ _needsLayout = false; |
+ markNeedsPaint(); |
+ } |
+ bool get sizedByParent => false; // return true if the constraints are the only input to the sizing algorithm (in particular, child nodes have no impact) |
+ void performResize(); // set the local dimensions, using only the constraints (only called if sizedByParent is true) |
+ void performLayout(); |
// Override this to perform relayout without your parent's |
// involvement. |
// |
- // This is what is called after the first layout(), if you mark |
- // yourself dirty and don't have a _relayoutSubtreeRoot set; in |
- // other words, either if your parent doesn't care what size you |
- // are (and thus didn't pass a relayoutSubtreeRoot to your |
- // layout() method) or if you sized yourself entirely based on |
- // what your parents told you, and not based on your children (and |
- // thus you never called saveRelayoutSubtreeRoot()). |
- // |
- // In the former case, you can resize yourself here at will. In |
- // the latter case, just leave your dimensions unchanged. |
- // |
- // If _relayoutSubtreeRoot is set (i.e. you called saveRelayout- |
- // SubtreeRoot() in your layout(), with a relayoutSubtreeRoot |
- // argument that was non-null), then if you mark yourself as dirty |
- // then we'll tell that subtree root instead, and the layout will |
- // occur via the layout() tree rather than starting from this |
- // relayout() method. |
+ // This is called during layout. If sizedByParent is true, then |
+ // performLayout() should not change your dimensions, only do that |
+ // in performResize(). If sizedByParent is false, then set both |
+ // your dimensions and do your children's layout here. |
// |
- // when calling children's layout() methods, skip any children |
- // that have needsLayout == false unless the arguments you are |
- // passing in have changed since the last time |
- assert(_relayoutSubtreeRoot == null); |
- layoutDone(); |
- } |
- void layoutDone({bool needsPaint: true}) { |
- // make sure to call this at the end of your layout() or relayout() |
- _needsLayout = false; |
- if (needsPaint) |
- markNeedsPaint(); |
- } |
+ // When calling layout() on your children, pass in |
+ // "parentUsesSize: true" if your size or layout is dependent on |
+ // your child's size. |
// when the parent has rotated (e.g. when the screen has been turned |
// 90 degrees), immediately prior to layout() being called for the |
@@ -221,7 +180,7 @@ abstract class RenderNode extends AbstractNode { |
static bool _debugDoingPaint = false; |
void markNeedsPaint() { |
assert(!_debugDoingPaint); |
- // TODO(abarth): It's very redunant to call this for every node in the |
+ // TODO(abarth): It's very redundant to call this for every node in the |
// render tree during layout. We should instead compute a summary bit and |
// call it once at the end of layout. |
sky.view.scheduleFrame(); |
@@ -523,10 +482,19 @@ abstract class RenderBox extends RenderNode { |
return new BoxDimensions.withConstraints(constraints); |
} |
- void layout(BoxConstraints constraints, { RenderNode relayoutSubtreeRoot }) { |
+ BoxConstraints get constraints => super.constraints as BoxConstraints; |
+ void performResize() { |
+ // default behaviour for subclasses that have sizedByParent = true |
width = constraints.constrainWidth(0.0); |
height = constraints.constrainHeight(0.0); |
- layoutDone(); |
+ assert(height < double.INFINITY); |
+ assert(width < double.INFINITY); |
+ } |
+ void performLayout() { |
+ // descendants have to either override performLayout() to set both |
+ // width and height and lay out children, or, set sizedByParent to |
+ // true so that performResize()'s logic above does its thing. |
+ assert(sizedByParent); |
} |
bool hitTest(HitTestResult result, { double x, double y }) { |
@@ -566,19 +534,15 @@ class RenderPadding extends RenderBox with RenderNodeWithChildMixin<RenderBox> { |
return child.getIntrinsicDimensions(constraints); |
} |
- void layout(BoxConstraints constraints, { RenderNode relayoutSubtreeRoot }) { |
+ void performLayout() { |
assert(padding != null); |
- constraints = constraints.deflate(padding); |
+ BoxConstraints innerConstraints = constraints.deflate(padding); |
if (child == null) { |
- width = constraints.constrainWidth(padding.left + padding.right); |
- height = constraints.constrainHeight(padding.top + padding.bottom); |
+ width = innerConstraints.constrainWidth(padding.left + padding.right); |
+ height = innerConstraints.constrainHeight(padding.top + padding.bottom); |
return; |
} |
- if (relayoutSubtreeRoot != null) |
- saveRelayoutSubtreeRoot(relayoutSubtreeRoot); |
- else |
- relayoutSubtreeRoot = this; |
- child.layout(constraints, relayoutSubtreeRoot: relayoutSubtreeRoot); |
+ child.layout(innerConstraints, parentUsesSize: true); |
assert(child.parentData is BoxParentData); |
child.parentData.x = padding.left; |
child.parentData.y = padding.top; |
@@ -664,6 +628,18 @@ class RenderDecoratedCircle extends RenderDecoratedBox with RenderNodeWithChildM |
// RENDER VIEW LAYOUT MANAGER |
+class ViewConstraints { |
+ |
+ const ViewConstraints({ |
+ this.width: 0.0, this.height: 0.0, this.orientation: null |
+ }); |
+ |
+ final double width; |
+ final double height; |
+ final int orientation; |
+ |
+} |
+ |
class RenderView extends RenderNode with RenderNodeWithChildMixin<RenderBox> { |
RenderView({ |
@@ -682,36 +658,29 @@ class RenderView extends RenderNode with RenderNodeWithChildMixin<RenderBox> { |
int get orientation => _orientation; |
Duration timeForRotation; |
- void layout({ |
- double newWidth, |
- double newHeight, |
- int newOrientation |
- }) { |
- if (newOrientation != orientation) { |
- if (orientation != null && child != null) |
- child.rotate(oldAngle: orientation, newAngle: newOrientation, time: timeForRotation); |
- _orientation = newOrientation; |
- } |
- if ((newWidth != width) || (newHeight != height)) { |
- _width = newWidth; |
- _height = newHeight; |
- relayout(); |
- } else { |
- layoutDone(); |
+ ViewConstraints get constraints => super.constraints as ViewConstraints; |
+ bool get sizedByParent => true; |
+ void performResize() { |
+ if (constraints.orientation != _orientation) { |
+ if (_orientation != null && child != null) |
+ child.rotate(oldAngle: _orientation, newAngle: constraints.orientation, time: timeForRotation); |
+ _orientation = constraints.orientation; |
} |
+ _width = constraints.width; |
+ _height = constraints.height; |
+ assert(height < double.INFINITY); |
+ assert(width < double.INFINITY); |
} |
- |
- void relayout() { |
+ void performLayout() { |
if (child != null) { |
child.layout(new BoxConstraints.tight(width: width, height: height)); |
assert(child.width == width); |
assert(child.height == height); |
} |
- layoutDone(); |
} |
void rotate({ int oldAngle, int newAngle, Duration time }) { |
- assert(false); // nobody tells the screen to rotate, the whole rotate() dance is started from our layout() |
+ assert(false); // nobody tells the screen to rotate, the whole rotate() dance is started from our performResize() |
} |
bool hitTest(HitTestResult result, { double x, double y }) { |
@@ -805,38 +774,22 @@ class RenderBlock extends RenderDecoratedBox with ContainerRenderNodeMixin<Rende |
height: constraints.constrainHeight(outerHeight)); |
} |
- BoxConstraints _constraints; // value cached from parent for relayout call |
- void layout(BoxConstraints constraints, { RenderNode relayoutSubtreeRoot }) { |
- if (relayoutSubtreeRoot != null) |
- saveRelayoutSubtreeRoot(relayoutSubtreeRoot); |
- else |
- relayoutSubtreeRoot = this; |
+ void performLayout() { |
+ assert(constraints is BoxConstraints); |
width = constraints.constrainWidth(constraints.maxWidth); |
assert(width < double.INFINITY); |
- _constraints = constraints; |
- internalLayout(relayoutSubtreeRoot); |
- } |
- |
- void relayout() { |
- internalLayout(this); |
- } |
- |
- void internalLayout(RenderNode relayoutSubtreeRoot) { |
- assert(_constraints != null); |
double y = 0.0; |
double innerWidth = width; |
RenderBox child = firstChild; |
while (child != null) { |
- child.layout(new BoxConstraints(minWidth: innerWidth, maxWidth: innerWidth), |
- relayoutSubtreeRoot: relayoutSubtreeRoot); |
+ child.layout(new BoxConstraints(minWidth: innerWidth, maxWidth: innerWidth), parentUsesSize: true); |
assert(child.parentData is BlockParentData); |
child.parentData.x = 0.0; |
child.parentData.y = y; |
y += child.height; |
child = child.parentData.nextSibling; |
} |
- height = _constraints.constrainHeight(y); |
- layoutDone(); |
+ height = constraints.constrainHeight(y); |
} |
void hitTestChildren(HitTestResult result, { double x, double y }) { |
@@ -886,22 +839,12 @@ class RenderFlex extends RenderDecoratedBox with ContainerRenderNodeMixin<Render |
child.parentData = new FlexBoxParentData(); |
} |
- BoxConstraints _constraints; // value cached from parent for relayout call |
- void layout(BoxConstraints constraints, { RenderNode relayoutSubtreeRoot }) { |
- if (relayoutSubtreeRoot != null) |
- saveRelayoutSubtreeRoot(relayoutSubtreeRoot); |
- else |
- relayoutSubtreeRoot = this; |
- _constraints = constraints; |
+ bool get sizedByParent => true; |
+ void performResize() { |
width = _constraints.constrainWidth(_constraints.maxWidth); |
height = _constraints.constrainHeight(_constraints.maxHeight); |
assert(height < double.INFINITY); |
assert(width < double.INFINITY); |
- internalLayout(relayoutSubtreeRoot); |
- } |
- |
- void relayout() { |
- internalLayout(this); |
} |
int _getFlex(RenderBox child) { |
@@ -909,22 +852,21 @@ class RenderFlex extends RenderDecoratedBox with ContainerRenderNodeMixin<Render |
return child.parentData.flex != null ? child.parentData.flex : 0; |
} |
- void internalLayout(RenderNode relayoutSubtreeRoot) { |
+ void performLayout() { |
// Based on http://www.w3.org/TR/css-flexbox-1/ Section 9.7 Resolving Flexible Lengths |
// Steps 1-3. Determine used flex factor, size inflexible items, calculate free space |
int totalFlex = 0; |
- assert(_constraints != null); |
- double freeSpace = (_direction == FlexDirection.Horizontal) ? _constraints.maxWidth : _constraints.maxHeight; |
+ assert(constraints != null); |
+ double freeSpace = (_direction == FlexDirection.Horizontal) ? constraints.maxWidth : constraints.maxHeight; |
RenderBox child = firstChild; |
while (child != null) { |
int flex = _getFlex(child); |
if (flex > 0) { |
totalFlex += child.parentData.flex; |
} else { |
- BoxConstraints constraints = new BoxConstraints(maxHeight: _constraints.maxHeight, |
- maxWidth: _constraints.maxWidth); |
- child.layout(constraints, |
- relayoutSubtreeRoot: relayoutSubtreeRoot); |
+ BoxConstraints innerConstraints = new BoxConstraints(maxHeight: constraints.maxHeight, |
+ maxWidth: constraints.maxWidth); |
+ child.layout(innerConstraints, parentUsesSize: true); |
freeSpace -= (_direction == FlexDirection.Horizontal) ? child.width : child.height; |
} |
child = child.parentData.nextSibling; |
@@ -938,20 +880,20 @@ class RenderFlex extends RenderDecoratedBox with ContainerRenderNodeMixin<Render |
int flex = _getFlex(child); |
if (flex > 0) { |
double spaceForChild = spacePerFlex * flex; |
- BoxConstraints constraints; |
+ BoxConstraints innerConstraints; |
switch (_direction) { |
case FlexDirection.Horizontal: |
- constraints = new BoxConstraints(maxHeight: _constraints.maxHeight, |
- minWidth: spaceForChild, |
- maxWidth: spaceForChild); |
+ innerConstraints = new BoxConstraints(maxHeight: constraints.maxHeight, |
+ minWidth: spaceForChild, |
+ maxWidth: spaceForChild); |
break; |
case FlexDirection.Vertical: |
- constraints = new BoxConstraints(minHeight: spaceForChild, |
- maxHeight: spaceForChild, |
- maxWidth: _constraints.maxWidth); |
+ innerConstraints = new BoxConstraints(minHeight: spaceForChild, |
+ maxHeight: spaceForChild, |
+ maxWidth: constraints.maxWidth); |
break; |
} |
- child.layout(constraints, relayoutSubtreeRoot: relayoutSubtreeRoot); |
+ child.layout(innerConstraints, parentUsesSize: true); |
} |
// For now, center the flex items in the cross direction |
@@ -959,17 +901,16 @@ class RenderFlex extends RenderDecoratedBox with ContainerRenderNodeMixin<Render |
case FlexDirection.Horizontal: |
child.parentData.x = usedSpace; |
usedSpace += child.width; |
- child.parentData.y = height / 2 - child.height / 2; |
+ child.parentData.y = height / 2.0 - child.height / 2.0; |
break; |
case FlexDirection.Vertical: |
child.parentData.y = usedSpace; |
usedSpace += child.height; |
- child.parentData.x = width / 2 - child.width / 2; |
+ child.parentData.x = width / 2.0 - child.width / 2.0; |
break; |
} |
child = child.parentData.nextSibling; |
} |
- layoutDone(); |
} |
void hitTestChildren(HitTestResult result, { double x, double y }) { |