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