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