Store generic mouse regions on window that contain their event type id

This commit is contained in:
Nathan Sobo 2023-08-17 08:37:55 -06:00
parent 812d3f6af6
commit b95b2af3e0
8 changed files with 130 additions and 57 deletions

View file

@ -11,8 +11,11 @@ use gpui::{
EngineLayout, EventContext, RenderContext, ViewContext, EngineLayout, EventContext, RenderContext, ViewContext,
}; };
use playground_macros::tailwind_lengths; use playground_macros::tailwind_lengths;
use smallvec::SmallVec; use std::{
use std::{any::Any, cell::Cell, rc::Rc}; any::{Any, TypeId},
cell::Cell,
rc::Rc,
};
pub use crate::paint_context::PaintContext; pub use crate::paint_context::PaintContext;
pub use taffy::tree::NodeId; pub use taffy::tree::NodeId;
@ -22,28 +25,32 @@ pub struct Layout<'a, E: ?Sized> {
pub from_element: &'a mut E, pub from_element: &'a mut E,
} }
pub struct ElementHandlers<V> {
mouse_button: SmallVec<[Rc<dyn Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>)>; 2]>,
}
pub struct ElementMetadata<V> { pub struct ElementMetadata<V> {
pub style: ElementStyle, pub style: ElementStyle,
pub handlers: ElementHandlers<V>, pub handlers: Vec<EventHandler<V>>,
}
pub struct EventHandler<V> {
handler: Rc<dyn Fn(&mut V, &dyn Any, &mut EventContext<V>)>,
event_type: TypeId,
outside_bounds: bool,
}
impl<V> Clone for EventHandler<V> {
fn clone(&self) -> Self {
Self {
handler: self.handler.clone(),
event_type: self.event_type,
outside_bounds: self.outside_bounds,
}
}
} }
impl<V> Default for ElementMetadata<V> { impl<V> Default for ElementMetadata<V> {
fn default() -> Self { fn default() -> Self {
Self { Self {
style: ElementStyle::default(), style: ElementStyle::default(),
handlers: ElementHandlers::default(), handlers: Vec::new(),
}
}
}
impl<V> Default for ElementHandlers<V> {
fn default() -> Self {
ElementHandlers {
mouse_button: Default::default(),
} }
} }
} }
@ -52,7 +59,8 @@ pub trait Element<V: 'static>: 'static {
type Layout: 'static; type Layout: 'static;
fn style_mut(&mut self) -> &mut ElementStyle; fn style_mut(&mut self) -> &mut ElementStyle;
fn handlers_mut(&mut self) -> &mut ElementHandlers<V>; fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>>;
fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>)
-> Result<(NodeId, Self::Layout)>; -> Result<(NodeId, Self::Layout)>;
fn paint<'a>( fn paint<'a>(
@ -96,6 +104,12 @@ pub trait Element<V: 'static>: 'static {
pressed.set(true); pressed.set(true);
} }
}) })
.mouse_up_outside(button, {
let pressed = pressed.clone();
move |_, _, _| {
pressed.set(false);
}
})
.mouse_up(button, move |view, event, event_cx| { .mouse_up(button, move |view, event, event_cx| {
if pressed.get() { if pressed.get() {
pressed.set(false); pressed.set(false);
@ -112,13 +126,37 @@ pub trait Element<V: 'static>: 'static {
where where
Self: Sized, Self: Sized,
{ {
self.handlers_mut() self.handlers_mut().push(EventHandler {
.mouse_button handler: Rc::new(move |view, event, event_cx| {
.push(Rc::new(move |view, event, event_cx| { let event = event.downcast_ref::<MouseButtonEvent>().unwrap();
if event.button == button && event.is_down { if event.button == button && event.is_down {
handler(view, event, event_cx); handler(view, event, event_cx);
} }
})); }),
event_type: TypeId::of::<MouseButtonEvent>(),
outside_bounds: false,
});
self
}
fn mouse_down_outside(
mut self,
button: MouseButton,
handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
self.handlers_mut().push(EventHandler {
handler: Rc::new(move |view, event, event_cx| {
let event = event.downcast_ref::<MouseButtonEvent>().unwrap();
if event.button == button && event.is_down {
handler(view, event, event_cx);
}
}),
event_type: TypeId::of::<MouseButtonEvent>(),
outside_bounds: true,
});
self self
} }
@ -130,13 +168,37 @@ pub trait Element<V: 'static>: 'static {
where where
Self: Sized, Self: Sized,
{ {
self.handlers_mut() self.handlers_mut().push(EventHandler {
.mouse_button handler: Rc::new(move |view, event, event_cx| {
.push(Rc::new(move |view, event, event_cx| { let event = event.downcast_ref::<MouseButtonEvent>().unwrap();
if event.button == button && !event.is_down { if event.button == button && !event.is_down {
handler(view, event, event_cx); handler(view, event, event_cx);
} }
})); }),
event_type: TypeId::of::<MouseButtonEvent>(),
outside_bounds: false,
});
self
}
fn mouse_up_outside(
mut self,
button: MouseButton,
handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
) -> Self
where
Self: Sized,
{
self.handlers_mut().push(EventHandler {
handler: Rc::new(move |view, event, event_cx| {
let event = event.downcast_ref::<MouseButtonEvent>().unwrap();
if event.button == button && !event.is_down {
handler(view, event, event_cx);
}
}),
event_type: TypeId::of::<MouseButtonEvent>(),
outside_bounds: true,
});
self self
} }
@ -362,7 +424,7 @@ pub trait Element<V: 'static>: 'static {
// Object-safe counterpart of Element used by AnyElement to store elements as trait objects. // Object-safe counterpart of Element used by AnyElement to store elements as trait objects.
trait ElementObject<V> { trait ElementObject<V> {
fn style_mut(&mut self) -> &mut ElementStyle; fn style_mut(&mut self) -> &mut ElementStyle;
fn handlers_mut(&mut self) -> &mut ElementHandlers<V>; fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>>;
fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>)
-> Result<(NodeId, Box<dyn Any>)>; -> Result<(NodeId, Box<dyn Any>)>;
fn paint( fn paint(
@ -378,7 +440,7 @@ impl<V: 'static, E: Element<V>> ElementObject<V> for E {
Element::style_mut(self) Element::style_mut(self)
} }
fn handlers_mut(&mut self) -> &mut ElementHandlers<V> { fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>> {
Element::handlers_mut(self) Element::handlers_mut(self)
} }
@ -454,19 +516,27 @@ impl<V: 'static> AnyElement<V> {
from_element: element_layout.as_mut(), from_element: element_layout.as_mut(),
}; };
for handler in self.element.handlers_mut().mouse_button.iter().cloned() { for event_handler in self.element.handlers_mut().iter().cloned() {
let EngineLayout { order, bounds } = layout.from_engine; let EngineLayout { order, bounds } = layout.from_engine;
let view_id = cx.view_id(); let view_id = cx.view_id();
cx.draw_interactive_region( let view_event_handler = event_handler.handler.clone();
order,
bounds, // TODO: Tuck this into a method on PaintContext.
move |view, event: &MouseButtonEvent, window_cx| { cx.scene
let mut view_cx = ViewContext::mutable(window_cx, view_id); .interactive_regions
let mut event_cx = EventContext::new(&mut view_cx); .push(gpui::scene::InteractiveRegion {
(handler)(view, event, &mut event_cx); order,
}, bounds,
); outside_bounds: event_handler.outside_bounds,
event_handler: Rc::new(move |view, event, window_cx, view_id| {
let mut view_context = ViewContext::mutable(window_cx, view_id);
let mut event_context = EventContext::new(&mut view_context);
view_event_handler(view.downcast_mut().unwrap(), event, &mut event_context);
}),
event_type: event_handler.event_type,
view_id,
});
} }
self.element.paint(layout, view, cx)?; self.element.paint(layout, view, cx)?;
@ -485,7 +555,7 @@ impl<V: 'static> Element<V> for AnyElement<V> {
self.element.style_mut() self.element.style_mut()
} }
fn handlers_mut(&mut self) -> &mut ElementHandlers<V> { fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>> {
self.element.handlers_mut() self.element.handlers_mut()
} }

View file

@ -1,7 +1,6 @@
use crate::{ use crate::{
element::{ element::{
AnyElement, Element, ElementHandlers, IntoElement, Layout, LayoutContext, NodeId, AnyElement, Element, EventHandler, IntoElement, Layout, LayoutContext, NodeId, PaintContext,
PaintContext,
}, },
style::ElementStyle, style::ElementStyle,
}; };
@ -13,14 +12,14 @@ use playground_macros::IntoElement;
#[element_crate = "crate"] #[element_crate = "crate"]
pub struct Frame<V: 'static> { pub struct Frame<V: 'static> {
style: ElementStyle, style: ElementStyle,
handlers: ElementHandlers<V>, handlers: Vec<EventHandler<V>>,
children: Vec<AnyElement<V>>, children: Vec<AnyElement<V>>,
} }
pub fn frame<V>() -> Frame<V> { pub fn frame<V>() -> Frame<V> {
Frame { Frame {
style: ElementStyle::default(), style: ElementStyle::default(),
handlers: ElementHandlers::default(), handlers: Vec::new(),
children: Vec::new(), children: Vec::new(),
} }
} }
@ -32,7 +31,7 @@ impl<V: 'static> Element<V> for Frame<V> {
&mut self.style &mut self.style
} }
fn handlers_mut(&mut self) -> &mut ElementHandlers<V> { fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>> {
&mut self.handlers &mut self.handlers
} }

View file

@ -41,12 +41,14 @@ impl<'a, 'b, 'c, 'd, V: 'static> PaintContext<'a, 'b, 'c, 'd, V> {
&mut self, &mut self,
order: u32, order: u32,
bounds: RectF, bounds: RectF,
outside_bounds: bool,
handler: impl Fn(&mut V, &E, &mut EventContext<V>) + 'static, handler: impl Fn(&mut V, &E, &mut EventContext<V>) + 'static,
) { ) {
// We'll sort these by their order in `take_interactive_regions`. // We'll sort these by their order in `take_interactive_regions`.
self.scene.interactive_regions.push(InteractiveRegion { self.scene.interactive_regions.push(InteractiveRegion {
order, order,
bounds, bounds,
outside_bounds,
event_handler: Rc::new(move |view, event, window_cx, view_id| { event_handler: Rc::new(move |view, event, window_cx, view_id| {
let mut cx = ViewContext::mutable(window_cx, view_id); let mut cx = ViewContext::mutable(window_cx, view_id);
let mut cx = EventContext::new(&mut cx); let mut cx = EventContext::new(&mut cx);

View file

@ -49,12 +49,7 @@ fn playground<V: 'static>(theme: &ThemeColors) -> impl Element<V> {
.h_full() .h_full()
.w_half() .w_half()
.fill(theme.success(0.5)) .fill(theme.success(0.5))
.child( .child(button().label("Hello").click(|_, _, _| println!("click!")))
button()
.label("Hello")
.mouse_up(MouseButton::Left, |_, _, _| (println!("up!")))
.mouse_down(MouseButton::Left, |_, _, _| (println!("down!"))),
)
} }
// todo!() // todo!()

View file

@ -1,4 +1,4 @@
use crate::element::{Element, ElementMetadata, IntoElement}; use crate::element::{Element, ElementMetadata, EventHandler, IntoElement};
use gpui::{geometry::Size, text_layout::LineLayout, RenderContext}; use gpui::{geometry::Size, text_layout::LineLayout, RenderContext};
use parking_lot::Mutex; use parking_lot::Mutex;
use std::sync::Arc; use std::sync::Arc;
@ -26,10 +26,6 @@ impl<V: 'static> Element<V> for Text<V> {
&mut self.metadata.style &mut self.metadata.style
} }
fn handlers_mut(&mut self) -> &mut crate::element::ElementHandlers<V> {
&mut self.metadata.handlers
}
fn layout( fn layout(
&mut self, &mut self,
view: &mut V, view: &mut V,
@ -95,6 +91,10 @@ impl<V: 'static> Element<V> for Text<V> {
); );
Ok(()) Ok(())
} }
fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>> {
&mut self.metadata.handlers
}
} }
pub struct TextLayout { pub struct TextLayout {

View file

@ -83,7 +83,7 @@ pub fn derive_element(input: TokenStream) -> TokenStream {
&mut self.metadata.style &mut self.metadata.style
} }
fn handlers_mut(&mut self) -> &mut #crate_name::element::ElementHandlers<V> { fn handlers_mut(&mut self) -> &mut Vec<#crate_name::element::EventHandler<V>> {
&mut self.metadata.handlers &mut self.metadata.handlers
} }

View file

@ -118,6 +118,10 @@ impl Window {
.as_ref() .as_ref()
.expect("root_view called during window construction") .expect("root_view called during window construction")
} }
pub fn take_interactive_regions(&mut self) -> Vec<InteractiveRegion> {
mem::take(&mut self.interactive_regions)
}
} }
pub struct WindowContext<'a> { pub struct WindowContext<'a> {
@ -875,11 +879,13 @@ impl<'a> WindowContext<'a> {
fn dispatch_to_interactive_regions(&mut self, event: &Event) { fn dispatch_to_interactive_regions(&mut self, event: &Event) {
if let Some(mouse_event) = event.mouse_event() { if let Some(mouse_event) = event.mouse_event() {
let mouse_position = event.position().expect("mouse events must have a position"); let mouse_position = event.position().expect("mouse events must have a position");
let interactive_regions = std::mem::take(&mut self.window.interactive_regions); let interactive_regions = self.window.take_interactive_regions();
for region in interactive_regions.iter().rev() { for region in interactive_regions.iter().rev() {
if region.event_type == mouse_event.type_id() { if region.event_type == mouse_event.type_id() {
if region.bounds.contains_point(mouse_position) { let in_bounds = region.bounds.contains_point(mouse_position);
if in_bounds == !region.outside_bounds {
self.update_any_view(region.view_id, |view, window_cx| { self.update_any_view(region.view_id, |view, window_cx| {
(region.event_handler)( (region.event_handler)(
view.as_any_mut(), view.as_any_mut(),

View file

@ -711,6 +711,7 @@ impl MouseRegion {
pub struct InteractiveRegion { pub struct InteractiveRegion {
pub order: u32, pub order: u32,
pub bounds: RectF, pub bounds: RectF,
pub outside_bounds: bool,
pub event_handler: Rc<dyn Fn(&mut dyn Any, &dyn Any, &mut WindowContext, usize)>, pub event_handler: Rc<dyn Fn(&mut dyn Any, &dyn Any, &mut WindowContext, usize)>,
pub event_type: TypeId, pub event_type: TypeId,
pub view_id: usize, pub view_id: usize,