Add OvalBorder and BoxShape.oval (#103833)
This commit is contained in:
parent
c8c2e3d399
commit
22cb06b57f
@ -49,6 +49,7 @@ export 'src/painting/image_stream.dart';
|
|||||||
export 'src/painting/inline_span.dart';
|
export 'src/painting/inline_span.dart';
|
||||||
export 'src/painting/matrix_utils.dart';
|
export 'src/painting/matrix_utils.dart';
|
||||||
export 'src/painting/notched_shapes.dart';
|
export 'src/painting/notched_shapes.dart';
|
||||||
|
export 'src/painting/oval_border.dart';
|
||||||
export 'src/painting/paint_utilities.dart';
|
export 'src/painting/paint_utilities.dart';
|
||||||
export 'src/painting/placeholder_span.dart';
|
export 'src/painting/placeholder_span.dart';
|
||||||
export 'src/painting/rounded_rectangle_border.dart';
|
export 'src/painting/rounded_rectangle_border.dart';
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
import 'dart:math' as math;
|
import 'dart:ui' as ui show lerpDouble;
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
@ -18,16 +18,32 @@ import 'edge_insets.dart';
|
|||||||
/// When applied to a rectangular space, the border paints in the center of the
|
/// When applied to a rectangular space, the border paints in the center of the
|
||||||
/// rectangle.
|
/// rectangle.
|
||||||
///
|
///
|
||||||
|
/// The [eccentricity] parameter describes how much a circle will deform to
|
||||||
|
/// fit the rectangle it is a border for. A value of zero implies no
|
||||||
|
/// deformation (a circle touching at least two sides of the rectangle), a
|
||||||
|
/// value of one implies full deformation (an oval touching all sides of the
|
||||||
|
/// rectangle).
|
||||||
|
///
|
||||||
/// See also:
|
/// See also:
|
||||||
///
|
///
|
||||||
|
/// * [OvalBorder], which draws a Circle touching all the edges of the box.
|
||||||
/// * [BorderSide], which is used to describe each side of the box.
|
/// * [BorderSide], which is used to describe each side of the box.
|
||||||
/// * [Border], which, when used with [BoxDecoration], can also
|
/// * [Border], which, when used with [BoxDecoration], can also describe a circle.
|
||||||
/// describe a circle.
|
|
||||||
class CircleBorder extends OutlinedBorder {
|
class CircleBorder extends OutlinedBorder {
|
||||||
/// Create a circle border.
|
/// Create a circle border.
|
||||||
///
|
///
|
||||||
/// The [side] argument must not be null.
|
/// The [side] argument must not be null.
|
||||||
const CircleBorder({ super.side }) : assert(side != null);
|
const CircleBorder({ super.side, this.eccentricity = 0.0 })
|
||||||
|
: assert(side != null),
|
||||||
|
assert(eccentricity != null),
|
||||||
|
assert(eccentricity >= 0.0, 'The eccentricity argument $eccentricity is not greater than or equal to zero.'),
|
||||||
|
assert(eccentricity <= 1.0, 'The eccentricity argument $eccentricity is not less than or equal to one.');
|
||||||
|
|
||||||
|
/// Defines the ratio (0.0-1.0) from which the border will deform
|
||||||
|
/// to fit a rectangle.
|
||||||
|
/// When 0.0, it draws a circle touching at least two sides of the rectangle.
|
||||||
|
/// When 1.0, it draws an oval touching all sides of the rectangle.
|
||||||
|
final double eccentricity;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
EdgeInsetsGeometry get dimensions {
|
EdgeInsetsGeometry get dimensions {
|
||||||
@ -42,12 +58,15 @@ class CircleBorder extends OutlinedBorder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ShapeBorder scale(double t) => CircleBorder(side: side.scale(t));
|
ShapeBorder scale(double t) => CircleBorder(side: side.scale(t), eccentricity: eccentricity);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ShapeBorder? lerpFrom(ShapeBorder? a, double t) {
|
ShapeBorder? lerpFrom(ShapeBorder? a, double t) {
|
||||||
if (a is CircleBorder) {
|
if (a is CircleBorder) {
|
||||||
return CircleBorder(side: BorderSide.lerp(a.side, side, t));
|
return CircleBorder(
|
||||||
|
side: BorderSide.lerp(a.side, side, t),
|
||||||
|
eccentricity: clampDouble(ui.lerpDouble(a.eccentricity, eccentricity, t)!, 0.0, 1.0),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return super.lerpFrom(a, t);
|
return super.lerpFrom(a, t);
|
||||||
}
|
}
|
||||||
@ -55,45 +74,40 @@ class CircleBorder extends OutlinedBorder {
|
|||||||
@override
|
@override
|
||||||
ShapeBorder? lerpTo(ShapeBorder? b, double t) {
|
ShapeBorder? lerpTo(ShapeBorder? b, double t) {
|
||||||
if (b is CircleBorder) {
|
if (b is CircleBorder) {
|
||||||
return CircleBorder(side: BorderSide.lerp(side, b.side, t));
|
return CircleBorder(
|
||||||
|
side: BorderSide.lerp(side, b.side, t),
|
||||||
|
eccentricity: clampDouble(ui.lerpDouble(eccentricity, b.eccentricity, t)!, 0.0, 1.0),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return super.lerpTo(b, t);
|
return super.lerpTo(b, t);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Path getInnerPath(Rect rect, { TextDirection? textDirection }) {
|
Path getInnerPath(Rect rect, { TextDirection? textDirection }) {
|
||||||
final double radius = rect.shortestSide / 2.0;
|
final double delta;
|
||||||
final double adjustedRadius;
|
|
||||||
switch (side.strokeAlign) {
|
switch (side.strokeAlign) {
|
||||||
case StrokeAlign.inside:
|
case StrokeAlign.inside:
|
||||||
adjustedRadius = radius - side.width;
|
delta = side.width;
|
||||||
break;
|
break;
|
||||||
case StrokeAlign.center:
|
case StrokeAlign.center:
|
||||||
adjustedRadius = radius - side.width / 2.0;
|
delta = side.width / 2.0;
|
||||||
break;
|
break;
|
||||||
case StrokeAlign.outside:
|
case StrokeAlign.outside:
|
||||||
adjustedRadius = radius;
|
delta = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return Path()
|
final Rect adjustedRect = _adjustRect(rect).deflate(delta);
|
||||||
..addOval(Rect.fromCircle(
|
return Path()..addOval(adjustedRect);
|
||||||
center: rect.center,
|
|
||||||
radius: math.max(0.0, adjustedRadius),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Path getOuterPath(Rect rect, { TextDirection? textDirection }) {
|
Path getOuterPath(Rect rect, { TextDirection? textDirection }) {
|
||||||
return Path()
|
return Path()..addOval(_adjustRect(rect));
|
||||||
..addOval(Rect.fromCircle(
|
|
||||||
center: rect.center,
|
|
||||||
radius: rect.shortestSide / 2.0,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
CircleBorder copyWith({ BorderSide? side }) {
|
CircleBorder copyWith({ BorderSide? side, double? eccentricity }) {
|
||||||
return CircleBorder(side: side ?? this.side);
|
return CircleBorder(side: side ?? this.side, eccentricity: eccentricity ?? this.eccentricity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -102,6 +116,22 @@ class CircleBorder extends OutlinedBorder {
|
|||||||
case BorderStyle.none:
|
case BorderStyle.none:
|
||||||
break;
|
break;
|
||||||
case BorderStyle.solid:
|
case BorderStyle.solid:
|
||||||
|
if (eccentricity != 0.0) {
|
||||||
|
final Rect borderRect = _adjustRect(rect);
|
||||||
|
final Rect adjustedRect;
|
||||||
|
switch (side.strokeAlign) {
|
||||||
|
case StrokeAlign.inside:
|
||||||
|
adjustedRect = borderRect.deflate(side.width / 2.0);
|
||||||
|
break;
|
||||||
|
case StrokeAlign.center:
|
||||||
|
adjustedRect = borderRect;
|
||||||
|
break;
|
||||||
|
case StrokeAlign.outside:
|
||||||
|
adjustedRect = borderRect.inflate(side.width / 2.0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
canvas.drawOval(adjustedRect, side.toPaint());
|
||||||
|
} else {
|
||||||
final double radius;
|
final double radius;
|
||||||
switch (side.strokeAlign) {
|
switch (side.strokeAlign) {
|
||||||
case StrokeAlign.inside:
|
case StrokeAlign.inside:
|
||||||
@ -117,6 +147,30 @@ class CircleBorder extends OutlinedBorder {
|
|||||||
canvas.drawCircle(rect.center, radius, side.toPaint());
|
canvas.drawCircle(rect.center, radius, side.toPaint());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rect _adjustRect(Rect rect) {
|
||||||
|
if (eccentricity == 0.0 || rect.width == rect.height) {
|
||||||
|
return Rect.fromCircle(center: rect.center, radius: rect.shortestSide / 2.0);
|
||||||
|
}
|
||||||
|
if (rect.width < rect.height) {
|
||||||
|
final double delta = (1.0 - eccentricity) * (rect.height - rect.width) / 2.0;
|
||||||
|
return Rect.fromLTRB(
|
||||||
|
rect.left,
|
||||||
|
rect.top + delta,
|
||||||
|
rect.right,
|
||||||
|
rect.bottom - delta,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
final double delta = (1.0 - eccentricity) * (rect.width - rect.height) / 2.0;
|
||||||
|
return Rect.fromLTRB(
|
||||||
|
rect.left + delta,
|
||||||
|
rect.top,
|
||||||
|
rect.right - delta,
|
||||||
|
rect.bottom,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
@ -124,14 +178,18 @@ class CircleBorder extends OutlinedBorder {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return other is CircleBorder
|
return other is CircleBorder
|
||||||
&& other.side == side;
|
&& other.side == side
|
||||||
|
&& other.eccentricity == eccentricity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => side.hashCode;
|
int get hashCode => Object.hash(side, eccentricity);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
|
if (eccentricity != 0.0) {
|
||||||
|
return '${objectRuntimeType(this, 'CircleBorder')}($side, eccentricity: $eccentricity)';
|
||||||
|
}
|
||||||
return '${objectRuntimeType(this, 'CircleBorder')}($side)';
|
return '${objectRuntimeType(this, 'CircleBorder')}($side)';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
65
packages/flutter/lib/src/painting/oval_border.dart
Normal file
65
packages/flutter/lib/src/painting/oval_border.dart
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'dart:ui' as ui show lerpDouble;
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
import 'borders.dart';
|
||||||
|
import 'circle_border.dart';
|
||||||
|
|
||||||
|
/// A border that fits an elliptical shape.
|
||||||
|
///
|
||||||
|
/// Typically used with [ShapeDecoration] to draw an oval. Instead of centering
|
||||||
|
/// the [Border] to a square, like [CircleBorder], it fills the available space,
|
||||||
|
/// such that it touches the edges of the box. There is no difference between
|
||||||
|
/// `CircleBorder(eccentricity = 1.0)` and `OvalBorder()`. [OvalBorder] works as
|
||||||
|
/// an alias for users to discover this feature.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [CircleBorder], which draws a circle, centering when the box is rectangular.
|
||||||
|
/// * [Border], which, when used with [BoxDecoration], can also describe an oval.
|
||||||
|
class OvalBorder extends CircleBorder {
|
||||||
|
/// Create an oval border.
|
||||||
|
const OvalBorder({ super.side, super.eccentricity = 1.0 });
|
||||||
|
|
||||||
|
@override
|
||||||
|
ShapeBorder scale(double t) => OvalBorder(side: side.scale(t), eccentricity: eccentricity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
OvalBorder copyWith({ BorderSide? side, double? eccentricity }) {
|
||||||
|
return OvalBorder(side: side ?? this.side, eccentricity: eccentricity ?? this.eccentricity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
ShapeBorder? lerpFrom(ShapeBorder? a, double t) {
|
||||||
|
if (a is OvalBorder) {
|
||||||
|
return OvalBorder(
|
||||||
|
side: BorderSide.lerp(a.side, side, t),
|
||||||
|
eccentricity: clampDouble(ui.lerpDouble(a.eccentricity, eccentricity, t)!, 0.0, 1.0),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return super.lerpFrom(a, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
ShapeBorder? lerpTo(ShapeBorder? b, double t) {
|
||||||
|
if (b is OvalBorder) {
|
||||||
|
return OvalBorder(
|
||||||
|
side: BorderSide.lerp(side, b.side, t),
|
||||||
|
eccentricity: clampDouble(ui.lerpDouble(eccentricity, b.eccentricity, t)!, 0.0, 1.0),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return super.lerpTo(b, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
if (eccentricity != 1.0) {
|
||||||
|
return '${objectRuntimeType(this, 'OvalBorder')}($side, eccentricity: $eccentricity)';
|
||||||
|
}
|
||||||
|
return '${objectRuntimeType(this, 'OvalBorder')}($side)';
|
||||||
|
}
|
||||||
|
}
|
@ -71,6 +71,7 @@ class RoundedRectangleBorder extends OutlinedBorder {
|
|||||||
side: BorderSide.lerp(a.side, side, t),
|
side: BorderSide.lerp(a.side, side, t),
|
||||||
borderRadius: borderRadius,
|
borderRadius: borderRadius,
|
||||||
circleness: 1.0 - t,
|
circleness: 1.0 - t,
|
||||||
|
eccentricity: a.eccentricity,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return super.lerpFrom(a, t);
|
return super.lerpFrom(a, t);
|
||||||
@ -90,6 +91,7 @@ class RoundedRectangleBorder extends OutlinedBorder {
|
|||||||
side: BorderSide.lerp(side, b.side, t),
|
side: BorderSide.lerp(side, b.side, t),
|
||||||
borderRadius: borderRadius,
|
borderRadius: borderRadius,
|
||||||
circleness: t,
|
circleness: t,
|
||||||
|
eccentricity: b.eccentricity,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return super.lerpTo(b, t);
|
return super.lerpTo(b, t);
|
||||||
@ -187,17 +189,25 @@ class _RoundedRectangleToCircleBorder extends OutlinedBorder {
|
|||||||
super.side,
|
super.side,
|
||||||
this.borderRadius = BorderRadius.zero,
|
this.borderRadius = BorderRadius.zero,
|
||||||
required this.circleness,
|
required this.circleness,
|
||||||
|
required this.eccentricity,
|
||||||
}) : assert(side != null),
|
}) : assert(side != null),
|
||||||
assert(borderRadius != null),
|
assert(borderRadius != null),
|
||||||
assert(circleness != null);
|
assert(circleness != null);
|
||||||
|
|
||||||
final BorderRadiusGeometry borderRadius;
|
final BorderRadiusGeometry borderRadius;
|
||||||
|
|
||||||
final double circleness;
|
final double circleness;
|
||||||
|
final double eccentricity;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
EdgeInsetsGeometry get dimensions {
|
EdgeInsetsGeometry get dimensions {
|
||||||
|
switch (side.strokeAlign) {
|
||||||
|
case StrokeAlign.inside:
|
||||||
return EdgeInsets.all(side.width);
|
return EdgeInsets.all(side.width);
|
||||||
|
case StrokeAlign.center:
|
||||||
|
return EdgeInsets.all(side.width / 2);
|
||||||
|
case StrokeAlign.outside:
|
||||||
|
return EdgeInsets.zero;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -206,6 +216,7 @@ class _RoundedRectangleToCircleBorder extends OutlinedBorder {
|
|||||||
side: side.scale(t),
|
side: side.scale(t),
|
||||||
borderRadius: borderRadius * t,
|
borderRadius: borderRadius * t,
|
||||||
circleness: t,
|
circleness: t,
|
||||||
|
eccentricity: eccentricity,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,6 +228,7 @@ class _RoundedRectangleToCircleBorder extends OutlinedBorder {
|
|||||||
side: BorderSide.lerp(a.side, side, t),
|
side: BorderSide.lerp(a.side, side, t),
|
||||||
borderRadius: BorderRadiusGeometry.lerp(a.borderRadius, borderRadius, t)!,
|
borderRadius: BorderRadiusGeometry.lerp(a.borderRadius, borderRadius, t)!,
|
||||||
circleness: circleness * t,
|
circleness: circleness * t,
|
||||||
|
eccentricity: eccentricity,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (a is CircleBorder) {
|
if (a is CircleBorder) {
|
||||||
@ -224,6 +236,7 @@ class _RoundedRectangleToCircleBorder extends OutlinedBorder {
|
|||||||
side: BorderSide.lerp(a.side, side, t),
|
side: BorderSide.lerp(a.side, side, t),
|
||||||
borderRadius: borderRadius,
|
borderRadius: borderRadius,
|
||||||
circleness: circleness + (1.0 - circleness) * (1.0 - t),
|
circleness: circleness + (1.0 - circleness) * (1.0 - t),
|
||||||
|
eccentricity: a.eccentricity,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (a is _RoundedRectangleToCircleBorder) {
|
if (a is _RoundedRectangleToCircleBorder) {
|
||||||
@ -231,6 +244,7 @@ class _RoundedRectangleToCircleBorder extends OutlinedBorder {
|
|||||||
side: BorderSide.lerp(a.side, side, t),
|
side: BorderSide.lerp(a.side, side, t),
|
||||||
borderRadius: BorderRadiusGeometry.lerp(a.borderRadius, borderRadius, t)!,
|
borderRadius: BorderRadiusGeometry.lerp(a.borderRadius, borderRadius, t)!,
|
||||||
circleness: ui.lerpDouble(a.circleness, circleness, t)!,
|
circleness: ui.lerpDouble(a.circleness, circleness, t)!,
|
||||||
|
eccentricity: eccentricity,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return super.lerpFrom(a, t);
|
return super.lerpFrom(a, t);
|
||||||
@ -243,6 +257,7 @@ class _RoundedRectangleToCircleBorder extends OutlinedBorder {
|
|||||||
side: BorderSide.lerp(side, b.side, t),
|
side: BorderSide.lerp(side, b.side, t),
|
||||||
borderRadius: BorderRadiusGeometry.lerp(borderRadius, b.borderRadius, t)!,
|
borderRadius: BorderRadiusGeometry.lerp(borderRadius, b.borderRadius, t)!,
|
||||||
circleness: circleness * (1.0 - t),
|
circleness: circleness * (1.0 - t),
|
||||||
|
eccentricity: eccentricity,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (b is CircleBorder) {
|
if (b is CircleBorder) {
|
||||||
@ -250,6 +265,7 @@ class _RoundedRectangleToCircleBorder extends OutlinedBorder {
|
|||||||
side: BorderSide.lerp(side, b.side, t),
|
side: BorderSide.lerp(side, b.side, t),
|
||||||
borderRadius: borderRadius,
|
borderRadius: borderRadius,
|
||||||
circleness: circleness + (1.0 - circleness) * t,
|
circleness: circleness + (1.0 - circleness) * t,
|
||||||
|
eccentricity: b.eccentricity,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (b is _RoundedRectangleToCircleBorder) {
|
if (b is _RoundedRectangleToCircleBorder) {
|
||||||
@ -257,6 +273,7 @@ class _RoundedRectangleToCircleBorder extends OutlinedBorder {
|
|||||||
side: BorderSide.lerp(side, b.side, t),
|
side: BorderSide.lerp(side, b.side, t),
|
||||||
borderRadius: BorderRadiusGeometry.lerp(borderRadius, b.borderRadius, t)!,
|
borderRadius: BorderRadiusGeometry.lerp(borderRadius, b.borderRadius, t)!,
|
||||||
circleness: ui.lerpDouble(circleness, b.circleness, t)!,
|
circleness: ui.lerpDouble(circleness, b.circleness, t)!,
|
||||||
|
eccentricity: eccentricity,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return super.lerpTo(b, t);
|
return super.lerpTo(b, t);
|
||||||
@ -267,7 +284,8 @@ class _RoundedRectangleToCircleBorder extends OutlinedBorder {
|
|||||||
return rect;
|
return rect;
|
||||||
}
|
}
|
||||||
if (rect.width < rect.height) {
|
if (rect.width < rect.height) {
|
||||||
final double delta = circleness * (rect.height - rect.width) / 2.0;
|
final double partialDelta = (rect.height - rect.width) / 2;
|
||||||
|
final double delta = circleness * partialDelta * (1.0 - eccentricity);
|
||||||
return Rect.fromLTRB(
|
return Rect.fromLTRB(
|
||||||
rect.left,
|
rect.left,
|
||||||
rect.top + delta,
|
rect.top + delta,
|
||||||
@ -275,7 +293,8 @@ class _RoundedRectangleToCircleBorder extends OutlinedBorder {
|
|||||||
rect.bottom - delta,
|
rect.bottom - delta,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
final double delta = circleness * (rect.width - rect.height) / 2.0;
|
final double partialDelta = (rect.width - rect.height) / 2;
|
||||||
|
final double delta = circleness * partialDelta * (1.0 - eccentricity);
|
||||||
return Rect.fromLTRB(
|
return Rect.fromLTRB(
|
||||||
rect.left + delta,
|
rect.left + delta,
|
||||||
rect.top,
|
rect.top,
|
||||||
@ -290,7 +309,22 @@ class _RoundedRectangleToCircleBorder extends OutlinedBorder {
|
|||||||
if (circleness == 0.0) {
|
if (circleness == 0.0) {
|
||||||
return resolvedRadius;
|
return resolvedRadius;
|
||||||
}
|
}
|
||||||
return BorderRadius.lerp(resolvedRadius, BorderRadius.circular(rect.shortestSide / 2.0), circleness);
|
if (eccentricity != 0.0) {
|
||||||
|
if (rect.width < rect.height) {
|
||||||
|
return BorderRadius.lerp(
|
||||||
|
resolvedRadius,
|
||||||
|
BorderRadius.all(Radius.elliptical(rect.width / 2, (0.5 + eccentricity / 2) * rect.height / 2)),
|
||||||
|
circleness,
|
||||||
|
)!;
|
||||||
|
} else {
|
||||||
|
return BorderRadius.lerp(
|
||||||
|
resolvedRadius,
|
||||||
|
BorderRadius.all(Radius.elliptical((0.5 + eccentricity / 2) * rect.width / 2, rect.height / 2)),
|
||||||
|
circleness,
|
||||||
|
)!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return BorderRadius.lerp(resolvedRadius, BorderRadius.circular(rect.shortestSide / 2), circleness);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -319,11 +353,12 @@ class _RoundedRectangleToCircleBorder extends OutlinedBorder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_RoundedRectangleToCircleBorder copyWith({ BorderSide? side, BorderRadiusGeometry? borderRadius, double? circleness }) {
|
_RoundedRectangleToCircleBorder copyWith({ BorderSide? side, BorderRadiusGeometry? borderRadius, double? circleness, double? eccentricity }) {
|
||||||
return _RoundedRectangleToCircleBorder(
|
return _RoundedRectangleToCircleBorder(
|
||||||
side: side ?? this.side,
|
side: side ?? this.side,
|
||||||
borderRadius: borderRadius ?? this.borderRadius,
|
borderRadius: borderRadius ?? this.borderRadius,
|
||||||
circleness: circleness ?? this.circleness,
|
circleness: circleness ?? this.circleness,
|
||||||
|
eccentricity: eccentricity ?? this.eccentricity,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -371,6 +406,9 @@ class _RoundedRectangleToCircleBorder extends OutlinedBorder {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
|
if (eccentricity != 0.0) {
|
||||||
|
return 'RoundedRectangleBorder($side, $borderRadius, ${(circleness * 100).toStringAsFixed(1)}% of the way to being a CircleBorder that is ${(eccentricity * 100).toStringAsFixed(1)}% oval)';
|
||||||
|
}
|
||||||
return 'RoundedRectangleBorder($side, $borderRadius, ${(circleness * 100).toStringAsFixed(1)}% of the way to being a CircleBorder)';
|
return 'RoundedRectangleBorder($side, $borderRadius, ${(circleness * 100).toStringAsFixed(1)}% of the way to being a CircleBorder)';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,6 +55,7 @@ class StadiumBorder extends OutlinedBorder {
|
|||||||
return _StadiumToCircleBorder(
|
return _StadiumToCircleBorder(
|
||||||
side: BorderSide.lerp(a.side, side, t),
|
side: BorderSide.lerp(a.side, side, t),
|
||||||
circleness: 1.0 - t,
|
circleness: 1.0 - t,
|
||||||
|
eccentricity: a.eccentricity,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (a is RoundedRectangleBorder) {
|
if (a is RoundedRectangleBorder) {
|
||||||
@ -77,6 +78,7 @@ class StadiumBorder extends OutlinedBorder {
|
|||||||
return _StadiumToCircleBorder(
|
return _StadiumToCircleBorder(
|
||||||
side: BorderSide.lerp(side, b.side, t),
|
side: BorderSide.lerp(side, b.side, t),
|
||||||
circleness: t,
|
circleness: t,
|
||||||
|
eccentricity: b.eccentricity,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (b is RoundedRectangleBorder) {
|
if (b is RoundedRectangleBorder) {
|
||||||
@ -127,7 +129,7 @@ class StadiumBorder extends OutlinedBorder {
|
|||||||
case BorderStyle.none:
|
case BorderStyle.none:
|
||||||
break;
|
break;
|
||||||
case BorderStyle.solid:
|
case BorderStyle.solid:
|
||||||
final Radius radius = Radius.circular(rect.shortestSide / 2.0);
|
final Radius radius = Radius.circular(rect.shortestSide / 2);
|
||||||
final RRect borderRect = RRect.fromRectAndRadius(rect, radius);
|
final RRect borderRect = RRect.fromRectAndRadius(rect, radius);
|
||||||
final RRect adjustedRect;
|
final RRect adjustedRect;
|
||||||
switch (side.strokeAlign) {
|
switch (side.strokeAlign) {
|
||||||
@ -171,14 +173,23 @@ class _StadiumToCircleBorder extends OutlinedBorder {
|
|||||||
const _StadiumToCircleBorder({
|
const _StadiumToCircleBorder({
|
||||||
super.side,
|
super.side,
|
||||||
this.circleness = 0.0,
|
this.circleness = 0.0,
|
||||||
|
required this.eccentricity,
|
||||||
}) : assert(side != null),
|
}) : assert(side != null),
|
||||||
assert(circleness != null);
|
assert(circleness != null);
|
||||||
|
|
||||||
final double circleness;
|
final double circleness;
|
||||||
|
final double eccentricity;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
EdgeInsetsGeometry get dimensions {
|
EdgeInsetsGeometry get dimensions {
|
||||||
|
switch (side.strokeAlign) {
|
||||||
|
case StrokeAlign.inside:
|
||||||
return EdgeInsets.all(side.width);
|
return EdgeInsets.all(side.width);
|
||||||
|
case StrokeAlign.center:
|
||||||
|
return EdgeInsets.all(side.width / 2);
|
||||||
|
case StrokeAlign.outside:
|
||||||
|
return EdgeInsets.zero;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -186,6 +197,7 @@ class _StadiumToCircleBorder extends OutlinedBorder {
|
|||||||
return _StadiumToCircleBorder(
|
return _StadiumToCircleBorder(
|
||||||
side: side.scale(t),
|
side: side.scale(t),
|
||||||
circleness: t,
|
circleness: t,
|
||||||
|
eccentricity: eccentricity,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,18 +208,21 @@ class _StadiumToCircleBorder extends OutlinedBorder {
|
|||||||
return _StadiumToCircleBorder(
|
return _StadiumToCircleBorder(
|
||||||
side: BorderSide.lerp(a.side, side, t),
|
side: BorderSide.lerp(a.side, side, t),
|
||||||
circleness: circleness * t,
|
circleness: circleness * t,
|
||||||
|
eccentricity: eccentricity,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (a is CircleBorder) {
|
if (a is CircleBorder) {
|
||||||
return _StadiumToCircleBorder(
|
return _StadiumToCircleBorder(
|
||||||
side: BorderSide.lerp(a.side, side, t),
|
side: BorderSide.lerp(a.side, side, t),
|
||||||
circleness: circleness + (1.0 - circleness) * (1.0 - t),
|
circleness: circleness + (1.0 - circleness) * (1.0 - t),
|
||||||
|
eccentricity: a.eccentricity,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (a is _StadiumToCircleBorder) {
|
if (a is _StadiumToCircleBorder) {
|
||||||
return _StadiumToCircleBorder(
|
return _StadiumToCircleBorder(
|
||||||
side: BorderSide.lerp(a.side, side, t),
|
side: BorderSide.lerp(a.side, side, t),
|
||||||
circleness: ui.lerpDouble(a.circleness, circleness, t)!,
|
circleness: ui.lerpDouble(a.circleness, circleness, t)!,
|
||||||
|
eccentricity: ui.lerpDouble(a.eccentricity, eccentricity, t)!,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return super.lerpFrom(a, t);
|
return super.lerpFrom(a, t);
|
||||||
@ -220,18 +235,21 @@ class _StadiumToCircleBorder extends OutlinedBorder {
|
|||||||
return _StadiumToCircleBorder(
|
return _StadiumToCircleBorder(
|
||||||
side: BorderSide.lerp(side, b.side, t),
|
side: BorderSide.lerp(side, b.side, t),
|
||||||
circleness: circleness * (1.0 - t),
|
circleness: circleness * (1.0 - t),
|
||||||
|
eccentricity: eccentricity,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (b is CircleBorder) {
|
if (b is CircleBorder) {
|
||||||
return _StadiumToCircleBorder(
|
return _StadiumToCircleBorder(
|
||||||
side: BorderSide.lerp(side, b.side, t),
|
side: BorderSide.lerp(side, b.side, t),
|
||||||
circleness: circleness + (1.0 - circleness) * t,
|
circleness: circleness + (1.0 - circleness) * t,
|
||||||
|
eccentricity: b.eccentricity,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (b is _StadiumToCircleBorder) {
|
if (b is _StadiumToCircleBorder) {
|
||||||
return _StadiumToCircleBorder(
|
return _StadiumToCircleBorder(
|
||||||
side: BorderSide.lerp(side, b.side, t),
|
side: BorderSide.lerp(side, b.side, t),
|
||||||
circleness: ui.lerpDouble(circleness, b.circleness, t)!,
|
circleness: ui.lerpDouble(circleness, b.circleness, t)!,
|
||||||
|
eccentricity: ui.lerpDouble(eccentricity, b.eccentricity, t)!,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return super.lerpTo(b, t);
|
return super.lerpTo(b, t);
|
||||||
@ -242,7 +260,8 @@ class _StadiumToCircleBorder extends OutlinedBorder {
|
|||||||
return rect;
|
return rect;
|
||||||
}
|
}
|
||||||
if (rect.width < rect.height) {
|
if (rect.width < rect.height) {
|
||||||
final double delta = circleness * (rect.height - rect.width) / 2.0;
|
final double partialDelta = (rect.height - rect.width) / 2;
|
||||||
|
final double delta = circleness * partialDelta * (1.0 - eccentricity);
|
||||||
return Rect.fromLTRB(
|
return Rect.fromLTRB(
|
||||||
rect.left,
|
rect.left,
|
||||||
rect.top + delta,
|
rect.top + delta,
|
||||||
@ -250,7 +269,8 @@ class _StadiumToCircleBorder extends OutlinedBorder {
|
|||||||
rect.bottom - delta,
|
rect.bottom - delta,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
final double delta = circleness * (rect.width - rect.height) / 2.0;
|
final double partialDelta = (rect.width - rect.height) / 2;
|
||||||
|
final double delta = circleness * partialDelta * (1.0 - eccentricity);
|
||||||
return Rect.fromLTRB(
|
return Rect.fromLTRB(
|
||||||
rect.left + delta,
|
rect.left + delta,
|
||||||
rect.top,
|
rect.top,
|
||||||
@ -261,7 +281,23 @@ class _StadiumToCircleBorder extends OutlinedBorder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
BorderRadius _adjustBorderRadius(Rect rect) {
|
BorderRadius _adjustBorderRadius(Rect rect) {
|
||||||
return BorderRadius.circular(rect.shortestSide / 2.0);
|
final BorderRadius circleRadius = BorderRadius.circular(rect.shortestSide / 2);
|
||||||
|
if (eccentricity != 0.0) {
|
||||||
|
if (rect.width < rect.height) {
|
||||||
|
return BorderRadius.lerp(
|
||||||
|
circleRadius,
|
||||||
|
BorderRadius.all(Radius.elliptical(rect.width / 2, (0.5 + eccentricity / 2) * rect.height / 2)),
|
||||||
|
circleness,
|
||||||
|
)!;
|
||||||
|
} else {
|
||||||
|
return BorderRadius.lerp(
|
||||||
|
circleRadius,
|
||||||
|
BorderRadius.all(Radius.elliptical((0.5 + eccentricity / 2) * rect.width / 2, rect.height / 2)),
|
||||||
|
circleness,
|
||||||
|
)!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return circleRadius;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -277,10 +313,11 @@ class _StadiumToCircleBorder extends OutlinedBorder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_StadiumToCircleBorder copyWith({ BorderSide? side, double? circleness }) {
|
_StadiumToCircleBorder copyWith({ BorderSide? side, double? circleness, double? eccentricity }) {
|
||||||
return _StadiumToCircleBorder(
|
return _StadiumToCircleBorder(
|
||||||
side: side ?? this.side,
|
side: side ?? this.side,
|
||||||
circleness: circleness ?? this.circleness,
|
circleness: circleness ?? this.circleness,
|
||||||
|
eccentricity: eccentricity ?? this.eccentricity,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,8 +364,10 @@ class _StadiumToCircleBorder extends OutlinedBorder {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'StadiumBorder($side, ${(circleness * 100).toStringAsFixed(1)}% '
|
if (eccentricity != 0.0) {
|
||||||
'of the way to being a CircleBorder)';
|
return 'StadiumBorder($side, ${(circleness * 100).toStringAsFixed(1)}% of the way to being a CircleBorder that is ${(eccentricity * 100).toStringAsFixed(1)}% oval)';
|
||||||
|
}
|
||||||
|
return 'StadiumBorder($side, ${(circleness * 100).toStringAsFixed(1)}% of the way to being a CircleBorder)';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2014,11 +2014,11 @@ class RenderPhysicalModel extends _RenderPhysicalModelBase<RRect> {
|
|||||||
RRect get _defaultClip {
|
RRect get _defaultClip {
|
||||||
assert(hasSize);
|
assert(hasSize);
|
||||||
assert(_shape != null);
|
assert(_shape != null);
|
||||||
|
final Rect rect = Offset.zero & size;
|
||||||
switch (_shape) {
|
switch (_shape) {
|
||||||
case BoxShape.rectangle:
|
case BoxShape.rectangle:
|
||||||
return (borderRadius ?? BorderRadius.zero).toRRect(Offset.zero & size);
|
return (borderRadius ?? BorderRadius.zero).toRRect(rect);
|
||||||
case BoxShape.circle:
|
case BoxShape.circle:
|
||||||
final Rect rect = Offset.zero & size;
|
|
||||||
return RRect.fromRectXY(rect, rect.width / 2, rect.height / 2);
|
return RRect.fromRectXY(rect, rect.width / 2, rect.height / 2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,11 +12,29 @@ void main() {
|
|||||||
test('CircleBorder defaults', () {
|
test('CircleBorder defaults', () {
|
||||||
const CircleBorder border = CircleBorder();
|
const CircleBorder border = CircleBorder();
|
||||||
expect(border.side, BorderSide.none);
|
expect(border.side, BorderSide.none);
|
||||||
|
expect(border.eccentricity, 0.0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('CircleBorder getInnerPath and getOuterPath', () {
|
||||||
|
const Rect circleRect = Rect.fromLTWH(50, 0, 100, 100);
|
||||||
|
const Rect rect = Rect.fromLTWH(0, 0, 200, 100);
|
||||||
|
|
||||||
|
expect(const CircleBorder().getInnerPath(rect).getBounds(), circleRect);
|
||||||
|
expect(const CircleBorder().getOuterPath(rect).getBounds(), circleRect);
|
||||||
|
|
||||||
|
const CircleBorder oval = CircleBorder(eccentricity: 1.0);
|
||||||
|
expect(oval.getOuterPath(rect).getBounds(), rect);
|
||||||
|
expect(oval.getInnerPath(rect).getBounds(), rect);
|
||||||
|
|
||||||
|
const CircleBorder o10 = CircleBorder(side: BorderSide(width: 10.0), eccentricity: 1.0);
|
||||||
|
expect(o10.getOuterPath(rect).getBounds(), Offset.zero & const Size(200, 100));
|
||||||
|
expect(o10.getInnerPath(rect).getBounds(), const Offset(10, 10) & const Size(180, 80));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('CircleBorder copyWith, ==, hashCode', () {
|
test('CircleBorder copyWith, ==, hashCode', () {
|
||||||
expect(const CircleBorder(), const CircleBorder().copyWith());
|
expect(const CircleBorder(), const CircleBorder().copyWith());
|
||||||
expect(const CircleBorder().hashCode, const CircleBorder().copyWith().hashCode);
|
expect(const CircleBorder().hashCode, const CircleBorder().copyWith().hashCode);
|
||||||
|
expect(const CircleBorder(eccentricity: 0.5).hashCode, const CircleBorder().copyWith(eccentricity: 0.5).hashCode);
|
||||||
const BorderSide side = BorderSide(width: 10.0, color: Color(0xff123456));
|
const BorderSide side = BorderSide(width: 10.0, color: Color(0xff123456));
|
||||||
expect(const CircleBorder().copyWith(side: side), const CircleBorder(side: side));
|
expect(const CircleBorder().copyWith(side: side), const CircleBorder(side: side));
|
||||||
});
|
});
|
||||||
|
48
packages/flutter/test/painting/oval_border_test.dart
Normal file
48
packages/flutter/test/painting/oval_border_test.dart
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// Copyright 2014 The Flutter 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';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('OvalBorder defaults', () {
|
||||||
|
const OvalBorder border = OvalBorder();
|
||||||
|
expect(border.side, BorderSide.none);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('OvalBorder copyWith, ==, hashCode', () {
|
||||||
|
expect(const OvalBorder(), const OvalBorder().copyWith());
|
||||||
|
expect(const OvalBorder().hashCode, const OvalBorder().copyWith().hashCode);
|
||||||
|
const BorderSide side = BorderSide(width: 10.0, color: Color(0xff123456));
|
||||||
|
expect(const OvalBorder().copyWith(side: side), const OvalBorder(side: side));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('OvalBorder', () {
|
||||||
|
const OvalBorder c10 = OvalBorder(side: BorderSide(width: 10.0));
|
||||||
|
const OvalBorder c15 = OvalBorder(side: BorderSide(width: 15.0));
|
||||||
|
const OvalBorder c20 = OvalBorder(side: BorderSide(width: 20.0));
|
||||||
|
expect(c10.dimensions, const EdgeInsets.all(10.0));
|
||||||
|
expect(c10.scale(2.0), c20);
|
||||||
|
expect(c20.scale(0.5), c10);
|
||||||
|
expect(ShapeBorder.lerp(c10, c20, 0.0), c10);
|
||||||
|
expect(ShapeBorder.lerp(c10, c20, 0.5), c15);
|
||||||
|
expect(ShapeBorder.lerp(c10, c20, 1.0), c20);
|
||||||
|
expect(
|
||||||
|
c10.getInnerPath(const Rect.fromLTWH(0, 0, 100, 40)),
|
||||||
|
isPathThat(
|
||||||
|
includes: const <Offset>[ Offset(12, 19), Offset(50, 10), Offset(88, 19), Offset(50, 29) ],
|
||||||
|
excludes: const <Offset>[ Offset(17, 26), Offset(15, 15), Offset(74, 10), Offset(76, 28) ],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
c10.getOuterPath(const Rect.fromLTWH(0, 0, 100, 20)),
|
||||||
|
isPathThat(
|
||||||
|
includes: const <Offset>[ Offset(2, 9), Offset(50, 0), Offset(98, 9), Offset(50, 19) ],
|
||||||
|
excludes: const <Offset>[ Offset(7, 16), Offset(10, 2), Offset(84, 1), Offset(86, 18) ],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
@ -45,13 +45,26 @@ void main() {
|
|||||||
test('ShapeDecoration.lerp and hit test', () {
|
test('ShapeDecoration.lerp and hit test', () {
|
||||||
const Decoration a = ShapeDecoration(shape: CircleBorder());
|
const Decoration a = ShapeDecoration(shape: CircleBorder());
|
||||||
const Decoration b = ShapeDecoration(shape: RoundedRectangleBorder());
|
const Decoration b = ShapeDecoration(shape: RoundedRectangleBorder());
|
||||||
|
const Decoration c = ShapeDecoration(shape: OvalBorder());
|
||||||
expect(Decoration.lerp(a, b, 0.0), a);
|
expect(Decoration.lerp(a, b, 0.0), a);
|
||||||
expect(Decoration.lerp(a, b, 1.0), b);
|
expect(Decoration.lerp(a, b, 1.0), b);
|
||||||
|
expect(Decoration.lerp(a, c, 0.0), a);
|
||||||
|
expect(Decoration.lerp(a, c, 1.0), c);
|
||||||
|
expect(Decoration.lerp(b, c, 0.0), b);
|
||||||
|
expect(Decoration.lerp(b, c, 1.0), c);
|
||||||
const Size size = Size(200.0, 100.0); // at t=0.5, width will be 150 (x=25 to x=175).
|
const Size size = Size(200.0, 100.0); // at t=0.5, width will be 150 (x=25 to x=175).
|
||||||
expect(a.hitTest(size, const Offset(20.0, 50.0)), isFalse);
|
expect(a.hitTest(size, const Offset(20.0, 50.0)), isFalse);
|
||||||
|
expect(c.hitTest(size, const Offset(50, 5.0)), isFalse);
|
||||||
|
expect(c.hitTest(size, const Offset(5, 30.0)), isFalse);
|
||||||
expect(Decoration.lerp(a, b, 0.1)!.hitTest(size, const Offset(20.0, 50.0)), isFalse);
|
expect(Decoration.lerp(a, b, 0.1)!.hitTest(size, const Offset(20.0, 50.0)), isFalse);
|
||||||
expect(Decoration.lerp(a, b, 0.5)!.hitTest(size, const Offset(20.0, 50.0)), isFalse);
|
expect(Decoration.lerp(a, b, 0.5)!.hitTest(size, const Offset(20.0, 50.0)), isFalse);
|
||||||
expect(Decoration.lerp(a, b, 0.9)!.hitTest(size, const Offset(20.0, 50.0)), isTrue);
|
expect(Decoration.lerp(a, b, 0.9)!.hitTest(size, const Offset(20.0, 50.0)), isTrue);
|
||||||
|
expect(Decoration.lerp(a, c, 0.1)!.hitTest(size, const Offset(30.0, 50.0)), isFalse);
|
||||||
|
expect(Decoration.lerp(a, c, 0.5)!.hitTest(size, const Offset(30.0, 50.0)), isTrue);
|
||||||
|
expect(Decoration.lerp(a, c, 0.9)!.hitTest(size, const Offset(30.0, 50.0)), isTrue);
|
||||||
|
expect(Decoration.lerp(b, c, 0.1)!.hitTest(size, const Offset(45.0, 10.0)), isTrue);
|
||||||
|
expect(Decoration.lerp(b, c, 0.5)!.hitTest(size, const Offset(30.0, 10.0)), isTrue);
|
||||||
|
expect(Decoration.lerp(b, c, 0.9)!.hitTest(size, const Offset(10.0, 30.0)), isTrue);
|
||||||
expect(b.hitTest(size, const Offset(20.0, 50.0)), isTrue);
|
expect(b.hitTest(size, const Offset(20.0, 50.0)), isTrue);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -107,6 +120,16 @@ void main() {
|
|||||||
);
|
);
|
||||||
expect(clipPath, isLookLikeExpectedPath);
|
expect(clipPath, isLookLikeExpectedPath);
|
||||||
});
|
});
|
||||||
|
test('ShapeDecoration.getClipPath for oval', () {
|
||||||
|
const ShapeDecoration decoration = ShapeDecoration(shape: OvalBorder());
|
||||||
|
const Rect rect = Rect.fromLTWH(0.0, 0.0, 100.0, 50.0);
|
||||||
|
final Path clipPath = decoration.getClipPath(rect, TextDirection.ltr);
|
||||||
|
final Matcher isLookLikeExpectedPath = isPathThat(
|
||||||
|
includes: const <Offset>[ Offset(50.0, 10.0), ],
|
||||||
|
excludes: const <Offset>[ Offset(1.0, 1.0), Offset(15.0, 1.0), Offset(99.0, 19.0), ],
|
||||||
|
);
|
||||||
|
expect(clipPath, isLookLikeExpectedPath);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
class TestImageProvider extends ImageProvider<TestImageProvider> {
|
class TestImageProvider extends ImageProvider<TestImageProvider> {
|
||||||
|
@ -1525,19 +1525,15 @@ class _RendersOnPhysicalModel extends _MatchRenderObject<RenderPhysicalShape, Re
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (borderRadius == null &&
|
||||||
borderRadius == null &&
|
|
||||||
shape == BoxShape.rectangle &&
|
shape == BoxShape.rectangle &&
|
||||||
!assertRoundedRectangle(shapeClipper, BorderRadius.zero, matchState)
|
!assertRoundedRectangle(shapeClipper, BorderRadius.zero, matchState)) {
|
||||||
) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (borderRadius == null &&
|
||||||
borderRadius == null &&
|
|
||||||
shape == BoxShape.circle &&
|
shape == BoxShape.circle &&
|
||||||
!assertCircle(shapeClipper, matchState)
|
!assertCircle(shapeClipper, matchState)) {
|
||||||
) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user