diff --git a/crates/gpui2/src/interactive.rs b/crates/gpui2/src/interactive.rs index acc97810f14b9d2800cbac8206ed98e1cc8d2d0d..0479d49f74865ffc925f847271b1cbefd3b7b025 100644 --- a/crates/gpui2/src/interactive.rs +++ b/crates/gpui2/src/interactive.rs @@ -336,6 +336,34 @@ pub trait StatefulInteractive: StatelessInteractive { })); self } + + fn drag_over(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self + where + Self: Sized, + { + self.stateful_interaction() + .drag_over_styles + .push((TypeId::of::(), f(StyleRefinement::default()))); + self + } + + fn group_drag_over( + mut self, + group_name: impl Into, + f: impl FnOnce(StyleRefinement) -> StyleRefinement, + ) -> Self + where + Self: Sized, + { + self.stateful_interaction().group_drag_over_styles.push(( + TypeId::of::(), + GroupStyle { + group: group_name.into(), + style: f(StyleRefinement::default()), + }, + )); + self + } } pub trait ElementInteraction: 'static + Send + Sync { @@ -398,6 +426,26 @@ pub trait ElementInteraction: 'static + Send + Sync { style.refine(&stateless.hover_style); } + if let Some(drag) = cx.active_drag.take() { + for (state_type, group_drag_style) in &self.as_stateless().group_drag_over_styles { + if let Some(group_bounds) = GroupBounds::get(&group_drag_style.group, cx) { + if *state_type == drag.state_type + && group_bounds.contains_point(&mouse_position) + { + style.refine(&group_drag_style.style); + } + } + } + + for (state_type, drag_over_style) in &self.as_stateless().drag_over_styles { + if *state_type == drag.state_type && bounds.contains_point(&mouse_position) { + style.refine(drag_over_style); + } + } + + cx.active_drag = Some(drag); + } + if let Some(stateful) = self.as_stateful() { let active_state = element_state.active_state.lock(); if active_state.group { @@ -450,11 +498,27 @@ pub trait ElementInteraction: 'static + Send + Sync { .and_then(|group_hover| GroupBounds::get(&group_hover.group, cx)); if let Some(group_bounds) = hover_group_bounds { - paint_hover_listener(group_bounds, cx); + let hovered = group_bounds.contains_point(&cx.mouse_position()); + cx.on_mouse_event(move |_, event: &MouseMoveEvent, phase, cx| { + if phase == DispatchPhase::Capture { + if group_bounds.contains_point(&event.position) != hovered { + cx.notify(); + } + } + }); } - if stateless.hover_style.is_some() { - paint_hover_listener(bounds, cx); + if stateless.hover_style.is_some() + || (cx.active_drag.is_some() && !stateless.drag_over_styles.is_empty()) + { + 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(); + } + } + }); } if let Some(stateful) = self.as_stateful() { @@ -466,6 +530,7 @@ pub trait ElementInteraction: 'static + Send + Sync { let mouse_down = pending_mouse_down.lock().clone(); if let Some(mouse_down) = mouse_down { if let Some(drag_listener) = drag_listener { + let active_state = element_state.active_state.clone(); cx.on_mouse_event(move |view_state, event: &MouseMoveEvent, phase, cx| { if cx.active_drag.is_some() { if phase == DispatchPhase::Capture { @@ -476,6 +541,7 @@ pub trait ElementInteraction: 'static + Send + Sync { { let cursor_offset = event.position - bounds.origin; let any_drag = drag_listener(view_state, cursor_offset, cx); + *active_state.lock() = ActiveState::default(); cx.start_drag(any_drag); cx.stop_propagation(); } @@ -570,30 +636,16 @@ pub trait ElementInteraction: 'static + Send + Sync { } } -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(); - } - } - }); -} - #[derive(Deref, DerefMut)] pub struct StatefulInteraction { pub id: ElementId, #[deref] #[deref_mut] stateless: StatelessInteraction, - pub click_listeners: SmallVec<[ClickListener; 2]>, - pub(crate) drag_listener: Option>, - pub active_style: StyleRefinement, - pub group_active_style: Option, + click_listeners: SmallVec<[ClickListener; 2]>, + active_style: StyleRefinement, + group_active_style: Option, + drag_listener: Option>, } impl ElementInteraction for StatefulInteraction @@ -642,6 +694,8 @@ pub struct StatelessInteraction { pub key_listeners: SmallVec<[(TypeId, KeyListener); 32]>, pub hover_style: StyleRefinement, pub group_hover_style: Option, + drag_over_styles: SmallVec<[(TypeId, StyleRefinement); 2]>, + group_drag_over_styles: SmallVec<[(TypeId, GroupStyle); 2]>, } impl StatelessInteraction @@ -732,6 +786,8 @@ impl Default for StatelessInteraction { key_listeners: SmallVec::new(), hover_style: StyleRefinement::default(), group_hover_style: None, + drag_over_styles: SmallVec::new(), + group_drag_over_styles: SmallVec::new(), } } } diff --git a/crates/ui2/src/components/tab.rs b/crates/ui2/src/components/tab.rs index 17157f506c911f2367b59dbec24d49251010b09d..1f37286f94e513edc239ba57e37fa580f503d4b4 100644 --- a/crates/ui2/src/components/tab.rs +++ b/crates/ui2/src/components/tab.rs @@ -125,6 +125,7 @@ impl Tab { .on_drag(move |_view, _cx| { Drag::new(drag_state.clone(), |view, cx| div().w_8().h_4().bg(red())) }) + .drag_over::(|d| d.bg(black())) .px_2() .py_0p5() .flex() @@ -160,7 +161,7 @@ impl Tab { } } -use gpui2::{red, Drag, ElementId}; +use gpui2::{black, red, Drag, ElementId}; #[cfg(feature = "stories")] pub use stories::*;