Add basic mouse event handling

This commit is contained in:
Nathan Sobo 2023-10-09 21:17:56 -06:00
parent f763ed9a7e
commit 8074e6b46a
6 changed files with 70 additions and 43 deletions

View file

@ -1,13 +1,11 @@
mod div; mod div;
mod img; mod img;
mod interactive;
mod stateless; mod stateless;
mod svg; mod svg;
mod text; mod text;
pub use div::*; pub use div::*;
pub use img::*; pub use img::*;
pub use interactive::*;
pub use stateless::*; pub use stateless::*;
pub use svg::*; pub use svg::*;
pub use text::*; pub use text::*;

View file

@ -1,6 +1,7 @@
use crate::{ use crate::{
AnyElement, Bounds, Element, LayoutId, Overflow, ParentElement, Pixels, Point, Refineable, AnyElement, Bounds, Element, Interactive, LayoutId, MouseEventListeners, Overflow,
RefinementCascade, Result, Style, StyleHelpers, Styled, ViewContext, ParentElement, Pixels, Point, Refineable, RefinementCascade, Result, Style, StyleHelpers,
Styled, ViewContext,
}; };
use parking_lot::Mutex; use parking_lot::Mutex;
use smallvec::SmallVec; use smallvec::SmallVec;
@ -9,7 +10,7 @@ use util::ResultExt;
pub struct Div<S: 'static> { pub struct Div<S: 'static> {
styles: RefinementCascade<Style>, styles: RefinementCascade<Style>,
// handlers: InteractionHandlers<V>, listeners: MouseEventListeners<S>,
children: SmallVec<[AnyElement<S>; 2]>, children: SmallVec<[AnyElement<S>; 2]>,
scroll_state: Option<ScrollState>, scroll_state: Option<ScrollState>,
} }
@ -17,7 +18,7 @@ pub struct Div<S: 'static> {
pub fn div<S>() -> Div<S> { pub fn div<S>() -> Div<S> {
Div { Div {
styles: Default::default(), styles: Default::default(),
// handlers: Default::default(), listeners: Default::default(),
children: Default::default(), children: Default::default(),
scroll_state: None, scroll_state: None,
} }
@ -42,7 +43,7 @@ impl<S: 'static + Send + Sync> Element for Div<S> {
&mut self, &mut self,
bounds: Bounds<Pixels>, bounds: Bounds<Pixels>,
state: &mut S, state: &mut S,
child_layouts: &mut Self::FrameState, child_layout_ids: &mut Self::FrameState,
cx: &mut ViewContext<S>, cx: &mut ViewContext<S>,
) -> Result<()> { ) -> Result<()> {
let style = self.computed_style(); let style = self.computed_style();
@ -51,10 +52,13 @@ impl<S: 'static + Send + Sync> Element for Div<S> {
let overflow = &style.overflow; let overflow = &style.overflow;
style.apply_text_style(cx, |cx| { style.apply_text_style(cx, |cx| {
cx.stack(1, |cx| { cx.stack(1, |cx| {
style.apply_overflow(bounds, cx, |cx| self.paint_children(overflow, state, cx)) style.apply_overflow(bounds, cx, |cx| {
self.listeners.paint(bounds, cx);
self.paint_children(overflow, state, cx)
})
}) })
})?; })?;
self.handle_scroll(bounds, style.overflow.clone(), child_layouts, cx); self.handle_scroll(bounds, style.overflow.clone(), child_layout_ids, cx);
// todo!("enable inspector") // todo!("enable inspector")
// if cx.is_inspector_enabled() { // if cx.is_inspector_enabled() {
@ -247,11 +251,11 @@ impl<V> Styled for Div<V> {
impl<V> StyleHelpers for Div<V> {} impl<V> StyleHelpers for Div<V> {}
// impl<V> Interactive<V> for Div<V> { impl<V: Send + Sync + 'static> Interactive<V> for Div<V> {
// fn interaction_handlers(&mut self) -> &mut InteractionHandlers<V> { fn listeners(&mut self) -> &mut MouseEventListeners<V> {
// &mut self.handlers &mut self.listeners
// } }
// } }
impl<V: 'static> ParentElement<V> for Div<V> { impl<V: 'static> ParentElement<V> for Div<V> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> { fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {

View file

@ -6,6 +6,7 @@ mod elements;
mod executor; mod executor;
mod geometry; mod geometry;
mod image_cache; mod image_cache;
mod interactive;
mod platform; mod platform;
mod scene; mod scene;
mod style; mod style;
@ -28,6 +29,7 @@ pub use executor::*;
pub use geometry::*; pub use geometry::*;
pub use gpui3_macros::*; pub use gpui3_macros::*;
pub use image_cache::*; pub use image_cache::*;
pub use interactive::*;
pub use platform::*; pub use platform::*;
pub use refineable::*; pub use refineable::*;
pub use scene::*; pub use scene::*;

View file

@ -6,7 +6,7 @@ use smallvec::SmallVec;
use std::sync::Arc; use std::sync::Arc;
pub trait Interactive<S: 'static + Send + Sync> { pub trait Interactive<S: 'static + Send + Sync> {
fn interaction_listeners(&mut self) -> &mut InteractionHandlers<S>; fn listeners(&mut self) -> &mut MouseEventListeners<S>;
fn on_mouse_down( fn on_mouse_down(
mut self, mut self,
@ -16,10 +16,13 @@ pub trait Interactive<S: 'static + Send + Sync> {
where where
Self: Sized, Self: Sized,
{ {
self.interaction_listeners() self.listeners()
.mouse_down .mouse_down
.push(Arc::new(move |view, event, phase, cx| { .push(Arc::new(move |view, event, bounds, phase, cx| {
if phase == DispatchPhase::Bubble && event.button == button { if phase == DispatchPhase::Bubble
&& event.button == button
&& bounds.contains_point(event.position)
{
handler(view, event, cx) handler(view, event, cx)
} }
})); }));
@ -34,10 +37,13 @@ pub trait Interactive<S: 'static + Send + Sync> {
where where
Self: Sized, Self: Sized,
{ {
self.interaction_listeners() self.listeners()
.mouse_up .mouse_up
.push(Arc::new(move |view, event, phase, cx| { .push(Arc::new(move |view, event, bounds, phase, cx| {
if phase == DispatchPhase::Bubble && event.button == button { if phase == DispatchPhase::Bubble
&& event.button == button
&& bounds.contains_point(event.position)
{
handler(view, event, cx) handler(view, event, cx)
} }
})); }));
@ -52,10 +58,13 @@ pub trait Interactive<S: 'static + Send + Sync> {
where where
Self: Sized, Self: Sized,
{ {
self.interaction_listeners() self.listeners()
.mouse_down .mouse_down
.push(Arc::new(move |view, event, phase, cx| { .push(Arc::new(move |view, event, bounds, phase, cx| {
if phase == DispatchPhase::Capture && event.button == button { if phase == DispatchPhase::Capture
&& event.button == button
&& !bounds.contains_point(event.position)
{
handler(view, event, cx) handler(view, event, cx)
} }
})); }));
@ -70,10 +79,13 @@ pub trait Interactive<S: 'static + Send + Sync> {
where where
Self: Sized, Self: Sized,
{ {
self.interaction_listeners() self.listeners()
.mouse_up .mouse_up
.push(Arc::new(move |view, event, phase, cx| { .push(Arc::new(move |view, event, bounds, phase, cx| {
if event.button == button && phase == DispatchPhase::Capture { if phase == DispatchPhase::Capture
&& event.button == button
&& !bounds.contains_point(event.position)
{
handler(view, event, cx); handler(view, event, cx);
} }
})); }));
@ -83,7 +95,7 @@ pub trait Interactive<S: 'static + Send + Sync> {
fn on_click( fn on_click(
self, self,
button: MouseButton, button: MouseButton,
handler: impl Fn(&mut S, &MouseDownEvent, &MouseUpEvent, &mut ViewContext<S>) handler: impl Fn(&mut S, (&MouseDownEvent, &MouseUpEvent), &mut ViewContext<S>)
+ Send + Send
+ Sync + Sync
+ 'static, + 'static,
@ -106,43 +118,50 @@ pub trait Interactive<S: 'static + Send + Sync> {
}) })
.on_mouse_up(button, move |view, event, cx| { .on_mouse_up(button, move |view, event, cx| {
if let Some(down_event) = down_event.lock().take() { if let Some(down_event) = down_event.lock().take() {
handler(view, &down_event, event, cx); handler(view, (&down_event, event), cx);
} }
}) })
} }
} }
type MouseDownHandler<V> = Arc< type MouseDownHandler<V> = Arc<
dyn Fn(&mut V, &MouseDownEvent, DispatchPhase, &mut ViewContext<V>) + Send + Sync + 'static, dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
+ Send
+ Sync
+ 'static,
>;
type MouseUpHandler<V> = Arc<
dyn Fn(&mut V, &MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
+ Send
+ Sync
+ 'static,
>; >;
type MouseUpHandler<V> =
Arc<dyn Fn(&mut V, &MouseUpEvent, DispatchPhase, &mut ViewContext<V>) + Send + Sync + 'static>;
pub struct InteractionHandlers<V: 'static> { pub struct MouseEventListeners<V: 'static> {
mouse_down: SmallVec<[MouseDownHandler<V>; 2]>, mouse_down: SmallVec<[MouseDownHandler<V>; 2]>,
mouse_up: SmallVec<[MouseUpHandler<V>; 2]>, mouse_up: SmallVec<[MouseUpHandler<V>; 2]>,
} }
impl<S: Send + Sync + 'static> InteractionHandlers<S> { impl<S: Send + Sync + 'static> MouseEventListeners<S> {
pub fn paint(&self, bounds: Bounds<Pixels>, cx: &mut ViewContext<S>) { pub fn paint(&self, bounds: Bounds<Pixels>, cx: &mut ViewContext<S>) {
for handler in self.mouse_down.iter().cloned() { for handler in self.mouse_down.iter().cloned() {
cx.on_mouse_event(move |view, event: &MouseDownEvent, phase, cx| { cx.on_mouse_event(move |view, event: &MouseDownEvent, phase, cx| {
if bounds.contains_point(event.position) { if bounds.contains_point(event.position) {
handler(view, event, phase, cx); handler(view, event, &bounds, phase, cx);
} }
}) })
} }
for handler in self.mouse_up.iter().cloned() { for handler in self.mouse_up.iter().cloned() {
cx.on_mouse_event(move |view, event: &MouseUpEvent, phase, cx| { cx.on_mouse_event(move |view, event: &MouseUpEvent, phase, cx| {
if bounds.contains_point(event.position) { if bounds.contains_point(event.position) {
handler(view, event, phase, cx); handler(view, event, &bounds, phase, cx);
} }
}) })
} }
} }
} }
impl<V> Default for InteractionHandlers<V> { impl<V> Default for MouseEventListeners<V> {
fn default() -> Self { fn default() -> Self {
Self { Self {
mouse_down: Default::default(), mouse_down: Default::default(),

View file

@ -278,7 +278,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
self.window.rem_size self.window.rem_size
} }
pub fn stop_event_propagation(&mut self) { pub fn stop_propagation(&mut self) {
self.window.propagate_event = false; self.window.propagate_event = false;
} }
@ -625,10 +625,10 @@ impl<'a, 'w> WindowContext<'a, 'w> {
.mouse_event_handlers .mouse_event_handlers
.remove(&any_mouse_event.type_id()) .remove(&any_mouse_event.type_id())
{ {
// We sort these every time, because handlers may add handlers. Probably fast enough. // Because handlers may add other handlers, we sort every time.
handlers.sort_by(|(a, _), (b, _)| a.cmp(b)); handlers.sort_by(|(a, _), (b, _)| a.cmp(b));
// Handlers may set this to false by calling `stop_propagation`; // Handlers may set this to false by calling `stop_propagation`
self.window.propagate_event = true; self.window.propagate_event = true;
// Capture phase, events bubble from back to front. Handlers for this phase are used for // Capture phase, events bubble from back to front. Handlers for this phase are used for
@ -640,7 +640,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
} }
} }
// Bubble phase // Bubble phase, where most normal handlers do their work.
if self.window.propagate_event { if self.window.propagate_event {
for (_, handler) in handlers.iter().rev() { for (_, handler) in handlers.iter().rev() {
handler(any_mouse_event, DispatchPhase::Bubble, self); handler(any_mouse_event, DispatchPhase::Bubble, self);
@ -650,6 +650,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
} }
} }
// Just in case any handlers added new handlers, which is weird, but possible.
handlers.extend( handlers.extend(
self.window self.window
.mouse_event_handlers .mouse_event_handlers

View file

@ -1,7 +1,7 @@
use crate::theme::{theme, Theme}; use crate::theme::{theme, Theme};
use gpui3::{ use gpui3::{
div, img, svg, view, AppContext, Context, Element, IntoAnyElement, ParentElement, ScrollState, div, img, svg, view, AppContext, Context, Element, Interactive, IntoAnyElement, MouseButton,
SharedString, StyleHelpers, View, ViewContext, WindowContext, ParentElement, ScrollState, SharedString, StyleHelpers, View, ViewContext, WindowContext,
}; };
pub struct CollabPanel { pub struct CollabPanel {
@ -44,6 +44,9 @@ impl CollabPanel {
// List Container // List Container
.child( .child(
div() div()
.on_click(MouseButton::Left, |_, _, _| {
dbg!("click!");
})
.fill(theme.lowest.base.default.background) .fill(theme.lowest.base.default.background)
.pb_1() .pb_1()
.border_color(theme.lowest.base.default.border) .border_color(theme.lowest.base.default.border)