Skip to main content

vizia_core/modifiers/
actions.rs

1use crate::prelude::*;
2use std::any::TypeId;
3
4pub(crate) struct ModalModel {
5    pub tooltip_visible: Signal<(bool, bool)>,
6    pub menu_visible: Signal<bool>,
7}
8
9/// An event used to modify the modal properties of a view, such as an attached tooltip.
10pub enum ModalEvent {
11    /// Show the attached tooltip.
12    ShowTooltip,
13    /// Hide the attached tooltip.
14    HideTooltip,
15    /// Show the attached menu.
16    ShowMenu,
17    /// Hide the attached menu.
18    HideMenu,
19}
20
21impl Model for ModalModel {
22    fn event(&mut self, _cx: &mut EventContext, event: &mut Event) {
23        event.map(|modal_event, _| match modal_event {
24            ModalEvent::ShowTooltip => {
25                self.tooltip_visible.set((true, true));
26            }
27
28            ModalEvent::HideTooltip => {
29                self.tooltip_visible.set((false, true));
30            }
31
32            ModalEvent::ShowMenu => {
33                self.menu_visible.set(true);
34            }
35
36            ModalEvent::HideMenu => {
37                self.menu_visible.set(false);
38            }
39        });
40
41        event.map(|window_event, _| match window_event {
42            WindowEvent::MouseOver => {
43                if !self.tooltip_visible.get().0 {
44                    self.tooltip_visible.set((true, true));
45                }
46            }
47            WindowEvent::MouseOut => self.tooltip_visible.set((false, true)),
48            WindowEvent::FocusIn => {
49                if !self.tooltip_visible.get().0 {
50                    self.tooltip_visible.set((true, false));
51                }
52            }
53            WindowEvent::FocusOut => self.tooltip_visible.set((false, false)),
54            WindowEvent::FocusVisibility(vis) if !(*vis) => {
55                self.tooltip_visible.set((false, false))
56            }
57            WindowEvent::KeyDown(code, _) if *code == Code::Escape => {
58                self.tooltip_visible.set((false, false));
59            }
60            WindowEvent::PressDown { mouse: _ } => self.tooltip_visible.set((false, true)),
61            _ => {}
62        });
63    }
64}
65
66pub(crate) struct ActionsModel {
67    pub(crate) on_press: Option<Box<dyn Fn(&mut EventContext) + Send + Sync>>,
68    pub(crate) on_press_down: Option<Box<dyn Fn(&mut EventContext) + Send + Sync>>,
69    pub(crate) on_double_click: Option<Box<dyn Fn(&mut EventContext, MouseButton) + Send + Sync>>,
70    pub(crate) on_hover: Option<Box<dyn Fn(&mut EventContext) + Send + Sync>>,
71    pub(crate) on_hover_out: Option<Box<dyn Fn(&mut EventContext) + Send + Sync>>,
72    pub(crate) on_over: Option<Box<dyn Fn(&mut EventContext) + Send + Sync>>,
73    pub(crate) on_over_out: Option<Box<dyn Fn(&mut EventContext) + Send + Sync>>,
74    pub(crate) on_mouse_move: Option<Box<dyn Fn(&mut EventContext, f32, f32) + Send + Sync>>,
75    pub(crate) on_mouse_down: Option<Box<dyn Fn(&mut EventContext, MouseButton) + Send + Sync>>,
76    pub(crate) on_mouse_up: Option<Box<dyn Fn(&mut EventContext, MouseButton) + Send + Sync>>,
77    pub(crate) on_focus_in: Option<Box<dyn Fn(&mut EventContext) + Send + Sync>>,
78    pub(crate) on_focus_out: Option<Box<dyn Fn(&mut EventContext) + Send + Sync>>,
79    pub(crate) on_geo_changed: Option<Box<dyn Fn(&mut EventContext, GeoChanged) + Send + Sync>>,
80}
81
82impl ActionsModel {
83    pub(crate) fn new() -> Self {
84        Self {
85            on_press: None,
86            on_press_down: None,
87            on_double_click: None,
88            on_hover: None,
89            on_hover_out: None,
90            on_over: None,
91            on_over_out: None,
92            on_mouse_move: None,
93            on_mouse_down: None,
94            on_mouse_up: None,
95            on_focus_in: None,
96            on_focus_out: None,
97            on_geo_changed: None,
98        }
99    }
100}
101
102impl Model for ActionsModel {
103    fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
104        event.take(|actions_event, _| match actions_event {
105            ActionsEvent::OnPress(on_press) => {
106                self.on_press = Some(on_press);
107            }
108
109            ActionsEvent::OnPressDown(on_press_down) => {
110                self.on_press_down = Some(on_press_down);
111            }
112
113            ActionsEvent::OnDoubleClick(on_double_click) => {
114                self.on_double_click = Some(on_double_click);
115            }
116
117            ActionsEvent::OnHover(on_hover) => {
118                self.on_hover = Some(on_hover);
119            }
120
121            ActionsEvent::OnHoverOut(on_hover_out) => {
122                self.on_hover_out = Some(on_hover_out);
123            }
124
125            ActionsEvent::OnOver(on_over) => {
126                self.on_over = Some(on_over);
127            }
128
129            ActionsEvent::OnOverOut(on_over_out) => {
130                self.on_over_out = Some(on_over_out);
131            }
132
133            ActionsEvent::OnMouseMove(on_move) => {
134                self.on_mouse_move = Some(on_move);
135            }
136
137            ActionsEvent::OnMouseDown(on_mouse_down) => {
138                self.on_mouse_down = Some(on_mouse_down);
139            }
140
141            ActionsEvent::OnMouseUp(on_mouse_up) => {
142                self.on_mouse_up = Some(on_mouse_up);
143            }
144
145            ActionsEvent::OnFocusIn(on_focus_in) => {
146                self.on_focus_in = Some(on_focus_in);
147            }
148
149            ActionsEvent::OnFocusOut(on_focus_out) => {
150                self.on_focus_out = Some(on_focus_out);
151            }
152
153            ActionsEvent::OnGeoChanged(on_geo_changed) => {
154                self.on_geo_changed = Some(on_geo_changed);
155                cx.cache.set_bounds(cx.current, BoundingBox::default());
156                cx.needs_relayout();
157            }
158        });
159
160        event.map(|window_event, meta| match window_event {
161            WindowEvent::Press { mouse } => {
162                let over = if *mouse { cx.hovered() } else { cx.focused() };
163                if cx.current() != over && !over.is_descendant_of(cx.tree, cx.current()) {
164                    return;
165                }
166
167                if !cx.is_disabled() && cx.current == meta.target {
168                    // Only grab keyboard focus if the view has opted in via
169                    // the FOCUSABLE ability. Without this gate, any view with
170                    // an `on_press` callback steals focus on click even when
171                    // `.focusable(false)` is set, and every subsequent
172                    // Space / Enter keypress then re-dispatches
173                    // `WindowEvent::Press` straight back to the clicked view
174                    // and re-runs its action — a confusing non-feature for
175                    // chrome buttons that are mouse-only by design.
176                    let focusable = cx
177                        .style
178                        .abilities
179                        .get(cx.current())
180                        .is_some_and(|abilities| abilities.contains(Abilities::FOCUSABLE));
181                    if focusable {
182                        cx.focus();
183                    }
184                    if let Some(action) = &self.on_press {
185                        (action)(cx);
186                    }
187                }
188            }
189
190            WindowEvent::PressDown { mouse } => {
191                let over = if *mouse { cx.hovered() } else { cx.focused() };
192                if cx.current() != over && !over.is_descendant_of(cx.tree, cx.current()) {
193                    return;
194                }
195                if !cx.is_disabled() && cx.current == meta.target {
196                    if let Some(action) = &self.on_press_down {
197                        (action)(cx);
198                    }
199                }
200            }
201
202            WindowEvent::MouseDoubleClick(button) => {
203                if meta.target == cx.current && !cx.is_disabled() {
204                    if let Some(action) = &self.on_double_click {
205                        (action)(cx, *button);
206                    }
207                }
208            }
209
210            WindowEvent::MouseEnter => {
211                if meta.target == cx.current() {
212                    if let Some(action) = &self.on_hover {
213                        (action)(cx);
214                    }
215                }
216            }
217
218            WindowEvent::MouseLeave => {
219                if meta.target == cx.current() {
220                    if let Some(action) = &self.on_hover_out {
221                        (action)(cx);
222                    }
223                }
224            }
225
226            WindowEvent::MouseOver => {
227                if let Some(action) = &self.on_over {
228                    (action)(cx);
229                }
230            }
231
232            WindowEvent::MouseOut => {
233                // if meta.target == cx.current() {
234                if let Some(action) = &self.on_over_out {
235                    (action)(cx);
236                }
237                // }
238            }
239
240            WindowEvent::MouseMove(x, y) => {
241                if let Some(action) = &self.on_mouse_move {
242                    (action)(cx, *x, *y);
243                }
244            }
245
246            WindowEvent::MouseDown(mouse_button) => {
247                if let Some(action) = &self.on_mouse_down {
248                    (action)(cx, *mouse_button);
249                }
250            }
251
252            WindowEvent::MouseUp(mouse_button) => {
253                if let Some(action) = &self.on_mouse_up {
254                    (action)(cx, *mouse_button);
255                }
256            }
257
258            WindowEvent::FocusIn => {
259                if let Some(action) = &self.on_focus_in {
260                    (action)(cx);
261                }
262            }
263
264            WindowEvent::FocusOut => {
265                if let Some(action) = &self.on_focus_out {
266                    (action)(cx);
267                }
268            }
269
270            WindowEvent::GeometryChanged(geo) => {
271                if meta.target == cx.current() {
272                    if let Some(action) = &self.on_geo_changed {
273                        (action)(cx, *geo);
274                    }
275                }
276            }
277
278            _ => {}
279        });
280    }
281}
282
283pub(crate) enum ActionsEvent {
284    OnPress(Box<dyn Fn(&mut EventContext) + Send + Sync>),
285    OnPressDown(Box<dyn Fn(&mut EventContext) + Send + Sync>),
286    OnDoubleClick(Box<dyn Fn(&mut EventContext, MouseButton) + Send + Sync>),
287    OnHover(Box<dyn Fn(&mut EventContext) + Send + Sync>),
288    OnHoverOut(Box<dyn Fn(&mut EventContext) + Send + Sync>),
289    OnOver(Box<dyn Fn(&mut EventContext) + Send + Sync>),
290    OnOverOut(Box<dyn Fn(&mut EventContext) + Send + Sync>),
291    OnMouseMove(Box<dyn Fn(&mut EventContext, f32, f32) + Send + Sync>),
292    OnMouseDown(Box<dyn Fn(&mut EventContext, MouseButton) + Send + Sync>),
293    OnMouseUp(Box<dyn Fn(&mut EventContext, MouseButton) + Send + Sync>),
294    OnFocusIn(Box<dyn Fn(&mut EventContext) + Send + Sync>),
295    OnFocusOut(Box<dyn Fn(&mut EventContext) + Send + Sync>),
296    OnGeoChanged(Box<dyn Fn(&mut EventContext, GeoChanged) + Send + Sync>),
297}
298
299/// Modifiers which add an action callback to a view.
300pub trait ActionModifiers<V> {
301    /// Adds a callback which is performed when the the view receives the [`Press`](crate::prelude::WindowEvent::Press) event.
302    /// By default a view receives the [`Press`](crate::prelude::WindowEvent::Press) event when the left mouse button is pressed and then released on the view,
303    /// or when the space or enter keys are pressed and then released while the view is focused.
304    ///
305    /// # Example
306    /// ```rust
307    /// # use vizia_core::prelude::*;
308    /// # let mut cx = &mut Context::default();
309    /// Element::new(cx).on_press(|_| debug!("View was pressed!"));
310    /// ```
311    fn on_press<F>(self, action: F) -> Self
312    where
313        F: 'static + Fn(&mut EventContext) + Send + Sync;
314
315    /// Adds a callback which is performed when the the view receives the [`PressDown`](crate::prelude::WindowEvent::PressDown) event.
316    // By default a view receives the [`PressDown`](crate::prelude::WindowEvent::PressDown) event when the left mouse button is pressed on the view,
317    /// or when the space or enter keys are pressed while the view is focused.
318    ///
319    /// # Example
320    /// ```rust
321    /// # use vizia_core::prelude::*;
322    /// # let mut cx = &mut Context::default();
323    /// Element::new(cx).on_press_down(|_| debug!("View was pressed down!"));
324    /// ```
325    fn on_press_down<F>(self, action: F) -> Self
326    where
327        F: 'static + Fn(&mut EventContext) + Send + Sync;
328
329    /// Adds a callback which is performed when the the view receives the [`MouseDoubleClick`](crate::prelude::WindowEvent::MouseDoubleClick) event.
330    ///
331    /// # Example
332    /// ```rust
333    /// # use vizia_core::prelude::*;
334    /// # let mut cx = &mut Context::default();
335    /// Element::new(cx).on_double_click(|_, _button| debug!("View was double clicked on!"));
336    /// ```
337    fn on_double_click<F>(self, action: F) -> Self
338    where
339        F: 'static + Fn(&mut EventContext, MouseButton) + Send + Sync;
340
341    /// Adds a callback which is performed when the mouse pointer moves over a view.
342    /// This callback is not triggered when the mouse pointer moves over an overlapping child of the view.
343    ///
344    /// # Example
345    /// ```rust
346    /// # use vizia_core::prelude::*;
347    /// # let mut cx = &mut Context::default();
348    /// Element::new(cx).on_hover(|_| debug!("Mouse cursor entered the view!"));
349    /// ```
350    fn on_hover<F>(self, action: F) -> Self
351    where
352        F: 'static + Fn(&mut EventContext) + Send + Sync;
353
354    /// Adds a callback which is performed when the mouse pointer moves away from a view.
355    /// This callback is not triggered when the mouse pointer moves away from an overlapping child of the view.
356    ///
357    /// # Example
358    /// ```rust
359    /// # use vizia_core::prelude::*;
360    /// # let mut cx = &mut Context::default();
361    /// Element::new(cx).on_hover_out(|_| debug!("Mouse cursor left the view!"));
362    /// ```
363    fn on_hover_out<F>(self, action: F) -> Self
364    where
365        F: 'static + Fn(&mut EventContext) + Send + Sync;
366
367    /// Adds a callback which is performed when the mouse pointer moves over the bounds of a view,
368    /// including any overlapping children.
369    ///
370    /// # Example
371    /// ```rust
372    /// # use vizia_core::prelude::*;
373    /// # let mut cx = &mut Context::default();
374    /// Element::new(cx).on_over(|_| debug!("Mouse cursor entered the view bounds!"));
375    /// ```
376    fn on_over<F>(self, action: F) -> Self
377    where
378        F: 'static + Fn(&mut EventContext) + Send + Sync;
379
380    /// Adds a callback which is performed when the mouse pointer moves away from the bounds of a view,
381    /// including any overlapping children.
382    ///
383    /// # Example
384    /// ```rust
385    /// # use vizia_core::prelude::*;
386    /// # let mut cx = &mut Context::default();
387    /// Element::new(cx).on_over_out(|_| debug!("Mouse cursor left the view bounds!"));
388    /// ```
389    fn on_over_out<F>(self, action: F) -> Self
390    where
391        F: 'static + Fn(&mut EventContext) + Send + Sync;
392
393    /// Adds a callback which is performed when the mouse pointer moves within the bounds of a view.
394    ///
395    /// # Example
396    /// ```rust
397    /// # use vizia_core::prelude::*;
398    /// # let mut cx = &mut Context::default();
399    /// Element::new(cx).on_mouse_move(|_, x, y| debug!("Cursor moving: {} {}", x, y));
400    /// ```
401    fn on_mouse_move<F>(self, action: F) -> Self
402    where
403        F: 'static + Fn(&mut EventContext, f32, f32) + Send + Sync;
404
405    /// Adds a callback which is performed when a mouse button is pressed on the view.
406    /// Unlike the `on_press` callback, this callback is triggered for all mouse buttons and not for any keyboard keys.
407    ///
408    /// # Example
409    /// ```rust
410    /// # use vizia_core::prelude::*;
411    /// # let mut cx = &mut Context::default();
412    /// Element::new(cx).on_mouse_down(|_, button| debug!("Mouse button, {:?}, was pressed!", button));
413    /// ```
414    fn on_mouse_down<F>(self, action: F) -> Self
415    where
416        F: 'static + Fn(&mut EventContext, MouseButton) + Send + Sync;
417
418    /// Adds a callback which is performed when a mouse button is released on the view.
419    /// Unlike the `on_release` callback, this callback is triggered for all mouse buttons and not for any keyboard keys.
420    ///
421    /// # Example
422    /// ```rust
423    /// # use vizia_core::prelude::*;
424    /// # let mut cx = &mut Context::default();
425    /// Element::new(cx).on_mouse_up(|_, button| debug!("Mouse button, {:?}, was released!", button));
426    /// ```
427    fn on_mouse_up<F>(self, action: F) -> Self
428    where
429        F: 'static + Fn(&mut EventContext, MouseButton) + Send + Sync;
430
431    /// Adds a callback which is performed when the view gains keyboard focus.
432    ///
433    /// # Example
434    /// ```rust
435    /// # use vizia_core::prelude::*;
436    /// # let mut cx = &mut Context::default();
437    /// Element::new(cx).on_focus_in(|_| debug!("View gained keyboard focus!"));
438    /// ```
439    fn on_focus_in<F>(self, action: F) -> Self
440    where
441        F: 'static + Fn(&mut EventContext) + Send + Sync;
442
443    /// Adds a callback which is performed when the view loses keyboard focus.
444    ///
445    /// # Example
446    /// ```rust
447    /// # use vizia_core::prelude::*;
448    /// # let mut cx = &mut Context::default();
449    /// Element::new(cx).on_focus_out(|_| debug!("View lost keyboard focus!"));
450    /// ```
451    fn on_focus_out<F>(self, action: F) -> Self
452    where
453        F: 'static + Fn(&mut EventContext) + Send + Sync;
454
455    /// Adds a callback which is performed when the the view changes size or position after layout.
456    ///
457    /// # Example
458    /// ```rust
459    /// # use vizia_core::prelude::*;
460    /// # let mut cx = &mut Context::default();
461    /// Element::new(cx).on_geo_changed(|_, _| debug!("View geometry changed!"));
462    /// ```
463    fn on_geo_changed<F>(self, action: F) -> Self
464    where
465        F: 'static + Fn(&mut EventContext, GeoChanged) + Send + Sync;
466
467    /// Adds a popup tooltip to the view.
468    fn tooltip<C: Fn(&mut Context) -> Handle<'_, Tooltip> + 'static>(self, content: C) -> Self;
469
470    /// Adds a popup menu to the view.
471    fn menu<C: FnOnce(&mut Context) -> Handle<'_, T>, T: View>(self, content: C) -> Self;
472}
473
474// If the entity doesn't have an `ActionsModel` then add one to the entity
475fn build_action_model(cx: &mut Context, entity: Entity) {
476    if cx.models.get(&entity).and_then(|models| models.get(&TypeId::of::<ActionsModel>())).is_none()
477    {
478        cx.with_current(entity, |cx| {
479            ActionsModel::new().build(cx);
480        });
481    }
482}
483
484fn build_modal_model(cx: &mut Context, entity: Entity) {
485    if cx.models.get(&entity).and_then(|models| models.get(&TypeId::of::<ModalModel>())).is_none() {
486        cx.with_current(entity, |cx| {
487            ModalModel {
488                tooltip_visible: Signal::new((false, true)),
489                menu_visible: Signal::new(false),
490            }
491            .build(cx);
492        });
493    }
494}
495
496impl<V: View> ActionModifiers<V> for Handle<'_, V> {
497    fn tooltip<C: Fn(&mut Context) -> Handle<'_, Tooltip> + 'static>(self, content: C) -> Self {
498        let entity = self.entity();
499
500        build_modal_model(self.cx, entity);
501
502        self.cx.with_current(entity, move |cx| {
503            let tooltip_visible = cx.data::<ModalModel>().tooltip_visible;
504            Binding::new(cx, tooltip_visible, move |cx| {
505                let tooltip_visible = tooltip_visible.get();
506                let tooltip_delay = cx.environment().tooltip_delay;
507                if tooltip_visible.0 {
508                    (content)(cx)
509                        .on_build(|cx| {
510                            if tooltip_visible.1 {
511                                cx.play_animation(
512                                    "tooltip_fade",
513                                    Duration::from_millis(100),
514                                    tooltip_delay,
515                                )
516                            }
517                        })
518                        .id(format!("{entity}"));
519                }
520            });
521        });
522
523        self.described_by(format!("{entity}"))
524    }
525
526    fn menu<C: FnOnce(&mut Context) -> Handle<'_, T>, T: View>(self, content: C) -> Self {
527        let entity = self.entity();
528
529        build_modal_model(self.cx, entity);
530
531        self.cx.with_current(entity, |cx| {
532            let menu_visible = cx.data::<ModalModel>().menu_visible;
533            (content)(cx).bind(menu_visible, move |mut handle| {
534                let is_visible = menu_visible.get();
535                handle = handle.toggle_class("vis", is_visible);
536
537                if is_visible {
538                    handle.context().emit(WindowEvent::GeometryChanged(GeoChanged::empty()));
539                }
540            });
541        });
542
543        self
544    }
545
546    fn on_press<F>(mut self, action: F) -> Self
547    where
548        F: 'static + Fn(&mut EventContext) + Send + Sync,
549    {
550        self = self.hoverable(true);
551
552        if let Some(view) = self
553            .cx
554            .views
555            .get_mut(&self.entity)
556            .and_then(|view_handler| view_handler.downcast_mut::<Button>())
557        {
558            view.action = Some(Box::new(action));
559            return self;
560        }
561
562        build_action_model(self.cx, self.entity);
563
564        self.cx.emit_custom(
565            Event::new(ActionsEvent::OnPress(Box::new(action)))
566                .target(self.entity)
567                .origin(self.entity),
568        );
569
570        self
571    }
572
573    fn on_press_down<F>(self, action: F) -> Self
574    where
575        F: 'static + Fn(&mut EventContext) + Send + Sync,
576    {
577        build_action_model(self.cx, self.entity);
578
579        self.cx.emit_custom(
580            Event::new(ActionsEvent::OnPressDown(Box::new(action)))
581                .target(self.entity)
582                .origin(self.entity),
583        );
584
585        self
586    }
587
588    fn on_double_click<F>(self, action: F) -> Self
589    where
590        F: 'static + Fn(&mut EventContext, MouseButton) + Send + Sync,
591    {
592        build_action_model(self.cx, self.entity);
593
594        self.cx.emit_custom(
595            Event::new(ActionsEvent::OnDoubleClick(Box::new(action)))
596                .target(self.entity)
597                .origin(self.entity),
598        );
599
600        self
601    }
602
603    fn on_hover<F>(self, action: F) -> Self
604    where
605        F: 'static + Fn(&mut EventContext) + Send + Sync,
606    {
607        build_action_model(self.cx, self.entity);
608
609        self.cx.emit_custom(
610            Event::new(ActionsEvent::OnHover(Box::new(action)))
611                .target(self.entity)
612                .origin(self.entity),
613        );
614
615        self
616    }
617
618    fn on_hover_out<F>(self, action: F) -> Self
619    where
620        F: 'static + Fn(&mut EventContext) + Send + Sync,
621    {
622        build_action_model(self.cx, self.entity);
623
624        self.cx.emit_custom(
625            Event::new(ActionsEvent::OnHoverOut(Box::new(action)))
626                .target(self.entity)
627                .origin(self.entity),
628        );
629
630        self
631    }
632
633    fn on_over<F>(self, action: F) -> Self
634    where
635        F: 'static + Fn(&mut EventContext) + Send + Sync,
636    {
637        build_action_model(self.cx, self.entity);
638
639        self.cx.emit_custom(
640            Event::new(ActionsEvent::OnOver(Box::new(action)))
641                .target(self.entity)
642                .origin(self.entity),
643        );
644
645        self
646    }
647
648    fn on_over_out<F>(self, action: F) -> Self
649    where
650        F: 'static + Fn(&mut EventContext) + Send + Sync,
651    {
652        build_action_model(self.cx, self.entity);
653
654        self.cx.emit_custom(
655            Event::new(ActionsEvent::OnOverOut(Box::new(action)))
656                .target(self.entity)
657                .origin(self.entity),
658        );
659
660        self
661    }
662
663    fn on_mouse_move<F>(self, action: F) -> Self
664    where
665        F: 'static + Fn(&mut EventContext, f32, f32) + Send + Sync,
666    {
667        build_action_model(self.cx, self.entity);
668
669        self.cx.emit_custom(
670            Event::new(ActionsEvent::OnMouseMove(Box::new(action)))
671                .target(self.entity)
672                .origin(self.entity),
673        );
674
675        self
676    }
677
678    fn on_mouse_down<F>(self, action: F) -> Self
679    where
680        F: 'static + Fn(&mut EventContext, MouseButton) + Send + Sync,
681    {
682        build_action_model(self.cx, self.entity);
683
684        self.cx.emit_custom(
685            Event::new(ActionsEvent::OnMouseDown(Box::new(action)))
686                .target(self.entity)
687                .origin(self.entity),
688        );
689
690        self
691    }
692
693    fn on_mouse_up<F>(self, action: F) -> Self
694    where
695        F: 'static + Fn(&mut EventContext, MouseButton) + Send + Sync,
696    {
697        build_action_model(self.cx, self.entity);
698
699        self.cx.emit_custom(
700            Event::new(ActionsEvent::OnMouseUp(Box::new(action)))
701                .target(self.entity)
702                .origin(self.entity),
703        );
704
705        self
706    }
707
708    fn on_focus_in<F>(self, action: F) -> Self
709    where
710        F: 'static + Fn(&mut EventContext) + Send + Sync,
711    {
712        build_action_model(self.cx, self.entity);
713
714        self.cx.emit_custom(
715            Event::new(ActionsEvent::OnFocusIn(Box::new(action)))
716                .target(self.entity)
717                .origin(self.entity),
718        );
719
720        self
721    }
722
723    fn on_focus_out<F>(self, action: F) -> Self
724    where
725        F: 'static + Fn(&mut EventContext) + Send + Sync,
726    {
727        build_action_model(self.cx, self.entity);
728
729        self.cx.emit_custom(
730            Event::new(ActionsEvent::OnFocusOut(Box::new(action)))
731                .target(self.entity)
732                .origin(self.entity),
733        );
734
735        self
736    }
737
738    fn on_geo_changed<F>(self, action: F) -> Self
739    where
740        F: 'static + Fn(&mut EventContext, GeoChanged) + Send + Sync,
741    {
742        build_action_model(self.cx, self.entity);
743
744        self.cx.emit_custom(
745            Event::new(ActionsEvent::OnGeoChanged(Box::new(action)))
746                .target(self.entity)
747                .origin(self.entity),
748        );
749
750        self
751    }
752}