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

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com