interactive.rs

  1use gpui::{
  2    geometry::rect::RectF,
  3    platform::{MouseButton, MouseButtonEvent},
  4    EventContext,
  5};
  6use smallvec::SmallVec;
  7use std::{cell::Cell, rc::Rc};
  8
  9use crate::element::PaintContext;
 10
 11pub trait Interactive<V: 'static> {
 12    fn interaction_handlers(&mut self) -> &mut InteractionHandlers<V>;
 13
 14    // One line change.
 15    fn on_mouse_down(
 16        mut self,
 17        button: MouseButton,
 18        handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
 19    ) -> Self
 20    where
 21        Self: Sized,
 22    {
 23        self.interaction_handlers()
 24            .mouse_down
 25            .push(Rc::new(handler));
 26        self
 27    }
 28
 29    fn on_mouse_up(
 30        mut self,
 31        button: MouseButton,
 32        handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
 33    ) -> Self
 34    where
 35        Self: Sized,
 36    {
 37        self.interaction_handlers().mouse_up.push(Rc::new(handler));
 38        self
 39    }
 40
 41    fn on_mouse_down_out(
 42        mut self,
 43        button: MouseButton,
 44        handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
 45    ) -> Self
 46    where
 47        Self: Sized,
 48    {
 49        self.interaction_handlers()
 50            .mouse_down_out
 51            .push(Rc::new(handler));
 52        self
 53    }
 54
 55    fn on_mouse_up_out(
 56        mut self,
 57        button: MouseButton,
 58        handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
 59    ) -> Self
 60    where
 61        Self: Sized,
 62    {
 63        self.interaction_handlers()
 64            .mouse_up_out
 65            .push(Rc::new(handler));
 66        self
 67    }
 68
 69    fn on_click(
 70        self,
 71        button: MouseButton,
 72        handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
 73    ) -> Self
 74    where
 75        Self: Sized,
 76    {
 77        let pressed = Rc::new(Cell::new(false));
 78        self.on_mouse_down(button, {
 79            let pressed = pressed.clone();
 80            move |_, _, _| {
 81                pressed.set(true);
 82            }
 83        })
 84        .on_mouse_up_out(button, {
 85            let pressed = pressed.clone();
 86            move |_, _, _| {
 87                pressed.set(false);
 88            }
 89        })
 90        .on_mouse_up(button, move |view, event, cx| {
 91            if pressed.get() {
 92                pressed.set(false);
 93                handler(view, event, cx);
 94            }
 95        })
 96    }
 97}
 98
 99pub struct InteractionHandlers<V: 'static> {
100    mouse_down: SmallVec<[Rc<dyn Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>)>; 2]>,
101    mouse_down_out: SmallVec<[Rc<dyn Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>)>; 2]>,
102    mouse_up: SmallVec<[Rc<dyn Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>)>; 2]>,
103    mouse_up_out: SmallVec<[Rc<dyn Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>)>; 2]>,
104}
105
106impl<V: 'static> InteractionHandlers<V> {
107    pub fn paint(&self, order: u32, bounds: RectF, cx: &mut PaintContext<V>) {
108        for handler in self.mouse_down.iter().cloned() {
109            cx.on_event(order, move |view, event: &MouseButtonEvent, cx| {
110                if event.is_down && bounds.contains_point(event.position) {
111                    handler(view, event, cx);
112                }
113            })
114        }
115        for handler in self.mouse_up.iter().cloned() {
116            cx.on_event(order, move |view, event: &MouseButtonEvent, cx| {
117                if !event.is_down && bounds.contains_point(event.position) {
118                    handler(view, event, cx);
119                }
120            })
121        }
122        for handler in self.mouse_down_out.iter().cloned() {
123            cx.on_event(order, move |view, event: &MouseButtonEvent, cx| {
124                if event.is_down && !bounds.contains_point(event.position) {
125                    handler(view, event, cx);
126                }
127            })
128        }
129        for handler in self.mouse_up_out.iter().cloned() {
130            cx.on_event(order, move |view, event: &MouseButtonEvent, cx| {
131                if !event.is_down && !bounds.contains_point(event.position) {
132                    handler(view, event, cx);
133                }
134            })
135        }
136    }
137}
138
139impl<V> Default for InteractionHandlers<V> {
140    fn default() -> Self {
141        Self {
142            mouse_down: Default::default(),
143            mouse_up: Default::default(),
144            mouse_down_out: Default::default(),
145            mouse_up_out: Default::default(),
146        }
147    }
148}