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        self.get_children().extend(children);
222        self
223    }
224
225    fn maybe_child<C: IntoElement>(mut self, child: Option<C>) -> Self {
226        if let Some(child) = child {
227            self.get_children().push(child.into_element());
228        }
229        self
230    }
231
232    fn child<C: IntoElement>(mut self, child: C) -> Self {
233        self.get_children().push(child.into_element());
234        self
235    }
236}
237
238pub trait KeyExt: Sized {
239    fn write_key(&mut self) -> &mut DiffKey;
240
241    fn key(mut self, key: impl Hash) -> Self {
242        let mut hasher = FxHasher::default();
243        key.hash(&mut hasher);
244        *self.write_key() = DiffKey::U64(hasher.finish());
245        self
246    }
247}
248
249pub trait ListExt {
250    fn with(self, other: Self) -> Self;
251}
252
253impl<T> ListExt for Vec<T> {
254    fn with(mut self, other: Self) -> Self {
255        self.extend(other);
256        self
257    }
258}
259
260macro_rules! event_handlers {
261    (
262        $handler_variant:ident, $event_data:ty;
263        $(
264            $name:ident => $event_variant:expr ;
265        )*
266    ) => {
267        paste! {
268            $(
269                fn [<on_$name>](mut self, [<on_$name>]: impl Into<EventHandler<Event<$event_data>>>) -> Self {
270                    self.get_event_handlers()
271                        .insert($event_variant, EventHandlerType::$handler_variant([<on_$name>].into()));
272                    self
273                }
274            )*
275        }
276    };
277}
278
279pub trait EventHandlersExt: Sized + LayoutExt {
280    fn get_event_handlers(&mut self) -> &mut FxHashMap<EventName, EventHandlerType>;
281
282    event_handlers! {
283        Mouse,
284        MouseEventData;
285
286        mouse_down => EventName::MouseDown;
287        mouse_up => EventName::MouseUp;
288        mouse_move => EventName::MouseMove;
289
290        global_mouse_up => EventName::GlobalMouseUp;
291        global_mouse_down => EventName::GlobalMouseDown;
292        global_mouse_move => EventName::GlobalMouseMove;
293
294        capture_global_mouse_move => EventName::CaptureGlobalMouseMove;
295        capture_global_mouse_up => EventName::CaptureGlobalMouseUp;
296    }
297
298    event_handlers! {
299        Keyboard,
300        KeyboardEventData;
301
302        key_down => EventName::KeyDown;
303        key_up => EventName::KeyUp;
304
305        global_key_down => EventName::GlobalKeyDown;
306        global_key_up => EventName::GlobalKeyUp;
307    }
308
309    event_handlers! {
310        Wheel,
311        WheelEventData;
312
313        wheel => EventName::Wheel;
314    }
315
316    event_handlers! {
317        Touch,
318        TouchEventData;
319
320        touch_cancel => EventName::TouchCancel;
321        touch_start => EventName::TouchStart;
322        touch_move => EventName::TouchMove;
323        touch_end => EventName::TouchEnd;
324    }
325
326    event_handlers! {
327        Pointer,
328        PointerEventData;
329
330        pointer_press => EventName::PointerPress;
331        pointer_down => EventName::PointerDown;
332        pointer_enter => EventName::PointerEnter;
333        pointer_leave => EventName::PointerLeave;
334    }
335
336    event_handlers! {
337        File,
338        FileEventData;
339
340        file_drop => EventName::FileDrop;
341        global_file_hover => EventName::GlobalFileHover;
342        global_file_hover_cancelled => EventName::GlobalFileHoverCancelled;
343    }
344
345    event_handlers! {
346        ImePreedit,
347        ImePreeditEventData;
348
349        ime_preedit => EventName::ImePreedit;
350    }
351
352    fn on_sized(mut self, on_sized: impl Into<EventHandler<Event<SizedEventData>>>) -> Self {
353        self.get_event_handlers()
354            .insert(EventName::Sized, EventHandlerType::Sized(on_sized.into()));
355        self.get_layout().layout.has_layout_references = true;
356        self
357    }
358
359    /// This is generally the best event in which to run "press" logic, this might be called `onClick`, `onActivate`, or `onConnect` in other platforms.
360    ///
361    /// Gets triggered when:
362    /// - **Click**: There is a `MouseUp` event (Left button) with the in the same element that there had been a `MouseDown` just before
363    /// - **Touched**: There is a `TouchEnd` event in the same element that there had been a `TouchStart` just before
364    /// - **Activated**: The element is focused and there is a keydown event pressing the OS activation key (e.g Space, Enter)
365    fn on_press(self, on_press: impl Into<EventHandler<Event<PressEventData>>>) -> Self {
366        let on_press = on_press.into();
367        self.on_pointer_press({
368            let on_press = on_press.clone();
369            move |e: Event<PointerEventData>| {
370                let event = e.try_map(|d| match d {
371                    PointerEventData::Mouse(m) if m.button == Some(MouseButton::Left) => {
372                        Some(PressEventData::Mouse(m))
373                    }
374                    PointerEventData::Touch(t) => Some(PressEventData::Touch(t)),
375                    _ => None,
376                });
377                if let Some(event) = event {
378                    on_press.call(event);
379                }
380            }
381        })
382        .on_key_down({
383            let on_press = on_press.clone();
384            move |e: Event<KeyboardEventData>| {
385                if Focus::is_pressed(&e) {
386                    on_press.call(e.map(PressEventData::Keyboard))
387                }
388            }
389        })
390    }
391
392    /// Also called the context menu click in other platforms.
393    /// Gets triggered when:
394    /// - **Click**: There is a `MouseUp` (Right button) event in the same element that there had been a `MouseDown` just before
395    fn on_secondary_press(
396        self,
397        on_pointer_press: impl Into<EventHandler<Event<PressEventData>>>,
398    ) -> Self {
399        let on_pointer_press = on_pointer_press.into();
400        self.on_pointer_press({
401            let on_pointer_press = on_pointer_press.clone();
402            move |e: Event<PointerEventData>| {
403                let event = e.try_map(|d| match d {
404                    PointerEventData::Mouse(m) if m.button == Some(MouseButton::Right) => {
405                        Some(PressEventData::Mouse(m))
406                    }
407                    _ => None,
408                });
409                if let Some(event) = event {
410                    on_pointer_press.call(event);
411                }
412            }
413        })
414    }
415
416    /// Gets triggered when:
417    /// - **Click**: There is a `MouseUp` event (Any button) with the in the same element that there had been a `MouseDown` just before
418    /// - **Touched**: There is a `TouchEnd` event in the same element that there had been a `TouchStart` just before
419    /// - **Activated**: The element is focused and there is a keydown event pressing the OS activation key (e.g Space, Enter)
420    fn on_all_press(self, on_press: impl Into<EventHandler<Event<PressEventData>>>) -> Self {
421        let on_press = on_press.into();
422        self.on_pointer_press({
423            let on_press = on_press.clone();
424            move |e: Event<PointerEventData>| {
425                let event = e.try_map(|d| match d {
426                    PointerEventData::Mouse(m) => Some(PressEventData::Mouse(m)),
427                    PointerEventData::Touch(t) => Some(PressEventData::Touch(t)),
428                });
429                if let Some(event) = event {
430                    on_press.call(event);
431                }
432            }
433        })
434        .on_key_down({
435            let on_press = on_press.clone();
436            move |e: Event<KeyboardEventData>| {
437                if Focus::is_pressed(&e) {
438                    on_press.call(e.map(PressEventData::Keyboard))
439                }
440            }
441        })
442    }
443}
444
445#[derive(Debug, Clone, PartialEq)]
446pub enum PressEventData {
447    Mouse(MouseEventData),
448    Keyboard(KeyboardEventData),
449    Touch(TouchEventData),
450}
451
452pub trait ContainerWithContentExt
453where
454    Self: LayoutExt,
455{
456    fn direction(mut self, direction: Direction) -> Self {
457        self.get_layout().layout.direction = direction;
458        self
459    }
460    fn main_align(mut self, main_align: Alignment) -> Self {
461        self.get_layout().layout.main_alignment = main_align;
462        self
463    }
464
465    fn cross_align(mut self, cross_align: Alignment) -> Self {
466        self.get_layout().layout.cross_alignment = cross_align;
467        self
468    }
469
470    fn spacing(mut self, spacing: impl Into<f32>) -> Self {
471        self.get_layout().layout.spacing = Length::new(spacing.into());
472        self
473    }
474
475    fn content(mut self, content: Content) -> Self {
476        self.get_layout().layout.content = content;
477        self
478    }
479    fn center(mut self) -> Self {
480        self.get_layout().layout.main_alignment = Alignment::Center;
481        self.get_layout().layout.cross_alignment = Alignment::Center;
482
483        self
484    }
485
486    fn offset_x(mut self, offset_x: impl Into<f32>) -> Self {
487        self.get_layout().layout.offset_x = Length::new(offset_x.into());
488        self
489    }
490
491    fn offset_y(mut self, offset_y: impl Into<f32>) -> Self {
492        self.get_layout().layout.offset_y = Length::new(offset_y.into());
493        self
494    }
495
496    fn vertical(mut self) -> Self {
497        self.get_layout().layout.direction = Direction::vertical();
498        self
499    }
500
501    fn horizontal(mut self) -> Self {
502        self.get_layout().layout.direction = Direction::horizontal();
503        self
504    }
505}
506
507pub trait ContainerSizeExt
508where
509    Self: LayoutExt,
510{
511    fn width(mut self, width: impl Into<Size>) -> Self {
512        self.get_layout().layout.width = width.into();
513        self
514    }
515
516    fn height(mut self, height: impl Into<Size>) -> Self {
517        self.get_layout().layout.height = height.into();
518        self
519    }
520
521    /// Expand both `width` and `height` using [Size::fill()].
522    fn expanded(mut self) -> Self {
523        self.get_layout().layout.width = Size::fill();
524        self.get_layout().layout.height = Size::fill();
525        self
526    }
527}
528
529impl<T: ContainerExt> ContainerSizeExt for T {}
530
531pub trait ContainerExt
532where
533    Self: LayoutExt,
534{
535    fn position(mut self, position: impl Into<Position>) -> Self {
536        self.get_layout().layout.position = position.into();
537        self
538    }
539
540    fn padding(mut self, padding: impl Into<Gaps>) -> Self {
541        self.get_layout().layout.padding = padding.into();
542        self
543    }
544
545    fn margin(mut self, margin: impl Into<Gaps>) -> Self {
546        self.get_layout().layout.margin = margin.into();
547        self
548    }
549
550    fn min_width(mut self, minimum_width: impl Into<Size>) -> Self {
551        self.get_layout().layout.minimum_width = minimum_width.into();
552        self
553    }
554
555    fn min_height(mut self, minimum_height: impl Into<Size>) -> Self {
556        self.get_layout().layout.minimum_height = minimum_height.into();
557        self
558    }
559
560    fn max_width(mut self, maximum_width: impl Into<Size>) -> Self {
561        self.get_layout().layout.maximum_width = maximum_width.into();
562        self
563    }
564
565    fn max_height(mut self, maximum_height: impl Into<Size>) -> Self {
566        self.get_layout().layout.maximum_height = maximum_height.into();
567        self
568    }
569
570    fn visible_width(mut self, visible_width: impl Into<VisibleSize>) -> Self {
571        self.get_layout().layout.visible_width = visible_width.into();
572        self
573    }
574
575    fn visible_height(mut self, visible_height: impl Into<VisibleSize>) -> Self {
576        self.get_layout().layout.visible_height = visible_height.into();
577        self
578    }
579}
580
581pub trait LayoutExt
582where
583    Self: Sized,
584{
585    fn get_layout(&mut self) -> &mut LayoutData;
586
587    fn layout(mut self, layout: LayoutData) -> Self {
588        *self.get_layout() = layout;
589        self
590    }
591}
592
593pub trait ImageExt
594where
595    Self: LayoutExt,
596{
597    fn get_image_data(&mut self) -> &mut ImageData;
598
599    fn image_data(mut self, image_data: ImageData) -> Self {
600        *self.get_image_data() = image_data;
601        self
602    }
603
604    fn sampling_mode(mut self, sampling_mode: SamplingMode) -> Self {
605        self.get_image_data().sampling_mode = sampling_mode;
606        self
607    }
608
609    fn aspect_ratio(mut self, aspect_ratio: AspectRatio) -> Self {
610        self.get_image_data().aspect_ratio = aspect_ratio;
611        self
612    }
613
614    fn image_cover(mut self, image_cover: ImageCover) -> Self {
615        self.get_image_data().image_cover = image_cover;
616        self
617    }
618}
619
620pub trait AccessibilityExt: Sized {
621    fn get_accessibility_data(&mut self) -> &mut AccessibilityData;
622
623    fn accessibility(mut self, accessibility: AccessibilityData) -> Self {
624        *self.get_accessibility_data() = accessibility;
625        self
626    }
627
628    fn a11y_id(mut self, a11y_id: impl Into<Option<AccessibilityId>>) -> Self {
629        self.get_accessibility_data().a11y_id = a11y_id.into();
630        self
631    }
632
633    fn a11y_focusable(mut self, a11y_focusable: impl Into<Focusable>) -> Self {
634        self.get_accessibility_data().a11y_focusable = a11y_focusable.into();
635        self
636    }
637
638    fn a11y_auto_focus(mut self, a11y_auto_focus: impl Into<bool>) -> Self {
639        self.get_accessibility_data().a11y_auto_focus = a11y_auto_focus.into();
640        self
641    }
642
643    fn a11y_member_of(mut self, a11y_member_of: impl Into<AccessibilityId>) -> Self {
644        self.get_accessibility_data()
645            .builder
646            .set_member_of(a11y_member_of.into());
647        self
648    }
649
650    fn a11y_role(mut self, a11y_role: impl Into<AccessibilityRole>) -> Self {
651        self.get_accessibility_data()
652            .builder
653            .set_role(a11y_role.into());
654        self
655    }
656
657    fn a11y_alt(mut self, value: impl Into<Box<str>>) -> Self {
658        self.get_accessibility_data().builder.set_label(value);
659        self
660    }
661
662    fn a11y_builder(mut self, with: impl FnOnce(&mut accesskit::Node)) -> Self {
663        with(&mut self.get_accessibility_data().builder);
664        self
665    }
666}
667
668pub trait TextStyleExt
669where
670    Self: Sized,
671{
672    fn get_text_style_data(&mut self) -> &mut TextStyleData;
673
674    fn color(mut self, color: impl Into<Color>) -> Self {
675        self.get_text_style_data().color = Some(color.into());
676        self
677    }
678
679    fn text_align(mut self, text_align: impl Into<TextAlign>) -> Self {
680        self.get_text_style_data().text_align = Some(text_align.into());
681        self
682    }
683
684    fn font_size(mut self, font_size: impl Into<FontSize>) -> Self {
685        self.get_text_style_data().font_size = Some(font_size.into());
686        self
687    }
688
689    fn font_family(mut self, font_family: impl Into<Cow<'static, str>>) -> Self {
690        self.get_text_style_data()
691            .font_families
692            .push(font_family.into());
693        self
694    }
695
696    fn font_slant(mut self, font_slant: impl Into<FontSlant>) -> Self {
697        self.get_text_style_data().font_slant = Some(font_slant.into());
698        self
699    }
700
701    fn font_weight(mut self, font_weight: impl Into<FontWeight>) -> Self {
702        self.get_text_style_data().font_weight = Some(font_weight.into());
703        self
704    }
705
706    fn font_width(mut self, font_width: impl Into<FontWidth>) -> Self {
707        self.get_text_style_data().font_width = Some(font_width.into());
708        self
709    }
710
711    fn text_height(mut self, text_height: impl Into<TextHeightBehavior>) -> Self {
712        self.get_text_style_data().text_height = Some(text_height.into());
713        self
714    }
715
716    fn text_overflow(mut self, text_overflow: impl Into<TextOverflow>) -> Self {
717        self.get_text_style_data().text_overflow = Some(text_overflow.into());
718        self
719    }
720
721    fn text_shadow(mut self, text_shadow: impl Into<TextShadow>) -> Self {
722        self.get_text_style_data()
723            .text_shadows
724            .push(text_shadow.into());
725        self
726    }
727}
728
729pub trait StyleExt
730where
731    Self: Sized,
732{
733    fn get_style(&mut self) -> &mut StyleState;
734
735    fn background<S: Into<Color>>(mut self, background: S) -> Self {
736        self.get_style().background = Fill::Color(background.into());
737        self
738    }
739
740    fn background_conic_gradient<S: Into<ConicGradient>>(mut self, background: S) -> Self {
741        self.get_style().background = Fill::ConicGradient(Box::new(background.into()));
742        self
743    }
744
745    fn background_linear_gradient<S: Into<LinearGradient>>(mut self, background: S) -> Self {
746        self.get_style().background = Fill::LinearGradient(Box::new(background.into()));
747        self
748    }
749
750    fn background_radial_gradient<S: Into<RadialGradient>>(mut self, background: S) -> Self {
751        self.get_style().background = Fill::RadialGradient(Box::new(background.into()));
752        self
753    }
754
755    fn border(mut self, border: impl Into<Option<Border>>) -> Self {
756        if let Some(border) = border.into() {
757            self.get_style().borders.push(border);
758        }
759        self
760    }
761
762    fn shadow(mut self, shadow: impl Into<Shadow>) -> Self {
763        self.get_style().shadows.push(shadow.into());
764        self
765    }
766
767    fn corner_radius(mut self, corner_radius: impl Into<CornerRadius>) -> Self {
768        self.get_style().corner_radius = corner_radius.into();
769        self
770    }
771}
772
773impl<T: StyleExt> CornerRadiusExt for T {
774    fn with_corner_radius(mut self, corner_radius: f32) -> Self {
775        self.get_style().corner_radius = CornerRadius::new_all(corner_radius);
776        self
777    }
778}
779
780pub trait CornerRadiusExt: Sized {
781    fn with_corner_radius(self, corner_radius: f32) -> Self;
782
783    /// Shortcut for `corner_radius(0.)` - removes border radius.
784    fn rounded_none(self) -> Self {
785        self.with_corner_radius(0.)
786    }
787
788    /// Shortcut for `corner_radius(6.)` - default border radius.
789    fn rounded(self) -> Self {
790        self.with_corner_radius(6.)
791    }
792
793    /// Shortcut for `corner_radius(4.)` - small border radius.
794    fn rounded_sm(self) -> Self {
795        self.with_corner_radius(4.)
796    }
797
798    /// Shortcut for `corner_radius(6.)` - medium border radius.
799    fn rounded_md(self) -> Self {
800        self.with_corner_radius(6.)
801    }
802
803    /// Shortcut for `corner_radius(8.)` - large border radius.
804    fn rounded_lg(self) -> Self {
805        self.with_corner_radius(8.)
806    }
807
808    /// Shortcut for `corner_radius(12.)` - extra large border radius.
809    fn rounded_xl(self) -> Self {
810        self.with_corner_radius(12.)
811    }
812
813    /// Shortcut for `corner_radius(16.)` - extra large border radius.
814    fn rounded_2xl(self) -> Self {
815        self.with_corner_radius(16.)
816    }
817
818    /// Shortcut for `corner_radius(24.)` - extra large border radius.
819    fn rounded_3xl(self) -> Self {
820        self.with_corner_radius(24.)
821    }
822
823    /// Shortcut for `corner_radius(32.)` - extra large border radius.
824    fn rounded_4xl(self) -> Self {
825        self.with_corner_radius(32.)
826    }
827
828    /// Shortcut for `corner_radius(99.)` - fully rounded (pill shape).
829    fn rounded_full(self) -> Self {
830        self.with_corner_radius(99.)
831    }
832}
833
834pub trait MaybeExt
835where
836    Self: Sized,
837{
838    fn maybe(self, bool: impl Into<bool>, then: impl FnOnce(Self) -> Self) -> Self {
839        if bool.into() { then(self) } else { self }
840    }
841
842    fn map<T>(self, data: Option<T>, then: impl FnOnce(Self, T) -> Self) -> Self {
843        if let Some(data) = data {
844            then(self, data)
845        } else {
846            self
847        }
848    }
849}
850
851pub trait LayerExt
852where
853    Self: Sized,
854{
855    fn get_layer(&mut self) -> &mut Layer;
856
857    fn layer(mut self, layer: impl Into<Layer>) -> Self {
858        *self.get_layer() = layer.into();
859        self
860    }
861}
862
863pub trait ScrollableExt
864where
865    Self: Sized,
866{
867    fn get_effect(&mut self) -> &mut EffectData;
868
869    fn scrollable(mut self, scrollable: impl Into<bool>) -> Self {
870        self.get_effect().scrollable = scrollable.into();
871        self
872    }
873}
874
875pub trait InteractiveExt
876where
877    Self: Sized,
878{
879    fn get_effect(&mut self) -> &mut EffectData;
880
881    fn interactive(mut self, interactive: impl Into<Interactive>) -> Self {
882        self.get_effect().interactive = interactive.into();
883        self
884    }
885}