diff --git a/crates/gpui3/src/element.rs b/crates/gpui3/src/element.rs index 8585bfdc9f..32f7a1c95c 100644 --- a/crates/gpui3/src/element.rs +++ b/crates/gpui3/src/element.rs @@ -1,9 +1,4 @@ -use std::sync::Arc; - -use crate::{ - BorrowWindow, Bounds, Clickable, ElementId, Group, LayoutId, MouseDownEvent, MouseUpEvent, - Pixels, Point, SharedString, ViewContext, -}; +use crate::{BorrowWindow, Bounds, ElementId, LayoutId, Pixels, Point, ViewContext}; use derive_more::{Deref, DerefMut}; pub(crate) use smallvec::SmallVec; @@ -27,35 +22,12 @@ pub trait Element: 'static + Send + Sync + IntoAnyElement { element_state: &mut Self::ElementState, cx: &mut ViewContext, ); - - fn group(self, name: impl Into) -> Group - where - Self: Sized, - { - Group::new(name.into(), self) - } } pub trait IdentifiedElement: Element { - fn element_id(&self) -> ElementId { + fn id(&self) -> ElementId { 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 e0ad68abfc..9278fac33d 100644 --- a/crates/gpui3/src/elements.rs +++ b/crates/gpui3/src/elements.rs @@ -1,20 +1,15 @@ mod clickable; mod div; -mod group; mod hoverable; -mod identified; mod img; -mod nested; -mod pressable; +mod layout_node; mod svg; mod text; pub use clickable::*; pub use div::*; -pub use group::*; pub use hoverable::*; -pub use identified::*; pub use img::*; -pub use pressable::*; +pub use layout_node::*; pub use svg::*; pub use text::*; diff --git a/crates/gpui3/src/elements/clickable.rs b/crates/gpui3/src/elements/clickable.rs index 0b99adcf1e..0d98d2cafc 100644 --- a/crates/gpui3/src/elements/clickable.rs +++ b/crates/gpui3/src/elements/clickable.rs @@ -1,71 +1,90 @@ use crate::{ - AnyElement, Bounds, DispatchPhase, Element, IdentifiedElement, Interactive, IntoAnyElement, - MouseDownEvent, MouseEventListeners, MouseUpEvent, ParentElement, Pixels, Styled, ViewContext, + AnyElement, Bounds, DispatchPhase, Element, ElementId, ElementKind, Hoverable, + IdentifiedElement, IntoAnyElement, LayoutId, LayoutNode, MouseDownEvent, MouseUpEvent, Pixels, + SharedString, StyleRefinement, Styled, ViewContext, }; use parking_lot::Mutex; -use refineable::Cascade; +use refineable::CascadeSlot; use smallvec::SmallVec; use std::sync::Arc; -pub type ClickListener = - dyn Fn(&mut S, (&MouseDownEvent, &MouseUpEvent), &mut ViewContext) + Send + Sync + 'static; +pub trait Clickable: Element + Sized { + fn active_style(&mut self) -> &mut StyleRefinement; + fn listeners(&mut self) -> &mut ClickListeners; -pub struct Clickable { + 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 + } +} + +pub type ClickListeners = + SmallVec<[Arc) + Send + Sync>; 1]>; + +pub struct ClickableElementState { + mouse_down: Arc>>, + child_state: E, +} + +pub struct MouseClickEvent { + pub down: MouseDownEvent, + pub up: MouseUpEvent, +} + +pub struct ClickableElement { child: E, - listener: Arc>, + listeners: ClickListeners, + active_style: StyleRefinement, + cascade_slot: CascadeSlot, } -pub struct ClickableState { - last_mouse_down: Arc>>, - child_state: S, -} - -impl Clickable { - pub fn new(child: E, listener: Arc>) -> Self { - Self { child, listener } +impl ClickableElement { + pub fn replace_child>( + self, + replace: impl FnOnce(E) -> E2, + ) -> ClickableElement { + ClickableElement { + child: replace(self.child), + listeners: self.listeners, + active_style: self.active_style, + cascade_slot: self.cascade_slot, + } } } -impl Styled for Clickable +impl IntoAnyElement for ClickableElement where - E: Styled + IdentifiedElement, + E: Styled + Element, { - type Style = E::Style; - - fn style_cascade(&mut self) -> &mut Cascade { - 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 IntoAnyElement for Clickable { fn into_any(self) -> AnyElement { AnyElement::new(self) } } -impl Element for Clickable +impl Element for ClickableElement where - E: IdentifiedElement, + E: Styled + Element, { type ViewState = E::ViewState; - type ElementState = ClickableState; + type ElementState = ClickableElementState; - fn id(&self) -> Option { - Some(IdentifiedElement::element_id(&self.child)) + fn id(&self) -> Option { + self.child.id() } fn layout( @@ -73,24 +92,33 @@ where state: &mut Self::ViewState, element_state: Option, cx: &mut ViewContext, - ) -> (crate::LayoutId, Self::ElementState) { + ) -> (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); - - let element_state = ClickableState { - last_mouse_down: element_state.last_mouse_down, - child_state, - }; - (layout_id, element_state) + ( + layout_id, + ClickableElementState { + mouse_down: element_state.mouse_down, + child_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) + ( + layout_id, + ClickableElementState { + mouse_down: Default::default(), + child_state, + }, + ) } } @@ -101,31 +129,39 @@ where 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 !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, + ); + } + } - 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"); + 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(); } - } - }) - } 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 @@ -133,12 +169,53 @@ where } } -impl ParentElement for Clickable { - type State = E::State; +impl IdentifiedElement for ClickableElement {} - fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> { +impl LayoutNode for ClickableElement +where + E: Element + LayoutNode, + K: ElementKind, +{ + fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> { self.child.children_mut() } + + fn group_mut(&mut self) -> &mut Option { + self.child.group_mut() + } } -impl IdentifiedElement for Clickable where E: IdentifiedElement + Styled {} +impl Styled for ClickableElement +where + E: Styled + Element, +{ + fn style_cascade(&mut self) -> &mut crate::StyleCascade { + self.child.style_cascade() + } + + fn computed_style(&mut self) -> &crate::Style { + self.child.computed_style() + } +} + +impl Hoverable for ClickableElement +where + E: Element + Hoverable, +{ + fn hover_style(&mut self) -> &mut StyleRefinement { + self.child.hover_style() + } +} + +impl Clickable for ClickableElement +where + E: Styled + IdentifiedElement, +{ + fn active_style(&mut self) -> &mut StyleRefinement { + &mut self.active_style + } + + fn listeners(&mut self) -> &mut ClickListeners { + &mut self.listeners + } +} diff --git a/crates/gpui3/src/elements/div.rs b/crates/gpui3/src/elements/div.rs index 03229dc1da..bdd450f7b1 100644 --- a/crates/gpui3/src/elements/div.rs +++ b/crates/gpui3/src/elements/div.rs @@ -1,228 +1,12 @@ use crate::{ - AnyElement, BorrowWindow, Bounds, Cascade, Element, ElementId, IdentifiedElement, Interactive, - IntoAnyElement, LayoutId, MouseEventListeners, Overflow, ParentElement, Pixels, Point, - Refineable, Style, Styled, ViewContext, + AnonymousElementKind, AnyElement, Bounds, Clickable, ClickableElement, ClickableElementState, + Element, ElementId, ElementKind, Hoverable, HoverableElement, IdentifiedElementKind, + IntoAnyElement, LayoutId, LayoutNode, LayoutNodeElement, Overflow, Pixels, Point, SharedString, + Style, StyleCascade, StyleRefinement, Styled, ViewContext, ClickListeners, }; use parking_lot::Mutex; use smallvec::SmallVec; -use std::{marker::PhantomData, sync::Arc}; - -pub enum HasId {} - -pub struct Div { - styles: Cascade