Port first sector demo to fn3
Also, fix warnings in rendering/sector_layout.dart
Also, fix hit testing in rendering/sector_layout.dart
Also, add WidgetToRenderBoxAdapter
Also, make the rendering library debugging tools more resilient to
dumping stuff before layout is complete.
diff --git a/examples/raw/pubspec.yaml b/examples/raw/pubspec.yaml
index 02ca8c1..0443125 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 0000000..8944c5f
--- /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<RenderSector> { }
+
+class RenderSectorWithChildren extends RenderDecoratedSector with ContainerRenderObjectMixin<RenderSector, SectorChildListParentData> {
+ 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<RenderSector> {
+
+ 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 0eb70a3..5158fb4 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 912127a..ac3fc46 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<RenderSector> { }
-
-class RenderSectorWithChildren extends RenderDecoratedSector with ContainerRenderObjectMixin<RenderSector, SectorChildListParentData> {
- 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 1a48361..8cc7e8c 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 0b78121..38bf742 100644
--- a/examples/widgets/sector.dart.old
+++ b/examples/widgets/sector.dart
@@ -7,7 +7,7 @@
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 @@
);
}
-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<SectorApp> {
+
+ final RenderBoxToRenderSectorAdapter sectors = initCircle();
+ final math.Random rand = new math.Random(1);
void addSector() {
double deltaTheta;
@@ -52,27 +56,27 @@
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(<Widget>[
new Container(
padding: new EdgeDims.symmetric(horizontal: 8.0, vertical: 25.0),
- child: new Row([
+ child: new Row(<Widget>[
new RaisedButton(
- enabled: enabledAdd,
+ enabled: _enabledAdd,
child: new IntrinsicWidth(
- child: new Row([
+ child: new Row(<Widget>[
new Container(
padding: new EdgeDims.all(4.0),
margin: new EdgeDims.only(right: 10.0),
@@ -84,9 +88,9 @@
onPressed: addSector
),
new RaisedButton(
- enabled: enabledRemove,
+ enabled: _enabledRemove,
child: new IntrinsicWidth(
- child: new Row([
+ child: new Row(<Widget>[
new Container(
padding: new EdgeDims.all(4.0),
margin: new EdgeDims.only(right: 10.0),
@@ -117,18 +121,20 @@
);
}
- 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: <String, RouteBuilder>{
+ '/': (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 cfc9cd2..25bb5d8 100644
--- a/packages/flutter/lib/src/rendering/box.dart
+++ b/packages/flutter/lib/src/rendering/box.dart
@@ -633,7 +633,7 @@
}
}
- 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 f4f3d1c..346a418 100644
--- a/packages/flutter/lib/src/widgets/basic.dart
+++ b/packages/flutter/lib/src/widgets/basic.dart
@@ -961,6 +961,22 @@
}
}
+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