Border RTL (#12407)
This commit is contained in:
parent
cc3f5767f4
commit
cd3715a854
@ -194,10 +194,13 @@ class BorderSide {
|
||||
return a;
|
||||
if (t == 1.0)
|
||||
return b;
|
||||
final double width = ui.lerpDouble(a.width, b.width, t);
|
||||
if (width < 0.0)
|
||||
return BorderSide.none;
|
||||
if (a.style == b.style) {
|
||||
return new BorderSide(
|
||||
color: Color.lerp(a.color, b.color, t),
|
||||
width: math.max(0.0, ui.lerpDouble(a.width, b.width, t)),
|
||||
width: width,
|
||||
style: a.style, // == b.style
|
||||
);
|
||||
}
|
||||
@ -220,7 +223,7 @@ class BorderSide {
|
||||
}
|
||||
return new BorderSide(
|
||||
color: Color.lerp(colorA, colorB, t),
|
||||
width: math.max(0.0, ui.lerpDouble(a.width, b.width, t)),
|
||||
width: width,
|
||||
style: BorderStyle.solid,
|
||||
);
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
// 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';
|
||||
import 'border_radius.dart';
|
||||
import 'borders.dart';
|
||||
@ -21,7 +23,180 @@ enum BoxShape {
|
||||
circle,
|
||||
}
|
||||
|
||||
/// A border of a box, comprised of four sides.
|
||||
/// Base class for box borders that can paint as rectangle, circles, or rounded
|
||||
/// rectangles.
|
||||
///
|
||||
/// This class is extended by [Border] and [BorderDirectional] to provide
|
||||
/// concrete versions of four-sided borders using different conventions for
|
||||
/// specifying the sides.
|
||||
///
|
||||
/// The only API difference that this class introduces over [ShapeBorder] is
|
||||
/// that its [paint] method takes additional arguments.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [BorderSide], which is used to describe each side of the box.
|
||||
/// * [RoundedRectangleBorder], another way of describing a box's border.
|
||||
/// * [CircleBorder], another way of describing a circle border.
|
||||
/// * [BoxDecoration], which uses a [BoxBorder] to describe its borders.
|
||||
abstract class BoxBorder extends ShapeBorder {
|
||||
/// Abstract const constructor. This constructor enables subclasses to provide
|
||||
/// const constructors so that they can be used in const expressions.
|
||||
const BoxBorder();
|
||||
|
||||
// We override this to tighten the return value, so that callers can assume
|
||||
// that we'll return a BoxBorder.
|
||||
@override
|
||||
BoxBorder add(ShapeBorder other, { bool reversed: false }) => null;
|
||||
|
||||
/// Linearly interpolate between two borders.
|
||||
///
|
||||
/// If a border is null, it is treated as having four [BorderSide.none]
|
||||
/// borders.
|
||||
///
|
||||
/// This supports interpolating between [Border] and [BorderDirectional]
|
||||
/// objects. If both objects are different types but both have sides on one or
|
||||
/// both of their lateral edges (the two sides that aren't the top and bottom)
|
||||
/// other than [BorderSide.none], then the sides are interpolated by reducing
|
||||
/// `a`'s lateral edges to [BorderSide.none] over the first half of the
|
||||
/// animation, and then bringing `b`'s lateral edges _from_ [BorderSide.none]
|
||||
/// over the second half of the animation.
|
||||
///
|
||||
/// For a more flexible approach, consider [ShapeBorder.lerp], which would
|
||||
/// instead [add] the two sets of sides and interpolate them simultaneously.
|
||||
static BoxBorder lerp(BoxBorder a, BoxBorder b, double t) {
|
||||
if ((a is Border || a == null) && (b is Border || b == null))
|
||||
return Border.lerp(a, b, t);
|
||||
if ((a is BorderDirectional || a == null) && (b is BorderDirectional || b == null))
|
||||
return BorderDirectional.lerp(a, b, t);
|
||||
if (b is Border && a is BorderDirectional) {
|
||||
final BoxBorder c = b;
|
||||
b = a;
|
||||
a = c;
|
||||
t = 1.0 - t;
|
||||
// fall through to next case
|
||||
}
|
||||
if (a is Border && b is BorderDirectional) {
|
||||
if (b.start == BorderSide.none && b.end == BorderSide.none) {
|
||||
// The fact that b is a BorderDirectional really doesn't matter, it turns out.
|
||||
return new Border(
|
||||
top: BorderSide.lerp(a.top, b.top, t),
|
||||
right: BorderSide.lerp(a.right, BorderSide.none, t),
|
||||
bottom: BorderSide.lerp(a.bottom, b.bottom, t),
|
||||
left: BorderSide.lerp(a.left, BorderSide.none, t),
|
||||
);
|
||||
}
|
||||
if (a.left == BorderSide.none && a.right == BorderSide.none) {
|
||||
// The fact that a is a Border really doesn't matter, it turns out.
|
||||
return new BorderDirectional(
|
||||
top: BorderSide.lerp(a.top, b.top, t),
|
||||
start: BorderSide.lerp(BorderSide.none, b.start, t),
|
||||
end: BorderSide.lerp(BorderSide.none, b.end, t),
|
||||
bottom: BorderSide.lerp(a.bottom, b.bottom, t),
|
||||
);
|
||||
}
|
||||
// Since we have to swap a visual border for a directional one,
|
||||
// we speed up the horizontal sides' transitions and switch from
|
||||
// one mode to the other at t=0.5.
|
||||
if (t < 0.5) {
|
||||
return new Border(
|
||||
top: BorderSide.lerp(a.top, b.top, t),
|
||||
right: BorderSide.lerp(a.right, BorderSide.none, t * 2.0),
|
||||
bottom: BorderSide.lerp(a.bottom, b.bottom, t),
|
||||
left: BorderSide.lerp(a.left, BorderSide.none, t * 2.0),
|
||||
);
|
||||
}
|
||||
return new BorderDirectional(
|
||||
top: BorderSide.lerp(a.top, b.top, t),
|
||||
start: BorderSide.lerp(BorderSide.none, b.start, (t - 0.5) * 2.0),
|
||||
end: BorderSide.lerp(BorderSide.none, b.end, (t - 0.5) * 2.0),
|
||||
bottom: BorderSide.lerp(a.bottom, b.bottom, t),
|
||||
);
|
||||
}
|
||||
throw new FlutterError(
|
||||
'BoxBorder.lerp can only interpolate Border and BorderDirectional classes.\n'
|
||||
'BoxBorder.lerp() was called with two objects of type ${a.runtimeType} and ${b.runtimeType}:\n'
|
||||
' $a\n'
|
||||
' $b\n'
|
||||
'However, only Border and BorderDirectional classes are supported by this method. '
|
||||
'For a more general interpolation method, consider using ShapeBorder.lerp instead.'
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Path getInnerPath(Rect rect, { @required TextDirection textDirection }) {
|
||||
assert(textDirection != null, 'The textDirection argument to $runtimeType.getInnerPath must not be null.');
|
||||
return new Path()
|
||||
..addRect(dimensions.resolve(textDirection).deflateRect(rect));
|
||||
}
|
||||
|
||||
@override
|
||||
Path getOuterPath(Rect rect, { @required TextDirection textDirection }) {
|
||||
assert(textDirection != null, 'The textDirection argument to $runtimeType.getOuterPath must not be null.');
|
||||
return new Path()
|
||||
..addRect(rect);
|
||||
}
|
||||
|
||||
/// Paints the border within the given [Rect] on the given [Canvas].
|
||||
///
|
||||
/// This is an extension of the [ShapeBorder.paint] method. It allows
|
||||
/// [BoxBorder] borders to be applied to different [BoxShape]s and with
|
||||
/// different [borderRadius] parameters, without changing the [BoxBorder]
|
||||
/// object itself.
|
||||
///
|
||||
/// The `shape` argument specifies the [BoxShape] to draw the border on.
|
||||
///
|
||||
/// If the `shape` is specifies a rectangular box shape
|
||||
/// ([BoxShape.rectangle]), then the `borderRadius` argument describes the
|
||||
/// corners of the rectangle.
|
||||
///
|
||||
/// The [getInnerPath] and [getOuterPath] methods do not know about the
|
||||
/// `shape` and `borderRadius` arguments.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [paintBorder], which is used if the border is not uniform.
|
||||
@override
|
||||
void paint(Canvas canvas, Rect rect, {
|
||||
TextDirection textDirection,
|
||||
BoxShape shape: BoxShape.rectangle,
|
||||
BorderRadius borderRadius,
|
||||
});
|
||||
|
||||
static void _paintUniformBorderWithRadius(Canvas canvas, Rect rect, BorderSide side, BorderRadius borderRadius) {
|
||||
assert(side.style != BorderStyle.none);
|
||||
final Paint paint = new Paint()
|
||||
..color = side.color;
|
||||
final RRect outer = borderRadius.toRRect(rect);
|
||||
final double width = side.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);
|
||||
}
|
||||
}
|
||||
|
||||
static void _paintUniformBorderWithCircle(Canvas canvas, Rect rect, BorderSide side) {
|
||||
assert(side.style != BorderStyle.none);
|
||||
final double width = side.width;
|
||||
final Paint paint = side.toPaint();
|
||||
final double radius = (rect.shortestSide - width) / 2.0;
|
||||
canvas.drawCircle(rect.center, radius, paint);
|
||||
}
|
||||
|
||||
static void _paintUniformBorderWithRectangle(Canvas canvas, Rect rect, BorderSide side) {
|
||||
assert(side.style != BorderStyle.none);
|
||||
final double width = side.width;
|
||||
final Paint paint = side.toPaint();
|
||||
canvas.drawRect(rect.deflate(width / 2.0), paint);
|
||||
}
|
||||
}
|
||||
|
||||
/// A border of a box, comprised of four sides: top, right, bottom, left.
|
||||
///
|
||||
/// The sides are represented by [BorderSide] objects.
|
||||
///
|
||||
@ -77,16 +252,21 @@ enum BoxShape {
|
||||
/// * [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.
|
||||
class Border extends ShapeBorder {
|
||||
class Border extends BoxBorder {
|
||||
/// Creates a border.
|
||||
///
|
||||
/// All the sides of the border default to [BorderSide.none].
|
||||
///
|
||||
/// The arguments must not be null.
|
||||
const Border({
|
||||
this.top: BorderSide.none,
|
||||
this.right: BorderSide.none,
|
||||
this.bottom: BorderSide.none,
|
||||
this.left: BorderSide.none,
|
||||
});
|
||||
}) : assert(top != null),
|
||||
assert(right != null),
|
||||
assert(bottom != null),
|
||||
assert(left != null);
|
||||
|
||||
/// A uniform border with all sides the same color and width.
|
||||
///
|
||||
@ -142,11 +322,6 @@ class Border extends ShapeBorder {
|
||||
/// 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 ||
|
||||
@ -240,18 +415,6 @@ class Border extends ShapeBorder {
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Path getInnerPath(Rect rect, { TextDirection textDirection }) {
|
||||
return new Path()
|
||||
..addRect(dimensions.resolve(textDirection).deflateRect(rect));
|
||||
}
|
||||
|
||||
@override
|
||||
Path getOuterPath(Rect rect, { TextDirection textDirection }) {
|
||||
return new Path()
|
||||
..addRect(rect);
|
||||
}
|
||||
|
||||
/// Paints the border within the given [Rect] on the given [Canvas].
|
||||
///
|
||||
/// Uniform borders are more efficient to paint than more complex borders.
|
||||
@ -284,14 +447,14 @@ class Border extends ShapeBorder {
|
||||
case BorderStyle.solid:
|
||||
if (shape == BoxShape.circle) {
|
||||
assert(borderRadius == null, 'A borderRadius can only be given for rectangular boxes.');
|
||||
_paintUniformBorderWithCircle(canvas, rect);
|
||||
BoxBorder._paintUniformBorderWithCircle(canvas, rect, top);
|
||||
return;
|
||||
}
|
||||
if (borderRadius != null) {
|
||||
_paintUniformBorderWithRadius(canvas, rect, borderRadius);
|
||||
BoxBorder._paintUniformBorderWithRadius(canvas, rect, top, borderRadius);
|
||||
return;
|
||||
}
|
||||
_paintUniformBorderWithRectangle(canvas, rect);
|
||||
BoxBorder._paintUniformBorderWithRectangle(canvas, rect, top);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -302,42 +465,6 @@ class Border extends ShapeBorder {
|
||||
paintBorder(canvas, rect, top: top, right: right, bottom: bottom, left: left);
|
||||
}
|
||||
|
||||
void _paintUniformBorderWithRadius(Canvas canvas, Rect rect,
|
||||
BorderRadius borderRadius) {
|
||||
assert(isUniform);
|
||||
assert(top.style != BorderStyle.none);
|
||||
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 _paintUniformBorderWithCircle(Canvas canvas, Rect rect) {
|
||||
assert(isUniform);
|
||||
assert(top.style != BorderStyle.none);
|
||||
final double width = top.width;
|
||||
final Paint paint = top.toPaint();
|
||||
final double radius = (rect.shortestSide - width) / 2.0;
|
||||
canvas.drawCircle(rect.center, radius, paint);
|
||||
}
|
||||
|
||||
void _paintUniformBorderWithRectangle(Canvas canvas, Rect rect) {
|
||||
assert(isUniform);
|
||||
assert(top.style != BorderStyle.none);
|
||||
final double width = top.width;
|
||||
final Paint paint = top.toPaint();
|
||||
canvas.drawRect(rect.deflate(width / 2.0), paint);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic other) {
|
||||
if (identical(this, other))
|
||||
@ -358,6 +485,328 @@ class Border extends ShapeBorder {
|
||||
String toString() {
|
||||
if (isUniform)
|
||||
return 'Border.all($top)';
|
||||
return 'Border($top, $right, $bottom, $left)';
|
||||
final List<String> arguments = <String>[];
|
||||
if (top != BorderSide.none)
|
||||
arguments.add('top: $top');
|
||||
if (right != BorderSide.none)
|
||||
arguments.add('right: $right');
|
||||
if (bottom != BorderSide.none)
|
||||
arguments.add('bottom: $bottom');
|
||||
if (left != BorderSide.none)
|
||||
arguments.add('left: $left');
|
||||
return 'Border(${arguments.join(", ")})';
|
||||
}
|
||||
}
|
||||
|
||||
/// A border of a box, comprised of four sides, the lateral sides of which
|
||||
/// flip over based on the reading direction.
|
||||
///
|
||||
/// The lateral sides are called [start] and [end]. When painted in
|
||||
/// left-to-right environments, the [start] side will be painted on the left and
|
||||
/// the [end] side on the right; in right-to-left environments, it is the
|
||||
/// reverse. The other two sides are [top] and [bottom].
|
||||
///
|
||||
/// The sides are represented by [BorderSide] objects.
|
||||
///
|
||||
/// If the [start] and [end] sides are the same, then it is slightly more
|
||||
/// efficient to use a [Border] object rather than a [BorderDirectional] object.
|
||||
///
|
||||
/// 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.
|
||||
class BorderDirectional extends BoxBorder {
|
||||
/// Creates a border.
|
||||
///
|
||||
/// The [start] and [end] sides represent the horizontal sides; the start side
|
||||
/// is on the leading edge given the reading direction, and the end side is on
|
||||
/// the trailing edge. They are resolved during [paint].
|
||||
///
|
||||
/// All the sides of the border default to [BorderSide.none].
|
||||
///
|
||||
/// The arguments must not be null.
|
||||
const BorderDirectional({
|
||||
this.top: BorderSide.none,
|
||||
this.start: BorderSide.none,
|
||||
this.end: BorderSide.none,
|
||||
this.bottom: BorderSide.none,
|
||||
}) : assert(top != null),
|
||||
assert(start != null),
|
||||
assert(end != null),
|
||||
assert(bottom != null);
|
||||
|
||||
/// Creates a [BorderDirectional] that represents the addition of the two
|
||||
/// given [BorderDirectional]s.
|
||||
///
|
||||
/// It is only valid to call this if [BorderSide.canMerge] returns true for
|
||||
/// the pairwise combination of each side on both [BorderDirectional]s.
|
||||
///
|
||||
/// The arguments must not be null.
|
||||
static BorderDirectional merge(BorderDirectional a, BorderDirectional b) {
|
||||
assert(a != null);
|
||||
assert(b != null);
|
||||
assert(BorderSide.canMerge(a.top, b.top));
|
||||
assert(BorderSide.canMerge(a.start, b.start));
|
||||
assert(BorderSide.canMerge(a.end, b.end));
|
||||
assert(BorderSide.canMerge(a.bottom, b.bottom));
|
||||
return new BorderDirectional(
|
||||
top: BorderSide.merge(a.top, b.top),
|
||||
start: BorderSide.merge(a.start, b.start),
|
||||
end: BorderSide.merge(a.end, b.end),
|
||||
bottom: BorderSide.merge(a.bottom, b.bottom),
|
||||
);
|
||||
}
|
||||
|
||||
/// The top side of this border.
|
||||
final BorderSide top;
|
||||
|
||||
/// The start side of this border.
|
||||
///
|
||||
/// This is the side on the left in left-to-right text and on the right in
|
||||
/// right-to-left text.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [TextDirection], which is used to describe the reading direction.
|
||||
final BorderSide start;
|
||||
|
||||
/// The end side of this border.
|
||||
///
|
||||
/// This is the side on the right in left-to-right text and on the left in
|
||||
/// right-to-left text.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [TextDirection], which is used to describe the reading direction.
|
||||
final BorderSide end;
|
||||
|
||||
/// The bottom side of this border.
|
||||
final BorderSide bottom;
|
||||
|
||||
@override
|
||||
EdgeInsetsGeometry get dimensions {
|
||||
return new EdgeInsetsDirectional.fromSTEB(start.width, top.width, end.width, bottom.width);
|
||||
}
|
||||
|
||||
/// Whether all four sides of the border are identical. Uniform borders are
|
||||
/// typically more efficient to paint.
|
||||
bool get isUniform {
|
||||
final Color topColor = top.color;
|
||||
if (start.color != topColor ||
|
||||
end.color != topColor ||
|
||||
bottom.color != topColor)
|
||||
return false;
|
||||
|
||||
final double topWidth = top.width;
|
||||
if (start.width != topWidth ||
|
||||
end.width != topWidth ||
|
||||
bottom.width != topWidth)
|
||||
return false;
|
||||
|
||||
final BorderStyle topStyle = top.style;
|
||||
if (start.style != topStyle ||
|
||||
end.style != topStyle ||
|
||||
bottom.style != topStyle)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
BoxBorder add(ShapeBorder other, { bool reversed: false }) {
|
||||
if (other is BorderDirectional) {
|
||||
final BorderDirectional typedOther = other;
|
||||
if (BorderSide.canMerge(top, typedOther.top) &&
|
||||
BorderSide.canMerge(start, typedOther.start) &&
|
||||
BorderSide.canMerge(end, typedOther.end) &&
|
||||
BorderSide.canMerge(bottom, typedOther.bottom)) {
|
||||
return BorderDirectional.merge(this, typedOther);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (other is Border) {
|
||||
final Border typedOther = other;
|
||||
if (!BorderSide.canMerge(typedOther.top, top) ||
|
||||
!BorderSide.canMerge(typedOther.bottom, bottom))
|
||||
return null;
|
||||
if (start != BorderSide.none ||
|
||||
end != BorderSide.none) {
|
||||
if (typedOther.left != BorderSide.none ||
|
||||
typedOther.right != BorderSide.none)
|
||||
return null;
|
||||
assert(typedOther.left == BorderSide.none);
|
||||
assert(typedOther.right == BorderSide.none);
|
||||
return new BorderDirectional(
|
||||
top: BorderSide.merge(typedOther.top, top),
|
||||
start: start,
|
||||
end: end,
|
||||
bottom: BorderSide.merge(typedOther.bottom, bottom),
|
||||
);
|
||||
}
|
||||
assert(start == BorderSide.none);
|
||||
assert(end == BorderSide.none);
|
||||
return new Border(
|
||||
top: BorderSide.merge(typedOther.top, top),
|
||||
right: typedOther.right,
|
||||
bottom: BorderSide.merge(typedOther.bottom, bottom),
|
||||
left: typedOther.left,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Creates a new border with the widths of this border multiplied by `t`.
|
||||
@override
|
||||
BorderDirectional scale(double t) {
|
||||
return new BorderDirectional(
|
||||
top: top.scale(t),
|
||||
start: start.scale(t),
|
||||
end: end.scale(t),
|
||||
bottom: bottom.scale(t),
|
||||
);
|
||||
}
|
||||
|
||||
/// Linearly interpolates from `a` to [this].
|
||||
///
|
||||
/// If `a` is null, this defers to [scale].
|
||||
///
|
||||
/// If `a` is also a [BorderDirectional], this uses [BorderDirectional.lerp].
|
||||
///
|
||||
/// Otherwise, it defers to [ShapeBorder.lerpFrom].
|
||||
@override
|
||||
ShapeBorder lerpFrom(ShapeBorder a, double t) {
|
||||
if (a is BorderDirectional)
|
||||
return BorderDirectional.lerp(a, this, t);
|
||||
return super.lerpFrom(a, t);
|
||||
}
|
||||
|
||||
/// Linearly interpolates from [this] to `b`.
|
||||
///
|
||||
/// If `b` is null, this defers to [scale].
|
||||
///
|
||||
/// If `b` is also a [BorderDirectional], this uses [BorderDirectional.lerp].
|
||||
///
|
||||
/// Otherwise, it defers to [ShapeBorder.lerpTo].
|
||||
@override
|
||||
ShapeBorder lerpTo(ShapeBorder b, double t) {
|
||||
if (b is BorderDirectional)
|
||||
return BorderDirectional.lerp(this, b, t);
|
||||
return super.lerpTo(b, t);
|
||||
}
|
||||
|
||||
/// Linearly interpolate between two borders.
|
||||
///
|
||||
/// If a border is null, it is treated as having four [BorderSide.none]
|
||||
/// borders.
|
||||
static BorderDirectional lerp(BorderDirectional a, BorderDirectional 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 BorderDirectional(
|
||||
top: BorderSide.lerp(a.top, b.top, t),
|
||||
end: BorderSide.lerp(a.end, b.end, t),
|
||||
bottom: BorderSide.lerp(a.bottom, b.bottom, t),
|
||||
start: BorderSide.lerp(a.start, b.start, 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 `borderRadius` is specified, there is
|
||||
/// the requirement that the border [isUniform].
|
||||
///
|
||||
/// The [getInnerPath] and [getOuterPath] methods do not know about the
|
||||
/// `shape` and `borderRadius` arguments.
|
||||
///
|
||||
/// The `textDirection` argument is used to determine which of [start] and
|
||||
/// [end] map to the left and right. For [TextDirection.ltr], the [start] is
|
||||
/// the left and the [end] is the right; for [TextDirection.rtl], it is the
|
||||
/// reverse.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [paintBorder], which is used if the border is not uniform.
|
||||
@override
|
||||
void paint(Canvas canvas, Rect rect, {
|
||||
TextDirection textDirection,
|
||||
BoxShape shape: BoxShape.rectangle,
|
||||
BorderRadius borderRadius,
|
||||
}) {
|
||||
if (isUniform) {
|
||||
switch (top.style) {
|
||||
case BorderStyle.none:
|
||||
return;
|
||||
case BorderStyle.solid:
|
||||
if (shape == BoxShape.circle) {
|
||||
assert(borderRadius == null, 'A borderRadius can only be given for rectangular boxes.');
|
||||
BoxBorder._paintUniformBorderWithCircle(canvas, rect, top);
|
||||
return;
|
||||
}
|
||||
if (borderRadius != null) {
|
||||
BoxBorder._paintUniformBorderWithRadius(canvas, rect, top, borderRadius);
|
||||
return;
|
||||
}
|
||||
BoxBorder._paintUniformBorderWithRectangle(canvas, rect, top);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
assert(borderRadius == null, 'A borderRadius can only be given for uniform borders.');
|
||||
assert(shape == BoxShape.rectangle, 'A border can only be drawn as a circle if it is uniform.');
|
||||
|
||||
BorderSide left, right;
|
||||
assert(textDirection != null, 'Non-uniform BorderDirectional objects require a TextDirection when painting.');
|
||||
switch (textDirection) {
|
||||
case TextDirection.rtl:
|
||||
left = end;
|
||||
right = start;
|
||||
break;
|
||||
case TextDirection.ltr:
|
||||
left = start;
|
||||
right = end;
|
||||
break;
|
||||
}
|
||||
paintBorder(canvas, rect, top: top, left: left, bottom: bottom, right: right);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(dynamic other) {
|
||||
if (identical(this, other))
|
||||
return true;
|
||||
if (runtimeType != other.runtimeType)
|
||||
return false;
|
||||
final BorderDirectional typedOther = other;
|
||||
return top == typedOther.top &&
|
||||
start == typedOther.start &&
|
||||
end == typedOther.end &&
|
||||
bottom == typedOther.bottom;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => hashValues(top, start, end, bottom);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
final List<String> arguments = <String>[];
|
||||
if (top != BorderSide.none)
|
||||
arguments.add('top: $top');
|
||||
if (start != BorderSide.none)
|
||||
arguments.add('start: $start');
|
||||
if (end != BorderSide.none)
|
||||
arguments.add('end: $end');
|
||||
if (bottom != BorderSide.none)
|
||||
arguments.add('bottom: $bottom');
|
||||
return 'BorderDirectional(${arguments.join(", ")})';
|
||||
}
|
||||
}
|
||||
|
@ -110,7 +110,14 @@ class BoxDecoration extends Decoration {
|
||||
/// A border to draw above the background [color], [gradient], or [image].
|
||||
///
|
||||
/// Follows the [shape] and [borderRadius].
|
||||
final Border border;
|
||||
///
|
||||
/// Use [Border] objects to describe borders that do not depend on the reading
|
||||
/// direction.
|
||||
///
|
||||
/// Use [BoxBorder] objects to describe borders that should flip their left
|
||||
/// and right edges based on whether the text is being read left-to-right or
|
||||
/// right-to-left.
|
||||
final BoxBorder border;
|
||||
|
||||
/// If non-null, the corners of this box are rounded by this [BorderRadius].
|
||||
///
|
||||
@ -137,7 +144,7 @@ class BoxDecoration extends Decoration {
|
||||
final BoxShape shape;
|
||||
|
||||
@override
|
||||
EdgeInsets get padding => border?.dimensions;
|
||||
EdgeInsetsGeometry get padding => border?.dimensions;
|
||||
|
||||
/// Returns a new box decoration that is scaled by the given factor.
|
||||
BoxDecoration scale(double factor) {
|
||||
@ -145,7 +152,7 @@ class BoxDecoration extends Decoration {
|
||||
return new BoxDecoration(
|
||||
color: Color.lerp(null, color, factor),
|
||||
image: image,
|
||||
border: Border.lerp(null, border, factor),
|
||||
border: BoxBorder.lerp(null, border, factor),
|
||||
borderRadius: BorderRadius.lerp(null, borderRadius, factor),
|
||||
boxShadow: BoxShadow.lerpList(null, boxShadow, factor),
|
||||
gradient: gradient,
|
||||
@ -192,7 +199,7 @@ class BoxDecoration extends Decoration {
|
||||
return new BoxDecoration(
|
||||
color: Color.lerp(a.color, b.color, t),
|
||||
image: t < 0.5 ? a.image : b.image,
|
||||
border: Border.lerp(a.border, b.border, t),
|
||||
border: BoxBorder.lerp(a.border, b.border, t),
|
||||
borderRadius: BorderRadius.lerp(a.borderRadius, b.borderRadius, t),
|
||||
boxShadow: BoxShadow.lerpList(a.boxShadow, b.boxShadow, t),
|
||||
gradient: t < 0.5 ? a.gradient : b.gradient,
|
||||
@ -238,7 +245,7 @@ class BoxDecoration extends Decoration {
|
||||
|
||||
properties.add(new DiagnosticsProperty<Color>('color', color, defaultValue: null));
|
||||
properties.add(new DiagnosticsProperty<DecorationImage>('image', image, defaultValue: null));
|
||||
properties.add(new DiagnosticsProperty<Border>('border', border, defaultValue: null));
|
||||
properties.add(new DiagnosticsProperty<BoxBorder>('border', border, defaultValue: null));
|
||||
properties.add(new DiagnosticsProperty<BorderRadius>('borderRadius', borderRadius, defaultValue: null));
|
||||
properties.add(new IterableProperty<BoxShadow>('boxShadow', boxShadow, defaultValue: null, style: DiagnosticsTreeStyle.whitespace));
|
||||
properties.add(new DiagnosticsProperty<Gradient>('gradient', gradient, defaultValue: null));
|
||||
@ -421,7 +428,8 @@ class _BoxDecorationPainter extends BoxPainter {
|
||||
canvas,
|
||||
rect,
|
||||
shape: _decoration.shape,
|
||||
borderRadius: _decoration.borderRadius
|
||||
borderRadius: _decoration.borderRadius,
|
||||
textDirection: configuration.textDirection,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +53,12 @@ abstract class Decoration extends Diagnosticable {
|
||||
/// does not take into account that the circle is drawn in the center of the
|
||||
/// box regardless of the ratio of the box; it does not provide the extra
|
||||
/// padding that is implied by changing the ratio.
|
||||
EdgeInsets get padding => EdgeInsets.zero;
|
||||
///
|
||||
/// The value returned by this getter must be resolved (using
|
||||
/// [EdgeInsetsGeometry.resolve] to obtain an absolute [EdgeInsets]. (For
|
||||
/// example, [BorderDirectional] will return an [EdgeInsetsDirectional] for
|
||||
/// its [padding].)
|
||||
EdgeInsetsGeometry get padding => EdgeInsets.zero;
|
||||
|
||||
/// Whether this decoration is complex enough to benefit from caching its painting.
|
||||
bool get isComplex => false;
|
||||
|
671
packages/flutter/test/painting/border_rtl_test.dart
Normal file
671
packages/flutter/test/painting/border_rtl_test.dart
Normal file
@ -0,0 +1,671 @@
|
||||
// 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 'package:flutter/painting.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../rendering/mock_canvas.dart';
|
||||
|
||||
class SillyBorder extends BoxBorder {
|
||||
@override
|
||||
dynamic noSuchMethod(Invocation invocation) => null;
|
||||
}
|
||||
|
||||
void main() {
|
||||
test('BoxBorder.lerp', () {
|
||||
// names of the form fooAtX are foo, lerped X% of the way to null
|
||||
final BoxBorder directionalWithMagentaTop5 = const BorderDirectional(top: const BorderSide(color: const Color(0xFFFF00FF), width: 5.0));
|
||||
final BoxBorder directionalWithMagentaTop5At25 = const BorderDirectional(top: const BorderSide(color: const Color(0x3F3F003F), width: 1.25));
|
||||
final BoxBorder directionalWithMagentaTop5At75 = const BorderDirectional(top: const BorderSide(color: const Color(0xBFBF00BF), width: 3.75));
|
||||
final BoxBorder directionalWithSides10 = const BorderDirectional(start: const BorderSide(width: 10.0), end: const BorderSide(width: 20.0));
|
||||
final BoxBorder directionalWithSides10At25 = const BorderDirectional(start: const BorderSide(width: 2.5, color: const Color(0x3F000000)), end: const BorderSide(width: 5.0, color: const Color(0x3F000000)));
|
||||
final BoxBorder directionalWithSides10At50 = const BorderDirectional(start: const BorderSide(width: 5.0, color: const Color(0x7F000000)), end: const BorderSide(width: 10.0, color: const Color(0x7F000000)));
|
||||
final BoxBorder directionalWithSides10At75 = const BorderDirectional(start: const BorderSide(width: 7.5, color: const Color(0xBF000000)), end: const BorderSide(width: 15.0, color: const Color(0xBF000000)));
|
||||
final BoxBorder directionalWithSides20 = const BorderDirectional(start: const BorderSide(width: 20.0), end: const BorderSide(width: 40.0));
|
||||
final BoxBorder directionalWithSides30 = const BorderDirectional(start: const BorderSide(width: 30.0), end: const BorderSide(width: 60.0));
|
||||
final BoxBorder directionalWithTop10 = const BorderDirectional(top: const BorderSide(width: 10.0));
|
||||
final BoxBorder directionalWithYellowTop10 = const BorderDirectional(top: const BorderSide(color: const Color(0xFFFFFF00), width: 10.0));
|
||||
final BoxBorder directionalWithYellowTop5 = const BorderDirectional(top: const BorderSide(color: const Color(0xFFFFFF00), width: 5.0));
|
||||
final BoxBorder visualWithMagentaTop10 = const Border(top: const BorderSide(color: const Color(0xFFFF00FF), width: 10.0));
|
||||
final BoxBorder visualWithMagentaTop5 = const Border(top: const BorderSide(color: const Color(0xFFFF00FF), width: 5.0));
|
||||
final BoxBorder visualWithSides10 = const Border(left: const BorderSide(width: 10.0), right: const BorderSide(width: 20.0));
|
||||
final BoxBorder visualWithSides10At25 = const Border(left: const BorderSide(width: 2.5, color: const Color(0x3F000000)), right: const BorderSide(width: 5.0, color: const Color(0x3F000000)));
|
||||
final BoxBorder visualWithSides10At50 = const Border(left: const BorderSide(width: 5.0, color: const Color(0x7F000000)), right: const BorderSide(width: 10.0, color: const Color(0x7F000000)));
|
||||
final BoxBorder visualWithSides10At75 = const Border(left: const BorderSide(width: 7.5, color: const Color(0xBF000000)), right: const BorderSide(width: 15.0, color: const Color(0xBF000000)));
|
||||
final BoxBorder visualWithSides20 = const Border(left: const BorderSide(width: 20.0), right: const BorderSide(width: 40.0));
|
||||
final BoxBorder visualWithSides30 = const Border(left: const BorderSide(width: 30.0), right: const BorderSide(width: 60.0));
|
||||
final BoxBorder visualWithTop10 = const Border(top: const BorderSide(width: 10.0));
|
||||
final BoxBorder visualWithTop100 = const Border(top: const BorderSide(width: 100.0));
|
||||
final BoxBorder visualWithTop190 = const Border(top: const BorderSide(width: 190.0));
|
||||
final BoxBorder visualWithYellowTop5 = const Border(top: const BorderSide(color: const Color(0xFFFFFF00), width: 5.0));
|
||||
final BoxBorder visualWithYellowTop5At25 = const Border(top: const BorderSide(color: const Color(0x3F3F3F00), width: 1.25));
|
||||
final BoxBorder visualWithYellowTop5At75 = const Border(top: const BorderSide(color: const Color(0xBFBFBF00), width: 3.75));
|
||||
|
||||
expect(BoxBorder.lerp(null, null, -1.0), null);
|
||||
expect(BoxBorder.lerp(new Border.all(width: 10.0), null, -1.0), new Border.all(width: 20.0));
|
||||
expect(BoxBorder.lerp(null, new Border.all(width: 10.0), -1.0), new Border.all(width: 0.0, style: BorderStyle.none));
|
||||
expect(BoxBorder.lerp(directionalWithTop10, null, -1.0), const BorderDirectional(top: const BorderSide(width: 20.0)));
|
||||
expect(BoxBorder.lerp(null, directionalWithTop10, -1.0), const BorderDirectional());
|
||||
expect(BoxBorder.lerp(directionalWithTop10, visualWithTop100, -1.0), const Border());
|
||||
expect(BoxBorder.lerp(visualWithSides10, directionalWithMagentaTop5, -1.0), visualWithSides20);
|
||||
expect(BoxBorder.lerp(visualWithYellowTop5, directionalWithMagentaTop5, -1.0), const Border(top: const BorderSide(color: const Color(0xFFFFFF00), width: 5.0)));
|
||||
expect(BoxBorder.lerp(visualWithSides10, directionalWithSides10, -1.0), visualWithSides30);
|
||||
expect(BoxBorder.lerp(visualWithYellowTop5, directionalWithSides10, -1.0), directionalWithYellowTop10);
|
||||
expect(() => BoxBorder.lerp(new SillyBorder(), const Border(), -1.0), throwsFlutterError);
|
||||
|
||||
expect(BoxBorder.lerp(null, null, 0.0), null);
|
||||
expect(BoxBorder.lerp(new Border.all(width: 10.0), null, 0.0), new Border.all(width: 10.0));
|
||||
expect(BoxBorder.lerp(null, new Border.all(width: 10.0), 0.0), const Border());
|
||||
expect(BoxBorder.lerp(directionalWithTop10, null, 0.0), const BorderDirectional(top: const BorderSide(width: 10.0)));
|
||||
expect(BoxBorder.lerp(null, directionalWithTop10, 0.0), const BorderDirectional());
|
||||
expect(BoxBorder.lerp(directionalWithTop10, visualWithTop100, 0.0), visualWithTop10);
|
||||
expect(BoxBorder.lerp(visualWithSides10, directionalWithMagentaTop5, 0.0), visualWithSides10);
|
||||
expect(BoxBorder.lerp(visualWithYellowTop5, directionalWithMagentaTop5, 0.0), const Border(top: const BorderSide(color: const Color(0xFFFFFF00), width: 5.0)));
|
||||
expect(BoxBorder.lerp(visualWithSides10, directionalWithSides10, 0.0), visualWithSides10);
|
||||
expect(BoxBorder.lerp(visualWithYellowTop5, directionalWithSides10, 0.0), directionalWithYellowTop5);
|
||||
expect(() => BoxBorder.lerp(new SillyBorder(), const Border(), 0.0), throwsFlutterError);
|
||||
|
||||
expect(BoxBorder.lerp(null, null, 0.25), null);
|
||||
expect(BoxBorder.lerp(new Border.all(width: 10.0), null, 0.25), new Border.all(width: 7.5));
|
||||
expect(BoxBorder.lerp(null, new Border.all(width: 10.0), 0.25), new Border.all(width: 2.5));
|
||||
expect(BoxBorder.lerp(directionalWithTop10, null, 0.25), const BorderDirectional(top: const BorderSide(width: 7.5)));
|
||||
expect(BoxBorder.lerp(null, directionalWithTop10, 0.25), const BorderDirectional(top: const BorderSide(width: 2.5)));
|
||||
expect(BoxBorder.lerp(directionalWithTop10, visualWithTop100, 0.25), const Border(top: const BorderSide(width: 32.5)));
|
||||
expect(BoxBorder.lerp(visualWithSides10, directionalWithMagentaTop5, 0.25), visualWithSides10At75 + directionalWithMagentaTop5At25);
|
||||
expect(BoxBorder.lerp(visualWithYellowTop5, directionalWithMagentaTop5, 0.25), new Border(top: new BorderSide(width: 5.0, color: Color.lerp(const Color(0xFFFFFF00), const Color(0xFFFF00FF), 0.25))));
|
||||
expect(BoxBorder.lerp(visualWithSides10, directionalWithSides10, 0.25), visualWithSides10At50);
|
||||
expect(BoxBorder.lerp(visualWithYellowTop5, directionalWithSides10, 0.25), visualWithYellowTop5At75 + directionalWithSides10At25);
|
||||
expect(() => BoxBorder.lerp(new SillyBorder(), const Border(), 0.25), throwsFlutterError);
|
||||
|
||||
expect(BoxBorder.lerp(null, null, 0.75), null);
|
||||
expect(BoxBorder.lerp(new Border.all(width: 10.0), null, 0.75), new Border.all(width: 2.5));
|
||||
expect(BoxBorder.lerp(null, new Border.all(width: 10.0), 0.75), new Border.all(width: 7.5));
|
||||
expect(BoxBorder.lerp(directionalWithTop10, null, 0.75), const BorderDirectional(top: const BorderSide(width: 2.5)));
|
||||
expect(BoxBorder.lerp(null, directionalWithTop10, 0.75), const BorderDirectional(top: const BorderSide(width: 7.5)));
|
||||
expect(BoxBorder.lerp(directionalWithTop10, visualWithTop100, 0.75), const Border(top: const BorderSide(width: 77.5)));
|
||||
expect(BoxBorder.lerp(visualWithSides10, directionalWithMagentaTop5, 0.75), visualWithSides10At25 + directionalWithMagentaTop5At75);
|
||||
expect(BoxBorder.lerp(visualWithYellowTop5, directionalWithMagentaTop5, 0.75), new Border(top: new BorderSide(width: 5.0, color: Color.lerp(const Color(0xFFFFFF00), const Color(0xFFFF00FF), 0.75))));
|
||||
expect(BoxBorder.lerp(visualWithSides10, directionalWithSides10, 0.75), directionalWithSides10At50);
|
||||
expect(BoxBorder.lerp(visualWithYellowTop5, directionalWithSides10, 0.75), visualWithYellowTop5At25 + directionalWithSides10At75);
|
||||
expect(() => BoxBorder.lerp(new SillyBorder(), const Border(), 0.75), throwsFlutterError);
|
||||
|
||||
expect(BoxBorder.lerp(null, null, 1.0), null);
|
||||
expect(BoxBorder.lerp(new Border.all(width: 10.0), null, 1.0), new Border.all(width: 0.0, style: BorderStyle.none));
|
||||
expect(BoxBorder.lerp(null, new Border.all(width: 10.0), 1.0), new Border.all(width: 10.0));
|
||||
expect(BoxBorder.lerp(directionalWithTop10, null, 1.0), const BorderDirectional());
|
||||
expect(BoxBorder.lerp(null, directionalWithTop10, 1.0), const BorderDirectional(top: const BorderSide(width: 10.0)));
|
||||
expect(BoxBorder.lerp(directionalWithTop10, visualWithTop100, 1.0), visualWithTop100);
|
||||
expect(BoxBorder.lerp(visualWithSides10, directionalWithMagentaTop5, 1.0), visualWithMagentaTop5);
|
||||
expect(BoxBorder.lerp(visualWithYellowTop5, directionalWithMagentaTop5, 1.0), visualWithMagentaTop5);
|
||||
expect(BoxBorder.lerp(visualWithSides10, directionalWithSides10, 1.0), directionalWithSides10);
|
||||
expect(BoxBorder.lerp(visualWithYellowTop5, directionalWithSides10, 1.0), directionalWithSides10);
|
||||
expect(() => BoxBorder.lerp(new SillyBorder(), const Border(), 1.0), throwsFlutterError);
|
||||
|
||||
expect(BoxBorder.lerp(null, null, 2.0), null);
|
||||
expect(BoxBorder.lerp(new Border.all(width: 10.0), null, 2.0), new Border.all(width: 0.0, style: BorderStyle.none));
|
||||
expect(BoxBorder.lerp(null, new Border.all(width: 10.0), 2.0), new Border.all(width: 20.0));
|
||||
expect(BoxBorder.lerp(directionalWithTop10, null, 2.0), const BorderDirectional());
|
||||
expect(BoxBorder.lerp(null, directionalWithTop10, 2.0), const BorderDirectional(top: const BorderSide(width: 20.0)));
|
||||
expect(BoxBorder.lerp(directionalWithTop10, visualWithTop100, 2.0), visualWithTop190);
|
||||
expect(BoxBorder.lerp(visualWithSides10, directionalWithMagentaTop5, 2.0), visualWithMagentaTop10);
|
||||
expect(BoxBorder.lerp(visualWithYellowTop5, directionalWithMagentaTop5, 2.0), visualWithMagentaTop5);
|
||||
expect(BoxBorder.lerp(visualWithSides10, directionalWithSides10, 2.0), directionalWithSides30);
|
||||
expect(BoxBorder.lerp(visualWithYellowTop5, directionalWithSides10, 2.0), directionalWithSides20);
|
||||
expect(() => BoxBorder.lerp(new SillyBorder(), const Border(), 2.0), throwsFlutterError);
|
||||
});
|
||||
|
||||
void verifyPath(Path path, {
|
||||
Iterable<Offset> includes: const <Offset>[],
|
||||
Iterable<Offset> excludes: const <Offset>[],
|
||||
}) {
|
||||
for (Offset offset in includes)
|
||||
expect(path.contains(offset), isTrue, reason: 'Offset $offset should be inside the path.');
|
||||
for (Offset offset in excludes)
|
||||
expect(path.contains(offset), isFalse, reason: 'Offset $offset should be outside the path.');
|
||||
}
|
||||
|
||||
test('BoxBorder.getInnerPath / BoxBorder.getOuterPath', () {
|
||||
// for Border, BorderDirectional
|
||||
final Border border = const Border(top: const BorderSide(width: 10.0), right: const BorderSide(width: 20.0));
|
||||
final BorderDirectional borderDirectional = const BorderDirectional(top: const BorderSide(width: 10.0), end: const BorderSide(width: 20.0));
|
||||
verifyPath(
|
||||
border.getOuterPath(new Rect.fromLTRB(50.0, 60.0, 110.0, 190.0), textDirection: TextDirection.rtl),
|
||||
includes: <Offset>[
|
||||
const Offset(50.0, 60.0),
|
||||
const Offset(60.0, 60.0),
|
||||
const Offset(60.0, 70.0),
|
||||
const Offset(80.0, 190.0),
|
||||
const Offset(109.0, 189.0),
|
||||
const Offset(110.0, 80.0),
|
||||
const Offset(110.0, 190.0),
|
||||
],
|
||||
excludes: <Offset>[
|
||||
const Offset(40.0, 60.0),
|
||||
const Offset(50.0, 50.0),
|
||||
const Offset(111.0, 190.0),
|
||||
const Offset(110.0, 191.0),
|
||||
const Offset(111.0, 191.0),
|
||||
const Offset(0.0, 0.0),
|
||||
const Offset(-10.0, -10.0),
|
||||
const Offset(0.0, -10.0),
|
||||
const Offset(-10.0, 0.0),
|
||||
const Offset(1000.0, 1000.0),
|
||||
],
|
||||
);
|
||||
verifyPath(
|
||||
border.getInnerPath(new Rect.fromLTRB(50.0, 60.0, 110.0, 190.0), textDirection: TextDirection.rtl),
|
||||
// inner path is a rect from 50.0,70.0 to 90.0,190.0
|
||||
includes: <Offset>[
|
||||
const Offset(50.0, 70.0),
|
||||
const Offset(55.0, 70.0),
|
||||
const Offset(50.0, 75.0),
|
||||
const Offset(70.0, 70.0),
|
||||
const Offset(70.0, 71.0),
|
||||
const Offset(71.0, 70.0),
|
||||
const Offset(71.0, 71.0),
|
||||
const Offset(80.0, 180.0),
|
||||
const Offset(80.0, 190.0),
|
||||
const Offset(89.0, 189.0),
|
||||
const Offset(90.0, 190.0),
|
||||
],
|
||||
excludes: <Offset>[
|
||||
const Offset(40.0, 60.0),
|
||||
const Offset(50.0, 50.0),
|
||||
const Offset(50.0, 60.0),
|
||||
const Offset(60.0, 60.0),
|
||||
const Offset(0.0, 0.0),
|
||||
const Offset(-10.0, -10.0),
|
||||
const Offset(0.0, -10.0),
|
||||
const Offset(-10.0, 0.0),
|
||||
const Offset(110.0, 80.0),
|
||||
const Offset(89.0, 191.0),
|
||||
const Offset(90.0, 191.0),
|
||||
const Offset(91.0, 189.0),
|
||||
const Offset(91.0, 190.0),
|
||||
const Offset(91.0, 191.0),
|
||||
const Offset(109.0, 189.0),
|
||||
const Offset(110.0, 190.0),
|
||||
const Offset(1000.0, 1000.0),
|
||||
],
|
||||
);
|
||||
verifyPath(
|
||||
borderDirectional.getOuterPath(new Rect.fromLTRB(50.0, 60.0, 110.0, 190.0), textDirection: TextDirection.rtl),
|
||||
includes: <Offset>[
|
||||
const Offset(50.0, 60.0),
|
||||
const Offset(60.0, 60.0),
|
||||
const Offset(60.0, 70.0),
|
||||
const Offset(80.0, 190.0),
|
||||
const Offset(109.0, 189.0),
|
||||
const Offset(110.0, 80.0),
|
||||
const Offset(110.0, 190.0),
|
||||
],
|
||||
excludes: <Offset>[
|
||||
const Offset(40.0, 60.0),
|
||||
const Offset(50.0, 50.0),
|
||||
const Offset(111.0, 190.0),
|
||||
const Offset(110.0, 191.0),
|
||||
const Offset(111.0, 191.0),
|
||||
const Offset(0.0, 0.0),
|
||||
const Offset(-10.0, -10.0),
|
||||
const Offset(0.0, -10.0),
|
||||
const Offset(-10.0, 0.0),
|
||||
const Offset(1000.0, 1000.0),
|
||||
],
|
||||
);
|
||||
verifyPath(
|
||||
borderDirectional.getInnerPath(new Rect.fromLTRB(50.0, 60.0, 110.0, 190.0), textDirection: TextDirection.rtl),
|
||||
// inner path is a rect from 70.0,70.0 to 110.0,190.0
|
||||
includes: <Offset>[
|
||||
const Offset(70.0, 70.0),
|
||||
const Offset(70.0, 71.0),
|
||||
const Offset(71.0, 70.0),
|
||||
const Offset(71.0, 71.0),
|
||||
const Offset(80.0, 180.0),
|
||||
const Offset(80.0, 190.0),
|
||||
const Offset(89.0, 189.0),
|
||||
const Offset(90.0, 190.0),
|
||||
const Offset(91.0, 189.0),
|
||||
const Offset(91.0, 190.0),
|
||||
const Offset(109.0, 189.0),
|
||||
const Offset(110.0, 80.0),
|
||||
const Offset(110.0, 190.0),
|
||||
],
|
||||
excludes: <Offset>[
|
||||
const Offset(40.0, 60.0),
|
||||
const Offset(50.0, 50.0),
|
||||
const Offset(50.0, 60.0),
|
||||
const Offset(50.0, 70.0),
|
||||
const Offset(50.0, 75.0),
|
||||
const Offset(55.0, 70.0),
|
||||
const Offset(60.0, 60.0),
|
||||
const Offset(0.0, 0.0),
|
||||
const Offset(-10.0, -10.0),
|
||||
const Offset(0.0, -10.0),
|
||||
const Offset(-10.0, 0.0),
|
||||
const Offset(89.0, 191.0),
|
||||
const Offset(90.0, 191.0),
|
||||
const Offset(91.0, 191.0),
|
||||
const Offset(110.0, 191.0),
|
||||
const Offset(111.0, 190.0),
|
||||
const Offset(111.0, 191.0),
|
||||
const Offset(1000.0, 1000.0),
|
||||
],
|
||||
);
|
||||
verifyPath(
|
||||
borderDirectional.getOuterPath(new Rect.fromLTRB(50.0, 60.0, 110.0, 190.0), textDirection: TextDirection.ltr),
|
||||
includes: <Offset>[
|
||||
const Offset(50.0, 60.0),
|
||||
const Offset(60.0, 60.0),
|
||||
const Offset(60.0, 70.0),
|
||||
const Offset(80.0, 190.0),
|
||||
const Offset(109.0, 189.0),
|
||||
const Offset(110.0, 80.0),
|
||||
const Offset(110.0, 190.0),
|
||||
],
|
||||
excludes: <Offset>[
|
||||
const Offset(40.0, 60.0),
|
||||
const Offset(50.0, 50.0),
|
||||
const Offset(111.0, 190.0),
|
||||
const Offset(110.0, 191.0),
|
||||
const Offset(111.0, 191.0),
|
||||
const Offset(0.0, 0.0),
|
||||
const Offset(-10.0, -10.0),
|
||||
const Offset(0.0, -10.0),
|
||||
const Offset(-10.0, 0.0),
|
||||
const Offset(1000.0, 1000.0),
|
||||
],
|
||||
);
|
||||
verifyPath(
|
||||
borderDirectional.getInnerPath(new Rect.fromLTRB(50.0, 60.0, 110.0, 190.0), textDirection: TextDirection.ltr),
|
||||
// inner path is a rect from 50.0,70.0 to 90.0,190.0
|
||||
includes: <Offset>[
|
||||
const Offset(50.0, 70.0),
|
||||
const Offset(50.0, 75.0),
|
||||
const Offset(55.0, 70.0),
|
||||
const Offset(70.0, 70.0),
|
||||
const Offset(70.0, 71.0),
|
||||
const Offset(71.0, 70.0),
|
||||
const Offset(71.0, 71.0),
|
||||
const Offset(80.0, 180.0),
|
||||
const Offset(80.0, 190.0),
|
||||
const Offset(89.0, 189.0),
|
||||
const Offset(90.0, 190.0),
|
||||
],
|
||||
excludes: <Offset>[
|
||||
const Offset(50.0, 50.0),
|
||||
const Offset(40.0, 60.0),
|
||||
const Offset(50.0, 60.0),
|
||||
const Offset(60.0, 60.0),
|
||||
const Offset(0.0, 0.0),
|
||||
const Offset(-10.0, -10.0),
|
||||
const Offset(0.0, -10.0),
|
||||
const Offset(-10.0, 0.0),
|
||||
const Offset(110.0, 80.0),
|
||||
const Offset(89.0, 191.0),
|
||||
const Offset(90.0, 191.0),
|
||||
const Offset(91.0, 189.0),
|
||||
const Offset(91.0, 190.0),
|
||||
const Offset(91.0, 191.0),
|
||||
const Offset(109.0, 189.0),
|
||||
const Offset(110.0, 190.0),
|
||||
const Offset(1000.0, 1000.0),
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
test('BorderDirectional constructor', () {
|
||||
final Null $null = null;
|
||||
expect(() => new BorderDirectional(top: $null), throwsAssertionError);
|
||||
expect(() => new BorderDirectional(start: $null), throwsAssertionError);
|
||||
expect(() => new BorderDirectional(end: $null), throwsAssertionError);
|
||||
expect(() => new BorderDirectional(bottom: $null), throwsAssertionError);
|
||||
});
|
||||
|
||||
test('BorderDirectional.merge', () {
|
||||
final BorderSide magenta3 = const BorderSide(color: const Color(0xFFFF00FF), width: 3.0);
|
||||
final BorderSide magenta6 = const BorderSide(color: const Color(0xFFFF00FF), width: 6.0);
|
||||
final BorderSide yellow2 = const BorderSide(color: const Color(0xFFFFFF00), width: 2.0);
|
||||
final BorderSide yellowNone0 = const BorderSide(color: const Color(0xFFFFFF00), width: 0.0, style: BorderStyle.none);
|
||||
expect(
|
||||
BorderDirectional.merge(
|
||||
new BorderDirectional(top: yellow2),
|
||||
new BorderDirectional(end: magenta3),
|
||||
),
|
||||
new BorderDirectional(top: yellow2, end: magenta3),
|
||||
);
|
||||
expect(
|
||||
BorderDirectional.merge(
|
||||
new BorderDirectional(bottom: magenta3),
|
||||
new BorderDirectional(bottom: magenta3),
|
||||
),
|
||||
new BorderDirectional(bottom: magenta6),
|
||||
);
|
||||
expect(
|
||||
BorderDirectional.merge(
|
||||
new BorderDirectional(start: magenta3, end: yellowNone0),
|
||||
new BorderDirectional(end: yellow2),
|
||||
),
|
||||
new BorderDirectional(start: magenta3, end: yellow2),
|
||||
);
|
||||
expect(
|
||||
BorderDirectional.merge(const BorderDirectional(), const BorderDirectional()),
|
||||
const BorderDirectional(),
|
||||
);
|
||||
expect(
|
||||
() => BorderDirectional.merge(
|
||||
new BorderDirectional(start: magenta3),
|
||||
new BorderDirectional(start: yellow2),
|
||||
),
|
||||
throwsAssertionError,
|
||||
);
|
||||
});
|
||||
|
||||
test('BorderDirectional.dimensions', () {
|
||||
expect(
|
||||
const BorderDirectional(
|
||||
top: const BorderSide(width: 3.0),
|
||||
start: const BorderSide(width: 2.0),
|
||||
end: const BorderSide(width: 7.0),
|
||||
bottom: const BorderSide(width: 5.0),
|
||||
).dimensions,
|
||||
const EdgeInsetsDirectional.fromSTEB(2.0, 3.0, 7.0, 5.0),
|
||||
);
|
||||
});
|
||||
|
||||
test('BorderDirectional.isUniform', () {
|
||||
expect(
|
||||
const BorderDirectional(
|
||||
top: const BorderSide(width: 3.0),
|
||||
start: const BorderSide(width: 3.0),
|
||||
end: const BorderSide(width: 3.0),
|
||||
bottom: const BorderSide(width: 3.1),
|
||||
).isUniform,
|
||||
false,
|
||||
);
|
||||
expect(
|
||||
const BorderDirectional(
|
||||
top: const BorderSide(width: 3.0),
|
||||
start: const BorderSide(width: 3.0),
|
||||
end: const BorderSide(width: 3.0),
|
||||
bottom: const BorderSide(width: 3.0),
|
||||
).isUniform,
|
||||
true,
|
||||
);
|
||||
expect(
|
||||
const BorderDirectional(
|
||||
top: const BorderSide(color: const Color(0xFFFFFFFF)),
|
||||
start: const BorderSide(color: const Color(0xFFFFFFFE)),
|
||||
end: const BorderSide(color: const Color(0xFFFFFFFF)),
|
||||
bottom: const BorderSide(color: const Color(0xFFFFFFFF)),
|
||||
).isUniform,
|
||||
false,
|
||||
);
|
||||
expect(
|
||||
const BorderDirectional(
|
||||
top: const BorderSide(color: const Color(0xFFFFFFFF)),
|
||||
start: const BorderSide(color: const Color(0xFFFFFFFF)),
|
||||
end: const BorderSide(color: const Color(0xFFFFFFFF)),
|
||||
bottom: const BorderSide(color: const Color(0xFFFFFFFF)),
|
||||
).isUniform,
|
||||
true,
|
||||
);
|
||||
expect(
|
||||
const BorderDirectional(
|
||||
top: const BorderSide(style: BorderStyle.none),
|
||||
start: const BorderSide(style: BorderStyle.none),
|
||||
end: const BorderSide(style: BorderStyle.none),
|
||||
bottom: const BorderSide(style: BorderStyle.solid, width: 0.0),
|
||||
).isUniform,
|
||||
false,
|
||||
);
|
||||
expect(
|
||||
const BorderDirectional(
|
||||
top: const BorderSide(style: BorderStyle.none),
|
||||
start: const BorderSide(style: BorderStyle.none),
|
||||
end: const BorderSide(style: BorderStyle.none),
|
||||
bottom: BorderSide.none,
|
||||
).isUniform,
|
||||
false,
|
||||
);
|
||||
expect(
|
||||
const BorderDirectional(
|
||||
top: const BorderSide(style: BorderStyle.none, width: 0.0),
|
||||
start: const BorderSide(style: BorderStyle.none, width: 0.0),
|
||||
end: const BorderSide(style: BorderStyle.none, width: 0.0),
|
||||
bottom: BorderSide.none,
|
||||
).isUniform,
|
||||
true,
|
||||
);
|
||||
expect(
|
||||
const BorderDirectional().isUniform,
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
test('BorderDirectional.add - all directional', () {
|
||||
final BorderSide magenta3 = const BorderSide(color: const Color(0xFFFF00FF), width: 3.0);
|
||||
final BorderSide magenta6 = const BorderSide(color: const Color(0xFFFF00FF), width: 6.0);
|
||||
final BorderSide yellow2 = const BorderSide(color: const Color(0xFFFFFF00), width: 2.0);
|
||||
final BorderSide yellowNone0 = const BorderSide(color: const Color(0xFFFFFF00), width: 0.0, style: BorderStyle.none);
|
||||
expect(
|
||||
new BorderDirectional(top: yellow2) + new BorderDirectional(end: magenta3),
|
||||
new BorderDirectional(top: yellow2, end: magenta3),
|
||||
);
|
||||
expect(
|
||||
new BorderDirectional(bottom: magenta3) + new BorderDirectional(bottom: magenta3),
|
||||
new BorderDirectional(bottom: magenta6),
|
||||
);
|
||||
expect(
|
||||
new BorderDirectional(start: magenta3, end: yellowNone0) + new BorderDirectional(end: yellow2),
|
||||
new BorderDirectional(start: magenta3, end: yellow2),
|
||||
);
|
||||
expect(
|
||||
const BorderDirectional() + const BorderDirectional(),
|
||||
const BorderDirectional(),
|
||||
);
|
||||
expect(
|
||||
new BorderDirectional(start: magenta3) + new BorderDirectional(start: yellow2),
|
||||
isNot(const isInstanceOf<BorderDirectional>()), // see shape_border_test.dart for better tests of this case
|
||||
);
|
||||
final BorderDirectional b3 = new BorderDirectional(top: magenta3);
|
||||
final BorderDirectional b6 = new BorderDirectional(top: magenta6);
|
||||
expect(b3 + b3, b6);
|
||||
final BorderDirectional b0 = new BorderDirectional(top: yellowNone0);
|
||||
final BorderDirectional bZ = const BorderDirectional();
|
||||
expect(b0 + b0, bZ);
|
||||
expect(bZ + bZ, bZ);
|
||||
expect(b0 + bZ, bZ);
|
||||
expect(bZ + b0, bZ);
|
||||
});
|
||||
|
||||
test('BorderDirectional.add', () {
|
||||
const BorderSide side1 = const BorderSide(color: const Color(0x11111111));
|
||||
const BorderSide doubleSide1 = const BorderSide(color: const Color(0x11111111), width: 2.0);
|
||||
const BorderSide side2 = const BorderSide(color: const Color(0x22222222));
|
||||
const BorderSide doubleSide2 = const BorderSide(color: const Color(0x22222222), width: 2.0);
|
||||
|
||||
// adding tops and sides
|
||||
expect(const Border(left: side1) + const BorderDirectional(top: side2), const Border(left: side1, top: side2));
|
||||
expect(const BorderDirectional(start: side1) + const Border(top: side2), const BorderDirectional(start: side1, top: side2));
|
||||
expect(const Border(top: side2) + const BorderDirectional(start: side1), const BorderDirectional(start: side1, top: side2));
|
||||
expect(const BorderDirectional(top: side2) + const Border(left: side1), const Border(left: side1, top: side2));
|
||||
|
||||
// adding incompatible tops and bottoms
|
||||
expect((const Border(top: side1) + const BorderDirectional(top: side2)).toString(), contains(' + '));
|
||||
expect((const BorderDirectional(top: side2) + const Border(top: side1)).toString(), contains(' + '));
|
||||
expect((const Border(bottom: side1) + const BorderDirectional(bottom: side2)).toString(), contains(' + '));
|
||||
expect((const BorderDirectional(bottom: side2) + const Border(bottom: side1)).toString(), contains(' + '));
|
||||
|
||||
// adding compatible tops and bottoms
|
||||
expect(const BorderDirectional(top: side1) + const Border(top: side1), const Border(top: doubleSide1));
|
||||
expect(const Border(top: side1) + const BorderDirectional(top: side1), const Border(top: doubleSide1));
|
||||
expect(const BorderDirectional(bottom: side1) + const Border(bottom: side1), const Border(bottom: doubleSide1));
|
||||
expect(const Border(bottom: side1) + const BorderDirectional(bottom: side1), const Border(bottom: doubleSide1));
|
||||
|
||||
const Border borderWithLeft = const Border(left: side1, top: side2, bottom: side2);
|
||||
const Border borderWithRight = const Border(right: side1, top: side2, bottom: side2);
|
||||
const Border borderWithoutSides = const Border(top: side2, bottom: side2);
|
||||
const BorderDirectional borderDirectionalWithStart = const BorderDirectional(start: side1, top: side2, bottom: side2);
|
||||
const BorderDirectional borderDirectionalWithEnd = const BorderDirectional(end: side1, top: side2, bottom: side2);
|
||||
const BorderDirectional borderDirectionalWithoutSides = const BorderDirectional(top: side2, bottom: side2);
|
||||
|
||||
expect((borderWithLeft + borderDirectionalWithStart).toString(), '$borderWithLeft + $borderDirectionalWithStart');
|
||||
expect((borderWithLeft + borderDirectionalWithEnd).toString(), '$borderWithLeft + $borderDirectionalWithEnd');
|
||||
expect((borderWithLeft + borderDirectionalWithoutSides).toString(), '${const Border(left: side1, top: doubleSide2, bottom: doubleSide2)}');
|
||||
expect((borderWithRight + borderDirectionalWithStart).toString(), '$borderWithRight + $borderDirectionalWithStart');
|
||||
expect((borderWithRight + borderDirectionalWithEnd).toString(), '$borderWithRight + $borderDirectionalWithEnd');
|
||||
expect((borderWithRight + borderDirectionalWithoutSides).toString(), '${const Border(right: side1, top: doubleSide2, bottom: doubleSide2)}');
|
||||
expect((borderWithoutSides + borderDirectionalWithStart).toString(), '${const BorderDirectional(start: side1, top: doubleSide2, bottom: doubleSide2)}');
|
||||
expect((borderWithoutSides + borderDirectionalWithEnd).toString(), '${const BorderDirectional(end: side1, top: doubleSide2, bottom: doubleSide2)}');
|
||||
expect((borderWithoutSides + borderDirectionalWithoutSides).toString(), '${const Border(top: doubleSide2, bottom: doubleSide2)}');
|
||||
|
||||
expect((borderDirectionalWithStart + borderWithLeft).toString(), '$borderDirectionalWithStart + $borderWithLeft');
|
||||
expect((borderDirectionalWithEnd + borderWithLeft).toString(), '$borderDirectionalWithEnd + $borderWithLeft');
|
||||
expect((borderDirectionalWithoutSides + borderWithLeft).toString(), '${const Border(left: side1, top: doubleSide2, bottom: doubleSide2)}');
|
||||
expect((borderDirectionalWithStart + borderWithRight).toString(), '$borderDirectionalWithStart + $borderWithRight');
|
||||
expect((borderDirectionalWithEnd + borderWithRight).toString(), '$borderDirectionalWithEnd + $borderWithRight');
|
||||
expect((borderDirectionalWithoutSides + borderWithRight).toString(), '${const Border(right: side1, top: doubleSide2, bottom: doubleSide2)}');
|
||||
expect((borderDirectionalWithStart + borderWithoutSides).toString(), '${const BorderDirectional(start: side1, top: doubleSide2, bottom: doubleSide2)}');
|
||||
expect((borderDirectionalWithEnd + borderWithoutSides).toString(), '${const BorderDirectional(end: side1, top: doubleSide2, bottom: doubleSide2)}');
|
||||
expect((borderDirectionalWithoutSides + borderWithoutSides).toString(), '${const Border(top: doubleSide2, bottom: doubleSide2)}');
|
||||
});
|
||||
|
||||
test('BorderDirectional.scale', () {
|
||||
final BorderSide magenta3 = const BorderSide(color: const Color(0xFFFF00FF), width: 3.0);
|
||||
final BorderSide magenta6 = const BorderSide(color: const Color(0xFFFF00FF), width: 6.0);
|
||||
final BorderSide yellow2 = const BorderSide(color: const Color(0xFFFFFF00), width: 2.0);
|
||||
final BorderSide yellowNone0 = const BorderSide(color: const Color(0xFFFFFF00), width: 0.0, style: BorderStyle.none);
|
||||
final BorderDirectional b3 = new BorderDirectional(start: magenta3);
|
||||
final BorderDirectional b6 = new BorderDirectional(start: magenta6);
|
||||
expect(b3.scale(2.0), b6);
|
||||
final BorderDirectional bY0 = new BorderDirectional(top: yellowNone0);
|
||||
expect(bY0.scale(3.0), bY0);
|
||||
final BorderDirectional bY2 = new BorderDirectional(top: yellow2);
|
||||
expect(bY2.scale(0.0), bY0);
|
||||
});
|
||||
|
||||
test('BorderDirectional.lerp', () {
|
||||
final BorderDirectional directionalWithTop10 = const BorderDirectional(top: const BorderSide(width: 10.0));
|
||||
final BorderDirectional atMinus100 = const BorderDirectional(start: const BorderSide(width: 0.0), end: const BorderSide(width: 300.0));
|
||||
final BorderDirectional at0 = const BorderDirectional(start: const BorderSide(width: 100.0), end: const BorderSide(width: 200.0));
|
||||
final BorderDirectional at25 = const BorderDirectional(start: const BorderSide(width: 125.0), end: const BorderSide(width: 175.0));
|
||||
final BorderDirectional at75 = const BorderDirectional(start: const BorderSide(width: 175.0), end: const BorderSide(width: 125.0));
|
||||
final BorderDirectional at100 = const BorderDirectional(start: const BorderSide(width: 200.0), end: const BorderSide(width: 100.0));
|
||||
final BorderDirectional at200 = const BorderDirectional(start: const BorderSide(width: 300.0), end: const BorderSide(width: 0.0));
|
||||
|
||||
expect(BorderDirectional.lerp(null, null, -1.0), null);
|
||||
expect(BorderDirectional.lerp(directionalWithTop10, null, -1.0), const BorderDirectional(top: const BorderSide(width: 20.0)));
|
||||
expect(BorderDirectional.lerp(null, directionalWithTop10, -1.0), const BorderDirectional());
|
||||
expect(BorderDirectional.lerp(at0, at100, -1.0), atMinus100);
|
||||
|
||||
expect(BorderDirectional.lerp(null, null, 0.0), null);
|
||||
expect(BorderDirectional.lerp(directionalWithTop10, null, 0.0), const BorderDirectional(top: const BorderSide(width: 10.0)));
|
||||
expect(BorderDirectional.lerp(null, directionalWithTop10, 0.0), const BorderDirectional());
|
||||
expect(BorderDirectional.lerp(at0, at100, 0.0), at0);
|
||||
|
||||
expect(BorderDirectional.lerp(null, null, 0.25), null);
|
||||
expect(BorderDirectional.lerp(directionalWithTop10, null, 0.25), const BorderDirectional(top: const BorderSide(width: 7.5)));
|
||||
expect(BorderDirectional.lerp(null, directionalWithTop10, 0.25), const BorderDirectional(top: const BorderSide(width: 2.5)));
|
||||
expect(BorderDirectional.lerp(at0, at100, 0.25), at25);
|
||||
|
||||
expect(BorderDirectional.lerp(null, null, 0.75), null);
|
||||
expect(BorderDirectional.lerp(directionalWithTop10, null, 0.75), const BorderDirectional(top: const BorderSide(width: 2.5)));
|
||||
expect(BorderDirectional.lerp(null, directionalWithTop10, 0.75), const BorderDirectional(top: const BorderSide(width: 7.5)));
|
||||
expect(BorderDirectional.lerp(at0, at100, 0.75), at75);
|
||||
|
||||
expect(BorderDirectional.lerp(null, null, 1.0), null);
|
||||
expect(BorderDirectional.lerp(directionalWithTop10, null, 1.0), const BorderDirectional());
|
||||
expect(BorderDirectional.lerp(null, directionalWithTop10, 1.0), const BorderDirectional(top: const BorderSide(width: 10.0)));
|
||||
expect(BorderDirectional.lerp(at0, at100, 1.0), at100);
|
||||
|
||||
expect(BorderDirectional.lerp(null, null, 2.0), null);
|
||||
expect(BorderDirectional.lerp(directionalWithTop10, null, 2.0), const BorderDirectional());
|
||||
expect(BorderDirectional.lerp(null, directionalWithTop10, 2.0), const BorderDirectional(top: const BorderSide(width: 20.0)));
|
||||
expect(BorderDirectional.lerp(at0, at100, 2.0), at200);
|
||||
});
|
||||
|
||||
test('BorderDirectional.paint', () {
|
||||
expect(
|
||||
(Canvas canvas) {
|
||||
const BorderDirectional(end: const BorderSide(width: 10.0, color: const Color(0xFF00FF00)))
|
||||
.paint(canvas, new Rect.fromLTRB(10.0, 20.0, 30.0, 40.0), textDirection: TextDirection.rtl);
|
||||
},
|
||||
paints
|
||||
..path(
|
||||
includes: <Offset>[const Offset(15.0, 30.0)],
|
||||
excludes: <Offset>[const Offset(25.0, 30.0)],
|
||||
color: const Color(0xFF00FF00),
|
||||
)
|
||||
);
|
||||
expect(
|
||||
(Canvas canvas) {
|
||||
const BorderDirectional(end: const BorderSide(width: 10.0, color: const Color(0xFF00FF00)))
|
||||
.paint(canvas, new Rect.fromLTRB(10.0, 20.0, 30.0, 40.0), textDirection: TextDirection.ltr);
|
||||
},
|
||||
paints
|
||||
..path(
|
||||
includes: <Offset>[const Offset(25.0, 30.0)],
|
||||
excludes: <Offset>[const Offset(15.0, 30.0)],
|
||||
color: const Color(0xFF00FF00),
|
||||
)
|
||||
);
|
||||
expect(
|
||||
(Canvas canvas) {
|
||||
const BorderDirectional(end: const BorderSide(width: 10.0, color: const Color(0xFF00FF00)))
|
||||
.paint(canvas, new Rect.fromLTRB(10.0, 20.0, 30.0, 40.0));
|
||||
},
|
||||
paintsAssertion // no TextDirection
|
||||
);
|
||||
});
|
||||
|
||||
test('BorderDirectional hashCode', () {
|
||||
final BorderSide side = const BorderSide(width: 2.0);
|
||||
expect(new BorderDirectional(top: side).hashCode, new BorderDirectional(top: side).hashCode);
|
||||
expect(new BorderDirectional(top: side).hashCode, isNot(new BorderDirectional(bottom: side).hashCode));
|
||||
});
|
||||
|
||||
test('BoxDecoration.border takes a BorderDirectional', () {
|
||||
const BoxDecoration decoration2 = const BoxDecoration(
|
||||
border: const BorderDirectional(start: const BorderSide(width: 2.0)),
|
||||
);
|
||||
const BoxDecoration decoration4 = const BoxDecoration(
|
||||
border: const BorderDirectional(start: const BorderSide(width: 4.0)),
|
||||
);
|
||||
const BoxDecoration decoration6 = const BoxDecoration(
|
||||
border: const BorderDirectional(start: const BorderSide(width: 6.0)),
|
||||
);
|
||||
final BoxPainter painter = decoration2.createBoxPainter();
|
||||
expect(
|
||||
(Canvas canvas) {
|
||||
painter.paint(
|
||||
canvas,
|
||||
const Offset(30.0, 0.0),
|
||||
const ImageConfiguration(size: const Size(20.0, 20.0), textDirection: TextDirection.rtl),
|
||||
);
|
||||
},
|
||||
paints
|
||||
..path(
|
||||
includes: <Offset>[const Offset(49.0, 10.0)],
|
||||
excludes: <Offset>[const Offset(31.0, 10.0)],
|
||||
)
|
||||
);
|
||||
expect(
|
||||
(Canvas canvas) {
|
||||
painter.paint(
|
||||
canvas,
|
||||
const Offset(30.0, 0.0),
|
||||
const ImageConfiguration(size: const Size(20.0, 20.0), textDirection: TextDirection.ltr),
|
||||
);
|
||||
},
|
||||
paints
|
||||
..path(
|
||||
includes: <Offset>[const Offset(31.0, 10.0)],
|
||||
excludes: <Offset>[const Offset(49.0, 10.0)],
|
||||
)
|
||||
);
|
||||
expect(decoration2.padding, const EdgeInsetsDirectional.fromSTEB(2.0, 0.0, 0.0, 0.0));
|
||||
expect(decoration2.scale(2.0), decoration4);
|
||||
expect(BoxDecoration.lerp(decoration2, decoration6, 0.5), decoration4);
|
||||
});
|
||||
}
|
@ -128,10 +128,11 @@ void main() {
|
||||
final BorderSide side0 = const BorderSide(width: 0.0);
|
||||
final BorderSide side1 = const BorderSide(width: 1.0);
|
||||
final BorderSide side2 = const BorderSide(width: 2.0);
|
||||
expect(BorderSide.lerp(side2, side1, 10.0), side0);
|
||||
expect(BorderSide.lerp(side1, side2, -10.0), side0);
|
||||
expect(BorderSide.lerp(side2, side1, 10.0), BorderSide.none);
|
||||
expect(BorderSide.lerp(side1, side2, -10.0), BorderSide.none);
|
||||
expect(BorderSide.lerp(side0, side1, 2.0), side2);
|
||||
expect(BorderSide.lerp(side1, side0, 2.0), side0);
|
||||
expect(BorderSide.lerp(side1, side0, 2.0), BorderSide.none);
|
||||
expect(BorderSide.lerp(side2, side1, 2.0), side0);
|
||||
});
|
||||
test('BorderSide - toString', () {
|
||||
expect(
|
||||
|
@ -6,6 +6,14 @@ import 'package:flutter/painting.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
test('Border constructor', () {
|
||||
final Null $null = null;
|
||||
expect(() => new Border(left: $null), throwsAssertionError);
|
||||
expect(() => new Border(top: $null), throwsAssertionError);
|
||||
expect(() => new Border(right: $null), throwsAssertionError);
|
||||
expect(() => new Border(bottom: $null), throwsAssertionError);
|
||||
});
|
||||
|
||||
test('Border.merge', () {
|
||||
final BorderSide magenta3 = const BorderSide(color: const Color(0xFFFF00FF), width: 3.0);
|
||||
final BorderSide magenta6 = const BorderSide(color: const Color(0xFFFF00FF), width: 6.0);
|
||||
@ -94,4 +102,135 @@ void main() {
|
||||
final Border bY2 = new Border(top: yellow2);
|
||||
expect(bY2.scale(0.0), bY0);
|
||||
});
|
||||
|
||||
test('Border.dimensions', () {
|
||||
expect(
|
||||
const Border(
|
||||
left: const BorderSide(width: 2.0),
|
||||
top: const BorderSide(width: 3.0),
|
||||
bottom: const BorderSide(width: 5.0),
|
||||
right: const BorderSide(width: 7.0),
|
||||
).dimensions,
|
||||
const EdgeInsets.fromLTRB(2.0, 3.0, 7.0, 5.0),
|
||||
);
|
||||
});
|
||||
|
||||
test('Border.isUniform', () {
|
||||
expect(
|
||||
const Border(
|
||||
left: const BorderSide(width: 3.0),
|
||||
top: const BorderSide(width: 3.0),
|
||||
right: const BorderSide(width: 3.0),
|
||||
bottom: const BorderSide(width: 3.1),
|
||||
).isUniform,
|
||||
false,
|
||||
);
|
||||
expect(
|
||||
const Border(
|
||||
left: const BorderSide(width: 3.0),
|
||||
top: const BorderSide(width: 3.0),
|
||||
right: const BorderSide(width: 3.0),
|
||||
bottom: const BorderSide(width: 3.0),
|
||||
).isUniform,
|
||||
true,
|
||||
);
|
||||
expect(
|
||||
const Border(
|
||||
left: const BorderSide(color: const Color(0xFFFFFFFE)),
|
||||
top: const BorderSide(color: const Color(0xFFFFFFFF)),
|
||||
right: const BorderSide(color: const Color(0xFFFFFFFF)),
|
||||
bottom: const BorderSide(color: const Color(0xFFFFFFFF)),
|
||||
).isUniform,
|
||||
false,
|
||||
);
|
||||
expect(
|
||||
const Border(
|
||||
left: const BorderSide(color: const Color(0xFFFFFFFF)),
|
||||
top: const BorderSide(color: const Color(0xFFFFFFFF)),
|
||||
right: const BorderSide(color: const Color(0xFFFFFFFF)),
|
||||
bottom: const BorderSide(color: const Color(0xFFFFFFFF)),
|
||||
).isUniform,
|
||||
true,
|
||||
);
|
||||
expect(
|
||||
const Border(
|
||||
left: const BorderSide(style: BorderStyle.none),
|
||||
top: const BorderSide(style: BorderStyle.none),
|
||||
right: const BorderSide(style: BorderStyle.none),
|
||||
bottom: const BorderSide(style: BorderStyle.solid, width: 0.0),
|
||||
).isUniform,
|
||||
false,
|
||||
);
|
||||
expect(
|
||||
const Border(
|
||||
left: const BorderSide(style: BorderStyle.none),
|
||||
top: const BorderSide(style: BorderStyle.none),
|
||||
right: const BorderSide(style: BorderStyle.none),
|
||||
bottom: const BorderSide(style: BorderStyle.solid, width: 0.0),
|
||||
).isUniform,
|
||||
false,
|
||||
);
|
||||
expect(
|
||||
const Border(
|
||||
left: const BorderSide(style: BorderStyle.none),
|
||||
top: const BorderSide(style: BorderStyle.none),
|
||||
right: const BorderSide(style: BorderStyle.none),
|
||||
bottom: BorderSide.none,
|
||||
).isUniform,
|
||||
false,
|
||||
);
|
||||
expect(
|
||||
const Border(
|
||||
left: const BorderSide(style: BorderStyle.none, width: 0.0),
|
||||
top: const BorderSide(style: BorderStyle.none, width: 0.0),
|
||||
right: const BorderSide(style: BorderStyle.none, width: 0.0),
|
||||
bottom: BorderSide.none,
|
||||
).isUniform,
|
||||
true,
|
||||
);
|
||||
expect(
|
||||
const Border().isUniform,
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
test('Border.lerp', () {
|
||||
final Border visualWithTop10 = const Border(top: const BorderSide(width: 10.0));
|
||||
final Border atMinus100 = const Border(left: const BorderSide(width: 0.0), right: const BorderSide(width: 300.0));
|
||||
final Border at0 = const Border(left: const BorderSide(width: 100.0), right: const BorderSide(width: 200.0));
|
||||
final Border at25 = const Border(left: const BorderSide(width: 125.0), right: const BorderSide(width: 175.0));
|
||||
final Border at75 = const Border(left: const BorderSide(width: 175.0), right: const BorderSide(width: 125.0));
|
||||
final Border at100 = const Border(left: const BorderSide(width: 200.0), right: const BorderSide(width: 100.0));
|
||||
final Border at200 = const Border(left: const BorderSide(width: 300.0), right: const BorderSide(width: 0.0));
|
||||
|
||||
expect(Border.lerp(null, null, -1.0), null);
|
||||
expect(Border.lerp(visualWithTop10, null, -1.0), const Border(top: const BorderSide(width: 20.0)));
|
||||
expect(Border.lerp(null, visualWithTop10, -1.0), const Border());
|
||||
expect(Border.lerp(at0, at100, -1.0), atMinus100);
|
||||
|
||||
expect(Border.lerp(null, null, 0.0), null);
|
||||
expect(Border.lerp(visualWithTop10, null, 0.0), const Border(top: const BorderSide(width: 10.0)));
|
||||
expect(Border.lerp(null, visualWithTop10, 0.0), const Border());
|
||||
expect(Border.lerp(at0, at100, 0.0), at0);
|
||||
|
||||
expect(Border.lerp(null, null, 0.25), null);
|
||||
expect(Border.lerp(visualWithTop10, null, 0.25), const Border(top: const BorderSide(width: 7.5)));
|
||||
expect(Border.lerp(null, visualWithTop10, 0.25), const Border(top: const BorderSide(width: 2.5)));
|
||||
expect(Border.lerp(at0, at100, 0.25), at25);
|
||||
|
||||
expect(Border.lerp(null, null, 0.75), null);
|
||||
expect(Border.lerp(visualWithTop10, null, 0.75), const Border(top: const BorderSide(width: 2.5)));
|
||||
expect(Border.lerp(null, visualWithTop10, 0.75), const Border(top: const BorderSide(width: 7.5)));
|
||||
expect(Border.lerp(at0, at100, 0.75), at75);
|
||||
|
||||
expect(Border.lerp(null, null, 1.0), null);
|
||||
expect(Border.lerp(visualWithTop10, null, 1.0), const Border());
|
||||
expect(Border.lerp(null, visualWithTop10, 1.0), const Border(top: const BorderSide(width: 10.0)));
|
||||
expect(Border.lerp(at0, at100, 1.0), at100);
|
||||
|
||||
expect(Border.lerp(null, null, 2.0), null);
|
||||
expect(Border.lerp(visualWithTop10, null, 2.0), const Border());
|
||||
expect(Border.lerp(null, visualWithTop10, 2.0), const Border(top: const BorderSide(width: 20.0)));
|
||||
expect(Border.lerp(at0, at100, 2.0), at200);
|
||||
});
|
||||
}
|
@ -70,4 +70,69 @@ void main() {
|
||||
..rect(rect: rect.deflate(2.5), color: b1.top.color)
|
||||
);
|
||||
});
|
||||
|
||||
test('Compound borders', () {
|
||||
final BorderSide side1 = const BorderSide(color: const Color(0xFF00FF00));
|
||||
final BorderSide side2 = const BorderSide(color: const Color(0xFF0000FF));
|
||||
final BorderDirectional b1 = new BorderDirectional(top: side1, start: side1, end: side1, bottom: side1);
|
||||
final BorderDirectional b2 = new BorderDirectional(top: side2, start: side2, end: side2, bottom: side2);
|
||||
expect(
|
||||
(b1 + b2).toString(),
|
||||
'BorderDirectional(top: BorderSide(Color(0xff00ff00), 1.0, BorderStyle.solid), start: BorderSide(Color(0xff00ff00), 1.0, BorderStyle.solid), end: BorderSide(Color(0xff00ff00), 1.0, BorderStyle.solid), bottom: BorderSide(Color(0xff00ff00), 1.0, BorderStyle.solid)) + '
|
||||
'BorderDirectional(top: BorderSide(Color(0xff0000ff), 1.0, BorderStyle.solid), start: BorderSide(Color(0xff0000ff), 1.0, BorderStyle.solid), end: BorderSide(Color(0xff0000ff), 1.0, BorderStyle.solid), bottom: BorderSide(Color(0xff0000ff), 1.0, BorderStyle.solid))',
|
||||
);
|
||||
expect(
|
||||
(b1 + (b2 + b2)).toString(),
|
||||
'BorderDirectional(top: BorderSide(Color(0xff00ff00), 1.0, BorderStyle.solid), start: BorderSide(Color(0xff00ff00), 1.0, BorderStyle.solid), end: BorderSide(Color(0xff00ff00), 1.0, BorderStyle.solid), bottom: BorderSide(Color(0xff00ff00), 1.0, BorderStyle.solid)) + '
|
||||
'BorderDirectional(top: BorderSide(Color(0xff0000ff), 2.0, BorderStyle.solid), start: BorderSide(Color(0xff0000ff), 2.0, BorderStyle.solid), end: BorderSide(Color(0xff0000ff), 2.0, BorderStyle.solid), bottom: BorderSide(Color(0xff0000ff), 2.0, BorderStyle.solid))',
|
||||
);
|
||||
expect(
|
||||
((b1 + b2) + b2).toString(),
|
||||
'BorderDirectional(top: BorderSide(Color(0xff00ff00), 1.0, BorderStyle.solid), start: BorderSide(Color(0xff00ff00), 1.0, BorderStyle.solid), end: BorderSide(Color(0xff00ff00), 1.0, BorderStyle.solid), bottom: BorderSide(Color(0xff00ff00), 1.0, BorderStyle.solid)) + '
|
||||
'BorderDirectional(top: BorderSide(Color(0xff0000ff), 2.0, BorderStyle.solid), start: BorderSide(Color(0xff0000ff), 2.0, BorderStyle.solid), end: BorderSide(Color(0xff0000ff), 2.0, BorderStyle.solid), bottom: BorderSide(Color(0xff0000ff), 2.0, BorderStyle.solid))',
|
||||
);
|
||||
expect((b1 + b2) + b2, b1 + (b2 + b2));
|
||||
expect(
|
||||
(b1 + b2).scale(3.0).toString(),
|
||||
'BorderDirectional(top: BorderSide(Color(0xff00ff00), 3.0, BorderStyle.solid), start: BorderSide(Color(0xff00ff00), 3.0, BorderStyle.solid), end: BorderSide(Color(0xff00ff00), 3.0, BorderStyle.solid), bottom: BorderSide(Color(0xff00ff00), 3.0, BorderStyle.solid)) + '
|
||||
'BorderDirectional(top: BorderSide(Color(0xff0000ff), 3.0, BorderStyle.solid), start: BorderSide(Color(0xff0000ff), 3.0, BorderStyle.solid), end: BorderSide(Color(0xff0000ff), 3.0, BorderStyle.solid), bottom: BorderSide(Color(0xff0000ff), 3.0, BorderStyle.solid))',
|
||||
);
|
||||
expect(
|
||||
(b1 + b2).scale(0.0).toString(),
|
||||
'BorderDirectional(top: BorderSide(Color(0xff00ff00), 0.0, BorderStyle.none), start: BorderSide(Color(0xff00ff00), 0.0, BorderStyle.none), end: BorderSide(Color(0xff00ff00), 0.0, BorderStyle.none), bottom: BorderSide(Color(0xff00ff00), 0.0, BorderStyle.none)) + '
|
||||
'BorderDirectional(top: BorderSide(Color(0xff0000ff), 0.0, BorderStyle.none), start: BorderSide(Color(0xff0000ff), 0.0, BorderStyle.none), end: BorderSide(Color(0xff0000ff), 0.0, BorderStyle.none), bottom: BorderSide(Color(0xff0000ff), 0.0, BorderStyle.none))',
|
||||
);
|
||||
expect(
|
||||
ShapeBorder.lerp(b2 + b1, b1 + b2, 0.0).toString(),
|
||||
'BorderDirectional(top: BorderSide(Color(0xff0000ff), 1.0, BorderStyle.solid), start: BorderSide(Color(0xff0000ff), 1.0, BorderStyle.solid), end: BorderSide(Color(0xff0000ff), 1.0, BorderStyle.solid), bottom: BorderSide(Color(0xff0000ff), 1.0, BorderStyle.solid)) + '
|
||||
'BorderDirectional(top: BorderSide(Color(0xff00ff00), 1.0, BorderStyle.solid), start: BorderSide(Color(0xff00ff00), 1.0, BorderStyle.solid), end: BorderSide(Color(0xff00ff00), 1.0, BorderStyle.solid), bottom: BorderSide(Color(0xff00ff00), 1.0, BorderStyle.solid))',
|
||||
);
|
||||
expect(
|
||||
ShapeBorder.lerp(b2 + b1, b1 + b2, 0.25).toString(),
|
||||
'BorderDirectional(top: BorderSide(Color(0xff003fbf), 1.0, BorderStyle.solid), start: BorderSide(Color(0xff003fbf), 1.0, BorderStyle.solid), end: BorderSide(Color(0xff003fbf), 1.0, BorderStyle.solid), bottom: BorderSide(Color(0xff003fbf), 1.0, BorderStyle.solid)) + '
|
||||
'BorderDirectional(top: BorderSide(Color(0xff00bf3f), 1.0, BorderStyle.solid), start: BorderSide(Color(0xff00bf3f), 1.0, BorderStyle.solid), end: BorderSide(Color(0xff00bf3f), 1.0, BorderStyle.solid), bottom: BorderSide(Color(0xff00bf3f), 1.0, BorderStyle.solid))',
|
||||
);
|
||||
expect(
|
||||
ShapeBorder.lerp(b2 + b1, b1 + b2, 0.5).toString(),
|
||||
'BorderDirectional(top: BorderSide(Color(0xff007f7f), 1.0, BorderStyle.solid), start: BorderSide(Color(0xff007f7f), 1.0, BorderStyle.solid), end: BorderSide(Color(0xff007f7f), 1.0, BorderStyle.solid), bottom: BorderSide(Color(0xff007f7f), 1.0, BorderStyle.solid)) + '
|
||||
'BorderDirectional(top: BorderSide(Color(0xff007f7f), 1.0, BorderStyle.solid), start: BorderSide(Color(0xff007f7f), 1.0, BorderStyle.solid), end: BorderSide(Color(0xff007f7f), 1.0, BorderStyle.solid), bottom: BorderSide(Color(0xff007f7f), 1.0, BorderStyle.solid))',
|
||||
);
|
||||
expect(
|
||||
ShapeBorder.lerp(b2 + b1, b1 + b2, 1.0).toString(),
|
||||
'BorderDirectional(top: BorderSide(Color(0xff00ff00), 1.0, BorderStyle.solid), start: BorderSide(Color(0xff00ff00), 1.0, BorderStyle.solid), end: BorderSide(Color(0xff00ff00), 1.0, BorderStyle.solid), bottom: BorderSide(Color(0xff00ff00), 1.0, BorderStyle.solid)) + '
|
||||
'BorderDirectional(top: BorderSide(Color(0xff0000ff), 1.0, BorderStyle.solid), start: BorderSide(Color(0xff0000ff), 1.0, BorderStyle.solid), end: BorderSide(Color(0xff0000ff), 1.0, BorderStyle.solid), bottom: BorderSide(Color(0xff0000ff), 1.0, BorderStyle.solid))'
|
||||
);
|
||||
expect((b1 + b2).dimensions, const EdgeInsetsDirectional.fromSTEB(2.0, 2.0, 2.0, 2.0));
|
||||
final Rect rect = new Rect.fromLTRB(11.0, 15.0, 299.0, 175.0);
|
||||
expect((Canvas canvas) => (b1 + b2).paint(canvas, rect, textDirection: TextDirection.rtl), paints
|
||||
..rect(rect: rect.deflate(0.5), color: b2.top.color)
|
||||
..rect(rect: rect.deflate(1.5), color: b1.top.color)
|
||||
);
|
||||
expect((b1 + b2 + b1).dimensions, const EdgeInsetsDirectional.fromSTEB(3.0, 3.0, 3.0, 3.0));
|
||||
expect((Canvas canvas) => (b1 + b2 + b1).paint(canvas, rect, textDirection: TextDirection.rtl), paints
|
||||
..rect(rect: rect.deflate(0.5), color: b1.top.color)
|
||||
..rect(rect: rect.deflate(1.5), color: b2.top.color)
|
||||
..rect(rect: rect.deflate(2.5), color: b1.top.color)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
@ -38,11 +38,16 @@ import 'recording_canvas.dart';
|
||||
/// See [PaintPattern] for a discussion of the semantics of paint patterns.
|
||||
///
|
||||
/// To match something which paints nothing, see [paintsNothing].
|
||||
///
|
||||
/// To match something which asserts instead of painting, see [paintsAssertion].
|
||||
PaintPattern get paints => new _TestRecordingCanvasPatternMatcher();
|
||||
|
||||
/// Matches objects or functions that paint an empty display list.
|
||||
Matcher get paintsNothing => new _TestRecordingCanvasPaintsNothingMatcher();
|
||||
|
||||
/// Matches objects or functions that assert when they try to paint.
|
||||
Matcher get paintsAssertion => new _TestRecordingCanvasPaintsAssertionMatcher();
|
||||
|
||||
/// Signature for [PaintPattern.something] predicate argument.
|
||||
///
|
||||
/// Used by the [paints] matcher.
|
||||
@ -218,8 +223,10 @@ abstract class PaintPattern {
|
||||
/// are compared to the actual [Canvas.drawPath] call's `paint` argument, and
|
||||
/// any mismatches result in failure.
|
||||
///
|
||||
/// There is currently no way to check the actual path itself.
|
||||
// See https://github.com/flutter/flutter/issues/93 which tracks that issue.
|
||||
/// To introspect the Path object (as it stands after the painting has
|
||||
/// completed), the `includes` and `excludes` arguments can be provided to
|
||||
/// specify points that should be considered inside or outside the path
|
||||
/// (respectively).
|
||||
///
|
||||
/// If no call to [Canvas.drawPath] was made, then this results in failure.
|
||||
///
|
||||
@ -231,7 +238,7 @@ abstract class PaintPattern {
|
||||
/// painting has completed, not at the time of the call. If the same [Paint]
|
||||
/// object is reused multiple times, then this may not match the actual
|
||||
/// arguments as they were seen by the method.
|
||||
void path({ Color color, double strokeWidth, bool hasMaskFilter, PaintingStyle style });
|
||||
void path({ Iterable<Offset> includes, Iterable<Offset> excludes, Color color, double strokeWidth, bool hasMaskFilter, PaintingStyle style });
|
||||
|
||||
/// Indicates that a line is expected next.
|
||||
///
|
||||
@ -327,6 +334,29 @@ class _MismatchedCall {
|
||||
final RecordedInvocation call;
|
||||
}
|
||||
|
||||
bool _evaluatePainter(Object object, Canvas canvas, PaintingContext context) {
|
||||
if (object is _ContextPainterFunction) {
|
||||
final _ContextPainterFunction function = object;
|
||||
function(context, Offset.zero);
|
||||
} else if (object is _CanvasPainterFunction) {
|
||||
final _CanvasPainterFunction function = object;
|
||||
function(canvas);
|
||||
} else {
|
||||
if (object is Finder) {
|
||||
TestAsyncUtils.guardSync();
|
||||
final Finder finder = object;
|
||||
object = finder.evaluate().single.renderObject;
|
||||
}
|
||||
if (object is RenderObject) {
|
||||
final RenderObject renderObject = object;
|
||||
renderObject.paint(context, Offset.zero);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
abstract class _TestRecordingCanvasMatcher extends Matcher {
|
||||
@override
|
||||
bool matches(Object object, Map<dynamic, dynamic> matchState) {
|
||||
@ -336,25 +366,9 @@ abstract class _TestRecordingCanvasMatcher extends Matcher {
|
||||
String prefixMessage = 'unexpectedly failed.';
|
||||
bool result = false;
|
||||
try {
|
||||
if (object is _ContextPainterFunction) {
|
||||
final _ContextPainterFunction function = object;
|
||||
function(context, Offset.zero);
|
||||
} else if (object is _CanvasPainterFunction) {
|
||||
final _CanvasPainterFunction function = object;
|
||||
function(canvas);
|
||||
} else {
|
||||
if (object is Finder) {
|
||||
TestAsyncUtils.guardSync();
|
||||
final Finder finder = object;
|
||||
object = finder.evaluate().single.renderObject;
|
||||
}
|
||||
if (object is RenderObject) {
|
||||
final RenderObject renderObject = object;
|
||||
renderObject.paint(context, Offset.zero);
|
||||
} else {
|
||||
matchState[this] = 'was not one of the supported objects for the "paints" matcher.';
|
||||
return false;
|
||||
}
|
||||
if (!_evaluatePainter(object, canvas, context)) {
|
||||
matchState[this] = 'was not one of the supported objects for the "paints" matcher.';
|
||||
return false;
|
||||
}
|
||||
result = _evaluatePredicates(canvas.invocations, description);
|
||||
if (!result)
|
||||
@ -407,6 +421,55 @@ class _TestRecordingCanvasPaintsNothingMatcher extends _TestRecordingCanvasMatch
|
||||
}
|
||||
}
|
||||
|
||||
class _TestRecordingCanvasPaintsAssertionMatcher extends Matcher {
|
||||
@override
|
||||
bool matches(Object object, Map<dynamic, dynamic> matchState) {
|
||||
final TestRecordingCanvas canvas = new TestRecordingCanvas();
|
||||
final TestRecordingPaintingContext context = new TestRecordingPaintingContext(canvas);
|
||||
final StringBuffer description = new StringBuffer();
|
||||
String prefixMessage = 'unexpectedly failed.';
|
||||
bool result = false;
|
||||
try {
|
||||
if (!_evaluatePainter(object, canvas, context)) {
|
||||
matchState[this] = 'was not one of the supported objects for the "paints" matcher.';
|
||||
return false;
|
||||
}
|
||||
prefixMessage = 'did not assert.';
|
||||
} on AssertionError {
|
||||
result = true;
|
||||
} catch (error, stack) {
|
||||
prefixMessage = 'threw the following exception:';
|
||||
description.writeln(error.toString());
|
||||
description.write(stack.toString());
|
||||
result = false;
|
||||
}
|
||||
if (!result) {
|
||||
if (canvas.invocations.isNotEmpty) {
|
||||
description.write('The complete display list was:');
|
||||
for (RecordedInvocation call in canvas.invocations)
|
||||
description.write('\n * $call');
|
||||
}
|
||||
matchState[this] = '$prefixMessage\n$description';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@override
|
||||
Description describe(Description description) {
|
||||
return description.add('An object or closure that asserts when it tries to paint.');
|
||||
}
|
||||
|
||||
@override
|
||||
Description describeMismatch(
|
||||
dynamic item,
|
||||
Description description,
|
||||
Map<dynamic, dynamic> matchState,
|
||||
bool verbose,
|
||||
) {
|
||||
return description.add(matchState[this]);
|
||||
}
|
||||
}
|
||||
|
||||
class _TestRecordingCanvasPatternMatcher extends _TestRecordingCanvasMatcher implements PaintPattern {
|
||||
final List<_PaintPredicate> _predicates = <_PaintPredicate>[];
|
||||
|
||||
@ -471,8 +534,8 @@ class _TestRecordingCanvasPatternMatcher extends _TestRecordingCanvasMatcher imp
|
||||
}
|
||||
|
||||
@override
|
||||
void path({ Color color, double strokeWidth, bool hasMaskFilter, PaintingStyle style }) {
|
||||
_predicates.add(new _PathPaintPredicate(color: color, strokeWidth: strokeWidth, hasMaskFilter: hasMaskFilter, style: style));
|
||||
void path({ Iterable<Offset> includes, Iterable<Offset> excludes, Color color, double strokeWidth, bool hasMaskFilter, PaintingStyle style }) {
|
||||
_predicates.add(new _PathPaintPredicate(includes: includes, excludes: excludes, color: color, strokeWidth: strokeWidth, hasMaskFilter: hasMaskFilter, style: style));
|
||||
}
|
||||
|
||||
@override
|
||||
@ -805,9 +868,42 @@ class _CirclePaintPredicate extends _DrawCommandPaintPredicate {
|
||||
}
|
||||
|
||||
class _PathPaintPredicate extends _DrawCommandPaintPredicate {
|
||||
_PathPaintPredicate({ Color color, double strokeWidth, bool hasMaskFilter, PaintingStyle style }) : super(
|
||||
_PathPaintPredicate({ this.includes, this.excludes, Color color, double strokeWidth, bool hasMaskFilter, PaintingStyle style }) : super(
|
||||
#drawPath, 'a path', 2, 1, color: color, strokeWidth: strokeWidth, hasMaskFilter: hasMaskFilter, style: style
|
||||
);
|
||||
|
||||
final Iterable<Offset> includes;
|
||||
final Iterable<Offset> excludes;
|
||||
|
||||
@override
|
||||
void verifyArguments(List<dynamic> arguments) {
|
||||
super.verifyArguments(arguments);
|
||||
final Path pathArgument = arguments[0];
|
||||
if (includes != null) {
|
||||
for (Offset offset in includes) {
|
||||
if (!pathArgument.contains(offset))
|
||||
throw 'It called $methodName with a path that unexpectedly did not contain $offset.';
|
||||
}
|
||||
}
|
||||
if (excludes != null) {
|
||||
for (Offset offset in excludes) {
|
||||
if (pathArgument.contains(offset))
|
||||
throw 'It called $methodName with a path that unexpectedly contained $offset.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void debugFillDescription(List<String> description) {
|
||||
super.debugFillDescription(description);
|
||||
if (includes != null && excludes != null) {
|
||||
description.add('that contains $includes and does not contain $excludes');
|
||||
} else if (includes != null) {
|
||||
description.add('that contains $includes');
|
||||
} else if (excludes != null) {
|
||||
description.add('that does not contain $excludes');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(ianh): add arguments to test the points, length, angle, that kind of thing
|
||||
|
@ -77,9 +77,11 @@ void main() {
|
||||
actualDecoration = actualBox.decoration;
|
||||
|
||||
expect(actualDecoration.color, const Color(0xFF7F7F7F));
|
||||
expect(actualDecoration.border.left.width, 2.5);
|
||||
expect(actualDecoration.border.left.style, BorderStyle.solid);
|
||||
expect(actualDecoration.border.left.color, const Color(0xFF101010));
|
||||
expect(actualDecoration.border, const isInstanceOf<Border>());
|
||||
final Border border = actualDecoration.border;
|
||||
expect(border.left.width, 2.5);
|
||||
expect(border.left.style, BorderStyle.solid);
|
||||
expect(border.left.color, const Color(0xFF101010));
|
||||
expect(actualDecoration.borderRadius, new BorderRadius.circular(5.0));
|
||||
expect(actualDecoration.shape, BoxShape.rectangle);
|
||||
expect(actualDecoration.boxShadow[0].blurRadius, 5.0);
|
||||
@ -131,9 +133,11 @@ void main() {
|
||||
// Same as the test above but the values should be much closer to the
|
||||
// tween's end values given the easeOut curve.
|
||||
expect(actualDecoration.color, const Color(0xFF505050));
|
||||
expect(actualDecoration.border.left.width, closeTo(1.9, 0.1));
|
||||
expect(actualDecoration.border.left.style, BorderStyle.solid);
|
||||
expect(actualDecoration.border.left.color, const Color(0xFF151515));
|
||||
expect(actualDecoration.border, const isInstanceOf<Border>());
|
||||
final Border border = actualDecoration.border;
|
||||
expect(border.left.width, closeTo(1.9, 0.1));
|
||||
expect(border.left.style, BorderStyle.solid);
|
||||
expect(border.left.color, const Color(0xFF151515));
|
||||
expect(actualDecoration.borderRadius.topLeft.x, closeTo(6.8, 0.1));
|
||||
expect(actualDecoration.shape, BoxShape.rectangle);
|
||||
expect(actualDecoration.boxShadow[0].blurRadius, closeTo(3.1, 0.1));
|
||||
|
Loading…
x
Reference in New Issue
Block a user