1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// 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:math' as math;
import 'package:flutter/foundation.dart';
import 'basic_types.dart';
import 'border_radius.dart';
import 'borders.dart';
import 'edge_insets.dart';
/// A rectangular border with flattened or "beveled" corners.
///
/// The line segments that connect the rectangle's four sides will
/// begin and at locations offset by the corresponding border radius,
/// but not farther than the side's center. If all the border radii
/// exceed the sides' half widths/heights the resulting shape is
/// diamond made by connecting the centers of the sides.
class BeveledRectangleBorder extends OutlinedBorder {
/// Creates a border like a [RoundedRectangleBorder] except that the corners
/// are joined by straight lines instead of arcs.
///
/// The arguments must not be null.
const BeveledRectangleBorder({
BorderSide side = BorderSide.none,
this.borderRadius = BorderRadius.zero,
}) : assert(side != null),
assert(borderRadius != null),
super(side: side);
/// The radii for each corner.
///
/// Each corner [Radius] defines the endpoints of a line segment that
/// spans the corner. The endpoints are located in the same place as
/// they would be for [RoundedRectangleBorder], but they're connected
/// by a straight line instead of an arc.
///
/// Negative radius values are clamped to 0.0 by [getInnerPath] and
/// [getOuterPath].
final BorderRadiusGeometry borderRadius;
@override
EdgeInsetsGeometry get dimensions {
return EdgeInsets.all(side.width);
}
@override
ShapeBorder scale(double t) {
return BeveledRectangleBorder(
side: side.scale(t),
borderRadius: borderRadius * t,
);
}
@override
ShapeBorder? lerpFrom(ShapeBorder? a, double t) {
assert(t != null);
if (a is BeveledRectangleBorder) {
return BeveledRectangleBorder(
side: BorderSide.lerp(a.side, side, t),
borderRadius: BorderRadiusGeometry.lerp(a.borderRadius, borderRadius, t)!,
);
}
return super.lerpFrom(a, t);
}
@override
ShapeBorder? lerpTo(ShapeBorder? b, double t) {
assert(t != null);
if (b is BeveledRectangleBorder) {
return BeveledRectangleBorder(
side: BorderSide.lerp(side, b.side, t),
borderRadius: BorderRadiusGeometry.lerp(borderRadius, b.borderRadius, t)!,
);
}
return super.lerpTo(b, t);
}
/// Returns a copy of this RoundedRectangleBorder with the given fields
/// replaced with the new values.
@override
BeveledRectangleBorder copyWith({ BorderSide? side, BorderRadius? borderRadius }) {
return BeveledRectangleBorder(
side: side ?? this.side,
borderRadius: borderRadius ?? this.borderRadius,
);
}
Path _getPath(RRect rrect) {
final Offset centerLeft = Offset(rrect.left, rrect.center.dy);
final Offset centerRight = Offset(rrect.right, rrect.center.dy);
final Offset centerTop = Offset(rrect.center.dx, rrect.top);
final Offset centerBottom = Offset(rrect.center.dx, rrect.bottom);
final double tlRadiusX = math.max(0.0, rrect.tlRadiusX);
final double tlRadiusY = math.max(0.0, rrect.tlRadiusY);
final double trRadiusX = math.max(0.0, rrect.trRadiusX);
final double trRadiusY = math.max(0.0, rrect.trRadiusY);
final double blRadiusX = math.max(0.0, rrect.blRadiusX);
final double blRadiusY = math.max(0.0, rrect.blRadiusY);
final double brRadiusX = math.max(0.0, rrect.brRadiusX);
final double brRadiusY = math.max(0.0, rrect.brRadiusY);
final List<Offset> vertices = <Offset>[
Offset(rrect.left, math.min(centerLeft.dy, rrect.top + tlRadiusY)),
Offset(math.min(centerTop.dx, rrect.left + tlRadiusX), rrect.top),
Offset(math.max(centerTop.dx, rrect.right -trRadiusX), rrect.top),
Offset(rrect.right, math.min(centerRight.dy, rrect.top + trRadiusY)),
Offset(rrect.right, math.max(centerRight.dy, rrect.bottom - brRadiusY)),
Offset(math.max(centerBottom.dx, rrect.right - brRadiusX), rrect.bottom),
Offset(math.min(centerBottom.dx, rrect.left + blRadiusX), rrect.bottom),
Offset(rrect.left, math.max(centerLeft.dy, rrect.bottom - blRadiusY)),
];
return Path()..addPolygon(vertices, true);
}
@override
Path getInnerPath(Rect rect, { TextDirection? textDirection }) {
return _getPath(borderRadius.resolve(textDirection).toRRect(rect).deflate(side.width));
}
@override
Path getOuterPath(Rect rect, { TextDirection? textDirection }) {
return _getPath(borderRadius.resolve(textDirection).toRRect(rect));
}
@override
void paint(Canvas canvas, Rect rect, { TextDirection? textDirection }) {
if (rect.isEmpty)
return;
switch (side.style) {
case BorderStyle.none:
break;
case BorderStyle.solid:
final Path path = getOuterPath(rect, textDirection: textDirection)
..addPath(getInnerPath(rect, textDirection: textDirection), Offset.zero);
canvas.drawPath(path, side.toPaint());
break;
}
}
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType)
return false;
return other is BeveledRectangleBorder
&& other.side == side
&& other.borderRadius == borderRadius;
}
@override
int get hashCode => hashValues(side, borderRadius);
@override
String toString() {
return '${objectRuntimeType(this, 'BeveledRectangleBorder')}($side, $borderRadius)';
}
}