sector_layout.dart 19.7 KB
Newer Older
Hixie's avatar
Hixie committed
1 2 3 4 5 6 7
// 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 'package:flutter/rendering.dart';
8
import 'package:flutter/gestures.dart';
Hixie's avatar
Hixie committed
9

10
const double kTwoPi = 2 * math.pi;
Hixie's avatar
Hixie committed
11 12 13

class SectorConstraints extends Constraints {
  const SectorConstraints({
14 15 16 17
    this.minDeltaRadius = 0.0,
    this.maxDeltaRadius = double.infinity,
    this.minDeltaTheta = 0.0,
    this.maxDeltaTheta = kTwoPi
18 19
  }) : assert(maxDeltaRadius >= minDeltaRadius),
       assert(maxDeltaTheta >= minDeltaTheta);
Hixie's avatar
Hixie committed
20

21
  const SectorConstraints.tight({ double deltaRadius = 0.0, double deltaTheta = 0.0 })
Hixie's avatar
Hixie committed
22 23 24 25 26 27 28 29 30 31 32
    : minDeltaRadius = deltaRadius,
      maxDeltaRadius = deltaRadius,
      minDeltaTheta = deltaTheta,
      maxDeltaTheta = deltaTheta;

  final double minDeltaRadius;
  final double maxDeltaRadius;
  final double minDeltaTheta;
  final double maxDeltaTheta;

  double constrainDeltaRadius(double deltaRadius) {
33
    return deltaRadius.clamp(minDeltaRadius, maxDeltaRadius);
Hixie's avatar
Hixie committed
34 35 36
  }

  double constrainDeltaTheta(double deltaTheta) {
37
    return deltaTheta.clamp(minDeltaTheta, maxDeltaTheta);
Hixie's avatar
Hixie committed
38 39
  }

40
  @override
Hixie's avatar
Hixie committed
41
  bool get isTight => minDeltaTheta >= maxDeltaTheta && minDeltaTheta >= maxDeltaTheta;
42

43
  @override
44
  bool get isNormalized => minDeltaRadius <= maxDeltaRadius && minDeltaTheta <= maxDeltaTheta;
45 46

  @override
47
  bool debugAssertIsValid({
48
    bool isAppliedConstraint = false,
49 50
    InformationCollector informationCollector
  }) {
51 52 53
    assert(isNormalized);
    return isNormalized;
  }
Hixie's avatar
Hixie committed
54 55 56
}

class SectorDimensions {
57
  const SectorDimensions({ this.deltaRadius = 0.0, this.deltaTheta = 0.0 });
Hixie's avatar
Hixie committed
58 59 60

  factory SectorDimensions.withConstraints(
    SectorConstraints constraints,
61
    { double deltaRadius = 0.0, double deltaTheta = 0.0 }
Hixie's avatar
Hixie committed
62
  ) {
63
    return SectorDimensions(
Hixie's avatar
Hixie committed
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
      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 {

80
  @override
Hixie's avatar
Hixie committed
81 82
  void setupParentData(RenderObject child) {
    if (child.parentData is! SectorParentData)
83
      child.parentData = SectorParentData();
Hixie's avatar
Hixie committed
84 85 86 87
  }

  // RenderSectors always use SectorParentData subclasses, as they need to be
  // able to read their position information for painting and hit testing.
88
  @override
Hixie's avatar
Hixie committed
89 90 91
  SectorParentData get parentData => super.parentData;

  SectorDimensions getIntrinsicDimensions(SectorConstraints constraints, double radius) {
92
    return SectorDimensions.withConstraints(constraints);
Hixie's avatar
Hixie committed
93 94
  }

95
  @override
Hixie's avatar
Hixie committed
96
  SectorConstraints get constraints => super.constraints;
97 98

  @override
99
  void debugAssertDoesMeetConstraints() {
Hixie's avatar
Hixie committed
100 101
    assert(constraints != null);
    assert(deltaRadius != null);
102
    assert(deltaRadius < double.infinity);
Hixie's avatar
Hixie committed
103
    assert(deltaTheta != null);
104
    assert(deltaTheta < double.infinity);
105 106 107 108
    assert(constraints.minDeltaRadius <= deltaRadius);
    assert(deltaRadius <= math.max(constraints.minDeltaRadius, constraints.maxDeltaRadius));
    assert(constraints.minDeltaTheta <= deltaTheta);
    assert(deltaTheta <= math.max(constraints.minDeltaTheta, constraints.maxDeltaTheta));
Hixie's avatar
Hixie committed
109
  }
110 111

  @override
Hixie's avatar
Hixie committed
112
  void performResize() {
113
    // default behavior for subclasses that have sizedByParent = true
Hixie's avatar
Hixie committed
114 115 116
    deltaRadius = constraints.constrainDeltaRadius(0.0);
    deltaTheta = constraints.constrainDeltaTheta(0.0);
  }
117 118

  @override
Hixie's avatar
Hixie committed
119 120 121 122 123 124 125
  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);
  }

126
  @override
127
  Rect get paintBounds => Rect.fromLTWH(0.0, 0.0, 2.0 * deltaRadius, 2.0 * deltaRadius);
128 129

  @override
130
  Rect get semanticBounds => Rect.fromLTWH(-deltaRadius, -deltaRadius, 2.0 * deltaRadius, 2.0 * deltaRadius);
Hixie's avatar
Hixie committed
131 132 133 134 135 136

  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);
137
    result.add(HitTestEntry(this));
Hixie's avatar
Hixie committed
138 139 140 141 142 143 144 145 146 147 148 149 150 151
    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;
152
  set decoration(BoxDecoration value) {
Hixie's avatar
Hixie committed
153 154 155 156 157 158 159
    if (value == _decoration)
      return;
    _decoration = value;
    markNeedsPaint();
  }

  // offset must point to the center of the circle
160
  @override
Hixie's avatar
Hixie committed
161 162 163 164 165 166 167 168
  void paint(PaintingContext context, Offset offset) {
    assert(deltaRadius != null);
    assert(deltaTheta != null);
    assert(parentData is SectorParentData);

    if (_decoration == null)
      return;

169
    if (_decoration.color != null) {
Adam Barth's avatar
Adam Barth committed
170
      final Canvas canvas = context.canvas;
171 172
      final Paint paint = Paint()..color = _decoration.color;
      final Path path = Path();
173
      final double outerRadius = parentData.radius + deltaRadius;
174
      final Rect outerBounds = Rect.fromLTRB(offset.dx-outerRadius, offset.dy-outerRadius, offset.dx+outerRadius, offset.dy+outerRadius);
Hixie's avatar
Hixie committed
175
      path.arcTo(outerBounds, parentData.theta, deltaTheta, true);
176
      final double innerRadius = parentData.radius;
177
      final Rect innerBounds = Rect.fromLTRB(offset.dx-innerRadius, offset.dy-innerRadius, offset.dx+innerRadius, offset.dy+innerRadius);
Hixie's avatar
Hixie committed
178 179 180 181 182 183 184 185 186 187 188 189 190
      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);

191
  @override
Hixie's avatar
Hixie committed
192 193 194 195 196 197 198 199 200 201
  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;
    }
  }

202
  @override
Hixie's avatar
Hixie committed
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
  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,
218 219
    double deltaRadius = double.infinity,
    double padding = 0.0
220 221 222 223
  }) : _padding = padding,
       assert(deltaRadius >= 0.0),
       _desiredDeltaRadius = deltaRadius,
       super(decoration);
Hixie's avatar
Hixie committed
224 225 226

  double _desiredDeltaRadius;
  double get desiredDeltaRadius => _desiredDeltaRadius;
227
  set desiredDeltaRadius(double value) {
Hixie's avatar
Hixie committed
228
    assert(value != null);
229
    assert(value >= 0);
Hixie's avatar
Hixie committed
230 231 232 233 234 235 236 237
    if (_desiredDeltaRadius != value) {
      _desiredDeltaRadius = value;
      markNeedsLayout();
    }
  }

  double _padding;
  double get padding => _padding;
238
  set padding(double value) {
Hixie's avatar
Hixie committed
239 240 241 242 243 244 245 246
    // TODO(ianh): avoid code duplication
    assert(value != null);
    if (_padding != value) {
      _padding = value;
      markNeedsLayout();
    }
  }

247
  @override
Hixie's avatar
Hixie committed
248 249 250
  void setupParentData(RenderObject child) {
    // TODO(ianh): avoid code duplication
    if (child.parentData is! SectorChildListParentData)
251
      child.parentData = SectorChildListParentData();
Hixie's avatar
Hixie committed
252 253
  }

254
  @override
Hixie's avatar
Hixie committed
255
  SectorDimensions getIntrinsicDimensions(SectorConstraints constraints, double radius) {
256
    final double outerDeltaRadius = constraints.constrainDeltaRadius(desiredDeltaRadius);
257
    final double innerDeltaRadius = math.max(0.0, outerDeltaRadius - padding * 2.0);
258 259
    final double childRadius = radius + padding;
    final double paddingTheta = math.atan(padding / (radius + outerDeltaRadius));
Hixie's avatar
Hixie committed
260
    double innerTheta = paddingTheta; // increments with each child
261
    double remainingDeltaTheta = math.max(0.0, constraints.maxDeltaTheta - (innerTheta + paddingTheta));
Hixie's avatar
Hixie committed
262 263
    RenderSector child = firstChild;
    while (child != null) {
264
      final SectorConstraints innerConstraints = SectorConstraints(
Hixie's avatar
Hixie committed
265 266 267
        maxDeltaRadius: innerDeltaRadius,
        maxDeltaTheta: remainingDeltaTheta
      );
268
      final SectorDimensions childDimensions = child.getIntrinsicDimensions(innerConstraints, childRadius);
Hixie's avatar
Hixie committed
269 270 271 272 273 274 275 276 277
      innerTheta += childDimensions.deltaTheta;
      remainingDeltaTheta -= childDimensions.deltaTheta;
      final SectorChildListParentData childParentData = child.parentData;
      child = childParentData.nextSibling;
      if (child != null) {
        innerTheta += paddingTheta;
        remainingDeltaTheta -= paddingTheta;
      }
    }
278
    return SectorDimensions.withConstraints(constraints,
Hixie's avatar
Hixie committed
279 280 281 282
                                                deltaRadius: outerDeltaRadius,
                                                deltaTheta: innerTheta);
  }

283
  @override
Hixie's avatar
Hixie committed
284
  void performLayout() {
285
    assert(parentData is SectorParentData);
Hixie's avatar
Hixie committed
286
    deltaRadius = constraints.constrainDeltaRadius(desiredDeltaRadius);
287
    assert(deltaRadius < double.infinity);
288
    final double innerDeltaRadius = deltaRadius - padding * 2.0;
289 290
    final double childRadius = parentData.radius + padding;
    final double paddingTheta = math.atan(padding / (parentData.radius + deltaRadius));
Hixie's avatar
Hixie committed
291 292 293 294
    double innerTheta = paddingTheta; // increments with each child
    double remainingDeltaTheta = constraints.maxDeltaTheta - (innerTheta + paddingTheta);
    RenderSector child = firstChild;
    while (child != null) {
295
      final SectorConstraints innerConstraints = SectorConstraints(
Hixie's avatar
Hixie committed
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
        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
317
  @override
Hixie's avatar
Hixie committed
318 319 320 321 322
  void paint(PaintingContext context, Offset offset) {
    // TODO(ianh): avoid code duplication
    super.paint(context, offset);
    RenderSector child = firstChild;
    while (child != null) {
Adam Barth's avatar
Adam Barth committed
323
      context.paintChild(child, offset);
Hixie's avatar
Hixie committed
324 325 326 327 328 329 330 331 332 333 334 335
      final SectorChildListParentData childParentData = child.parentData;
      child = childParentData.nextSibling;
    }
  }

}

class RenderSectorSlice extends RenderSectorWithChildren {
  // lays out RenderSector children in a stack

  RenderSectorSlice({
    BoxDecoration decoration,
336 337
    double deltaTheta = kTwoPi,
    double padding = 0.0
Hixie's avatar
Hixie committed
338 339 340 341
  }) : _padding = padding, _desiredDeltaTheta = deltaTheta, super(decoration);

  double _desiredDeltaTheta;
  double get desiredDeltaTheta => _desiredDeltaTheta;
342
  set desiredDeltaTheta(double value) {
Hixie's avatar
Hixie committed
343 344 345 346 347 348 349 350 351
    assert(value != null);
    if (_desiredDeltaTheta != value) {
      _desiredDeltaTheta = value;
      markNeedsLayout();
    }
  }

  double _padding;
  double get padding => _padding;
352
  set padding(double value) {
Hixie's avatar
Hixie committed
353 354 355 356 357 358 359 360
    // TODO(ianh): avoid code duplication
    assert(value != null);
    if (_padding != value) {
      _padding = value;
      markNeedsLayout();
    }
  }

361
  @override
Hixie's avatar
Hixie committed
362 363 364
  void setupParentData(RenderObject child) {
    // TODO(ianh): avoid code duplication
    if (child.parentData is! SectorChildListParentData)
365
      child.parentData = SectorChildListParentData();
Hixie's avatar
Hixie committed
366 367
  }

368
  @override
Hixie's avatar
Hixie committed
369
  SectorDimensions getIntrinsicDimensions(SectorConstraints constraints, double radius) {
370 371
    assert(parentData is SectorParentData);
    final double paddingTheta = math.atan(padding / parentData.radius);
372 373
    final double outerDeltaTheta = constraints.constrainDeltaTheta(desiredDeltaTheta);
    final double innerDeltaTheta = outerDeltaTheta - paddingTheta * 2.0;
374
    double childRadius = parentData.radius + padding;
Hixie's avatar
Hixie committed
375 376 377
    double remainingDeltaRadius = constraints.maxDeltaRadius - (padding * 2.0);
    RenderSector child = firstChild;
    while (child != null) {
378
      final SectorConstraints innerConstraints = SectorConstraints(
Hixie's avatar
Hixie committed
379 380 381
        maxDeltaRadius: remainingDeltaRadius,
        maxDeltaTheta: innerDeltaTheta
      );
382
      final SectorDimensions childDimensions = child.getIntrinsicDimensions(innerConstraints, childRadius);
Hixie's avatar
Hixie committed
383 384 385 386 387 388 389
      childRadius += childDimensions.deltaRadius;
      remainingDeltaRadius -= childDimensions.deltaRadius;
      final SectorChildListParentData childParentData = child.parentData;
      child = childParentData.nextSibling;
      childRadius += padding;
      remainingDeltaRadius -= padding;
    }
390
    return SectorDimensions.withConstraints(constraints,
391
                                                deltaRadius: childRadius - parentData.radius,
Hixie's avatar
Hixie committed
392 393 394
                                                deltaTheta: outerDeltaTheta);
  }

395
  @override
Hixie's avatar
Hixie committed
396
  void performLayout() {
397
    assert(parentData is SectorParentData);
Hixie's avatar
Hixie committed
398 399
    deltaTheta = constraints.constrainDeltaTheta(desiredDeltaTheta);
    assert(deltaTheta <= kTwoPi);
400 401
    final double paddingTheta = math.atan(padding / parentData.radius);
    final double innerTheta = parentData.theta + paddingTheta;
402
    final double innerDeltaTheta = deltaTheta - paddingTheta * 2.0;
403
    double childRadius = parentData.radius + padding;
Hixie's avatar
Hixie committed
404 405 406
    double remainingDeltaRadius = constraints.maxDeltaRadius - (padding * 2.0);
    RenderSector child = firstChild;
    while (child != null) {
407
      final SectorConstraints innerConstraints = SectorConstraints(
Hixie's avatar
Hixie committed
408 409 410 411 412 413 414 415 416 417 418 419 420
        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;
    }
421
    deltaRadius = childRadius - parentData.radius;
Hixie's avatar
Hixie committed
422 423 424 425
  }

  // offset must point to the center of our circle
  // each sector then knows how to paint itself at its location
426
  @override
Hixie's avatar
Hixie committed
427 428 429 430 431 432
  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);
Adam Barth's avatar
Adam Barth committed
433
      context.paintChild(child, offset);
Hixie's avatar
Hixie committed
434 435 436 437 438 439 440 441 442
      final SectorChildListParentData childParentData = child.parentData;
      child = childParentData.nextSibling;
    }
  }

}

class RenderBoxToRenderSectorAdapter extends RenderBox with RenderObjectWithChildMixin<RenderSector> {

443
  RenderBoxToRenderSectorAdapter({ double innerRadius = 0.0, RenderSector child }) :
Hixie's avatar
Hixie committed
444 445 446 447 448 449
    _innerRadius = innerRadius {
    this.child = child;
  }

  double _innerRadius;
  double get innerRadius => _innerRadius;
450
  set innerRadius(double value) {
Hixie's avatar
Hixie committed
451 452 453 454
    _innerRadius = value;
    markNeedsLayout();
  }

455
  @override
Hixie's avatar
Hixie committed
456 457
  void setupParentData(RenderObject child) {
    if (child.parentData is! SectorParentData)
458
      child.parentData = SectorParentData();
Hixie's avatar
Hixie committed
459 460
  }

461
  @override
462
  double computeMinIntrinsicWidth(double height) {
Hixie's avatar
Hixie committed
463
    if (child == null)
464 465
      return 0.0;
    return getIntrinsicDimensions(height: height).width;
Hixie's avatar
Hixie committed
466 467
  }

468
  @override
469
  double computeMaxIntrinsicWidth(double height) {
Hixie's avatar
Hixie committed
470
    if (child == null)
471 472
      return 0.0;
    return getIntrinsicDimensions(height: height).width;
Hixie's avatar
Hixie committed
473 474
  }

475
  @override
476
  double computeMinIntrinsicHeight(double width) {
Hixie's avatar
Hixie committed
477
    if (child == null)
478 479
      return 0.0;
    return getIntrinsicDimensions(width: width).height;
Hixie's avatar
Hixie committed
480 481
  }

482
  @override
483
  double computeMaxIntrinsicHeight(double width) {
Hixie's avatar
Hixie committed
484
    if (child == null)
485 486
      return 0.0;
    return getIntrinsicDimensions(width: width).height;
Hixie's avatar
Hixie committed
487 488
  }

489
  Size getIntrinsicDimensions({
490 491
    double width = double.infinity,
    double height = double.infinity
492
  }) {
Hixie's avatar
Hixie committed
493 494
    assert(child is RenderSector);
    assert(child.parentData is SectorParentData);
495 496
    assert(width != null);
    assert(height != null);
497 498 499
    if (!width.isFinite && !height.isFinite)
      return Size.zero;
    final double maxChildDeltaRadius = math.max(0.0, math.min(width, height) / 2.0 - innerRadius);
500
    final SectorDimensions childDimensions = child.getIntrinsicDimensions(SectorConstraints(maxDeltaRadius: maxChildDeltaRadius), innerRadius);
501
    final double dimension = (innerRadius + childDimensions.deltaRadius) * 2.0;
502
    return Size.square(dimension);
Hixie's avatar
Hixie committed
503 504
  }

505
  @override
Hixie's avatar
Hixie committed
506
  void performLayout() {
507
    if (child == null || (!constraints.hasBoundedWidth && !constraints.hasBoundedHeight)) {
Hixie's avatar
Hixie committed
508
      size = constraints.constrain(Size.zero);
509
      return;
Hixie's avatar
Hixie committed
510
    }
511 512 513 514 515
    assert(child is RenderSector);
    assert(child.parentData is SectorParentData);
    final double maxChildDeltaRadius = math.min(constraints.maxWidth, constraints.maxHeight) / 2.0 - innerRadius;
    child.parentData.radius = innerRadius;
    child.parentData.theta = 0.0;
516
    child.layout(SectorConstraints(maxDeltaRadius: maxChildDeltaRadius), parentUsesSize: true);
517
    final double dimension = (innerRadius + child.deltaRadius) * 2.0;
518
    size = constraints.constrain(Size(dimension, dimension));
Hixie's avatar
Hixie committed
519 520
  }

521
  @override
Hixie's avatar
Hixie committed
522 523 524
  void paint(PaintingContext context, Offset offset) {
    super.paint(context, offset);
    if (child != null) {
525
      final Rect bounds = offset & size;
Hixie's avatar
Hixie committed
526
      // we move the offset to the center of the circle for the RenderSectors
527
      context.paintChild(child, bounds.center);
Hixie's avatar
Hixie committed
528 529 530
    }
  }

531
  @override
532
  bool hitTest(HitTestResult result, { Offset position }) {
Hixie's avatar
Hixie committed
533 534
    if (child == null)
      return false;
535 536
    double x = position.dx;
    double y = position.dy;
Hixie's avatar
Hixie committed
537
    // translate to our origin
538 539
    x -= size.width / 2.0;
    y -= size.height / 2.0;
Hixie's avatar
Hixie committed
540
    // convert to radius/theta
541
    final double radius = math.sqrt(x * x + y * y);
542
    final double theta = (math.atan2(x, -y) - math.pi / 2.0) % kTwoPi;
Hixie's avatar
Hixie committed
543 544 545 546 547 548 549
    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);
550
    result.add(BoxHitTestEntry(this, position));
Hixie's avatar
Hixie committed
551 552 553 554 555 556
    return true;
  }

}

class RenderSolidColor extends RenderDecoratedSector {
557
  RenderSolidColor(this.backgroundColor, {
558 559
    this.desiredDeltaRadius = double.infinity,
    this.desiredDeltaTheta = kTwoPi
560
  }) : super(BoxDecoration(color: backgroundColor));
Hixie's avatar
Hixie committed
561 562 563 564 565

  double desiredDeltaRadius;
  double desiredDeltaTheta;
  final Color backgroundColor;

566
  @override
Hixie's avatar
Hixie committed
567
  SectorDimensions getIntrinsicDimensions(SectorConstraints constraints, double radius) {
568
    return SectorDimensions.withConstraints(constraints, deltaTheta: desiredDeltaTheta);
Hixie's avatar
Hixie committed
569 570
  }

571
  @override
Hixie's avatar
Hixie committed
572 573 574 575 576
  void performLayout() {
    deltaRadius = constraints.constrainDeltaRadius(desiredDeltaRadius);
    deltaTheta = constraints.constrainDeltaTheta(desiredDeltaTheta);
  }

577
  @override
Ian Hickson's avatar
Ian Hickson committed
578 579
  void handleEvent(PointerEvent event, HitTestEntry entry) {
    if (event is PointerDownEvent) {
580
      decoration = const BoxDecoration(color: Color(0xFFFF0000));
Ian Hickson's avatar
Ian Hickson committed
581
    } else if (event is PointerUpEvent) {
582
      decoration = BoxDecoration(color: backgroundColor);
Ian Hickson's avatar
Ian Hickson committed
583
    }
Hixie's avatar
Hixie committed
584 585
  }
}