diff --git a/crates/gpui3/src/element.rs b/crates/gpui3/src/element.rs index 0a9273fb23..6abce18c56 100644 --- a/crates/gpui3/src/element.rs +++ b/crates/gpui3/src/element.rs @@ -1,4 +1,9 @@ -use crate::{BorrowWindow, Bounds, ElementId, LayoutId, Pixels, Point, ViewContext}; +use std::sync::Arc; + +use crate::{ + BorrowWindow, Bounds, Clickable, ElementId, LayoutId, MouseDownEvent, MouseUpEvent, Pixels, + Point, ViewContext, +}; use derive_more::{Deref, DerefMut}; pub(crate) use smallvec::SmallVec; @@ -24,10 +29,26 @@ pub trait Element: 'static + Send + Sync { ); } -pub trait StatefulElement: Element { +pub trait IdentifiedElement: Element { fn element_id(&self) -> ElementId { Element::element_id(self).unwrap() } + + fn on_click( + self, + listener: impl Fn( + &mut Self::ViewState, + (&MouseDownEvent, &MouseUpEvent), + &mut ViewContext, + ) + Send + + Sync + + 'static, + ) -> Clickable + where + Self: Sized, + { + Clickable::new(self, Arc::from(listener)) + } } #[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)] diff --git a/crates/gpui3/src/elements.rs b/crates/gpui3/src/elements.rs index 5ee517b6e9..01c5553dd4 100644 --- a/crates/gpui3/src/elements.rs +++ b/crates/gpui3/src/elements.rs @@ -1,3 +1,4 @@ +mod clickable; mod div; mod hoverable; mod identified; @@ -6,6 +7,7 @@ mod pressable; mod svg; mod text; +pub use clickable::*; pub use div::*; pub use hoverable::*; pub use identified::*; diff --git a/crates/gpui3/src/elements/clickable.rs b/crates/gpui3/src/elements/clickable.rs new file mode 100644 index 0000000000..e1dc961b03 --- /dev/null +++ b/crates/gpui3/src/elements/clickable.rs @@ -0,0 +1,138 @@ +use crate::{ + AnyElement, Bounds, DispatchPhase, Element, IdentifiedElement, Interactive, MouseDownEvent, + MouseEventListeners, MouseUpEvent, ParentElement, Pixels, Styled, ViewContext, +}; +use parking_lot::Mutex; +use refineable::RefinementCascade; +use smallvec::SmallVec; +use std::sync::Arc; + +pub type ClickListener = + dyn Fn(&mut S, (&MouseDownEvent, &MouseUpEvent), &mut ViewContext) + Send + Sync + 'static; + +pub struct Clickable { + child: E, + listener: Arc>, +} + +pub struct ClickableState { + last_mouse_down: Arc>>, + child_state: S, +} + +impl Clickable { + pub fn new(child: E, listener: Arc>) -> Self { + Self { child, listener } + } +} + +impl Styled for Clickable +where + E: Styled + IdentifiedElement, +{ + type Style = E::Style; + + fn style_cascade(&mut self) -> &mut RefinementCascade { + self.child.style_cascade() + } + + fn declared_style(&mut self) -> &mut ::Refinement { + self.child.declared_style() + } +} + +impl Interactive for Clickable +where + S: 'static + Send + Sync, + E: IdentifiedElement + Interactive, +{ + fn listeners(&mut self) -> &mut MouseEventListeners { + self.child.listeners() + } +} + +impl Element for Clickable +where + E: IdentifiedElement, +{ + type ViewState = E::ViewState; + type ElementState = ClickableState; + + fn element_id(&self) -> Option { + Some(IdentifiedElement::element_id(&self.child)) + } + + fn layout( + &mut self, + state: &mut Self::ViewState, + element_state: Option, + cx: &mut ViewContext, + ) -> (crate::LayoutId, Self::ElementState) { + if let Some(element_state) = element_state { + let (layout_id, child_state) = + self.child + .layout(state, Some(element_state.child_state), cx); + + let element_state = ClickableState { + last_mouse_down: element_state.last_mouse_down, + child_state, + }; + (layout_id, element_state) + } else { + let (layout_id, child_state) = self.child.layout(state, None, cx); + let element_state = ClickableState { + last_mouse_down: Default::default(), + child_state, + }; + (layout_id, element_state) + } + } + + fn paint( + &mut self, + bounds: Bounds, + state: &mut Self::ViewState, + element_state: &mut Self::ElementState, + cx: &mut ViewContext, + ) { + let last_mouse_down = element_state.last_mouse_down.clone(); + let is_some = last_mouse_down.lock().is_some(); + + if is_some { + let listener = self.listener.clone(); + cx.on_mouse_event(move |view, up_event: &MouseUpEvent, phase, cx| { + if phase == DispatchPhase::Capture && !bounds.contains_point(up_event.position) { + *last_mouse_down.lock() = None; + } else if phase == DispatchPhase::Bubble && bounds.contains_point(up_event.position) + { + if let Some(down_event) = last_mouse_down.lock().take() { + listener(view, (&down_event, up_event), cx); + } else { + log::error!("No mouse down event found for click event"); + } + } + }) + } else { + cx.on_mouse_event(move |_, event: &MouseDownEvent, phase, _| { + if phase == DispatchPhase::Bubble { + if bounds.contains_point(event.position) { + *last_mouse_down.lock() = Some(event.clone()); + } + } + }) + } + + self.child + .paint(bounds, state, &mut element_state.child_state, cx); + } +} + +impl ParentElement for Clickable { + type State = E::State; + + fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> { + self.child.children_mut() + } +} + +impl IdentifiedElement for Clickable where E: IdentifiedElement + Styled {} diff --git a/crates/gpui3/src/elements/div.rs b/crates/gpui3/src/elements/div.rs index 2cb3e47f9f..ee0bb22779 100644 --- a/crates/gpui3/src/elements/div.rs +++ b/crates/gpui3/src/elements/div.rs @@ -1,7 +1,7 @@ use crate::{ - AnyElement, Bounds, Element, ElementId, Interactive, LayoutId, MouseEventListeners, Overflow, - ParentElement, Pixels, Point, Refineable, RefinementCascade, StatefulElement, Style, Styled, - ViewContext, + AnyElement, Bounds, Element, ElementId, IdentifiedElement, Interactive, LayoutId, + MouseEventListeners, Overflow, ParentElement, Pixels, Point, Refineable, RefinementCascade, + Style, Styled, ViewContext, }; use parking_lot::Mutex; use smallvec::SmallVec; @@ -171,7 +171,7 @@ impl Styled for Div StatefulElement for Div {} +impl IdentifiedElement for Div {} impl Interactive for Div { fn listeners(&mut self) -> &mut MouseEventListeners { @@ -179,10 +179,10 @@ impl Interactive for Div { } } -impl ParentElement for Div { - type State = S; +impl ParentElement for Div { + type State = V; - fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> { + fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> { &mut self.children } } diff --git a/crates/gpui3/src/elements/hoverable.rs b/crates/gpui3/src/elements/hoverable.rs index 60fbb53795..7d57ff1f40 100644 --- a/crates/gpui3/src/elements/hoverable.rs +++ b/crates/gpui3/src/elements/hoverable.rs @@ -1,6 +1,6 @@ use crate::{ - AnyElement, Bounds, DispatchPhase, Element, ElementId, Interactive, MouseEventListeners, - MouseMoveEvent, ParentElement, Pixels, StatefulElement, Styled, ViewContext, + AnyElement, Bounds, DispatchPhase, Element, ElementId, IdentifiedElement, Interactive, + MouseEventListeners, MouseMoveEvent, ParentElement, Pixels, Styled, ViewContext, }; use refineable::{CascadeSlot, Refineable, RefinementCascade}; use smallvec::SmallVec; @@ -104,9 +104,9 @@ impl ParentElement for Hoverable { } } -impl StatefulElement for Hoverable +impl IdentifiedElement for Hoverable where - E: StatefulElement + Styled, + E: IdentifiedElement + Styled, ::Style: 'static + Refineable + Send + Sync + Default, <::Style as Refineable>::Refinement: 'static + Refineable + Send + Sync + Default, { diff --git a/crates/gpui3/src/elements/identified.rs b/crates/gpui3/src/elements/identified.rs index 66249137a2..fdb1ebde8c 100644 --- a/crates/gpui3/src/elements/identified.rs +++ b/crates/gpui3/src/elements/identified.rs @@ -2,8 +2,8 @@ use refineable::{Refineable, RefinementCascade}; use smallvec::SmallVec; use crate::{ - AnyElement, BorrowWindow, Bounds, Element, ElementId, LayoutId, ParentElement, StatefulElement, - Styled, ViewContext, + AnyElement, BorrowWindow, Bounds, Element, ElementId, IdentifiedElement, LayoutId, + ParentElement, Styled, ViewContext, }; pub struct Identified { @@ -41,7 +41,7 @@ impl Element for Identified { } } -impl StatefulElement for Identified {} +impl IdentifiedElement for Identified {} impl Styled for Identified { type Style = E::Style; diff --git a/crates/gpui3/src/elements/pressable.rs b/crates/gpui3/src/elements/pressable.rs index ab586a125c..2f88a523ce 100644 --- a/crates/gpui3/src/elements/pressable.rs +++ b/crates/gpui3/src/elements/pressable.rs @@ -1,6 +1,6 @@ use crate::{ - AnyElement, Bounds, DispatchPhase, Element, Interactive, MouseDownEvent, MouseEventListeners, - MouseUpEvent, ParentElement, Pixels, StatefulElement, Styled, ViewContext, + AnyElement, Bounds, DispatchPhase, Element, IdentifiedElement, Interactive, MouseDownEvent, + MouseEventListeners, MouseUpEvent, ParentElement, Pixels, Styled, ViewContext, }; use refineable::{CascadeSlot, Refineable, RefinementCascade}; use smallvec::SmallVec; @@ -53,7 +53,7 @@ impl + Styled> Interactive for Pr impl Element for Pressable where - E: Styled + StatefulElement, + E: Styled + IdentifiedElement, ::Style: 'static + Refineable + Send + Sync + Default, <::Style as Refineable>::Refinement: 'static + Refineable + Send + Sync + Default, { @@ -61,7 +61,7 @@ where type ElementState = PressableState; fn element_id(&self) -> Option { - Some(StatefulElement::element_id(&self.child)) + Some(IdentifiedElement::element_id(&self.child)) } fn layout( @@ -127,7 +127,10 @@ where } } -impl ParentElement for Pressable { +impl ParentElement for Pressable +where + E: ParentElement + IdentifiedElement + Styled, +{ type State = E::State; fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> { @@ -135,9 +138,9 @@ impl ParentElement for Pressable { } } -impl StatefulElement for Pressable +impl IdentifiedElement for Pressable where - E: StatefulElement + Styled, + E: IdentifiedElement + Styled, ::Style: 'static + Refineable + Send + Sync + Default, <::Style as Refineable>::Refinement: 'static + Refineable + Send + Sync + Default, { diff --git a/crates/gpui3/src/interactive.rs b/crates/gpui3/src/interactive.rs index c7413a8027..a7cc2dda3c 100644 --- a/crates/gpui3/src/interactive.rs +++ b/crates/gpui3/src/interactive.rs @@ -2,7 +2,6 @@ use crate::{ Bounds, DispatchPhase, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, ScrollWheelEvent, ViewContext, }; -use parking_lot::Mutex; use smallvec::SmallVec; use std::sync::Arc; @@ -93,37 +92,6 @@ pub trait Interactive { self } - fn on_click( - self, - button: MouseButton, - handler: impl Fn(&mut S, (&MouseDownEvent, &MouseUpEvent), &mut ViewContext) - + Send - + Sync - + 'static, - ) -> Self - where - Self: Sized, - { - let down_event = Arc::new(Mutex::new(None)); - self.on_mouse_down(button, { - let down_event = down_event.clone(); - move |_, event, _| { - down_event.lock().replace(event.clone()); - } - }) - .on_mouse_up_out(button, { - let down_event = down_event.clone(); - move |_, _, _| { - down_event.lock().take(); - } - }) - .on_mouse_up(button, move |view, event, cx| { - if let Some(down_event) = down_event.lock().take() { - handler(view, (&down_event, event), cx); - } - }) - } - fn on_mouse_move( mut self, handler: impl Fn(&mut S, &MouseMoveEvent, &mut ViewContext) + Send + Sync + 'static, diff --git a/crates/storybook2/src/collab_panel.rs b/crates/storybook2/src/collab_panel.rs index ac65ada5d2..7a51310a33 100644 --- a/crates/storybook2/src/collab_panel.rs +++ b/crates/storybook2/src/collab_panel.rs @@ -1,8 +1,8 @@ use crate::theme::{theme, Theme}; use gpui3::{ - div, img, svg, view, AppContext, Context, Element, ElementId, IntoAnyElement, MouseButton, - ParentElement, ScrollState, SharedString, StyleHelpers, Styled, View, ViewContext, - WindowContext, + div, img, svg, view, AppContext, Context, Element, ElementId, IdentifiedElement, + IntoAnyElement, ParentElement, ScrollState, SharedString, StyleHelpers, Styled, View, + ViewContext, WindowContext, }; pub struct CollabPanel { @@ -45,7 +45,8 @@ impl CollabPanel { // List Container .child( div() - .on_click(MouseButton::Left, |_, _, _| { + .id(0) + .on_click(|_, _, _| { dbg!("click!"); }) .fill(theme.lowest.base.default.background) diff --git a/crates/storybook2/src/theme.rs b/crates/storybook2/src/theme.rs index 4e8b7c1adc..8e8ee7a120 100644 --- a/crates/storybook2/src/theme.rs +++ b/crates/storybook2/src/theme.rs @@ -147,6 +147,10 @@ impl Element for Themed { type ViewState = E::ViewState; type ElementState = E::ElementState; + fn element_id(&self) -> Option { + None + } + fn layout( &mut self, state: &mut E::ViewState, diff --git a/crates/storybook2/src/workspace.rs b/crates/storybook2/src/workspace.rs index e91e267f06..fd48cea009 100644 --- a/crates/storybook2/src/workspace.rs +++ b/crates/storybook2/src/workspace.rs @@ -47,8 +47,7 @@ impl Workspace { .flex_row() .overflow_hidden() .child(self.left_panel.clone()) - .child(div().h_full().flex_1()) - .child(self.right_panel.clone()), + .child(div().h_full().flex_1()), // .child(self.right_panel.clone()), ) .child(statusbar::statusbar(cx)) })