Detailed changes
@@ -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<Self::ViewState>,
+ ) + Send
+ + Sync
+ + 'static,
+ ) -> Clickable<Self>
+ where
+ Self: Sized,
+ {
+ Clickable::new(self, Arc::from(listener))
+ }
}
#[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)]
@@ -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::*;
@@ -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<S> =
+ dyn Fn(&mut S, (&MouseDownEvent, &MouseUpEvent), &mut ViewContext<S>) + Send + Sync + 'static;
+
+pub struct Clickable<E: Element> {
+ child: E,
+ listener: Arc<ClickListener<E::ViewState>>,
+}
+
+pub struct ClickableState<S> {
+ last_mouse_down: Arc<Mutex<Option<MouseDownEvent>>>,
+ child_state: S,
+}
+
+impl<E: Element> Clickable<E> {
+ pub fn new(child: E, listener: Arc<ClickListener<E::ViewState>>) -> Self {
+ Self { child, listener }
+ }
+}
+
+impl<E> Styled for Clickable<E>
+where
+ E: Styled + IdentifiedElement,
+{
+ type Style = E::Style;
+
+ fn style_cascade(&mut self) -> &mut RefinementCascade<E::Style> {
+ self.child.style_cascade()
+ }
+
+ fn declared_style(&mut self) -> &mut <Self::Style as refineable::Refineable>::Refinement {
+ self.child.declared_style()
+ }
+}
+
+impl<S, E> Interactive<S> for Clickable<E>
+where
+ S: 'static + Send + Sync,
+ E: IdentifiedElement + Interactive<S>,
+{
+ fn listeners(&mut self) -> &mut MouseEventListeners<S> {
+ self.child.listeners()
+ }
+}
+
+impl<E> Element for Clickable<E>
+where
+ E: IdentifiedElement,
+{
+ type ViewState = E::ViewState;
+ type ElementState = ClickableState<E::ElementState>;
+
+ fn element_id(&self) -> Option<crate::ElementId> {
+ Some(IdentifiedElement::element_id(&self.child))
+ }
+
+ fn layout(
+ &mut self,
+ state: &mut Self::ViewState,
+ element_state: Option<Self::ElementState>,
+ cx: &mut ViewContext<Self::ViewState>,
+ ) -> (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<Pixels>,
+ state: &mut Self::ViewState,
+ element_state: &mut Self::ElementState,
+ cx: &mut ViewContext<Self::ViewState>,
+ ) {
+ 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<E: IdentifiedElement + ParentElement> ParentElement for Clickable<E> {
+ type State = E::State;
+
+ fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::State>; 2]> {
+ self.child.children_mut()
+ }
+}
+
+impl<E> IdentifiedElement for Clickable<E> where E: IdentifiedElement + Styled {}
@@ -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<V: 'static + Send + Sync, Marker: 'static + Send + Sync> Styled for Div<V,
}
}
-impl<V: Send + Sync + 'static> StatefulElement for Div<V, HasId> {}
+impl<V: Send + Sync + 'static> IdentifiedElement for Div<V, HasId> {}
impl<V: Send + Sync + 'static> Interactive<V> for Div<V, HasId> {
fn listeners(&mut self) -> &mut MouseEventListeners<V> {
@@ -179,10 +179,10 @@ impl<V: Send + Sync + 'static> Interactive<V> for Div<V, HasId> {
}
}
-impl<S: 'static> ParentElement for Div<S> {
- type State = S;
+impl<V: 'static, Marker: 'static + Send + Sync> ParentElement for Div<V, Marker> {
+ type State = V;
- fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<S>; 2]> {
+ fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
&mut self.children
}
}
@@ -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<E: ParentElement + Styled> ParentElement for Hoverable<E> {
}
}
-impl<E> StatefulElement for Hoverable<E>
+impl<E> IdentifiedElement for Hoverable<E>
where
- E: StatefulElement + Styled,
+ E: IdentifiedElement + Styled,
<E as Styled>::Style: 'static + Refineable + Send + Sync + Default,
<<E as Styled>::Style as Refineable>::Refinement: 'static + Refineable + Send + Sync + Default,
{
@@ -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<E> {
@@ -41,7 +41,7 @@ impl<E: Element> Element for Identified<E> {
}
}
-impl<E: Element> StatefulElement for Identified<E> {}
+impl<E: Element> IdentifiedElement for Identified<E> {}
impl<E: Styled> Styled for Identified<E> {
type Style = E::Style;
@@ -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<S: 'static + Send + Sync, E: Interactive<S> + Styled> Interactive<S> for Pr
impl<E> Element for Pressable<E>
where
- E: Styled + StatefulElement,
+ E: Styled + IdentifiedElement,
<E as Styled>::Style: 'static + Refineable + Send + Sync + Default,
<<E as Styled>::Style as Refineable>::Refinement: 'static + Refineable + Send + Sync + Default,
{
@@ -61,7 +61,7 @@ where
type ElementState = PressableState<E::ElementState>;
fn element_id(&self) -> Option<crate::ElementId> {
- Some(StatefulElement::element_id(&self.child))
+ Some(IdentifiedElement::element_id(&self.child))
}
fn layout(
@@ -127,7 +127,10 @@ where
}
}
-impl<E: ParentElement + Styled> ParentElement for Pressable<E> {
+impl<E> ParentElement for Pressable<E>
+where
+ E: ParentElement + IdentifiedElement + Styled,
+{
type State = E::State;
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<Self::State>; 2]> {
@@ -135,9 +138,9 @@ impl<E: ParentElement + Styled> ParentElement for Pressable<E> {
}
}
-impl<E> StatefulElement for Pressable<E>
+impl<E> IdentifiedElement for Pressable<E>
where
- E: StatefulElement + Styled,
+ E: IdentifiedElement + Styled,
<E as Styled>::Style: 'static + Refineable + Send + Sync + Default,
<<E as Styled>::Style as Refineable>::Refinement: 'static + Refineable + Send + Sync + Default,
{
@@ -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<S: 'static + Send + Sync> {
self
}
- fn on_click(
- self,
- button: MouseButton,
- handler: impl Fn(&mut S, (&MouseDownEvent, &MouseUpEvent), &mut ViewContext<S>)
- + 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<S>) + Send + Sync + 'static,
@@ -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)
@@ -147,6 +147,10 @@ impl<E: Element> Element for Themed<E> {
type ViewState = E::ViewState;
type ElementState = E::ElementState;
+ fn element_id(&self) -> Option<gpui3::ElementId> {
+ None
+ }
+
fn layout(
&mut self,
state: &mut E::ViewState,
@@ -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))
})