circle_border.dart 2.58 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
// Copyright 2017 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 'basic_types.dart';
import 'borders.dart';
import 'edge_insets.dart';

/// A border that fits a circle within the available space.
///
/// Typically used with [ShapeDecoration] to draw a circle.
///
/// The [dimensions] assume that the border is being used in a square space.
/// When applied to a rectangular space, the border paints in the center of the
/// rectangle.
///
/// See also:
///
///  * [BorderSide], which is used to describe each side of the box.
///  * [Border], which, when used with [BoxDecoration], can also
///    describe a circle.
class CircleBorder extends ShapeBorder {
  /// Create a circle border.
  ///
  /// The [side] argument must not be null.
28
  const CircleBorder({ this.side = BorderSide.none }) : assert(side != null);
Ian Hickson's avatar
Ian Hickson committed
29 30 31 32 33 34

  /// The style of this border.
  final BorderSide side;

  @override
  EdgeInsetsGeometry get dimensions {
35
    return EdgeInsets.all(side.width);
Ian Hickson's avatar
Ian Hickson committed
36 37 38
  }

  @override
39
  ShapeBorder scale(double t) => CircleBorder(side: side.scale(t));
Ian Hickson's avatar
Ian Hickson committed
40 41 42 43

  @override
  ShapeBorder lerpFrom(ShapeBorder a, double t) {
    if (a is CircleBorder)
44
      return CircleBorder(side: BorderSide.lerp(a.side, side, t));
Ian Hickson's avatar
Ian Hickson committed
45 46 47 48 49 50
    return super.lerpFrom(a, t);
  }

  @override
  ShapeBorder lerpTo(ShapeBorder b, double t) {
    if (b is CircleBorder)
51
      return CircleBorder(side: BorderSide.lerp(side, b.side, t));
Ian Hickson's avatar
Ian Hickson committed
52 53 54 55 56
    return super.lerpTo(b, t);
  }

  @override
  Path getInnerPath(Rect rect, { TextDirection textDirection }) {
57 58
    return Path()
      ..addOval(Rect.fromCircle(
Ian Hickson's avatar
Ian Hickson committed
59 60 61 62 63 64 65
        center: rect.center,
        radius: math.max(0.0, rect.shortestSide / 2.0 - side.width),
      ));
  }

  @override
  Path getOuterPath(Rect rect, { TextDirection textDirection }) {
66 67
    return Path()
      ..addOval(Rect.fromCircle(
Ian Hickson's avatar
Ian Hickson committed
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
        center: rect.center,
        radius: rect.shortestSide / 2.0,
      ));
  }

  @override
  void paint(Canvas canvas, Rect rect, { TextDirection textDirection }) {
    switch (side.style) {
      case BorderStyle.none:
        break;
      case BorderStyle.solid:
        canvas.drawCircle(rect.center, (rect.shortestSide - side.width) / 2.0, side.toPaint());
    }
  }

  @override
  bool operator ==(dynamic other) {
    if (runtimeType != other.runtimeType)
      return false;
    final CircleBorder typedOther = other;
    return side == typedOther.side;
  }

  @override
  int get hashCode => side.hashCode;

  @override
  String toString() {
    return '$runtimeType($side)';
  }
}