Skip to main content

freya_core/elements/
extensions.rs

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