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}