interactive.rs

  1use crate::{
  2    point, Action, Bounds, DispatchContext, DispatchPhase, Element, FocusHandle, GroupStyle,
  3    Keystroke, Modifiers, Pixels, Point, SharedString, StatefulInteractivity,
  4    StatelessInteractivity, StyleRefinement, ViewContext,
  5};
  6use std::{
  7    any::{Any, TypeId},
  8    ops::Deref,
  9    sync::Arc,
 10};
 11
 12pub trait StatelesslyInteractive: Element {
 13    fn stateless_interactivity(&mut self) -> &mut StatelessInteractivity<Self::ViewState>;
 14
 15    fn on_mouse_down(
 16        mut self,
 17        button: MouseButton,
 18        handler: impl Fn(&mut Self::ViewState, &MouseDownEvent, &mut ViewContext<Self::ViewState>)
 19            + Send
 20            + Sync
 21            + 'static,
 22    ) -> Self
 23    where
 24        Self: Sized,
 25    {
 26        self.stateless_interactivity()
 27            .mouse_down_listeners
 28            .push(Arc::new(move |view, event, bounds, phase, cx| {
 29                if phase == DispatchPhase::Bubble
 30                    && event.button == button
 31                    && bounds.contains_point(&event.position)
 32                {
 33                    handler(view, event, cx)
 34                }
 35            }));
 36        self
 37    }
 38
 39    fn on_mouse_up(
 40        mut self,
 41        button: MouseButton,
 42        handler: impl Fn(&mut Self::ViewState, &MouseUpEvent, &mut ViewContext<Self::ViewState>)
 43            + Send
 44            + Sync
 45            + 'static,
 46    ) -> Self
 47    where
 48        Self: Sized,
 49    {
 50        self.stateless_interactivity()
 51            .mouse_up_listeners
 52            .push(Arc::new(move |view, event, bounds, phase, cx| {
 53                if phase == DispatchPhase::Bubble
 54                    && event.button == button
 55                    && bounds.contains_point(&event.position)
 56                {
 57                    handler(view, event, cx)
 58                }
 59            }));
 60        self
 61    }
 62
 63    fn on_mouse_down_out(
 64        mut self,
 65        button: MouseButton,
 66        handler: impl Fn(&mut Self::ViewState, &MouseDownEvent, &mut ViewContext<Self::ViewState>)
 67            + Send
 68            + Sync
 69            + 'static,
 70    ) -> Self
 71    where
 72        Self: Sized,
 73    {
 74        self.stateless_interactivity()
 75            .mouse_down_listeners
 76            .push(Arc::new(move |view, event, bounds, phase, cx| {
 77                if phase == DispatchPhase::Capture
 78                    && event.button == button
 79                    && !bounds.contains_point(&event.position)
 80                {
 81                    handler(view, event, cx)
 82                }
 83            }));
 84        self
 85    }
 86
 87    fn on_mouse_up_out(
 88        mut self,
 89        button: MouseButton,
 90        handler: impl Fn(&mut Self::ViewState, &MouseUpEvent, &mut ViewContext<Self::ViewState>)
 91            + Send
 92            + Sync
 93            + 'static,
 94    ) -> Self
 95    where
 96        Self: Sized,
 97    {
 98        self.stateless_interactivity()
 99            .mouse_up_listeners
100            .push(Arc::new(move |view, event, bounds, phase, cx| {
101                if phase == DispatchPhase::Capture
102                    && event.button == button
103                    && !bounds.contains_point(&event.position)
104                {
105                    handler(view, event, cx);
106                }
107            }));
108        self
109    }
110
111    fn on_mouse_move(
112        mut self,
113        handler: impl Fn(&mut Self::ViewState, &MouseMoveEvent, &mut ViewContext<Self::ViewState>)
114            + Send
115            + Sync
116            + 'static,
117    ) -> Self
118    where
119        Self: Sized,
120    {
121        self.stateless_interactivity()
122            .mouse_move_listeners
123            .push(Arc::new(move |view, event, bounds, phase, cx| {
124                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
125                    handler(view, event, cx);
126                }
127            }));
128        self
129    }
130
131    fn on_scroll_wheel(
132        mut self,
133        handler: impl Fn(&mut Self::ViewState, &ScrollWheelEvent, &mut ViewContext<Self::ViewState>)
134            + Send
135            + Sync
136            + 'static,
137    ) -> Self
138    where
139        Self: Sized,
140    {
141        self.stateless_interactivity()
142            .scroll_wheel_listeners
143            .push(Arc::new(move |view, event, bounds, phase, cx| {
144                if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
145                    handler(view, event, cx);
146                }
147            }));
148        self
149    }
150
151    fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
152    where
153        Self: Sized,
154    {
155        self.stateless_interactivity().hover_style = f(StyleRefinement::default());
156        self
157    }
158
159    fn group_hover(
160        mut self,
161        group_name: impl Into<SharedString>,
162        f: impl FnOnce(StyleRefinement) -> StyleRefinement,
163    ) -> Self
164    where
165        Self: Sized,
166    {
167        self.stateless_interactivity().group_hover_style = Some(GroupStyle {
168            group: group_name.into(),
169            style: f(StyleRefinement::default()),
170        });
171        self
172    }
173
174    fn on_key_down(
175        mut self,
176        listener: impl Fn(
177                &mut Self::ViewState,
178                &KeyDownEvent,
179                DispatchPhase,
180                &mut ViewContext<Self::ViewState>,
181            ) + Send
182            + Sync
183            + 'static,
184    ) -> Self
185    where
186        Self: Sized,
187    {
188        self.stateless_interactivity().key_listeners.push((
189            TypeId::of::<KeyDownEvent>(),
190            Arc::new(move |view, event, _, phase, cx| {
191                let event = event.downcast_ref().unwrap();
192                listener(view, event, phase, cx);
193                None
194            }),
195        ));
196        self
197    }
198
199    fn on_key_up(
200        mut self,
201        listener: impl Fn(&mut Self::ViewState, &KeyUpEvent, DispatchPhase, &mut ViewContext<Self::ViewState>)
202            + Send
203            + Sync
204            + 'static,
205    ) -> Self
206    where
207        Self: Sized,
208    {
209        self.stateless_interactivity().key_listeners.push((
210            TypeId::of::<KeyUpEvent>(),
211            Arc::new(move |view, event, _, phase, cx| {
212                let event = event.downcast_ref().unwrap();
213                listener(view, event, phase, cx);
214                None
215            }),
216        ));
217        self
218    }
219
220    fn on_action<A: 'static>(
221        mut self,
222        listener: impl Fn(&mut Self::ViewState, &A, DispatchPhase, &mut ViewContext<Self::ViewState>)
223            + Send
224            + Sync
225            + 'static,
226    ) -> Self
227    where
228        Self: Sized,
229    {
230        self.stateless_interactivity().key_listeners.push((
231            TypeId::of::<A>(),
232            Arc::new(move |view, event, _, phase, cx| {
233                let event = event.downcast_ref().unwrap();
234                listener(view, event, phase, cx);
235                None
236            }),
237        ));
238        self
239    }
240}
241
242pub trait StatefullyInteractive: StatelesslyInteractive {
243    fn stateful_interactivity(&mut self) -> &mut StatefulInteractivity<Self::ViewState>;
244
245    fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
246    where
247        Self: Sized,
248    {
249        self.stateful_interactivity().active_style = f(StyleRefinement::default());
250        self
251    }
252
253    fn group_active(
254        mut self,
255        group_name: impl Into<SharedString>,
256        f: impl FnOnce(StyleRefinement) -> StyleRefinement,
257    ) -> Self
258    where
259        Self: Sized,
260    {
261        self.stateful_interactivity().group_active_style = Some(GroupStyle {
262            group: group_name.into(),
263            style: f(StyleRefinement::default()),
264        });
265        self
266    }
267
268    fn on_click(
269        mut self,
270        handler: impl Fn(&mut Self::ViewState, &MouseClickEvent, &mut ViewContext<Self::ViewState>)
271            + Send
272            + Sync
273            + 'static,
274    ) -> Self
275    where
276        Self: Sized,
277    {
278        self.stateful_interactivity()
279            .mouse_click_listeners
280            .push(Arc::new(move |view, event, cx| handler(view, event, cx)));
281        self
282    }
283}
284
285#[derive(Clone, Debug, Eq, PartialEq)]
286pub struct KeyDownEvent {
287    pub keystroke: Keystroke,
288    pub is_held: bool,
289}
290
291#[derive(Clone, Debug)]
292pub struct KeyUpEvent {
293    pub keystroke: Keystroke,
294}
295
296#[derive(Clone, Debug, Default)]
297pub struct ModifiersChangedEvent {
298    pub modifiers: Modifiers,
299}
300
301impl Deref for ModifiersChangedEvent {
302    type Target = Modifiers;
303
304    fn deref(&self) -> &Self::Target {
305        &self.modifiers
306    }
307}
308
309/// The phase of a touch motion event.
310/// Based on the winit enum of the same name.
311#[derive(Clone, Copy, Debug)]
312pub enum TouchPhase {
313    Started,
314    Moved,
315    Ended,
316}
317
318#[derive(Clone, Debug, Default)]
319pub struct MouseDownEvent {
320    pub button: MouseButton,
321    pub position: Point<Pixels>,
322    pub modifiers: Modifiers,
323    pub click_count: usize,
324}
325
326#[derive(Clone, Debug, Default)]
327pub struct MouseUpEvent {
328    pub button: MouseButton,
329    pub position: Point<Pixels>,
330    pub modifiers: Modifiers,
331    pub click_count: usize,
332}
333
334#[derive(Clone, Debug, Default)]
335pub struct MouseClickEvent {
336    pub down: MouseDownEvent,
337    pub up: MouseUpEvent,
338}
339
340#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
341pub enum MouseButton {
342    Left,
343    Right,
344    Middle,
345    Navigate(NavigationDirection),
346}
347
348impl MouseButton {
349    pub fn all() -> Vec<Self> {
350        vec![
351            MouseButton::Left,
352            MouseButton::Right,
353            MouseButton::Middle,
354            MouseButton::Navigate(NavigationDirection::Back),
355            MouseButton::Navigate(NavigationDirection::Forward),
356        ]
357    }
358}
359
360impl Default for MouseButton {
361    fn default() -> Self {
362        Self::Left
363    }
364}
365
366#[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)]
367pub enum NavigationDirection {
368    Back,
369    Forward,
370}
371
372impl Default for NavigationDirection {
373    fn default() -> Self {
374        Self::Back
375    }
376}
377
378#[derive(Clone, Debug, Default)]
379pub struct MouseMoveEvent {
380    pub position: Point<Pixels>,
381    pub pressed_button: Option<MouseButton>,
382    pub modifiers: Modifiers,
383}
384
385#[derive(Clone, Debug)]
386pub struct ScrollWheelEvent {
387    pub position: Point<Pixels>,
388    pub delta: ScrollDelta,
389    pub modifiers: Modifiers,
390    pub touch_phase: TouchPhase,
391}
392
393impl Deref for ScrollWheelEvent {
394    type Target = Modifiers;
395
396    fn deref(&self) -> &Self::Target {
397        &self.modifiers
398    }
399}
400
401#[derive(Clone, Copy, Debug)]
402pub enum ScrollDelta {
403    Pixels(Point<Pixels>),
404    Lines(Point<f32>),
405}
406
407impl Default for ScrollDelta {
408    fn default() -> Self {
409        Self::Lines(Default::default())
410    }
411}
412
413impl ScrollDelta {
414    pub fn precise(&self) -> bool {
415        match self {
416            ScrollDelta::Pixels(_) => true,
417            ScrollDelta::Lines(_) => false,
418        }
419    }
420
421    pub fn pixel_delta(&self, line_height: Pixels) -> Point<Pixels> {
422        match self {
423            ScrollDelta::Pixels(delta) => *delta,
424            ScrollDelta::Lines(delta) => point(line_height * delta.x, line_height * delta.y),
425        }
426    }
427}
428
429#[derive(Clone, Debug, Default)]
430pub struct MouseExitEvent {
431    pub position: Point<Pixels>,
432    pub pressed_button: Option<MouseButton>,
433    pub modifiers: Modifiers,
434}
435
436impl Deref for MouseExitEvent {
437    type Target = Modifiers;
438
439    fn deref(&self) -> &Self::Target {
440        &self.modifiers
441    }
442}
443
444#[derive(Clone, Debug)]
445pub enum InputEvent {
446    KeyDown(KeyDownEvent),
447    KeyUp(KeyUpEvent),
448    ModifiersChanged(ModifiersChangedEvent),
449    MouseDown(MouseDownEvent),
450    MouseUp(MouseUpEvent),
451    MouseMoved(MouseMoveEvent),
452    MouseExited(MouseExitEvent),
453    ScrollWheel(ScrollWheelEvent),
454}
455
456impl InputEvent {
457    pub fn position(&self) -> Option<Point<Pixels>> {
458        match self {
459            InputEvent::KeyDown { .. } => None,
460            InputEvent::KeyUp { .. } => None,
461            InputEvent::ModifiersChanged { .. } => None,
462            InputEvent::MouseDown(event) => Some(event.position),
463            InputEvent::MouseUp(event) => Some(event.position),
464            InputEvent::MouseMoved(event) => Some(event.position),
465            InputEvent::MouseExited(event) => Some(event.position),
466            InputEvent::ScrollWheel(event) => Some(event.position),
467        }
468    }
469
470    pub fn mouse_event<'a>(&'a self) -> Option<&'a dyn Any> {
471        match self {
472            InputEvent::KeyDown { .. } => None,
473            InputEvent::KeyUp { .. } => None,
474            InputEvent::ModifiersChanged { .. } => None,
475            InputEvent::MouseDown(event) => Some(event),
476            InputEvent::MouseUp(event) => Some(event),
477            InputEvent::MouseMoved(event) => Some(event),
478            InputEvent::MouseExited(event) => Some(event),
479            InputEvent::ScrollWheel(event) => Some(event),
480        }
481    }
482
483    pub fn keyboard_event<'a>(&'a self) -> Option<&'a dyn Any> {
484        match self {
485            InputEvent::KeyDown(event) => Some(event),
486            InputEvent::KeyUp(event) => Some(event),
487            InputEvent::ModifiersChanged(event) => Some(event),
488            InputEvent::MouseDown(_) => None,
489            InputEvent::MouseUp(_) => None,
490            InputEvent::MouseMoved(_) => None,
491            InputEvent::MouseExited(_) => None,
492            InputEvent::ScrollWheel(_) => None,
493        }
494    }
495}
496
497pub struct FocusEvent {
498    pub blurred: Option<FocusHandle>,
499    pub focused: Option<FocusHandle>,
500}
501
502pub type MouseDownListener<V> = Arc<
503    dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
504        + Send
505        + Sync
506        + 'static,
507>;
508pub type MouseUpListener<V> = Arc<
509    dyn Fn(&mut V, &MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
510        + Send
511        + Sync
512        + 'static,
513>;
514pub type MouseClickListener<V> =
515    Arc<dyn Fn(&mut V, &MouseClickEvent, &mut ViewContext<V>) + Send + Sync + 'static>;
516
517pub type MouseMoveListener<V> = Arc<
518    dyn Fn(&mut V, &MouseMoveEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
519        + Send
520        + Sync
521        + 'static,
522>;
523
524pub type ScrollWheelListener<V> = Arc<
525    dyn Fn(&mut V, &ScrollWheelEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
526        + Send
527        + Sync
528        + 'static,
529>;
530
531pub type KeyListener<V> = Arc<
532    dyn Fn(
533            &mut V,
534            &dyn Any,
535            &[&DispatchContext],
536            DispatchPhase,
537            &mut ViewContext<V>,
538        ) -> Option<Box<dyn Action>>
539        + Send
540        + Sync
541        + 'static,
542>;