Factor out Border and BorderRadius into their own files. (#12055)
This will make it more tractable to convert them for RTL.
This commit is contained in:
parent
701d534ba9
commit
90841c9617
@ -18,6 +18,8 @@
|
||||
library painting;
|
||||
|
||||
export 'src/painting/basic_types.dart';
|
||||
export 'src/painting/border.dart';
|
||||
export 'src/painting/border_radius.dart';
|
||||
export 'src/painting/box_fit.dart';
|
||||
export 'src/painting/box_painter.dart';
|
||||
export 'src/painting/colors.dart';
|
||||
|
493
packages/flutter/lib/src/painting/border.dart
Normal file
493
packages/flutter/lib/src/painting/border.dart
Normal file
@ -0,0 +1,493 @@
|
||||
// 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:ui' as ui show lerpDouble;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'basic_types.dart';
|
||||
import 'border_radius.dart';
|
||||
import 'decoration.dart';
|
||||
import 'edge_insets.dart';
|
||||
|
||||
export 'edge_insets.dart' show EdgeInsets;
|
||||
|
||||
/// The shape to use when rendering a [Border] or [BoxDecoration].
|
||||
enum BoxShape {
|
||||
/// An axis-aligned, 2D rectangle. May have rounded corners (described by a
|
||||
/// [BorderRadius]). The edges of the rectangle will match the edges of the box
|
||||
/// into which the [Border] or [BoxDecoration] is painted.
|
||||
rectangle,
|
||||
|
||||
/// A circle centered in the middle of the box into which the [Border] or
|
||||
/// [BoxDecoration] is painted. The diameter of the circle is the shortest
|
||||
/// dimension of the box, either the width or the height, such that the circle
|
||||
/// touches the edges of the box.
|
||||
circle,
|
||||
}
|
||||
|
||||
/// The style of line to draw for a [BorderSide] in a [Border].
|
||||
enum BorderStyle {
|
||||
/// Skip the border.
|
||||
none,
|
||||
|
||||
/// Draw the border as a solid line.
|
||||
solid,
|
||||
|
||||
// if you add more, think about how they will lerp
|
||||
}
|
||||
|
||||
/// A side of a border of a box.
|
||||
///
|
||||
/// A [Border] consists of four [BorderSide] objects: [Border.top],
|
||||
/// [Border.left], [Border.right], and [Border.bottom].
|
||||
///
|
||||
/// ## Sample code
|
||||
///
|
||||
/// This sample shows how [BorderSide] objects can be used in a [Container], via
|
||||
/// a [BoxDecoration] and a [Border], to decorate some [Text]. In this example,
|
||||
/// the text has a thick bar above it that is light blue, and a thick bar below
|
||||
/// it that is a darker shade of blue.
|
||||
///
|
||||
/// ```dart
|
||||
/// new Container(
|
||||
/// padding: new EdgeInsets.all(8.0),
|
||||
/// decoration: new BoxDecoration(
|
||||
/// border: new Border(
|
||||
/// top: new BorderSide(width: 16.0, color: Colors.lightBlue.shade50),
|
||||
/// bottom: new BorderSide(width: 16.0, color: Colors.lightBlue.shade900),
|
||||
/// ),
|
||||
/// ),
|
||||
/// child: new Text('Flutter in the sky', textAlign: TextAlign.center),
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [Border], which uses [BorderSide] objects to represent its sides.
|
||||
/// * [BoxDecoration], which optionally takes a [Border] object.
|
||||
/// * [TableBorder], which extends [Border] to have two more sides
|
||||
/// ([TableBorder.horizontalInside] and [TableBorder.verticalInside]), both
|
||||
/// of which are also [BorderSide] objects.
|
||||
@immutable
|
||||
class BorderSide {
|
||||
/// Creates the side of a border.
|
||||
///
|
||||
/// By default, the border is 1.0 logical pixels wide and solid black.
|
||||
const BorderSide({
|
||||
this.color: const Color(0xFF000000),
|
||||
this.width: 1.0,
|
||||
this.style: BorderStyle.solid
|
||||
});
|
||||
|
||||
/// The color of this side of the border.
|
||||
final Color color;
|
||||
|
||||
/// The width of this side of the border, in logical pixels. A
|
||||
/// zero-width border is a hairline border. To omit the border
|
||||
/// entirely, set the [style] to [BorderStyle.none].
|
||||
final double width;
|
||||
|
||||
/// The style of this side of the border.
|
||||
///
|
||||
/// To omit a side, set [style] to [BorderStyle.none]. This skips
|
||||
/// painting the border, but the border still has a [width].
|
||||
final BorderStyle style;
|
||||
|
||||
/// A hairline black border that is not rendered.
|
||||
static const BorderSide none = const BorderSide(width: 0.0, style: BorderStyle.none);
|
||||
|
||||
/// Creates a copy of this border but with the given fields replaced with the new values.
|
||||
BorderSide copyWith({
|
||||
Color color,
|
||||
double width,
|
||||
BorderStyle style
|
||||
}) {
|
||||
return new BorderSide(
|
||||
color: color ?? this.color,
|
||||
width: width ?? this.width,
|
||||
style: style ?? this.style
|
||||
);
|
||||
}
|
||||
|
||||
/// Linearly interpolate between two border sides.
|
||||
static BorderSide lerp(BorderSide a, BorderSide b, double t) {
|
||||
assert(a != null);
|
||||
assert(b != null);
|
||||
if (t == 0.0)
|
||||
return a;
|
||||
if (t == 1.0)
|
||||
return b;
|
||||
if (a.style == b.style) {
|
||||
return new BorderSide(
|
||||
color: Color.lerp(a.color, b.color, t),
|
||||
width: ui.lerpDouble(a.width, b.width, t),
|
||||
style: a.style // == b.style
|
||||
);
|
||||
}
|
||||
Color colorA, colorB;
|
||||
switch (a.style) {
|
||||
case BorderStyle.solid:
|
||||
colorA = a.color;
|
||||
break;
|
||||
case BorderStyle.none:
|
||||
colorA = a.color.withAlpha(0x00);
|
||||
break;
|
||||
}
|
||||
switch (b.style) {
|
||||
case BorderStyle.solid:
|
||||
colorB = b.color;
|
||||
break;
|
||||
case BorderStyle.none:
|
||||
colorB = b.color.withAlpha(0x00);
|
||||
break;
|
||||
}
|
||||
return new BorderSide(
|
||||
color: Color.lerp(colorA, colorB, t),
|
||||
width: ui.lerpDouble(a.width, b.width, t),
|
||||
style: BorderStyle.solid,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic other) {
|
||||
if (identical(this, other))
|
||||
return true;
|
||||
if (runtimeType != other.runtimeType)
|
||||
return false;
|
||||
final BorderSide typedOther = other;
|
||||
return color == typedOther.color &&
|
||||
width == typedOther.width &&
|
||||
style == typedOther.style;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => hashValues(color, width, style);
|
||||
|
||||
@override
|
||||
String toString() => 'BorderSide($color, $width, $style)';
|
||||
}
|
||||
|
||||
/// A border of a box, comprised of four sides.
|
||||
///
|
||||
/// The sides are represented by [BorderSide] objects.
|
||||
///
|
||||
/// ## Sample code
|
||||
///
|
||||
/// All four borders the same, two-pixel wide solid white:
|
||||
///
|
||||
/// ```dart
|
||||
/// new Border.all(width: 2.0, color: const Color(0xFFFFFFFF))
|
||||
/// ```
|
||||
///
|
||||
/// The border for a material design divider:
|
||||
///
|
||||
/// ```dart
|
||||
/// new Border(bottom: new BorderSide(color: Theme.of(context).dividerColor))
|
||||
/// ```
|
||||
///
|
||||
/// A 1990s-era "OK" button:
|
||||
///
|
||||
/// ```dart
|
||||
/// new Container(
|
||||
/// decoration: const BoxDecoration(
|
||||
/// border: const Border(
|
||||
/// top: const BorderSide(width: 1.0, color: const Color(0xFFFFFFFFFF)),
|
||||
/// left: const BorderSide(width: 1.0, color: const Color(0xFFFFFFFFFF)),
|
||||
/// right: const BorderSide(width: 1.0, color: const Color(0xFFFF000000)),
|
||||
/// bottom: const BorderSide(width: 1.0, color: const Color(0xFFFF000000)),
|
||||
/// ),
|
||||
/// ),
|
||||
/// child: new Container(
|
||||
/// padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 2.0),
|
||||
/// decoration: const BoxDecoration(
|
||||
/// border: const Border(
|
||||
/// top: const BorderSide(width: 1.0, color: const Color(0xFFFFDFDFDF)),
|
||||
/// left: const BorderSide(width: 1.0, color: const Color(0xFFFFDFDFDF)),
|
||||
/// right: const BorderSide(width: 1.0, color: const Color(0xFFFF7F7F7F)),
|
||||
/// bottom: const BorderSide(width: 1.0, color: const Color(0xFFFF7F7F7F)),
|
||||
/// ),
|
||||
/// color: const Color(0xFFBFBFBF),
|
||||
/// ),
|
||||
/// child: const Text(
|
||||
/// 'OK',
|
||||
/// textAlign: TextAlign.center,
|
||||
/// style: const TextStyle(color: const Color(0xFF000000))
|
||||
/// ),
|
||||
/// ),
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [BoxDecoration], which uses this class to describe its edge decoration.
|
||||
/// * [BorderSide], which is used to describe each side of the box.
|
||||
/// * [Theme], from the material layer, which can be queried to obtain appropriate colors
|
||||
/// to use for borders in a material app, as shown in the "divider" sample above.
|
||||
@immutable
|
||||
class Border {
|
||||
/// Creates a border.
|
||||
///
|
||||
/// All the sides of the border default to [BorderSide.none].
|
||||
const Border({
|
||||
this.top: BorderSide.none,
|
||||
this.right: BorderSide.none,
|
||||
this.bottom: BorderSide.none,
|
||||
this.left: BorderSide.none,
|
||||
});
|
||||
|
||||
/// A uniform border with all sides the same color and width.
|
||||
///
|
||||
/// The sides default to black solid borders, one logical pixel wide.
|
||||
factory Border.all({
|
||||
Color color: const Color(0xFF000000),
|
||||
double width: 1.0,
|
||||
BorderStyle style: BorderStyle.solid,
|
||||
}) {
|
||||
final BorderSide side = new BorderSide(color: color, width: width, style: style);
|
||||
return new Border(top: side, right: side, bottom: side, left: side);
|
||||
}
|
||||
|
||||
/// The top side of this border.
|
||||
final BorderSide top;
|
||||
|
||||
/// The right side of this border.
|
||||
final BorderSide right;
|
||||
|
||||
/// The bottom side of this border.
|
||||
final BorderSide bottom;
|
||||
|
||||
/// The left side of this border.
|
||||
final BorderSide left;
|
||||
|
||||
/// The widths of the sides of this border represented as an [EdgeInsets].
|
||||
///
|
||||
/// This can be used, for example, with a [Padding] widget to inset a box by
|
||||
/// the size of these borders.
|
||||
EdgeInsets get dimensions {
|
||||
return new EdgeInsets.fromLTRB(left.width, top.width, right.width, bottom.width);
|
||||
}
|
||||
|
||||
/// Whether all four sides of the border are identical. Uniform borders are
|
||||
/// typically more efficient to paint.
|
||||
bool get isUniform {
|
||||
assert(top != null);
|
||||
assert(right != null);
|
||||
assert(bottom != null);
|
||||
assert(left != null);
|
||||
|
||||
final Color topColor = top.color;
|
||||
if (right.color != topColor ||
|
||||
bottom.color != topColor ||
|
||||
left.color != topColor)
|
||||
return false;
|
||||
|
||||
final double topWidth = top.width;
|
||||
if (right.width != topWidth ||
|
||||
bottom.width != topWidth ||
|
||||
left.width != topWidth)
|
||||
return false;
|
||||
|
||||
final BorderStyle topStyle = top.style;
|
||||
if (right.style != topStyle ||
|
||||
bottom.style != topStyle ||
|
||||
left.style != topStyle)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Creates a new border with the widths of this border multiplied by `t`.
|
||||
Border scale(double t) {
|
||||
return new Border(
|
||||
top: top.copyWith(width: t * top.width),
|
||||
right: right.copyWith(width: t * right.width),
|
||||
bottom: bottom.copyWith(width: t * bottom.width),
|
||||
left: left.copyWith(width: t * left.width)
|
||||
);
|
||||
}
|
||||
|
||||
/// Linearly interpolate between two borders.
|
||||
///
|
||||
/// If a border is null, it is treated as having four [BorderSide.none]
|
||||
/// borders.
|
||||
static Border lerp(Border a, Border b, double t) {
|
||||
if (a == null && b == null)
|
||||
return null;
|
||||
if (a == null)
|
||||
return b.scale(t);
|
||||
if (b == null)
|
||||
return a.scale(1.0 - t);
|
||||
return new Border(
|
||||
top: BorderSide.lerp(a.top, b.top, t),
|
||||
right: BorderSide.lerp(a.right, b.right, t),
|
||||
bottom: BorderSide.lerp(a.bottom, b.bottom, t),
|
||||
left: BorderSide.lerp(a.left, b.left, t)
|
||||
);
|
||||
}
|
||||
|
||||
/// Paints the border within the given [Rect] on the given [Canvas].
|
||||
///
|
||||
/// Uniform borders are more efficient to paint than more complex borders.
|
||||
///
|
||||
/// You can provide a [BoxShape] to draw the border on. If the shape in
|
||||
/// [BoxShape.circle], there is the requirement that the border [isUniform].
|
||||
///
|
||||
/// If you specify a rectangular box shape (BoxShape.rectangle), then you may
|
||||
/// specify a [BorderRadius]. If a border radius is specified, there is the
|
||||
/// requirement that the border [isUniform].
|
||||
void paint(Canvas canvas, Rect rect, {
|
||||
BoxShape shape: BoxShape.rectangle,
|
||||
BorderRadius borderRadius,
|
||||
}) {
|
||||
if (isUniform) {
|
||||
if (borderRadius != null) {
|
||||
assert(shape == BoxShape.rectangle, 'A borderRadius can only be given for rectangular boxes.');
|
||||
_paintBorderWithRadius(canvas, rect, borderRadius);
|
||||
return;
|
||||
}
|
||||
if (shape == BoxShape.circle) {
|
||||
assert(borderRadius == null);
|
||||
_paintBorderWithCircle(canvas, rect);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
assert(borderRadius == null, 'A borderRadius can only be given for uniform borders.'); // TODO(abarth): Support non-uniform rounded borders.
|
||||
assert(shape == BoxShape.rectangle, 'A border can only be drawn as a circle if it is uniform.'); // TODO(ianh): Support non-uniform borders on circles.
|
||||
|
||||
assert(top != null);
|
||||
assert(right != null);
|
||||
assert(bottom != null);
|
||||
assert(left != null);
|
||||
|
||||
final Paint paint = new Paint()
|
||||
..strokeWidth = 0.0; // used for hairline borders
|
||||
Path path;
|
||||
|
||||
switch (top.style) {
|
||||
case BorderStyle.solid:
|
||||
paint.color = top.color;
|
||||
path = new Path();
|
||||
path.moveTo(rect.left, rect.top);
|
||||
path.lineTo(rect.right, rect.top);
|
||||
if (top.width == 0.0) {
|
||||
paint.style = PaintingStyle.stroke;
|
||||
} else {
|
||||
paint.style = PaintingStyle.fill;
|
||||
path.lineTo(rect.right - right.width, rect.top + top.width);
|
||||
path.lineTo(rect.left + left.width, rect.top + top.width);
|
||||
}
|
||||
canvas.drawPath(path, paint);
|
||||
break;
|
||||
case BorderStyle.none:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (right.style) {
|
||||
case BorderStyle.solid:
|
||||
paint.color = right.color;
|
||||
path = new Path();
|
||||
path.moveTo(rect.right, rect.top);
|
||||
path.lineTo(rect.right, rect.bottom);
|
||||
if (right.width == 0.0) {
|
||||
paint.style = PaintingStyle.stroke;
|
||||
} else {
|
||||
paint.style = PaintingStyle.fill;
|
||||
path.lineTo(rect.right - right.width, rect.bottom - bottom.width);
|
||||
path.lineTo(rect.right - right.width, rect.top + top.width);
|
||||
}
|
||||
canvas.drawPath(path, paint);
|
||||
break;
|
||||
case BorderStyle.none:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (bottom.style) {
|
||||
case BorderStyle.solid:
|
||||
paint.color = bottom.color;
|
||||
path = new Path();
|
||||
path.moveTo(rect.right, rect.bottom);
|
||||
path.lineTo(rect.left, rect.bottom);
|
||||
if (bottom.width == 0.0) {
|
||||
paint.style = PaintingStyle.stroke;
|
||||
} else {
|
||||
paint.style = PaintingStyle.fill;
|
||||
path.lineTo(rect.left + left.width, rect.bottom - bottom.width);
|
||||
path.lineTo(rect.right - right.width, rect.bottom - bottom.width);
|
||||
}
|
||||
canvas.drawPath(path, paint);
|
||||
break;
|
||||
case BorderStyle.none:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (left.style) {
|
||||
case BorderStyle.solid:
|
||||
paint.color = left.color;
|
||||
path = new Path();
|
||||
path.moveTo(rect.left, rect.bottom);
|
||||
path.lineTo(rect.left, rect.top);
|
||||
if (left.width == 0.0) {
|
||||
paint.style = PaintingStyle.stroke;
|
||||
} else {
|
||||
paint.style = PaintingStyle.fill;
|
||||
path.lineTo(rect.left + left.width, rect.top + top.width);
|
||||
path.lineTo(rect.left + left.width, rect.bottom - bottom.width);
|
||||
}
|
||||
canvas.drawPath(path, paint);
|
||||
break;
|
||||
case BorderStyle.none:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _paintBorderWithRadius(Canvas canvas, Rect rect,
|
||||
BorderRadius borderRadius) {
|
||||
assert(isUniform);
|
||||
final Paint paint = new Paint()
|
||||
..color = top.color;
|
||||
final RRect outer = borderRadius.toRRect(rect);
|
||||
final double width = top.width;
|
||||
if (width == 0.0) {
|
||||
paint
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 0.0;
|
||||
canvas.drawRRect(outer, paint);
|
||||
} else {
|
||||
final RRect inner = outer.deflate(width);
|
||||
canvas.drawDRRect(outer, inner, paint);
|
||||
}
|
||||
}
|
||||
|
||||
void _paintBorderWithCircle(Canvas canvas, Rect rect) {
|
||||
assert(isUniform);
|
||||
final double width = top.width;
|
||||
final Paint paint = new Paint()
|
||||
..color = top.color
|
||||
..strokeWidth = width
|
||||
..style = PaintingStyle.stroke;
|
||||
final double radius = (rect.shortestSide - width) / 2.0;
|
||||
canvas.drawCircle(rect.center, radius, paint);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic other) {
|
||||
if (identical(this, other))
|
||||
return true;
|
||||
if (runtimeType != other.runtimeType)
|
||||
return false;
|
||||
final Border typedOther = other;
|
||||
return top == typedOther.top &&
|
||||
right == typedOther.right &&
|
||||
bottom == typedOther.bottom &&
|
||||
left == typedOther.left;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => hashValues(top, right, bottom, left);
|
||||
|
||||
@override
|
||||
String toString() => 'Border($top, $right, $bottom, $left)';
|
||||
}
|
157
packages/flutter/lib/src/painting/border_radius.dart
Normal file
157
packages/flutter/lib/src/painting/border_radius.dart
Normal file
@ -0,0 +1,157 @@
|
||||
// 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 'package:flutter/foundation.dart';
|
||||
|
||||
import 'basic_types.dart';
|
||||
|
||||
/// An immutable set of radii for each corner of a rectangle.
|
||||
///
|
||||
/// Used by [BoxDecoration] when the shape is a [BoxShape.rectangle].
|
||||
@immutable
|
||||
class BorderRadius {
|
||||
/// Creates a border radius where all radii are [radius].
|
||||
const BorderRadius.all(Radius radius) : this.only(
|
||||
topLeft: radius,
|
||||
topRight: radius,
|
||||
bottomRight: radius,
|
||||
bottomLeft: radius
|
||||
);
|
||||
|
||||
/// Creates a border radius where all radii are [Radius.circular(radius)].
|
||||
BorderRadius.circular(double radius) : this.all(
|
||||
new Radius.circular(radius)
|
||||
);
|
||||
|
||||
/// Creates a vertically symmetric border radius where the top and bottom
|
||||
/// sides of the rectangle have the same radii.
|
||||
const BorderRadius.vertical({
|
||||
Radius top: Radius.zero,
|
||||
Radius bottom: Radius.zero
|
||||
}) : this.only(
|
||||
topLeft: top,
|
||||
topRight: top,
|
||||
bottomRight: bottom,
|
||||
bottomLeft: bottom
|
||||
);
|
||||
|
||||
/// Creates a horizontally symmetrical border radius where the left and right
|
||||
/// sides of the rectangle have the same radii.
|
||||
const BorderRadius.horizontal({
|
||||
Radius left: Radius.zero,
|
||||
Radius right: Radius.zero
|
||||
}) : this.only(
|
||||
topLeft: left,
|
||||
topRight: right,
|
||||
bottomRight: right,
|
||||
bottomLeft: left
|
||||
);
|
||||
|
||||
/// Creates a border radius with only the given non-zero values. The other
|
||||
/// corners will be right angles.
|
||||
const BorderRadius.only({
|
||||
this.topLeft: Radius.zero,
|
||||
this.topRight: Radius.zero,
|
||||
this.bottomRight: Radius.zero,
|
||||
this.bottomLeft: Radius.zero
|
||||
});
|
||||
|
||||
/// A border radius with all zero radii.
|
||||
static const BorderRadius zero = const BorderRadius.all(Radius.zero);
|
||||
|
||||
/// The top-left [Radius].
|
||||
final Radius topLeft;
|
||||
/// The top-right [Radius].
|
||||
final Radius topRight;
|
||||
/// The bottom-right [Radius].
|
||||
final Radius bottomRight;
|
||||
/// The bottom-left [Radius].
|
||||
final Radius bottomLeft;
|
||||
|
||||
/// Linearly interpolates between two [BorderRadius] objects.
|
||||
///
|
||||
/// If either is null, this function interpolates from [BorderRadius.zero].
|
||||
static BorderRadius lerp(BorderRadius a, BorderRadius b, double t) {
|
||||
if (a == null && b == null)
|
||||
return null;
|
||||
return new BorderRadius.only(
|
||||
topLeft: Radius.lerp(a.topLeft, b.topLeft, t),
|
||||
topRight: Radius.lerp(a.topRight, b.topRight, t),
|
||||
bottomRight: Radius.lerp(a.bottomRight, b.bottomRight, t),
|
||||
bottomLeft: Radius.lerp(a.bottomLeft, b.bottomLeft, t)
|
||||
);
|
||||
}
|
||||
|
||||
/// Creates a [RRect] from the current border radius and a [Rect].
|
||||
RRect toRRect(Rect rect) {
|
||||
return new RRect.fromRectAndCorners(
|
||||
rect,
|
||||
topLeft: topLeft,
|
||||
topRight: topRight,
|
||||
bottomRight: bottomRight,
|
||||
bottomLeft: bottomLeft
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic other) {
|
||||
if (identical(this, other))
|
||||
return true;
|
||||
if (runtimeType != other.runtimeType)
|
||||
return false;
|
||||
final BorderRadius typedOther = other;
|
||||
return topLeft == typedOther.topLeft &&
|
||||
topRight == typedOther.topRight &&
|
||||
bottomRight == typedOther.bottomRight &&
|
||||
bottomLeft == typedOther.bottomLeft;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => hashValues(topLeft, topRight, bottomRight, bottomLeft);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
if (topLeft == topRight &&
|
||||
topRight == bottomRight &&
|
||||
bottomRight == bottomLeft) {
|
||||
if (topLeft == Radius.zero)
|
||||
return 'BorderRadius.zero';
|
||||
if (topLeft.x == topLeft.y)
|
||||
return 'BorderRadius.circular(${topLeft.x.toStringAsFixed(1)})';
|
||||
return 'BorderRadius.all($topLeft)';
|
||||
}
|
||||
if (topLeft == Radius.zero ||
|
||||
topRight == Radius.zero ||
|
||||
bottomLeft == Radius.zero ||
|
||||
bottomRight == Radius.zero) {
|
||||
final StringBuffer result = new StringBuffer();
|
||||
result.write('BorderRadius.only(');
|
||||
bool comma = false;
|
||||
if (topLeft != Radius.zero) {
|
||||
result.write('topLeft: $topLeft');
|
||||
comma = true;
|
||||
}
|
||||
if (topRight != Radius.zero) {
|
||||
if (comma)
|
||||
result.write(', ');
|
||||
result.write('topRight: $topRight');
|
||||
comma = true;
|
||||
}
|
||||
if (bottomLeft != Radius.zero) {
|
||||
if (comma)
|
||||
result.write(', ');
|
||||
result.write('bottomLeft: $bottomLeft');
|
||||
comma = true;
|
||||
}
|
||||
if (bottomRight != Radius.zero) {
|
||||
if (comma)
|
||||
result.write(', ');
|
||||
result.write('bottomRight: $bottomRight');
|
||||
}
|
||||
result.write(')');
|
||||
return result.toString();
|
||||
}
|
||||
return 'BorderRadius($topLeft, $topRight, $bottomRight, $bottomLeft)';
|
||||
}
|
||||
}
|
@ -9,6 +9,8 @@ import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'basic_types.dart';
|
||||
import 'border.dart';
|
||||
import 'border_radius.dart';
|
||||
import 'box_fit.dart';
|
||||
import 'decoration.dart';
|
||||
import 'edge_insets.dart';
|
||||
@ -16,635 +18,6 @@ import 'fractional_offset.dart';
|
||||
|
||||
export 'edge_insets.dart' show EdgeInsets;
|
||||
|
||||
/// The shape to use when rendering a BoxDecoration.
|
||||
enum BoxShape {
|
||||
/// An axis-aligned, 2D rectangle. May have rounded corners (described by a
|
||||
/// [BorderRadius]). The edges of the rectangle will match the edges of the box
|
||||
/// into which the [BoxDecoration] is painted.
|
||||
rectangle,
|
||||
|
||||
/// A circle centered in the middle of the box into which the [BoxDecoration]
|
||||
/// is painted. The diameter of the circle is the shortest dimension of the
|
||||
/// box, either the width or the height, such that the circle touches the
|
||||
/// edges of the box.
|
||||
circle,
|
||||
}
|
||||
|
||||
/// An immutable set of radii for each corner of a rectangle.
|
||||
///
|
||||
/// Used by [BoxDecoration] when the shape is a [BoxShape.rectangle].
|
||||
@immutable
|
||||
class BorderRadius {
|
||||
/// Creates a border radius where all radii are [radius].
|
||||
const BorderRadius.all(Radius radius) : this.only(
|
||||
topLeft: radius,
|
||||
topRight: radius,
|
||||
bottomRight: radius,
|
||||
bottomLeft: radius
|
||||
);
|
||||
|
||||
/// Creates a border radius where all radii are [Radius.circular(radius)].
|
||||
BorderRadius.circular(double radius) : this.all(
|
||||
new Radius.circular(radius)
|
||||
);
|
||||
|
||||
/// Creates a vertically symmetric border radius where the top and bottom
|
||||
/// sides of the rectangle have the same radii.
|
||||
const BorderRadius.vertical({
|
||||
Radius top: Radius.zero,
|
||||
Radius bottom: Radius.zero
|
||||
}) : this.only(
|
||||
topLeft: top,
|
||||
topRight: top,
|
||||
bottomRight: bottom,
|
||||
bottomLeft: bottom
|
||||
);
|
||||
|
||||
/// Creates a horizontally symmetrical border radius where the left and right
|
||||
/// sides of the rectangle have the same radii.
|
||||
const BorderRadius.horizontal({
|
||||
Radius left: Radius.zero,
|
||||
Radius right: Radius.zero
|
||||
}) : this.only(
|
||||
topLeft: left,
|
||||
topRight: right,
|
||||
bottomRight: right,
|
||||
bottomLeft: left
|
||||
);
|
||||
|
||||
/// Creates a border radius with only the given non-zero values. The other
|
||||
/// corners will be right angles.
|
||||
const BorderRadius.only({
|
||||
this.topLeft: Radius.zero,
|
||||
this.topRight: Radius.zero,
|
||||
this.bottomRight: Radius.zero,
|
||||
this.bottomLeft: Radius.zero
|
||||
});
|
||||
|
||||
/// A border radius with all zero radii.
|
||||
static const BorderRadius zero = const BorderRadius.all(Radius.zero);
|
||||
|
||||
/// The top-left [Radius].
|
||||
final Radius topLeft;
|
||||
/// The top-right [Radius].
|
||||
final Radius topRight;
|
||||
/// The bottom-right [Radius].
|
||||
final Radius bottomRight;
|
||||
/// The bottom-left [Radius].
|
||||
final Radius bottomLeft;
|
||||
|
||||
/// Linearly interpolates between two [BorderRadius] objects.
|
||||
///
|
||||
/// If either is null, this function interpolates from [BorderRadius.zero].
|
||||
static BorderRadius lerp(BorderRadius a, BorderRadius b, double t) {
|
||||
if (a == null && b == null)
|
||||
return null;
|
||||
return new BorderRadius.only(
|
||||
topLeft: Radius.lerp(a.topLeft, b.topLeft, t),
|
||||
topRight: Radius.lerp(a.topRight, b.topRight, t),
|
||||
bottomRight: Radius.lerp(a.bottomRight, b.bottomRight, t),
|
||||
bottomLeft: Radius.lerp(a.bottomLeft, b.bottomLeft, t)
|
||||
);
|
||||
}
|
||||
|
||||
/// Creates a [RRect] from the current border radius and a [Rect].
|
||||
RRect toRRect(Rect rect) {
|
||||
return new RRect.fromRectAndCorners(
|
||||
rect,
|
||||
topLeft: topLeft,
|
||||
topRight: topRight,
|
||||
bottomRight: bottomRight,
|
||||
bottomLeft: bottomLeft
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic other) {
|
||||
if (identical(this, other))
|
||||
return true;
|
||||
if (runtimeType != other.runtimeType)
|
||||
return false;
|
||||
final BorderRadius typedOther = other;
|
||||
return topLeft == typedOther.topLeft &&
|
||||
topRight == typedOther.topRight &&
|
||||
bottomRight == typedOther.bottomRight &&
|
||||
bottomLeft == typedOther.bottomLeft;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => hashValues(topLeft, topRight, bottomRight, bottomLeft);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
if (topLeft == topRight &&
|
||||
topRight == bottomRight &&
|
||||
bottomRight == bottomLeft) {
|
||||
if (topLeft == Radius.zero)
|
||||
return 'BorderRadius.zero';
|
||||
if (topLeft.x == topLeft.y)
|
||||
return 'BorderRadius.circular(${topLeft.x.toStringAsFixed(1)})';
|
||||
return 'BorderRadius.all($topLeft)';
|
||||
}
|
||||
if (topLeft == Radius.zero ||
|
||||
topRight == Radius.zero ||
|
||||
bottomLeft == Radius.zero ||
|
||||
bottomRight == Radius.zero) {
|
||||
final StringBuffer result = new StringBuffer();
|
||||
result.write('BorderRadius.only(');
|
||||
bool comma = false;
|
||||
if (topLeft != Radius.zero) {
|
||||
result.write('topLeft: $topLeft');
|
||||
comma = true;
|
||||
}
|
||||
if (topRight != Radius.zero) {
|
||||
if (comma)
|
||||
result.write(', ');
|
||||
result.write('topRight: $topRight');
|
||||
comma = true;
|
||||
}
|
||||
if (bottomLeft != Radius.zero) {
|
||||
if (comma)
|
||||
result.write(', ');
|
||||
result.write('bottomLeft: $bottomLeft');
|
||||
comma = true;
|
||||
}
|
||||
if (bottomRight != Radius.zero) {
|
||||
if (comma)
|
||||
result.write(', ');
|
||||
result.write('bottomRight: $bottomRight');
|
||||
}
|
||||
result.write(')');
|
||||
return result.toString();
|
||||
}
|
||||
return 'BorderRadius($topLeft, $topRight, $bottomRight, $bottomLeft)';
|
||||
}
|
||||
}
|
||||
|
||||
/// The style of line to draw for a [BorderSide] in a [Border].
|
||||
enum BorderStyle {
|
||||
/// Skip the border.
|
||||
none,
|
||||
|
||||
/// Draw the border as a solid line.
|
||||
solid,
|
||||
|
||||
// if you add more, think about how they will lerp
|
||||
}
|
||||
|
||||
/// A side of a border of a box.
|
||||
///
|
||||
/// A [Border] consists of four [BorderSide] objects: [Border.top],
|
||||
/// [Border.left], [Border.right], and [Border.bottom].
|
||||
///
|
||||
/// ## Sample code
|
||||
///
|
||||
/// This sample shows how [BorderSide] objects can be used in a [Container], via
|
||||
/// a [BoxDecoration] and a [Border], to decorate some [Text]. In this example,
|
||||
/// the text has a thick bar above it that is light blue, and a thick bar below
|
||||
/// it that is a darker shade of blue.
|
||||
///
|
||||
/// ```dart
|
||||
/// new Container(
|
||||
/// padding: new EdgeInsets.all(8.0),
|
||||
/// decoration: new BoxDecoration(
|
||||
/// border: new Border(
|
||||
/// top: new BorderSide(width: 16.0, color: Colors.lightBlue.shade50),
|
||||
/// bottom: new BorderSide(width: 16.0, color: Colors.lightBlue.shade900),
|
||||
/// ),
|
||||
/// ),
|
||||
/// child: new Text('Flutter in the sky', textAlign: TextAlign.center),
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [Border], which uses [BorderSide] objects to represent its sides.
|
||||
/// * [BoxDecoration], which optionally takes a [Border] object.
|
||||
/// * [TableBorder], which extends [Border] to have two more sides
|
||||
/// ([TableBorder.horizontalInside] and [TableBorder.verticalInside]), both
|
||||
/// of which are also [BorderSide] objects.
|
||||
@immutable
|
||||
class BorderSide {
|
||||
/// Creates the side of a border.
|
||||
///
|
||||
/// By default, the border is 1.0 logical pixels wide and solid black.
|
||||
const BorderSide({
|
||||
this.color: const Color(0xFF000000),
|
||||
this.width: 1.0,
|
||||
this.style: BorderStyle.solid
|
||||
});
|
||||
|
||||
/// The color of this side of the border.
|
||||
final Color color;
|
||||
|
||||
/// The width of this side of the border, in logical pixels. A
|
||||
/// zero-width border is a hairline border. To omit the border
|
||||
/// entirely, set the [style] to [BorderStyle.none].
|
||||
final double width;
|
||||
|
||||
/// The style of this side of the border.
|
||||
///
|
||||
/// To omit a side, set [style] to [BorderStyle.none]. This skips
|
||||
/// painting the border, but the border still has a [width].
|
||||
final BorderStyle style;
|
||||
|
||||
/// A hairline black border that is not rendered.
|
||||
static const BorderSide none = const BorderSide(width: 0.0, style: BorderStyle.none);
|
||||
|
||||
/// Creates a copy of this border but with the given fields replaced with the new values.
|
||||
BorderSide copyWith({
|
||||
Color color,
|
||||
double width,
|
||||
BorderStyle style
|
||||
}) {
|
||||
return new BorderSide(
|
||||
color: color ?? this.color,
|
||||
width: width ?? this.width,
|
||||
style: style ?? this.style
|
||||
);
|
||||
}
|
||||
|
||||
/// Linearly interpolate between two border sides.
|
||||
static BorderSide lerp(BorderSide a, BorderSide b, double t) {
|
||||
assert(a != null);
|
||||
assert(b != null);
|
||||
if (t == 0.0)
|
||||
return a;
|
||||
if (t == 1.0)
|
||||
return b;
|
||||
if (a.style == b.style) {
|
||||
return new BorderSide(
|
||||
color: Color.lerp(a.color, b.color, t),
|
||||
width: ui.lerpDouble(a.width, b.width, t),
|
||||
style: a.style // == b.style
|
||||
);
|
||||
}
|
||||
Color colorA, colorB;
|
||||
switch (a.style) {
|
||||
case BorderStyle.solid:
|
||||
colorA = a.color;
|
||||
break;
|
||||
case BorderStyle.none:
|
||||
colorA = a.color.withAlpha(0x00);
|
||||
break;
|
||||
}
|
||||
switch (b.style) {
|
||||
case BorderStyle.solid:
|
||||
colorB = b.color;
|
||||
break;
|
||||
case BorderStyle.none:
|
||||
colorB = b.color.withAlpha(0x00);
|
||||
break;
|
||||
}
|
||||
return new BorderSide(
|
||||
color: Color.lerp(colorA, colorB, t),
|
||||
width: ui.lerpDouble(a.width, b.width, t),
|
||||
style: BorderStyle.solid,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic other) {
|
||||
if (identical(this, other))
|
||||
return true;
|
||||
if (runtimeType != other.runtimeType)
|
||||
return false;
|
||||
final BorderSide typedOther = other;
|
||||
return color == typedOther.color &&
|
||||
width == typedOther.width &&
|
||||
style == typedOther.style;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => hashValues(color, width, style);
|
||||
|
||||
@override
|
||||
String toString() => 'BorderSide($color, $width, $style)';
|
||||
}
|
||||
|
||||
/// A border of a box, comprised of four sides.
|
||||
///
|
||||
/// The sides are represented by [BorderSide] objects.
|
||||
///
|
||||
/// ## Sample code
|
||||
///
|
||||
/// All four borders the same, two-pixel wide solid white:
|
||||
///
|
||||
/// ```dart
|
||||
/// new Border.all(width: 2.0, color: const Color(0xFFFFFFFF))
|
||||
/// ```
|
||||
///
|
||||
/// The border for a material design divider:
|
||||
///
|
||||
/// ```dart
|
||||
/// new Border(bottom: new BorderSide(color: Theme.of(context).dividerColor))
|
||||
/// ```
|
||||
///
|
||||
/// A 1990s-era "OK" button:
|
||||
///
|
||||
/// ```dart
|
||||
/// new Container(
|
||||
/// decoration: const BoxDecoration(
|
||||
/// border: const Border(
|
||||
/// top: const BorderSide(width: 1.0, color: const Color(0xFFFFFFFFFF)),
|
||||
/// left: const BorderSide(width: 1.0, color: const Color(0xFFFFFFFFFF)),
|
||||
/// right: const BorderSide(width: 1.0, color: const Color(0xFFFF000000)),
|
||||
/// bottom: const BorderSide(width: 1.0, color: const Color(0xFFFF000000)),
|
||||
/// ),
|
||||
/// ),
|
||||
/// child: new Container(
|
||||
/// padding: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 2.0),
|
||||
/// decoration: const BoxDecoration(
|
||||
/// border: const Border(
|
||||
/// top: const BorderSide(width: 1.0, color: const Color(0xFFFFDFDFDF)),
|
||||
/// left: const BorderSide(width: 1.0, color: const Color(0xFFFFDFDFDF)),
|
||||
/// right: const BorderSide(width: 1.0, color: const Color(0xFFFF7F7F7F)),
|
||||
/// bottom: const BorderSide(width: 1.0, color: const Color(0xFFFF7F7F7F)),
|
||||
/// ),
|
||||
/// color: const Color(0xFFBFBFBF),
|
||||
/// ),
|
||||
/// child: const Text(
|
||||
/// 'OK',
|
||||
/// textAlign: TextAlign.center,
|
||||
/// style: const TextStyle(color: const Color(0xFF000000))
|
||||
/// ),
|
||||
/// ),
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [BoxDecoration], which uses this class to describe its edge decoration.
|
||||
/// * [BorderSide], which is used to describe each side of the box.
|
||||
/// * [Theme], from the material layer, which can be queried to obtain appropriate colors
|
||||
/// to use for borders in a material app, as shown in the "divider" sample above.
|
||||
@immutable
|
||||
class Border {
|
||||
/// Creates a border.
|
||||
///
|
||||
/// All the sides of the border default to [BorderSide.none].
|
||||
const Border({
|
||||
this.top: BorderSide.none,
|
||||
this.right: BorderSide.none,
|
||||
this.bottom: BorderSide.none,
|
||||
this.left: BorderSide.none,
|
||||
});
|
||||
|
||||
/// A uniform border with all sides the same color and width.
|
||||
///
|
||||
/// The sides default to black solid borders, one logical pixel wide.
|
||||
factory Border.all({
|
||||
Color color: const Color(0xFF000000),
|
||||
double width: 1.0,
|
||||
BorderStyle style: BorderStyle.solid,
|
||||
}) {
|
||||
final BorderSide side = new BorderSide(color: color, width: width, style: style);
|
||||
return new Border(top: side, right: side, bottom: side, left: side);
|
||||
}
|
||||
|
||||
/// The top side of this border.
|
||||
final BorderSide top;
|
||||
|
||||
/// The right side of this border.
|
||||
final BorderSide right;
|
||||
|
||||
/// The bottom side of this border.
|
||||
final BorderSide bottom;
|
||||
|
||||
/// The left side of this border.
|
||||
final BorderSide left;
|
||||
|
||||
/// The widths of the sides of this border represented as an [EdgeInsets].
|
||||
///
|
||||
/// This can be used, for example, with a [Padding] widget to inset a box by
|
||||
/// the size of these borders.
|
||||
EdgeInsets get dimensions {
|
||||
return new EdgeInsets.fromLTRB(left.width, top.width, right.width, bottom.width);
|
||||
}
|
||||
|
||||
/// Whether all four sides of the border are identical. Uniform borders are
|
||||
/// typically more efficient to paint.
|
||||
bool get isUniform {
|
||||
assert(top != null);
|
||||
assert(right != null);
|
||||
assert(bottom != null);
|
||||
assert(left != null);
|
||||
|
||||
final Color topColor = top.color;
|
||||
if (right.color != topColor ||
|
||||
bottom.color != topColor ||
|
||||
left.color != topColor)
|
||||
return false;
|
||||
|
||||
final double topWidth = top.width;
|
||||
if (right.width != topWidth ||
|
||||
bottom.width != topWidth ||
|
||||
left.width != topWidth)
|
||||
return false;
|
||||
|
||||
final BorderStyle topStyle = top.style;
|
||||
if (right.style != topStyle ||
|
||||
bottom.style != topStyle ||
|
||||
left.style != topStyle)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Creates a new border with the widths of this border multiplied by `t`.
|
||||
Border scale(double t) {
|
||||
return new Border(
|
||||
top: top.copyWith(width: t * top.width),
|
||||
right: right.copyWith(width: t * right.width),
|
||||
bottom: bottom.copyWith(width: t * bottom.width),
|
||||
left: left.copyWith(width: t * left.width)
|
||||
);
|
||||
}
|
||||
|
||||
/// Linearly interpolate between two borders.
|
||||
///
|
||||
/// If a border is null, it is treated as having four [BorderSide.none]
|
||||
/// borders.
|
||||
static Border lerp(Border a, Border b, double t) {
|
||||
if (a == null && b == null)
|
||||
return null;
|
||||
if (a == null)
|
||||
return b.scale(t);
|
||||
if (b == null)
|
||||
return a.scale(1.0 - t);
|
||||
return new Border(
|
||||
top: BorderSide.lerp(a.top, b.top, t),
|
||||
right: BorderSide.lerp(a.right, b.right, t),
|
||||
bottom: BorderSide.lerp(a.bottom, b.bottom, t),
|
||||
left: BorderSide.lerp(a.left, b.left, t)
|
||||
);
|
||||
}
|
||||
|
||||
/// Paints the border within the given [Rect] on the given [Canvas].
|
||||
///
|
||||
/// Uniform borders are more efficient to paint than more complex borders.
|
||||
///
|
||||
/// You can provide a [BoxShape] to draw the border on. If the shape in
|
||||
/// [BoxShape.circle], there is the requirement that the border [isUniform].
|
||||
///
|
||||
/// If you specify a rectangular box shape (BoxShape.rectangle), then you may
|
||||
/// specify a [BorderRadius]. If a border radius is specified, there is the
|
||||
/// requirement that the border [isUniform].
|
||||
void paint(Canvas canvas, Rect rect, {
|
||||
BoxShape shape: BoxShape.rectangle,
|
||||
BorderRadius borderRadius,
|
||||
}) {
|
||||
if (isUniform) {
|
||||
if (borderRadius != null) {
|
||||
assert(shape == BoxShape.rectangle, 'A borderRadius can only be given for rectangular boxes.');
|
||||
_paintBorderWithRadius(canvas, rect, borderRadius);
|
||||
return;
|
||||
}
|
||||
if (shape == BoxShape.circle) {
|
||||
assert(borderRadius == null);
|
||||
_paintBorderWithCircle(canvas, rect);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
assert(borderRadius == null, 'A borderRadius can only be given for uniform borders.'); // TODO(abarth): Support non-uniform rounded borders.
|
||||
assert(shape == BoxShape.rectangle, 'A border can only be drawn as a circle if it is uniform.'); // TODO(ianh): Support non-uniform borders on circles.
|
||||
|
||||
assert(top != null);
|
||||
assert(right != null);
|
||||
assert(bottom != null);
|
||||
assert(left != null);
|
||||
|
||||
final Paint paint = new Paint()
|
||||
..strokeWidth = 0.0; // used for hairline borders
|
||||
Path path;
|
||||
|
||||
switch (top.style) {
|
||||
case BorderStyle.solid:
|
||||
paint.color = top.color;
|
||||
path = new Path();
|
||||
path.moveTo(rect.left, rect.top);
|
||||
path.lineTo(rect.right, rect.top);
|
||||
if (top.width == 0.0) {
|
||||
paint.style = PaintingStyle.stroke;
|
||||
} else {
|
||||
paint.style = PaintingStyle.fill;
|
||||
path.lineTo(rect.right - right.width, rect.top + top.width);
|
||||
path.lineTo(rect.left + left.width, rect.top + top.width);
|
||||
}
|
||||
canvas.drawPath(path, paint);
|
||||
break;
|
||||
case BorderStyle.none:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (right.style) {
|
||||
case BorderStyle.solid:
|
||||
paint.color = right.color;
|
||||
path = new Path();
|
||||
path.moveTo(rect.right, rect.top);
|
||||
path.lineTo(rect.right, rect.bottom);
|
||||
if (right.width == 0.0) {
|
||||
paint.style = PaintingStyle.stroke;
|
||||
} else {
|
||||
paint.style = PaintingStyle.fill;
|
||||
path.lineTo(rect.right - right.width, rect.bottom - bottom.width);
|
||||
path.lineTo(rect.right - right.width, rect.top + top.width);
|
||||
}
|
||||
canvas.drawPath(path, paint);
|
||||
break;
|
||||
case BorderStyle.none:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (bottom.style) {
|
||||
case BorderStyle.solid:
|
||||
paint.color = bottom.color;
|
||||
path = new Path();
|
||||
path.moveTo(rect.right, rect.bottom);
|
||||
path.lineTo(rect.left, rect.bottom);
|
||||
if (bottom.width == 0.0) {
|
||||
paint.style = PaintingStyle.stroke;
|
||||
} else {
|
||||
paint.style = PaintingStyle.fill;
|
||||
path.lineTo(rect.left + left.width, rect.bottom - bottom.width);
|
||||
path.lineTo(rect.right - right.width, rect.bottom - bottom.width);
|
||||
}
|
||||
canvas.drawPath(path, paint);
|
||||
break;
|
||||
case BorderStyle.none:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (left.style) {
|
||||
case BorderStyle.solid:
|
||||
paint.color = left.color;
|
||||
path = new Path();
|
||||
path.moveTo(rect.left, rect.bottom);
|
||||
path.lineTo(rect.left, rect.top);
|
||||
if (left.width == 0.0) {
|
||||
paint.style = PaintingStyle.stroke;
|
||||
} else {
|
||||
paint.style = PaintingStyle.fill;
|
||||
path.lineTo(rect.left + left.width, rect.top + top.width);
|
||||
path.lineTo(rect.left + left.width, rect.bottom - bottom.width);
|
||||
}
|
||||
canvas.drawPath(path, paint);
|
||||
break;
|
||||
case BorderStyle.none:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _paintBorderWithRadius(Canvas canvas, Rect rect,
|
||||
BorderRadius borderRadius) {
|
||||
assert(isUniform);
|
||||
final Paint paint = new Paint()
|
||||
..color = top.color;
|
||||
final RRect outer = borderRadius.toRRect(rect);
|
||||
final double width = top.width;
|
||||
if (width == 0.0) {
|
||||
paint
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = 0.0;
|
||||
canvas.drawRRect(outer, paint);
|
||||
} else {
|
||||
final RRect inner = outer.deflate(width);
|
||||
canvas.drawDRRect(outer, inner, paint);
|
||||
}
|
||||
}
|
||||
|
||||
void _paintBorderWithCircle(Canvas canvas, Rect rect) {
|
||||
assert(isUniform);
|
||||
final double width = top.width;
|
||||
final Paint paint = new Paint()
|
||||
..color = top.color
|
||||
..strokeWidth = width
|
||||
..style = PaintingStyle.stroke;
|
||||
final double radius = (rect.shortestSide - width) / 2.0;
|
||||
canvas.drawCircle(rect.center, radius, paint);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic other) {
|
||||
if (identical(this, other))
|
||||
return true;
|
||||
if (runtimeType != other.runtimeType)
|
||||
return false;
|
||||
final Border typedOther = other;
|
||||
return top == typedOther.top &&
|
||||
right == typedOther.right &&
|
||||
bottom == typedOther.bottom &&
|
||||
left == typedOther.left;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => hashValues(top, right, bottom, left);
|
||||
|
||||
@override
|
||||
String toString() => 'Border($top, $right, $bottom, $left)';
|
||||
}
|
||||
|
||||
/// A shadow cast by a box.
|
||||
///
|
||||
/// BoxShadow can cast non-rectangular shadows if the box is non-rectangular
|
||||
|
Loading…
x
Reference in New Issue
Block a user