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
124/// Trait for giving an element a stable identity across renders.
125pub trait KeyExt: Sized {
126    /// Returns a mutable reference to the element's diff key.
127    fn write_key(&mut self) -> &mut DiffKey;
128
129    /// Assign a key derived from any hashable value, used to reconcile elements in dynamic lists.
130    fn key(mut self, key: impl Hash) -> Self {
131        let mut hasher = FxHasher::default();
132        key.hash(&mut hasher);
133        *self.write_key() = DiffKey::U64(hasher.finish());
134        self
135    }
136}
137
138/// Trait for concatenating two lists into one.
139pub trait ListExt {
140    /// Append the contents of `other`, returning the combined list.
141    fn with(self, other: Self) -> Self;
142}
143
144impl<T> ListExt for Vec<T> {
145    fn with(mut self, other: Self) -> Self {
146        self.extend(other);
147        self
148    }
149}
150
151macro_rules! event_handlers {
152    (
153        $handler_variant:ident, $event_data:ty;
154        $(
155            $(#[$attr:meta])*
156            $name:ident => $event_variant:expr ;
157        )*
158    ) => {
159        paste! {
160            $(
161                $(#[$attr])*
162                fn [<on_$name>](mut self, [<on_$name>]: impl Into<EventHandler<Event<$event_data>>>) -> Self {
163                    self.get_event_handlers()
164                        .insert($event_variant, EventHandlerType::$handler_variant([<on_$name>].into()));
165                    self
166                }
167            )*
168        }
169    };
170}
171
172/// Methods for attaching event handlers to an element.
173///
174/// Many events come in three flavors: the plain one fires only while the pointer is over the
175/// element; the `global_` variants fire no matter where the event happens; and the `capture_`
176/// variants fire during the top-down capture phase, before the event reaches the inner element.
177///
178/// For high-level press handling, prefer [`on_press`](EventHandlersExt::on_press) over the raw mouse/pointer events.
179pub trait EventHandlersExt: Sized {
180    /// Returns a mutable reference to the element's event handler map.
181    fn get_event_handlers(&mut self) -> &mut FxHashMap<EventName, EventHandlerType>;
182
183    /// Replace all of this element's event handlers with the given map.
184    fn with_event_handlers(
185        mut self,
186        event_handlers: FxHashMap<EventName, EventHandlerType>,
187    ) -> Self {
188        *self.get_event_handlers() = event_handlers;
189        self
190    }
191
192    event_handlers! {
193        Mouse,
194        MouseEventData;
195
196        /// Fires when a mouse button is pressed down over the element.
197        mouse_down => EventName::MouseDown;
198        /// Fires when a mouse button is released over the element.
199        mouse_up => EventName::MouseUp;
200        /// Fires when the cursor moves over the element.
201        mouse_move => EventName::MouseMove;
202
203    }
204
205    event_handlers! {
206        Pointer,
207        PointerEventData;
208
209        /// Fires when a pointer (mouse or touch) is pressed anywhere, even outside the element.
210        global_pointer_press => EventName::GlobalPointerPress;
211        /// Fires when a pointer (mouse or touch) goes down anywhere, even outside the element.
212        global_pointer_down => EventName::GlobalPointerDown;
213        /// Fires when a pointer (mouse or touch) moves anywhere, even outside the element.
214        global_pointer_move => EventName::GlobalPointerMove;
215
216        /// Like [`on_global_pointer_move`](Self::on_global_pointer_move), but fires during the top-down capture phase.
217        capture_global_pointer_move => EventName::CaptureGlobalPointerMove;
218        /// Like [`on_global_pointer_press`](Self::on_global_pointer_press), but fires during the top-down capture phase.
219        capture_global_pointer_press => EventName::CaptureGlobalPointerPress;
220    }
221
222    event_handlers! {
223        Keyboard,
224        KeyboardEventData;
225
226        /// Fires when a key is pressed down while the element is focused.
227        key_down => EventName::KeyDown;
228        /// Fires when a key is released while the element is focused.
229        key_up => EventName::KeyUp;
230
231        /// Fires when a key is pressed down, regardless of which element is focused.
232        global_key_down => EventName::GlobalKeyDown;
233        /// Fires when a key is released, regardless of which element is focused.
234        global_key_up => EventName::GlobalKeyUp;
235    }
236
237    event_handlers! {
238        Wheel,
239        WheelEventData;
240
241        /// Fires when the scroll wheel is used over the element.
242        wheel => EventName::Wheel;
243    }
244
245    event_handlers! {
246        Touch,
247        TouchEventData;
248
249        /// Fires when an ongoing touch is cancelled by the system.
250        touch_cancel => EventName::TouchCancel;
251        /// Fires when a touch point is placed on the element.
252        touch_start => EventName::TouchStart;
253        /// Fires when a touch point moves across the element.
254        touch_move => EventName::TouchMove;
255        /// Fires when a touch point is lifted from the element.
256        touch_end => EventName::TouchEnd;
257    }
258
259    event_handlers! {
260        Pointer,
261        PointerEventData;
262
263        /// Fires when the element is pressed and released by a pointer (mouse or touch).
264        pointer_press => EventName::PointerPress;
265        /// Fires when a pointer (mouse or touch) goes down over the element.
266        pointer_down => EventName::PointerDown;
267        /// Fires when a pointer (mouse or touch) moves over the element.
268        pointer_move => EventName::PointerMove;
269        /// Fires when a pointer enters the element.
270        pointer_enter => EventName::PointerEnter;
271        /// Fires when a pointer leaves the element.
272        pointer_leave => EventName::PointerLeave;
273        /// Fires when a pointer is over the element, including over its children.
274        pointer_over => EventName::PointerOver;
275        /// Fires when a pointer leaves the element or one of its children.
276        pointer_out => EventName::PointerOut;
277    }
278
279    event_handlers! {
280        File,
281        FileEventData;
282
283        /// Fires when a file is dropped onto the element.
284        file_drop => EventName::FileDrop;
285        /// Fires when a dragged file hovers anywhere over the window.
286        global_file_hover => EventName::GlobalFileHover;
287        /// Fires when a dragged file stops hovering over the window.
288        global_file_hover_cancelled => EventName::GlobalFileHoverCancelled;
289    }
290
291    event_handlers! {
292        ImePreedit,
293        ImePreeditEventData;
294
295        /// Fires while text is being composed through an input method editor (IME).
296        ime_preedit => EventName::ImePreedit;
297    }
298
299    /// Fires when the element's measured size or position changes.
300    fn on_sized(mut self, on_sized: impl Into<EventHandler<Event<SizedEventData>>>) -> Self
301    where
302        Self: LayoutExt,
303    {
304        self.get_event_handlers()
305            .insert(EventName::Sized, EventHandlerType::Sized(on_sized.into()));
306        self.get_layout().layout.has_layout_references = true;
307        self
308    }
309
310    /// This is generally the best event in which to run "press" logic, this might be called `onClick`, `onActivate`, or `onConnect` in other platforms.
311    ///
312    /// Gets triggered when:
313    /// - **Click**: There is a `MouseUp` event (Left button) with the in the same element that there had been a `MouseDown` just before
314    /// - **Touched**: There is a `TouchEnd` event in the same element that there had been a `TouchStart` just before
315    /// - **Activated**: The element is focused and there is a keydown event pressing the OS activation key (e.g Space, Enter)
316    fn on_press(self, on_press: impl Into<EventHandler<Event<PressEventData>>>) -> Self {
317        let on_press = on_press.into();
318        self.on_pointer_press({
319            let on_press = on_press.clone();
320            move |e: Event<PointerEventData>| {
321                let event = e.try_map(|d| match d {
322                    PointerEventData::Mouse(m) if m.button == Some(MouseButton::Left) => {
323                        Some(PressEventData::Mouse(m))
324                    }
325                    PointerEventData::Touch(t) => Some(PressEventData::Touch(t)),
326                    _ => None,
327                });
328                if let Some(event) = event {
329                    on_press.call(event);
330                }
331            }
332        })
333        .on_key_down(move |e: Event<KeyboardEventData>| {
334            if e.is_press_event() {
335                on_press.call(e.map(PressEventData::Keyboard))
336            }
337        })
338    }
339
340    /// Also called the context menu click in other platforms.
341    /// Gets triggered when:
342    /// - **Click**: There is a `MouseDown` (Right button) event
343    fn on_secondary_down(
344        self,
345        on_secondary_down: impl Into<EventHandler<Event<PressEventData>>>,
346    ) -> Self {
347        let on_secondary_down = on_secondary_down.into();
348        self.on_pointer_down(move |e: Event<PointerEventData>| {
349            let event = e.try_map(|d| match d {
350                PointerEventData::Mouse(m) if m.button == Some(MouseButton::Right) => {
351                    Some(PressEventData::Mouse(m))
352                }
353                _ => None,
354            });
355            if let Some(event) = event {
356                on_secondary_down.call(event);
357            }
358        })
359    }
360
361    /// Gets triggered when:
362    /// - **Click**: There is a `MouseUp` event (Any button) with the in the same element that there had been a `MouseDown` just before
363    /// - **Touched**: There is a `TouchEnd` event in the same element that there had been a `TouchStart` just before
364    /// - **Activated**: The element is focused and there is a keydown event pressing the OS activation key (e.g Space, Enter)
365    fn on_all_press(self, on_press: impl Into<EventHandler<Event<PressEventData>>>) -> Self {
366        let on_press = on_press.into();
367        self.on_pointer_press({
368            let on_press = on_press.clone();
369            move |e: Event<PointerEventData>| {
370                let event = e.map(|d| match d {
371                    PointerEventData::Mouse(m) => PressEventData::Mouse(m),
372                    PointerEventData::Touch(t) => PressEventData::Touch(t),
373                });
374                on_press.call(event);
375            }
376        })
377        .on_key_down(move |e: Event<KeyboardEventData>| {
378            if e.is_press_event() {
379                on_press.call(e.map(PressEventData::Keyboard))
380            }
381        })
382    }
383    /// Gets triggered when:
384    /// - **Started clicking**: There is a `MouseDown` event (Left button)
385    /// - **Touched**: There is a `TouchEnd` event in the same element that there had been a `TouchStart` just before
386    ///
387    /// This event is intended to focus elements such as text inputs following each platform style.
388    fn on_focus_press(
389        self,
390        on_focus_press: impl Into<EventHandler<Event<FocusPressEventData>>>,
391    ) -> Self {
392        let on_focus_press = on_focus_press.into();
393        if cfg!(target_os = "android") {
394            self.on_pointer_press(move |e: Event<PointerEventData>| {
395                let event = e.try_map(|d| match d {
396                    PointerEventData::Mouse(m) if m.button == Some(MouseButton::Left) => {
397                        Some(FocusPressEventData::Mouse(m))
398                    }
399                    PointerEventData::Touch(t) => Some(FocusPressEventData::Touch(t)),
400                    _ => None,
401                });
402                if let Some(event) = event {
403                    on_focus_press.call(event);
404                }
405            })
406        } else {
407            self.on_pointer_down(move |e: Event<PointerEventData>| {
408                let event = e.try_map(|d| match d {
409                    PointerEventData::Mouse(m) if m.button == Some(MouseButton::Left) => {
410                        Some(FocusPressEventData::Mouse(m))
411                    }
412                    PointerEventData::Touch(t) => Some(FocusPressEventData::Touch(t)),
413                    _ => None,
414                });
415                if let Some(event) = event {
416                    on_focus_press.call(event);
417                }
418            })
419        }
420    }
421}
422
423/// Data delivered to [`on_focus_press`](EventHandlersExt::on_focus_press), which can originate from a mouse or a touch.
424#[derive(Debug, Clone, PartialEq)]
425pub enum FocusPressEventData {
426    Mouse(MouseEventData),
427    Touch(TouchEventData),
428}
429
430impl FocusPressEventData {
431    pub fn global_location(&self) -> CursorPoint {
432        match self {
433            Self::Mouse(m) => m.global_location,
434            Self::Touch(t) => t.global_location,
435        }
436    }
437
438    pub fn element_location(&self) -> CursorPoint {
439        match self {
440            Self::Mouse(m) => m.element_location,
441            Self::Touch(t) => t.element_location,
442        }
443    }
444
445    pub fn button(&self) -> Option<MouseButton> {
446        match self {
447            Self::Mouse(m) => m.button,
448            Self::Touch(_) => None,
449        }
450    }
451}
452
453/// Data delivered to [`on_press`](EventHandlersExt::on_press), which can originate from a mouse, the keyboard or a touch.
454#[derive(Debug, Clone, PartialEq)]
455pub enum PressEventData {
456    Mouse(MouseEventData),
457    Keyboard(KeyboardEventData),
458    Touch(TouchEventData),
459}
460
461/// Layout methods for containers that arrange children along a direction axis.
462pub trait ContainerWithContentExt
463where
464    Self: LayoutExt,
465{
466    /// Set the axis children are stacked along. See [`Direction`].
467    fn direction(mut self, direction: Direction) -> Self {
468        self.get_layout().layout.direction = direction;
469        self
470    }
471    /// Set how children are aligned along the direction axis. See [`Alignment`].
472    fn main_align(mut self, main_align: Alignment) -> Self {
473        self.get_layout().layout.main_alignment = main_align;
474        self
475    }
476
477    /// Set how children are aligned across the direction axis. See [`Alignment`].
478    fn cross_align(mut self, cross_align: Alignment) -> Self {
479        self.get_layout().layout.cross_alignment = cross_align;
480        self
481    }
482
483    /// Set the gap inserted between adjacent children, in pixels.
484    fn spacing(mut self, spacing: impl Into<f32>) -> Self {
485        self.get_layout().layout.spacing = Length::new(spacing.into());
486        self
487    }
488
489    /// Set how children share the available space along the direction axis. See [`Content`].
490    fn content(mut self, content: Content) -> Self {
491        self.get_layout().layout.content = content;
492        self
493    }
494    /// Center children on both axes. Shorthand for [`main_align`](Self::main_align) and [`cross_align`](Self::cross_align) set to [`Alignment::Center`].
495    fn center(mut self) -> Self {
496        self.get_layout().layout.main_alignment = Alignment::Center;
497        self.get_layout().layout.cross_alignment = Alignment::Center;
498
499        self
500    }
501
502    /// Shift the element's children horizontally by the given pixels.
503    fn offset_x(mut self, offset_x: impl Into<f32>) -> Self {
504        self.get_layout().layout.offset_x = Length::new(offset_x.into());
505        self
506    }
507
508    /// Shift the element's children vertically by the given pixels.
509    fn offset_y(mut self, offset_y: impl Into<f32>) -> Self {
510        self.get_layout().layout.offset_y = Length::new(offset_y.into());
511        self
512    }
513
514    /// Stack children vertically. Shorthand for [`direction`](Self::direction) set to [`Direction::Vertical`].
515    fn vertical(mut self) -> Self {
516        self.get_layout().layout.direction = Direction::vertical();
517        self
518    }
519
520    /// Stack children horizontally. Shorthand for [`direction`](Self::direction) set to [`Direction::Horizontal`].
521    fn horizontal(mut self) -> Self {
522        self.get_layout().layout.direction = Direction::horizontal();
523        self
524    }
525}
526
527/// Methods for setting an element's width and height.
528pub trait ContainerSizeExt
529where
530    Self: LayoutExt,
531{
532    /// Set the element's width. See [`Size`].
533    fn width(mut self, width: impl Into<Size>) -> Self {
534        self.get_layout().layout.width = width.into();
535        self
536    }
537
538    /// Set the element's height. See [`Size`].
539    fn height(mut self, height: impl Into<Size>) -> Self {
540        self.get_layout().layout.height = height.into();
541        self
542    }
543
544    /// Expand both `width` and `height` using [Size::fill()].
545    fn expanded(mut self) -> Self {
546        self.get_layout().layout.width = Size::fill();
547        self.get_layout().layout.height = Size::fill();
548        self
549    }
550}
551
552impl<T: ContainerExt> ContainerSizeExt for T {}
553
554/// Methods controlling an element's position and size constraints.
555pub trait ContainerExt
556where
557    Self: LayoutExt,
558{
559    /// Set how the element is placed relative to its parent or the window. See [`Position`].
560    fn position(mut self, position: impl Into<Position>) -> Self {
561        self.get_layout().layout.position = position.into();
562        self
563    }
564
565    /// Set the inner spacing between the element's edges and its content. See [`Gaps`].
566    fn padding(mut self, padding: impl Into<Gaps>) -> Self {
567        self.get_layout().layout.padding = padding.into();
568        self
569    }
570
571    /// Set the outer spacing between the element's edges and its surroundings. See [`Gaps`].
572    fn margin(mut self, margin: impl Into<Gaps>) -> Self {
573        self.get_layout().layout.margin = margin.into();
574        self
575    }
576
577    /// Set the minimum width the element can shrink to. See [`Size`].
578    fn min_width(mut self, minimum_width: impl Into<Size>) -> Self {
579        self.get_layout().layout.minimum_width = minimum_width.into();
580        self
581    }
582
583    /// Set the minimum height the element can shrink to. See [`Size`].
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    /// Set the maximum width the element can grow to. See [`Size`].
590    fn max_width(mut self, maximum_width: impl Into<Size>) -> Self {
591        self.get_layout().layout.maximum_width = maximum_width.into();
592        self
593    }
594
595    /// Set the maximum height the element can grow to. See [`Size`].
596    fn max_height(mut self, maximum_height: impl Into<Size>) -> Self {
597        self.get_layout().layout.maximum_height = maximum_height.into();
598        self
599    }
600
601    /// Set how much of the measured width is actually used in layout. See [`VisibleSize`].
602    fn visible_width(mut self, visible_width: impl Into<VisibleSize>) -> Self {
603        self.get_layout().layout.visible_width = visible_width.into();
604        self
605    }
606
607    /// Set how much of the measured height is actually used in layout. See [`VisibleSize`].
608    fn visible_height(mut self, visible_height: impl Into<VisibleSize>) -> Self {
609        self.get_layout().layout.visible_height = visible_height.into();
610        self
611    }
612}
613
614/// Low-level access to an element's [`LayoutData`].
615pub trait LayoutExt
616where
617    Self: Sized,
618{
619    /// Returns a mutable reference to the element's layout data.
620    fn get_layout(&mut self) -> &mut LayoutData;
621
622    /// Replace all of the element's layout data at once. See [`LayoutData`].
623    fn layout(mut self, layout: LayoutData) -> Self {
624        *self.get_layout() = layout;
625        self
626    }
627}
628
629/// Methods for configuring how an image is scaled and sampled.
630pub trait ImageExt
631where
632    Self: LayoutExt,
633{
634    /// Returns a mutable reference to the element's image data.
635    fn get_image_data(&mut self) -> &mut ImageData;
636
637    /// Replace all of the element's image data at once. See [`ImageData`].
638    fn image_data(mut self, image_data: ImageData) -> Self {
639        *self.get_image_data() = image_data;
640        self
641    }
642
643    /// Set the filtering used when the image is scaled. See [`SamplingMode`].
644    fn sampling_mode(mut self, sampling_mode: SamplingMode) -> Self {
645        self.get_image_data().sampling_mode = sampling_mode;
646        self
647    }
648
649    /// Set how the image is scaled to fit its bounds. See [`AspectRatio`].
650    fn aspect_ratio(mut self, aspect_ratio: AspectRatio) -> Self {
651        self.get_image_data().aspect_ratio = aspect_ratio;
652        self
653    }
654
655    /// Set how the image is positioned within its bounds. See [`ImageCover`].
656    fn image_cover(mut self, image_cover: ImageCover) -> Self {
657        self.get_image_data().image_cover = image_cover;
658        self
659    }
660}
661
662/// Methods for describing an element in the accessibility tree.
663pub trait AccessibilityExt: Sized {
664    /// Returns a mutable reference to the element's accessibility data.
665    fn get_accessibility_data(&mut self) -> &mut AccessibilityData;
666
667    /// Replace all of the element's accessibility data at once. See [`AccessibilityData`].
668    fn accessibility(mut self, accessibility: AccessibilityData) -> Self {
669        *self.get_accessibility_data() = accessibility;
670        self
671    }
672
673    /// Set an explicit accessibility id instead of an autogenerated one. See [`AccessibilityId`].
674    fn a11y_id(mut self, a11y_id: impl Into<Option<AccessibilityId>>) -> Self {
675        self.get_accessibility_data().a11y_id = a11y_id.into();
676        self
677    }
678
679    /// Set whether the element can receive keyboard focus. See [`Focusable`].
680    fn a11y_focusable(mut self, a11y_focusable: impl Into<Focusable>) -> Self {
681        self.get_accessibility_data().a11y_focusable = a11y_focusable.into();
682        self
683    }
684
685    /// Request that the element be focused automatically when it is mounted.
686    fn a11y_auto_focus(mut self, a11y_auto_focus: impl Into<bool>) -> Self {
687        self.get_accessibility_data().a11y_auto_focus = a11y_auto_focus.into();
688        self
689    }
690
691    /// Mark the element as a member of the group identified by the given [`AccessibilityId`].
692    fn a11y_member_of(mut self, a11y_member_of: impl Into<AccessibilityId>) -> Self {
693        self.get_accessibility_data()
694            .builder
695            .set_member_of(a11y_member_of.into());
696        self
697    }
698
699    /// Set the accessibility role exposed in the accessibility tree. See [`AccessibilityRole`].
700    fn a11y_role(mut self, a11y_role: impl Into<AccessibilityRole>) -> Self {
701        self.get_accessibility_data()
702            .builder
703            .set_role(a11y_role.into());
704        self
705    }
706
707    /// Set the text label that describes the element in the accessibility tree.
708    fn a11y_alt(mut self, value: impl Into<Box<str>>) -> Self {
709        self.get_accessibility_data().builder.set_label(value);
710        self
711    }
712
713    /// Edit the underlying `accesskit` node directly for advanced accessibility properties.
714    fn a11y_builder(mut self, with: impl FnOnce(&mut accesskit::Node)) -> Self {
715        with(&mut self.get_accessibility_data().builder);
716        self
717    }
718}
719
720/// Methods for styling the text rendered by an element and inherited by its children.
721pub trait TextStyleExt
722where
723    Self: Sized,
724{
725    /// Returns a mutable reference to the element's text style data.
726    fn get_text_style_data(&mut self) -> &mut TextStyleData;
727
728    /// Replace all of the element's text style data at once. See [`TextStyleData`].
729    fn text_style(mut self, data: TextStyleData) -> Self {
730        *self.get_text_style_data() = data;
731        self
732    }
733
734    /// Set the text color to a solid [`Color`].
735    fn color(mut self, color: impl Into<Color>) -> Self {
736        self.get_text_style_data().color = Some(Fill::Color(color.into()));
737        self
738    }
739
740    /// Paint the text with a [`ConicGradient`].
741    fn color_conic_gradient<S: Into<ConicGradient>>(mut self, color: S) -> Self {
742        self.get_text_style_data().color = Some(Fill::ConicGradient(Box::new(color.into())));
743        self
744    }
745
746    /// Paint the text with a [`LinearGradient`].
747    fn color_linear_gradient<S: Into<LinearGradient>>(mut self, color: S) -> Self {
748        self.get_text_style_data().color = Some(Fill::LinearGradient(Box::new(color.into())));
749        self
750    }
751
752    /// Paint the text with a [`RadialGradient`].
753    fn color_radial_gradient<S: Into<RadialGradient>>(mut self, color: S) -> Self {
754        self.get_text_style_data().color = Some(Fill::RadialGradient(Box::new(color.into())));
755        self
756    }
757
758    /// Paint the text with a custom shader. See [`ShaderFill`].
759    fn color_shader(mut self, color: impl Into<ShaderFill>) -> Self {
760        self.get_text_style_data().color = Some(Fill::Shader(Box::new(color.into())));
761        self
762    }
763
764    /// Set the horizontal alignment of the text. See [`TextAlign`].
765    fn text_align(mut self, text_align: impl Into<TextAlign>) -> Self {
766        self.get_text_style_data().text_align = Some(text_align.into());
767        self
768    }
769
770    /// Set the text size in pixels. See [`FontSize`].
771    fn font_size(mut self, font_size: impl Into<FontSize>) -> Self {
772        self.get_text_style_data().font_size = Some(font_size.into());
773        self
774    }
775
776    /// Add a font family to try, in order of preference.
777    fn font_family(mut self, font_family: impl Into<Cow<'static, str>>) -> Self {
778        self.get_text_style_data()
779            .font_families
780            .push(font_family.into());
781        self
782    }
783
784    /// Set the slant (style) of the font. See [`FontSlant`].
785    fn font_slant(mut self, font_slant: impl Into<FontSlant>) -> Self {
786        self.get_text_style_data().font_slant = Some(font_slant.into());
787        self
788    }
789
790    /// Set the thickness of the font. See [`FontWeight`].
791    fn font_weight(mut self, font_weight: impl Into<FontWeight>) -> Self {
792        self.get_text_style_data().font_weight = Some(font_weight.into());
793        self
794    }
795
796    /// Set the horizontal width of the font. See [`FontWidth`].
797    fn font_width(mut self, font_width: impl Into<FontWidth>) -> Self {
798        self.get_text_style_data().font_width = Some(font_width.into());
799        self
800    }
801
802    /// Set how the leading of the first and last lines is handled. See [`TextHeightBehavior`].
803    fn text_height(mut self, text_height: impl Into<TextHeightBehavior>) -> Self {
804        self.get_text_style_data().text_height = Some(text_height.into());
805        self
806    }
807
808    /// Set how text that does not fit its bounds is truncated. See [`TextOverflow`].
809    fn text_overflow(mut self, text_overflow: impl Into<TextOverflow>) -> Self {
810        self.get_text_style_data().text_overflow = Some(text_overflow.into());
811        self
812    }
813
814    /// Add a shadow cast behind the text. See [`TextShadow`].
815    fn text_shadow(mut self, text_shadow: impl Into<TextShadow>) -> Self {
816        self.get_text_style_data()
817            .text_shadows
818            .push(text_shadow.into());
819        self
820    }
821
822    /// Set a line drawn through, under or over the text. See [`TextDecoration`].
823    fn text_decoration(mut self, text_decoration: impl Into<TextDecoration>) -> Self {
824        self.get_text_style_data().text_decoration = Some(text_decoration.into());
825        self
826    }
827}
828
829/// Methods for styling an element's box: background, borders, shadows and corners.
830pub trait StyleExt
831where
832    Self: Sized,
833{
834    /// Returns a mutable reference to the element's style data.
835    fn get_style(&mut self) -> &mut StyleState;
836
837    /// Set the background to a solid [`Color`].
838    fn background<S: Into<Color>>(mut self, background: S) -> Self {
839        self.get_style().background = Fill::Color(background.into());
840        self
841    }
842
843    /// Paint the background with a [`ConicGradient`].
844    fn background_conic_gradient<S: Into<ConicGradient>>(mut self, background: S) -> Self {
845        self.get_style().background = Fill::ConicGradient(Box::new(background.into()));
846        self
847    }
848
849    /// Paint the background with a [`LinearGradient`].
850    fn background_linear_gradient<S: Into<LinearGradient>>(mut self, background: S) -> Self {
851        self.get_style().background = Fill::LinearGradient(Box::new(background.into()));
852        self
853    }
854
855    /// Paint the background with a [`RadialGradient`].
856    fn background_radial_gradient<S: Into<RadialGradient>>(mut self, background: S) -> Self {
857        self.get_style().background = Fill::RadialGradient(Box::new(background.into()));
858        self
859    }
860
861    /// Paint the background with a custom shader. See [`ShaderFill`].
862    fn background_shader(mut self, background: impl Into<ShaderFill>) -> Self {
863        self.get_style().background = Fill::Shader(Box::new(background.into()));
864        self
865    }
866
867    /// Add an outline around the element. See [`Border`].
868    fn border(mut self, border: impl Into<Option<Border>>) -> Self {
869        if let Some(border) = border.into() {
870            self.get_style().borders.push(border);
871        }
872        self
873    }
874
875    /// Add a shadow cast by the element. See [`Shadow`].
876    fn shadow(mut self, shadow: impl Into<Shadow>) -> Self {
877        self.get_style().shadows.push(shadow.into());
878        self
879    }
880
881    /// Round the element's corners. See [`CornerRadius`].
882    fn corner_radius(mut self, corner_radius: impl Into<CornerRadius>) -> Self {
883        self.get_style().corner_radius = corner_radius.into();
884        self
885    }
886}
887
888impl<T: StyleExt> CornerRadiusExt for T {
889    fn with_corner_radius(mut self, corner_radius: f32) -> Self {
890        self.get_style().corner_radius = CornerRadius::new_all(corner_radius);
891        self
892    }
893}
894
895/// Shorthand methods for setting an element's [`CornerRadius`] to common values.
896pub trait CornerRadiusExt: Sized {
897    /// Round all four corners to the given radius in pixels.
898    fn with_corner_radius(self, corner_radius: f32) -> Self;
899
900    /// Shortcut for `corner_radius(0.)` - removes border radius.
901    fn rounded_none(self) -> Self {
902        self.with_corner_radius(0.)
903    }
904
905    /// Shortcut for `corner_radius(6.)` - default border radius.
906    fn rounded(self) -> Self {
907        self.with_corner_radius(6.)
908    }
909
910    /// Shortcut for `corner_radius(4.)` - small border radius.
911    fn rounded_sm(self) -> Self {
912        self.with_corner_radius(4.)
913    }
914
915    /// Shortcut for `corner_radius(6.)` - medium border radius.
916    fn rounded_md(self) -> Self {
917        self.with_corner_radius(6.)
918    }
919
920    /// Shortcut for `corner_radius(8.)` - large border radius.
921    fn rounded_lg(self) -> Self {
922        self.with_corner_radius(8.)
923    }
924
925    /// Shortcut for `corner_radius(12.)` - extra large border radius.
926    fn rounded_xl(self) -> Self {
927        self.with_corner_radius(12.)
928    }
929
930    /// Shortcut for `corner_radius(16.)` - extra large border radius.
931    fn rounded_2xl(self) -> Self {
932        self.with_corner_radius(16.)
933    }
934
935    /// Shortcut for `corner_radius(24.)` - extra large border radius.
936    fn rounded_3xl(self) -> Self {
937        self.with_corner_radius(24.)
938    }
939
940    /// Shortcut for `corner_radius(32.)` - extra large border radius.
941    fn rounded_4xl(self) -> Self {
942        self.with_corner_radius(32.)
943    }
944
945    /// Shortcut for `corner_radius(99.)` - fully rounded (pill shape).
946    fn rounded_full(self) -> Self {
947        self.with_corner_radius(99.)
948    }
949}
950
951/// Methods for applying changes to an element conditionally.
952pub trait MaybeExt
953where
954    Self: Sized,
955{
956    /// Apply `then` to the element only when the condition is `true`.
957    fn maybe(self, bool: impl Into<bool>, then: impl FnOnce(Self) -> Self) -> Self {
958        if bool.into() { then(self) } else { self }
959    }
960
961    /// Apply `then` to the element only when the [`Option`] is [`Some`], passing the inner value.
962    fn map<T>(self, data: Option<T>, then: impl FnOnce(Self, T) -> Self) -> Self {
963        if let Some(data) = data {
964            then(self, data)
965        } else {
966            self
967        }
968    }
969}
970
971/// Method for controlling which painting layer an element belongs to.
972pub trait LayerExt
973where
974    Self: Sized,
975{
976    /// Returns a mutable reference to the element's layer.
977    fn get_layer(&mut self) -> &mut Layer;
978
979    /// Set the painting layer of the element. See [`Layer`].
980    fn layer(mut self, layer: impl Into<Layer>) -> Self {
981        *self.get_layer() = layer.into();
982        self
983    }
984}
985
986pub trait ScrollableExt
987where
988    Self: Sized,
989{
990    /// Returns a mutable reference to the element's effect data.
991    fn get_effect(&mut self) -> &mut EffectData;
992
993    /// Mark this element as scrollable.
994    /// You are probably looking for the `ScrollView` component instead.
995    fn scrollable(mut self, scrollable: impl Into<bool>) -> Self {
996        self.get_effect().scrollable = scrollable.into();
997        self
998    }
999}
1000
1001/// Method for controlling whether an element responds to pointer events.
1002pub trait InteractiveExt
1003where
1004    Self: Sized,
1005{
1006    /// Returns a mutable reference to the element's effect data.
1007    fn get_effect(&mut self) -> &mut EffectData;
1008
1009    /// Set whether the element receives pointer events. See [`Interactive`].
1010    fn interactive(mut self, interactive: impl Into<Interactive>) -> Self {
1011        self.get_effect().interactive = interactive.into();
1012        self
1013    }
1014}
1015
1016/// Methods for visual effects applied to an element: clipping, blur, rotation, opacity and scale.
1017pub trait EffectExt: Sized {
1018    /// Returns a mutable reference to the element's effect data.
1019    fn get_effect(&mut self) -> &mut EffectData;
1020
1021    /// Replace all of the element's effect data at once. See [`EffectData`].
1022    fn effect(mut self, effect: EffectData) -> Self {
1023        *self.get_effect() = effect;
1024        self
1025    }
1026
1027    /// Set whether content overflowing the element's bounds is clipped. See [`Overflow`].
1028    fn overflow(mut self, overflow: impl Into<Overflow>) -> Self {
1029        self.get_effect().overflow = overflow.into();
1030        self
1031    }
1032
1033    /// Apply a gaussian blur of the given radius to the element.
1034    fn blur(mut self, blur: impl Into<f32>) -> Self {
1035        self.get_effect().blur = Some(blur.into());
1036        self
1037    }
1038
1039    /// Rotate the element by the given angle in degrees.
1040    fn rotation(mut self, rotation: impl Into<f32>) -> Self {
1041        self.get_effect().rotation = Some(rotation.into());
1042        self
1043    }
1044
1045    /// Set the element's opacity, from `0.0` (transparent) to `1.0` (opaque).
1046    fn opacity(mut self, opacity: impl Into<f32>) -> Self {
1047        self.get_effect().opacity = Some(opacity.into());
1048        self
1049    }
1050
1051    /// Scale the element. See [`Scale`].
1052    fn scale(mut self, scale: impl Into<Scale>) -> Self {
1053        self.get_effect().scale = Some(scale.into());
1054        self
1055    }
1056
1057    /// Set the point that the scale and rotation effects pivot around.
1058    ///
1059    /// Defaults to the element's center.
1060    fn transform_origin(mut self, transform_origin: impl Into<TransformOrigin>) -> Self {
1061        self.get_effect().transform_origin = transform_origin.into();
1062        self
1063    }
1064}