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