diff --git a/crates/gpui3/src/element.rs b/crates/gpui3/src/element.rs index 8dec1bacba..60a0171664 100644 --- a/crates/gpui3/src/element.rs +++ b/crates/gpui3/src/element.rs @@ -33,6 +33,26 @@ pub trait IdentifiedElement: Element { #[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)] pub(crate) struct GlobalElementId(SmallVec<[ElementId; 8]>); +pub trait ElementKind: 'static + Send + Sync { + fn id(&self) -> Option; +} + +pub struct IdentifiedElementKind(pub(crate) ElementId); +pub struct AnonymousElementKind; + +impl ElementKind for IdentifiedElementKind { + fn id(&self) -> Option { + Some(self.0.clone()) + } +} + +impl ElementKind for AnonymousElementKind { + fn id(&self) -> Option { + None + } +} + + pub trait ParentElement: Element { fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]>; fn group_mut(&mut self) -> &mut Option; diff --git a/crates/gpui3/src/elements.rs b/crates/gpui3/src/elements.rs index 3b6b226313..83c27b8a3b 100644 --- a/crates/gpui3/src/elements.rs +++ b/crates/gpui3/src/elements.rs @@ -1,16 +1,9 @@ -mod clickable; mod div; -mod hoverable; mod img; -mod layout_node; mod svg; mod text; -mod div2; -pub use clickable::*; pub use div::*; -pub use hoverable::*; pub use img::*; -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 deleted file mode 100644 index a3ca671d4f..0000000000 --- a/crates/gpui3/src/elements/clickable.rs +++ /dev/null @@ -1,230 +0,0 @@ -use crate::{ - AnyElement, Bounds, DispatchPhase, Element, ElementId, Hoverable, IdentifiedElement, - IntoAnyElement, LayoutId, MouseDownEvent, MouseUpEvent, ParentElement, Pixels, SharedString, - StyleRefinement, Styled, ViewContext, -}; -use parking_lot::Mutex; -use refineable::CascadeSlot; -use smallvec::SmallVec; -use std::sync::Arc; - -pub trait Clickable: Element + 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 - } -} - -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, - listeners: ClickListeners, - active_style: StyleRefinement, - cascade_slot: CascadeSlot, -} - -impl ClickableElement { - pub fn new(mut child: E) -> Self { - let cascade_slot = child.style_cascade().reserve(); - ClickableElement { - child, - listeners: Default::default(), - active_style: Default::default(), - cascade_slot, - } - } - - 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 IntoAnyElement for ClickableElement -where - E: Styled + Element, -{ - fn into_any(self) -> AnyElement { - AnyElement::new(self) - } -} - -impl Element for ClickableElement -where - E: Styled + Element, -{ - type ViewState = E::ViewState; - type ElementState = ClickableElementState; - - fn id(&self) -> Option { - self.child.id() - } - - fn layout( - &mut self, - state: &mut Self::ViewState, - element_state: Option, - cx: &mut ViewContext, - ) -> (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: Bounds, - state: &mut Self::ViewState, - element_state: &mut Self::ElementState, - cx: &mut 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); - } -} - -impl IdentifiedElement for ClickableElement {} - -impl ParentElement for ClickableElement -where - E: Styled + ParentElement, -{ - 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 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 b95ee1be42..43a917975f 100644 --- a/crates/gpui3/src/elements/div.rs +++ b/crates/gpui3/src/elements/div.rs @@ -1,14 +1,43 @@ use crate::{ - AnonymousElementKind, AnyElement, Bounds, ClickListeners, Clickable, ClickableElement, - ClickableElementState, Element, ElementId, ElementKind, Hoverable, HoverableElement, - IdentifiedElement, IdentifiedElementKind, IntoAnyElement, LayoutId, LayoutNodeElement, - Overflow, ParentElement, Pixels, Point, SharedString, Style, StyleCascade, StyleRefinement, - Styled, ViewContext, + AnonymousElementKind, AnyElement, AppContext, BorrowWindow, Bounds, DispatchPhase, Element, + ElementId, ElementKind, IdentifiedElement, IdentifiedElementKind, IntoAnyElement, LayoutId, + MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Overflow, Pixels, Point, + ScrollWheelEvent, SharedString, Style, StyleRefinement, Styled, ViewContext, }; +use collections::HashMap; use parking_lot::Mutex; +use refineable::Refineable; use smallvec::SmallVec; use std::sync::Arc; +#[derive(Default)] +pub struct DivState { + active_state: Arc>, + pending_click: Arc>>, +} + +#[derive(Copy, Clone, Default, Eq, PartialEq)] +struct ActiveState { + group: bool, + element: bool, +} + +impl ActiveState { + pub fn is_none(&self) -> bool { + !self.group && !self.element + } +} + +#[derive(Default)] +struct GroupBounds(HashMap; 1]>>); + +pub fn group_bounds(name: &SharedString, cx: &mut AppContext) -> Option> { + cx.default_global::() + .0 + .get(name) + .and_then(|bounds_stack| bounds_stack.last().cloned()) +} + #[derive(Default, Clone)] pub struct ScrollState(Arc>>); @@ -34,116 +63,459 @@ pub fn div() -> Div where S: 'static + Send + Sync, { - Div(ClickableElement::new(HoverableElement::new( - LayoutNodeElement::new(), - ))) + Div { + kind: AnonymousElementKind, + children: SmallVec::new(), + group: None, + base_style: StyleRefinement::default(), + hover_style: StyleRefinement::default(), + group_hover: None, + active_style: StyleRefinement::default(), + group_active: None, + listeners: MouseEventListeners::default(), + } } -pub struct Div( - ClickableElement>>, -); +pub struct Div { + kind: K, + children: SmallVec<[AnyElement; 2]>, + group: Option, + base_style: StyleRefinement, + hover_style: StyleRefinement, + group_hover: Option, + active_style: StyleRefinement, + group_active: Option, + listeners: MouseEventListeners, +} -impl Div { +struct GroupStyle { + group: SharedString, + style: StyleRefinement, +} + +impl Div +where + V: 'static + Send + Sync, +{ + pub fn id(self, id: impl Into) -> Div { + Div { + kind: IdentifiedElementKind(id.into()), + children: self.children, + group: self.group, + base_style: self.base_style, + hover_style: self.hover_style, + group_hover: self.group_hover, + active_style: self.active_style, + group_active: self.group_active, + listeners: self.listeners, + } + } +} + +impl Div +where + V: 'static + Send + Sync, +{ + pub fn on_mouse_down( + mut self, + button: MouseButton, + handler: impl Fn(&mut V, &MouseDownEvent, &mut ViewContext) + Send + Sync + 'static, + ) -> Self { + self.listeners + .mouse_down + .push(Arc::new(move |view, event, bounds, phase, cx| { + if phase == DispatchPhase::Bubble + && event.button == button + && bounds.contains_point(&event.position) + { + handler(view, event, cx) + } + })); + self + } + + pub fn on_mouse_up( + mut self, + button: MouseButton, + handler: impl Fn(&mut V, &MouseUpEvent, &mut ViewContext) + Send + Sync + 'static, + ) -> Self { + self.listeners + .mouse_up + .push(Arc::new(move |view, event, bounds, phase, cx| { + if phase == DispatchPhase::Bubble + && event.button == button + && bounds.contains_point(&event.position) + { + handler(view, event, cx) + } + })); + self + } + + pub fn on_mouse_down_out( + mut self, + button: MouseButton, + handler: impl Fn(&mut V, &MouseDownEvent, &mut ViewContext) + Send + Sync + 'static, + ) -> Self { + self.listeners + .mouse_down + .push(Arc::new(move |view, event, bounds, phase, cx| { + if phase == DispatchPhase::Capture + && event.button == button + && !bounds.contains_point(&event.position) + { + handler(view, event, cx) + } + })); + self + } + + pub fn on_mouse_up_out( + mut self, + button: MouseButton, + handler: impl Fn(&mut V, &MouseUpEvent, &mut ViewContext) + Send + Sync + 'static, + ) -> Self { + self.listeners + .mouse_up + .push(Arc::new(move |view, event, bounds, phase, cx| { + if phase == DispatchPhase::Capture + && event.button == button + && !bounds.contains_point(&event.position) + { + handler(view, event, cx); + } + })); + self + } + + pub fn on_mouse_move( + mut self, + handler: impl Fn(&mut V, &MouseMoveEvent, &mut ViewContext) + Send + Sync + 'static, + ) -> Self { + self.listeners + .mouse_move + .push(Arc::new(move |view, event, bounds, phase, cx| { + if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) { + handler(view, event, cx); + } + })); + self + } + + pub fn on_scroll_wheel( + mut self, + handler: impl Fn(&mut V, &ScrollWheelEvent, &mut ViewContext) + Send + Sync + 'static, + ) -> Self { + self.listeners + .scroll_wheel + .push(Arc::new(move |view, event, bounds, phase, cx| { + if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) { + handler(view, event, cx); + } + })); + self + } +} + +impl Div +where + V: 'static + Send + Sync, + K: ElementKind, +{ pub fn group(mut self, group: impl Into) -> Self { - *self.0.group_mut() = Some(group.into()); + self.group = Some(group.into()); self } pub fn z_index(mut self, z_index: u32) -> Self { - self.base_style().z_index = Some(z_index); + self.base_style.z_index = Some(z_index); self } pub fn overflow_hidden(mut self) -> Self { - self.base_style().overflow.x = Some(Overflow::Hidden); - self.base_style().overflow.y = Some(Overflow::Hidden); + self.base_style.overflow.x = Some(Overflow::Hidden); + self.base_style.overflow.y = Some(Overflow::Hidden); self } pub fn overflow_hidden_x(mut self) -> Self { - self.base_style().overflow.x = Some(Overflow::Hidden); + self.base_style.overflow.x = Some(Overflow::Hidden); self } pub fn overflow_hidden_y(mut self) -> Self { - self.base_style().overflow.y = Some(Overflow::Hidden); + self.base_style.overflow.y = Some(Overflow::Hidden); self } pub fn overflow_scroll(mut self, _scroll_state: ScrollState) -> Self { // todo!("impl scrolling") // self.scroll_state = Some(scroll_state); - self.base_style().overflow.x = Some(Overflow::Scroll); - self.base_style().overflow.y = Some(Overflow::Scroll); + self.base_style.overflow.x = Some(Overflow::Scroll); + self.base_style.overflow.y = Some(Overflow::Scroll); self } pub fn overflow_x_scroll(mut self, _scroll_state: ScrollState) -> Self { // todo!("impl scrolling") // self.scroll_state = Some(scroll_state); - self.base_style().overflow.x = Some(Overflow::Scroll); + self.base_style.overflow.x = Some(Overflow::Scroll); self } pub fn overflow_y_scroll(mut self, _scroll_state: ScrollState) -> Self { // todo!("impl scrolling") // self.scroll_state = Some(scroll_state); - self.base_style().overflow.y = Some(Overflow::Scroll); + self.base_style.overflow.y = Some(Overflow::Scroll); self } - fn base_style(&mut self) -> &mut StyleRefinement { - self.style_cascade().base() + fn with_element_id( + &mut self, + cx: &mut ViewContext, + f: impl FnOnce(&mut Self, &mut ViewContext) -> R, + ) -> R { + if let Some(id) = self.id() { + cx.with_element_id(id, |cx| f(self, cx)) + } else { + f(self, cx) + } + } + + pub fn compute_style( + &self, + bounds: Bounds, + state: &DivState, + cx: &mut ViewContext, + ) -> Style { + let mut computed_style = Style::default(); + computed_style.refine(&self.base_style); + + let mouse_position = cx.mouse_position(); + + if let Some(group_hover) = self.group_hover.as_ref() { + if let Some(group_bounds) = group_bounds(&group_hover.group, cx) { + if group_bounds.contains_point(&mouse_position) { + computed_style.refine(&group_hover.style); + } + } + } + if bounds.contains_point(&mouse_position) { + computed_style.refine(&self.hover_style); + } + + let active_state = *state.active_state.lock(); + if active_state.group { + if let Some(GroupStyle { style, .. }) = self.group_active.as_ref() { + computed_style.refine(style); + } + } + + if active_state.element { + computed_style.refine(&self.active_style); + } + + computed_style + } + + fn paint_hover_listeners( + &self, + bounds: Bounds, + group_bounds: Option>, + cx: &mut ViewContext, + ) { + if let Some(group_bounds) = group_bounds { + paint_hover_listener(group_bounds, cx); + } + + if self.hover_style.is_some() { + paint_hover_listener(bounds, cx); + } + } + + fn paint_active_listener( + &self, + bounds: Bounds, + group_bounds: Option>, + active_state: Arc>, + cx: &mut ViewContext, + ) { + if active_state.lock().is_none() { + cx.on_mouse_event(move |_view, down: &MouseDownEvent, phase, cx| { + if phase == DispatchPhase::Bubble { + let group = + group_bounds.map_or(false, |bounds| bounds.contains_point(&down.position)); + let element = bounds.contains_point(&down.position); + if group || element { + *active_state.lock() = ActiveState { group, element }; + cx.notify(); + } + } + }); + } else { + cx.on_mouse_event(move |_, _: &MouseUpEvent, phase, cx| { + if phase == DispatchPhase::Capture { + *active_state.lock() = ActiveState::default(); + cx.notify(); + } + }); + } + } + + fn paint_event_listeners( + &self, + bounds: Bounds, + pending_click: Arc>>, + cx: &mut ViewContext, + ) { + let click_listeners = self.listeners.mouse_click.clone(); + let mouse_down = pending_click.lock().clone(); + if let Some(mouse_down) = mouse_down { + cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| { + if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) { + let mouse_click = MouseClickEvent { + down: mouse_down.clone(), + up: event.clone(), + }; + for listener in &click_listeners { + listener(state, &mouse_click, &bounds, cx); + } + } + + *pending_click.lock() = None; + }); + } else { + cx.on_mouse_event(move |_state, event: &MouseDownEvent, phase, _cx| { + if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) { + *pending_click.lock() = Some(event.clone()); + } + }); + } + + for listener in self.listeners.mouse_down.iter().cloned() { + cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| { + listener(state, event, &bounds, phase, cx); + }) + } + + for listener in self.listeners.mouse_up.iter().cloned() { + cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| { + listener(state, event, &bounds, phase, cx); + }) + } + + for listener in self.listeners.mouse_move.iter().cloned() { + cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| { + listener(state, event, &bounds, phase, cx); + }) + } + + for listener in self.listeners.scroll_wheel.iter().cloned() { + cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| { + listener(state, event, &bounds, phase, cx); + }) + } } } -impl Div { - pub fn id(self, id: impl Into) -> Div { - Div(self.0.replace_child(|hoverable| { - hoverable.replace_child(|layout_node| layout_node.identify(id)) - })) +impl Element for Div +where + V: 'static + Send + Sync, + K: ElementKind, +{ + type ViewState = V; + type ElementState = DivState; + + fn id(&self) -> Option { + self.kind.id() + } + + fn layout( + &mut self, + view_state: &mut Self::ViewState, + element_state: Option, + cx: &mut ViewContext, + ) -> (LayoutId, Self::ElementState) { + self.with_element_id(cx, |this, cx| { + let layout_ids = this + .children + .iter_mut() + .map(|child| child.layout(view_state, cx)) + .collect::>(); + + let element_state = element_state.unwrap_or_default(); + let style = this.compute_style(Bounds::default(), &element_state, cx); + let layout_id = cx.request_layout(&style, layout_ids); + (layout_id, element_state) + }) + } + + fn paint( + &mut self, + bounds: Bounds, + view_state: &mut Self::ViewState, + element_state: &mut Self::ElementState, + cx: &mut ViewContext, + ) { + self.with_element_id(cx, |this, cx| { + if let Some(group) = this.group.clone() { + cx.default_global::() + .0 + .entry(group) + .or_default() + .push(bounds); + } + + let hover_group_bounds = this + .group_hover + .as_ref() + .and_then(|group_hover| group_bounds(&group_hover.group, cx)); + let active_group_bounds = this + .group_active + .as_ref() + .and_then(|group_active| group_bounds(&group_active.group, cx)); + let style = this.compute_style(bounds, element_state, cx); + let z_index = style.z_index.unwrap_or(0); + + // Paint background and event handlers. + cx.stack(z_index, |cx| { + cx.stack(0, |cx| { + style.paint(bounds, cx); + this.paint_hover_listeners(bounds, hover_group_bounds, cx); + this.paint_active_listener( + bounds, + active_group_bounds, + element_state.active_state.clone(), + cx, + ); + this.paint_event_listeners(bounds, element_state.pending_click.clone(), cx); + }); + }); + + style.apply_text_style(cx, |cx| { + style.apply_overflow(bounds, cx, |cx| { + cx.stack(z_index + 1, |cx| { + for child in &mut this.children { + child.paint(view_state, None, cx); + } + }) + }) + }); + + if let Some(group) = this.group.as_ref() { + cx.default_global::() + .0 + .get_mut(group) + .unwrap() + .pop(); + } + }) } } impl IdentifiedElement for Div { fn id(&self) -> ElementId { - IdentifiedElement::id(&self.0) - } -} - -impl ParentElement for Div { - fn children_mut(&mut self) -> &mut SmallVec<[AnyElement; 2]> { - self.0.children_mut() - } - - fn group_mut(&mut self) -> &mut Option { - self.0.group_mut() - } -} - -impl Styled for Div { - fn style_cascade(&mut self) -> &mut StyleCascade { - self.0.style_cascade() - } - - fn computed_style(&mut self) -> &Style { - self.0.computed_style() - } -} - -impl Hoverable for Div { - fn hover_style(&mut self) -> &mut StyleRefinement { - self.0.hover_style() - } -} - -impl Clickable for Div { - fn active_style(&mut self) -> &mut StyleRefinement { - self.0.active_style() - } - - fn listeners(&mut self) -> &mut ClickListeners { - self.0.listeners() + self.kind.0.clone() } } @@ -157,34 +529,80 @@ where } } -impl Element for Div +impl Styled for Div where V: 'static + Send + Sync, K: ElementKind, { - type ViewState = V; - type ElementState = ClickableElementState<()>; - - fn id(&self) -> Option { - self.0.id() - } - - fn layout( - &mut self, - state: &mut Self::ViewState, - element_state: Option, - cx: &mut ViewContext, - ) -> (LayoutId, Self::ElementState) { - self.0.layout(state, element_state, cx) - } - - fn paint( - &mut self, - bounds: Bounds, - state: &mut Self::ViewState, - element_state: &mut Self::ElementState, - cx: &mut ViewContext, - ) { - self.0.paint(bounds, state, element_state, cx); + fn style(&mut self) -> &mut StyleRefinement { + &mut self.base_style } } + +pub struct MouseClickEvent { + pub down: MouseDownEvent, + pub up: MouseUpEvent, +} + +type MouseDownHandler = Arc< + dyn Fn(&mut V, &MouseDownEvent, &Bounds, DispatchPhase, &mut ViewContext) + + Send + + Sync + + 'static, +>; +type MouseUpHandler = Arc< + dyn Fn(&mut V, &MouseUpEvent, &Bounds, DispatchPhase, &mut ViewContext) + + Send + + Sync + + 'static, +>; +type MouseClickHandler = Arc< + dyn Fn(&mut V, &MouseClickEvent, &Bounds, &mut ViewContext) + Send + Sync + 'static, +>; + +type MouseMoveHandler = Arc< + dyn Fn(&mut V, &MouseMoveEvent, &Bounds, DispatchPhase, &mut ViewContext) + + Send + + Sync + + 'static, +>; +type ScrollWheelHandler = Arc< + dyn Fn(&mut V, &ScrollWheelEvent, &Bounds, DispatchPhase, &mut ViewContext) + + Send + + Sync + + 'static, +>; + +pub struct MouseEventListeners { + mouse_down: SmallVec<[MouseDownHandler; 2]>, + mouse_up: SmallVec<[MouseUpHandler; 2]>, + mouse_click: SmallVec<[MouseClickHandler; 2]>, + mouse_move: SmallVec<[MouseMoveHandler; 2]>, + scroll_wheel: SmallVec<[ScrollWheelHandler; 2]>, +} + +impl Default for MouseEventListeners { + fn default() -> Self { + Self { + mouse_down: SmallVec::new(), + mouse_up: SmallVec::new(), + mouse_click: SmallVec::new(), + mouse_move: SmallVec::new(), + scroll_wheel: SmallVec::new(), + } + } +} + +fn paint_hover_listener(bounds: Bounds, cx: &mut ViewContext) +where + V: 'static + Send + Sync, +{ + let hovered = bounds.contains_point(&cx.mouse_position()); + cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| { + if phase == DispatchPhase::Capture { + if bounds.contains_point(&event.position) != hovered { + cx.notify(); + } + } + }); +} diff --git a/crates/gpui3/src/elements/div2.rs b/crates/gpui3/src/elements/div2.rs deleted file mode 100644 index 5c8d44c718..0000000000 --- a/crates/gpui3/src/elements/div2.rs +++ /dev/null @@ -1,498 +0,0 @@ -use crate::{ - AnonymousElementKind, AnyElement, AppContext, BorrowWindow, Bounds, DispatchPhase, Element, - ElementId, ElementKind, IdentifiedElement, IdentifiedElementKind, IntoAnyElement, LayoutId, - MouseDownEvent, MouseMoveEvent, MouseUpEvent, Overflow, Pixels, Point, ScrollWheelEvent, - SharedString, Style, StyleRefinement, ViewContext, -}; -use collections::HashMap; -use parking_lot::Mutex; -use refineable::Refineable; -use smallvec::SmallVec; -use std::sync::Arc; - -#[derive(Default)] -pub struct DivState { - active_state: Arc>, - pending_click: Arc>>, -} - -#[derive(Copy, Clone, Default, Eq, PartialEq)] -struct ActiveState { - group: bool, - element: bool, -} - -impl ActiveState { - pub fn is_none(&self) -> bool { - !self.group && !self.element - } -} - -#[derive(Default)] -struct GroupBounds(HashMap; 1]>>); - -pub fn group_bounds(name: &SharedString, cx: &mut AppContext) -> Option> { - cx.default_global::() - .0 - .get(name) - .and_then(|bounds_stack| bounds_stack.last().cloned()) -} - -#[derive(Default, Clone)] -pub struct ScrollState(Arc>>); - -impl ScrollState { - pub fn x(&self) -> Pixels { - self.0.lock().x - } - - pub fn set_x(&self, value: Pixels) { - self.0.lock().x = value; - } - - pub fn y(&self) -> Pixels { - self.0.lock().y - } - - pub fn set_y(&self, value: Pixels) { - self.0.lock().y = value; - } -} - -pub fn div() -> Div -where - S: 'static + Send + Sync, -{ - Div { - kind: AnonymousElementKind, - children: SmallVec::new(), - group: None, - base_style: StyleRefinement::default(), - hover_style: StyleRefinement::default(), - group_hover: None, - active_style: StyleRefinement::default(), - group_active: None, - listeners: MouseEventListeners::default(), - } -} - -pub struct Div { - kind: K, - children: SmallVec<[AnyElement; 2]>, - group: Option, - base_style: StyleRefinement, - hover_style: StyleRefinement, - group_hover: Option, - active_style: StyleRefinement, - group_active: Option, - listeners: MouseEventListeners, -} - -struct GroupStyle { - group: SharedString, - style: StyleRefinement, -} - -impl Div -where - V: 'static + Send + Sync, -{ - pub fn id(self, id: ElementId) -> Div { - Div { - kind: IdentifiedElementKind(id), - children: self.children, - group: self.group, - base_style: self.base_style, - hover_style: self.hover_style, - group_hover: self.group_hover, - active_style: self.active_style, - group_active: self.group_active, - listeners: self.listeners, - } - } -} - -impl Div -where - V: 'static + Send + Sync, - K: ElementKind, -{ - pub fn group(mut self, group: impl Into) -> Self { - self.group = Some(group.into()); - self - } - - pub fn z_index(mut self, z_index: u32) -> Self { - self.base_style.z_index = Some(z_index); - self - } - - pub fn overflow_hidden(mut self) -> Self { - self.base_style.overflow.x = Some(Overflow::Hidden); - self.base_style.overflow.y = Some(Overflow::Hidden); - self - } - - pub fn overflow_hidden_x(mut self) -> Self { - self.base_style.overflow.x = Some(Overflow::Hidden); - self - } - - pub fn overflow_hidden_y(mut self) -> Self { - self.base_style.overflow.y = Some(Overflow::Hidden); - self - } - - pub fn overflow_scroll(mut self, _scroll_state: ScrollState) -> Self { - // todo!("impl scrolling") - // self.scroll_state = Some(scroll_state); - self.base_style.overflow.x = Some(Overflow::Scroll); - self.base_style.overflow.y = Some(Overflow::Scroll); - self - } - - pub fn overflow_x_scroll(mut self, _scroll_state: ScrollState) -> Self { - // todo!("impl scrolling") - // self.scroll_state = Some(scroll_state); - self.base_style.overflow.x = Some(Overflow::Scroll); - self - } - - pub fn overflow_y_scroll(mut self, _scroll_state: ScrollState) -> Self { - // todo!("impl scrolling") - // self.scroll_state = Some(scroll_state); - self.base_style.overflow.y = Some(Overflow::Scroll); - self - } - - fn with_element_id( - &mut self, - cx: &mut ViewContext, - f: impl FnOnce(&mut Self, &mut ViewContext) -> R, - ) -> R { - if let Some(id) = self.id() { - cx.with_element_id(id, |cx| f(self, cx)) - } else { - f(self, cx) - } - } - - fn compute_style( - &self, - bounds: Bounds, - group_bounds: Option>, - active_state: ActiveState, - cx: &mut ViewContext, - ) -> Style { - let mut computed_style = Style::default(); - computed_style.refine(&self.base_style); - - let mouse_position = cx.mouse_position(); - if let Some(group_bounds) = group_bounds { - if group_bounds.contains_point(mouse_position) { - if let Some(GroupStyle { style, .. }) = self.group_hover.as_ref() { - computed_style.refine(style); - } - } - } - if bounds.contains_point(mouse_position) { - computed_style.refine(&self.hover_style); - } - - if active_state.group { - if let Some(GroupStyle { style, .. }) = self.group_active.as_ref() { - computed_style.refine(style); - } - } - - if active_state.element { - computed_style.refine(&self.active_style); - } - - computed_style - } - - fn paint_hover_listeners( - &self, - bounds: Bounds, - group_bounds: Option>, - cx: &mut ViewContext, - ) { - if let Some(group_bounds) = group_bounds { - paint_hover_listener(group_bounds, cx); - } - - if self.hover_style.is_some() { - paint_hover_listener(bounds, cx); - } - } - - fn paint_active_listener( - &self, - bounds: Bounds, - group_bounds: Option>, - active_state: Arc>, - cx: &mut ViewContext, - ) { - if active_state.lock().is_none() { - cx.on_mouse_event(move |_view, down: &MouseDownEvent, phase, cx| { - if phase == DispatchPhase::Bubble { - let group = - group_bounds.map_or(false, |bounds| bounds.contains_point(down.position)); - let element = bounds.contains_point(down.position); - if group || element { - *active_state.lock() = ActiveState { group, element }; - cx.notify(); - } - } - }); - } else { - cx.on_mouse_event(move |_, _: &MouseUpEvent, phase, cx| { - if phase == DispatchPhase::Capture { - *active_state.lock() = ActiveState::default(); - cx.notify(); - } - }); - } - } - - fn paint_event_listeners( - &self, - bounds: Bounds, - pending_click: Arc>>, - cx: &mut ViewContext, - ) { - let click_listeners = self.listeners.mouse_click.clone(); - let mouse_down = pending_click.lock().clone(); - if let Some(mouse_down) = mouse_down { - cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| { - if phase == DispatchPhase::Bubble && bounds.contains_point(event.position) { - let mouse_click = MouseClickEvent { - down: mouse_down.clone(), - up: event.clone(), - }; - for listener in &click_listeners { - listener(state, &mouse_click, &bounds, cx); - } - } - - *pending_click.lock() = None; - }); - } else { - cx.on_mouse_event(move |_state, event: &MouseDownEvent, phase, _cx| { - if phase == DispatchPhase::Bubble && bounds.contains_point(event.position) { - *pending_click.lock() = Some(event.clone()); - } - }); - } - - for listener in self.listeners.mouse_down.iter().cloned() { - cx.on_mouse_event(move |state, event: &MouseDownEvent, phase, cx| { - listener(state, event, &bounds, phase, cx); - }) - } - - for listener in self.listeners.mouse_up.iter().cloned() { - cx.on_mouse_event(move |state, event: &MouseUpEvent, phase, cx| { - listener(state, event, &bounds, phase, cx); - }) - } - - for listener in self.listeners.mouse_move.iter().cloned() { - cx.on_mouse_event(move |state, event: &MouseMoveEvent, phase, cx| { - listener(state, event, &bounds, phase, cx); - }) - } - - for listener in self.listeners.scroll_wheel.iter().cloned() { - cx.on_mouse_event(move |state, event: &ScrollWheelEvent, phase, cx| { - listener(state, event, &bounds, phase, cx); - }) - } - } -} - -impl Element for Div -where - V: 'static + Send + Sync, - K: ElementKind, -{ - type ViewState = V; - type ElementState = DivState; - - fn id(&self) -> Option { - self.kind.id() - } - - fn layout( - &mut self, - view_state: &mut Self::ViewState, - element_state: Option, - cx: &mut ViewContext, - ) -> (LayoutId, Self::ElementState) { - self.with_element_id(cx, |this, cx| { - let layout_ids = this - .children - .iter_mut() - .map(|child| child.layout(view_state, cx)) - .collect::>(); - - let element_state = element_state.unwrap_or_default(); - let style = this.compute_style( - Bounds::default(), - None, - *element_state.active_state.lock(), - cx, - ); - let layout_id = cx.request_layout(&style, layout_ids); - (layout_id, element_state) - }) - } - - fn paint( - &mut self, - bounds: Bounds, - view_state: &mut Self::ViewState, - element_state: &mut Self::ElementState, - cx: &mut ViewContext, - ) { - self.with_element_id(cx, |this, cx| { - if let Some(group) = this.group.clone() { - cx.default_global::() - .0 - .entry(group) - .or_default() - .push(bounds); - } - - let hover_group_bounds = this - .group_hover - .as_ref() - .and_then(|group_hover| group_bounds(&group_hover.group, cx)); - let active_group_bounds = this - .group_active - .as_ref() - .and_then(|group_active| group_bounds(&group_active.group, cx)); - let active_state = *element_state.active_state.lock(); - let style = this.compute_style(bounds, hover_group_bounds, active_state, cx); - let z_index = style.z_index.unwrap_or(0); - - // Paint background and event handlers. - cx.stack(z_index, |cx| { - cx.stack(0, |cx| { - style.paint(bounds, cx); - this.paint_hover_listeners(bounds, hover_group_bounds, cx); - this.paint_active_listener( - bounds, - active_group_bounds, - element_state.active_state.clone(), - cx, - ); - this.paint_event_listeners(bounds, element_state.pending_click.clone(), cx); - }); - }); - - style.apply_text_style(cx, |cx| { - style.apply_overflow(bounds, cx, |cx| { - cx.stack(z_index + 1, |cx| { - for child in &mut this.children { - child.paint(view_state, None, cx); - } - }) - }) - }); - - if let Some(group) = this.group.as_ref() { - cx.default_global::() - .0 - .get_mut(group) - .unwrap() - .pop(); - } - }) - } -} - -impl IdentifiedElement for Div { - fn id(&self) -> ElementId { - self.kind.0.clone() - } -} - -impl IntoAnyElement for Div -where - V: 'static + Send + Sync, - K: ElementKind, -{ - fn into_any(self) -> AnyElement { - AnyElement::new(self) - } -} - -pub struct MouseClickEvent { - pub down: MouseDownEvent, - pub up: MouseUpEvent, -} - -type MouseDownHandler = Arc< - dyn Fn(&mut V, &MouseDownEvent, &Bounds, DispatchPhase, &mut ViewContext) - + Send - + Sync - + 'static, ->; -type MouseUpHandler = Arc< - dyn Fn(&mut V, &MouseUpEvent, &Bounds, DispatchPhase, &mut ViewContext) - + Send - + Sync - + 'static, ->; -type MouseClickHandler = Arc< - dyn Fn(&mut V, &MouseClickEvent, &Bounds, &mut ViewContext) + Send + Sync + 'static, ->; - -type MouseMoveHandler = Arc< - dyn Fn(&mut V, &MouseMoveEvent, &Bounds, DispatchPhase, &mut ViewContext) - + Send - + Sync - + 'static, ->; -type ScrollWheelHandler = Arc< - dyn Fn(&mut V, &ScrollWheelEvent, &Bounds, DispatchPhase, &mut ViewContext) - + Send - + Sync - + 'static, ->; - -pub struct MouseEventListeners { - mouse_down: SmallVec<[MouseDownHandler; 2]>, - mouse_up: SmallVec<[MouseUpHandler; 2]>, - mouse_click: SmallVec<[MouseClickHandler; 2]>, - mouse_move: SmallVec<[MouseMoveHandler; 2]>, - scroll_wheel: SmallVec<[ScrollWheelHandler; 2]>, -} - -impl Default for MouseEventListeners { - fn default() -> Self { - Self { - mouse_down: SmallVec::new(), - mouse_up: SmallVec::new(), - mouse_click: SmallVec::new(), - mouse_move: SmallVec::new(), - scroll_wheel: SmallVec::new(), - } - } -} - -fn paint_hover_listener(bounds: Bounds, cx: &mut ViewContext) -where - V: 'static + Send + Sync, -{ - let hovered = bounds.contains_point(cx.mouse_position()); - cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| { - if phase == DispatchPhase::Capture { - if bounds.contains_point(event.position) != hovered { - cx.notify(); - } - } - }); -} diff --git a/crates/gpui3/src/elements/hoverable.rs b/crates/gpui3/src/elements/hoverable.rs deleted file mode 100644 index bdc37ad03b..0000000000 --- a/crates/gpui3/src/elements/hoverable.rs +++ /dev/null @@ -1,149 +0,0 @@ -use crate::{ - group_bounds, AnyElement, Bounds, DispatchPhase, Element, ElementId, IdentifiedElement, - IntoAnyElement, LayoutId, MouseMoveEvent, ParentElement, Pixels, SharedString, Style, - StyleCascade, StyleRefinement, Styled, ViewContext, -}; -use refineable::CascadeSlot; -use std::sync::{ - atomic::{AtomicBool, Ordering::SeqCst}, - Arc, -}; - -pub trait Hoverable { - fn hover_style(&mut self) -> &mut StyleRefinement; - - fn hover(mut self, f: impl FnOnce(&mut StyleRefinement) -> &mut StyleRefinement) -> Self - where - Self: Sized, - { - f(self.hover_style()); - self - } -} - -pub struct HoverableElement { - hover_style: StyleRefinement, - group: Option, - cascade_slot: CascadeSlot, - hovered: Arc, - child: E, -} - -impl HoverableElement { - pub fn new(mut child: E) -> Self { - let cascade_slot = child.style_cascade().reserve(); - HoverableElement { - hover_style: StyleRefinement::default(), - group: None, - cascade_slot, - hovered: Arc::new(AtomicBool::new(false)), - child, - } - } - - pub fn replace_child>( - self, - replace: impl FnOnce(E) -> E2, - ) -> HoverableElement { - HoverableElement { - hover_style: self.hover_style, - group: self.group, - cascade_slot: self.cascade_slot, - hovered: self.hovered, - child: replace(self.child), - } - } -} - -impl IntoAnyElement for HoverableElement -where - E: Styled + Element, -{ - fn into_any(self) -> AnyElement { - AnyElement::new(self) - } -} - -impl Element for HoverableElement -where - E: Styled + Element, -{ - type ViewState = E::ViewState; - type ElementState = E::ElementState; - - fn id(&self) -> Option { - self.child.id() - } - - fn layout( - &mut self, - state: &mut Self::ViewState, - element_state: Option, - cx: &mut ViewContext, - ) -> (LayoutId, Self::ElementState) { - self.child.layout(state, element_state, cx) - } - - fn paint( - &mut self, - bounds: Bounds, - state: &mut Self::ViewState, - element_state: &mut Self::ElementState, - cx: &mut ViewContext, - ) { - let target_bounds = self - .group - .as_ref() - .and_then(|group| group_bounds(group, cx)) - .unwrap_or(bounds); - - let hovered = target_bounds.contains_point(cx.mouse_position()); - - let slot = self.cascade_slot; - let style = hovered.then_some(self.hover_style.clone()); - self.child.style_cascade().set(slot, style); - self.hovered.store(hovered, SeqCst); - - let hovered = self.hovered.clone(); - cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| { - if phase == DispatchPhase::Capture { - if target_bounds.contains_point(event.position) != hovered.load(SeqCst) { - cx.notify(); - } - } - }); - - self.child.paint(bounds, state, element_state, cx); - } -} - -impl ParentElement for HoverableElement -where - E: Styled + ParentElement, -{ - fn children_mut(&mut self) -> &mut smallvec::SmallVec<[AnyElement; 2]> { - self.child.children_mut() - } - - fn group_mut(&mut self) -> &mut Option { - self.child.group_mut() - } -} - -impl Hoverable for HoverableElement { - fn hover_style(&mut self) -> &mut StyleRefinement { - &mut self.hover_style - } -} - -impl Styled for HoverableElement { - fn style_cascade(&mut self) -> &mut StyleCascade { - self.child.style_cascade() - } - - fn computed_style(&mut self) -> &Style { - self.child.computed_style() - } -} - -impl IdentifiedElement for HoverableElement {} diff --git a/crates/gpui3/src/elements/img.rs b/crates/gpui3/src/elements/img.rs index 5efe7afe31..e90a865723 100644 --- a/crates/gpui3/src/elements/img.rs +++ b/crates/gpui3/src/elements/img.rs @@ -1,15 +1,13 @@ use crate::{ - AnonymousElementKind, AnyElement, BorrowWindow, Bounds, ClickListeners, Clickable, - ClickableElement, ClickableElementState, Element, ElementId, ElementKind, Hoverable, - HoverableElement, IdentifiedElement, IdentifiedElementKind, IntoAnyElement, LayoutId, - LayoutNodeElement, Pixels, SharedString, Style, StyleRefinement, Styled, ViewContext, + div, AnonymousElementKind, AnyElement, BorrowWindow, Bounds, Div, DivState, Element, ElementId, + ElementKind, IdentifiedElement, IdentifiedElementKind, IntoAnyElement, LayoutId, Pixels, + SharedString, StyleRefinement, Styled, ViewContext, }; use futures::FutureExt; -use refineable::Cascade; use util::ResultExt; pub struct Img { - layout_node: ClickableElement>>, + base: Div, uri: Option, grayscale: bool, } @@ -19,7 +17,7 @@ where V: 'static + Send + Sync, { Img { - layout_node: ClickableElement::new(HoverableElement::new(LayoutNodeElement::new())), + base: div(), uri: None, grayscale: false, } @@ -44,9 +42,7 @@ where impl Img { pub fn id(self, id: impl Into) -> Img { Img { - layout_node: self.layout_node.replace_child(|hoverable| { - hoverable.replace_child(|layout_node| layout_node.identify(id)) - }), + base: self.base.id(id), uri: self.uri, grayscale: self.grayscale, } @@ -69,10 +65,10 @@ where K: ElementKind, { type ViewState = V; - type ElementState = ClickableElementState<()>; + type ElementState = DivState; fn id(&self) -> Option { - self.layout_node.id() + self.base.id() } fn layout( @@ -84,7 +80,7 @@ where where Self: Sized, { - self.layout_node.layout(view_state, element_state, cx) + self.base.layout(view_state, element_state, cx) } fn paint( @@ -95,10 +91,10 @@ where cx: &mut ViewContext, ) { cx.stack(1, |cx| { - self.layout_node.paint(bounds, view, element_state, cx); + self.base.paint(bounds, view, element_state, cx); }); - let style = self.computed_style(); + let style = self.base.compute_style(bounds, element_state, cx); let corner_radii = style.corner_radii; if let Some(uri) = self.uri.clone() { @@ -127,7 +123,7 @@ where impl IdentifiedElement for Img { fn id(&self) -> ElementId { - IdentifiedElement::id(&self.layout_node) + IdentifiedElement::id(&self.base) } } @@ -136,27 +132,7 @@ where V: 'static + Send + Sync, K: ElementKind, { - fn style_cascade(&mut self) -> &mut Cascade