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