diff --git a/crates/gpui3/src/elements.rs b/crates/gpui3/src/elements.rs index 9278fac33d..3b6b226313 100644 --- a/crates/gpui3/src/elements.rs +++ b/crates/gpui3/src/elements.rs @@ -5,6 +5,7 @@ mod img; mod layout_node; mod svg; mod text; +mod div2; pub use clickable::*; pub use div::*; diff --git a/crates/gpui3/src/elements/div2.rs b/crates/gpui3/src/elements/div2.rs new file mode 100644 index 0000000000..6a624b80e2 --- /dev/null +++ b/crates/gpui3/src/elements/div2.rs @@ -0,0 +1,353 @@ +use crate::{ + AnonymousElementKind, AnyElement, AppContext, BorrowWindow, Bounds, DispatchPhase, Element, + ElementId, ElementKind, IntoAnyElement, LayoutId, MouseDownEvent, MouseMoveEvent, MouseUpEvent, + Pixels, 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>, +} + +#[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()) +} + +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, + K: ElementKind, +{ + 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(); + } + }); + } + + // 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); + // }) + // } + } +} + +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(); + } + } + }); +} + +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, + ); + }); + }); + + 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 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(), + } + } +}