Skip to main content

freya_core/elements/
extensions.rs

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