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>(),
63 48,
64 "RectShape changed size! If it shrank - good! Update this test. If it grew - bad! Try to find a way to avoid it."
65 );
66 assert!(
67 std::mem::size_of::<RectShape>() <= 64,
68 "RectShape is getting way too big!"
69 );
70}
71
72impl RectShape {
73 /// See also [`Self::filled`] and [`Self::stroke`].
74 #[inline]
75 pub fn new(
76 rect: Rect,
77 corner_radius: impl Into<CornerRadius>,
78 fill_color: impl Into<Color32>,
79 stroke: impl Into<Stroke>,
80 stroke_kind: StrokeKind,
81 ) -> Self {
82 Self {
83 rect,
84 corner_radius: corner_radius.into(),
85 fill: fill_color.into(),
86 stroke: stroke.into(),
87 stroke_kind,
88 round_to_pixels: None,
89 blur_width: 0.0,
90 brush: Default::default(),
91 }
92 }
93
94 #[inline]
95 pub fn filled(
96 rect: Rect,
97 corner_radius: impl Into<CornerRadius>,
98 fill_color: impl Into<Color32>,
99 ) -> Self {
100 Self::new(
101 rect,
102 corner_radius,
103 fill_color,
104 Stroke::NONE,
105 StrokeKind::Outside, // doesn't matter
106 )
107 }
108
109 #[inline]
110 pub fn stroke(
111 rect: Rect,
112 corner_radius: impl Into<CornerRadius>,
113 stroke: impl Into<Stroke>,
114 stroke_kind: StrokeKind,
115 ) -> Self {
116 let fill = Color32::TRANSPARENT;
117 Self::new(rect, corner_radius, fill, stroke, stroke_kind)
118 }
119
120 /// Set if the stroke is on the inside, outside, or centered on the rectangle.
121 #[inline]
122 pub fn with_stroke_kind(mut self, stroke_kind: StrokeKind) -> Self {
123 self.stroke_kind = stroke_kind;
124 self
125 }
126
127 /// Snap the rectangle to pixels?
128 ///
129 /// Rounding produces sharper rectangles.
130 ///
131 /// If `None`, [`crate::TessellationOptions::round_rects_to_pixels`] will be used.
132 #[inline]
133 pub fn with_round_to_pixels(mut self, round_to_pixels: bool) -> Self {
134 self.round_to_pixels = Some(round_to_pixels);
135 self
136 }
137
138 /// If larger than zero, the edges of the rectangle
139 /// (for both fill and stroke) will be blurred.
140 ///
141 /// This can be used to produce shadows and glow effects.
142 ///
143 /// The blur is currently implemented using a simple linear blur in `sRGBA` gamma space.
144 #[inline]
145 pub fn with_blur_width(mut self, blur_width: f32) -> Self {
146 self.blur_width = blur_width;
147 self
148 }
149
150 /// Set the texture to use when painting this rectangle, if any.
151 #[inline]
152 pub fn with_texture(mut self, fill_texture_id: TextureId, uv: Rect) -> Self {
153 self.brush = Some(Arc::new(Brush {
154 fill_texture_id,
155 uv,
156 }));
157 self
158 }
159
160 /// The visual bounding rectangle (includes stroke width)
161 #[inline]
162 pub fn visual_bounding_rect(&self) -> Rect {
163 if self.fill == Color32::TRANSPARENT && self.stroke.is_empty() {
164 Rect::NOTHING
165 } else {
166 let expand = match self.stroke_kind {
167 StrokeKind::Inside => 0.0,
168 StrokeKind::Middle => self.stroke.width / 2.0,
169 StrokeKind::Outside => self.stroke.width,
170 };
171 self.rect.expand(expand + self.blur_width / 2.0)
172 }
173 }
174
175 /// The texture to use when painting this rectangle, if any.
176 ///
177 /// If no texture is set, this will return [`TextureId::default`].
178 pub fn fill_texture_id(&self) -> TextureId {
179 self.brush
180 .as_ref()
181 .map_or_else(TextureId::default, |brush| brush.fill_texture_id)
182 }
183}
184
185impl From<RectShape> for Shape {
186 #[inline(always)]
187 fn from(shape: RectShape) -> Self {
188 Self::Rect(shape)
189 }
190}