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