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