diff --git a/crates/gpui3/src/elements/nested.rs b/crates/gpui3/src/elements/nested.rs index 94b67886c1ec1c9de5110c4f69d3811bf3f142fe..c1ef373842a1d0f30755a26e211923dbb070c4da 100644 --- a/crates/gpui3/src/elements/nested.rs +++ b/crates/gpui3/src/elements/nested.rs @@ -1,7 +1,9 @@ use crate::{ - group_bounds, AnyElement, DispatchPhase, Element, IntoAnyElement, MouseMoveEvent, SharedString, - Style, StyleCascade, StyleRefinement, + group_bounds, AnyElement, DispatchPhase, Element, IdentifiedElement, IntoAnyElement, + MouseDownEvent, MouseMoveEvent, MouseUpEvent, SharedString, Style, StyleCascade, + StyleRefinement, ViewContext, }; +use parking_lot::Mutex; use refineable::{CascadeSlot, Refineable}; use smallvec::SmallVec; use std::sync::{ @@ -222,6 +224,153 @@ where } } +pub trait Clickable: IdentifiedElement + Sized { + fn active_style(&mut self) -> &mut StyleRefinement; + fn listeners(&mut self) -> &mut ClickListeners; + + fn on_click( + &mut self, + f: impl Fn(&mut Self::ViewState, &MouseClickEvent, &mut ViewContext) + + 'static + + Send + + Sync, + ) where + Self: Sized, + { + self.listeners().push(Arc::new(f)); + } + + fn active(mut self, f: impl FnOnce(&mut StyleRefinement) -> &mut StyleRefinement) -> Self + where + Self: Sized, + { + f(self.active_style()); + self + } +} + +type ClickListeners = + SmallVec<[Arc) + Send + Sync>; 1]>; + +pub struct ClickableElementState { + mouse_down: Arc>>, + child_state: E::ElementState, +} + +pub struct MouseClickEvent { + down: MouseDownEvent, + up: MouseUpEvent, +} + +pub struct ClickableElement { + child: E, + listeners: ClickListeners, + active_style: StyleRefinement, + cascade_slot: CascadeSlot, +} + +impl IntoAnyElement for ClickableElement +where + E: IdentifiedElement + Styled, +{ + fn into_any(self) -> AnyElement { + AnyElement::new(self) + } +} + +impl Element for ClickableElement +where + E: IdentifiedElement + Styled, +{ + type ViewState = E::ViewState; + type ElementState = ClickableElementState; + + fn element_id(&self) -> Option { + Some(IdentifiedElement::element_id(&self.child)) + } + + fn layout( + &mut self, + state: &mut Self::ViewState, + element_state: Option, + cx: &mut crate::ViewContext, + ) -> (crate::LayoutId, Self::ElementState) { + if let Some(element_state) = element_state { + if element_state.mouse_down.lock().is_some() { + self.child + .style_cascade() + .set(self.cascade_slot, Some(self.active_style.clone())); + } + + let (layout_id, child_state) = + self.child + .layout(state, Some(element_state.child_state), cx); + ( + layout_id, + ClickableElementState { + mouse_down: element_state.mouse_down, + child_state, + }, + ) + } else { + let (layout_id, child_state) = self.child.layout(state, None, cx); + ( + layout_id, + ClickableElementState { + mouse_down: Default::default(), + child_state, + }, + ) + } + } + + fn paint( + &mut self, + bounds: crate::Bounds, + state: &mut Self::ViewState, + element_state: &mut Self::ElementState, + cx: &mut crate::ViewContext, + ) { + if !self.listeners.is_empty() || self.active_style.is_some() { + if let Some(mouse_down) = element_state.mouse_down.lock().clone() { + self.child + .style_cascade() + .set(self.cascade_slot, Some(self.active_style.clone())); + let listeners = self.listeners.clone(); + let mouse_down_mutex = element_state.mouse_down.clone(); + cx.on_mouse_event(move |view, up: &MouseUpEvent, phase, cx| { + if phase == DispatchPhase::Bubble && bounds.contains_point(up.position) { + for listener in &*listeners { + listener( + view, + &MouseClickEvent { + down: mouse_down.clone(), + up: up.clone(), + }, + cx, + ); + } + } + + mouse_down_mutex.lock().take(); + cx.notify(); + }); + } else { + let mouse_down_mutex = element_state.mouse_down.clone(); + cx.on_mouse_event(move |_view, down: &MouseDownEvent, phase, cx| { + if phase == DispatchPhase::Bubble && bounds.contains_point(down.position) { + *mouse_down_mutex.lock() = Some(down.clone()); + cx.notify(); + } + }); + } + } + + self.child + .paint(bounds, state, &mut element_state.child_state, cx); + } +} + struct Div(HoverableElement>); impl LayoutNode for Div {