use gpui::{ geometry::rect::RectF, platform::{MouseButton, MouseButtonEvent}, EventContext, }; use smallvec::SmallVec; use std::{cell::Cell, rc::Rc}; use crate::element::PaintContext; pub trait Interactive { fn interaction_handlers(&mut self) -> &mut InteractionHandlers; fn on_mouse_down( mut self, button: MouseButton, handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext) + 'static, ) -> Self where Self: Sized, { self.interaction_handlers() .mouse_down .push(Rc::new(handler)); self } fn on_mouse_up( mut self, button: MouseButton, handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext) + 'static, ) -> Self where Self: Sized, { self.interaction_handlers().mouse_up.push(Rc::new(handler)); self } fn on_mouse_down_out( mut self, button: MouseButton, handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext) + 'static, ) -> Self where Self: Sized, { self.interaction_handlers() .mouse_down_out .push(Rc::new(handler)); self } fn on_mouse_up_out( mut self, button: MouseButton, handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext) + 'static, ) -> Self where Self: Sized, { self.interaction_handlers() .mouse_up_out .push(Rc::new(handler)); self } fn on_click( self, button: MouseButton, handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext) + 'static, ) -> Self where Self: Sized, { let pressed = Rc::new(Cell::new(false)); self.on_mouse_down(button, { let pressed = pressed.clone(); move |_, _, _| { pressed.set(true); } }) .on_mouse_up_out(button, { let pressed = pressed.clone(); move |_, _, _| { pressed.set(false); } }) .on_mouse_up(button, move |view, event, cx| { if pressed.get() { pressed.set(false); handler(view, event, cx); } }) } } pub struct InteractionHandlers { mouse_down: SmallVec<[Rc)>; 2]>, mouse_down_out: SmallVec<[Rc)>; 2]>, mouse_up: SmallVec<[Rc)>; 2]>, mouse_up_out: SmallVec<[Rc)>; 2]>, } impl InteractionHandlers { pub fn paint(&self, order: u32, bounds: RectF, cx: &mut PaintContext) { for handler in self.mouse_down.iter().cloned() { cx.on_event(order, move |view, event: &MouseButtonEvent, cx| { if event.is_down && bounds.contains_point(event.position) { handler(view, event, cx); } }) } for handler in self.mouse_up.iter().cloned() { cx.on_event(order, move |view, event: &MouseButtonEvent, cx| { if !event.is_down && bounds.contains_point(event.position) { handler(view, event, cx); } }) } for handler in self.mouse_down_out.iter().cloned() { cx.on_event(order, move |view, event: &MouseButtonEvent, cx| { if event.is_down && !bounds.contains_point(event.position) { handler(view, event, cx); } }) } for handler in self.mouse_up_out.iter().cloned() { cx.on_event(order, move |view, event: &MouseButtonEvent, cx| { if !event.is_down && !bounds.contains_point(event.position) { handler(view, event, cx); } }) } } } impl Default for InteractionHandlers { fn default() -> Self { Self { mouse_down: Default::default(), mouse_up: Default::default(), mouse_down_out: Default::default(), mouse_up_out: Default::default(), } } }