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    pub(crate) on_drag_start: Option<Box<dyn Fn(&mut EventContext) + Send + Sync>>,
81    pub(crate) on_drop: Option<Box<dyn Fn(&mut EventContext, DropData) + Send + Sync>>,
82}
83
84impl ActionsModel {
85    pub(crate) fn new() -> Self {
86        Self {
87            on_press: None,
88            on_press_down: None,
89            on_double_click: None,
90            on_hover: None,
91            on_hover_out: None,
92            on_over: None,
93            on_over_out: None,
94            on_mouse_move: None,
95            on_mouse_down: None,
96            on_mouse_up: None,
97            on_focus_in: None,
98            on_focus_out: None,
99            on_geo_changed: None,
100            on_drag_start: None,
101            on_drop: None,
102        }
103    }
104}
105
106impl Model for ActionsModel {
107    fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
108        event.take(|actions_event, _| match actions_event {
109            ActionsEvent::OnPress(on_press) => {
110                self.on_press = Some(on_press);
111            }
112
113            ActionsEvent::OnPressDown(on_press_down) => {
114                self.on_press_down = Some(on_press_down);
115            }
116
117            ActionsEvent::OnDoubleClick(on_double_click) => {
118                self.on_double_click = Some(on_double_click);
119            }
120
121            ActionsEvent::OnHover(on_hover) => {
122                self.on_hover = Some(on_hover);
123            }
124
125            ActionsEvent::OnHoverOut(on_hover_out) => {
126                self.on_hover_out = Some(on_hover_out);
127            }
128
129            ActionsEvent::OnOver(on_over) => {
130                self.on_over = Some(on_over);
131            }
132
133            ActionsEvent::OnOverOut(on_over_out) => {
134                self.on_over_out = Some(on_over_out);
135            }
136
137            ActionsEvent::OnMouseMove(on_move) => {
138                self.on_mouse_move = Some(on_move);
139            }
140
141            ActionsEvent::OnMouseDown(on_mouse_down) => {
142                self.on_mouse_down = Some(on_mouse_down);
143            }
144
145            ActionsEvent::OnMouseUp(on_mouse_up) => {
146                self.on_mouse_up = Some(on_mouse_up);
147            }
148
149            ActionsEvent::OnFocusIn(on_focus_in) => {
150                self.on_focus_in = Some(on_focus_in);
151            }
152
153            ActionsEvent::OnFocusOut(on_focus_out) => {
154                self.on_focus_out = Some(on_focus_out);
155            }
156
157            ActionsEvent::OnGeoChanged(on_geo_changed) => {
158                self.on_geo_changed = Some(on_geo_changed);
159                cx.cache.set_bounds(cx.current, BoundingBox::default());
160                cx.needs_relayout();
161            }
162
163            ActionsEvent::OnDragStart(on_drag_start) => {
164                self.on_drag_start = Some(on_drag_start);
165            }
166
167            ActionsEvent::OnDrop(on_drop) => {
168                self.on_drop = Some(on_drop);
169            }
170        });
171
172        event.map(|window_event, meta| match window_event {
173            WindowEvent::Press { mouse } => {
174                let over = if *mouse { cx.hovered() } else { cx.focused() };
175                if cx.current() != over && !over.is_descendant_of(cx.tree, cx.current()) {
176                    return;
177                }
178
179                if !cx.is_disabled() && cx.current == meta.target {
180                    cx.focus();
181                    if let Some(action) = &self.on_press {
182                        (action)(cx);
183                    }
184                }
185            }
186
187            WindowEvent::PressDown { mouse } => {
188                let over = if *mouse { cx.hovered() } else { cx.focused() };
189                if cx.current() != over && !over.is_descendant_of(cx.tree, cx.current()) {
190                    return;
191                }
192                if !cx.is_disabled() && cx.current == meta.target {
193                    if let Some(action) = &self.on_press_down {
194                        (action)(cx);
195                    }
196                }
197            }
198
199            WindowEvent::MouseDoubleClick(button) => {
200                if meta.target == cx.current && !cx.is_disabled() {
201                    if let Some(action) = &self.on_double_click {
202                        (action)(cx, *button);
203                    }
204                }
205            }
206
207            WindowEvent::MouseEnter => {
208                if meta.target == cx.current() {
209                    if let Some(action) = &self.on_hover {
210                        (action)(cx);
211                    }
212                }
213            }
214
215            WindowEvent::MouseLeave => {
216                if meta.target == cx.current() {
217                    if let Some(action) = &self.on_hover_out {
218                        (action)(cx);
219                    }
220                }
221            }
222
223            WindowEvent::MouseOver => {
224                if let Some(action) = &self.on_over {
225                    (action)(cx);
226                }
227            }
228
229            WindowEvent::MouseOut => {
230                // if meta.target == cx.current() {
231                if let Some(action) = &self.on_over_out {
232                    (action)(cx);
233                }
234
235                if cx.mouse.left.state == MouseButtonState::Pressed
236                    && cx.mouse.left.pressed == cx.current()
237                    && cx.is_draggable()
238                {
239                    if let Some(action) = &self.on_drag_start {
240                        (action)(cx);
241                    }
242                }
243                // }
244            }
245
246            WindowEvent::MouseMove(x, y) => {
247                if let Some(action) = &self.on_mouse_move {
248                    (action)(cx, *x, *y);
249                }
250                if cx.mouse.left.state == MouseButtonState::Released {
251                    if let Some(drop_data) = cx.drop_data.take() {
252                        if let Some(action) = &self.on_drop {
253                            (action)(cx, drop_data);
254                        }
255                    }
256                }
257            }
258
259            WindowEvent::MouseDown(mouse_button) => {
260                if let Some(action) = &self.on_mouse_down {
261                    (action)(cx, *mouse_button);
262                }
263            }
264
265            WindowEvent::MouseUp(mouse_button) => {
266                if let Some(action) = &self.on_mouse_up {
267                    (action)(cx, *mouse_button);
268                }
269                if let Some(drop_data) = cx.drop_data.take() {
270                    if let Some(action) = &self.on_drop {
271                        (action)(cx, drop_data);
272                    }
273                }
274            }
275
276            WindowEvent::FocusIn => {
277                if let Some(action) = &self.on_focus_in {
278                    (action)(cx);
279                }
280            }
281
282            WindowEvent::FocusOut => {
283                if let Some(action) = &self.on_focus_out {
284                    (action)(cx);
285                }
286            }
287
288            WindowEvent::GeometryChanged(geo) => {
289                if meta.target == cx.current() {
290                    if let Some(action) = &self.on_geo_changed {
291                        (action)(cx, *geo);
292                    }
293                }
294            }
295
296            _ => {}
297        });
298    }
299}
300
301pub(crate) enum ActionsEvent {
302    OnPress(Box<dyn Fn(&mut EventContext) + Send + Sync>),
303    OnPressDown(Box<dyn Fn(&mut EventContext) + Send + Sync>),
304    OnDoubleClick(Box<dyn Fn(&mut EventContext, MouseButton) + Send + Sync>),
305    OnHover(Box<dyn Fn(&mut EventContext) + Send + Sync>),
306    OnHoverOut(Box<dyn Fn(&mut EventContext) + Send + Sync>),
307    OnOver(Box<dyn Fn(&mut EventContext) + Send + Sync>),
308    OnOverOut(Box<dyn Fn(&mut EventContext) + Send + Sync>),
309    OnMouseMove(Box<dyn Fn(&mut EventContext, f32, f32) + Send + Sync>),
310    OnMouseDown(Box<dyn Fn(&mut EventContext, MouseButton) + Send + Sync>),
311    OnMouseUp(Box<dyn Fn(&mut EventContext, MouseButton) + Send + Sync>),
312    OnFocusIn(Box<dyn Fn(&mut EventContext) + Send + Sync>),
313    OnFocusOut(Box<dyn Fn(&mut EventContext) + Send + Sync>),
314    OnGeoChanged(Box<dyn Fn(&mut EventContext, GeoChanged) + Send + Sync>),
315    OnDragStart(Box<dyn Fn(&mut EventContext) + Send + Sync>),
316    OnDrop(Box<dyn Fn(&mut EventContext, DropData) + Send + Sync>),
317}
318
319/// Modifiers which add an action callback to a view.
320pub trait ActionModifiers<V> {
321    /// Adds a callback which is performed when the the view receives the [`Press`](crate::prelude::WindowEvent::Press) event.
322    /// 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,
323    /// or when the space or enter keys are pressed and then released while the view is focused.
324    ///
325    /// # Example
326    /// ```rust
327    /// # use vizia_core::prelude::*;
328    /// # let mut cx = &mut Context::default();
329    /// Element::new(cx).on_press(|_| debug!("View was pressed!"));
330    /// ```
331    fn on_press<F>(self, action: F) -> Self
332    where
333        F: 'static + Fn(&mut EventContext) + Send + Sync;
334
335    /// Adds a callback which is performed when the the view receives the [`PressDown`](crate::prelude::WindowEvent::PressDown) event.
336    // By default a view receives the [`PressDown`](crate::prelude::WindowEvent::PressDown) event when the left mouse button is pressed on the view,
337    /// or when the space or enter keys are pressed while the view is focused.
338    ///
339    /// # Example
340    /// ```rust
341    /// # use vizia_core::prelude::*;
342    /// # let mut cx = &mut Context::default();
343    /// Element::new(cx).on_press_down(|_| debug!("View was pressed down!"));
344    /// ```
345    fn on_press_down<F>(self, action: F) -> Self
346    where
347        F: 'static + Fn(&mut EventContext) + Send + Sync;
348
349    /// Adds a callback which is performed when the the view receives the [`MouseDoubleClick`](crate::prelude::WindowEvent::MouseDoubleClick) event.
350    ///
351    /// # Example
352    /// ```rust
353    /// # use vizia_core::prelude::*;
354    /// # let mut cx = &mut Context::default();
355    /// Element::new(cx).on_double_click(|_, _button| debug!("View was double clicked on!"));
356    /// ```
357    fn on_double_click<F>(self, action: F) -> Self
358    where
359        F: 'static + Fn(&mut EventContext, MouseButton) + Send + Sync;
360
361    /// Adds a callback which is performed when the mouse pointer moves over a view.
362    /// This callback is not triggered when the mouse pointer moves over an overlapping child of the view.
363    ///
364    /// # Example
365    /// ```rust
366    /// # use vizia_core::prelude::*;
367    /// # let mut cx = &mut Context::default();
368    /// Element::new(cx).on_hover(|_| debug!("Mouse cursor entered the view!"));
369    /// ```
370    fn on_hover<F>(self, action: F) -> Self
371    where
372        F: 'static + Fn(&mut EventContext) + Send + Sync;
373
374    /// Adds a callback which is performed when the mouse pointer moves away from a view.
375    /// This callback is not triggered when the mouse pointer moves away from an overlapping child of the view.
376    ///
377    /// # Example
378    /// ```rust
379    /// # use vizia_core::prelude::*;
380    /// # let mut cx = &mut Context::default();
381    /// Element::new(cx).on_hover_out(|_| debug!("Mouse cursor left the view!"));
382    /// ```
383    fn on_hover_out<F>(self, action: F) -> Self
384    where
385        F: 'static + Fn(&mut EventContext) + Send + Sync;
386
387    /// Adds a callback which is performed when the mouse pointer moves over the bounds of a view,
388    /// including any overlapping children.
389    ///
390    /// # Example
391    /// ```rust
392    /// # use vizia_core::prelude::*;
393    /// # let mut cx = &mut Context::default();
394    /// Element::new(cx).on_over(|_| debug!("Mouse cursor entered the view bounds!"));
395    /// ```
396    fn on_over<F>(self, action: F) -> Self
397    where
398        F: 'static + Fn(&mut EventContext) + Send + Sync;
399
400    /// Adds a callback which is performed when the mouse pointer moves away from the bounds of a view,
401    /// including any overlapping children.
402    ///
403    /// # Example
404    /// ```rust
405    /// # use vizia_core::prelude::*;
406    /// # let mut cx = &mut Context::default();
407    /// Element::new(cx).on_over_out(|_| debug!("Mouse cursor left the view bounds!"));
408    /// ```
409    fn on_over_out<F>(self, action: F) -> Self
410    where
411        F: 'static + Fn(&mut EventContext) + Send + Sync;
412
413    /// Adds a callback which is performed when the mouse pointer moves within the bounds of a view.
414    ///
415    /// # Example
416    /// ```rust
417    /// # use vizia_core::prelude::*;
418    /// # let mut cx = &mut Context::default();
419    /// Element::new(cx).on_mouse_move(|_, x, y| debug!("Cursor moving: {} {}", x, y));
420    /// ```
421    fn on_mouse_move<F>(self, action: F) -> Self
422    where
423        F: 'static + Fn(&mut EventContext, f32, f32) + Send + Sync;
424
425    /// Adds a callback which is performed when a mouse button is pressed on the view.
426    /// Unlike the `on_press` callback, this callback is triggered for all mouse buttons and not for any keyboard keys.
427    ///
428    /// # Example
429    /// ```rust
430    /// # use vizia_core::prelude::*;
431    /// # let mut cx = &mut Context::default();
432    /// Element::new(cx).on_mouse_down(|_, button| debug!("Mouse button, {:?}, was pressed!", button));
433    /// ```
434    fn on_mouse_down<F>(self, action: F) -> Self
435    where
436        F: 'static + Fn(&mut EventContext, MouseButton) + Send + Sync;
437
438    /// Adds a callback which is performed when a mouse button is released on the view.
439    /// Unlike the `on_release` callback, this callback is triggered for all mouse buttons and not for any keyboard keys.
440    ///
441    /// # Example
442    /// ```rust
443    /// # use vizia_core::prelude::*;
444    /// # let mut cx = &mut Context::default();
445    /// Element::new(cx).on_mouse_up(|_, button| debug!("Mouse button, {:?}, was released!", button));
446    /// ```
447    fn on_mouse_up<F>(self, action: F) -> Self
448    where
449        F: 'static + Fn(&mut EventContext, MouseButton) + Send + Sync;
450
451    /// Adds a callback which is performed when the view gains keyboard focus.
452    ///
453    /// # Example
454    /// ```rust
455    /// # use vizia_core::prelude::*;
456    /// # let mut cx = &mut Context::default();
457    /// Element::new(cx).on_focus_in(|_| debug!("View gained keyboard focus!"));
458    /// ```
459    fn on_focus_in<F>(self, action: F) -> Self
460    where
461        F: 'static + Fn(&mut EventContext) + Send + Sync;
462
463    /// Adds a callback which is performed when the view loses keyboard focus.
464    ///
465    /// # Example
466    /// ```rust
467    /// # use vizia_core::prelude::*;
468    /// # let mut cx = &mut Context::default();
469    /// Element::new(cx).on_focus_out(|_| debug!("View lost keyboard focus!"));
470    /// ```
471    fn on_focus_out<F>(self, action: F) -> Self
472    where
473        F: 'static + Fn(&mut EventContext) + Send + Sync;
474
475    /// Adds a callback which is performed when the the view changes size or position after layout.
476    ///
477    /// # Example
478    /// ```rust
479    /// # use vizia_core::prelude::*;
480    /// # let mut cx = &mut Context::default();
481    /// Element::new(cx).on_geo_changed(|_, _| debug!("View geometry changed!"));
482    /// ```
483    fn on_geo_changed<F>(self, action: F) -> Self
484    where
485        F: 'static + Fn(&mut EventContext, GeoChanged) + Send + Sync;
486
487    /// Adds a popup tooltip to the view.
488    fn tooltip<C: Fn(&mut Context) -> Handle<'_, Tooltip> + 'static>(self, content: C) -> Self;
489
490    /// Adds a popup menu to the view.
491    fn menu<C: FnOnce(&mut Context) -> Handle<'_, T>, T: View>(self, content: C) -> Self;
492
493    /// Adds a callback which is performed when the view is dragged during a drag and drop operation.
494    fn on_drag<F>(self, action: F) -> Self
495    where
496        F: 'static + Fn(&mut EventContext) + Send + Sync;
497
498    /// Adds a callback which is performed when data is dropped on the view during a drag and drop operation.
499    fn on_drop<F>(self, action: F) -> Self
500    where
501        F: 'static + Fn(&mut EventContext, DropData) + Send + Sync;
502}
503
504// If the entity doesn't have an `ActionsModel` then add one to the entity
505fn build_action_model(cx: &mut Context, entity: Entity) {
506    if cx.models.get(&entity).and_then(|models| models.get(&TypeId::of::<ActionsModel>())).is_none()
507    {
508        cx.with_current(entity, |cx| {
509            ActionsModel::new().build(cx);
510        });
511    }
512}
513
514fn build_modal_model(cx: &mut Context, entity: Entity) {
515    if cx.models.get(&entity).and_then(|models| models.get(&TypeId::of::<ModalModel>())).is_none() {
516        cx.with_current(entity, |cx| {
517            ModalModel {
518                tooltip_visible: Signal::new((false, true)),
519                menu_visible: Signal::new(false),
520            }
521            .build(cx);
522        });
523    }
524}
525
526impl<V: View> ActionModifiers<V> for Handle<'_, V> {
527    fn tooltip<C: Fn(&mut Context) -> Handle<'_, Tooltip> + 'static>(self, content: C) -> Self {
528        let entity = self.entity();
529
530        build_modal_model(self.cx, entity);
531
532        self.cx.with_current(entity, move |cx| {
533            let tooltip_visible = cx.data::<ModalModel>().tooltip_visible;
534            Binding::new(cx, tooltip_visible, move |cx| {
535                let tooltip_visible = tooltip_visible.get();
536                let tooltip_delay = cx.environment().tooltip_delay;
537                if tooltip_visible.0 {
538                    (content)(cx)
539                        .on_build(|cx| {
540                            if tooltip_visible.1 {
541                                cx.play_animation(
542                                    "tooltip_fade",
543                                    Duration::from_millis(100),
544                                    tooltip_delay,
545                                )
546                            }
547                        })
548                        .id(format!("{entity}"));
549                }
550            });
551        });
552
553        self.described_by(format!("{entity}"))
554    }
555
556    fn menu<C: FnOnce(&mut Context) -> Handle<'_, T>, T: View>(self, content: C) -> Self {
557        let entity = self.entity();
558
559        build_modal_model(self.cx, entity);
560
561        self.cx.with_current(entity, |cx| {
562            let menu_visible = cx.data::<ModalModel>().menu_visible;
563            (content)(cx).bind(menu_visible, move |mut handle| {
564                let is_visible = menu_visible.get();
565                handle = handle.toggle_class("vis", is_visible);
566
567                if is_visible {
568                    handle.context().emit(WindowEvent::GeometryChanged(GeoChanged::empty()));
569                }
570            });
571        });
572
573        self
574    }
575
576    fn on_press<F>(mut self, action: F) -> Self
577    where
578        F: 'static + Fn(&mut EventContext) + Send + Sync,
579    {
580        self = self.hoverable(true);
581
582        if let Some(view) = self
583            .cx
584            .views
585            .get_mut(&self.entity)
586            .and_then(|view_handler| view_handler.downcast_mut::<Button>())
587        {
588            view.action = Some(Box::new(action));
589            return self;
590        }
591
592        build_action_model(self.cx, self.entity);
593
594        self.cx.emit_custom(
595            Event::new(ActionsEvent::OnPress(Box::new(action)))
596                .target(self.entity)
597                .origin(self.entity),
598        );
599
600        self
601    }
602
603    fn on_press_down<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::OnPressDown(Box::new(action)))
611                .target(self.entity)
612                .origin(self.entity),
613        );
614
615        self
616    }
617
618    fn on_double_click<F>(self, action: F) -> Self
619    where
620        F: 'static + Fn(&mut EventContext, MouseButton) + Send + Sync,
621    {
622        build_action_model(self.cx, self.entity);
623
624        self.cx.emit_custom(
625            Event::new(ActionsEvent::OnDoubleClick(Box::new(action)))
626                .target(self.entity)
627                .origin(self.entity),
628        );
629
630        self
631    }
632
633    fn on_hover<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::OnHover(Box::new(action)))
641                .target(self.entity)
642                .origin(self.entity),
643        );
644
645        self
646    }
647
648    fn on_hover_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::OnHoverOut(Box::new(action)))
656                .target(self.entity)
657                .origin(self.entity),
658        );
659
660        self
661    }
662
663    fn on_over<F>(self, action: F) -> Self
664    where
665        F: 'static + Fn(&mut EventContext) + Send + Sync,
666    {
667        build_action_model(self.cx, self.entity);
668
669        self.cx.emit_custom(
670            Event::new(ActionsEvent::OnOver(Box::new(action)))
671                .target(self.entity)
672                .origin(self.entity),
673        );
674
675        self
676    }
677
678    fn on_over_out<F>(self, action: F) -> Self
679    where
680        F: 'static + Fn(&mut EventContext) + Send + Sync,
681    {
682        build_action_model(self.cx, self.entity);
683
684        self.cx.emit_custom(
685            Event::new(ActionsEvent::OnOverOut(Box::new(action)))
686                .target(self.entity)
687                .origin(self.entity),
688        );
689
690        self
691    }
692
693    fn on_mouse_move<F>(self, action: F) -> Self
694    where
695        F: 'static + Fn(&mut EventContext, f32, f32) + Send + Sync,
696    {
697        build_action_model(self.cx, self.entity);
698
699        self.cx.emit_custom(
700            Event::new(ActionsEvent::OnMouseMove(Box::new(action)))
701                .target(self.entity)
702                .origin(self.entity),
703        );
704
705        self
706    }
707
708    fn on_mouse_down<F>(self, action: F) -> Self
709    where
710        F: 'static + Fn(&mut EventContext, MouseButton) + Send + Sync,
711    {
712        build_action_model(self.cx, self.entity);
713
714        self.cx.emit_custom(
715            Event::new(ActionsEvent::OnMouseDown(Box::new(action)))
716                .target(self.entity)
717                .origin(self.entity),
718        );
719
720        self
721    }
722
723    fn on_mouse_up<F>(self, action: F) -> Self
724    where
725        F: 'static + Fn(&mut EventContext, MouseButton) + Send + Sync,
726    {
727        build_action_model(self.cx, self.entity);
728
729        self.cx.emit_custom(
730            Event::new(ActionsEvent::OnMouseUp(Box::new(action)))
731                .target(self.entity)
732                .origin(self.entity),
733        );
734
735        self
736    }
737
738    fn on_focus_in<F>(self, action: F) -> Self
739    where
740        F: 'static + Fn(&mut EventContext) + Send + Sync,
741    {
742        build_action_model(self.cx, self.entity);
743
744        self.cx.emit_custom(
745            Event::new(ActionsEvent::OnFocusIn(Box::new(action)))
746                .target(self.entity)
747                .origin(self.entity),
748        );
749
750        self
751    }
752
753    fn on_focus_out<F>(self, action: F) -> Self
754    where
755        F: 'static + Fn(&mut EventContext) + Send + Sync,
756    {
757        build_action_model(self.cx, self.entity);
758
759        self.cx.emit_custom(
760            Event::new(ActionsEvent::OnFocusOut(Box::new(action)))
761                .target(self.entity)
762                .origin(self.entity),
763        );
764
765        self
766    }
767
768    fn on_geo_changed<F>(self, action: F) -> Self
769    where
770        F: 'static + Fn(&mut EventContext, GeoChanged) + Send + Sync,
771    {
772        build_action_model(self.cx, self.entity);
773
774        self.cx.emit_custom(
775            Event::new(ActionsEvent::OnGeoChanged(Box::new(action)))
776                .target(self.entity)
777                .origin(self.entity),
778        );
779
780        self
781    }
782
783    fn on_drag<F>(self, action: F) -> Self
784    where
785        F: 'static + Fn(&mut EventContext) + Send + Sync,
786    {
787        build_action_model(self.cx, self.entity);
788
789        if let Some(abilities) = self.cx.style.abilities.get_mut(self.entity) {
790            abilities.set(Abilities::DRAGGABLE, true);
791        }
792
793        self.cx.emit_custom(
794            Event::new(ActionsEvent::OnDragStart(Box::new(action)))
795                .target(self.entity)
796                .origin(self.entity),
797        );
798
799        self
800    }
801
802    fn on_drop<F>(self, action: F) -> Self
803    where
804        F: 'static + Fn(&mut EventContext, DropData) + Send + Sync,
805    {
806        build_action_model(self.cx, self.entity);
807
808        self.cx.emit_custom(
809            Event::new(ActionsEvent::OnDrop(Box::new(action)))
810                .target(self.entity)
811                .origin(self.entity),
812        );
813
814        self
815    }
816}