diff --git a/crates/gpui2/src/elements/div.rs b/crates/gpui2/src/elements/div.rs index 1eebbbe5dbb767096975b4dcb0e93fb3e1c4d1bc..2753d5f8fb290453ea4b8741b74249d6f2e0c5de 100644 --- a/crates/gpui2/src/elements/div.rs +++ b/crates/gpui2/src/elements/div.rs @@ -354,7 +354,7 @@ where F: ElementFocus, V: 'static + Send + Sync, { - fn stateless_interactivity(&mut self) -> &mut StatelessInteraction { + fn stateless_interaction(&mut self) -> &mut StatelessInteraction { self.interaction.as_stateless_mut() } } diff --git a/crates/gpui2/src/elements/img.rs b/crates/gpui2/src/elements/img.rs index 7c50a68def7bdb3b07167757ca9025811f309ac5..713bc8b98dbf667eb75957a382a07d1e0961a634 100644 --- a/crates/gpui2/src/elements/img.rs +++ b/crates/gpui2/src/elements/img.rs @@ -156,8 +156,8 @@ where I: ElementInteraction, F: ElementFocus, { - fn stateless_interactivity(&mut self) -> &mut StatelessInteraction { - self.base.stateless_interactivity() + fn stateless_interaction(&mut self) -> &mut StatelessInteraction { + self.base.stateless_interaction() } } diff --git a/crates/gpui2/src/elements/svg.rs b/crates/gpui2/src/elements/svg.rs index 35e6e143a461eef1617d0b5892ad14c4e7bbe952..3785ce480e16a2979addd3604ecc1ec66403b8d5 100644 --- a/crates/gpui2/src/elements/svg.rs +++ b/crates/gpui2/src/elements/svg.rs @@ -130,8 +130,8 @@ where I: ElementInteraction, F: ElementFocus, { - fn stateless_interactivity(&mut self) -> &mut StatelessInteraction { - self.base.stateless_interactivity() + fn stateless_interaction(&mut self) -> &mut StatelessInteraction { + self.base.stateless_interaction() } } diff --git a/crates/gpui2/src/interactive.rs b/crates/gpui2/src/interactive.rs index a078efff308994b8bf813c9a7f5b3d7a51169fb9..aa8cbf4826e9f9218e3fe6ba814e477a70e8d65e 100644 --- a/crates/gpui2/src/interactive.rs +++ b/crates/gpui2/src/interactive.rs @@ -1,5 +1,5 @@ use crate::{ - point, px, view, Action, AnyDrag, AppContext, BorrowWindow, Bounds, DispatchContext, + point, px, view, Action, AnyBox, AnyDrag, AppContext, BorrowWindow, Bounds, DispatchContext, DispatchPhase, Element, ElementId, FocusHandle, KeyMatch, Keystroke, Modifiers, Overflow, Pixels, Point, SharedString, Size, Style, StyleRefinement, ViewContext, }; @@ -19,13 +19,13 @@ use std::{ const DRAG_THRESHOLD: f64 = 2.; pub trait StatelessInteractive: Element { - fn stateless_interactivity(&mut self) -> &mut StatelessInteraction; + fn stateless_interaction(&mut self) -> &mut StatelessInteraction; fn hover(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self where Self: Sized, { - self.stateless_interactivity().hover_style = f(StyleRefinement::default()); + self.stateless_interaction().hover_style = f(StyleRefinement::default()); self } @@ -37,7 +37,7 @@ pub trait StatelessInteractive: Element { where Self: Sized, { - self.stateless_interactivity().group_hover_style = Some(GroupStyle { + self.stateless_interaction().group_hover_style = Some(GroupStyle { group: group_name.into(), style: f(StyleRefinement::default()), }); @@ -55,7 +55,7 @@ pub trait StatelessInteractive: Element { where Self: Sized, { - self.stateless_interactivity() + self.stateless_interaction() .mouse_down_listeners .push(Arc::new(move |view, event, bounds, phase, cx| { if phase == DispatchPhase::Bubble @@ -79,7 +79,7 @@ pub trait StatelessInteractive: Element { where Self: Sized, { - self.stateless_interactivity() + self.stateless_interaction() .mouse_up_listeners .push(Arc::new(move |view, event, bounds, phase, cx| { if phase == DispatchPhase::Bubble @@ -103,7 +103,7 @@ pub trait StatelessInteractive: Element { where Self: Sized, { - self.stateless_interactivity() + self.stateless_interaction() .mouse_down_listeners .push(Arc::new(move |view, event, bounds, phase, cx| { if phase == DispatchPhase::Capture @@ -127,7 +127,7 @@ pub trait StatelessInteractive: Element { where Self: Sized, { - self.stateless_interactivity() + self.stateless_interaction() .mouse_up_listeners .push(Arc::new(move |view, event, bounds, phase, cx| { if phase == DispatchPhase::Capture @@ -150,7 +150,7 @@ pub trait StatelessInteractive: Element { where Self: Sized, { - self.stateless_interactivity() + self.stateless_interaction() .mouse_move_listeners .push(Arc::new(move |view, event, bounds, phase, cx| { if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) { @@ -170,7 +170,7 @@ pub trait StatelessInteractive: Element { where Self: Sized, { - self.stateless_interactivity() + self.stateless_interaction() .scroll_wheel_listeners .push(Arc::new(move |view, event, bounds, phase, cx| { if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) { @@ -186,7 +186,7 @@ pub trait StatelessInteractive: Element { C: TryInto, C::Error: Debug, { - self.stateless_interactivity().dispatch_context = + self.stateless_interaction().dispatch_context = context.try_into().expect("invalid dispatch context"); self } @@ -201,7 +201,7 @@ pub trait StatelessInteractive: Element { where Self: Sized, { - self.stateless_interactivity().key_listeners.push(( + self.stateless_interaction().key_listeners.push(( TypeId::of::(), Arc::new(move |view, event, _, phase, cx| { let event = event.downcast_ref().unwrap(); @@ -226,7 +226,7 @@ pub trait StatelessInteractive: Element { where Self: Sized, { - self.stateless_interactivity().key_listeners.push(( + self.stateless_interaction().key_listeners.push(( TypeId::of::(), Arc::new(move |view, event, _, phase, cx| { let event = event.downcast_ref().unwrap(); @@ -247,7 +247,7 @@ pub trait StatelessInteractive: Element { where Self: Sized, { - self.stateless_interactivity().key_listeners.push(( + self.stateless_interaction().key_listeners.push(( TypeId::of::(), Arc::new(move |view, event, _, phase, cx| { let event = event.downcast_ref().unwrap(); @@ -257,6 +257,53 @@ pub trait StatelessInteractive: Element { )); self } + + fn drag_over(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self + where + Self: Sized, + { + self.stateless_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.stateless_interaction().group_drag_over_styles.push(( + TypeId::of::(), + GroupStyle { + group: group_name.into(), + style: f(StyleRefinement::default()), + }, + )); + self + } + + fn on_drop( + mut self, + listener: impl Fn(&mut Self::ViewState, S, &mut ViewContext) + + Send + + Sync + + 'static, + ) -> Self + where + Self: Sized, + { + self.stateless_interaction().drop_listeners.push(( + TypeId::of::(), + Arc::new(move |view, drag_state, cx| { + listener(view, *drag_state.downcast().unwrap(), cx); + }), + )); + self + } } pub trait StatefulInteractive: StatelessInteractive { @@ -338,34 +385,6 @@ 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 { @@ -523,6 +542,29 @@ pub trait ElementInteraction: 'static + Send + Sync { }); } + if cx.active_drag.is_some() { + let drop_listeners = stateless.drop_listeners.clone(); + cx.on_mouse_event(move |view, event: &MouseUpEvent, phase, cx| { + if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) { + if let Some(drag_state_type) = + cx.active_drag.as_ref().map(|drag| drag.state_type) + { + for (drop_state_type, listener) in &drop_listeners { + if *drop_state_type == drag_state_type { + let drag = cx + .active_drag + .take() + .expect("checked for type drag state type above"); + listener(view, drag.state, cx); + cx.notify(); + cx.stop_propagation(); + } + } + } + } + }); + } + if let Some(stateful) = self.as_stateful() { let click_listeners = stateful.click_listeners.clone(); let drag_listener = stateful.drag_listener.clone(); @@ -544,10 +586,11 @@ pub trait ElementInteraction: 'static + Send + Sync { && (event.position - mouse_down.position).magnitude() > DRAG_THRESHOLD { - 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); + let cursor_offset = event.position - bounds.origin; + let drag = drag_listener(view_state, cursor_offset, cx); + cx.active_drag = Some(drag); + cx.notify(); cx.stop_propagation(); } }); @@ -564,10 +607,6 @@ pub trait ElementInteraction: 'static + Send + Sync { listener(view_state, &mouse_click, cx); } } - - if cx.active_drag.is_some() { - cx.end_drag(); - } *pending_mouse_down.lock() = None; }); } else { @@ -690,6 +729,8 @@ where } } +type DropListener = dyn Fn(&mut V, AnyBox, &mut ViewContext) + Send + Sync; + pub struct StatelessInteraction { pub dispatch_context: DispatchContext, pub mouse_down_listeners: SmallVec<[MouseDownListener; 2]>, @@ -701,6 +742,7 @@ pub struct StatelessInteraction { pub group_hover_style: Option, drag_over_styles: SmallVec<[(TypeId, StyleRefinement); 2]>, group_drag_over_styles: SmallVec<[(TypeId, GroupStyle); 2]>, + drop_listeners: SmallVec<[(TypeId, Arc>); 2]>, } impl StatelessInteraction @@ -793,6 +835,7 @@ impl Default for StatelessInteraction { group_hover_style: None, drag_over_styles: SmallVec::new(), group_drag_over_styles: SmallVec::new(), + drop_listeners: SmallVec::new(), } } } diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index c54103b2a0aad9b87a50686e73cc4df15ebe6efe..a92530c6e72afc1e4d1e5cd32dfe5ca72746710d 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -3,11 +3,11 @@ use crate::{ BorrowAppContext, Bounds, BoxShadow, Context, Corners, DevicePixels, DispatchContext, DisplayId, Edges, Effect, Element, EntityId, EventEmitter, FocusEvent, FontId, GlobalElementId, GlyphId, Handle, Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch, KeyMatcher, - Keystroke, LayoutId, MainThread, MainThreadOnly, MonochromeSprite, MouseMoveEvent, Path, - Pixels, Platform, PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Quad, Reference, - RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, - SharedString, Size, Style, Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, - WeakHandle, WindowOptions, SUBPIXEL_VARIANTS, + Keystroke, LayoutId, MainThread, MainThreadOnly, MonochromeSprite, MouseMoveEvent, + MouseUpEvent, Path, Pixels, Platform, PlatformAtlas, PlatformWindow, Point, PolychromeSprite, + Quad, Reference, RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, + SceneBuilder, Shadow, SharedString, Size, Style, Subscription, TaffyLayoutEngine, Task, + Underline, UnderlineStyle, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS, }; use anyhow::Result; use collections::HashMap; @@ -925,6 +925,12 @@ impl<'a, 'w> WindowContext<'a, 'w> { } } + if self.app.propagate_event + && any_mouse_event.downcast_ref::().is_some() + { + self.active_drag = None; + } + // Just in case any handlers added new handlers, which is weird, but possible. handlers.extend( self.window @@ -1626,16 +1632,6 @@ impl<'a, 'w, V: Send + Sync + 'static> ViewContext<'a, 'w, V> { }) }); } - - pub(crate) fn start_drag(&mut self, drag: AnyDrag) { - self.app.active_drag = Some(drag); - self.notify(); - } - - pub(crate) fn end_drag(&mut self) { - self.app.active_drag = None; - self.notify(); - } } impl<'a, 'w, V: EventEmitter + Send + Sync + 'static> ViewContext<'a, 'w, V> { diff --git a/crates/ui2/src/components/tab.rs b/crates/ui2/src/components/tab.rs index 1f37286f94e513edc239ba57e37fa580f503d4b4..c13d4e5637cd4254caf26f57f38dc3de2a371e5b 100644 --- a/crates/ui2/src/components/tab.rs +++ b/crates/ui2/src/components/tab.rs @@ -17,7 +17,7 @@ pub struct Tab { close_side: IconSide, } -#[derive(Clone)] +#[derive(Clone, Debug)] struct TabDragState { title: String, } @@ -126,6 +126,9 @@ impl Tab { Drag::new(drag_state.clone(), |view, cx| div().w_8().h_4().bg(red())) }) .drag_over::(|d| d.bg(black())) + .on_drop(|_view, state: TabDragState, cx| { + dbg!(state); + }) .px_2() .py_0p5() .flex()