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