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}