pixels_graphics_lib/ui/layout/
relative.rs

1#[cfg(feature = "serde")]
2use serde::{Deserialize, Serialize};
3
4use crate::prelude::{coord, Rect, Shape};
5use crate::ui::layout::LayoutView;
6use crate::ui::PixelView;
7
8#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
9#[derive(Debug, PartialEq, Clone, Copy)]
10pub enum LayoutOffset {
11    Zero,
12    Pixels(isize),
13    /// percentage of parent
14    Percent(f32),
15}
16
17impl LayoutOffset {
18    pub fn calc(&self, total: usize) -> isize {
19        match self {
20            LayoutOffset::Zero => 0,
21            LayoutOffset::Pixels(px) => *px,
22            LayoutOffset::Percent(percent) => ((total as f32) * percent).round() as isize,
23        }
24    }
25}
26
27#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
28#[derive(Debug, Clone, PartialEq)]
29pub struct LayoutContext {
30    pub bounds: Rect,
31    pub default_offset: LayoutOffset,
32}
33
34impl LayoutContext {
35    pub fn new(bounds: Rect) -> LayoutContext {
36        LayoutContext {
37            bounds,
38            default_offset: LayoutOffset::Zero,
39        }
40    }
41
42    pub fn new_with_padding(bounds: Rect, padding: usize) -> LayoutContext {
43        LayoutContext {
44            bounds,
45            default_offset: LayoutOffset::Pixels(padding as isize),
46        }
47    }
48}
49
50#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
51pub enum ViewLayoutRule {
52    LeftToLeft,
53    RightToRight,
54    LeftToRight,
55    RightToLeft,
56    TopToTop,
57    BottomToBottom,
58    TopToBottom,
59    BottomToTop,
60    CenterHToLeft,
61    CenterHToRight,
62    CenterHToCenterH,
63    LeftToCenterH,
64    RightToCenterH,
65    CenterVToTop,
66    CenterVToBottom,
67    TopToCenterV,
68    BottomToCenterV,
69    CenterVToCenterV,
70}
71
72#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
73pub enum ParentLayoutRule {
74    FromLeft,
75    FromRight,
76    FromTop,
77    FromBottom,
78    FromCenterH,
79    FromCenterV,
80    FillVert,
81    FillHorz,
82}
83
84trait UpdateRect {
85    fn u_left(&self, value: isize) -> Rect;
86    fn u_top(&self, value: isize) -> Rect;
87    fn u_bottom(&self, value: isize) -> Rect;
88    fn u_right(&self, value: isize) -> Rect;
89    fn u_horz(&self, left: isize, right: isize) -> Rect;
90    fn u_vert(&self, top: isize, bottom: isize) -> Rect;
91}
92
93impl UpdateRect for Rect {
94    fn u_left(&self, value: isize) -> Rect {
95        Rect::new((value, self.top()), self.bottom_right())
96    }
97
98    fn u_top(&self, value: isize) -> Rect {
99        Rect::new((self.left(), value), self.bottom_right())
100    }
101
102    fn u_bottom(&self, value: isize) -> Rect {
103        Rect::new(self.top_left(), (self.right(), value))
104    }
105
106    fn u_right(&self, value: isize) -> Rect {
107        Rect::new(self.top_left(), (value, self.bottom()))
108    }
109
110    fn u_horz(&self, left: isize, right: isize) -> Rect {
111        Rect::new((left, self.top()), (right, self.bottom()))
112    }
113
114    fn u_vert(&self, top: isize, bottom: isize) -> Rect {
115        Rect::new((self.left(), top), (self.right(), bottom))
116    }
117}
118
119pub fn move_by_view(
120    parent: &Rect,
121    view: &mut dyn PixelView,
122    pivot: &dyn PixelView,
123    rule: ViewLayoutRule,
124    offset: LayoutOffset,
125) {
126    match rule {
127        ViewLayoutRule::LeftToLeft => view.set_position(coord!(
128            pivot.bounds().left() + offset.calc(parent.width()),
129            view.bounds().top(),
130        )),
131        ViewLayoutRule::RightToRight => view.set_position(coord!(
132            pivot.bounds().right() + offset.calc(parent.width()) - (view.bounds().width() as isize),
133            view.bounds().top(),
134        )),
135        ViewLayoutRule::LeftToRight => view.set_position(coord!(
136            pivot.bounds().right() + offset.calc(parent.width()),
137            view.bounds().top(),
138        )),
139        ViewLayoutRule::RightToLeft => view.set_position(coord!(
140            pivot.bounds().left() - offset.calc(parent.width()) - (view.bounds().width() as isize),
141            view.bounds().top()
142        )),
143        ViewLayoutRule::TopToTop => view.set_position(coord!(
144            view.bounds().left(),
145            pivot.bounds().top() + offset.calc(parent.height())
146        )),
147        ViewLayoutRule::BottomToBottom => view.set_position(coord!(
148            view.bounds().left(),
149            pivot.bounds().bottom()
150                - offset.calc(parent.height())
151                - (view.bounds().height() as isize)
152        )),
153        ViewLayoutRule::TopToBottom => view.set_position(coord!(
154            view.bounds().left(),
155            pivot.bounds().bottom() + offset.calc(parent.height())
156        )),
157        ViewLayoutRule::BottomToTop => view.set_position(coord!(
158            view.bounds().left(),
159            pivot.bounds().top() - offset.calc(parent.height()) - (view.bounds().height() as isize)
160        )),
161        ViewLayoutRule::CenterHToLeft => view.set_position(coord!(
162            pivot.bounds().left() + offset.calc(parent.width())
163                - (view.bounds().width() as isize / 2),
164            view.bounds().top()
165        )),
166        ViewLayoutRule::CenterHToRight => view.set_position(coord!(
167            pivot.bounds().left()
168                + offset.calc(parent.width())
169                + (view.bounds().width() as isize / 2),
170            view.bounds().top()
171        )),
172        ViewLayoutRule::LeftToCenterH => view.set_position(coord!(
173            pivot.bounds().center().x + offset.calc(parent.width()),
174            view.bounds().top()
175        )),
176        ViewLayoutRule::RightToCenterH => view.set_position(coord!(
177            pivot.bounds().center().x
178                - offset.calc(parent.width())
179                - (view.bounds().width() as isize),
180            view.bounds().top()
181        )),
182        ViewLayoutRule::CenterVToTop => view.set_position(coord!(
183            view.bounds().left(),
184            pivot.bounds().top() + offset.calc(parent.height())
185                - (view.bounds().height() as isize / 2)
186        )),
187        ViewLayoutRule::CenterVToBottom => view.set_position(coord!(
188            view.bounds().left(),
189            pivot.bounds().top()
190                + offset.calc(parent.height())
191                + (view.bounds().height() as isize / 2)
192        )),
193        ViewLayoutRule::TopToCenterV => view.set_position(coord!(
194            view.bounds().left(),
195            pivot.bounds().center().y + offset.calc(parent.height()),
196        )),
197        ViewLayoutRule::BottomToCenterV => view.set_position(coord!(
198            view.bounds().left(),
199            pivot.bounds().center().y + offset.calc(parent.height())
200                - (view.bounds().height() as isize),
201        )),
202        ViewLayoutRule::CenterHToCenterH => view.set_position(coord!(
203            pivot.bounds().center().x + offset.calc(parent.width())
204                - (view.bounds().width() as isize / 2),
205            view.bounds().top()
206        )),
207        ViewLayoutRule::CenterVToCenterV => view.set_position(coord!(
208            view.bounds().left(),
209            pivot.bounds().center().y
210                - offset.calc(parent.height())
211                - (view.bounds().height() as isize / 2),
212        )),
213    }
214}
215
216pub fn center_between(
217    parent: &Rect,
218    view: &mut dyn LayoutView,
219    first: &dyn PixelView,
220    second: &dyn PixelView,
221    horz: bool,
222    offset: LayoutOffset,
223) {
224    if horz {
225        let half_width = (view.bounds().width() / 2) as isize;
226        view.set_position(coord!(
227            first
228                .bounds()
229                .center()
230                .mid_point(second.bounds().center())
231                .x
232                - half_width
233                + offset.calc(parent.width()),
234            view.bounds().top()
235        ));
236    } else {
237        let half_height = (view.bounds().height() / 2) as isize;
238        view.set_position(coord!(
239            view.bounds().left(),
240            first
241                .bounds()
242                .center()
243                .mid_point(second.bounds().center())
244                .y
245                - half_height
246                + offset.calc(parent.height())
247        ));
248    }
249}
250
251pub fn grow_by_view(
252    parent: &Rect,
253    view: &mut dyn LayoutView,
254    pivot: &dyn PixelView,
255    rule: ViewLayoutRule,
256    offset: LayoutOffset,
257) {
258    match rule {
259        ViewLayoutRule::LeftToLeft => view.set_bounds(
260            view.bounds()
261                .u_left(pivot.bounds().left() + offset.calc(parent.width())),
262        ),
263        ViewLayoutRule::RightToRight => view.set_bounds(
264            view.bounds()
265                .u_right(pivot.bounds().right() - offset.calc(parent.width())),
266        ),
267        ViewLayoutRule::LeftToRight => view.set_bounds(
268            view.bounds()
269                .u_left(pivot.bounds().right() + offset.calc(parent.width())),
270        ),
271        ViewLayoutRule::RightToLeft => view.set_bounds(
272            view.bounds()
273                .u_right(pivot.bounds().left() - offset.calc(parent.width())),
274        ),
275        ViewLayoutRule::TopToTop => view.set_bounds(
276            view.bounds()
277                .u_top(pivot.bounds().top() + offset.calc(parent.height())),
278        ),
279        ViewLayoutRule::BottomToBottom => view.set_bounds(
280            view.bounds()
281                .u_bottom(pivot.bounds().bottom() - offset.calc(parent.height())),
282        ),
283        ViewLayoutRule::TopToBottom => view.set_bounds(
284            view.bounds()
285                .u_top(pivot.bounds().bottom() + offset.calc(parent.height())),
286        ),
287        ViewLayoutRule::BottomToTop => view.set_bounds(
288            view.bounds()
289                .u_bottom(pivot.bounds().top() - offset.calc(parent.height())),
290        ),
291        ViewLayoutRule::LeftToCenterH => view.set_bounds(
292            view.bounds()
293                .u_left(pivot.bounds().center().x + offset.calc(parent.width())),
294        ),
295        ViewLayoutRule::RightToCenterH => view.set_bounds(
296            view.bounds()
297                .u_right(pivot.bounds().center().x + offset.calc(parent.width())),
298        ),
299        ViewLayoutRule::TopToCenterV => view.set_bounds(
300            view.bounds()
301                .u_top(pivot.bounds().center().y + offset.calc(parent.width())),
302        ),
303        ViewLayoutRule::BottomToCenterV => view.set_bounds(
304            view.bounds()
305                .u_bottom(pivot.bounds().center().y + offset.calc(parent.width())),
306        ),
307        _ => {}
308    }
309}
310
311pub fn move_by_parent(
312    parent: &Rect,
313    view: &mut dyn PixelView,
314    rule: ParentLayoutRule,
315    offset: LayoutOffset,
316) {
317    match rule {
318        ParentLayoutRule::FromLeft => view.set_position(coord!(
319            parent.left() + offset.calc(parent.width()),
320            view.bounds().top(),
321        )),
322        ParentLayoutRule::FromRight => view.set_position(coord!(
323            parent.right() - (view.bounds().width() as isize) - offset.calc(parent.width()),
324            view.bounds().top(),
325        )),
326        ParentLayoutRule::FromTop => view.set_position(coord!(
327            view.bounds().left(),
328            parent.top() + offset.calc(parent.height()),
329        )),
330        ParentLayoutRule::FromBottom => view.set_position(coord!(
331            view.bounds().left(),
332            parent.bottom() - (view.bounds().height() as isize) - offset.calc(parent.height()),
333        )),
334        ParentLayoutRule::FromCenterH => view.set_position(coord!(
335            parent.center().x + offset.calc(parent.width()) - (view.bounds().width() as isize / 2),
336            view.bounds().top(),
337        )),
338        ParentLayoutRule::FromCenterV => view.set_position(coord!(
339            view.bounds().left(),
340            parent.center().y + offset.calc(parent.height())
341                - (view.bounds().height() as isize / 2),
342        )),
343        _ => {}
344    }
345}
346
347pub fn grow_by_parent(
348    parent: &Rect,
349    view: &mut dyn LayoutView,
350    rule: ParentLayoutRule,
351    offset: LayoutOffset,
352) {
353    match rule {
354        ParentLayoutRule::FromLeft => view.set_bounds(
355            view.bounds()
356                .u_left(parent.left() + offset.calc(parent.width())),
357        ),
358        ParentLayoutRule::FromRight => view.set_bounds(
359            view.bounds()
360                .u_right(parent.right() - offset.calc(parent.width())),
361        ),
362        ParentLayoutRule::FromTop => view.set_bounds(
363            view.bounds()
364                .u_top(parent.top() + offset.calc(parent.height())),
365        ),
366        ParentLayoutRule::FromBottom => view.set_bounds(
367            view.bounds()
368                .u_bottom(parent.bottom() - offset.calc(parent.height())),
369        ),
370        ParentLayoutRule::FillVert => view.set_bounds(view.bounds().u_vert(
371            parent.top() + offset.calc(parent.height()),
372            parent.bottom() - offset.calc(parent.height()),
373        )),
374        ParentLayoutRule::FillHorz => view.set_bounds(view.bounds().u_horz(
375            parent.left() + offset.calc(parent.width()),
376            parent.right() - offset.calc(parent.width()),
377        )),
378        ParentLayoutRule::FromCenterH => {
379            if view.bounds().center().x < parent.center().x {
380                view.set_bounds(
381                    view.bounds()
382                        .u_right(parent.center().x + offset.calc(parent.width())),
383                )
384            } else {
385                view.set_bounds(
386                    view.bounds()
387                        .u_left(parent.center().x - offset.calc(parent.width())),
388                )
389            }
390        }
391        ParentLayoutRule::FromCenterV => {
392            if view.bounds().center().y < parent.center().y {
393                view.set_bounds(
394                    view.bounds()
395                        .u_bottom(parent.center().y + offset.calc(parent.height())),
396                )
397            } else {
398                view.set_bounds(
399                    view.bounds()
400                        .u_top(parent.center().y - offset.calc(parent.height())),
401                )
402            }
403        }
404    }
405}
406
407/// Position and size a view relative to the parent or another view
408///
409/// # Format
410///
411/// layout!(context, [command] view, alignment [pivot_view],[second_pivot_view][, offset]);
412///
413/// Views must impl [PixelView] and to use `grow` they must also impl [LayoutView]
414///
415/// `offset` replaces the default offset from context (if it was set)
416///
417/// # Usage
418///
419/// ```ignore
420/// let mut view1 = Button::new(...);
421/// let mut view2 = Button::new(...);
422/// let context = LayoutContext::new(Rect::new((0,0), (200,200)));
423/// layout!(context, view1, left_to_left_of view2);
424/// ```
425///
426/// # Examples
427/// Move a button below another with some spacing
428/// ```ignore
429/// let button1 = Button::new(...);
430/// let button2 = Button::new(...);
431/// let context = LayoutContext::new(...);
432/// layout!(context, button2, top_to_bottom_of button1, px(8));
433/// ```
434///
435/// Move a button to the edge of the screen and grow it's right side to match another view
436/// ```ignore
437/// let button1 = Button::new(...);
438/// let button2 = Button::new(...);
439/// let context = LayoutContext::new(...);
440/// layout!(context, button2, align_left);
441/// layout!(context, grow button1, right_to_right_of button1);
442/// ```
443///
444/// # Command
445/// * `grow` - Moves the edge of the view, but not the position
446///
447/// # Alignment
448/// *View*
449/// * `left_to_left_of` - Makes view.x = pivot_view.x
450/// * `top_to_top_of` - Makes view.y = pivot_view.y
451/// * `right_to_right_of` - Makes view.x = pivot_view.right - view.width
452/// * `bottom_to_bottom_of` - Makes view.y = pivot_view.bottom - view.height
453/// * `left_to_right_of` - Makes view.x = pivot_view.x + pivot_view.width
454/// * `right_to_left_of` - Makes view.x = pivot_view.x - view.width
455/// * `top_to_bottom_of` - Makes view.y = pivot_view.bottom
456/// * `bottom_to_top_of` - Makes view.y = pivot_view.y - view.height
457/// * `centerh_to_centerh_of` - Makes view.center.x = pivot_view.center.x (`grow` not supported)
458/// * `centerv_to_centerv_of` - Makes view.center.y = pivot_view.center.y (`grow` not supported)
459/// * `centerv_to_top_of` - Makes view.center.y = pivot_view.y (`grow` not supported)
460/// * `centerv_to_bottom_of` - Makes view.center.y = pivot_view.bottom (`grow` not supported)
461/// * `centerh_to_left_of` - Makes view.center.x = pivot_view.left (`grow` not supported)
462/// * `centerh_to_right_of` - Makes view.center.x = pivot_view.right (`grow` not supported)
463/// * `left_to_centerh_of` - Makes view.x = pivot_view.center.x
464/// * `right_to_centerh_of` - Makes view.x = pivot_view.center.x - view.width
465/// * `top_to_centerv_of` - Makes view.y = pivot_view.center.y
466/// * `bottom_to_centerv_of` - Makes view.y = pivot_view.center.y - view.height
467/// * `center_between_horz` - Make view.center.x = pivot_view.right midpoint second_pivot_view.left
468/// * `center_between_vert` - Make view.center.y = pivot_view.bottom midpoint second_pivot_view.top
469///
470/// *Parent*
471/// * `fill_width` - Set x to context.left, width to context.width (`grow` only)
472/// * `fill_height` - Set y to context.top, height to context.height (`grow` only)
473/// * `align_left` - Set x to context.left
474/// * `align_right` - Set x to (context.right - view.width)
475/// * `align_top` - Set y to context.top
476/// * `align_bottom` - Set y to (context.bottom - view.height)
477/// * `align_centerh` - Set x to context.center.x (when using `grow` moves view.left or view.right instead)
478/// * `align_centerv` - Set y to context.center.y (when using `grow` moves view.top or view.bottom instead)
479#[macro_export]
480macro_rules! layout {
481    ($context:expr, $view:expr, left_to_left_of $pivot:expr $(, $offset:expr)?) => {
482        $crate::ui::layout::relative::move_by_view(&$context.bounds, &mut $view, &$pivot, $crate::ui::layout::relative::ViewLayoutRule::LeftToLeft, $crate::or_else!($($offset)?, $context.default_offset));
483    };
484    ($context:expr, $view:expr, left_to_right_of $pivot:expr $(, $offset:expr)?) => {
485        $crate::ui::layout::relative::move_by_view(&$context.bounds, &mut $view, &$pivot, $crate::ui::layout::relative::ViewLayoutRule::LeftToRight, $crate::or_else!($($offset)?, $context.default_offset));
486    };
487    ($context:expr, $view:expr, right_to_left_of $pivot:expr $(, $offset:expr)?) => {
488        $crate::ui::layout::relative::move_by_view(&$context.bounds, &mut $view, &$pivot, $crate::ui::layout::relative::ViewLayoutRule::RightToLeft, $crate::or_else!($($offset)?, $context.default_offset));
489    };
490    ($context:expr, $view:expr, top_to_bottom_of $pivot:expr $(, $offset:expr)?) => {
491        $crate::ui::layout::relative::move_by_view(&$context.bounds, &mut $view, &$pivot, $crate::ui::layout::relative::ViewLayoutRule::TopToBottom, $crate::or_else!($($offset)?, $context.default_offset));
492    };
493    ($context:expr, $view:expr, bottom_to_top_of $pivot:expr $(, $offset:expr)?) => {
494        $crate::ui::layout::relative::move_by_view(&$context.bounds, &mut $view, &$pivot, $crate::ui::layout::relative::ViewLayoutRule::BottomToTop, $crate::or_else!($($offset)?, $context.default_offset));
495    };
496    ($context:expr, $view:expr, top_to_top_of $pivot:expr $(, $offset:expr)?) => {
497        $crate::ui::layout::relative::move_by_view(&$context.bounds, &mut $view, &$pivot, $crate::ui::layout::relative::ViewLayoutRule::TopToTop, $crate::or_else!($($offset)?, $context.default_offset));
498    };
499    ($context:expr, $view:expr, bottom_to_bottom_of $pivot:expr $(, $offset:expr)?) => {
500        $crate::ui::layout::relative::move_by_view(&$context.bounds, &mut $view, &$pivot, $crate::ui::layout::relative::ViewLayoutRule::BottomToBottom, $crate::or_else!($($offset)?, $context.default_offset));
501    };
502    ($context:expr, $view:expr, right_to_right_of $pivot:expr $(, $offset:expr)?) => {
503        $crate::ui::layout::relative::move_by_view(&$context.bounds, &mut $view, &$pivot, $crate::ui::layout::relative::ViewLayoutRule::RightToRight, $crate::or_else!($($offset)?, $context.default_offset));
504    };
505    ($context:expr, grow $view:expr, right_to_right_of $pivot:expr $(, $offset:expr)?) => {
506        $crate::ui::layout::relative::grow_by_view(&$context.bounds, &mut $view, &$pivot, $crate::ui::layout::relative::ViewLayoutRule::RightToRight, $crate::or_else!($($offset)?, $context.default_offset));
507    };
508    ($context:expr, grow $view:expr, right_to_left_of $pivot:expr $(, $offset:expr)?) => {
509        $crate::ui::layout::relative::grow_by_view(&$context.bounds, &mut $view, &$pivot, $crate::ui::layout::relative::ViewLayoutRule::RightToLeft, $crate::or_else!($($offset)?, $context.default_offset));
510    };
511    ($context:expr, grow $view:expr, bottom_to_bottom_of $pivot:expr $(, $offset:expr)?) => {
512        $crate::ui::layout::relative::grow_by_view(&$context.bounds, &mut $view, &$pivot, $crate::ui::layout::relative::ViewLayoutRule::BottomToBottom, $crate::or_else!($($offset)?, $context.default_offset));
513    };
514    ($context:expr, grow $view:expr, bottom_to_top_of $pivot:expr $(, $offset:expr)?) => {
515        $crate::ui::layout::relative::grow_by_view(&$context.bounds, &mut $view, &$pivot, $crate::ui::layout::relative::ViewLayoutRule::BottomToTop, $crate::or_else!($($offset)?, $context.default_offset));
516    };
517    ($context:expr, grow $view:expr, left_to_left_of $pivot:expr $(, $offset:expr)?) => {
518        $crate::ui::layout::relative::grow_by_view(&$context.bounds, &mut $view, &$pivot, $crate::ui::layout::relative::ViewLayoutRule::LeftToLeft, $crate::or_else!($($offset)?, $context.default_offset));
519    };
520    ($context:expr, grow $view:expr, left_to_right_of $pivot:expr $(, $offset:expr)?) => {
521        $crate::ui::layout::relative::grow_by_view(&$context.bounds, &mut $view, &$pivot, $crate::ui::layout::relative::ViewLayoutRule::LeftToRight, $crate::or_else!($($offset)?, $context.default_offset));
522    };
523    ($context:expr, grow $view:expr, top_to_top_of $pivot:expr $(, $offset:expr)?) => {
524        $crate::ui::layout::relative::grow_by_view(&$context.bounds, &mut $view, &$pivot, $crate::ui::layout::relative::ViewLayoutRule::TopToTop, $crate::or_else!($($offset)?, $context.default_offset));
525    };
526    ($context:expr, $view:expr, centerh_to_centerh_of $pivot:expr $(, $offset:expr)?) => {
527        $crate::ui::layout::relative::move_by_view(&$context.bounds, &mut $view, &$pivot, $crate::ui::layout::relative::ViewLayoutRule::CenterHToCenterH, $crate::or_else!($($offset)?, $context.default_offset));
528    };
529    ($context:expr, $view:expr, centerh_to_left_of $pivot:expr $(, $offset:expr)?) => {
530        $crate::ui::layout::relative::move_by_view(&$context.bounds, &mut $view, &$pivot, $crate::ui::layout::relative::ViewLayoutRule::CenterHToLeft, $crate::or_else!($($offset)?, $context.default_offset));
531    };
532    ($context:expr, $view:expr, centerh_to_right_of $pivot:expr $(, $offset:expr)?) => {
533        $crate::ui::layout::relative::move_by_view(&$context.bounds, &mut $view, &$pivot, $crate::ui::layout::relative::ViewLayoutRule::CenterHToRight, $crate::or_else!($($offset)?, $context.default_offset));
534    };
535    ($context:expr, $view:expr, centerv_to_centerv_of $pivot:expr $(, $offset:expr)?) => {
536        $crate::ui::layout::relative::move_by_view(&$context.bounds, &mut $view, &$pivot, $crate::ui::layout::relative::ViewLayoutRule::CenterVToCenterV, $crate::or_else!($($offset)?, $context.default_offset));
537    };
538    ($context:expr, $view:expr, centerv_to_top_of $pivot:expr $(, $offset:expr)?) => {
539        $crate::ui::layout::relative::move_by_view(&$context.bounds, &mut $view, &$pivot, $crate::ui::layout::relative::ViewLayoutRule::CenterVToTop, $crate::or_else!($($offset)?, $context.default_offset));
540    };
541    ($context:expr, $view:expr, centerv_to_bottom_of $pivot:expr $(, $offset:expr)?) => {
542        $crate::ui::layout::relative::move_by_view(&$context.bounds, &mut $view, &$pivot, $crate::ui::layout::relative::ViewLayoutRule::CenterVToBottom, $crate::or_else!($($offset)?, $context.default_offset));
543    };
544    ($context:expr, $view:expr, left_to_centerh_of $pivot:expr $(, $offset:expr)?) => {
545        $crate::ui::layout::relative::move_by_view(&$context.bounds, &mut $view, &$pivot, $crate::ui::layout::relative::ViewLayoutRule::LeftToCenterH, $crate::or_else!($($offset)?, $context.default_offset));
546    };
547    ($context:expr, $view:expr, right_to_centerh_of $pivot:expr $(, $offset:expr)?) => {
548        $crate::ui::layout::relative::move_by_view(&$context.bounds, &mut $view, &$pivot, $crate::ui::layout::relative::ViewLayoutRule::RightToCenterH, $crate::or_else!($($offset)?, $context.default_offset));
549    };
550    ($context:expr, grow $view:expr, left_to_centerh_of $pivot:expr $(, $offset:expr)?) => {
551        $crate::ui::layout::relative::grow_by_view(&$context.bounds, &mut $view, &$pivot, $crate::ui::layout::relative::ViewLayoutRule::LeftToCenterH, $crate::or_else!($($offset)?, $context.default_offset));
552    };
553    ($context:expr, grow $view:expr, right_to_centerh_of $pivot:expr $(, $offset:expr)?) => {
554        $crate::ui::layout::relative::grow_by_view(&$context.bounds, &mut $view, &$pivot, $crate::ui::layout::relative::ViewLayoutRule::RightToCenterH, $crate::or_else!($($offset)?, $context.default_offset));
555    };
556    ($context:expr, $view:expr, top_to_centerv_of $pivot:expr $(, $offset:expr)?) => {
557        $crate::ui::layout::relative::move_by_view(&$context.bounds, &mut $view, &$pivot, $crate::ui::layout::relative::ViewLayoutRule::TopToCenterV, $crate::or_else!($($offset)?, $context.default_offset));
558    };
559    ($context:expr, $view:expr, bottom_to_centerv_of $pivot:expr $(, $offset:expr)?) => {
560        $crate::ui::layout::relative::move_by_view(&$context.bounds, &mut $view, &$pivot, $crate::ui::layout::relative::ViewLayoutRule::BottomToCenterV, $crate::or_else!($($offset)?, $context.default_offset));
561    };
562    ($context:expr, grow $view:expr, top_to_centerv_of $pivot:expr $(, $offset:expr)?) => {
563        $crate::ui::layout::relative::grow_by_view(&$context.bounds, &mut $view, &$pivot, $crate::ui::layout::relative::ViewLayoutRule::TopToCenterV, $crate::or_else!($($offset)?, $context.default_offset));
564    };
565    ($context:expr, grow $view:expr, bottom_to_centerv_of $pivot:expr $(, $offset:expr)?) => {
566        $crate::ui::layout::relative::grow_by_view(&$context.bounds, &mut $view, &$pivot, $crate::ui::layout::relative::ViewLayoutRule::BottomToCenterV, $crate::or_else!($($offset)?, $context.default_offset));
567    };
568    ($context:expr, grow $view:expr, top_to_bottom_of $pivot:expr $(, $offset:expr)?) => {
569        $crate::ui::layout::relative::grow_by_view(&$context.bounds, &mut $view, &$pivot, $crate::ui::layout::relative::ViewLayoutRule::TopToBottom, $crate::or_else!($($offset)?, $context.default_offset));
570    };
571    ($context:expr, grow $view:expr, fill_width $(, $offset:expr)?) => {
572        $crate::ui::layout::relative::grow_by_parent(&$context.bounds, &mut $view, $crate::ui::layout::relative::ParentLayoutRule::FillHorz, $crate::or_else!($($offset)?, $context.default_offset));
573    };
574    ($context:expr, grow $view:expr, fill_height $(, $offset:expr)?) => {
575        $crate::ui::layout::relative::grow_by_parent(&$context.bounds, &mut $view, $crate::ui::layout::relative::ParentLayoutRule::FillVert, $crate::or_else!($($offset)?, $context.default_offset));
576    };
577    ($context:expr, grow $view:expr, align_left $(, $offset:expr)?) => {
578        $crate::ui::layout::relative::grow_by_parent(&$context.bounds, &mut $view, $crate::ui::layout::relative::ParentLayoutRule::FromLeft, $crate::or_else!($($offset)?, $context.default_offset));
579    };
580    ($context:expr, grow $view:expr, align_top $(, $offset:expr)?) => {
581        $crate::ui::layout::relative::grow_by_parent(&$context.bounds, &mut $view, $crate::ui::layout::relative::ParentLayoutRule::FromTop, $crate::or_else!($($offset)?, $context.default_offset));
582    };
583    ($context:expr, grow $view:expr, align_right $(, $offset:expr)?) => {
584        $crate::ui::layout::relative::grow_by_parent(&$context.bounds, &mut $view, $crate::ui::layout::relative::ParentLayoutRule::FromRight, $crate::or_else!($($offset)?, $context.default_offset));
585    };
586    ($context:expr, grow $view:expr, align_bottom $(, $offset:expr)?) => {
587        $crate::ui::layout::relative::grow_by_parent(&$context.bounds, &mut $view, $crate::ui::layout::relative::ParentLayoutRule::FromBottom, $crate::or_else!($($offset)?, $context.default_offset));
588    };
589    ($context:expr, $view:expr, align_left $(, $offset:expr)?) => {
590        $crate::ui::layout::relative::move_by_parent(&$context.bounds, &mut $view, $crate::ui::layout::relative::ParentLayoutRule::FromLeft, $crate::or_else!($($offset)?, $context.default_offset));
591    };
592    ($context:expr, $view:expr, align_top $(, $offset:expr)?) => {
593        $crate::ui::layout::relative::move_by_parent(&$context.bounds, &mut $view, $crate::ui::layout::relative::ParentLayoutRule::FromTop, $crate::or_else!($($offset)?, $context.default_offset));
594    };
595    ($context:expr, $view:expr, align_right $(, $offset:expr)?) => {
596        $crate::ui::layout::relative::move_by_parent(&$context.bounds, &mut $view, $crate::ui::layout::relative::ParentLayoutRule::FromRight, $crate::or_else!($($offset)?, $context.default_offset));
597    };
598    ($context:expr, $view:expr, align_bottom $(, $offset:expr)?) => {
599        $crate::ui::layout::relative::move_by_parent(&$context.bounds, &mut $view, $crate::ui::layout::relative::ParentLayoutRule::FromBottom, $crate::or_else!($($offset)?, $context.default_offset));
600    };
601    ($context:expr, $view:expr, align_centerh $(, $offset:expr)?) => {
602        $crate::ui::layout::relative::move_by_parent(&$context.bounds, &mut $view, $crate::ui::layout::relative::ParentLayoutRule::FromCenterH, $crate::or_else!($($offset)?, $context.default_offset));
603    };
604    ($context:expr, $view:expr, align_centerv $(, $offset:expr)?) => {
605        $crate::ui::layout::relative::move_by_parent(&$context.bounds, &mut $view, $crate::ui::layout::relative::ParentLayoutRule::FromCenterV, $crate::or_else!($($offset)?, $context.default_offset));
606    };
607    ($context:expr, grow $view:expr, align_centerh $(, $offset:expr)?) => {
608        $crate::ui::layout::relative::grow_by_parent(&$context.bounds, &mut $view, $crate::ui::layout::relative::ParentLayoutRule::FromCenterH, $crate::or_else!($($offset)?, $context.default_offset));
609    };
610    ($context:expr, grow $view:expr, align_centerv $(, $offset:expr)?) => {
611        $crate::ui::layout::relative::grow_by_parent(&$context.bounds, &mut $view, $crate::ui::layout::relative::ParentLayoutRule::FromCenterV, $crate::or_else!($($offset)?, $context.default_offset));
612    };
613    ($context:expr, centerh $view:expr, between $first:expr, $second:expr $(, $offset:expr)?) => {
614        $crate::ui::layout::relative::center_between(&$context.bounds, &mut $view, &$first, &$second, true, $context.default_offset);
615    };
616    ($context:expr, centerv $view:expr, between $first:expr, $second:expr $(, $offset:expr)?) => {
617        $crate::ui::layout::relative::center_between(&$context.bounds, &mut $view, &$first, &$second, false, $context.default_offset);
618    };
619}
620
621#[macro_export]
622macro_rules! px {
623    ($number:expr) => {
624        $crate::ui::layout::relative::LayoutOffset::Pixels($number)
625    };
626}
627
628#[macro_export]
629macro_rules! parent {
630    ($number:expr) => {
631        $crate::ui::layout::relative::LayoutOffset::Percent($number)
632    };
633}