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