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}