epaint/shapes/rect_shape.rs
1use std::sync::Arc;
2
3use crate::*;
4
5/// How to paint a rectangle.
6#[derive(Clone, Debug, PartialEq)]
7#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
8pub struct RectShape {
9 pub rect: Rect,
10
11 /// How rounded the corners of the rectangle are.
12 ///
13 /// Use [`CornerRadius::ZERO`] for for sharp corners.
14 ///
15 /// This is the corner radii of the rectangle.
16 /// If there is a stroke, then the stroke will have an inner and outer corner radius,
17 /// and those will depend on [`StrokeKind`] and the stroke width.
18 ///
19 /// For [`StrokeKind::Inside`], the outside of the stroke coincides with the rectangle,
20 /// so the rounding will in this case specify the outer corner radius.
21 pub corner_radius: CornerRadius,
22
23 /// How to fill the rectangle.
24 pub fill: Color32,
25
26 /// The thickness and color of the outline.
27 ///
28 /// Whether or not the stroke is inside or outside the edge of [`Self::rect`],
29 /// is controlled by [`Self::stroke_kind`].
30 pub stroke: Stroke,
31
32 /// Is the stroke on the inside, outside, or centered on the rectangle?
33 ///
34 /// If you want to perfectly tile rectangles, use [`StrokeKind::Inside`].
35 pub stroke_kind: StrokeKind,
36
37 /// Snap the rectangle to pixels?
38 ///
39 /// Rounding produces sharper rectangles.
40 ///
41 /// If `None`, [`crate::TessellationOptions::round_rects_to_pixels`] will be used.
42 pub round_to_pixels: Option<bool>,
43
44 /// If larger than zero, the edges of the rectangle
45 /// (for both fill and stroke) will be blurred.
46 ///
47 /// This can be used to produce shadows and glow effects.
48 ///
49 /// The blur is currently implemented using a simple linear blur in sRGBA gamma space.
50 pub blur_width: f32,
51
52 /// Controls texturing, if any.
53 ///
54 /// Since most rectangles do not have a texture, this is optional and in an `Arc`,
55 /// so that [`RectShape`] is kept small..
56 pub brush: Option<Arc<Brush>>,
57}
58
59#[test]
60fn rect_shape_size() {
61 assert_eq!(
62 std::mem::size_of::<RectShape>(), 48,
63 "RectShape changed size! If it shrank - good! Update this test. If it grew - bad! Try to find a way to avoid it."
64 );
65 assert!(
66 std::mem::size_of::<RectShape>() <= 64,
67 "RectShape is getting way too big!"
68 );
69}
70
71impl RectShape {
72 /// See also [`Self::filled`] and [`Self::stroke`].
73 #[inline]
74 pub fn new(
75 rect: Rect,
76 corner_radius: impl Into<CornerRadius>,
77 fill_color: impl Into<Color32>,
78 stroke: impl Into<Stroke>,
79 stroke_kind: StrokeKind,
80 ) -> Self {
81 Self {
82 rect,
83 corner_radius: corner_radius.into(),
84 fill: fill_color.into(),
85 stroke: stroke.into(),
86 stroke_kind,
87 round_to_pixels: None,
88 blur_width: 0.0,
89 brush: Default::default(),
90 }
91 }
92
93 #[inline]
94 pub fn filled(
95 rect: Rect,
96 corner_radius: impl Into<CornerRadius>,
97 fill_color: impl Into<Color32>,
98 ) -> Self {
99 Self::new(
100 rect,
101 corner_radius,
102 fill_color,
103 Stroke::NONE,
104 StrokeKind::Outside, // doesn't matter
105 )
106 }
107
108 #[inline]
109 pub fn stroke(
110 rect: Rect,
111 corner_radius: impl Into<CornerRadius>,
112 stroke: impl Into<Stroke>,
113 stroke_kind: StrokeKind,
114 ) -> Self {
115 let fill = Color32::TRANSPARENT;
116 Self::new(rect, corner_radius, fill, stroke, stroke_kind)
117 }
118
119 /// Set if the stroke is on the inside, outside, or centered on the rectangle.
120 #[inline]
121 pub fn with_stroke_kind(mut self, stroke_kind: StrokeKind) -> Self {
122 self.stroke_kind = stroke_kind;
123 self
124 }
125
126 /// Snap the rectangle to pixels?
127 ///
128 /// Rounding produces sharper rectangles.
129 ///
130 /// If `None`, [`crate::TessellationOptions::round_rects_to_pixels`] will be used.
131 #[inline]
132 pub fn with_round_to_pixels(mut self, round_to_pixels: bool) -> Self {
133 self.round_to_pixels = Some(round_to_pixels);
134 self
135 }
136
137 /// If larger than zero, the edges of the rectangle
138 /// (for both fill and stroke) will be blurred.
139 ///
140 /// This can be used to produce shadows and glow effects.
141 ///
142 /// The blur is currently implemented using a simple linear blur in `sRGBA` gamma space.
143 #[inline]
144 pub fn with_blur_width(mut self, blur_width: f32) -> Self {
145 self.blur_width = blur_width;
146 self
147 }
148
149 /// Set the texture to use when painting this rectangle, if any.
150 #[inline]
151 pub fn with_texture(mut self, fill_texture_id: TextureId, uv: Rect) -> Self {
152 self.brush = Some(Arc::new(Brush {
153 fill_texture_id,
154 uv,
155 }));
156 self
157 }
158
159 /// The visual bounding rectangle (includes stroke width)
160 #[inline]
161 pub fn visual_bounding_rect(&self) -> Rect {
162 if self.fill == Color32::TRANSPARENT && self.stroke.is_empty() {
163 Rect::NOTHING
164 } else {
165 let expand = match self.stroke_kind {
166 StrokeKind::Inside => 0.0,
167 StrokeKind::Middle => self.stroke.width / 2.0,
168 StrokeKind::Outside => self.stroke.width,
169 };
170 self.rect.expand(expand + self.blur_width / 2.0)
171 }
172 }
173
174 /// The texture to use when painting this rectangle, if any.
175 ///
176 /// If no texture is set, this will return [`TextureId::default`].
177 pub fn fill_texture_id(&self) -> TextureId {
178 self.brush
179 .as_ref()
180 .map_or_else(TextureId::default, |brush| brush.fill_texture_id)
181 }
182}
183
184impl From<RectShape> for Shape {
185 #[inline(always)]
186 fn from(shape: RectShape) -> Self {
187 Self::Rect(shape)
188 }
189}