WIP

Antonio Scandurra created

Change summary

crates/gpui3/src/elements.rs      |   1 
crates/gpui3/src/elements/div2.rs | 353 +++++++++++++++++++++++++++++++++
2 files changed, 354 insertions(+)

Detailed changes

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<Mutex<ActiveState>>,
+}
+
+#[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<SharedString, SmallVec<[Bounds<Pixels>; 1]>>);
+
+pub fn group_bounds(name: &SharedString, cx: &mut AppContext) -> Option<Bounds<Pixels>> {
+    cx.default_global::<GroupBounds>()
+        .0
+        .get(name)
+        .and_then(|bounds_stack| bounds_stack.last().cloned())
+}
+
+pub struct Div<V: 'static + Send + Sync, K: ElementKind = AnonymousElementKind> {
+    kind: K,
+    children: SmallVec<[AnyElement<V>; 2]>,
+    group: Option<SharedString>,
+    base_style: StyleRefinement,
+    hover_style: StyleRefinement,
+    group_hover: Option<GroupStyle>,
+    active_style: StyleRefinement,
+    group_active: Option<GroupStyle>,
+    listeners: MouseEventListeners<V>,
+}
+
+struct GroupStyle {
+    group: SharedString,
+    style: StyleRefinement,
+}
+
+impl<V, K> Div<V, K>
+where
+    V: 'static + Send + Sync,
+    K: ElementKind,
+{
+    fn with_element_id<R>(
+        &mut self,
+        cx: &mut ViewContext<V>,
+        f: impl FnOnce(&mut Self, &mut ViewContext<V>) -> 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<Pixels>,
+        group_bounds: Option<Bounds<Pixels>>,
+        active_state: ActiveState,
+        cx: &mut ViewContext<V>,
+    ) -> 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<Pixels>,
+        group_bounds: Option<Bounds<Pixels>>,
+        cx: &mut ViewContext<V>,
+    ) {
+        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<Pixels>,
+        group_bounds: Option<Bounds<Pixels>>,
+        active_state: Arc<Mutex<ActiveState>>,
+        cx: &mut ViewContext<V>,
+    ) {
+        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<V>(bounds: Bounds<Pixels>, cx: &mut ViewContext<V>)
+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<V, K> Element for Div<V, K>
+where
+    V: 'static + Send + Sync,
+    K: ElementKind,
+{
+    type ViewState = V;
+    type ElementState = DivState;
+
+    fn id(&self) -> Option<ElementId> {
+        self.kind.id()
+    }
+
+    fn layout(
+        &mut self,
+        view_state: &mut Self::ViewState,
+        element_state: Option<Self::ElementState>,
+        cx: &mut ViewContext<Self::ViewState>,
+    ) -> (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::<Vec<_>>();
+
+            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<Pixels>,
+        view_state: &mut Self::ViewState,
+        element_state: &mut Self::ElementState,
+        cx: &mut ViewContext<Self::ViewState>,
+    ) {
+        self.with_element_id(cx, |this, cx| {
+            if let Some(group) = this.group.clone() {
+                cx.default_global::<GroupBounds>()
+                    .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::<GroupBounds>()
+                    .0
+                    .get_mut(group)
+                    .unwrap()
+                    .pop();
+            }
+        })
+    }
+}
+
+impl<V, K> IntoAnyElement<V> for Div<V, K>
+where
+    V: 'static + Send + Sync,
+    K: ElementKind,
+{
+    fn into_any(self) -> AnyElement<V> {
+        AnyElement::new(self)
+    }
+}
+
+pub struct MouseClickEvent {
+    pub down: MouseDownEvent,
+    pub up: MouseUpEvent,
+}
+
+type MouseDownHandler<V> = Arc<
+    dyn Fn(&mut V, &MouseDownEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
+        + Send
+        + Sync
+        + 'static,
+>;
+type MouseUpHandler<V> = Arc<
+    dyn Fn(&mut V, &MouseUpEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
+        + Send
+        + Sync
+        + 'static,
+>;
+type MouseClickHandler<V> = Arc<
+    dyn Fn(&mut V, &MouseClickEvent, &Bounds<Pixels>, &mut ViewContext<V>) + Send + Sync + 'static,
+>;
+
+type MouseMoveHandler<V> = Arc<
+    dyn Fn(&mut V, &MouseMoveEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
+        + Send
+        + Sync
+        + 'static,
+>;
+type ScrollWheelHandler<V> = Arc<
+    dyn Fn(&mut V, &ScrollWheelEvent, &Bounds<Pixels>, DispatchPhase, &mut ViewContext<V>)
+        + Send
+        + Sync
+        + 'static,
+>;
+
+pub struct MouseEventListeners<V: 'static> {
+    mouse_down: SmallVec<[MouseDownHandler<V>; 2]>,
+    mouse_up: SmallVec<[MouseUpHandler<V>; 2]>,
+    mouse_click: SmallVec<[MouseClickHandler<V>; 2]>,
+    mouse_move: SmallVec<[MouseMoveHandler<V>; 2]>,
+    scroll_wheel: SmallVec<[ScrollWheelHandler<V>; 2]>,
+}
+
+impl<V> Default for MouseEventListeners<V> {
+    fn default() -> Self {
+        Self {
+            mouse_down: SmallVec::new(),
+            mouse_up: SmallVec::new(),
+            mouse_click: SmallVec::new(),
+            mouse_move: SmallVec::new(),
+            scroll_wheel: SmallVec::new(),
+        }
+    }
+}