diff --git a/examples/raw/pubspec.yaml b/examples/raw/pubspec.yaml index 02ca8c18af1..0443125d5f4 100644 --- a/examples/raw/pubspec.yaml +++ b/examples/raw/pubspec.yaml @@ -1,4 +1,4 @@ -name: raw +name: sky_raw_examples dependencies: flutter: ">=0.0.3 <0.1.0" sky_tools: any diff --git a/examples/rendering/lib/sector_layout.dart b/examples/rendering/lib/sector_layout.dart new file mode 100644 index 00000000000..8944c5f6727 --- /dev/null +++ b/examples/rendering/lib/sector_layout.dart @@ -0,0 +1,527 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:math' as math; +import 'dart:ui' as ui; + +import 'package:flutter/rendering.dart'; + +const double kTwoPi = 2 * math.PI; + +class SectorConstraints extends Constraints { + const SectorConstraints({ + this.minDeltaRadius: 0.0, + this.maxDeltaRadius: double.INFINITY, + this.minDeltaTheta: 0.0, + this.maxDeltaTheta: kTwoPi + }); + + const SectorConstraints.tight({ double deltaRadius: 0.0, double deltaTheta: 0.0 }) + : minDeltaRadius = deltaRadius, + maxDeltaRadius = deltaRadius, + minDeltaTheta = deltaTheta, + maxDeltaTheta = deltaTheta; + + final double minDeltaRadius; + final double maxDeltaRadius; + final double minDeltaTheta; + final double maxDeltaTheta; + + double constrainDeltaRadius(double deltaRadius) { + return clamp(min: minDeltaRadius, max: maxDeltaRadius, value: deltaRadius); + } + + double constrainDeltaTheta(double deltaTheta) { + return clamp(min: minDeltaTheta, max: maxDeltaTheta, value: deltaTheta); + } + + bool get isTight => minDeltaTheta >= maxDeltaTheta && minDeltaTheta >= maxDeltaTheta; +} + +class SectorDimensions { + const SectorDimensions({ this.deltaRadius: 0.0, this.deltaTheta: 0.0 }); + + factory SectorDimensions.withConstraints( + SectorConstraints constraints, + { double deltaRadius: 0.0, double deltaTheta: 0.0 } + ) { + return new SectorDimensions( + deltaRadius: constraints.constrainDeltaRadius(deltaRadius), + deltaTheta: constraints.constrainDeltaTheta(deltaTheta) + ); + } + + final double deltaRadius; + final double deltaTheta; +} + +class SectorParentData extends ParentData { + double radius = 0.0; + double theta = 0.0; +} + +abstract class RenderSector extends RenderObject { + + void setupParentData(RenderObject child) { + if (child.parentData is! SectorParentData) + child.parentData = new SectorParentData(); + } + + // RenderSectors always use SectorParentData subclasses, as they need to be + // able to read their position information for painting and hit testing. + SectorParentData get parentData => super.parentData; + + SectorDimensions getIntrinsicDimensions(SectorConstraints constraints, double radius) { + return new SectorDimensions.withConstraints(constraints); + } + + SectorConstraints get constraints => super.constraints; + bool debugDoesMeetConstraints() { + assert(constraints != null); + assert(deltaRadius != null); + assert(deltaRadius < double.INFINITY); + assert(deltaTheta != null); + assert(deltaTheta < double.INFINITY); + return constraints.minDeltaRadius <= deltaRadius && + deltaRadius <= math.max(constraints.minDeltaRadius, constraints.maxDeltaRadius) && + constraints.minDeltaTheta <= deltaTheta && + deltaTheta <= math.max(constraints.minDeltaTheta, constraints.maxDeltaTheta); + } + void performResize() { + // default behaviour for subclasses that have sizedByParent = true + deltaRadius = constraints.constrainDeltaRadius(0.0); + deltaTheta = constraints.constrainDeltaTheta(0.0); + } + 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); + } + + Rect get paintBounds => new Rect.fromLTWH(0.0, 0.0, 2.0 * deltaRadius, 2.0 * deltaRadius); + + bool hitTest(HitTestResult result, { double radius, double theta }) { + if (radius < parentData.radius || radius >= parentData.radius + deltaRadius || + theta < parentData.theta || theta >= parentData.theta + deltaTheta) + return false; + hitTestChildren(result, radius: radius, theta: theta); + result.add(new HitTestEntry(this)); + return true; + } + void hitTestChildren(HitTestResult result, { double radius, double theta }) { } + + double deltaRadius; + double deltaTheta; +} + +abstract class RenderDecoratedSector extends RenderSector { + + RenderDecoratedSector(BoxDecoration decoration) : _decoration = decoration; + + BoxDecoration _decoration; + BoxDecoration get decoration => _decoration; + void set decoration (BoxDecoration value) { + if (value == _decoration) + return; + _decoration = value; + markNeedsPaint(); + } + + // offset must point to the center of the circle + void paint(PaintingContext context, Offset offset) { + assert(deltaRadius != null); + assert(deltaTheta != null); + assert(parentData is SectorParentData); + + if (_decoration == null) + return; + + if (_decoration.backgroundColor != null) { + final PaintingCanvas canvas = context.canvas; + Paint paint = new Paint()..color = _decoration.backgroundColor; + Path path = new Path(); + double outerRadius = (parentData.radius + deltaRadius); + Rect outerBounds = new Rect.fromLTRB(offset.dx-outerRadius, offset.dy-outerRadius, offset.dx+outerRadius, offset.dy+outerRadius); + path.arcTo(outerBounds, parentData.theta, deltaTheta, true); + double innerRadius = parentData.radius; + Rect innerBounds = new Rect.fromLTRB(offset.dx-innerRadius, offset.dy-innerRadius, offset.dx+innerRadius, offset.dy+innerRadius); + path.arcTo(innerBounds, parentData.theta + deltaTheta, -deltaTheta, false); + path.close(); + canvas.drawPath(path, paint); + } + } + +} + +class SectorChildListParentData extends SectorParentData with ContainerParentDataMixin { } + +class RenderSectorWithChildren extends RenderDecoratedSector with ContainerRenderObjectMixin { + RenderSectorWithChildren(BoxDecoration decoration) : super(decoration); + + void hitTestChildren(HitTestResult result, { double radius, double theta }) { + RenderSector child = lastChild; + while (child != null) { + if (child.hitTest(result, radius: radius, theta: theta)) + return; + final SectorChildListParentData childParentData = child.parentData; + child = childParentData.previousSibling; + } + } + + void visitChildren(RenderObjectVisitor visitor) { + RenderSector child = lastChild; + while (child != null) { + visitor(child); + final SectorChildListParentData childParentData = child.parentData; + child = childParentData.previousSibling; + } + } +} + +class RenderSectorRing extends RenderSectorWithChildren { + // lays out RenderSector children in a ring + + RenderSectorRing({ + BoxDecoration decoration, + double deltaRadius: double.INFINITY, + double padding: 0.0 + }) : _padding = padding, _desiredDeltaRadius = deltaRadius, super(decoration); + + double _desiredDeltaRadius; + double get desiredDeltaRadius => _desiredDeltaRadius; + void set desiredDeltaRadius(double value) { + assert(value != null); + if (_desiredDeltaRadius != value) { + _desiredDeltaRadius = value; + markNeedsLayout(); + } + } + + double _padding; + double get padding => _padding; + void set padding(double value) { + // TODO(ianh): avoid code duplication + assert(value != null); + if (_padding != value) { + _padding = value; + markNeedsLayout(); + } + } + + void setupParentData(RenderObject child) { + // TODO(ianh): avoid code duplication + if (child.parentData is! SectorChildListParentData) + child.parentData = new SectorChildListParentData(); + } + + SectorDimensions getIntrinsicDimensions(SectorConstraints constraints, double radius) { + double outerDeltaRadius = constraints.constrainDeltaRadius(desiredDeltaRadius); + double innerDeltaRadius = outerDeltaRadius - padding * 2.0; + double childRadius = radius + padding; + double paddingTheta = math.atan(padding / (radius + outerDeltaRadius)); + double innerTheta = paddingTheta; // increments with each child + double remainingDeltaTheta = constraints.maxDeltaTheta - (innerTheta + paddingTheta); + RenderSector child = firstChild; + while (child != null) { + SectorConstraints innerConstraints = new SectorConstraints( + maxDeltaRadius: innerDeltaRadius, + maxDeltaTheta: remainingDeltaTheta + ); + SectorDimensions childDimensions = child.getIntrinsicDimensions(innerConstraints, childRadius); + innerTheta += childDimensions.deltaTheta; + remainingDeltaTheta -= childDimensions.deltaTheta; + final SectorChildListParentData childParentData = child.parentData; + child = childParentData.nextSibling; + if (child != null) { + innerTheta += paddingTheta; + remainingDeltaTheta -= paddingTheta; + } + } + return new SectorDimensions.withConstraints(constraints, + deltaRadius: outerDeltaRadius, + deltaTheta: innerTheta); + } + + void performLayout() { + assert(this.parentData is SectorParentData); + deltaRadius = constraints.constrainDeltaRadius(desiredDeltaRadius); + assert(deltaRadius < double.INFINITY); + 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); + RenderSector child = firstChild; + while (child != null) { + SectorConstraints innerConstraints = new SectorConstraints( + maxDeltaRadius: innerDeltaRadius, + maxDeltaTheta: remainingDeltaTheta + ); + assert(child.parentData is SectorParentData); + child.parentData.theta = innerTheta; + child.parentData.radius = childRadius; + child.layout(innerConstraints, parentUsesSize: true); + innerTheta += child.deltaTheta; + remainingDeltaTheta -= child.deltaTheta; + final SectorChildListParentData childParentData = child.parentData; + child = childParentData.nextSibling; + if (child != null) { + innerTheta += paddingTheta; + remainingDeltaTheta -= paddingTheta; + } + } + deltaTheta = innerTheta; + } + + // offset must point to the center of our circle + // each sector then knows how to paint itself at its location + void paint(PaintingContext context, Offset offset) { + // TODO(ianh): avoid code duplication + super.paint(context, offset); + RenderSector child = firstChild; + while (child != null) { + context.paintChild(child, offset.toPoint()); + final SectorChildListParentData childParentData = child.parentData; + child = childParentData.nextSibling; + } + } + +} + +class RenderSectorSlice extends RenderSectorWithChildren { + // lays out RenderSector children in a stack + + RenderSectorSlice({ + BoxDecoration decoration, + double deltaTheta: kTwoPi, + double padding: 0.0 + }) : _padding = padding, _desiredDeltaTheta = deltaTheta, super(decoration); + + double _desiredDeltaTheta; + double get desiredDeltaTheta => _desiredDeltaTheta; + void set desiredDeltaTheta(double value) { + assert(value != null); + if (_desiredDeltaTheta != value) { + _desiredDeltaTheta = value; + markNeedsLayout(); + } + } + + double _padding; + double get padding => _padding; + void set padding(double value) { + // TODO(ianh): avoid code duplication + assert(value != null); + if (_padding != value) { + _padding = value; + markNeedsLayout(); + } + } + + void setupParentData(RenderObject child) { + // TODO(ianh): avoid code duplication + if (child.parentData is! SectorChildListParentData) + child.parentData = new SectorChildListParentData(); + } + + SectorDimensions getIntrinsicDimensions(SectorConstraints constraints, double radius) { + assert(this.parentData is SectorParentData); + double paddingTheta = math.atan(padding / this.parentData.radius); + double outerDeltaTheta = constraints.constrainDeltaTheta(desiredDeltaTheta); + double innerDeltaTheta = outerDeltaTheta - paddingTheta * 2.0; + double childRadius = this.parentData.radius + padding; + double remainingDeltaRadius = constraints.maxDeltaRadius - (padding * 2.0); + RenderSector child = firstChild; + while (child != null) { + SectorConstraints innerConstraints = new SectorConstraints( + maxDeltaRadius: remainingDeltaRadius, + maxDeltaTheta: innerDeltaTheta + ); + SectorDimensions childDimensions = child.getIntrinsicDimensions(innerConstraints, childRadius); + childRadius += childDimensions.deltaRadius; + remainingDeltaRadius -= childDimensions.deltaRadius; + final SectorChildListParentData childParentData = child.parentData; + child = childParentData.nextSibling; + childRadius += padding; + remainingDeltaRadius -= padding; + } + return new SectorDimensions.withConstraints(constraints, + deltaRadius: childRadius - this.parentData.radius, + deltaTheta: outerDeltaTheta); + } + + void performLayout() { + assert(this.parentData is SectorParentData); + deltaTheta = constraints.constrainDeltaTheta(desiredDeltaTheta); + assert(deltaTheta <= kTwoPi); + 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); + RenderSector child = firstChild; + while (child != null) { + SectorConstraints innerConstraints = new SectorConstraints( + maxDeltaRadius: remainingDeltaRadius, + maxDeltaTheta: innerDeltaTheta + ); + child.parentData.theta = innerTheta; + child.parentData.radius = childRadius; + child.layout(innerConstraints, parentUsesSize: true); + childRadius += child.deltaRadius; + remainingDeltaRadius -= child.deltaRadius; + final SectorChildListParentData childParentData = child.parentData; + child = childParentData.nextSibling; + childRadius += padding; + remainingDeltaRadius -= padding; + } + deltaRadius = childRadius - this.parentData.radius; + } + + // offset must point to the center of our circle + // each sector then knows how to paint itself at its location + void paint(PaintingContext context, Offset offset) { + // TODO(ianh): avoid code duplication + super.paint(context, offset); + RenderSector child = firstChild; + while (child != null) { + assert(child.parentData is SectorChildListParentData); + context.paintChild(child, offset.toPoint()); + final SectorChildListParentData childParentData = child.parentData; + child = childParentData.nextSibling; + } + } + +} + +class RenderBoxToRenderSectorAdapter extends RenderBox with RenderObjectWithChildMixin { + + RenderBoxToRenderSectorAdapter({ double innerRadius: 0.0, RenderSector child }) : + _innerRadius = innerRadius { + this.child = child; + } + + double _innerRadius; + double get innerRadius => _innerRadius; + void set innerRadius(double value) { + _innerRadius = value; + markNeedsLayout(); + } + + void setupParentData(RenderObject child) { + if (child.parentData is! SectorParentData) + child.parentData = new SectorParentData(); + } + + double getMinIntrinsicWidth(BoxConstraints constraints) { + if (child == null) + return super.getMinIntrinsicWidth(constraints); + return getIntrinsicDimensions(constraints).width; + } + + double getMaxIntrinsicWidth(BoxConstraints constraints) { + if (child == null) + return super.getMaxIntrinsicWidth(constraints); + return getIntrinsicDimensions(constraints).width; + } + + double getMinIntrinsicHeight(BoxConstraints constraints) { + if (child == null) + return super.getMinIntrinsicHeight(constraints); + return getIntrinsicDimensions(constraints).height; + } + + double getMaxIntrinsicHeight(BoxConstraints constraints) { + if (child == null) + return super.getMaxIntrinsicHeight(constraints); + return getIntrinsicDimensions(constraints).height; + } + + Size getIntrinsicDimensions(BoxConstraints constraints) { + assert(child is RenderSector); + assert(child.parentData is SectorParentData); + assert(constraints.maxWidth < double.INFINITY || constraints.maxHeight < double.INFINITY); + double maxChildDeltaRadius = math.min(constraints.maxWidth, constraints.maxHeight) / 2.0 - innerRadius; + SectorDimensions childDimensions = child.getIntrinsicDimensions(new SectorConstraints(maxDeltaRadius: maxChildDeltaRadius), innerRadius); + double dimension = (innerRadius + childDimensions.deltaRadius) * 2.0; + return constraints.constrain(new Size(dimension, dimension)); + } + + void performLayout() { + if (child == null) { + size = constraints.constrain(Size.zero); + } else { + assert(child is RenderSector); + assert(constraints.maxWidth < double.INFINITY || constraints.maxHeight < double.INFINITY); + double maxChildDeltaRadius = math.min(constraints.maxWidth, constraints.maxHeight) / 2.0 - innerRadius; + assert(child.parentData is SectorParentData); + child.parentData.radius = innerRadius; + child.parentData.theta = 0.0; + child.layout(new SectorConstraints(maxDeltaRadius: maxChildDeltaRadius), parentUsesSize: true); + double dimension = (innerRadius + child.deltaRadius) * 2.0; + size = constraints.constrain(new Size(dimension, dimension)); + } + } + + void paint(PaintingContext context, Offset offset) { + super.paint(context, offset); + if (child != null) { + Rect bounds = offset & size; + // we move the offset to the center of the circle for the RenderSectors + context.paintChild(child, bounds.center); + } + } + + bool hitTest(HitTestResult result, { Point position }) { + if (child == null) + return false; + double x = position.x; + double y = position.y; + // translate to our origin + x -= size.width/2.0; + y -= size.height/2.0; + // convert to radius/theta + double radius = math.sqrt(x*x+y*y); + double theta = (math.atan2(x, -y) - math.PI/2.0) % kTwoPi; + if (radius < innerRadius) + return false; + if (radius >= innerRadius + child.deltaRadius) + return false; + if (theta > child.deltaTheta) + return false; + child.hitTest(result, radius: radius, theta: theta); + result.add(new BoxHitTestEntry(this, position)); + return true; + } + +} + +class RenderSolidColor extends RenderDecoratedSector { + RenderSolidColor(Color backgroundColor, { + this.desiredDeltaRadius: double.INFINITY, + this.desiredDeltaTheta: kTwoPi + }) : this.backgroundColor = backgroundColor, + super(new BoxDecoration(backgroundColor: backgroundColor)); + + double desiredDeltaRadius; + double desiredDeltaTheta; + final Color backgroundColor; + + SectorDimensions getIntrinsicDimensions(SectorConstraints constraints, double radius) { + return new SectorDimensions.withConstraints(constraints, deltaTheta: desiredDeltaTheta); + } + + void performLayout() { + deltaRadius = constraints.constrainDeltaRadius(desiredDeltaRadius); + deltaTheta = constraints.constrainDeltaTheta(desiredDeltaTheta); + } + + void handleEvent(ui.Event event, HitTestEntry entry) { + if (event.type == 'pointerdown') + decoration = new BoxDecoration(backgroundColor: const Color(0xFFFF0000)); + else if (event.type == 'pointerup') + decoration = new BoxDecoration(backgroundColor: backgroundColor); + } +} diff --git a/examples/rendering/pubspec.yaml b/examples/rendering/pubspec.yaml index 0eb70a38f84..5158fb48356 100644 --- a/examples/rendering/pubspec.yaml +++ b/examples/rendering/pubspec.yaml @@ -1,4 +1,4 @@ -name: rendering +name: flutter_rendering_examples dependencies: flutter: ">=0.0.3 <0.1.0" sky_tools: any diff --git a/examples/rendering/sector_layout.dart b/examples/rendering/sector_layout.dart index 912127ad0ae..ac3fc4699e2 100644 --- a/examples/rendering/sector_layout.dart +++ b/examples/rendering/sector_layout.dart @@ -2,539 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:math' as math; -import 'dart:ui' as ui; - +import 'dart:async'; import 'package:flutter/rendering.dart'; - -const double kTwoPi = 2 * math.PI; - -class SectorConstraints extends Constraints { - const SectorConstraints({ - this.minDeltaRadius: 0.0, - this.maxDeltaRadius: double.INFINITY, - this.minDeltaTheta: 0.0, - this.maxDeltaTheta: kTwoPi - }); - - const SectorConstraints.tight({ double deltaRadius: 0.0, double deltaTheta: 0.0 }) - : minDeltaRadius = deltaRadius, - maxDeltaRadius = deltaRadius, - minDeltaTheta = deltaTheta, - maxDeltaTheta = deltaTheta; - - final double minDeltaRadius; - final double maxDeltaRadius; - final double minDeltaTheta; - final double maxDeltaTheta; - - double constrainDeltaRadius(double deltaRadius) { - return clamp(min: minDeltaRadius, max: maxDeltaRadius, value: deltaRadius); - } - - double constrainDeltaTheta(double deltaTheta) { - return clamp(min: minDeltaTheta, max: maxDeltaTheta, value: deltaTheta); - } - - bool get isTight => minDeltaTheta >= maxDeltaTheta && minDeltaTheta >= maxDeltaTheta; -} - -class SectorDimensions { - const SectorDimensions({ this.deltaRadius: 0.0, this.deltaTheta: 0.0 }); - - factory SectorDimensions.withConstraints( - SectorConstraints constraints, - { double deltaRadius: 0.0, double deltaTheta: 0.0 } - ) { - return new SectorDimensions( - deltaRadius: constraints.constrainDeltaRadius(deltaRadius), - deltaTheta: constraints.constrainDeltaTheta(deltaTheta) - ); - } - - final double deltaRadius; - final double deltaTheta; -} - -class SectorParentData extends ParentData { - double radius = 0.0; - double theta = 0.0; -} - -abstract class RenderSector extends RenderObject { - - void setupParentData(RenderObject child) { - if (child.parentData is! SectorParentData) - child.parentData = new SectorParentData(); - } - - SectorDimensions getIntrinsicDimensions(SectorConstraints constraints, double radius) { - return new SectorDimensions.withConstraints(constraints); - } - - SectorConstraints get constraints => super.constraints; - bool debugDoesMeetConstraints() { - assert(constraints != null); - assert(deltaRadius != null); - assert(deltaRadius < double.INFINITY); - assert(deltaTheta != null); - assert(deltaTheta < double.INFINITY); - return constraints.minDeltaRadius <= deltaRadius && - deltaRadius <= math.max(constraints.minDeltaRadius, constraints.maxDeltaRadius) && - constraints.minDeltaTheta <= deltaTheta && - deltaTheta <= math.max(constraints.minDeltaTheta, constraints.maxDeltaTheta); - } - void performResize() { - // default behaviour for subclasses that have sizedByParent = true - deltaRadius = constraints.constrainDeltaRadius(0.0); - deltaTheta = constraints.constrainDeltaTheta(0.0); - } - 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); - } - - Rect get paintBounds => new Rect.fromLTWH(0.0, 0.0, 2.0 * deltaRadius, 2.0 * deltaRadius); - - bool hitTest(HitTestResult result, { double radius, double theta }) { - assert(parentData is SectorParentData); - if (radius < parentData.radius || radius >= parentData.radius + deltaRadius || - theta < parentData.theta || theta >= parentData.theta + deltaTheta) - return false; - hitTestChildren(result, radius: radius, theta: theta); - result.add(new HitTestEntry(this)); - return true; - } - void hitTestChildren(HitTestResult result, { double radius, double theta }) { } - - double deltaRadius; - double deltaTheta; -} - -abstract class RenderDecoratedSector extends RenderSector { - - RenderDecoratedSector(BoxDecoration decoration) : _decoration = decoration; - - BoxDecoration _decoration; - BoxDecoration get decoration => _decoration; - void set decoration (BoxDecoration value) { - if (value == _decoration) - return; - _decoration = value; - markNeedsPaint(); - } - - // offset must point to the center of the circle - void paint(PaintingContext context, Offset offset) { - assert(deltaRadius != null); - assert(deltaTheta != null); - assert(parentData is SectorParentData); - - if (_decoration == null) - return; - - if (_decoration.backgroundColor != null) { - final PaintingCanvas canvas = context.canvas; - Paint paint = new Paint()..color = _decoration.backgroundColor; - Path path = new Path(); - double outerRadius = (parentData.radius + deltaRadius); - Rect outerBounds = new Rect.fromLTRB(offset.dx-outerRadius, offset.dy-outerRadius, offset.dx+outerRadius, offset.dy+outerRadius); - path.arcTo(outerBounds, parentData.theta, deltaTheta, true); - double innerRadius = parentData.radius; - Rect innerBounds = new Rect.fromLTRB(offset.dx-innerRadius, offset.dy-innerRadius, offset.dx+innerRadius, offset.dy+innerRadius); - path.arcTo(innerBounds, parentData.theta + deltaTheta, -deltaTheta, false); - path.close(); - canvas.drawPath(path, paint); - } - } - -} - -class SectorChildListParentData extends SectorParentData with ContainerParentDataMixin { } - -class RenderSectorWithChildren extends RenderDecoratedSector with ContainerRenderObjectMixin { - RenderSectorWithChildren(BoxDecoration decoration) : super(decoration); - - void hitTestChildren(HitTestResult result, { double radius, double theta }) { - RenderSector child = lastChild; - while (child != null) { - assert(child.parentData is SectorChildListParentData); - if (child.hitTest(result, radius: radius, theta: theta)) - return; - child = child.parentData.previousSibling; - } - } - - void visitChildren(RenderObjectVisitor visitor) { - RenderSector child = lastChild; - while (child != null) { - visitor(child); - child = child.parentData.previousSibling; - } - } -} - -class RenderSectorRing extends RenderSectorWithChildren { - // lays out RenderSector children in a ring - - RenderSectorRing({ - BoxDecoration decoration, - double deltaRadius: double.INFINITY, - double padding: 0.0 - }) : super(decoration), _padding = padding, _desiredDeltaRadius = deltaRadius; - - double _desiredDeltaRadius; - double get desiredDeltaRadius => _desiredDeltaRadius; - void set desiredDeltaRadius(double value) { - assert(value != null); - if (_desiredDeltaRadius != value) { - _desiredDeltaRadius = value; - markNeedsLayout(); - } - } - - double _padding; - double get padding => _padding; - void set padding(double value) { - // TODO(ianh): avoid code duplication - assert(value != null); - if (_padding != value) { - _padding = value; - markNeedsLayout(); - } - } - - void setupParentData(RenderObject child) { - // TODO(ianh): avoid code duplication - if (child.parentData is! SectorChildListParentData) - child.parentData = new SectorChildListParentData(); - } - - SectorDimensions getIntrinsicDimensions(SectorConstraints constraints, double radius) { - double outerDeltaRadius = constraints.constrainDeltaRadius(desiredDeltaRadius); - double innerDeltaRadius = outerDeltaRadius - padding * 2.0; - double childRadius = radius + padding; - double paddingTheta = math.atan(padding / (radius + outerDeltaRadius)); - double innerTheta = paddingTheta; // increments with each child - double remainingDeltaTheta = constraints.maxDeltaTheta - (innerTheta + paddingTheta); - RenderSector child = firstChild; - while (child != null) { - SectorConstraints innerConstraints = new SectorConstraints( - maxDeltaRadius: innerDeltaRadius, - maxDeltaTheta: remainingDeltaTheta - ); - SectorDimensions childDimensions = child.getIntrinsicDimensions(innerConstraints, childRadius); - innerTheta += childDimensions.deltaTheta; - remainingDeltaTheta -= childDimensions.deltaTheta; - assert(child.parentData is SectorChildListParentData); - child = child.parentData.nextSibling; - if (child != null) { - innerTheta += paddingTheta; - remainingDeltaTheta -= paddingTheta; - } - } - return new SectorDimensions.withConstraints(constraints, - deltaRadius: outerDeltaRadius, - deltaTheta: innerTheta); - } - - void performLayout() { - assert(this.parentData is SectorParentData); - deltaRadius = constraints.constrainDeltaRadius(desiredDeltaRadius); - assert(deltaRadius < double.INFINITY); - 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); - RenderSector child = firstChild; - while (child != null) { - SectorConstraints innerConstraints = new SectorConstraints( - maxDeltaRadius: innerDeltaRadius, - maxDeltaTheta: remainingDeltaTheta - ); - assert(child.parentData is SectorParentData); - child.parentData.theta = innerTheta; - child.parentData.radius = childRadius; - child.layout(innerConstraints, parentUsesSize: true); - innerTheta += child.deltaTheta; - remainingDeltaTheta -= child.deltaTheta; - assert(child.parentData is SectorChildListParentData); - child = child.parentData.nextSibling; - if (child != null) { - innerTheta += paddingTheta; - remainingDeltaTheta -= paddingTheta; - } - } - deltaTheta = innerTheta; - } - - // offset must point to the center of our circle - // each sector then knows how to paint itself at its location - void paint(PaintingContext context, Offset offset) { - // TODO(ianh): avoid code duplication - super.paint(context, offset); - RenderSector child = firstChild; - while (child != null) { - assert(child.parentData is SectorChildListParentData); - context.paintChild(child, offset.toPoint()); - child = child.parentData.nextSibling; - } - } - -} - -class RenderSectorSlice extends RenderSectorWithChildren { - // lays out RenderSector children in a stack - - RenderSectorSlice({ - BoxDecoration decoration, - double deltaTheta: kTwoPi, - double padding: 0.0 - }) : super(decoration), _padding = padding, _desiredDeltaTheta = deltaTheta; - - double _desiredDeltaTheta; - double get desiredDeltaTheta => _desiredDeltaTheta; - void set desiredDeltaTheta(double value) { - assert(value != null); - if (_desiredDeltaTheta != value) { - _desiredDeltaTheta = value; - markNeedsLayout(); - } - } - - double _padding; - double get padding => _padding; - void set padding(double value) { - // TODO(ianh): avoid code duplication - assert(value != null); - if (_padding != value) { - _padding = value; - markNeedsLayout(); - } - } - - void setupParentData(RenderObject child) { - // TODO(ianh): avoid code duplication - if (child.parentData is! SectorChildListParentData) - child.parentData = new SectorChildListParentData(); - } - - SectorDimensions getIntrinsicDimensions(SectorConstraints constraints, double radius) { - assert(this.parentData is SectorParentData); - double paddingTheta = math.atan(padding / this.parentData.radius); - double outerDeltaTheta = constraints.constrainDeltaTheta(desiredDeltaTheta); - double innerDeltaTheta = outerDeltaTheta - paddingTheta * 2.0; - double childRadius = this.parentData.radius + padding; - double remainingDeltaRadius = constraints.maxDeltaRadius - (padding * 2.0); - RenderSector child = firstChild; - while (child != null) { - SectorConstraints innerConstraints = new SectorConstraints( - maxDeltaRadius: remainingDeltaRadius, - maxDeltaTheta: innerDeltaTheta - ); - SectorDimensions childDimensions = child.getIntrinsicDimensions(innerConstraints, childRadius); - childRadius += childDimensions.deltaRadius; - remainingDeltaRadius -= childDimensions.deltaRadius; - assert(child.parentData is SectorChildListParentData); - child = child.parentData.nextSibling; - childRadius += padding; - remainingDeltaRadius -= padding; - } - return new SectorDimensions.withConstraints(constraints, - deltaRadius: childRadius - this.parentData.radius, - deltaTheta: outerDeltaTheta); - } - - void performLayout() { - assert(this.parentData is SectorParentData); - deltaTheta = constraints.constrainDeltaTheta(desiredDeltaTheta); - assert(deltaTheta <= kTwoPi); - 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); - RenderSector child = firstChild; - while (child != null) { - SectorConstraints innerConstraints = new SectorConstraints( - maxDeltaRadius: remainingDeltaRadius, - maxDeltaTheta: innerDeltaTheta - ); - child.parentData.theta = innerTheta; - child.parentData.radius = childRadius; - child.layout(innerConstraints, parentUsesSize: true); - childRadius += child.deltaRadius; - remainingDeltaRadius -= child.deltaRadius; - assert(child.parentData is SectorChildListParentData); - child = child.parentData.nextSibling; - childRadius += padding; - remainingDeltaRadius -= padding; - } - deltaRadius = childRadius - this.parentData.radius; - } - - // offset must point to the center of our circle - // each sector then knows how to paint itself at its location - void paint(PaintingContext context, Offset offset) { - // TODO(ianh): avoid code duplication - super.paint(context, offset); - RenderSector child = firstChild; - while (child != null) { - assert(child.parentData is SectorChildListParentData); - context.paintChild(child, offset.toPoint()); - child = child.parentData.nextSibling; - } - } - -} - -class RenderBoxToRenderSectorAdapter extends RenderBox { - - RenderBoxToRenderSectorAdapter({ double innerRadius: 0.0, RenderSector child }) : - _innerRadius = innerRadius { - _child = child; - adoptChild(_child); - } - - double _innerRadius; - double get innerRadius => _innerRadius; - void set innerRadius(double value) { - _innerRadius = value; - markNeedsLayout(); - } - - RenderSector _child; - RenderSector get child => _child; - void set child(RenderSector value) { - if (_child != null) - dropChild(_child); - _child = value; - adoptChild(_child); - markNeedsLayout(); - } - - void setupParentData(RenderObject child) { - if (child.parentData is! SectorParentData) - child.parentData = new SectorParentData(); - } - - void visitChildren(RenderObjectVisitor visitor) { - visitor(_child); - } - - double getMinIntrinsicWidth(BoxConstraints constraints) { - if (child == null) - return super.getMinIntrinsicWidth(constraints); - return getIntrinsicDimensions(constraints).width; - } - - double getMaxIntrinsicWidth(BoxConstraints constraints) { - if (child == null) - return super.getMaxIntrinsicWidth(constraints); - return getIntrinsicDimensions(constraints).width; - } - - double getMinIntrinsicHeight(BoxConstraints constraints) { - if (child == null) - return super.getMinIntrinsicHeight(constraints); - return getIntrinsicDimensions(constraints).height; - } - - double getMaxIntrinsicHeight(BoxConstraints constraints) { - if (child == null) - return super.getMaxIntrinsicHeight(constraints); - return getIntrinsicDimensions(constraints).height; - } - - Size getIntrinsicDimensions(BoxConstraints constraints) { - assert(child is RenderSector); - assert(child.parentData is SectorParentData); - assert(constraints.maxWidth < double.INFINITY || constraints.maxHeight < double.INFINITY); - double maxChildDeltaRadius = math.min(constraints.maxWidth, constraints.maxHeight) / 2.0 - innerRadius; - SectorDimensions childDimensions = child.getIntrinsicDimensions(new SectorConstraints(maxDeltaRadius: maxChildDeltaRadius), innerRadius); - double dimension = (innerRadius + childDimensions.deltaRadius) * 2.0; - return constraints.constrain(new Size(dimension, dimension)); - } - - void performLayout() { - if (child == null) { - size = constraints.constrain(Size.zero); - } else { - assert(child is RenderSector); - assert(constraints.maxWidth < double.INFINITY || constraints.maxHeight < double.INFINITY); - double maxChildDeltaRadius = math.min(constraints.maxWidth, constraints.maxHeight) / 2.0 - innerRadius; - assert(child.parentData is SectorParentData); - child.parentData.radius = innerRadius; - child.parentData.theta = 0.0; - child.layout(new SectorConstraints(maxDeltaRadius: maxChildDeltaRadius), parentUsesSize: true); - double dimension = (innerRadius + child.deltaRadius) * 2.0; - size = constraints.constrain(new Size(dimension, dimension)); - } - } - - void paint(PaintingContext context, Offset offset) { - super.paint(context, offset); - if (child != null) { - Rect bounds = offset & size; - // we move the offset to the center of the circle for the RenderSectors - context.paintChild(child, bounds.center); - } - } - - bool hitTest(HitTestResult result, { Point position }) { - double x = position.x; - double y = position.y; - if (child == null) - return false; - // translate to our origin - x -= size.width/2.0; - y -= size.height/2.0; - // convert to radius/theta - double radius = math.sqrt(x*x+y*y); - double theta = (math.atan2(x, -y) - math.PI/2.0) % kTwoPi; - if (radius < innerRadius) - return false; - if (radius >= innerRadius + child.deltaRadius) - return false; - if (theta > child.deltaTheta) - return false; - child.hitTest(result, radius: radius, theta: theta); - result.add(new BoxHitTestEntry(this, position)); - return true; - } - -} - -class RenderSolidColor extends RenderDecoratedSector { - RenderSolidColor(Color backgroundColor, { - this.desiredDeltaRadius: double.INFINITY, - this.desiredDeltaTheta: kTwoPi - }) : this.backgroundColor = backgroundColor, - super(new BoxDecoration(backgroundColor: backgroundColor)); - - double desiredDeltaRadius; - double desiredDeltaTheta; - final Color backgroundColor; - - SectorDimensions getIntrinsicDimensions(SectorConstraints constraints, double radius) { - return new SectorDimensions.withConstraints(constraints, deltaTheta: desiredDeltaTheta); - } - - void performLayout() { - deltaRadius = constraints.constrainDeltaRadius(desiredDeltaRadius); - deltaTheta = constraints.constrainDeltaTheta(desiredDeltaTheta); - } - - void handleEvent(ui.Event event, HitTestEntry entry) { - if (event.type == 'pointerdown') - decoration = new BoxDecoration(backgroundColor: const Color(0xFFFF0000)); - else if (event.type == 'pointerup') - decoration = new BoxDecoration(backgroundColor: backgroundColor); - } -} +import 'lib/sector_layout.dart'; RenderBox buildSectorExample() { RenderSectorRing rootCircle = new RenderSectorRing(padding: 20.0); diff --git a/examples/widgets/pubspec.yaml b/examples/widgets/pubspec.yaml index 1a483612b63..8cc7e8c1508 100644 --- a/examples/widgets/pubspec.yaml +++ b/examples/widgets/pubspec.yaml @@ -1,9 +1,12 @@ -name: widgets +name: sky_widgets_examples dependencies: flutter: ">=0.0.3 <0.1.0" sky_tools: any + flutter_rendering_examples: any dependency_overrides: material_design_icons: path: ../../sky/packages/material_design_icons flutter: path: ../../sky/packages/sky + flutter_rendering_examples: + path: ../rendering diff --git a/examples/widgets/sector.dart.old b/examples/widgets/sector.dart similarity index 76% rename from examples/widgets/sector.dart.old rename to examples/widgets/sector.dart index 0b7812118e6..38bf7420212 100644 --- a/examples/widgets/sector.dart.old +++ b/examples/widgets/sector.dart @@ -7,7 +7,7 @@ import 'dart:math' as math; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; -import '../rendering/sector_layout.dart'; +import 'package:flutter_rendering_examples/sector_layout.dart'; RenderBox initCircle() { return new RenderBoxToRenderSectorAdapter( @@ -16,10 +16,14 @@ RenderBox initCircle() { ); } -class SectorApp extends MaterialApp { +class SectorApp extends StatefulComponent { + SectorAppState createState() => new SectorAppState(); +} - RenderBoxToRenderSectorAdapter sectors = initCircle(); - math.Random rand = new math.Random(1); +class SectorAppState extends State { + + final RenderBoxToRenderSectorAdapter sectors = initCircle(); + final math.Random rand = new math.Random(1); void addSector() { double deltaTheta; @@ -52,27 +56,27 @@ class SectorApp extends MaterialApp { RenderBoxToRenderSectorAdapter sectorAddIcon = initSector(const Color(0xFF00DD00)); RenderBoxToRenderSectorAdapter sectorRemoveIcon = initSector(const Color(0xFFDD0000)); - bool enabledAdd = true; - bool enabledRemove = false; + bool _enabledAdd = true; + bool _enabledRemove = false; void updateEnabledState() { setState(() { var ring = (sectors.child as RenderSectorRing); SectorDimensions currentSize = ring.getIntrinsicDimensions(const SectorConstraints(), ring.deltaRadius); - enabledAdd = currentSize.deltaTheta < kTwoPi; - enabledRemove = ring.firstChild != null; + _enabledAdd = currentSize.deltaTheta < kTwoPi; + _enabledRemove = ring.firstChild != null; }); } Widget buildBody() { return new Material( - child: new Column([ + child: new Column([ new Container( padding: new EdgeDims.symmetric(horizontal: 8.0, vertical: 25.0), - child: new Row([ + child: new Row([ new RaisedButton( - enabled: enabledAdd, + enabled: _enabledAdd, child: new IntrinsicWidth( - child: new Row([ + child: new Row([ new Container( padding: new EdgeDims.all(4.0), margin: new EdgeDims.only(right: 10.0), @@ -84,9 +88,9 @@ class SectorApp extends MaterialApp { onPressed: addSector ), new RaisedButton( - enabled: enabledRemove, + enabled: _enabledRemove, child: new IntrinsicWidth( - child: new Row([ + child: new Row([ new Container( padding: new EdgeDims.all(4.0), margin: new EdgeDims.only(right: 10.0), @@ -117,18 +121,20 @@ class SectorApp extends MaterialApp { ); } - Widget build() { - return new Theme( - data: new ThemeData.light(), - child: new Title( - title: 'Sector Layout', - child: new Scaffold( - toolBar: new ToolBar( - center: new Text('Sector Layout in a Widget Tree') - ), - body: buildBody() - ) - ) + Widget build(BuildContext context) { + return new MaterialApp( + theme: new ThemeData.light(), + title: 'Sector Layout', + routes: { + '/': (RouteArguments args) { + return new Scaffold( + toolBar: new ToolBar( + center: new Text('Sector Layout in a Widget Tree') + ), + body: buildBody() + ); + } + } ); } } diff --git a/packages/flutter/lib/src/rendering/box.dart b/packages/flutter/lib/src/rendering/box.dart index cfc9cd2cd55..25bb5d88291 100644 --- a/packages/flutter/lib/src/rendering/box.dart +++ b/packages/flutter/lib/src/rendering/box.dart @@ -633,7 +633,7 @@ abstract class RenderBox extends RenderObject { } } - String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}size: $size\n'; + String debugDescribeSettings(String prefix) => '${super.debugDescribeSettings(prefix)}${prefix}size: ${ hasSize ? size : "MISSING" }\n'; } /// A mixin that provides useful default behaviors for boxes with children diff --git a/packages/flutter/lib/src/widgets/basic.dart b/packages/flutter/lib/src/widgets/basic.dart index f4f3d1c4c02..346a4181a6b 100644 --- a/packages/flutter/lib/src/widgets/basic.dart +++ b/packages/flutter/lib/src/widgets/basic.dart @@ -961,6 +961,22 @@ class AssetImage extends StatelessComponent { } } +class WidgetToRenderBoxAdapter extends LeafRenderObjectWidget { + WidgetToRenderBoxAdapter(RenderBox renderBox) + : renderBox = renderBox, + // WidgetToRenderBoxAdapter objects are keyed to their render box. This + // prevents the widget being used in the widget hierarchy in two different + // places, which would cause the RenderBox to get inserted in multiple + // places in the RenderObject tree. + super(key: new GlobalObjectKey(renderBox)) { + assert(renderBox != null); + } + + final RenderBox renderBox; + + RenderBox createRenderObject() => renderBox; +} + // EVENT HANDLING