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