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