diff --git a/crates/gpui2/src/color.rs b/crates/gpui2/src/color.rs index d05caba1a6..f6ea2549ec 100644 --- a/crates/gpui2/src/color.rs +++ b/crates/gpui2/src/color.rs @@ -155,6 +155,15 @@ pub fn white() -> Hsla { } } +pub fn red() -> Hsla { + Hsla { + h: 0., + s: 1., + l: 0.5, + a: 1., + } +} + impl Hsla { /// Returns true if the HSLA color is fully transparent, false otherwise. pub fn is_transparent(&self) -> bool { diff --git a/crates/gpui2/src/elements/div.rs b/crates/gpui2/src/elements/div.rs index 08551eaf70..1eebbbe5db 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; @@ -298,7 +298,7 @@ where style.apply_text_style(cx, |cx| { style.apply_overflow(bounds, cx, |cx| { let scroll_offset = element_state.interactive.scroll_offset(); - cx.with_scroll_offset(scroll_offset, |cx| { + cx.with_element_offset(scroll_offset, |cx| { for child in &mut this.children { child.paint(view_state, cx); } diff --git a/crates/gpui2/src/geometry.rs b/crates/gpui2/src/geometry.rs index 08602352ee..923bb0bf9f 100644 --- a/crates/gpui2/src/geometry.rs +++ b/crates/gpui2/src/geometry.rs @@ -7,7 +7,9 @@ use std::{ ops::{Add, Div, Mul, MulAssign, Sub}, }; -#[derive(Refineable, Default, Add, AddAssign, Sub, SubAssign, Copy, Debug, PartialEq, Eq, Hash)] +#[derive( + Refineable, Default, Add, AddAssign, Sub, SubAssign, Copy, Debug, PartialEq, Eq, Hash, Neg, +)] #[refineable(debug)] #[repr(C)] pub struct Point { diff --git a/crates/gpui2/src/interactive.rs b/crates/gpui2/src/interactive.rs index 2e7c7649ff..aeaa5b959a 100644 --- a/crates/gpui2/src/interactive.rs +++ b/crates/gpui2/src/interactive.rs @@ -464,10 +464,17 @@ 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 { - cx.on_mouse_event(move |view_state, _: &MouseMoveEvent, phase, cx| { - if phase == DispatchPhase::Bubble { + cx.on_mouse_event(move |view_state, event: &MouseMoveEvent, phase, cx| { + if cx.active_drag.is_some() { + if phase == DispatchPhase::Capture { + cx.notify(); + } + } else if phase == DispatchPhase::Bubble + && bounds.contains_point(&event.position) + { let any_drag = drag_listener(view_state, cx); cx.start_drag(any_drag); + cx.stop_propagation(); } }); } @@ -484,7 +491,9 @@ pub trait ElementInteraction: 'static + Send + Sync { } } - cx.end_drag(); + if cx.active_drag.is_some() { + cx.end_drag(); + } *pending_mouse_down.lock() = None; }); } else { diff --git a/crates/gpui2/src/platform/mac/events.rs b/crates/gpui2/src/platform/mac/events.rs index c40c665481..916b9a18be 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, }; - dbg!(window_height.map(|window_height| { + 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/taffy.rs b/crates/gpui2/src/taffy.rs index 71844bb8a6..cee0f7592f 100644 --- a/crates/gpui2/src/taffy.rs +++ b/crates/gpui2/src/taffy.rs @@ -129,7 +129,7 @@ impl TaffyLayoutEngine { self.taffy .compute_layout(id.into(), available_space.into()) .expect(EXPECT_MESSAGE); - println!("compute_layout took {:?}", started_at.elapsed()); + // println!("compute_layout took {:?}", started_at.elapsed()); } pub fn layout_bounds(&mut self, id: LayoutId) -> Bounds { diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index 966d25b7e2..0211c8d9a1 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -159,7 +159,7 @@ pub struct Window { key_matchers: HashMap, z_index_stack: StackingOrder, content_mask_stack: Vec>, - scroll_offset_stack: Vec>, + element_offset_stack: Vec>, mouse_listeners: HashMap>, key_dispatch_stack: Vec, freeze_key_dispatch_stack: bool, @@ -177,7 +177,7 @@ pub struct Window { } impl Window { - pub fn new( + pub(crate) fn new( handle: AnyWindowHandle, options: WindowOptions, cx: &mut MainThread, @@ -234,7 +234,7 @@ impl Window { key_matchers: HashMap::default(), z_index_stack: StackingOrder(SmallVec::new()), content_mask_stack: Vec::new(), - scroll_offset_stack: Vec::new(), + element_offset_stack: Vec::new(), mouse_listeners: HashMap::default(), key_dispatch_stack: Vec::new(), freeze_key_dispatch_stack: false, @@ -469,7 +469,7 @@ impl<'a, 'w> WindowContext<'a, 'w> { .layout_engine .layout_bounds(layout_id) .map(Into::into); - bounds.origin -= self.scroll_offset(); + bounds.origin -= self.element_offset(); bounds } @@ -805,14 +805,22 @@ impl<'a, 'w> WindowContext<'a, 'w> { let mut root_view = cx.window.root_view.take().unwrap(); - if let Some(element_id) = root_view.id() { - cx.with_element_state(element_id, |element_state, cx| { - let element_state = draw_with_element_state(&mut root_view, element_state, cx); - ((), element_state) + cx.stack(0, |cx| { + let available_space = cx.window.content_size.map(Into::into); + draw_any_view(&mut root_view, available_space, cx); + }); + + if let Some(mut active_drag) = cx.active_drag.take() { + cx.stack(1, |cx| { + let mouse_position = -cx.mouse_position(); + cx.with_element_offset(Some(mouse_position), |cx| { + let available_space = + size(AvailableSpace::MinContent, AvailableSpace::MinContent); + draw_any_view(&mut active_drag.drag_handle_view, available_space, cx); + cx.active_drag = Some(active_drag); + }); }); - } else { - draw_with_element_state(&mut root_view, None, cx); - }; + } cx.window.root_view = Some(root_view); let scene = cx.window.scene_builder.build(); @@ -827,20 +835,21 @@ impl<'a, 'w> WindowContext<'a, 'w> { .detach(); }); - fn draw_with_element_state( - root_view: &mut AnyView, - element_state: Option, + fn draw_any_view( + view: &mut AnyView, + available_space: Size, cx: &mut ViewContext<()>, - ) -> AnyBox { - let mut element_state = root_view.initialize(&mut (), element_state, cx); - let layout_id = root_view.layout(&mut (), &mut element_state, cx); - let available_space = cx.window.content_size.map(Into::into); - cx.window - .layout_engine - .compute_layout(layout_id, available_space); - let bounds = cx.window.layout_engine.layout_bounds(layout_id); - root_view.paint(bounds, &mut (), &mut element_state, cx); - element_state + ) { + cx.with_optional_element_state(view.id(), |element_state, cx| { + let mut element_state = view.initialize(&mut (), element_state, cx); + let layout_id = view.layout(&mut (), &mut element_state, cx); + cx.window + .layout_engine + .compute_layout(layout_id, available_space); + let bounds = cx.window.layout_engine.layout_bounds(layout_id); + view.paint(bounds, &mut (), &mut element_state, cx); + ((), element_state) + }); } } @@ -1209,7 +1218,7 @@ pub trait BorrowWindow: BorrowAppContext { result } - fn with_scroll_offset( + fn with_element_offset( &mut self, offset: Option>, f: impl FnOnce(&mut Self) -> R, @@ -1218,16 +1227,16 @@ pub trait BorrowWindow: BorrowAppContext { return f(self); }; - let offset = self.scroll_offset() + offset; - self.window_mut().scroll_offset_stack.push(offset); + let offset = self.element_offset() + offset; + self.window_mut().element_offset_stack.push(offset); let result = f(self); - self.window_mut().scroll_offset_stack.pop(); + self.window_mut().element_offset_stack.pop(); result } - fn scroll_offset(&self) -> Point { + fn element_offset(&self) -> Point { self.window() - .scroll_offset_stack + .element_offset_stack .last() .copied() .unwrap_or_default() @@ -1266,6 +1275,18 @@ pub trait BorrowWindow: BorrowAppContext { }) } + fn with_optional_element_state( + &mut self, + element_id: Option, + f: impl FnOnce(Option, &mut Self) -> (R, S), + ) -> R { + if let Some(element_id) = element_id { + self.with_element_state(element_id, f) + } else { + f(None, self).0 + } + } + fn content_mask(&self) -> ContentMask { self.window() .content_mask_stack @@ -1608,10 +1629,12 @@ 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(); } } diff --git a/crates/ui2/src/components/tab.rs b/crates/ui2/src/components/tab.rs index c404b7e9ac..17157f506c 100644 --- a/crates/ui2/src/components/tab.rs +++ b/crates/ui2/src/components/tab.rs @@ -17,6 +17,7 @@ pub struct Tab { close_side: IconSide, } +#[derive(Clone)] struct TabDragState { title: String, } @@ -115,14 +116,15 @@ impl Tab { ), }; + let drag_state = TabDragState { + title: self.title.clone(), + }; + div() .id(self.id.clone()) - // .on_drag(|_view, _cx| Drag { - // element: div().w_8().h_4().bg(black()), - // state: TabDragState { - // title: self.title.clone(), - // }, - // }) + .on_drag(move |_view, _cx| { + Drag::new(drag_state.clone(), |view, cx| div().w_8().h_4().bg(red())) + }) .px_2() .py_0p5() .flex() @@ -158,7 +160,7 @@ impl Tab { } } -use gpui2::{black, ElementId}; +use gpui2::{red, Drag, ElementId}; #[cfg(feature = "stories")] pub use stories::*;