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