Commit cd3715a8 authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

Border RTL (#12407)

parent cc3f5767
......@@ -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;
......
// 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);
});
}
\ No newline at end of file
......@@ -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);
});
}
\ No newline at end of file
......@@ -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,15 +334,7 @@ class _MismatchedCall {
final RecordedInvocation call;
}
abstract class _TestRecordingCanvasMatcher 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 {
bool _evaluatePainter(Object object, Canvas canvas, PaintingContext context) {
if (object is _ContextPainterFunction) {
final _ContextPainterFunction function = object;
function(context, Offset.zero);
......@@ -352,10 +351,25 @@ abstract class _TestRecordingCanvasMatcher extends Matcher {
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;
}
}
return true;
}
abstract class _TestRecordingCanvasMatcher 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;
}
result = _evaluatePredicates(canvas.invocations, description);
if (!result)
prefixMessage = 'did not match the pattern.';
......@@ -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));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment