epaint/shapes/
rect_shape.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
use std::sync::Arc;

use crate::*;

/// How to paint a rectangle.
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
pub struct RectShape {
    pub rect: Rect,

    /// How rounded the corners of the rectangle are.
    ///
    /// Use [`CornerRadius::ZERO`] for for sharp corners.
    ///
    /// This is the corner radii of the rectangle.
    /// If there is a stroke, then the stroke will have an inner and outer corner radius,
    /// and those will depend on [`StrokeKind`] and the stroke width.
    ///
    /// For [`StrokeKind::Inside`], the outside of the stroke coincides with the rectangle,
    /// so the rounding will in this case specify the outer corner radius.
    pub corner_radius: CornerRadius,

    /// How to fill the rectangle.
    pub fill: Color32,

    /// The thickness and color of the outline.
    ///
    /// Whether or not the stroke is inside or outside the edge of [`Self::rect`],
    /// is controlled by [`Self::stroke_kind`].
    pub stroke: Stroke,

    /// Is the stroke on the inside, outside, or centered on the rectangle?
    ///
    /// If you want to perfectly tile rectangles, use [`StrokeKind::Inside`].
    pub stroke_kind: StrokeKind,

    /// Snap the rectangle to pixels?
    ///
    /// Rounding produces sharper rectangles.
    ///
    /// If `None`, [`crate::TessellationOptions::round_rects_to_pixels`] will be used.
    pub round_to_pixels: Option<bool>,

    /// If larger than zero, the edges of the rectangle
    /// (for both fill and stroke) will be blurred.
    ///
    /// This can be used to produce shadows and glow effects.
    ///
    /// The blur is currently implemented using a simple linear blur in sRGBA gamma space.
    pub blur_width: f32,

    /// Controls texturing, if any.
    ///
    /// Since most rectangles do not have a texture, this is optional and in an `Arc`,
    /// so that [`RectShape`] is kept small..
    pub brush: Option<Arc<Brush>>,
}

#[test]
fn rect_shape_size() {
    assert_eq!(
        std::mem::size_of::<RectShape>(), 48,
        "RectShape changed size! If it shrank - good! Update this test. If it grew - bad! Try to find a way to avoid it."
    );
    assert!(
        std::mem::size_of::<RectShape>() <= 64,
        "RectShape is getting way too big!"
    );
}

impl RectShape {
    /// See also [`Self::filled`] and [`Self::stroke`].
    #[inline]
    pub fn new(
        rect: Rect,
        corner_radius: impl Into<CornerRadius>,
        fill_color: impl Into<Color32>,
        stroke: impl Into<Stroke>,
        stroke_kind: StrokeKind,
    ) -> Self {
        Self {
            rect,
            corner_radius: corner_radius.into(),
            fill: fill_color.into(),
            stroke: stroke.into(),
            stroke_kind,
            round_to_pixels: None,
            blur_width: 0.0,
            brush: Default::default(),
        }
    }

    #[inline]
    pub fn filled(
        rect: Rect,
        corner_radius: impl Into<CornerRadius>,
        fill_color: impl Into<Color32>,
    ) -> Self {
        Self::new(
            rect,
            corner_radius,
            fill_color,
            Stroke::NONE,
            StrokeKind::Outside, // doesn't matter
        )
    }

    #[inline]
    pub fn stroke(
        rect: Rect,
        corner_radius: impl Into<CornerRadius>,
        stroke: impl Into<Stroke>,
        stroke_kind: StrokeKind,
    ) -> Self {
        let fill = Color32::TRANSPARENT;
        Self::new(rect, corner_radius, fill, stroke, stroke_kind)
    }

    /// Set if the stroke is on the inside, outside, or centered on the rectangle.
    #[inline]
    pub fn with_stroke_kind(mut self, stroke_kind: StrokeKind) -> Self {
        self.stroke_kind = stroke_kind;
        self
    }

    /// Snap the rectangle to pixels?
    ///
    /// Rounding produces sharper rectangles.
    ///
    /// If `None`, [`crate::TessellationOptions::round_rects_to_pixels`] will be used.
    #[inline]
    pub fn with_round_to_pixels(mut self, round_to_pixels: bool) -> Self {
        self.round_to_pixels = Some(round_to_pixels);
        self
    }

    /// If larger than zero, the edges of the rectangle
    /// (for both fill and stroke) will be blurred.
    ///
    /// This can be used to produce shadows and glow effects.
    ///
    /// The blur is currently implemented using a simple linear blur in `sRGBA` gamma space.
    #[inline]
    pub fn with_blur_width(mut self, blur_width: f32) -> Self {
        self.blur_width = blur_width;
        self
    }

    /// Set the texture to use when painting this rectangle, if any.
    #[inline]
    pub fn with_texture(mut self, fill_texture_id: TextureId, uv: Rect) -> Self {
        self.brush = Some(Arc::new(Brush {
            fill_texture_id,
            uv,
        }));
        self
    }

    /// The visual bounding rectangle (includes stroke width)
    #[inline]
    pub fn visual_bounding_rect(&self) -> Rect {
        if self.fill == Color32::TRANSPARENT && self.stroke.is_empty() {
            Rect::NOTHING
        } else {
            let expand = match self.stroke_kind {
                StrokeKind::Inside => 0.0,
                StrokeKind::Middle => self.stroke.width / 2.0,
                StrokeKind::Outside => self.stroke.width,
            };
            self.rect.expand(expand + self.blur_width / 2.0)
        }
    }

    /// The texture to use when painting this rectangle, if any.
    ///
    /// If no texture is set, this will return [`TextureId::default`].
    pub fn fill_texture_id(&self) -> TextureId {
        self.brush
            .as_ref()
            .map_or_else(TextureId::default, |brush| brush.fill_texture_id)
    }
}

impl From<RectShape> for Shape {
    #[inline(always)]
    fn from(shape: RectShape) -> Self {
        Self::Rect(shape)
    }
}