gsk4/
rounded_rect.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::mem;
4
5use crate::ffi;
6use glib::translate::*;
7use graphene::{Point, Rect, Size};
8
9glib::wrapper! {
10    /// A rectangular region with rounded corners.
11    ///
12    /// Application code should normalize rectangles using
13    /// [`normalize()`][Self::normalize()]; this function will ensure that
14    /// the bounds of the rectangle are normalized and ensure that the corner
15    /// values are positive and the corners do not overlap.
16    ///
17    /// All functions taking a [`RoundedRect`][crate::RoundedRect] as an argument will internally
18    /// operate on a normalized copy; all functions returning a [`RoundedRect`][crate::RoundedRect]
19    /// will always return a normalized one.
20    ///
21    /// The algorithm used for normalizing corner sizes is described in
22    /// [the CSS specification](https://drafts.csswg.org/css-backgrounds-3/#border-radius).
23    #[doc(alias = "GskRoundedRect")]
24    pub struct RoundedRect(BoxedInline<ffi::GskRoundedRect>);
25}
26
27impl RoundedRect {
28    /// Initializes a rounded rectangle with the given values.
29    ///
30    /// This function will implicitly normalize the rounded rectangle
31    /// before returning.
32    /// ## `bounds`
33    /// a [`graphene::Rect`][crate::graphene::Rect] describing the bounds
34    /// ## `top_left`
35    /// the rounding radius of the top left corner
36    /// ## `top_right`
37    /// the rounding radius of the top right corner
38    /// ## `bottom_right`
39    /// the rounding radius of the bottom right corner
40    /// ## `bottom_left`
41    /// the rounding radius of the bottom left corner
42    ///
43    /// # Returns
44    ///
45    /// the initialized rounded rectangle
46    #[doc(alias = "gsk_rounded_rect_init")]
47    pub fn new(
48        bounds: Rect,
49        top_left: Size,
50        top_right: Size,
51        bottom_right: Size,
52        bottom_left: Size,
53    ) -> Self {
54        assert_initialized_main_thread!();
55        unsafe {
56            let mut rounded_rect = mem::MaybeUninit::uninit();
57            ffi::gsk_rounded_rect_init(
58                rounded_rect.as_mut_ptr(),
59                bounds.to_glib_none().0,
60                top_left.to_glib_none().0,
61                top_right.to_glib_none().0,
62                bottom_right.to_glib_none().0,
63                bottom_left.to_glib_none().0,
64            );
65            Self::unsafe_from(rounded_rect.assume_init())
66        }
67    }
68
69    /// Initializes a rounded rectangle to the given bounds
70    /// and sets the radius of all four corners equally.
71    /// ## `bounds`
72    /// a [`graphene::Rect`][crate::graphene::Rect]
73    /// ## `radius`
74    /// the border radius
75    ///
76    /// # Returns
77    ///
78    /// the initialized rounded rectangle
79    #[doc(alias = "gsk_rounded_rect_init_from_rect")]
80    #[doc(alias = "init_from_rect")]
81    pub fn from_rect(bounds: Rect, radius: f32) -> Self {
82        assert_initialized_main_thread!();
83        unsafe {
84            let mut rounded_rect = mem::MaybeUninit::uninit();
85            ffi::gsk_rounded_rect_init_from_rect(
86                rounded_rect.as_mut_ptr(),
87                bounds.to_glib_none().0,
88                radius,
89            );
90            Self::unsafe_from(rounded_rect.assume_init())
91        }
92    }
93
94    /// Normalizes a rounded rectangle.
95    ///
96    /// This function will ensure that the bounds of the rounded rectangle
97    /// are normalized and ensure that the corner values are positive
98    /// and the corners do not overlap.
99    ///
100    /// # Returns
101    ///
102    /// the normalized rounded rectangle
103    #[doc(alias = "gsk_rounded_rect_normalize")]
104    pub fn normalize(&mut self) {
105        unsafe {
106            ffi::gsk_rounded_rect_normalize(&mut self.inner);
107        }
108    }
109
110    /// Offsets the rounded rectangle's origin by @dx and @dy.
111    ///
112    /// The size and corners of the rounded rectangle are unchanged.
113    /// ## `dx`
114    /// the horizontal offset
115    /// ## `dy`
116    /// the vertical offset
117    ///
118    /// # Returns
119    ///
120    /// the offset rounded rectangle
121    #[doc(alias = "gsk_rounded_rect_offset")]
122    pub fn offset(&mut self, dx: f32, dy: f32) {
123        unsafe {
124            ffi::gsk_rounded_rect_offset(&mut self.inner, dx, dy);
125        }
126    }
127
128    /// Shrinks (or grows) a rounded rectangle by moving the 4 sides
129    /// according to the offsets given.
130    ///
131    /// The corner radii will be changed in a way that tries to keep
132    /// the center of the corner circle intact. This emulates CSS behavior.
133    ///
134    /// This function also works for growing rounded rectangles
135    /// if you pass negative values for the @top, @right, @bottom or @left.
136    /// ## `top`
137    /// how far to move the top side downwards
138    /// ## `right`
139    /// how far to move the right side to the left
140    /// ## `bottom`
141    /// how far to move the bottom side upwards
142    /// ## `left`
143    /// how far to move the left side to the right
144    ///
145    /// # Returns
146    ///
147    /// the resized rounded rectangle
148    #[doc(alias = "gsk_rounded_rect_shrink")]
149    pub fn shrink(&mut self, top: f32, right: f32, bottom: f32, left: f32) {
150        unsafe {
151            ffi::gsk_rounded_rect_shrink(&mut self.inner, top, right, bottom, left);
152        }
153    }
154
155    /// Checks if all corners of a rounded rectangle are right angles
156    /// and the rectangle covers all of its bounds.
157    ///
158    /// This information can be used to decide if [`ClipNode::new()`][crate::ClipNode::new()]
159    /// or [`RoundedClipNode::new()`][crate::RoundedClipNode::new()] should be called.
160    ///
161    /// # Returns
162    ///
163    /// true if the rounded rectangle is rectilinear
164    #[doc(alias = "gsk_rounded_rect_is_rectilinear")]
165    pub fn is_rectilinear(&self) -> bool {
166        unsafe { from_glib(ffi::gsk_rounded_rect_is_rectilinear(&self.inner)) }
167    }
168
169    /// Checks if the given point is inside the rounded rectangle.
170    /// ## `point`
171    /// the point to check
172    ///
173    /// # Returns
174    ///
175    /// true if the point is inside the rounded rectangle
176    #[doc(alias = "gsk_rounded_rect_contains_point")]
177    pub fn contains_point(&self, point: Point) -> bool {
178        unsafe {
179            from_glib(ffi::gsk_rounded_rect_contains_point(
180                &self.inner,
181                point.to_glib_none().0,
182            ))
183        }
184    }
185
186    /// Checks if the given rectangle is contained inside the rounded rectangle.
187    /// ## `rect`
188    /// the rectangle to check
189    ///
190    /// # Returns
191    ///
192    /// true if the @rect is fully contained inside the rounded rectangle
193    #[doc(alias = "gsk_rounded_rect_contains_rect")]
194    pub fn contains_rect(&self, rect: Rect) -> bool {
195        unsafe {
196            from_glib(ffi::gsk_rounded_rect_contains_rect(
197                &self.inner,
198                rect.to_glib_none().0,
199            ))
200        }
201    }
202
203    /// Checks if part a rectangle is contained
204    /// inside the rounded rectangle.
205    /// ## `rect`
206    /// the rectangle to check
207    ///
208    /// # Returns
209    ///
210    /// true if the @rect intersects with the rounded rectangle
211    #[doc(alias = "gsk_rounded_rect_intersects_rect")]
212    pub fn intersects_rect(&self, rect: Rect) -> bool {
213        unsafe {
214            from_glib(ffi::gsk_rounded_rect_intersects_rect(
215                &self.inner,
216                rect.to_glib_none().0,
217            ))
218        }
219    }
220
221    #[inline]
222    pub fn bounds(&self) -> &graphene::Rect {
223        unsafe {
224            &*(&self.inner.bounds as *const graphene::ffi::graphene_rect_t as *const graphene::Rect)
225        }
226    }
227
228    #[inline]
229    pub fn corner(&self) -> &[graphene::Size; 4] {
230        unsafe {
231            &*(&self.inner.corner as *const [graphene::ffi::graphene_size_t; 4]
232                as *const [graphene::Size; 4])
233        }
234    }
235}
236
237impl std::fmt::Debug for RoundedRect {
238    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
239        f.debug_struct("RoundedRect")
240            .field("is_rectilinear", &self.is_rectilinear())
241            .field("bounds", &self.bounds())
242            .field("corner", &self.corner())
243            .finish()
244    }
245}