freya_core/elements/
extensions.rs

1use std::{
2    borrow::Cow,
3    hash::{
4        Hash,
5        Hasher,
6    },
7};
8
9use paste::paste;
10use rustc_hash::{
11    FxHashMap,
12    FxHasher,
13};
14use torin::{
15    content::Content,
16    gaps::Gaps,
17    prelude::{
18        Alignment,
19        Direction,
20        Length,
21        Position,
22        VisibleSize,
23    },
24    size::{
25        Size,
26        SizeFn,
27        SizeFnContext,
28    },
29};
30
31use crate::{
32    data::{
33        AccessibilityData,
34        LayoutData,
35        TextStyleData,
36    },
37    diff_key::DiffKey,
38    element::{
39        Element,
40        EventHandlerType,
41    },
42    elements::image::{
43        AspectRatio,
44        ImageCover,
45        ImageData,
46        SamplingMode,
47    },
48    event_handler::EventHandler,
49    events::{
50        data::{
51            Event,
52            KeyboardEventData,
53            MouseEventData,
54            SizedEventData,
55            WheelEventData,
56        },
57        name::EventName,
58    },
59    layers::Layer,
60    prelude::*,
61    style::{
62        font_size::FontSize,
63        font_slant::FontSlant,
64        font_weight::FontWeight,
65        font_width::FontWidth,
66        text_height::TextHeightBehavior,
67        text_overflow::TextOverflow,
68        text_shadow::TextShadow,
69    },
70};
71
72pub trait SizeExt {
73    fn auto() -> Size;
74    fn fill() -> Size;
75    fn fill_minimum() -> Size;
76    fn percent(percent: impl Into<f32>) -> Size;
77    fn px(px: impl Into<f32>) -> Size;
78    fn window_percent(percent: impl Into<f32>) -> Size;
79    fn flex(flex: impl Into<f32>) -> Size;
80    fn func(func: impl Fn(SizeFnContext) -> Option<f32> + 'static + Sync + Send) -> Size;
81    fn func_data<D: Hash>(
82        func: impl Fn(SizeFnContext) -> Option<f32> + 'static + Sync + Send,
83        data: &D,
84    ) -> Size;
85}
86
87impl SizeExt for Size {
88    fn auto() -> Size {
89        Size::Inner
90    }
91
92    fn fill() -> Size {
93        Size::Fill
94    }
95
96    fn fill_minimum() -> Size {
97        Size::FillMinimum
98    }
99
100    fn percent(percent: impl Into<f32>) -> Size {
101        Size::Percentage(Length::new(percent.into()))
102    }
103
104    fn px(px: impl Into<f32>) -> Size {
105        Size::Pixels(Length::new(px.into()))
106    }
107
108    fn window_percent(percent: impl Into<f32>) -> Size {
109        Size::RootPercentage(Length::new(percent.into()))
110    }
111
112    fn flex(flex: impl Into<f32>) -> Size {
113        Size::Flex(Length::new(flex.into()))
114    }
115
116    fn func(func: impl Fn(SizeFnContext) -> Option<f32> + 'static + Sync + Send) -> Size {
117        Self::Fn(Box::new(SizeFn::new(func)))
118    }
119
120    fn func_data<D: Hash>(
121        func: impl Fn(SizeFnContext) -> Option<f32> + 'static + Sync + Send,
122        data: &D,
123    ) -> Size {
124        Self::Fn(Box::new(SizeFn::new_data(func, data)))
125    }
126}
127
128pub trait DirectionExt {
129    fn vertical() -> Direction;
130    fn horizontal() -> Direction;
131}
132
133impl DirectionExt for Direction {
134    fn vertical() -> Direction {
135        Direction::Vertical
136    }
137    fn horizontal() -> Direction {
138        Direction::Horizontal
139    }
140}
141
142pub trait AlignmentExt {
143    fn start() -> Alignment;
144    fn center() -> Alignment;
145    fn end() -> Alignment;
146    fn space_between() -> Alignment;
147    fn space_evenly() -> Alignment;
148    fn space_around() -> Alignment;
149}
150
151impl AlignmentExt for Alignment {
152    fn start() -> Alignment {
153        Alignment::Start
154    }
155
156    fn center() -> Alignment {
157        Alignment::Center
158    }
159
160    fn end() -> Alignment {
161        Alignment::End
162    }
163
164    fn space_between() -> Alignment {
165        Alignment::SpaceBetween
166    }
167
168    fn space_evenly() -> Alignment {
169        Alignment::SpaceEvenly
170    }
171
172    fn space_around() -> Alignment {
173        Alignment::SpaceAround
174    }
175}
176
177pub trait ContentExt {
178    fn normal() -> Content;
179    fn fit() -> Content;
180    fn flex() -> Content;
181    fn wrap() -> Content;
182}
183
184impl ContentExt for Content {
185    fn normal() -> Content {
186        Content::Normal
187    }
188
189    fn fit() -> Content {
190        Content::Fit
191    }
192
193    fn flex() -> Content {
194        Content::Flex
195    }
196
197    fn wrap() -> Content {
198        Content::Wrap
199    }
200}
201
202pub trait VisibleSizeExt {
203    fn full() -> VisibleSize;
204    fn inner_percent(value: impl Into<f32>) -> VisibleSize;
205}
206
207impl VisibleSizeExt for VisibleSize {
208    fn full() -> VisibleSize {
209        VisibleSize::Full
210    }
211
212    fn inner_percent(value: impl Into<f32>) -> VisibleSize {
213        VisibleSize::InnerPercentage(Length::new(value.into()))
214    }
215}
216
217pub trait ChildrenExt: Sized {
218    fn get_children(&mut self) -> &mut Vec<Element>;
219
220    fn children(mut self, children: impl IntoIterator<Item = Element>) -> Self        
221    {
222        self.get_children().extend(children);
223        self
224    }
225
226    fn maybe_child<C: IntoElement>(mut self, child: Option<C>) -> Self {
227        if let Some(child) = child {
228            self.get_children().push(child.into_element());
229        }
230        self
231    }
232
233    fn child<C: IntoElement>(mut self, child: C) -> Self {
234        self.get_children().push(child.into_element());
235        self
236    }
237}
238
239pub trait KeyExt: Sized {
240    fn write_key(&mut self) -> &mut DiffKey;
241
242    fn key(mut self, key: impl Hash) -> Self {
243        let mut hasher = FxHasher::default();
244        key.hash(&mut hasher);
245        *self.write_key() = DiffKey::U64(hasher.finish());
246        self
247    }
248}
249
250pub trait ListExt {
251    fn with(self, other: Self) -> Self;
252}
253
254impl<T> ListExt for Vec<T> {
255    fn with(mut self, other: Self) -> Self {
256        self.extend(other);
257        self
258    }
259}
260
261macro_rules! event_handlers {
262    (
263        $handler_variant:ident, $event_data:ty;
264        $(
265            $name:ident => $event_variant:expr ;
266        )*
267    ) => {
268        paste! {
269            $(
270                fn [<on_$name>](mut self, [<on_$name>]: impl Into<EventHandler<Event<$event_data>>>) -> Self {
271                    self.get_event_handlers()
272                        .insert($event_variant, EventHandlerType::$handler_variant([<on_$name>].into()));
273                    self
274                }
275            )*
276        }
277    };
278}
279
280pub trait EventHandlersExt: Sized + LayoutExt {
281    fn get_event_handlers(&mut self) -> &mut FxHashMap<EventName, EventHandlerType>;
282
283    event_handlers! {
284        Mouse,
285        MouseEventData;
286
287        mouse_down => EventName::MouseDown;
288        mouse_up => EventName::MouseUp;
289        mouse_move => EventName::MouseMove;
290
291        global_mouse_up => EventName::GlobalMouseUp;
292        global_mouse_down => EventName::GlobalMouseDown;
293        global_mouse_move => EventName::GlobalMouseMove;
294
295        capture_global_mouse_move => EventName::CaptureGlobalMouseMove;
296        capture_global_mouse_up => EventName::CaptureGlobalMouseUp;
297    }
298
299    event_handlers! {
300        Keyboard,
301        KeyboardEventData;
302
303        key_down => EventName::KeyDown;
304        key_up => EventName::KeyUp;
305
306        global_key_down => EventName::GlobalKeyDown;
307        global_key_up => EventName::GlobalKeyUp;
308    }
309
310    event_handlers! {
311        Wheel,
312        WheelEventData;
313
314        wheel => EventName::Wheel;
315    }
316
317    event_handlers! {
318        Touch,
319        TouchEventData;
320
321        touch_cancel => EventName::TouchCancel;
322        touch_start => EventName::TouchStart;
323        touch_move => EventName::TouchMove;
324        touch_end => EventName::TouchEnd;
325    }
326
327    event_handlers! {
328        Pointer,
329        PointerEventData;
330
331        pointer_press => EventName::PointerPress;
332        pointer_down => EventName::PointerDown;
333        pointer_enter => EventName::PointerEnter;
334        pointer_leave => EventName::PointerLeave;
335    }
336
337    event_handlers! {
338        File,
339        FileEventData;
340
341        file_drop => EventName::FileDrop;
342        global_file_hover => EventName::GlobalFileHover;
343        global_file_hover_cancelled => EventName::GlobalFileHoverCancelled;
344    }
345
346    event_handlers! {
347        ImePreedit,
348        ImePreeditEventData;
349
350        ime_preedit => EventName::ImePreedit;
351    }
352
353    fn on_sized(mut self, on_sized: impl Into<EventHandler<Event<SizedEventData>>>) -> Self {
354        self.get_event_handlers()
355            .insert(EventName::Sized, EventHandlerType::Sized(on_sized.into()));
356        self.get_layout().layout.has_layout_references = true;
357        self
358    }
359
360    /// This is generally the best event in which to run "press" logic, this might be called `onClick`, `onActivate`, or `onConnect` in other platforms.
361    ///
362    /// Gets triggered when:
363    /// - **Click**: There is a `MouseUp` event (Left button) with the in the same element that there had been a `MouseDown` just before
364    /// - **Touched**: There is a `TouchEnd` event in the same element that there had been a `TouchStart` just before
365    /// - **Activated**: The element is focused and there is a keydown event pressing the OS activation key (e.g Space, Enter)
366    fn on_press(self, on_press: impl Into<EventHandler<Event<PressEventData>>>) -> Self {
367        let on_press = on_press.into();
368        self.on_pointer_press({
369            let on_press = on_press.clone();
370            move |e: Event<PointerEventData>| {
371                let event = e.try_map(|d| match d {
372                    PointerEventData::Mouse(m) if m.button == Some(MouseButton::Left) => {
373                        Some(PressEventData::Mouse(m))
374                    }
375                    PointerEventData::Touch(t) => Some(PressEventData::Touch(t)),
376                    _ => None,
377                });
378                if let Some(event) = event {
379                    on_press.call(event);
380                }
381            }
382        })
383        .on_key_down({
384            let on_press = on_press.clone();
385            move |e: Event<KeyboardEventData>| {
386                if Focus::is_pressed(&e) {
387                    on_press.call(e.map(PressEventData::Keyboard))
388                }
389            }
390        })
391    }
392
393    /// Also called the context menu click in other platforms.
394    /// Gets triggered when:
395    /// - **Click**: There is a `MouseUp` (Right button) event in the same element that there had been a `MouseDown` just before
396    fn on_secondary_press(
397        self,
398        on_pointer_press: impl Into<EventHandler<Event<PressEventData>>>,
399    ) -> Self {
400        let on_pointer_press = on_pointer_press.into();
401        self.on_pointer_press({
402            let on_pointer_press = on_pointer_press.clone();
403            move |e: Event<PointerEventData>| {
404                let event = e.try_map(|d| match d {
405                    PointerEventData::Mouse(m) if m.button == Some(MouseButton::Right) => {
406                        Some(PressEventData::Mouse(m))
407                    }
408                    _ => None,
409                });
410                if let Some(event) = event {
411                    on_pointer_press.call(event);
412                }
413            }
414        })
415    }
416
417    /// Gets triggered when:
418    /// - **Click**: There is a `MouseUp` event (Any button) with the in the same element that there had been a `MouseDown` just before
419    /// - **Touched**: There is a `TouchEnd` event in the same element that there had been a `TouchStart` just before
420    /// - **Activated**: The element is focused and there is a keydown event pressing the OS activation key (e.g Space, Enter)
421    fn on_all_press(self, on_press: impl Into<EventHandler<Event<PressEventData>>>) -> Self {
422        let on_press = on_press.into();
423        self.on_pointer_press({
424            let on_press = on_press.clone();
425            move |e: Event<PointerEventData>| {
426                let event = e.try_map(|d| match d {
427                    PointerEventData::Mouse(m) => Some(PressEventData::Mouse(m)),
428                    PointerEventData::Touch(t) => Some(PressEventData::Touch(t)),
429                });
430                if let Some(event) = event {
431                    on_press.call(event);
432                }
433            }
434        })
435        .on_key_down({
436            let on_press = on_press.clone();
437            move |e: Event<KeyboardEventData>| {
438                if Focus::is_pressed(&e) {
439                    on_press.call(e.map(PressEventData::Keyboard))
440                }
441            }
442        })
443    }
444}
445
446#[derive(Debug, Clone, PartialEq)]
447pub enum PressEventData {
448    Mouse(MouseEventData),
449    Keyboard(KeyboardEventData),
450    Touch(TouchEventData),
451}
452
453pub trait ContainerWithContentExt
454where
455    Self: LayoutExt,
456{
457    fn direction(mut self, direction: Direction) -> Self {
458        self.get_layout().layout.direction = direction;
459        self
460    }
461    fn main_align(mut self, main_align: Alignment) -> Self {
462        self.get_layout().layout.main_alignment = main_align;
463        self
464    }
465
466    fn cross_align(mut self, cross_align: Alignment) -> Self {
467        self.get_layout().layout.cross_alignment = cross_align;
468        self
469    }
470
471    fn spacing(mut self, spacing: impl Into<f32>) -> Self {
472        self.get_layout().layout.spacing = Length::new(spacing.into());
473        self
474    }
475
476    fn content(mut self, content: Content) -> Self {
477        self.get_layout().layout.content = content;
478        self
479    }
480    fn center(mut self) -> Self {
481        self.get_layout().layout.main_alignment = Alignment::Center;
482        self.get_layout().layout.cross_alignment = Alignment::Center;
483
484        self
485    }
486
487    fn offset_x(mut self, offset_x: impl Into<f32>) -> Self {
488        self.get_layout().layout.offset_x = Length::new(offset_x.into());
489        self
490    }
491
492    fn offset_y(mut self, offset_y: impl Into<f32>) -> Self {
493        self.get_layout().layout.offset_y = Length::new(offset_y.into());
494        self
495    }
496
497    fn vertical(mut self) -> Self {
498        self.get_layout().layout.direction = Direction::vertical();
499        self
500    }
501
502    fn horizontal(mut self) -> Self {
503        self.get_layout().layout.direction = Direction::horizontal();
504        self
505    }
506}
507
508pub trait ContainerSizeExt
509where
510    Self: LayoutExt,
511{
512    fn width(mut self, width: impl Into<Size>) -> Self {
513        self.get_layout().layout.width = width.into();
514        self
515    }
516
517    fn height(mut self, height: impl Into<Size>) -> Self {
518        self.get_layout().layout.height = height.into();
519        self
520    }
521
522    /// Expand both `width` and `height` using [Size::fill()].
523    fn expanded(mut self) -> Self {
524        self.get_layout().layout.width = Size::fill();
525        self.get_layout().layout.height = Size::fill();
526        self
527    }
528}
529
530impl<T: ContainerExt> ContainerSizeExt for T {}
531
532pub trait ContainerExt
533where
534    Self: LayoutExt,
535{
536    fn position(mut self, position: impl Into<Position>) -> Self {
537        self.get_layout().layout.position = position.into();
538        self
539    }
540
541    fn padding(mut self, padding: impl Into<Gaps>) -> Self {
542        self.get_layout().layout.padding = padding.into();
543        self
544    }
545
546    fn margin(mut self, margin: impl Into<Gaps>) -> Self {
547        self.get_layout().layout.margin = margin.into();
548        self
549    }
550
551    fn min_width(mut self, minimum_width: impl Into<Size>) -> Self {
552        self.get_layout().layout.minimum_width = minimum_width.into();
553        self
554    }
555
556    fn min_height(mut self, minimum_height: impl Into<Size>) -> Self {
557        self.get_layout().layout.minimum_height = minimum_height.into();
558        self
559    }
560
561    fn max_width(mut self, maximum_width: impl Into<Size>) -> Self {
562        self.get_layout().layout.maximum_width = maximum_width.into();
563        self
564    }
565
566    fn max_height(mut self, maximum_height: impl Into<Size>) -> Self {
567        self.get_layout().layout.maximum_height = maximum_height.into();
568        self
569    }
570
571    fn visible_width(mut self, visible_width: impl Into<VisibleSize>) -> Self {
572        self.get_layout().layout.visible_width = visible_width.into();
573        self
574    }
575
576    fn visible_height(mut self, visible_height: impl Into<VisibleSize>) -> Self {
577        self.get_layout().layout.visible_height = visible_height.into();
578        self
579    }
580}
581
582pub trait LayoutExt
583where
584    Self: Sized,
585{
586    fn get_layout(&mut self) -> &mut LayoutData;
587
588    fn layout(mut self, layout: LayoutData) -> Self {
589        *self.get_layout() = layout;
590        self
591    }
592}
593
594pub trait ImageExt
595where
596    Self: LayoutExt,
597{
598    fn width(mut self, width: Size) -> Self {
599        self.get_layout().layout.width = width;
600        self
601    }
602
603    fn height(mut self, height: Size) -> Self {
604        self.get_layout().layout.height = height;
605        self
606    }
607
608    fn get_image_data(&mut self) -> &mut ImageData;
609
610    fn image_data(mut self, image_data: ImageData) -> Self {
611        *self.get_image_data() = image_data;
612        self
613    }
614
615    fn sampling_mode(mut self, sampling_mode: SamplingMode) -> Self {
616        self.get_image_data().sampling_mode = sampling_mode;
617        self
618    }
619
620    fn aspect_ratio(mut self, aspect_ratio: AspectRatio) -> Self {
621        self.get_image_data().aspect_ratio = aspect_ratio;
622        self
623    }
624
625    fn image_cover(mut self, image_cover: ImageCover) -> Self {
626        self.get_image_data().image_cover = image_cover;
627        self
628    }
629}
630
631pub trait AccessibilityExt: Sized {
632    fn get_accessibility_data(&mut self) -> &mut AccessibilityData;
633
634    fn accessibility(mut self, accessibility: AccessibilityData) -> Self {
635        *self.get_accessibility_data() = accessibility;
636        self
637    }
638
639    fn a11y_id(mut self, a11y_id: impl Into<Option<AccessibilityId>>) -> Self {
640        self.get_accessibility_data().a11y_id = a11y_id.into();
641        self
642    }
643
644    fn a11y_focusable(mut self, a11y_focusable: impl Into<Focusable>) -> Self {
645        self.get_accessibility_data().a11y_focusable = a11y_focusable.into();
646        self
647    }
648
649    fn a11y_auto_focus(mut self, a11y_auto_focus: impl Into<bool>) -> Self {
650        self.get_accessibility_data().a11y_auto_focus = a11y_auto_focus.into();
651        self
652    }
653
654    fn a11y_member_of(mut self, a11y_member_of: impl Into<AccessibilityId>) -> Self {
655        self.get_accessibility_data()
656            .builder
657            .set_member_of(a11y_member_of.into());
658        self
659    }
660
661    fn a11y_role(mut self, a11y_role: impl Into<AccessibilityRole>) -> Self {
662        self.get_accessibility_data()
663            .builder
664            .set_role(a11y_role.into());
665        self
666    }
667
668    fn a11y_alt(mut self, value: impl Into<Box<str>>) -> Self {
669        self.get_accessibility_data().builder.set_label(value);
670        self
671    }
672
673    fn a11y_builder(mut self, with: impl FnOnce(&mut accesskit::Node)) -> Self {
674        with(&mut self.get_accessibility_data().builder);
675        self
676    }
677}
678
679pub trait TextStyleExt
680where
681    Self: Sized,
682{
683    fn get_text_style_data(&mut self) -> &mut TextStyleData;
684
685    fn color(mut self, color: impl Into<Color>) -> Self {
686        self.get_text_style_data().color = Some(color.into());
687        self
688    }
689
690    fn text_align(mut self, text_align: impl Into<TextAlign>) -> Self {
691        self.get_text_style_data().text_align = Some(text_align.into());
692        self
693    }
694
695    fn font_size(mut self, font_size: impl Into<FontSize>) -> Self {
696        self.get_text_style_data().font_size = Some(font_size.into());
697        self
698    }
699
700    fn font_family(mut self, font_family: impl Into<Cow<'static, str>>) -> Self {
701        self.get_text_style_data()
702            .font_families
703            .push(font_family.into());
704        self
705    }
706
707    fn font_slant(mut self, font_slant: impl Into<FontSlant>) -> Self {
708        self.get_text_style_data().font_slant = Some(font_slant.into());
709        self
710    }
711
712    fn font_weight(mut self, font_weight: impl Into<FontWeight>) -> Self {
713        self.get_text_style_data().font_weight = Some(font_weight.into());
714        self
715    }
716
717    fn font_width(mut self, font_width: impl Into<FontWidth>) -> Self {
718        self.get_text_style_data().font_width = Some(font_width.into());
719        self
720    }
721
722    fn text_height(mut self, text_height: impl Into<TextHeightBehavior>) -> Self {
723        self.get_text_style_data().text_height = Some(text_height.into());
724        self
725    }
726
727    fn text_overflow(mut self, text_overflow: impl Into<TextOverflow>) -> Self {
728        self.get_text_style_data().text_overflow = Some(text_overflow.into());
729        self
730    }
731
732    fn text_shadow(mut self, text_shadow: impl Into<TextShadow>) -> Self {
733        self.get_text_style_data()
734            .text_shadows
735            .push(text_shadow.into());
736        self
737    }
738}
739
740pub trait StyleExt
741where
742    Self: Sized,
743{
744    fn get_style(&mut self) -> &mut StyleState;
745
746    fn background<S: Into<Color>>(mut self, background: S) -> Self {
747        self.get_style().background = Fill::Color(background.into());
748        self
749    }
750
751    fn background_conic_gradient<S: Into<ConicGradient>>(mut self, background: S) -> Self {
752        self.get_style().background = Fill::ConicGradient(Box::new(background.into()));
753        self
754    }
755
756    fn background_linear_gradient<S: Into<LinearGradient>>(mut self, background: S) -> Self {
757        self.get_style().background = Fill::LinearGradient(Box::new(background.into()));
758        self
759    }
760
761    fn background_radial_gradient<S: Into<RadialGradient>>(mut self, background: S) -> Self {
762        self.get_style().background = Fill::RadialGradient(Box::new(background.into()));
763        self
764    }
765
766    fn border(mut self, border: impl Into<Option<Border>>) -> Self {
767        if let Some(border) = border.into() {
768            self.get_style().borders.push(border);
769        }
770        self
771    }
772
773    fn shadow(mut self, shadow: impl Into<Shadow>) -> Self {
774        self.get_style().shadows.push(shadow.into());
775        self
776    }
777
778    fn corner_radius(mut self, corner_radius: impl Into<CornerRadius>) -> Self {
779        self.get_style().corner_radius = corner_radius.into();
780        self
781    }
782}
783
784impl<T: StyleExt> CornerRadiusExt for T {
785    fn with_corner_radius(mut self, corner_radius: f32) -> Self {
786        self.get_style().corner_radius = CornerRadius::new_all(corner_radius);
787        self
788    }
789}
790
791pub trait CornerRadiusExt: Sized {
792    fn with_corner_radius(self, corner_radius: f32) -> Self;
793
794    /// Shortcut for `corner_radius(0.)` - removes border radius.
795    fn rounded_none(self) -> Self {
796        self.with_corner_radius(0.)
797    }
798
799    /// Shortcut for `corner_radius(6.)` - default border radius.
800    fn rounded(self) -> Self {
801        self.with_corner_radius(6.)
802    }
803
804    /// Shortcut for `corner_radius(4.)` - small border radius.
805    fn rounded_sm(self) -> Self {
806        self.with_corner_radius(4.)
807    }
808
809    /// Shortcut for `corner_radius(6.)` - medium border radius.
810    fn rounded_md(self) -> Self {
811        self.with_corner_radius(6.)
812    }
813
814    /// Shortcut for `corner_radius(8.)` - large border radius.
815    fn rounded_lg(self) -> Self {
816        self.with_corner_radius(8.)
817    }
818
819    /// Shortcut for `corner_radius(12.)` - extra large border radius.
820    fn rounded_xl(self) -> Self {
821        self.with_corner_radius(12.)
822    }
823
824    /// Shortcut for `corner_radius(16.)` - extra large border radius.
825    fn rounded_2xl(self) -> Self {
826        self.with_corner_radius(16.)
827    }
828
829    /// Shortcut for `corner_radius(24.)` - extra large border radius.
830    fn rounded_3xl(self) -> Self {
831        self.with_corner_radius(24.)
832    }
833
834    /// Shortcut for `corner_radius(32.)` - extra large border radius.
835    fn rounded_4xl(self) -> Self {
836        self.with_corner_radius(32.)
837    }
838
839    /// Shortcut for `corner_radius(99.)` - fully rounded (pill shape).
840    fn rounded_full(self) -> Self {
841        self.with_corner_radius(99.)
842    }
843}
844
845pub trait MaybeExt
846where
847    Self: Sized,
848{
849    fn maybe(self, bool: impl Into<bool>, then: impl FnOnce(Self) -> Self) -> Self {
850        if bool.into() { then(self) } else { self }
851    }
852
853    fn map<T>(self, data: Option<T>, then: impl FnOnce(Self, T) -> Self) -> Self {
854        if let Some(data) = data {
855            then(self, data)
856        } else {
857            self
858        }
859    }
860}
861
862pub trait LayerExt
863where
864    Self: Sized,
865{
866    fn get_layer(&mut self) -> &mut Layer;
867
868    fn layer(mut self, layer: impl Into<Layer>) -> Self {
869        *self.get_layer() = layer.into();
870        self
871    }
872}
873
874pub trait ScrollableExt
875where
876    Self: Sized,
877{
878    fn get_effect(&mut self) -> &mut EffectData;
879
880    fn scrollable(mut self, scrollable: impl Into<bool>) -> Self {
881        self.get_effect().scrollable = scrollable.into();
882        self
883    }
884}
885
886pub trait InteractiveExt
887where
888    Self: Sized,
889{
890    fn get_effect(&mut self) -> &mut EffectData;
891
892    fn interactive(mut self, interactive: impl Into<Interactive>) -> Self {
893        self.get_effect().interactive = interactive.into();
894        self
895    }
896}