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        pointer_over => EventName::PointerOver;
359        pointer_out => EventName::PointerOut;
360    }
361
362    event_handlers! {
363        File,
364        FileEventData;
365
366        file_drop => EventName::FileDrop;
367        global_file_hover => EventName::GlobalFileHover;
368        global_file_hover_cancelled => EventName::GlobalFileHoverCancelled;
369    }
370
371    event_handlers! {
372        ImePreedit,
373        ImePreeditEventData;
374
375        ime_preedit => EventName::ImePreedit;
376    }
377
378    fn on_sized(mut self, on_sized: impl Into<EventHandler<Event<SizedEventData>>>) -> Self
379    where
380        Self: LayoutExt,
381    {
382        self.get_event_handlers()
383            .insert(EventName::Sized, EventHandlerType::Sized(on_sized.into()));
384        self.get_layout().layout.has_layout_references = true;
385        self
386    }
387
388    /// This is generally the best event in which to run "press" logic, this might be called `onClick`, `onActivate`, or `onConnect` in other platforms.
389    ///
390    /// Gets triggered when:
391    /// - **Click**: There is a `MouseUp` event (Left button) with the in the same element that there had been a `MouseDown` just before
392    /// - **Touched**: There is a `TouchEnd` event in the same element that there had been a `TouchStart` just before
393    /// - **Activated**: The element is focused and there is a keydown event pressing the OS activation key (e.g Space, Enter)
394    fn on_press(self, on_press: impl Into<EventHandler<Event<PressEventData>>>) -> Self {
395        let on_press = on_press.into();
396        self.on_pointer_press({
397            let on_press = on_press.clone();
398            move |e: Event<PointerEventData>| {
399                let event = e.try_map(|d| match d {
400                    PointerEventData::Mouse(m) if m.button == Some(MouseButton::Left) => {
401                        Some(PressEventData::Mouse(m))
402                    }
403                    PointerEventData::Touch(t) => Some(PressEventData::Touch(t)),
404                    _ => None,
405                });
406                if let Some(event) = event {
407                    on_press.call(event);
408                }
409            }
410        })
411        .on_key_down({
412            let on_press = on_press.clone();
413            move |e: Event<KeyboardEventData>| {
414                if Focus::is_pressed(&e) {
415                    on_press.call(e.map(PressEventData::Keyboard))
416                }
417            }
418        })
419    }
420
421    /// Also called the context menu click in other platforms.
422    /// Gets triggered when:
423    /// - **Click**: There is a `MouseUp` (Right button) event in the same element that there had been a `MouseDown` just before
424    fn on_secondary_press(
425        self,
426        on_pointer_press: impl Into<EventHandler<Event<PressEventData>>>,
427    ) -> Self {
428        let on_pointer_press = on_pointer_press.into();
429        self.on_pointer_press({
430            let on_pointer_press = on_pointer_press.clone();
431            move |e: Event<PointerEventData>| {
432                let event = e.try_map(|d| match d {
433                    PointerEventData::Mouse(m) if m.button == Some(MouseButton::Right) => {
434                        Some(PressEventData::Mouse(m))
435                    }
436                    _ => None,
437                });
438                if let Some(event) = event {
439                    on_pointer_press.call(event);
440                }
441            }
442        })
443    }
444
445    /// Gets triggered when:
446    /// - **Click**: There is a `MouseUp` event (Any button) with the in the same element that there had been a `MouseDown` just before
447    /// - **Touched**: There is a `TouchEnd` event in the same element that there had been a `TouchStart` just before
448    /// - **Activated**: The element is focused and there is a keydown event pressing the OS activation key (e.g Space, Enter)
449    fn on_all_press(self, on_press: impl Into<EventHandler<Event<PressEventData>>>) -> Self {
450        let on_press = on_press.into();
451        self.on_pointer_press({
452            let on_press = on_press.clone();
453            move |e: Event<PointerEventData>| {
454                let event = e.try_map(|d| match d {
455                    PointerEventData::Mouse(m) => Some(PressEventData::Mouse(m)),
456                    PointerEventData::Touch(t) => Some(PressEventData::Touch(t)),
457                });
458                if let Some(event) = event {
459                    on_press.call(event);
460                }
461            }
462        })
463        .on_key_down({
464            let on_press = on_press.clone();
465            move |e: Event<KeyboardEventData>| {
466                if Focus::is_pressed(&e) {
467                    on_press.call(e.map(PressEventData::Keyboard))
468                }
469            }
470        })
471    }
472}
473
474#[derive(Debug, Clone, PartialEq)]
475pub enum PressEventData {
476    Mouse(MouseEventData),
477    Keyboard(KeyboardEventData),
478    Touch(TouchEventData),
479}
480
481pub trait ContainerWithContentExt
482where
483    Self: LayoutExt,
484{
485    fn direction(mut self, direction: Direction) -> Self {
486        self.get_layout().layout.direction = direction;
487        self
488    }
489    fn main_align(mut self, main_align: Alignment) -> Self {
490        self.get_layout().layout.main_alignment = main_align;
491        self
492    }
493
494    fn cross_align(mut self, cross_align: Alignment) -> Self {
495        self.get_layout().layout.cross_alignment = cross_align;
496        self
497    }
498
499    fn spacing(mut self, spacing: impl Into<f32>) -> Self {
500        self.get_layout().layout.spacing = Length::new(spacing.into());
501        self
502    }
503
504    fn content(mut self, content: Content) -> Self {
505        self.get_layout().layout.content = content;
506        self
507    }
508    fn center(mut self) -> Self {
509        self.get_layout().layout.main_alignment = Alignment::Center;
510        self.get_layout().layout.cross_alignment = Alignment::Center;
511
512        self
513    }
514
515    fn offset_x(mut self, offset_x: impl Into<f32>) -> Self {
516        self.get_layout().layout.offset_x = Length::new(offset_x.into());
517        self
518    }
519
520    fn offset_y(mut self, offset_y: impl Into<f32>) -> Self {
521        self.get_layout().layout.offset_y = Length::new(offset_y.into());
522        self
523    }
524
525    fn vertical(mut self) -> Self {
526        self.get_layout().layout.direction = Direction::vertical();
527        self
528    }
529
530    fn horizontal(mut self) -> Self {
531        self.get_layout().layout.direction = Direction::horizontal();
532        self
533    }
534}
535
536pub trait ContainerSizeExt
537where
538    Self: LayoutExt,
539{
540    fn width(mut self, width: impl Into<Size>) -> Self {
541        self.get_layout().layout.width = width.into();
542        self
543    }
544
545    fn height(mut self, height: impl Into<Size>) -> Self {
546        self.get_layout().layout.height = height.into();
547        self
548    }
549
550    /// Expand both `width` and `height` using [Size::fill()].
551    fn expanded(mut self) -> Self {
552        self.get_layout().layout.width = Size::fill();
553        self.get_layout().layout.height = Size::fill();
554        self
555    }
556}
557
558impl<T: ContainerExt> ContainerSizeExt for T {}
559
560pub trait ContainerExt
561where
562    Self: LayoutExt,
563{
564    fn position(mut self, position: impl Into<Position>) -> Self {
565        self.get_layout().layout.position = position.into();
566        self
567    }
568
569    fn padding(mut self, padding: impl Into<Gaps>) -> Self {
570        self.get_layout().layout.padding = padding.into();
571        self
572    }
573
574    fn margin(mut self, margin: impl Into<Gaps>) -> Self {
575        self.get_layout().layout.margin = margin.into();
576        self
577    }
578
579    fn min_width(mut self, minimum_width: impl Into<Size>) -> Self {
580        self.get_layout().layout.minimum_width = minimum_width.into();
581        self
582    }
583
584    fn min_height(mut self, minimum_height: impl Into<Size>) -> Self {
585        self.get_layout().layout.minimum_height = minimum_height.into();
586        self
587    }
588
589    fn max_width(mut self, maximum_width: impl Into<Size>) -> Self {
590        self.get_layout().layout.maximum_width = maximum_width.into();
591        self
592    }
593
594    fn max_height(mut self, maximum_height: impl Into<Size>) -> Self {
595        self.get_layout().layout.maximum_height = maximum_height.into();
596        self
597    }
598
599    fn visible_width(mut self, visible_width: impl Into<VisibleSize>) -> Self {
600        self.get_layout().layout.visible_width = visible_width.into();
601        self
602    }
603
604    fn visible_height(mut self, visible_height: impl Into<VisibleSize>) -> Self {
605        self.get_layout().layout.visible_height = visible_height.into();
606        self
607    }
608}
609
610pub trait LayoutExt
611where
612    Self: Sized,
613{
614    fn get_layout(&mut self) -> &mut LayoutData;
615
616    fn layout(mut self, layout: LayoutData) -> Self {
617        *self.get_layout() = layout;
618        self
619    }
620}
621
622pub trait ImageExt
623where
624    Self: LayoutExt,
625{
626    fn get_image_data(&mut self) -> &mut ImageData;
627
628    fn image_data(mut self, image_data: ImageData) -> Self {
629        *self.get_image_data() = image_data;
630        self
631    }
632
633    fn sampling_mode(mut self, sampling_mode: SamplingMode) -> Self {
634        self.get_image_data().sampling_mode = sampling_mode;
635        self
636    }
637
638    fn aspect_ratio(mut self, aspect_ratio: AspectRatio) -> Self {
639        self.get_image_data().aspect_ratio = aspect_ratio;
640        self
641    }
642
643    fn image_cover(mut self, image_cover: ImageCover) -> Self {
644        self.get_image_data().image_cover = image_cover;
645        self
646    }
647}
648
649pub trait AccessibilityExt: Sized {
650    fn get_accessibility_data(&mut self) -> &mut AccessibilityData;
651
652    fn accessibility(mut self, accessibility: AccessibilityData) -> Self {
653        *self.get_accessibility_data() = accessibility;
654        self
655    }
656
657    fn a11y_id(mut self, a11y_id: impl Into<Option<AccessibilityId>>) -> Self {
658        self.get_accessibility_data().a11y_id = a11y_id.into();
659        self
660    }
661
662    fn a11y_focusable(mut self, a11y_focusable: impl Into<Focusable>) -> Self {
663        self.get_accessibility_data().a11y_focusable = a11y_focusable.into();
664        self
665    }
666
667    fn a11y_auto_focus(mut self, a11y_auto_focus: impl Into<bool>) -> Self {
668        self.get_accessibility_data().a11y_auto_focus = a11y_auto_focus.into();
669        self
670    }
671
672    fn a11y_member_of(mut self, a11y_member_of: impl Into<AccessibilityId>) -> Self {
673        self.get_accessibility_data()
674            .builder
675            .set_member_of(a11y_member_of.into());
676        self
677    }
678
679    fn a11y_role(mut self, a11y_role: impl Into<AccessibilityRole>) -> Self {
680        self.get_accessibility_data()
681            .builder
682            .set_role(a11y_role.into());
683        self
684    }
685
686    fn a11y_alt(mut self, value: impl Into<Box<str>>) -> Self {
687        self.get_accessibility_data().builder.set_label(value);
688        self
689    }
690
691    fn a11y_builder(mut self, with: impl FnOnce(&mut accesskit::Node)) -> Self {
692        with(&mut self.get_accessibility_data().builder);
693        self
694    }
695}
696
697pub trait TextStyleExt
698where
699    Self: Sized,
700{
701    fn get_text_style_data(&mut self) -> &mut TextStyleData;
702
703    fn color(mut self, color: impl Into<Color>) -> Self {
704        self.get_text_style_data().color = Some(color.into());
705        self
706    }
707
708    fn text_align(mut self, text_align: impl Into<TextAlign>) -> Self {
709        self.get_text_style_data().text_align = Some(text_align.into());
710        self
711    }
712
713    fn font_size(mut self, font_size: impl Into<FontSize>) -> Self {
714        self.get_text_style_data().font_size = Some(font_size.into());
715        self
716    }
717
718    fn font_family(mut self, font_family: impl Into<Cow<'static, str>>) -> Self {
719        self.get_text_style_data()
720            .font_families
721            .push(font_family.into());
722        self
723    }
724
725    fn font_slant(mut self, font_slant: impl Into<FontSlant>) -> Self {
726        self.get_text_style_data().font_slant = Some(font_slant.into());
727        self
728    }
729
730    fn font_weight(mut self, font_weight: impl Into<FontWeight>) -> Self {
731        self.get_text_style_data().font_weight = Some(font_weight.into());
732        self
733    }
734
735    fn font_width(mut self, font_width: impl Into<FontWidth>) -> Self {
736        self.get_text_style_data().font_width = Some(font_width.into());
737        self
738    }
739
740    fn text_height(mut self, text_height: impl Into<TextHeightBehavior>) -> Self {
741        self.get_text_style_data().text_height = Some(text_height.into());
742        self
743    }
744
745    fn text_overflow(mut self, text_overflow: impl Into<TextOverflow>) -> Self {
746        self.get_text_style_data().text_overflow = Some(text_overflow.into());
747        self
748    }
749
750    fn text_shadow(mut self, text_shadow: impl Into<TextShadow>) -> Self {
751        self.get_text_style_data()
752            .text_shadows
753            .push(text_shadow.into());
754        self
755    }
756}
757
758pub trait StyleExt
759where
760    Self: Sized,
761{
762    fn get_style(&mut self) -> &mut StyleState;
763
764    fn background<S: Into<Color>>(mut self, background: S) -> Self {
765        self.get_style().background = Fill::Color(background.into());
766        self
767    }
768
769    fn background_conic_gradient<S: Into<ConicGradient>>(mut self, background: S) -> Self {
770        self.get_style().background = Fill::ConicGradient(Box::new(background.into()));
771        self
772    }
773
774    fn background_linear_gradient<S: Into<LinearGradient>>(mut self, background: S) -> Self {
775        self.get_style().background = Fill::LinearGradient(Box::new(background.into()));
776        self
777    }
778
779    fn background_radial_gradient<S: Into<RadialGradient>>(mut self, background: S) -> Self {
780        self.get_style().background = Fill::RadialGradient(Box::new(background.into()));
781        self
782    }
783
784    fn border(mut self, border: impl Into<Option<Border>>) -> Self {
785        if let Some(border) = border.into() {
786            self.get_style().borders.push(border);
787        }
788        self
789    }
790
791    fn shadow(mut self, shadow: impl Into<Shadow>) -> Self {
792        self.get_style().shadows.push(shadow.into());
793        self
794    }
795
796    fn corner_radius(mut self, corner_radius: impl Into<CornerRadius>) -> Self {
797        self.get_style().corner_radius = corner_radius.into();
798        self
799    }
800}
801
802impl<T: StyleExt> CornerRadiusExt for T {
803    fn with_corner_radius(mut self, corner_radius: f32) -> Self {
804        self.get_style().corner_radius = CornerRadius::new_all(corner_radius);
805        self
806    }
807}
808
809pub trait CornerRadiusExt: Sized {
810    fn with_corner_radius(self, corner_radius: f32) -> Self;
811
812    /// Shortcut for `corner_radius(0.)` - removes border radius.
813    fn rounded_none(self) -> Self {
814        self.with_corner_radius(0.)
815    }
816
817    /// Shortcut for `corner_radius(6.)` - default border radius.
818    fn rounded(self) -> Self {
819        self.with_corner_radius(6.)
820    }
821
822    /// Shortcut for `corner_radius(4.)` - small border radius.
823    fn rounded_sm(self) -> Self {
824        self.with_corner_radius(4.)
825    }
826
827    /// Shortcut for `corner_radius(6.)` - medium border radius.
828    fn rounded_md(self) -> Self {
829        self.with_corner_radius(6.)
830    }
831
832    /// Shortcut for `corner_radius(8.)` - large border radius.
833    fn rounded_lg(self) -> Self {
834        self.with_corner_radius(8.)
835    }
836
837    /// Shortcut for `corner_radius(12.)` - extra large border radius.
838    fn rounded_xl(self) -> Self {
839        self.with_corner_radius(12.)
840    }
841
842    /// Shortcut for `corner_radius(16.)` - extra large border radius.
843    fn rounded_2xl(self) -> Self {
844        self.with_corner_radius(16.)
845    }
846
847    /// Shortcut for `corner_radius(24.)` - extra large border radius.
848    fn rounded_3xl(self) -> Self {
849        self.with_corner_radius(24.)
850    }
851
852    /// Shortcut for `corner_radius(32.)` - extra large border radius.
853    fn rounded_4xl(self) -> Self {
854        self.with_corner_radius(32.)
855    }
856
857    /// Shortcut for `corner_radius(99.)` - fully rounded (pill shape).
858    fn rounded_full(self) -> Self {
859        self.with_corner_radius(99.)
860    }
861}
862
863pub trait MaybeExt
864where
865    Self: Sized,
866{
867    fn maybe(self, bool: impl Into<bool>, then: impl FnOnce(Self) -> Self) -> Self {
868        if bool.into() { then(self) } else { self }
869    }
870
871    fn map<T>(self, data: Option<T>, then: impl FnOnce(Self, T) -> Self) -> Self {
872        if let Some(data) = data {
873            then(self, data)
874        } else {
875            self
876        }
877    }
878}
879
880pub trait LayerExt
881where
882    Self: Sized,
883{
884    fn get_layer(&mut self) -> &mut Layer;
885
886    fn layer(mut self, layer: impl Into<Layer>) -> Self {
887        *self.get_layer() = layer.into();
888        self
889    }
890}
891
892pub trait ScrollableExt
893where
894    Self: Sized,
895{
896    fn get_effect(&mut self) -> &mut EffectData;
897
898    fn scrollable(mut self, scrollable: impl Into<bool>) -> Self {
899        self.get_effect().scrollable = scrollable.into();
900        self
901    }
902}
903
904pub trait InteractiveExt
905where
906    Self: Sized,
907{
908    fn get_effect(&mut self) -> &mut EffectData;
909
910    fn interactive(mut self, interactive: impl Into<Interactive>) -> Self {
911        self.get_effect().interactive = interactive.into();
912        self
913    }
914}
915
916pub trait EffectExt: Sized {
917    fn get_effect(&mut self) -> &mut EffectData;
918
919    fn effect(mut self, effect: EffectData) -> Self {
920        *self.get_effect() = effect;
921        self
922    }
923
924    fn overflow(mut self, overflow: impl Into<Overflow>) -> Self {
925        self.get_effect().overflow = overflow.into();
926        self
927    }
928
929    fn blur(mut self, blur: impl Into<f32>) -> Self {
930        self.get_effect().blur = Some(blur.into());
931        self
932    }
933
934    fn rotation(mut self, rotation: impl Into<f32>) -> Self {
935        self.get_effect().rotation = Some(rotation.into());
936        self
937    }
938
939    fn opacity(mut self, opacity: impl Into<f32>) -> Self {
940        self.get_effect().opacity = Some(opacity.into());
941        self
942    }
943
944    fn scale(mut self, scale: impl Into<Scale>) -> Self {
945        self.get_effect().scale = Some(scale.into());
946        self
947    }
948}