diff --git a/examples/raw/sector-layout.dart b/examples/raw/sector-layout.dart index 3b6d388edf96a9d4819d3d89489904e424d960de..4c6dfb68a83dbd221353f52e02e07d5fb1ace5db 100644 --- a/examples/raw/sector-layout.dart +++ b/examples/raw/sector-layout.dart @@ -72,10 +72,17 @@ abstract class RenderSector extends RenderNode { return new SectorDimensions.withConstraints(constraints); } - void layout(SectorConstraints constraints, { RenderNode relayoutSubtreeRoot }) { + SectorConstraints get constraints => super.constraints as SectorConstraints; + void performResize() { + // default behaviour for subclasses that have sizedByParent = true deltaRadius = constraints.constrainDeltaRadius(0.0); deltaTheta = constraints.constrainDeltaTheta(0.0); - layoutDone(); + } + void performLayout() { + // descendants have to either override performLayout() to set both + // the dimensions 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 radius, double theta }) { @@ -210,30 +217,15 @@ class RenderSectorRing extends RenderSectorWithChildren { deltaTheta: innerTheta); } - SectorConstraints _constraints; - void layout(SectorConstraints constraints, { RenderNode relayoutSubtreeRoot }) { - if (relayoutSubtreeRoot != null) - saveRelayoutSubtreeRoot(relayoutSubtreeRoot); - relayoutSubtreeRoot = relayoutSubtreeRoot == null ? this : relayoutSubtreeRoot; + void performLayout() { + assert(this.parentData is SectorParentData); deltaRadius = constraints.constrainDeltaRadius(desiredDeltaRadius); assert(deltaRadius < double.INFINITY); - _constraints = constraints; - internalLayout(relayoutSubtreeRoot); - } - - void relayout() { - // TODO(ianh): avoid code duplication - assert(parentData is SectorParentData); - internalLayout(this); - } - - void internalLayout(RenderNode relayoutSubtreeRoot) { - assert(this.parentData is SectorParentData); double innerDeltaRadius = deltaRadius - padding * 2.0; double childRadius = this.parentData.radius + padding; double paddingTheta = math.atan(padding / (this.parentData.radius + deltaRadius)); double innerTheta = paddingTheta; // increments with each child - double remainingDeltaTheta = _constraints.maxDeltaTheta - (innerTheta + paddingTheta); + double remainingDeltaTheta = constraints.maxDeltaTheta - (innerTheta + paddingTheta); RenderSector child = firstChild; while (child != null) { SectorConstraints innerConstraints = new SectorConstraints( @@ -243,7 +235,7 @@ class RenderSectorRing extends RenderSectorWithChildren { assert(child.parentData is SectorParentData); child.parentData.theta = innerTheta; child.parentData.radius = childRadius; - child.layout(innerConstraints, relayoutSubtreeRoot: relayoutSubtreeRoot); + child.layout(innerConstraints, parentUsesSize: true); innerTheta += child.deltaTheta; remainingDeltaTheta -= child.deltaTheta; assert(child.parentData is SectorChildListParentData); @@ -333,30 +325,15 @@ class RenderSectorSlice extends RenderSectorWithChildren { deltaTheta: outerDeltaTheta); } - SectorConstraints _constraints; - void layout(SectorConstraints constraints, { RenderNode relayoutSubtreeRoot }) { - if (relayoutSubtreeRoot != null) - saveRelayoutSubtreeRoot(relayoutSubtreeRoot); - relayoutSubtreeRoot = relayoutSubtreeRoot == null ? this : relayoutSubtreeRoot; + void performLayout() { + assert(this.parentData is SectorParentData); deltaTheta = constraints.constrainDeltaTheta(desiredDeltaTheta); assert(deltaTheta <= kTwoPi); - _constraints = constraints; - internalLayout(relayoutSubtreeRoot); - } - - void relayout() { - // TODO(ianh): avoid code duplication - assert(parentData is SectorParentData); - internalLayout(this); - } - - void internalLayout(RenderNode relayoutSubtreeRoot) { - assert(this.parentData is SectorParentData); double paddingTheta = math.atan(padding / this.parentData.radius); double innerTheta = this.parentData.theta + paddingTheta; double innerDeltaTheta = deltaTheta - paddingTheta * 2.0; double childRadius = this.parentData.radius + padding; - double remainingDeltaRadius = _constraints.maxDeltaRadius - (padding * 2.0); + double remainingDeltaRadius = constraints.maxDeltaRadius - (padding * 2.0); RenderSector child = firstChild; while (child != null) { SectorConstraints innerConstraints = new SectorConstraints( @@ -365,7 +342,7 @@ class RenderSectorSlice extends RenderSectorWithChildren { ); child.parentData.theta = innerTheta; child.parentData.radius = childRadius; - child.layout(innerConstraints); + child.layout(innerConstraints, parentUsesSize: true); childRadius += child.deltaRadius; remainingDeltaRadius -= child.deltaRadius; assert(child.parentData is SectorChildListParentData); @@ -433,10 +410,7 @@ class RenderBoxToRenderSectorAdapter extends RenderBox { return new BoxDimensions.withConstraints(constraints, width: dimension, height: dimension); } - void layout(BoxConstraints constraints, { RenderNode relayoutSubtreeRoot }) { - if (relayoutSubtreeRoot != null) - saveRelayoutSubtreeRoot(relayoutSubtreeRoot); - relayoutSubtreeRoot = relayoutSubtreeRoot == null ? this : relayoutSubtreeRoot; + void performLayout() { BoxDimensions ourDimensions; if (child == null) { ourDimensions = new BoxDimensions.withConstraints(constraints, width: 0.0, height: 0.0); @@ -447,13 +421,12 @@ class RenderBoxToRenderSectorAdapter extends RenderBox { assert(child.parentData is SectorParentData); child.parentData.radius = innerRadius; child.parentData.theta = 0.0; - child.layout(new SectorConstraints(maxDeltaRadius: maxChildDeltaRadius), relayoutSubtreeRoot: relayoutSubtreeRoot); + child.layout(new SectorConstraints(maxDeltaRadius: maxChildDeltaRadius), parentUsesSize: true); double dimension = (innerRadius + child.deltaRadius) * 2.0; ourDimensions = new BoxDimensions.withConstraints(constraints, width: dimension, height: dimension); } width = ourDimensions.width; height = ourDimensions.height; - layoutDone(); } double width; @@ -503,10 +476,9 @@ class RenderSolidColor extends RenderDecoratedSector { return new SectorDimensions.withConstraints(constraints, deltaTheta: 1.0); // 1.0 radians } - void layout(SectorConstraints constraints, { RenderNode relayoutSubtreeRoot }) { + void performLayout() { deltaRadius = constraints.constrainDeltaRadius(desiredDeltaRadius); deltaTheta = constraints.constrainDeltaTheta(desiredDeltaTheta); - layoutDone(); } void handlePointer(sky.PointerEvent event) { diff --git a/examples/raw/simple_render_tree.dart b/examples/raw/simple_render_tree.dart index 569c86ac11994268f40dd8a7d0b340aedd170a78..f2db8ee3dfd263b09c9a35bf6f406940a9f6d167 100644 --- a/examples/raw/simple_render_tree.dart +++ b/examples/raw/simple_render_tree.dart @@ -22,10 +22,9 @@ class RenderSolidColor extends RenderDecoratedBox { width: desiredWidth); } - void layout(BoxConstraints constraints, { RenderNode relayoutSubtreeRoot }) { + void performLayout() { width = constraints.constrainWidth(desiredWidth); height = constraints.constrainHeight(desiredHeight); - layoutDone(); } void handlePointer(PointerEvent event) { diff --git a/sdk/lib/framework/app.dart b/sdk/lib/framework/app.dart index 7ba95e71b70a99936c8bd32f643220f4b9978992..6f536e7bfc21570e2a62b3eb221240e2a0fe784e 100644 --- a/sdk/lib/framework/app.dart +++ b/sdk/lib/framework/app.dart @@ -13,7 +13,7 @@ class AppView { _renderView = new RenderView(child: root); _renderView.attach(); - _renderView.layout(newWidth: sky.view.width, newHeight: sky.view.height); + _renderView.layout(new ViewConstraints(width: sky.view.width, height: sky.view.height)); sky.view.scheduleFrame(); } diff --git a/sdk/lib/framework/components2/scaffold.dart b/sdk/lib/framework/components2/scaffold.dart index 0df15ea822bd15f15225a65e930db22ebbc7b29c..fd654fe7e526da002ce2efd7f755d1ab27556284 100644 --- a/sdk/lib/framework/components2/scaffold.dart +++ b/sdk/lib/framework/components2/scaffold.dart @@ -79,12 +79,12 @@ class RenderScaffold extends RenderDecoratedBox { markNeedsLayout(); } - void layout(BoxConstraints constraints, { RenderNode relayoutSubtreeRoot }) { + bool get sizedByParent => true; + void performResize() { width = constraints.constrainWidth(double.INFINITY); assert(width < double.INFINITY); height = constraints.constrainHeight(double.INFINITY); assert(height < double.INFINITY); - relayout(); } static const kToolbarHeight = 100.0; @@ -92,7 +92,7 @@ class RenderScaffold extends RenderDecoratedBox { static const kButtonX = -16.0; // from right edge of body static const kButtonY = -16.0; // from bottom edge of body - void relayout() { + void performLayout() { double bodyHeight = height; double bodyPosition = 0.0; if (toolbar != null) { @@ -128,7 +128,6 @@ class RenderScaffold extends RenderDecoratedBox { floatingActionButton.parentData.x = width - xButtonX; floatingActionButton.parentData.y = bodyPosition + bodyHeight - kButtonY; } - layoutDone(); } void paint(RenderNodeDisplayList canvas) { diff --git a/sdk/lib/framework/fn2.dart b/sdk/lib/framework/fn2.dart index a83535857bbe69f40d63e7c73b6169cb15a69855..ef76a543c56702c6314713396bfe02d8b9ab99e3 100644 --- a/sdk/lib/framework/fn2.dart +++ b/sdk/lib/framework/fn2.dart @@ -564,7 +564,7 @@ class Paragraph extends OneChildListRenderNodeWrapper { class FlexContainer extends OneChildListRenderNodeWrapper { RenderFlex root; - RenderFlex createNode() => new RenderFlex(this, this.direction); + RenderFlex createNode() => new RenderFlex(direction: this.direction); static final FlexContainer _emptyContainer = new FlexContainer(); // direction doesn't matter if it's empty @@ -958,10 +958,9 @@ class RenderSolidColor extends RenderDecoratedBox { width: desiredWidth); } - void layout(BoxConstraints constraints, { RenderNode relayoutSubtreeRoot }) { + void performLayout() { width = constraints.constrainWidth(desiredWidth); height = constraints.constrainHeight(desiredHeight); - layoutDone(); } void handlePointer(sky.PointerEvent event) { diff --git a/sdk/lib/framework/layout2.dart b/sdk/lib/framework/layout2.dart index e7e0b6d9a17a0523c52429a29db24b62d12629d2..98a929bc1f27bffee425dd4a0493cac81875e199 100644 --- a/sdk/lib/framework/layout2.dart +++ b/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 { 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 { RenderView({ @@ -682,36 +658,29 @@ class RenderView extends RenderNode with RenderNodeWithChildMixin { 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 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 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 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