Skip to main content

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