From 96f2c4a9de2f9673e30ac651a4203ac40b54e153 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Mon, 23 Oct 2023 14:15:12 +0200 Subject: [PATCH] Checkpoint --- crates/gpui2/src/app.rs | 18 ++- crates/gpui2/src/elements/div.rs | 12 +- crates/gpui2/src/elements/img.rs | 4 +- crates/gpui2/src/elements/svg.rs | 4 +- crates/gpui2/src/interactive.rs | 163 ++++++++++++++++++------ crates/gpui2/src/platform/mac/events.rs | 4 +- crates/gpui2/src/window.rs | 10 +- crates/ui2/src/components/tab.rs | 12 +- 8 files changed, 170 insertions(+), 57 deletions(-) diff --git a/crates/gpui2/src/app.rs b/crates/gpui2/src/app.rs index 1e4852ff4d462e967dc2bbd0b5aa373bdd962225..89abf277d3ca559dad685a212fac9bdc60ea2dee 100644 --- a/crates/gpui2/src/app.rs +++ b/crates/gpui2/src/app.rs @@ -9,11 +9,11 @@ use refineable::Refineable; use smallvec::SmallVec; use crate::{ - current_platform, image_cache::ImageCache, Action, AppMetadata, AssetSource, Context, - DispatchPhase, DisplayId, Executor, FocusEvent, FocusHandle, FocusId, KeyBinding, Keymap, - LayoutId, MainThread, MainThreadOnly, Platform, SharedString, SubscriberSet, Subscription, - SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, Window, WindowContext, - WindowHandle, WindowId, + current_platform, image_cache::ImageCache, Action, AnyBox, AnyView, AppMetadata, AssetSource, + Context, DispatchPhase, DisplayId, Executor, FocusEvent, FocusHandle, FocusId, KeyBinding, + Keymap, LayoutId, MainThread, MainThreadOnly, Platform, SharedString, SubscriberSet, + Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, Window, + WindowContext, WindowHandle, WindowId, }; use anyhow::{anyhow, Result}; use collections::{HashMap, HashSet, VecDeque}; @@ -91,6 +91,7 @@ impl App { global_observers: SubscriberSet::new(), layout_id_buffer: Default::default(), propagate_event: true, + active_drag: None, }) })) } @@ -168,6 +169,7 @@ pub struct AppContext { text_system: Arc, flushing_effects: bool, pending_updates: usize, + pub(crate) active_drag: Option, pub(crate) next_frame_callbacks: HashMap>, pub(crate) executor: Executor, pub(crate) svg_renderer: SvgRenderer, @@ -732,6 +734,12 @@ pub(crate) enum Effect { }, } +pub(crate) struct AnyDrag { + pub drag_handle_view: AnyView, + pub state: AnyBox, + pub state_type: TypeId, +} + #[cfg(test)] mod tests { use super::AppContext; diff --git a/crates/gpui2/src/elements/div.rs b/crates/gpui2/src/elements/div.rs index e955309112071a863ff1386bba9857b48b6442ab..08551eaf70b0783da3586a4c4c35dab9d139c4ca 100644 --- a/crates/gpui2/src/elements/div.rs +++ b/crates/gpui2/src/elements/div.rs @@ -1,9 +1,9 @@ use crate::{ - point, AnyElement, BorrowWindow, Bounds, Element, ElementFocus, ElementId, ElementInteraction, - FocusDisabled, FocusEnabled, FocusHandle, FocusListeners, Focusable, GlobalElementId, - GroupBounds, InteractiveElementState, IntoAnyElement, LayoutId, Overflow, ParentElement, - Pixels, Point, SharedString, StatefulInteraction, StatefulInteractive, StatelessInteraction, - StatelessInteractive, Style, StyleRefinement, Styled, ViewContext, + point, AnyElement, BorrowWindow, Bounds, Element, ElementFocus, ElementId, + ElementInteraction, FocusDisabled, FocusEnabled, FocusHandle, FocusListeners, Focusable, + GlobalElementId, GroupBounds, InteractiveElementState, IntoAnyElement, LayoutId, Overflow, + ParentElement, Pixels, Point, SharedString, StatefulInteraction, StatefulInteractive, + StatelessInteraction, StatelessInteractive, Style, StyleRefinement, Styled, ViewContext, }; use refineable::Refineable; use smallvec::SmallVec; @@ -364,7 +364,7 @@ where F: ElementFocus, V: 'static + Send + Sync, { - fn stateful_interactivity(&mut self) -> &mut StatefulInteraction { + fn stateful_interaction(&mut self) -> &mut StatefulInteraction { &mut self.interaction } } diff --git a/crates/gpui2/src/elements/img.rs b/crates/gpui2/src/elements/img.rs index 50e9e29fc58f2fb79ba6ca43d1be8cf752d8b0ae..7c50a68def7bdb3b07167757ca9025811f309ac5 100644 --- a/crates/gpui2/src/elements/img.rs +++ b/crates/gpui2/src/elements/img.rs @@ -166,8 +166,8 @@ where V: 'static + Send + Sync, F: ElementFocus, { - fn stateful_interactivity(&mut self) -> &mut StatefulInteraction { - self.base.stateful_interactivity() + fn stateful_interaction(&mut self) -> &mut StatefulInteraction { + self.base.stateful_interaction() } } diff --git a/crates/gpui2/src/elements/svg.rs b/crates/gpui2/src/elements/svg.rs index fd55296a3a6aff6ac221614685154c16273b949d..35e6e143a461eef1617d0b5892ad14c4e7bbe952 100644 --- a/crates/gpui2/src/elements/svg.rs +++ b/crates/gpui2/src/elements/svg.rs @@ -140,8 +140,8 @@ where V: 'static + Send + Sync, F: ElementFocus, { - fn stateful_interactivity(&mut self) -> &mut StatefulInteraction { - self.base.stateful_interactivity() + fn stateful_interaction(&mut self) -> &mut StatefulInteraction { + self.base.stateful_interaction() } } diff --git a/crates/gpui2/src/interactive.rs b/crates/gpui2/src/interactive.rs index 475cbb75a470427b5c33f3356c6f6f0cbb59364f..2e7c7649ff0f2ddd2ee8e6386150bcfc555b6be1 100644 --- a/crates/gpui2/src/interactive.rs +++ b/crates/gpui2/src/interactive.rs @@ -1,7 +1,7 @@ use crate::{ - point, px, Action, AppContext, BorrowWindow, Bounds, DispatchContext, DispatchPhase, Element, - ElementId, FocusHandle, KeyMatch, Keystroke, Modifiers, Overflow, Pixels, Point, SharedString, - Size, Style, StyleRefinement, ViewContext, + point, px, view, Action, AnyDrag, AppContext, BorrowWindow, Bounds, DispatchContext, + DispatchPhase, Element, ElementId, FocusHandle, KeyMatch, Keystroke, Modifiers, Overflow, + Pixels, Point, SharedString, Size, Style, StyleRefinement, ViewContext, }; use collections::HashMap; use derive_more::{Deref, DerefMut}; @@ -11,6 +11,7 @@ use smallvec::SmallVec; use std::{ any::{Any, TypeId}, fmt::Debug, + marker::PhantomData, ops::Deref, sync::Arc, }; @@ -257,13 +258,13 @@ pub trait StatelessInteractive: Element { } pub trait StatefulInteractive: StatelessInteractive { - fn stateful_interactivity(&mut self) -> &mut StatefulInteraction; + fn stateful_interaction(&mut self) -> &mut StatefulInteraction; fn active(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self where Self: Sized, { - self.stateful_interactivity().active_style = f(StyleRefinement::default()); + self.stateful_interaction().active_style = f(StyleRefinement::default()); self } @@ -275,7 +276,7 @@ pub trait StatefulInteractive: StatelessInteractive { where Self: Sized, { - self.stateful_interactivity().group_active_style = Some(GroupStyle { + self.stateful_interaction().group_active_style = Some(GroupStyle { group: group_name.into(), style: f(StyleRefinement::default()), }); @@ -284,7 +285,7 @@ pub trait StatefulInteractive: StatelessInteractive { fn on_click( mut self, - handler: impl Fn(&mut Self::ViewState, &MouseClickEvent, &mut ViewContext) + listener: impl Fn(&mut Self::ViewState, &ClickEvent, &mut ViewContext) + Send + Sync + 'static, @@ -292,9 +293,45 @@ pub trait StatefulInteractive: StatelessInteractive { where Self: Sized, { - self.stateful_interactivity() - .mouse_click_listeners - .push(Arc::new(move |view, event, cx| handler(view, event, cx))); + self.stateful_interaction() + .click_listeners + .push(Arc::new(move |view, event, cx| listener(view, event, cx))); + self + } + + fn on_drag( + mut self, + listener: impl Fn( + &mut Self::ViewState, + &mut ViewContext, + ) -> Drag + + Send + + Sync + + 'static, + ) -> Self + where + Self: Sized, + S: 'static + Send + Sync, + R: 'static + Fn(&mut Self::ViewState, &mut ViewContext) -> E + Send + Sync, + E: Element, + { + debug_assert!( + self.stateful_interaction().drag_listener.is_none(), + "calling on_drag more than once on the same element is not supported" + ); + self.stateful_interaction().drag_listener = Some(Arc::new(move |view_state, cx| { + let drag = listener(view_state, cx); + let view_handle = cx.handle().upgrade().unwrap(); + let drag_handle_view = view(view_handle, move |view_state, cx| { + (drag.render_drag_handle)(view_state, cx) + }) + .into_any(); + AnyDrag { + drag_handle_view, + state: Box::new(drag.state), + state_type: TypeId::of::(), + } + })); self } } @@ -419,30 +456,45 @@ pub trait ElementInteraction: 'static + Send + Sync { } if let Some(stateful) = self.as_stateful() { - let click_listeners = stateful.mouse_click_listeners.clone(); + let click_listeners = stateful.click_listeners.clone(); + let drag_listener = stateful.drag_listener.clone(); + + if !click_listeners.is_empty() || drag_listener.is_some() { + let pending_mouse_down = element_state.pending_mouse_down.clone(); + let mouse_down = pending_mouse_down.lock().clone(); + if let Some(mouse_down) = mouse_down { + if let Some(drag_listener) = drag_listener { + cx.on_mouse_event(move |view_state, _: &MouseMoveEvent, phase, cx| { + if phase == DispatchPhase::Bubble { + let any_drag = drag_listener(view_state, cx); + cx.start_drag(any_drag); + } + }); + } - let pending_click = element_state.pending_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, cx); + cx.on_mouse_event(move |view_state, event: &MouseUpEvent, phase, cx| { + if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) + { + let mouse_click = ClickEvent { + down: mouse_down.clone(), + up: event.clone(), + }; + for listener in &click_listeners { + listener(view_state, &mouse_click, 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()); - } - }); + cx.end_drag(); + *pending_mouse_down.lock() = None; + }); + } else { + cx.on_mouse_event(move |_state, event: &MouseDownEvent, phase, _cx| { + if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) + { + *pending_mouse_down.lock() = Some(event.clone()); + } + }); + } } let active_state = element_state.active_state.clone(); @@ -526,7 +578,8 @@ pub struct StatefulInteraction { #[deref] #[deref_mut] stateless: StatelessInteraction, - pub mouse_click_listeners: SmallVec<[MouseClickListener; 2]>, + pub click_listeners: SmallVec<[ClickListener; 2]>, + pub(crate) drag_listener: Option>, pub active_style: StyleRefinement, pub group_active_style: Option, } @@ -560,7 +613,8 @@ where Self { id, stateless: StatelessInteraction::default(), - mouse_click_listeners: SmallVec::new(), + click_listeners: SmallVec::new(), + drag_listener: None, active_style: StyleRefinement::default(), group_active_style: None, } @@ -586,7 +640,8 @@ where StatefulInteraction { id: id.into(), stateless: self, - mouse_click_listeners: SmallVec::new(), + click_listeners: SmallVec::new(), + drag_listener: None, active_style: StyleRefinement::default(), group_active_style: None, } @@ -642,7 +697,7 @@ impl ActiveState { #[derive(Default)] pub struct InteractiveElementState { active_state: Arc>, - pending_click: Arc>>, + pending_mouse_down: Arc>>, scroll_offset: Option>>>, } @@ -740,11 +795,39 @@ pub struct MouseUpEvent { } #[derive(Clone, Debug, Default)] -pub struct MouseClickEvent { +pub struct ClickEvent { pub down: MouseDownEvent, pub up: MouseUpEvent, } +pub struct Drag +where + S: 'static + Send + Sync, + R: Fn(&mut V, &mut ViewContext) -> E, + V: 'static + Send + Sync, + E: Element, +{ + pub state: S, + pub render_drag_handle: R, + view_type: PhantomData, +} + +impl Drag +where + S: 'static + Send + Sync, + R: Fn(&mut V, &mut ViewContext) -> E + Send + Sync, + V: 'static + Send + Sync, + E: Element, +{ + pub fn new(state: S, render_drag_handle: R) -> Self { + Drag { + state, + render_drag_handle, + view_type: PhantomData, + } + } +} + #[derive(Hash, PartialEq, Eq, Copy, Clone, Debug)] pub enum MouseButton { Left, @@ -919,8 +1002,6 @@ pub type MouseUpListener = Arc< + Sync + 'static, >; -pub type MouseClickListener = - Arc) + Send + Sync + 'static>; pub type MouseMoveListener = Arc< dyn Fn(&mut V, &MouseMoveEvent, &Bounds, DispatchPhase, &mut ViewContext) @@ -936,6 +1017,12 @@ pub type ScrollWheelListener = Arc< + 'static, >; +pub type ClickListener = + Arc) + Send + Sync + 'static>; + +pub(crate) type DragListener = + Arc) -> AnyDrag + Send + Sync + 'static>; + pub type KeyListener = Arc< dyn Fn( &mut V, diff --git a/crates/gpui2/src/platform/mac/events.rs b/crates/gpui2/src/platform/mac/events.rs index 916b9a18bebc3b1c4e07a98a9c045efbb2917c9b..c40c665481da950d0ee2f06a876ed4c04688f01d 100644 --- a/crates/gpui2/src/platform/mac/events.rs +++ b/crates/gpui2/src/platform/mac/events.rs @@ -201,7 +201,7 @@ impl InputEvent { _ => return None, }; - window_height.map(|window_height| { + dbg!(window_height.map(|window_height| { Self::MouseMoved(MouseMoveEvent { pressed_button: Some(pressed_button), position: point( @@ -210,7 +210,7 @@ impl InputEvent { ), modifiers: read_modifiers(native_event), }) - }) + })) } NSEventType::NSMouseMoved => window_height.map(|window_height| { Self::MouseMoved(MouseMoveEvent { diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index cfd800fbe1736e1edcab1ec408814d8cb11f11cf..966d25b7e2fd12ed6bb9804dec164c83bc0c72c0 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -1,5 +1,5 @@ use crate::{ - px, size, Action, AnyBox, AnyView, AppContext, AsyncWindowContext, AvailableSpace, + px, size, Action, AnyBox, AnyDrag, AnyView, AppContext, AsyncWindowContext, AvailableSpace, 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, @@ -1605,6 +1605,14 @@ 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); + } + + pub(crate) fn end_drag(&mut self) { + self.app.active_drag = None; + } } 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 2d787e6f025347f1498f629edce5dc4b4652e211..c404b7e9acce2872b6ec6ac472453521622bdd5b 100644 --- a/crates/ui2/src/components/tab.rs +++ b/crates/ui2/src/components/tab.rs @@ -17,6 +17,10 @@ pub struct Tab { close_side: IconSide, } +struct TabDragState { + title: String, +} + impl Tab { pub fn new(id: impl Into) -> Self { Self { @@ -113,6 +117,12 @@ impl Tab { div() .id(self.id.clone()) + // .on_drag(|_view, _cx| Drag { + // element: div().w_8().h_4().bg(black()), + // state: TabDragState { + // title: self.title.clone(), + // }, + // }) .px_2() .py_0p5() .flex() @@ -148,7 +158,7 @@ impl Tab { } } -use gpui2::ElementId; +use gpui2::{black, ElementId}; #[cfg(feature = "stories")] pub use stories::*;