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