From b9f98196372251ba8a2e0d930294fd5bc324c6db Mon Sep 17 00:00:00 2001 From: Julia Date: Fri, 21 Oct 2022 15:09:14 -0400 Subject: [PATCH 01/12] Handle tab drag end on pane items to insert after active item Co-Authored-By: Kay Simmons --- .../gpui/src/elements/mouse_event_handler.rs | 38 ++++++++++--------- crates/gpui/src/elements/stack.rs | 6 +-- crates/gpui/src/presenter.rs | 10 +++++ crates/workspace/src/dock.rs | 1 + crates/workspace/src/pane.rs | 35 +++++++++++++++-- 5 files changed, 66 insertions(+), 24 deletions(-) diff --git a/crates/gpui/src/elements/mouse_event_handler.rs b/crates/gpui/src/elements/mouse_event_handler.rs index c8ba330e70324fb8c11df4469810f19b38834d2b..b3b18ec3d012ed36b0fb0ede56e712fc44749edd 100644 --- a/crates/gpui/src/elements/mouse_event_handler.rs +++ b/crates/gpui/src/elements/mouse_event_handler.rs @@ -172,26 +172,28 @@ impl Element for MouseEventHandler { ) -> Self::PaintState { let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default(); let hit_bounds = self.hit_bounds(visible_bounds); - if let Some(style) = self.cursor_style { - cx.scene.push_cursor_region(CursorRegion { - bounds: hit_bounds, - style, - }); - } - - cx.scene.push_mouse_region( - MouseRegion::from_handlers::( - cx.current_view_id(), - self.region_id, - hit_bounds, - self.handlers.clone(), - ) - .with_hoverable(self.hoverable) - .with_notify_on_hover(self.notify_on_hover) - .with_notify_on_click(self.notify_on_click), - ); self.child.paint(bounds.origin(), visible_bounds, cx); + + cx.paint_stacking_context(None, |cx| { + if let Some(style) = self.cursor_style { + cx.scene.push_cursor_region(CursorRegion { + bounds: hit_bounds, + style, + }); + } + cx.scene.push_mouse_region( + MouseRegion::from_handlers::( + cx.current_view_id(), + self.region_id, + hit_bounds, + self.handlers.clone(), + ) + .with_hoverable(self.hoverable) + .with_notify_on_hover(self.notify_on_hover) + .with_notify_on_click(self.notify_on_click), + ); + }); } fn rect_for_text_range( diff --git a/crates/gpui/src/elements/stack.rs b/crates/gpui/src/elements/stack.rs index f08ce04649a4219eba27040c7e9e43dbfebde2f8..92a41749b366214c337e22fb1711a575298f2fbc 100644 --- a/crates/gpui/src/elements/stack.rs +++ b/crates/gpui/src/elements/stack.rs @@ -42,9 +42,9 @@ impl Element for Stack { cx: &mut PaintContext, ) -> Self::PaintState { for child in &mut self.children { - cx.scene.push_layer(None); - child.paint(bounds.origin(), visible_bounds, cx); - cx.scene.pop_layer(); + cx.paint_layer(None, |cx| { + child.paint(bounds.origin(), visible_bounds, cx); + }); } } diff --git a/crates/gpui/src/presenter.rs b/crates/gpui/src/presenter.rs index 74c2dddd662c9f733ecc2a456fadc473e52de9c9..e1fa1349da5de89459d13f35cf30be51e2fb8184 100644 --- a/crates/gpui/src/presenter.rs +++ b/crates/gpui/src/presenter.rs @@ -707,6 +707,16 @@ impl<'a> PaintContext<'a> { } } + #[inline] + pub fn paint_stacking_context(&mut self, clip_bounds: Option, f: F) + where + F: FnOnce(&mut Self), + { + self.scene.push_stacking_context(clip_bounds); + f(self); + self.scene.pop_stacking_context(); + } + #[inline] pub fn paint_layer(&mut self, clip_bounds: Option, f: F) where diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index cf6c8e287b397cdf38e00f28baa1d6967c28ecee..09f9cc91b4df95132ee754fddbfdf0a67e1fcb47 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -362,6 +362,7 @@ impl View for ToggleDockButton { } }) .with_cursor_style(CursorStyle::PointingHand); + .on_ if dock_position.is_visible() { button diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 882b501d2e59c3414589713d17c6752a9f69351c..cf5413c692d7555ce9161ea687a8fa9f0aec3233 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -1373,10 +1373,12 @@ impl View for Pane { .with_child( MouseEventHandler::::new(0, cx, |_, cx| { if let Some(active_item) = self.active_item() { + enum PaneContentTabDropTarget {} + Flex::column() .with_child({ let mut tab_row = Flex::row() - .with_child(self.render_tabs(cx).flex(1.0, true).named("tabs")); + .with_child(self.render_tabs(cx).flex(1., true).named("tabs")); // Render pane buttons let theme = cx.global::().theme.clone(); @@ -1440,8 +1442,35 @@ impl View for Pane { .flex(1., false) .named("tab bar") }) - .with_child(ChildView::new(&self.toolbar, cx).expanded().boxed()) - .with_child(ChildView::new(active_item, cx).flex(1., true).boxed()) + .with_child({ + let drop_index = self.active_item_index + 1; + MouseEventHandler::::new( + 0, + cx, + |_, cx| { + Flex::column() + .with_child( + ChildView::new(&self.toolbar, cx) + .expanded() + .boxed(), + ) + .with_child( + ChildView::new(active_item, cx) + .flex(1., true) + .boxed(), + ) + .boxed() + }, + ) + .on_up(MouseButton::Left, { + let pane = cx.handle(); + move |_, cx: &mut EventContext| { + Pane::handle_dropped_item(&pane, drop_index, cx) + } + }) + .flex(1., true) + .boxed() + }) .boxed() } else { enum EmptyPane {} From ba35536664a0e5658d077d9240d567390544426c Mon Sep 17 00:00:00 2001 From: Julia Date: Fri, 21 Oct 2022 15:45:05 -0400 Subject: [PATCH 02/12] Add action to move active item into the dock Co-Authored-By: Kay Simmons --- assets/keymaps/default.json | 6 +++++ crates/workspace/src/dock.rs | 38 +++++++++++++++++++++++++++---- crates/workspace/src/pane.rs | 12 ++++++---- crates/workspace/src/workspace.rs | 10 ++++---- crates/zed/src/zed.rs | 6 ++--- 5 files changed, 56 insertions(+), 16 deletions(-) diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json index 40d486c161f307185c7ceb5077856a0dfebc7c25..b4ce8c1b92ceb9eb29e92a2d6bb44d05202ed279 100644 --- a/assets/keymaps/default.json +++ b/assets/keymaps/default.json @@ -431,6 +431,12 @@ "shift-escape": "dock::HideDock" } }, + { + "context": "Pane", + "bindings": { + "cmd-escape": "dock::MoveActiveItemToDock" + } + }, { "context": "ProjectPanel", "bindings": { diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 09f9cc91b4df95132ee754fddbfdf0a67e1fcb47..aebb12727d87e2568d091ef3f16f6f8a4b5c9fc3 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -24,7 +24,8 @@ actions!( HideDock, AnchorDockRight, AnchorDockBottom, - ExpandDock + ExpandDock, + MoveActiveItemToDock, ] ); impl_internal_actions!(dock, [MoveDock, AddDefaultItemToDock]); @@ -48,6 +49,30 @@ pub fn init(cx: &mut MutableAppContext) { Dock::move_dock(workspace, &MoveDock(DockAnchor::Expanded), cx) }, ); + cx.add_action( + |workspace: &mut Workspace, _: &MoveActiveItemToDock, cx: &mut ViewContext| { + if let Some(active_item) = workspace.active_item(cx) { + let item_id = active_item.id(); + + let from = workspace.active_pane(); + let to = workspace.dock_pane(); + if from.id() == to.id() { + return; + } + + let destination_index = to.read(cx).items_len() + 1; + + Pane::move_item( + workspace, + from.clone(), + to.clone(), + item_id, + destination_index, + cx, + ); + } + }, + ); } #[derive(Copy, Clone, PartialEq, Eq, Debug)] @@ -338,7 +363,8 @@ impl View for ToggleDockButton { return Empty::new().boxed(); } - let dock_position = workspace.unwrap().read(cx).dock.position; + let workspace = workspace.unwrap(); + let dock_position = workspace.read(cx).dock.position; let theme = cx.global::().theme.clone(); let button = MouseEventHandler::::new(0, cx, { @@ -361,8 +387,12 @@ impl View for ToggleDockButton { .boxed() } }) - .with_cursor_style(CursorStyle::PointingHand); - .on_ + .with_cursor_style(CursorStyle::PointingHand) + .on_up(MouseButton::Left, move |_, cx| { + let dock_pane = workspace.read(cx.app).dock_pane(); + let drop_index = dock_pane.read(cx.app).items_len() + 1; + Pane::handle_dropped_item(&dock_pane.downgrade(), drop_index, cx); + }); if dock_position.is_visible() { button diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index cf5413c692d7555ce9161ea687a8fa9f0aec3233..4865b0aa51641120a3eabaec4c0eadbd14bece48 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -575,6 +575,10 @@ impl Pane { } } + pub fn items_len(&self) -> usize { + self.items.len() + } + pub fn items(&self) -> impl Iterator> { self.items.iter() } @@ -943,11 +947,11 @@ impl Pane { } } - fn move_item( + pub fn move_item( workspace: &mut Workspace, from: ViewHandle, to: ViewHandle, - item_to_move: usize, + item_id_to_move: usize, destination_index: usize, cx: &mut ViewContext, ) { @@ -955,7 +959,7 @@ impl Pane { .read(cx) .items() .enumerate() - .find(|(_, item_handle)| item_handle.id() == item_to_move); + .find(|(_, item_handle)| item_handle.id() == item_id_to_move); if item_to_move.is_none() { log::warn!("Tried to move item handle which was not in `from` pane. Maybe tab was closed during drop"); @@ -1321,7 +1325,7 @@ impl Pane { tab.constrained().with_height(tab_style.height).boxed() } - fn handle_dropped_item(pane: &WeakViewHandle, index: usize, cx: &mut EventContext) { + pub fn handle_dropped_item(pane: &WeakViewHandle, index: usize, cx: &mut EventContext) { if let Some((_, dragged_item)) = cx .global::>() .currently_dragged::(cx.window_id) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index e7752219c52db7c1e62bc2ce2138015cb28a20ae..714325a6996e2b8bd1f9694449fa2e51ddfaa149 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -3175,7 +3175,7 @@ mod tests { cx.foreground().run_until_parked(); pane.read_with(cx, |pane, _| { - assert_eq!(pane.items().count(), 4); + assert_eq!(pane.items_len(), 4); assert_eq!(pane.active_item().unwrap().id(), item1.id()); }); @@ -3185,7 +3185,7 @@ mod tests { assert_eq!(item1.read(cx).save_count, 1); assert_eq!(item1.read(cx).save_as_count, 0); assert_eq!(item1.read(cx).reload_count, 0); - assert_eq!(pane.items().count(), 3); + assert_eq!(pane.items_len(), 3); assert_eq!(pane.active_item().unwrap().id(), item3.id()); }); @@ -3195,7 +3195,7 @@ mod tests { assert_eq!(item3.read(cx).save_count, 0); assert_eq!(item3.read(cx).save_as_count, 0); assert_eq!(item3.read(cx).reload_count, 1); - assert_eq!(pane.items().count(), 2); + assert_eq!(pane.items_len(), 2); assert_eq!(pane.active_item().unwrap().id(), item4.id()); }); @@ -3207,7 +3207,7 @@ mod tests { assert_eq!(item4.read(cx).save_count, 0); assert_eq!(item4.read(cx).save_as_count, 1); assert_eq!(item4.read(cx).reload_count, 0); - assert_eq!(pane.items().count(), 1); + assert_eq!(pane.items_len(), 1); assert_eq!(pane.active_item().unwrap().id(), item2.id()); }); } @@ -3309,7 +3309,7 @@ mod tests { cx.foreground().run_until_parked(); close.await.unwrap(); left_pane.read_with(cx, |pane, _| { - assert_eq!(pane.items().count(), 0); + assert_eq!(pane.items_len(), 0); }); } diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 3319aebd09953d89e44a89c0961cd275f7dd6915..ef504026d5027c11e5319812f18db112d66a7533 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -811,7 +811,7 @@ mod tests { pane.active_item().unwrap().project_path(cx), Some(file1.clone()) ); - assert_eq!(pane.items().count(), 1); + assert_eq!(pane.items_len(), 1); }); // Open the second entry @@ -825,7 +825,7 @@ mod tests { pane.active_item().unwrap().project_path(cx), Some(file2.clone()) ); - assert_eq!(pane.items().count(), 2); + assert_eq!(pane.items_len(), 2); }); // Open the first entry again. The existing pane item is activated. @@ -841,7 +841,7 @@ mod tests { pane.active_item().unwrap().project_path(cx), Some(file1.clone()) ); - assert_eq!(pane.items().count(), 2); + assert_eq!(pane.items_len(), 2); }); // Split the pane with the first entry, then open the second entry again. From 70e2951e35d595086cef48aec528d8a20d8c7d64 Mon Sep 17 00:00:00 2001 From: K Simmons Date: Fri, 21 Oct 2022 15:53:43 -0700 Subject: [PATCH 03/12] add mouse region handler bool for adding the handler above the child --- crates/drag_and_drop/src/drag_and_drop.rs | 2 +- crates/editor/src/element.rs | 12 +- crates/gpui/src/elements/flex.rs | 2 +- .../gpui/src/elements/mouse_event_handler.rs | 72 +++-- crates/gpui/src/elements/overlay.rs | 35 ++- crates/gpui/src/elements/uniform_list.rs | 2 +- crates/gpui/src/presenter.rs | 6 +- crates/workspace/src/dock.rs | 39 +-- crates/workspace/src/pane.rs | 246 +++++++++--------- 9 files changed, 225 insertions(+), 191 deletions(-) diff --git a/crates/drag_and_drop/src/drag_and_drop.rs b/crates/drag_and_drop/src/drag_and_drop.rs index 31265a16972267a57b15bc5683d0e1f47c6bb656..bb660c750f5a1676e8802d66f8c82f2a9e62b3b2 100644 --- a/crates/drag_and_drop/src/drag_and_drop.rs +++ b/crates/drag_and_drop/src/drag_and_drop.rs @@ -125,7 +125,7 @@ impl DragAndDrop { cx.defer(|cx| { cx.update_global::(|this, cx| this.stop_dragging(cx)); }); - cx.propogate_event(); + cx.propagate_event(); }) .on_up_out(MouseButton::Left, |_, cx| { cx.defer(|cx| { diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index d8f5c83daffd4805ee089a5717229dbb88855e6b..5628b886fe8ce99ac038490ff0a7587c40cbfec9 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -137,7 +137,7 @@ impl EditorElement { gutter_bounds, cx, ) { - cx.propogate_event(); + cx.propagate_event(); } } }) @@ -150,7 +150,7 @@ impl EditorElement { text_bounds, cx, ) { - cx.propogate_event(); + cx.propagate_event(); } } }) @@ -167,7 +167,7 @@ impl EditorElement { text_bounds, cx, ) { - cx.propogate_event() + cx.propagate_event() } } }) @@ -182,7 +182,7 @@ impl EditorElement { text_bounds, cx, ) { - cx.propogate_event() + cx.propagate_event() } } }) @@ -190,7 +190,7 @@ impl EditorElement { let position_map = position_map.clone(); move |e, cx| { if !Self::mouse_moved(e.platform_event, &position_map, text_bounds, cx) { - cx.propogate_event() + cx.propagate_event() } } }) @@ -199,7 +199,7 @@ impl EditorElement { move |e, cx| { if !Self::scroll(e.position, e.delta, e.precise, &position_map, bounds, cx) { - cx.propogate_event() + cx.propagate_event() } } }), diff --git a/crates/gpui/src/elements/flex.rs b/crates/gpui/src/elements/flex.rs index 95477c75601452f7aa26400d5cc4cb880021deb6..129d36dadd3818e8d215ad4a1e85d5850c9add66 100644 --- a/crates/gpui/src/elements/flex.rs +++ b/crates/gpui/src/elements/flex.rs @@ -277,7 +277,7 @@ impl Element for Flex { cx.notify(); } else { - cx.propogate_event(); + cx.propagate_event(); } } }) diff --git a/crates/gpui/src/elements/mouse_event_handler.rs b/crates/gpui/src/elements/mouse_event_handler.rs index b3b18ec3d012ed36b0fb0ede56e712fc44749edd..01779fb362f061eb961419f85360e1849c72e1a2 100644 --- a/crates/gpui/src/elements/mouse_event_handler.rs +++ b/crates/gpui/src/elements/mouse_event_handler.rs @@ -23,10 +23,14 @@ pub struct MouseEventHandler { hoverable: bool, notify_on_hover: bool, notify_on_click: bool, + above: bool, padding: Padding, _tag: PhantomData, } +// MouseEventHandler::new +// MouseEventHandler::above + impl MouseEventHandler { pub fn new(region_id: usize, cx: &mut RenderContext, render_child: F) -> Self where @@ -45,11 +49,22 @@ impl MouseEventHandler { notify_on_hover, notify_on_click, hoverable: true, + above: false, padding: Default::default(), _tag: PhantomData, } } + pub fn above(region_id: usize, cx: &mut RenderContext, render_child: F) -> Self + where + V: View, + F: FnOnce(&mut MouseState, &mut RenderContext) -> ElementBox, + { + let mut handler = Self::new(region_id, cx, render_child); + handler.above = true; + handler + } + pub fn with_cursor_style(mut self, cursor: CursorStyle) -> Self { self.cursor_style = Some(cursor); self @@ -149,6 +164,29 @@ impl MouseEventHandler { ) .round_out() } + + fn paint_regions(&self, bounds: RectF, visible_bounds: RectF, cx: &mut PaintContext) { + let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default(); + let hit_bounds = self.hit_bounds(visible_bounds); + + if let Some(style) = self.cursor_style { + cx.scene.push_cursor_region(CursorRegion { + bounds: hit_bounds, + style, + }); + } + cx.scene.push_mouse_region( + MouseRegion::from_handlers::( + cx.current_view_id(), + self.region_id, + hit_bounds, + self.handlers.clone(), + ) + .with_hoverable(self.hoverable) + .with_notify_on_hover(self.notify_on_hover) + .with_notify_on_click(self.notify_on_click), + ); + } } impl Element for MouseEventHandler { @@ -170,30 +208,16 @@ impl Element for MouseEventHandler { _: &mut Self::LayoutState, cx: &mut PaintContext, ) -> Self::PaintState { - let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default(); - let hit_bounds = self.hit_bounds(visible_bounds); - - self.child.paint(bounds.origin(), visible_bounds, cx); - - cx.paint_stacking_context(None, |cx| { - if let Some(style) = self.cursor_style { - cx.scene.push_cursor_region(CursorRegion { - bounds: hit_bounds, - style, - }); - } - cx.scene.push_mouse_region( - MouseRegion::from_handlers::( - cx.current_view_id(), - self.region_id, - hit_bounds, - self.handlers.clone(), - ) - .with_hoverable(self.hoverable) - .with_notify_on_hover(self.notify_on_hover) - .with_notify_on_click(self.notify_on_click), - ); - }); + if self.above { + self.child.paint(bounds.origin(), visible_bounds, cx); + + cx.paint_layer(None, |cx| { + self.paint_regions(bounds, visible_bounds, cx); + }); + } else { + self.paint_regions(bounds, visible_bounds, cx); + self.child.paint(bounds.origin(), visible_bounds, cx); + } } fn rect_for_text_range( diff --git a/crates/gpui/src/elements/overlay.rs b/crates/gpui/src/elements/overlay.rs index 253c88f703a36abc30162053fa42b3c586738b6d..15d3d764f2a626d9a2b4e2530e284e44f85ec9f4 100644 --- a/crates/gpui/src/elements/overlay.rs +++ b/crates/gpui/src/elements/overlay.rs @@ -204,25 +204,24 @@ impl Element for Overlay { OverlayFitMode::None => {} } - cx.scene.push_stacking_context(None); - - if self.hoverable { - enum OverlayHoverCapture {} - // Block hovers in lower stacking contexts - cx.scene - .push_mouse_region(MouseRegion::new::( - cx.current_view_id(), - cx.current_view_id(), - bounds, - )); - } + cx.paint_stacking_context(None, |cx| { + if self.hoverable { + enum OverlayHoverCapture {} + // Block hovers in lower stacking contexts + cx.scene + .push_mouse_region(MouseRegion::new::( + cx.current_view_id(), + cx.current_view_id(), + bounds, + )); + } - self.child.paint( - bounds.origin(), - RectF::new(Vector2F::zero(), cx.window_size), - cx, - ); - cx.scene.pop_stacking_context(); + self.child.paint( + bounds.origin(), + RectF::new(Vector2F::zero(), cx.window_size), + cx, + ); + }); } fn rect_for_text_range( diff --git a/crates/gpui/src/elements/uniform_list.rs b/crates/gpui/src/elements/uniform_list.rs index ca3601491ab2a507bef84fdcbcdd53bd72d209b1..0eab7f0bc9baaee74a2090ecdcd4a00be051c83d 100644 --- a/crates/gpui/src/elements/uniform_list.rs +++ b/crates/gpui/src/elements/uniform_list.rs @@ -304,7 +304,7 @@ impl Element for UniformList { }, cx| { if !Self::scroll(state.clone(), position, delta, precise, scroll_max, cx) { - cx.propogate_event(); + cx.propagate_event(); } } }), diff --git a/crates/gpui/src/presenter.rs b/crates/gpui/src/presenter.rs index e1fa1349da5de89459d13f35cf30be51e2fb8184..453ee0d60d12dd2c8319eaf13d5bb2fce911b0b4 100644 --- a/crates/gpui/src/presenter.rs +++ b/crates/gpui/src/presenter.rs @@ -485,9 +485,7 @@ impl Presenter { event_cx.handled = true; event_cx.with_current_view(valid_region.id().view_id(), { let region_event = mouse_event.clone(); - |cx| { - callback(region_event, cx); - } + |cx| callback(region_event, cx) }); } @@ -804,7 +802,7 @@ impl<'a> EventContext<'a> { self.notify_count } - pub fn propogate_event(&mut self) { + pub fn propagate_event(&mut self) { self.handled = false; } } diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index aebb12727d87e2568d091ef3f16f6f8a4b5c9fc3..9b1dec8d2d54d5bdef6efb6d3f6582f964237975 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -1,7 +1,7 @@ use collections::HashMap; use gpui::{ actions, - elements::{ChildView, Container, Empty, MouseEventHandler, Side, Svg}, + elements::{ChildView, Container, Empty, MouseEventHandler, ParentElement, Side, Stack, Svg}, impl_internal_actions, Border, CursorStyle, Element, ElementBox, Entity, MouseButton, MutableAppContext, RenderContext, View, ViewContext, ViewHandle, WeakViewHandle, }; @@ -308,25 +308,34 @@ impl Dock { DockAnchor::Expanded => { enum ExpandedDockWash {} enum ExpandedDockPane {} - Container::new( - MouseEventHandler::::new(0, cx, |_state, cx| { + Stack::new() + .with_child( + // Render wash under the dock which when clicked hides it + MouseEventHandler::::new(0, cx, |_, _| { + Empty::new() + .contained() + .with_background_color(style.wash_color) + .boxed() + }) + .capture_all() + .on_down(MouseButton::Left, |_, cx| { + cx.dispatch_action(HideDock); + }) + .with_cursor_style(CursorStyle::Arrow) + .boxed(), + ) + .with_child( MouseEventHandler::::new(0, cx, |_state, cx| { ChildView::new(&self.pane, cx).boxed() }) + // Make sure all events directly under the dock pane + // are captured .capture_all() .contained() .with_style(style.maximized) - .boxed() - }) - .capture_all() - .on_down(MouseButton::Left, |_, cx| { - cx.dispatch_action(HideDock); - }) - .with_cursor_style(CursorStyle::Arrow) - .boxed(), - ) - .with_background_color(style.wash_color) - .boxed() + .boxed(), + ) + .boxed() } }) } @@ -391,7 +400,7 @@ impl View for ToggleDockButton { .on_up(MouseButton::Left, move |_, cx| { let dock_pane = workspace.read(cx.app).dock_pane(); let drop_index = dock_pane.read(cx.app).items_len() + 1; - Pane::handle_dropped_item(&dock_pane.downgrade(), drop_index, cx); + Pane::handle_dropped_item(&dock_pane.downgrade(), drop_index, false, cx); }); if dock_position.is_visible() { diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 4865b0aa51641120a3eabaec4c0eadbd14bece48..e3ac7a2947706ce75f0b074a067f1be771d80d7a 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -1061,127 +1061,123 @@ impl Pane { enum Tab {} enum Filler {} let pane = cx.handle(); - MouseEventHandler::::new(0, cx, |_, cx| { - let autoscroll = if mem::take(&mut self.autoscroll) { - Some(self.active_item_index) - } else { - None - }; - - let pane_active = self.is_active; - - let mut row = Flex::row().scrollable::(1, autoscroll, cx); - for (ix, (item, detail)) in self - .items - .iter() - .cloned() - .zip(self.tab_details(cx)) - .enumerate() - { - let detail = if detail == 0 { None } else { Some(detail) }; - let tab_active = ix == self.active_item_index; + let autoscroll = if mem::take(&mut self.autoscroll) { + Some(self.active_item_index) + } else { + None + }; - row.add_child({ - MouseEventHandler::::new(ix, cx, { - let item = item.clone(); - let pane = pane.clone(); - let detail = detail.clone(); + let pane_active = self.is_active; + let mut row = Flex::row().scrollable::(1, autoscroll, cx); + for (ix, (item, detail)) in self + .items + .iter() + .cloned() + .zip(self.tab_details(cx)) + .enumerate() + { + let detail = if detail == 0 { None } else { Some(detail) }; + let tab_active = ix == self.active_item_index; + + row.add_child({ + MouseEventHandler::::above(ix, cx, { + let item = item.clone(); + let pane = pane.clone(); + let detail = detail.clone(); + + let theme = cx.global::().theme.clone(); + + move |mouse_state, cx| { + let tab_style = theme.workspace.tab_bar.tab_style(pane_active, tab_active); + let hovered = mouse_state.hovered(); + Self::render_tab( + &item, + pane, + ix == 0, + detail, + hovered, + Self::tab_overlay_color(hovered, theme.as_ref(), cx), + tab_style, + cx, + ) + } + }) + .with_cursor_style(if pane_active && tab_active { + CursorStyle::Arrow + } else { + CursorStyle::PointingHand + }) + .on_down(MouseButton::Left, move |_, cx| { + cx.dispatch_action(ActivateItem(ix)); + cx.propagate_event(); + }) + .on_click(MouseButton::Middle, { + let item = item.clone(); + let pane = pane.clone(); + move |_, cx: &mut EventContext| { + cx.dispatch_action(CloseItem { + item_id: item.id(), + pane: pane.clone(), + }) + } + }) + .on_up(MouseButton::Left, { + let pane = pane.clone(); + move |_, cx: &mut EventContext| Pane::handle_dropped_item(&pane, ix, true, cx) + }) + .as_draggable( + DraggedItem { + item, + pane: pane.clone(), + }, + { let theme = cx.global::().theme.clone(); - move |mouse_state, cx| { - let tab_style = - theme.workspace.tab_bar.tab_style(pane_active, tab_active); - let hovered = mouse_state.hovered(); + let detail = detail.clone(); + move |dragged_item, cx: &mut RenderContext| { + let tab_style = &theme.workspace.tab_bar.dragged_tab; Self::render_tab( - &item, - pane, - ix == 0, + &dragged_item.item, + dragged_item.pane.clone(), + false, detail, - hovered, - Self::tab_overlay_color(hovered, theme.as_ref(), cx), - tab_style, + false, + None, + &tab_style, cx, ) } - }) - .with_cursor_style(if pane_active && tab_active { - CursorStyle::Arrow - } else { - CursorStyle::PointingHand - }) - .on_down(MouseButton::Left, move |_, cx| { - cx.dispatch_action(ActivateItem(ix)); - }) - .on_click(MouseButton::Middle, { - let item = item.clone(); - let pane = pane.clone(); - move |_, cx: &mut EventContext| { - cx.dispatch_action(CloseItem { - item_id: item.id(), - pane: pane.clone(), - }) - } - }) - .on_up(MouseButton::Left, { - let pane = pane.clone(); - move |_, cx: &mut EventContext| Pane::handle_dropped_item(&pane, ix, cx) - }) - .as_draggable( - DraggedItem { - item, - pane: pane.clone(), - }, - { - let theme = cx.global::().theme.clone(); - - let detail = detail.clone(); - move |dragged_item, cx: &mut RenderContext| { - let tab_style = &theme.workspace.tab_bar.dragged_tab; - Self::render_tab( - &dragged_item.item, - dragged_item.pane.clone(), - false, - detail, - false, - None, - &tab_style, - cx, - ) - } - }, - ) - .boxed() - }) - } + }, + ) + .boxed() + }) + } - // Use the inactive tab style along with the current pane's active status to decide how to render - // the filler - let filler_style = theme.workspace.tab_bar.tab_style(pane_active, false); - row.add_child( - MouseEventHandler::::new(0, cx, |mouse_state, cx| { - let mut filler = Empty::new() - .contained() - .with_style(filler_style.container) - .with_border(filler_style.container.border); - - if let Some(overlay) = - Self::tab_overlay_color(mouse_state.hovered(), &theme, cx) - { - filler = filler.with_overlay_color(overlay); - } + // Use the inactive tab style along with the current pane's active status to decide how to render + // the filler + let filler_style = theme.workspace.tab_bar.tab_style(pane_active, false); + row.add_child( + MouseEventHandler::::new(0, cx, |mouse_state, cx| { + let mut filler = Empty::new() + .contained() + .with_style(filler_style.container) + .with_border(filler_style.container.border); + + if let Some(overlay) = Self::tab_overlay_color(mouse_state.hovered(), &theme, cx) { + filler = filler.with_overlay_color(overlay); + } - filler.boxed() - }) - .flex(1., true) - .named("filler"), - ); + filler.boxed() + }) + .on_up(MouseButton::Left, move |_, cx| { + Pane::handle_dropped_item(&pane, filler_index, true, cx) + }) + .flex(1., true) + .named("filler"), + ); - row.boxed() - }) - .on_up(MouseButton::Left, move |_, cx| { - Pane::handle_dropped_item(&pane, filler_index, cx) - }) + row } fn tab_details(&self, cx: &AppContext) -> Vec { @@ -1305,7 +1301,6 @@ impl Pane { }) } }) - .on_click(MouseButton::Middle, |_, cx| cx.propogate_event()) .named("close-tab-icon") } else { Empty::new().boxed() @@ -1325,19 +1320,26 @@ impl Pane { tab.constrained().with_height(tab_style.height).boxed() } - pub fn handle_dropped_item(pane: &WeakViewHandle, index: usize, cx: &mut EventContext) { + pub fn handle_dropped_item( + pane: &WeakViewHandle, + index: usize, + allow_same_pane: bool, + cx: &mut EventContext, + ) { if let Some((_, dragged_item)) = cx .global::>() .currently_dragged::(cx.window_id) { - cx.dispatch_action(MoveItem { - item_id: dragged_item.item.id(), - from: dragged_item.pane.clone(), - to: pane.clone(), - destination_index: index, - }) + if pane != &dragged_item.pane || allow_same_pane { + cx.dispatch_action(MoveItem { + item_id: dragged_item.item.id(), + from: dragged_item.pane.clone(), + to: pane.clone(), + destination_index: index, + }) + } } else { - cx.propogate_event(); + cx.propagate_event(); } } @@ -1448,7 +1450,7 @@ impl View for Pane { }) .with_child({ let drop_index = self.active_item_index + 1; - MouseEventHandler::::new( + MouseEventHandler::::above( 0, cx, |_, cx| { @@ -1469,7 +1471,7 @@ impl View for Pane { .on_up(MouseButton::Left, { let pane = cx.handle(); move |_, cx: &mut EventContext| { - Pane::handle_dropped_item(&pane, drop_index, cx) + Pane::handle_dropped_item(&pane, drop_index, false, cx) } }) .flex(1., true) @@ -1491,7 +1493,9 @@ impl View for Pane { }) .on_up(MouseButton::Left, { let pane = this.clone(); - move |_, cx: &mut EventContext| Pane::handle_dropped_item(&pane, 0, cx) + move |_, cx: &mut EventContext| { + Pane::handle_dropped_item(&pane, 0, true, cx) + } }) .boxed() } From cfde3e348c2fa5b6ee16d0397973cc9a74d1d039 Mon Sep 17 00:00:00 2001 From: K Simmons Date: Fri, 21 Oct 2022 19:14:24 -0700 Subject: [PATCH 04/12] Add pane splitting by dragged item. Works, but the overlay doesn't clear quite right --- crates/gpui/src/app.rs | 18 ++ crates/gpui/src/app/test_app_context.rs | 1 + .../gpui/src/elements/mouse_event_handler.rs | 12 +- crates/gpui/src/presenter.rs | 28 ++- crates/gpui/src/scene/mouse_region.rs | 7 + crates/theme/src/theme.rs | 2 +- crates/workspace/src/dock.rs | 4 +- crates/workspace/src/pane.rs | 214 ++++++++++------ crates/workspace/src/pane_group.rs | 232 ++++-------------- crates/workspace/src/workspace.rs | 56 ++++- styles/src/styleTree/tabBar.ts | 4 - styles/src/styleTree/workspace.ts | 4 + 12 files changed, 305 insertions(+), 277 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 5cbc786b725adce8ab4dc9997990b58c4e1a5197..e7ce384f163b2b05a5c5a9f96e8c8abcb75d085c 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -21,6 +21,7 @@ use std::{ use anyhow::{anyhow, Context, Result}; use lazy_static::lazy_static; use parking_lot::Mutex; +use pathfinder_geometry::vector::Vector2F; use postage::oneshot; use smallvec::SmallVec; use smol::prelude::*; @@ -939,6 +940,7 @@ impl MutableAppContext { window_id, view_id, titlebar_height, + mouse_position: Default::default(), hovered_region_ids: Default::default(), clicked_region_ids: None, refreshing: false, @@ -3895,6 +3897,7 @@ pub struct RenderParams { pub window_id: usize, pub view_id: usize, pub titlebar_height: f32, + pub mouse_position: Vector2F, pub hovered_region_ids: HashSet, pub clicked_region_ids: Option<(HashSet, MouseButton)>, pub refreshing: bool, @@ -3905,6 +3908,7 @@ pub struct RenderContext<'a, T: View> { pub(crate) window_id: usize, pub(crate) view_id: usize, pub(crate) view_type: PhantomData, + pub(crate) mouse_position: Vector2F, pub(crate) hovered_region_ids: HashSet, pub(crate) clicked_region_ids: Option<(HashSet, MouseButton)>, pub app: &'a mut MutableAppContext, @@ -3916,12 +3920,19 @@ pub struct RenderContext<'a, T: View> { #[derive(Clone, Default)] pub struct MouseState { hovered: bool, + mouse_position: Vector2F, clicked: Option, + accessed_mouse_position: bool, accessed_hovered: bool, accessed_clicked: bool, } impl MouseState { + pub fn mouse_position(&mut self) -> Vector2F { + self.accessed_mouse_position = true; + self.mouse_position + } + pub fn hovered(&mut self) -> bool { self.accessed_hovered = true; self.hovered @@ -3932,6 +3943,10 @@ impl MouseState { self.clicked } + pub fn accessed_mouse_position(&self) -> bool { + self.accessed_mouse_position + } + pub fn accessed_hovered(&self) -> bool { self.accessed_hovered } @@ -3949,6 +3964,7 @@ impl<'a, V: View> RenderContext<'a, V> { view_id: params.view_id, view_type: PhantomData, titlebar_height: params.titlebar_height, + mouse_position: params.mouse_position, hovered_region_ids: params.hovered_region_ids.clone(), clicked_region_ids: params.clicked_region_ids.clone(), refreshing: params.refreshing, @@ -3971,6 +3987,7 @@ impl<'a, V: View> RenderContext<'a, V> { pub fn mouse_state(&self, region_id: usize) -> MouseState { let region_id = MouseRegionId::new::(self.view_id, region_id); MouseState { + mouse_position: self.mouse_position.clone(), hovered: self.hovered_region_ids.contains(®ion_id), clicked: self.clicked_region_ids.as_ref().and_then(|(ids, button)| { if ids.contains(®ion_id) { @@ -3979,6 +3996,7 @@ impl<'a, V: View> RenderContext<'a, V> { None } }), + accessed_mouse_position: false, accessed_hovered: false, accessed_clicked: false, } diff --git a/crates/gpui/src/app/test_app_context.rs b/crates/gpui/src/app/test_app_context.rs index 72f1f546fb8a32cb3c95fc932d2128fcbbc17cb7..0eebc6485d232f3e5153b508fb69bf27bdbd66c8 100644 --- a/crates/gpui/src/app/test_app_context.rs +++ b/crates/gpui/src/app/test_app_context.rs @@ -186,6 +186,7 @@ impl TestAppContext { view_id: handle.id(), view_type: PhantomData, titlebar_height: 0., + mouse_position: Default::default(), hovered_region_ids: Default::default(), clicked_region_ids: None, refreshing: false, diff --git a/crates/gpui/src/elements/mouse_event_handler.rs b/crates/gpui/src/elements/mouse_event_handler.rs index 01779fb362f061eb961419f85360e1849c72e1a2..3548b1a38c3a895504a89f2d3d5fa87213f7298a 100644 --- a/crates/gpui/src/elements/mouse_event_handler.rs +++ b/crates/gpui/src/elements/mouse_event_handler.rs @@ -21,6 +21,7 @@ pub struct MouseEventHandler { cursor_style: Option, handlers: HandlerSet, hoverable: bool, + notify_on_move: bool, notify_on_hover: bool, notify_on_click: bool, above: bool, @@ -28,9 +29,8 @@ pub struct MouseEventHandler { _tag: PhantomData, } -// MouseEventHandler::new -// MouseEventHandler::above - +/// Element which provides a render_child callback with a MouseState and paints a mouse +/// region under (or above) it for easy mouse event handling. impl MouseEventHandler { pub fn new(region_id: usize, cx: &mut RenderContext, render_child: F) -> Self where @@ -39,6 +39,7 @@ impl MouseEventHandler { { let mut mouse_state = cx.mouse_state::(region_id); let child = render_child(&mut mouse_state, cx); + let notify_on_move = mouse_state.accessed_mouse_position(); let notify_on_hover = mouse_state.accessed_hovered(); let notify_on_click = mouse_state.accessed_clicked(); Self { @@ -46,6 +47,7 @@ impl MouseEventHandler { region_id, cursor_style: None, handlers: Default::default(), + notify_on_move, notify_on_hover, notify_on_click, hoverable: true, @@ -55,6 +57,9 @@ impl MouseEventHandler { } } + /// Modifies the MouseEventHandler to render the MouseRegion above the child element. Useful + /// for drag and drop handling and similar events which should be captured before the child + /// gets the opportunity pub fn above(region_id: usize, cx: &mut RenderContext, render_child: F) -> Self where V: View, @@ -183,6 +188,7 @@ impl MouseEventHandler { self.handlers.clone(), ) .with_hoverable(self.hoverable) + .with_notify_on_move(self.notify_on_move) .with_notify_on_hover(self.notify_on_hover) .with_notify_on_click(self.notify_on_click), ); diff --git a/crates/gpui/src/presenter.rs b/crates/gpui/src/presenter.rs index 453ee0d60d12dd2c8319eaf13d5bb2fce911b0b4..16ea5da6429faddddace571f609d08730351b024 100644 --- a/crates/gpui/src/presenter.rs +++ b/crates/gpui/src/presenter.rs @@ -90,6 +90,7 @@ impl Presenter { window_id: self.window_id, view_id: *view_id, titlebar_height: self.titlebar_height, + mouse_position: self.mouse_position.clone(), hovered_region_ids: self.hovered_region_ids.clone(), clicked_region_ids: self .clicked_button @@ -116,6 +117,7 @@ impl Presenter { window_id: self.window_id, view_id: *view_id, titlebar_height: self.titlebar_height, + mouse_position: self.mouse_position.clone(), hovered_region_ids: self.hovered_region_ids.clone(), clicked_region_ids: self .clicked_button @@ -183,6 +185,7 @@ impl Presenter { asset_cache: &self.asset_cache, view_stack: Vec::new(), refreshing, + mouse_position: self.mouse_position.clone(), hovered_region_ids: self.hovered_region_ids.clone(), clicked_region_ids: self .clicked_button @@ -231,6 +234,10 @@ impl Presenter { let mut mouse_events = SmallVec::<[_; 2]>::new(); let mut notified_views: HashSet = Default::default(); + if let Some(mouse_position) = event.position() { + self.mouse_position = mouse_position; + } + // 1. Handle platform event. Keyboard events get dispatched immediately, while mouse events // get mapped into the mouse-specific MouseEvent type. // -> These are usually small: [Mouse Down] or [Mouse up, Click] or [Mouse Moved, Mouse Dragged?] @@ -402,10 +409,10 @@ impl Presenter { MouseEvent::Down(_) | MouseEvent::Up(_) => { for (region, _) in self.mouse_regions.iter().rev() { if region.bounds.contains_point(self.mouse_position) { + valid_regions.push(region.clone()); if region.notify_on_click { notified_views.insert(region.id().view_id()); } - valid_regions.push(region.clone()); } } } @@ -447,6 +454,16 @@ impl Presenter { } } } + MouseEvent::Move(_) => { + for (mouse_region, _) in self.mouse_regions.iter().rev() { + if mouse_region.bounds.contains_point(self.mouse_position) { + valid_regions.push(mouse_region.clone()); + if mouse_region.notify_on_move { + notified_views.insert(mouse_region.id().view_id()); + } + } + } + } _ => { for (mouse_region, _) in self.mouse_regions.iter().rev() { // Contains @@ -551,6 +568,7 @@ pub struct LayoutContext<'a> { pub window_size: Vector2F, titlebar_height: f32, appearance: Appearance, + mouse_position: Vector2F, hovered_region_ids: HashSet, clicked_region_ids: Option<(HashSet, MouseButton)>, } @@ -622,6 +640,7 @@ impl<'a> LayoutContext<'a> { view_id: handle.id(), view_type: PhantomData, titlebar_height: self.titlebar_height, + mouse_position: self.mouse_position.clone(), hovered_region_ids: self.hovered_region_ids.clone(), clicked_region_ids: self.clicked_region_ids.clone(), refreshing: self.refreshing, @@ -861,6 +880,13 @@ impl Axis { Self::Vertical => Self::Horizontal, } } + + pub fn component(&self, point: Vector2F) -> f32 { + match self { + Self::Horizontal => point.x(), + Self::Vertical => point.y(), + } + } } impl ToJson for Axis { diff --git a/crates/gpui/src/scene/mouse_region.rs b/crates/gpui/src/scene/mouse_region.rs index 4b5217cc2dec1355d350f921fa66233d8be39591..f567fb7fe0a5ac50e006ec4c3c198405a83bd515 100644 --- a/crates/gpui/src/scene/mouse_region.rs +++ b/crates/gpui/src/scene/mouse_region.rs @@ -20,6 +20,7 @@ pub struct MouseRegion { pub bounds: RectF, pub handlers: HandlerSet, pub hoverable: bool, + pub notify_on_move: bool, pub notify_on_hover: bool, pub notify_on_click: bool, } @@ -54,6 +55,7 @@ impl MouseRegion { bounds, handlers, hoverable: true, + notify_on_move: false, notify_on_hover: false, notify_on_click: false, } @@ -136,6 +138,11 @@ impl MouseRegion { self } + pub fn with_notify_on_move(mut self, notify: bool) -> Self { + self.notify_on_move = notify; + self + } + pub fn with_notify_on_hover(mut self, notify: bool) -> Self { self.notify_on_hover = notify; self diff --git a/crates/theme/src/theme.rs b/crates/theme/src/theme.rs index db6609fa829cce0d3ee487ac4ff32ec1e82b140f..eaf0972367994b2b5ac7c5f6577829322c56bb95 100644 --- a/crates/theme/src/theme.rs +++ b/crates/theme/src/theme.rs @@ -62,6 +62,7 @@ pub struct Workspace { pub joining_project_message: ContainedText, pub external_location_message: ContainedText, pub dock: Dock, + pub drop_target_overlay_color: Color, } #[derive(Clone, Deserialize, Default)] @@ -150,7 +151,6 @@ pub struct TabBar { pub inactive_pane: TabStyles, pub dragged_tab: Tab, pub height: f32, - pub drop_target_overlay_color: Color, } impl TabBar { diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 9b1dec8d2d54d5bdef6efb6d3f6582f964237975..283b50f4c3a99d88433972e6494a0a2f6b8a20d2 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -397,10 +397,10 @@ impl View for ToggleDockButton { } }) .with_cursor_style(CursorStyle::PointingHand) - .on_up(MouseButton::Left, move |_, cx| { + .on_up(MouseButton::Left, move |event, cx| { let dock_pane = workspace.read(cx.app).dock_pane(); let drop_index = dock_pane.read(cx.app).items_len() + 1; - Pane::handle_dropped_item(&dock_pane.downgrade(), drop_index, false, cx); + Pane::handle_dropped_item(event, &dock_pane.downgrade(), drop_index, false, None, cx); }); if dock_position.is_visible() { diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index e3ac7a2947706ce75f0b074a067f1be771d80d7a..9f103946bfa0f38378ad109e8adfe4737fb7f2e5 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -2,7 +2,7 @@ use super::{ItemHandle, SplitDirection}; use crate::{ dock::{icon_for_dock_anchor, AnchorDockBottom, AnchorDockRight, ExpandDock, HideDock}, toolbar::Toolbar, - Item, NewFile, NewSearch, NewTerminal, WeakItemHandle, Workspace, + Item, NewFile, NewSearch, NewTerminal, SplitWithItem, WeakItemHandle, Workspace, }; use anyhow::Result; use collections::{HashMap, HashSet, VecDeque}; @@ -19,6 +19,7 @@ use gpui::{ }, impl_actions, impl_internal_actions, platform::{CursorStyle, NavigationDirection}, + scene::MouseUp, Action, AnyViewHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity, EventContext, ModelHandle, MouseButton, MutableAppContext, PromptLevel, Quad, RenderContext, Task, View, ViewContext, ViewHandle, WeakViewHandle, @@ -98,7 +99,7 @@ impl_internal_actions!( DeploySplitMenu, DeployNewMenu, DeployDockMenu, - MoveItem + MoveItem, ] ); @@ -1097,7 +1098,7 @@ impl Pane { ix == 0, detail, hovered, - Self::tab_overlay_color(hovered, theme.as_ref(), cx), + Self::tab_overlay_color(hovered, cx), tab_style, cx, ) @@ -1124,7 +1125,7 @@ impl Pane { }) .on_up(MouseButton::Left, { let pane = pane.clone(); - move |_, cx: &mut EventContext| Pane::handle_dropped_item(&pane, ix, true, cx) + move |event, cx| Pane::handle_dropped_item(event, &pane, ix, true, None, cx) }) .as_draggable( DraggedItem { @@ -1164,14 +1165,14 @@ impl Pane { .with_style(filler_style.container) .with_border(filler_style.container.border); - if let Some(overlay) = Self::tab_overlay_color(mouse_state.hovered(), &theme, cx) { + if let Some(overlay) = Self::tab_overlay_color(mouse_state.hovered(), cx) { filler = filler.with_overlay_color(overlay); } filler.boxed() }) - .on_up(MouseButton::Left, move |_, cx| { - Pane::handle_dropped_item(&pane, filler_index, true, cx) + .on_up(MouseButton::Left, move |event, cx| { + Pane::handle_dropped_item(event, &pane, filler_index, true, None, cx) }) .flex(1., true) .named("filler"), @@ -1320,17 +1321,64 @@ impl Pane { tab.constrained().with_height(tab_style.height).boxed() } + fn render_tab_bar_buttons( + &mut self, + theme: &Theme, + cx: &mut RenderContext, + ) -> ElementBox { + Flex::row() + // New menu + .with_child(tab_bar_button(0, "icons/plus_12.svg", cx, |position| { + DeployNewMenu { position } + })) + .with_child( + self.docked + .map(|anchor| { + // Add the dock menu button if this pane is a dock + let dock_icon = icon_for_dock_anchor(anchor); + + tab_bar_button(1, dock_icon, cx, |position| DeployDockMenu { position }) + }) + .unwrap_or_else(|| { + // Add the split menu if this pane is not a dock + tab_bar_button(2, "icons/split_12.svg", cx, |position| DeploySplitMenu { + position, + }) + }), + ) + // Add the close dock button if this pane is a dock + .with_children( + self.docked + .map(|_| tab_bar_button(3, "icons/x_mark_thin_8.svg", cx, |_| HideDock)), + ) + .contained() + .with_style(theme.workspace.tab_bar.pane_button_container) + .flex(1., false) + .boxed() + } + pub fn handle_dropped_item( + event: MouseUp, pane: &WeakViewHandle, index: usize, allow_same_pane: bool, + split_margin: Option, cx: &mut EventContext, ) { if let Some((_, dragged_item)) = cx .global::>() .currently_dragged::(cx.window_id) { - if pane != &dragged_item.pane || allow_same_pane { + if let Some(split_direction) = split_margin + .and_then(|margin| Self::drop_split_direction(event.position, event.region, margin)) + { + cx.dispatch_action(SplitWithItem { + item_id_to_move: dragged_item.item.id(), + pane_to_split: pane.clone(), + split_direction, + }); + } else if pane != &dragged_item.pane || allow_same_pane { + // If no split margin or not close enough to the edge, just move the item cx.dispatch_action(MoveItem { item_id: dragged_item.item.id(), from: dragged_item.pane.clone(), @@ -1343,18 +1391,39 @@ impl Pane { } } - fn tab_overlay_color( - hovered: bool, - theme: &Theme, - cx: &mut RenderContext, - ) -> Option { + fn drop_split_direction( + position: Vector2F, + region: RectF, + split_margin: f32, + ) -> Option { + let mut min_direction = None; + let mut min_distance = split_margin; + for direction in SplitDirection::all() { + let edge_distance = + (direction.edge(region) - direction.axis().component(position)).abs(); + + if edge_distance < min_distance { + min_direction = Some(direction); + min_distance = edge_distance; + } + } + + min_direction + } + + fn tab_overlay_color(hovered: bool, cx: &mut RenderContext) -> Option { if hovered && cx .global::>() .currently_dragged::(cx.window_id()) .is_some() { - Some(theme.workspace.tab_bar.drop_target_overlay_color) + Some( + cx.global::() + .theme + .workspace + .drop_target_overlay_color, + ) } else { None } @@ -1389,55 +1458,7 @@ impl View for Pane { // Render pane buttons let theme = cx.global::().theme.clone(); if self.is_active { - tab_row.add_child( - Flex::row() - // New menu - .with_child(tab_bar_button( - 0, - "icons/plus_12.svg", - cx, - |position| DeployNewMenu { position }, - )) - .with_child( - self.docked - .map(|anchor| { - // Add the dock menu button if this pane is a dock - let dock_icon = - icon_for_dock_anchor(anchor); - - tab_bar_button( - 1, - dock_icon, - cx, - |position| DeployDockMenu { position }, - ) - }) - .unwrap_or_else(|| { - // Add the split menu if this pane is not a dock - tab_bar_button( - 2, - "icons/split_12.svg", - cx, - |position| DeploySplitMenu { position }, - ) - }), - ) - // Add the close dock button if this pane is a dock - .with_children(self.docked.map(|_| { - tab_bar_button( - 3, - "icons/x_mark_thin_8.svg", - cx, - |_| HideDock, - ) - })) - .contained() - .with_style( - theme.workspace.tab_bar.pane_button_container, - ) - .flex(1., false) - .boxed(), - ) + tab_row.add_child(self.render_tab_bar_buttons(&theme, cx)) } tab_row @@ -1453,25 +1474,66 @@ impl View for Pane { MouseEventHandler::::above( 0, cx, - |_, cx| { - Flex::column() + |state, cx| { + let overlay_color = Self::tab_overlay_color(true, cx); + let drag_position = cx + .global::>() + .currently_dragged::(cx.window_id()) + .map(|_| state.mouse_position()); + + Stack::new() .with_child( - ChildView::new(&self.toolbar, cx) - .expanded() - .boxed(), - ) - .with_child( - ChildView::new(active_item, cx) - .flex(1., true) + Flex::column() + .with_child( + ChildView::new(&self.toolbar, cx) + .expanded() + .boxed(), + ) + .with_child( + ChildView::new(active_item, cx) + .flex(1., true) + .boxed(), + ) .boxed(), ) + .with_children(drag_position.map(|drag_position| { + Canvas::new(move |region, _, cx| { + let overlay_region = + if let Some(split_direction) = + Self::drop_split_direction( + drag_position, + region, + 100., /* Replace with theme value */ + ) + { + split_direction.along_edge(region, 100.) + } else { + region + }; + + cx.scene.push_quad(Quad { + bounds: overlay_region, + background: overlay_color, + border: Default::default(), + corner_radius: 0., + }); + }) + .boxed() + })) .boxed() }, ) .on_up(MouseButton::Left, { let pane = cx.handle(); - move |_, cx: &mut EventContext| { - Pane::handle_dropped_item(&pane, drop_index, false, cx) + move |event, cx| { + Pane::handle_dropped_item( + event, + &pane, + drop_index, + false, + Some(100.), /* Use theme value */ + cx, + ) } }) .flex(1., true) @@ -1493,8 +1555,8 @@ impl View for Pane { }) .on_up(MouseButton::Left, { let pane = this.clone(); - move |_, cx: &mut EventContext| { - Pane::handle_dropped_item(&pane, 0, true, cx) + move |event, cx| { + Pane::handle_dropped_item(event, &pane, 0, true, None, cx) } }) .boxed() diff --git a/crates/workspace/src/pane_group.rs b/crates/workspace/src/pane_group.rs index 6c379ffd2a9dac960730dcbbba9ee1ab471d72e2..45b9af6f382284635f71159e7a624480475f3669 100644 --- a/crates/workspace/src/pane_group.rs +++ b/crates/workspace/src/pane_group.rs @@ -2,7 +2,9 @@ use crate::{FollowerStatesByLeader, JoinProject, Pane, Workspace}; use anyhow::{anyhow, Result}; use call::{ActiveCall, ParticipantLocation}; use gpui::{ - elements::*, Axis, Border, CursorStyle, ModelHandle, MouseButton, RenderContext, ViewHandle, + elements::*, + geometry::{rect::RectF, vector::Vector2F}, + Axis, Border, CursorStyle, ModelHandle, MouseButton, RenderContext, ViewHandle, }; use project::Project; use serde::Deserialize; @@ -263,9 +265,7 @@ impl PaneAxis { new_pane: &ViewHandle, direction: SplitDirection, ) -> Result<()> { - use SplitDirection::*; - - for (idx, member) in self.members.iter_mut().enumerate() { + for (mut idx, member) in self.members.iter_mut().enumerate() { match member { Member::Axis(axis) => { if axis.split(old_pane, new_pane, direction).is_ok() { @@ -274,15 +274,12 @@ impl PaneAxis { } Member::Pane(pane) => { if pane == old_pane { - if direction.matches_axis(self.axis) { - match direction { - Up | Left => { - self.members.insert(idx, Member::Pane(new_pane.clone())); - } - Down | Right => { - self.members.insert(idx + 1, Member::Pane(new_pane.clone())); - } + if direction.axis() == self.axis { + if direction.increasing() { + idx += 1; } + + self.members.insert(idx, Member::Pane(new_pane.clone())); } else { *member = Member::new_axis(old_pane.clone(), new_pane.clone(), direction); @@ -374,187 +371,46 @@ pub enum SplitDirection { } impl SplitDirection { - fn matches_axis(self, orientation: Axis) -> bool { - use Axis::*; - use SplitDirection::*; + pub fn all() -> [Self; 4] { + [Self::Up, Self::Down, Self::Left, Self::Right] + } + pub fn edge(&self, rect: RectF) -> f32 { match self { - Up | Down => match orientation { - Vertical => true, - Horizontal => false, - }, - Left | Right => match orientation { - Vertical => false, - Horizontal => true, - }, + Self::Up => rect.min_y(), + Self::Down => rect.max_y(), + Self::Left => rect.min_x(), + Self::Right => rect.max_x(), } } -} - -#[cfg(test)] -mod tests { - // use super::*; - // use serde_json::json; - - // #[test] - // fn test_split_and_remove() -> Result<()> { - // let mut group = PaneGroup::new(1); - // assert_eq!( - // serde_json::to_value(&group)?, - // json!({ - // "type": "pane", - // "paneId": 1, - // }) - // ); - - // group.split(1, 2, SplitDirection::Right)?; - // assert_eq!( - // serde_json::to_value(&group)?, - // json!({ - // "type": "axis", - // "orientation": "horizontal", - // "members": [ - // {"type": "pane", "paneId": 1}, - // {"type": "pane", "paneId": 2}, - // ] - // }) - // ); - - // group.split(2, 3, SplitDirection::Up)?; - // assert_eq!( - // serde_json::to_value(&group)?, - // json!({ - // "type": "axis", - // "orientation": "horizontal", - // "members": [ - // {"type": "pane", "paneId": 1}, - // { - // "type": "axis", - // "orientation": "vertical", - // "members": [ - // {"type": "pane", "paneId": 3}, - // {"type": "pane", "paneId": 2}, - // ] - // }, - // ] - // }) - // ); - - // group.split(1, 4, SplitDirection::Right)?; - // assert_eq!( - // serde_json::to_value(&group)?, - // json!({ - // "type": "axis", - // "orientation": "horizontal", - // "members": [ - // {"type": "pane", "paneId": 1}, - // {"type": "pane", "paneId": 4}, - // { - // "type": "axis", - // "orientation": "vertical", - // "members": [ - // {"type": "pane", "paneId": 3}, - // {"type": "pane", "paneId": 2}, - // ] - // }, - // ] - // }) - // ); - // group.split(2, 5, SplitDirection::Up)?; - // assert_eq!( - // serde_json::to_value(&group)?, - // json!({ - // "type": "axis", - // "orientation": "horizontal", - // "members": [ - // {"type": "pane", "paneId": 1}, - // {"type": "pane", "paneId": 4}, - // { - // "type": "axis", - // "orientation": "vertical", - // "members": [ - // {"type": "pane", "paneId": 3}, - // {"type": "pane", "paneId": 5}, - // {"type": "pane", "paneId": 2}, - // ] - // }, - // ] - // }) - // ); - - // assert_eq!(true, group.remove(5)?); - // assert_eq!( - // serde_json::to_value(&group)?, - // json!({ - // "type": "axis", - // "orientation": "horizontal", - // "members": [ - // {"type": "pane", "paneId": 1}, - // {"type": "pane", "paneId": 4}, - // { - // "type": "axis", - // "orientation": "vertical", - // "members": [ - // {"type": "pane", "paneId": 3}, - // {"type": "pane", "paneId": 2}, - // ] - // }, - // ] - // }) - // ); - - // assert_eq!(true, group.remove(4)?); - // assert_eq!( - // serde_json::to_value(&group)?, - // json!({ - // "type": "axis", - // "orientation": "horizontal", - // "members": [ - // {"type": "pane", "paneId": 1}, - // { - // "type": "axis", - // "orientation": "vertical", - // "members": [ - // {"type": "pane", "paneId": 3}, - // {"type": "pane", "paneId": 2}, - // ] - // }, - // ] - // }) - // ); - - // assert_eq!(true, group.remove(3)?); - // assert_eq!( - // serde_json::to_value(&group)?, - // json!({ - // "type": "axis", - // "orientation": "horizontal", - // "members": [ - // {"type": "pane", "paneId": 1}, - // {"type": "pane", "paneId": 2}, - // ] - // }) - // ); - - // assert_eq!(true, group.remove(2)?); - // assert_eq!( - // serde_json::to_value(&group)?, - // json!({ - // "type": "pane", - // "paneId": 1, - // }) - // ); + // Returns a new rectangle which shares an edge in SplitDirection and has `size` along SplitDirection + pub fn along_edge(&self, rect: RectF, size: f32) -> RectF { + match self { + Self::Up => RectF::new(rect.origin(), Vector2F::new(rect.width(), size)), + Self::Down => RectF::new( + rect.lower_left() - Vector2F::new(0., size), + Vector2F::new(rect.width(), size), + ), + Self::Left => RectF::new(rect.origin(), Vector2F::new(size, rect.height())), + Self::Right => RectF::new( + rect.upper_right() - Vector2F::new(size, 0.), + Vector2F::new(size, rect.height()), + ), + } + } - // assert_eq!(false, group.remove(1)?); - // assert_eq!( - // serde_json::to_value(&group)?, - // json!({ - // "type": "pane", - // "paneId": 1, - // }) - // ); + pub fn axis(&self) -> Axis { + match self { + Self::Up | Self::Down => Axis::Vertical, + Self::Left | Self::Right => Axis::Horizontal, + } + } - // Ok(()) - // } + pub fn increasing(&self) -> bool { + match self { + Self::Left | Self::Up => false, + Self::Down | Self::Right => true, + } + } } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 714325a6996e2b8bd1f9694449fa2e51ddfaa149..512ca239501755771f512c27f24cf3c08d56c8c9 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -100,7 +100,7 @@ actions!( ToggleLeftSidebar, ToggleRightSidebar, NewTerminal, - NewSearch + NewSearch, ] ); @@ -126,6 +126,12 @@ pub struct OpenSharedScreen { pub peer_id: PeerId, } +pub struct SplitWithItem { + pane_to_split: WeakViewHandle, + split_direction: SplitDirection, + item_id_to_move: usize, +} + impl_internal_actions!( workspace, [ @@ -133,7 +139,8 @@ impl_internal_actions!( ToggleFollow, JoinProject, OpenSharedScreen, - RemoveWorktreeFromProject + RemoveWorktreeFromProject, + SplitWithItem, ] ); impl_actions!(workspace, [ActivatePane]); @@ -206,6 +213,22 @@ pub fn init(app_state: Arc, cx: &mut MutableAppContext) { workspace.toggle_sidebar(SidebarSide::Right, cx); }); cx.add_action(Workspace::activate_pane_at_index); + cx.add_action( + |workspace: &mut Workspace, + SplitWithItem { + pane_to_split, + item_id_to_move, + split_direction, + }: &_, + cx| { + workspace.split_pane_with_item( + pane_to_split.clone(), + *item_id_to_move, + *split_direction, + cx, + ) + }, + ); let client = &app_state.client; client.add_view_request_handler(Workspace::handle_follow); @@ -1950,6 +1973,35 @@ impl Workspace { }) } + pub fn split_pane_with_item( + &mut self, + pane_to_split: WeakViewHandle, + item_id_to_move: usize, + split_direction: SplitDirection, + cx: &mut ViewContext, + ) { + if let Some(pane_to_split) = pane_to_split.upgrade(cx) { + if &pane_to_split == self.dock_pane() { + warn!("Can't split dock pane."); + return; + } + + let new_pane = self.add_pane(cx); + Pane::move_item( + self, + pane_to_split.clone(), + new_pane.clone(), + item_id_to_move, + 0, + cx, + ); + self.center + .split(&pane_to_split, &new_pane, split_direction) + .unwrap(); + cx.notify(); + } + } + fn remove_pane(&mut self, pane: ViewHandle, cx: &mut ViewContext) { if self.center.remove(&pane).unwrap() { self.panes.retain(|p| p != &pane); diff --git a/styles/src/styleTree/tabBar.ts b/styles/src/styleTree/tabBar.ts index bd6070e0ed9aa8bb9eb6560487b90d2d6aab6334..2824c43483fe602bcc7a17ec0b09d709830b3b47 100644 --- a/styles/src/styleTree/tabBar.ts +++ b/styles/src/styleTree/tabBar.ts @@ -75,10 +75,6 @@ export default function tabBar(colorScheme: ColorScheme) { return { height, background: background(layer), - dropTargetOverlayColor: withOpacity( - foreground(layer), - 0.6 - ), activePane: { activeTab: activePaneActiveTab, inactiveTab: tab, diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index 3edb7462240bda6d9eec9cce3ed6db210bdcc250..fea7df431339209611c3d407898937ae5fe413ff 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -227,5 +227,9 @@ export default function workspace(colorScheme: ColorScheme) { shadow: colorScheme.modalShadow, }, }, + dropTargetOverlayColor: withOpacity( + foreground(layer), + 0.6 + ), }; } From d7b8a189e49a1a4bb634676a2821af83cfc6ecd6 Mon Sep 17 00:00:00 2001 From: K Simmons Date: Sat, 22 Oct 2022 11:41:37 -0700 Subject: [PATCH 05/12] fix issue where empty pane is created --- crates/gpui/src/presenter.rs | 4 ++-- crates/workspace/src/pane.rs | 27 +++++++++++++++++---------- crates/workspace/src/workspace.rs | 15 ++++++--------- 3 files changed, 25 insertions(+), 21 deletions(-) diff --git a/crates/gpui/src/presenter.rs b/crates/gpui/src/presenter.rs index 16ea5da6429faddddace571f609d08730351b024..b04dde7e063ae9dd4cc33aa2be7a45aadbc79cd2 100644 --- a/crates/gpui/src/presenter.rs +++ b/crates/gpui/src/presenter.rs @@ -391,7 +391,7 @@ impl Presenter { //Ensure that hover entrance events aren't sent twice if self.hovered_region_ids.insert(region.id()) { valid_regions.push(region.clone()); - if region.notify_on_hover { + if region.notify_on_hover || region.notify_on_move { notified_views.insert(region.id().view_id()); } } @@ -399,7 +399,7 @@ impl Presenter { // Ensure that hover exit events aren't sent twice if self.hovered_region_ids.remove(®ion.id()) { valid_regions.push(region.clone()); - if region.notify_on_hover { + if region.notify_on_hover || region.notify_on_move { notified_views.insert(region.id().view_id()); } } diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 9f103946bfa0f38378ad109e8adfe4737fb7f2e5..1d50a846b453a3ec99a3dbe9404792e54cfbb6ec 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -1373,6 +1373,7 @@ impl Pane { .and_then(|margin| Self::drop_split_direction(event.position, event.region, margin)) { cx.dispatch_action(SplitWithItem { + from: dragged_item.pane.clone(), item_id_to_move: dragged_item.item.id(), pane_to_split: pane.clone(), split_direction, @@ -1476,9 +1477,13 @@ impl View for Pane { cx, |state, cx| { let overlay_color = Self::tab_overlay_color(true, cx); + // Hovered will cause a render when the mouse enters regardless + // of if mouse position was accessed before + let hovered = state.hovered(); let drag_position = cx .global::>() .currently_dragged::(cx.window_id()) + .filter(|_| hovered) .map(|_| state.mouse_position()); Stack::new() @@ -1498,25 +1503,27 @@ impl View for Pane { ) .with_children(drag_position.map(|drag_position| { Canvas::new(move |region, _, cx| { - let overlay_region = - if let Some(split_direction) = + if region.contains_point(drag_position) { + let overlay_region = if let Some( + split_direction, + ) = Self::drop_split_direction( drag_position, region, 100., /* Replace with theme value */ - ) - { + ) { split_direction.along_edge(region, 100.) } else { region }; - cx.scene.push_quad(Quad { - bounds: overlay_region, - background: overlay_color, - border: Default::default(), - corner_radius: 0., - }); + cx.scene.push_quad(Quad { + bounds: overlay_region, + background: overlay_color, + border: Default::default(), + corner_radius: 0., + }); + } }) .boxed() })) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 512ca239501755771f512c27f24cf3c08d56c8c9..4263f83ef106bcba0f473641e6555f3a9c0d66ae 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -127,6 +127,7 @@ pub struct OpenSharedScreen { } pub struct SplitWithItem { + from: WeakViewHandle, pane_to_split: WeakViewHandle, split_direction: SplitDirection, item_id_to_move: usize, @@ -216,12 +217,14 @@ pub fn init(app_state: Arc, cx: &mut MutableAppContext) { cx.add_action( |workspace: &mut Workspace, SplitWithItem { + from, pane_to_split, item_id_to_move, split_direction, }: &_, cx| { workspace.split_pane_with_item( + from.clone(), pane_to_split.clone(), *item_id_to_move, *split_direction, @@ -1975,26 +1978,20 @@ impl Workspace { pub fn split_pane_with_item( &mut self, + from: WeakViewHandle, pane_to_split: WeakViewHandle, item_id_to_move: usize, split_direction: SplitDirection, cx: &mut ViewContext, ) { - if let Some(pane_to_split) = pane_to_split.upgrade(cx) { + if let Some((pane_to_split, from)) = pane_to_split.upgrade(cx).zip(from.upgrade(cx)) { if &pane_to_split == self.dock_pane() { warn!("Can't split dock pane."); return; } let new_pane = self.add_pane(cx); - Pane::move_item( - self, - pane_to_split.clone(), - new_pane.clone(), - item_id_to_move, - 0, - cx, - ); + Pane::move_item(self, from.clone(), new_pane.clone(), item_id_to_move, 0, cx); self.center .split(&pane_to_split, &new_pane, split_direction) .unwrap(); From 8cde64d3f64fb6cd7ce7bb15b77d664985dc8072 Mon Sep 17 00:00:00 2001 From: K Simmons Date: Sat, 22 Oct 2022 21:32:07 -0700 Subject: [PATCH 06/12] extract dragged item target --- crates/gpui/src/elements/stack.rs | 3 +- crates/workspace/src/dock.rs | 7 +- crates/workspace/src/pane.rs | 233 +++--------------- .../src/pane/dragged_item_receiver.rs | 133 ++++++++++ 4 files changed, 179 insertions(+), 197 deletions(-) create mode 100644 crates/workspace/src/pane/dragged_item_receiver.rs diff --git a/crates/gpui/src/elements/stack.rs b/crates/gpui/src/elements/stack.rs index 92a41749b366214c337e22fb1711a575298f2fbc..b81749787e10c44fcf0f1f680275a1bcc0241259 100644 --- a/crates/gpui/src/elements/stack.rs +++ b/crates/gpui/src/elements/stack.rs @@ -24,12 +24,13 @@ impl Element for Stack { fn layout( &mut self, - constraint: SizeConstraint, + mut constraint: SizeConstraint, cx: &mut LayoutContext, ) -> (Vector2F, Self::LayoutState) { let mut size = constraint.min; for child in &mut self.children { size = size.max(child.layout(constraint, cx)); + constraint.min = size; } (size, ()) } diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 283b50f4c3a99d88433972e6494a0a2f6b8a20d2..b17a7ea22e0bb38293e05c553d0b5519bc698a91 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -9,7 +9,9 @@ use serde::Deserialize; use settings::{DockAnchor, Settings}; use theme::Theme; -use crate::{sidebar::SidebarSide, ItemHandle, Pane, StatusItemView, Workspace}; +use crate::{ + handle_dropped_item, sidebar::SidebarSide, ItemHandle, Pane, StatusItemView, Workspace, +}; #[derive(PartialEq, Clone, Deserialize)] pub struct MoveDock(pub DockAnchor); @@ -376,6 +378,7 @@ impl View for ToggleDockButton { let dock_position = workspace.read(cx).dock.position; let theme = cx.global::().theme.clone(); + let button = MouseEventHandler::::new(0, cx, { let theme = theme.clone(); move |state, _| { @@ -400,7 +403,7 @@ impl View for ToggleDockButton { .on_up(MouseButton::Left, move |event, cx| { let dock_pane = workspace.read(cx.app).dock_pane(); let drop_index = dock_pane.read(cx.app).items_len() + 1; - Pane::handle_dropped_item(event, &dock_pane.downgrade(), drop_index, false, None, cx); + handle_dropped_item(event, &dock_pane.downgrade(), drop_index, false, None, cx); }); if dock_position.is_visible() { diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 1d50a846b453a3ec99a3dbe9404792e54cfbb6ec..651afce1c602be9d43e78dbcc07d24958823d898 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -1,17 +1,19 @@ +mod dragged_item_receiver; + use super::{ItemHandle, SplitDirection}; use crate::{ dock::{icon_for_dock_anchor, AnchorDockBottom, AnchorDockRight, ExpandDock, HideDock}, toolbar::Toolbar, - Item, NewFile, NewSearch, NewTerminal, SplitWithItem, WeakItemHandle, Workspace, + Item, NewFile, NewSearch, NewTerminal, WeakItemHandle, Workspace, }; use anyhow::Result; use collections::{HashMap, HashSet, VecDeque}; use context_menu::{ContextMenu, ContextMenuItem}; -use drag_and_drop::{DragAndDrop, Draggable}; +use drag_and_drop::Draggable; +pub use dragged_item_receiver::{dragged_item_receiver, handle_dropped_item}; use futures::StreamExt; use gpui::{ actions, - color::Color, elements::*, geometry::{ rect::RectF, @@ -19,7 +21,6 @@ use gpui::{ }, impl_actions, impl_internal_actions, platform::{CursorStyle, NavigationDirection}, - scene::MouseUp, Action, AnyViewHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity, EventContext, ModelHandle, MouseButton, MutableAppContext, PromptLevel, Quad, RenderContext, Task, View, ViewContext, ViewHandle, WeakViewHandle, @@ -1056,11 +1057,7 @@ impl Pane { fn render_tabs(&mut self, cx: &mut RenderContext) -> impl Element { let theme = cx.global::().theme.clone(); - let filler_index = self.items.len(); - enum Tabs {} - enum Tab {} - enum Filler {} let pane = cx.handle(); let autoscroll = if mem::take(&mut self.autoscroll) { Some(self.active_item_index) @@ -1070,6 +1067,7 @@ impl Pane { let pane_active = self.is_active; + enum Tabs {} let mut row = Flex::row().scrollable::(1, autoscroll, cx); for (ix, (item, detail)) in self .items @@ -1082,7 +1080,8 @@ impl Pane { let tab_active = ix == self.active_item_index; row.add_child({ - MouseEventHandler::::above(ix, cx, { + enum Tab {} + dragged_item_receiver::(ix, ix, true, None, cx, { let item = item.clone(); let pane = pane.clone(); let detail = detail.clone(); @@ -1092,16 +1091,7 @@ impl Pane { move |mouse_state, cx| { let tab_style = theme.workspace.tab_bar.tab_style(pane_active, tab_active); let hovered = mouse_state.hovered(); - Self::render_tab( - &item, - pane, - ix == 0, - detail, - hovered, - Self::tab_overlay_color(hovered, cx), - tab_style, - cx, - ) + Self::render_tab(&item, pane, ix == 0, detail, hovered, tab_style, cx) } }) .with_cursor_style(if pane_active && tab_active { @@ -1123,10 +1113,6 @@ impl Pane { }) } }) - .on_up(MouseButton::Left, { - let pane = pane.clone(); - move |event, cx| Pane::handle_dropped_item(event, &pane, ix, true, None, cx) - }) .as_draggable( DraggedItem { item, @@ -1144,7 +1130,6 @@ impl Pane { false, detail, false, - None, &tab_style, cx, ) @@ -1157,22 +1142,16 @@ impl Pane { // Use the inactive tab style along with the current pane's active status to decide how to render // the filler + let filler_index = self.items.len(); let filler_style = theme.workspace.tab_bar.tab_style(pane_active, false); + enum Filler {} row.add_child( - MouseEventHandler::::new(0, cx, |mouse_state, cx| { - let mut filler = Empty::new() + dragged_item_receiver::(0, filler_index, true, None, cx, |_, _| { + Empty::new() .contained() .with_style(filler_style.container) - .with_border(filler_style.container.border); - - if let Some(overlay) = Self::tab_overlay_color(mouse_state.hovered(), cx) { - filler = filler.with_overlay_color(overlay); - } - - filler.boxed() - }) - .on_up(MouseButton::Left, move |event, cx| { - Pane::handle_dropped_item(event, &pane, filler_index, true, None, cx) + .with_border(filler_style.container.border) + .boxed() }) .flex(1., true) .named("filler"), @@ -1224,7 +1203,6 @@ impl Pane { first: bool, detail: Option, hovered: bool, - overlay: Option, tab_style: &theme::Tab, cx: &mut RenderContext, ) -> ElementBox { @@ -1234,7 +1212,7 @@ impl Pane { container.border.left = false; } - let mut tab = Flex::row() + Flex::row() .with_child( Align::new({ let diameter = 7.0; @@ -1312,13 +1290,10 @@ impl Pane { .boxed(), ) .contained() - .with_style(container); - - if let Some(overlay) = overlay { - tab = tab.with_overlay_color(overlay); - } - - tab.constrained().with_height(tab_style.height).boxed() + .with_style(container) + .constrained() + .with_height(tab_style.height) + .boxed() } fn render_tab_bar_buttons( @@ -1356,79 +1331,6 @@ impl Pane { .flex(1., false) .boxed() } - - pub fn handle_dropped_item( - event: MouseUp, - pane: &WeakViewHandle, - index: usize, - allow_same_pane: bool, - split_margin: Option, - cx: &mut EventContext, - ) { - if let Some((_, dragged_item)) = cx - .global::>() - .currently_dragged::(cx.window_id) - { - if let Some(split_direction) = split_margin - .and_then(|margin| Self::drop_split_direction(event.position, event.region, margin)) - { - cx.dispatch_action(SplitWithItem { - from: dragged_item.pane.clone(), - item_id_to_move: dragged_item.item.id(), - pane_to_split: pane.clone(), - split_direction, - }); - } else if pane != &dragged_item.pane || allow_same_pane { - // If no split margin or not close enough to the edge, just move the item - cx.dispatch_action(MoveItem { - item_id: dragged_item.item.id(), - from: dragged_item.pane.clone(), - to: pane.clone(), - destination_index: index, - }) - } - } else { - cx.propagate_event(); - } - } - - fn drop_split_direction( - position: Vector2F, - region: RectF, - split_margin: f32, - ) -> Option { - let mut min_direction = None; - let mut min_distance = split_margin; - for direction in SplitDirection::all() { - let edge_distance = - (direction.edge(region) - direction.axis().component(position)).abs(); - - if edge_distance < min_distance { - min_direction = Some(direction); - min_distance = edge_distance; - } - } - - min_direction - } - - fn tab_overlay_color(hovered: bool, cx: &mut RenderContext) -> Option { - if hovered - && cx - .global::>() - .currently_dragged::(cx.window_id()) - .is_some() - { - Some( - cx.global::() - .theme - .workspace - .drop_target_overlay_color, - ) - } else { - None - } - } } impl Entity for Pane { @@ -1449,8 +1351,6 @@ impl View for Pane { .with_child( MouseEventHandler::::new(0, cx, |_, cx| { if let Some(active_item) = self.active_item() { - enum PaneContentTabDropTarget {} - Flex::column() .with_child({ let mut tab_row = Flex::row() @@ -1471,78 +1371,29 @@ impl View for Pane { .named("tab bar") }) .with_child({ - let drop_index = self.active_item_index + 1; - MouseEventHandler::::above( + enum PaneContentTabDropTarget {} + dragged_item_receiver::( 0, + self.active_item_index + 1, + false, + Some(100.), cx, - |state, cx| { - let overlay_color = Self::tab_overlay_color(true, cx); - // Hovered will cause a render when the mouse enters regardless - // of if mouse position was accessed before - let hovered = state.hovered(); - let drag_position = cx - .global::>() - .currently_dragged::(cx.window_id()) - .filter(|_| hovered) - .map(|_| state.mouse_position()); - - Stack::new() - .with_child( - Flex::column() - .with_child( - ChildView::new(&self.toolbar, cx) - .expanded() - .boxed(), - ) - .with_child( - ChildView::new(active_item, cx) - .flex(1., true) - .boxed(), - ) - .boxed(), - ) - .with_children(drag_position.map(|drag_position| { - Canvas::new(move |region, _, cx| { - if region.contains_point(drag_position) { - let overlay_region = if let Some( - split_direction, - ) = - Self::drop_split_direction( - drag_position, - region, - 100., /* Replace with theme value */ - ) { - split_direction.along_edge(region, 100.) - } else { - region - }; - - cx.scene.push_quad(Quad { - bounds: overlay_region, - background: overlay_color, - border: Default::default(), - corner_radius: 0., - }); - } - }) + { + let toolbar = self.toolbar.clone(); + move |_, cx| { + Flex::column() + .with_child( + ChildView::new(&toolbar, cx).expanded().boxed(), + ) + .with_child( + ChildView::new(active_item, cx) + .flex(1., true) + .boxed(), + ) .boxed() - })) - .boxed() + } }, ) - .on_up(MouseButton::Left, { - let pane = cx.handle(); - move |event, cx| { - Pane::handle_dropped_item( - event, - &pane, - drop_index, - false, - Some(100.), /* Use theme value */ - cx, - ) - } - }) .flex(1., true) .boxed() }) @@ -1551,7 +1402,7 @@ impl View for Pane { enum EmptyPane {} let theme = cx.global::().theme.clone(); - MouseEventHandler::::new(0, cx, |_, _| { + dragged_item_receiver::(0, 0, false, None, cx, |_, _| { Empty::new() .contained() .with_background_color(theme.workspace.background) @@ -1560,12 +1411,6 @@ impl View for Pane { .on_down(MouseButton::Left, |_, cx| { cx.focus_parent_view(); }) - .on_up(MouseButton::Left, { - let pane = this.clone(); - move |event, cx| { - Pane::handle_dropped_item(event, &pane, 0, true, None, cx) - } - }) .boxed() } }) diff --git a/crates/workspace/src/pane/dragged_item_receiver.rs b/crates/workspace/src/pane/dragged_item_receiver.rs new file mode 100644 index 0000000000000000000000000000000000000000..afcb6b0fb9a086dfb5b09f1dab0af5aefb1b2c06 --- /dev/null +++ b/crates/workspace/src/pane/dragged_item_receiver.rs @@ -0,0 +1,133 @@ +use drag_and_drop::DragAndDrop; +use gpui::{ + color::Color, + elements::{Canvas, MouseEventHandler, ParentElement, Stack}, + geometry::{rect::RectF, vector::Vector2F}, + scene::MouseUp, + AppContext, Element, ElementBox, EventContext, MouseButton, MouseState, Quad, RenderContext, + WeakViewHandle, +}; +use settings::Settings; + +use crate::{MoveItem, Pane, SplitDirection, SplitWithItem, Workspace}; + +use super::DraggedItem; + +pub fn dragged_item_receiver( + region_id: usize, + drop_index: usize, + allow_same_pane: bool, + split_margin: Option, + cx: &mut RenderContext, + render_child: F, +) -> MouseEventHandler +where + Tag: 'static, + F: FnOnce(&mut MouseState, &mut RenderContext) -> ElementBox, +{ + MouseEventHandler::::above(region_id, cx, |state, cx| { + // Observing hovered will cause a render when the mouse enters regardless + // of if mouse position was accessed before + let hovered = state.hovered(); + let drag_position = cx + .global::>() + .currently_dragged::(cx.window_id()) + .filter(|_| hovered) + .map(|_| state.mouse_position()); + + Stack::new() + .with_child(render_child(state, cx)) + .with_children(drag_position.map(|drag_position| { + Canvas::new(move |bounds, _, cx| { + if bounds.contains_point(drag_position) { + let overlay_region = split_margin + .and_then(|split_margin| { + drop_split_direction(drag_position, bounds, split_margin) + .map(|dir| (dir, split_margin)) + }) + .map(|(dir, margin)| dir.along_edge(bounds, margin)) + .unwrap_or(bounds); + + cx.paint_stacking_context(None, |cx| { + cx.scene.push_quad(Quad { + bounds: overlay_region, + background: Some(overlay_color(cx)), + border: Default::default(), + corner_radius: 0., + }); + }); + } + }) + .boxed() + })) + .boxed() + }) + .on_up(MouseButton::Left, { + let pane = cx.handle(); + move |event, cx| { + handle_dropped_item(event, &pane, drop_index, allow_same_pane, split_margin, cx); + cx.notify(); + } + }) +} + +pub fn handle_dropped_item( + event: MouseUp, + pane: &WeakViewHandle, + index: usize, + allow_same_pane: bool, + split_margin: Option, + cx: &mut EventContext, +) { + if let Some((_, dragged_item)) = cx + .global::>() + .currently_dragged::(cx.window_id) + { + if let Some(split_direction) = split_margin + .and_then(|margin| drop_split_direction(event.position, event.region, margin)) + { + cx.dispatch_action(SplitWithItem { + from: dragged_item.pane.clone(), + item_id_to_move: dragged_item.item.id(), + pane_to_split: pane.clone(), + split_direction, + }); + } else if pane != &dragged_item.pane || allow_same_pane { + // If no split margin or not close enough to the edge, just move the item + cx.dispatch_action(MoveItem { + item_id: dragged_item.item.id(), + from: dragged_item.pane.clone(), + to: pane.clone(), + destination_index: index, + }) + } + } else { + cx.propagate_event(); + } +} + +fn drop_split_direction( + position: Vector2F, + region: RectF, + split_margin: f32, +) -> Option { + let mut min_direction = None; + let mut min_distance = split_margin; + for direction in SplitDirection::all() { + let edge_distance = (direction.edge(region) - direction.axis().component(position)).abs(); + + if edge_distance < min_distance { + min_direction = Some(direction); + min_distance = edge_distance; + } + } + + min_direction +} + +fn overlay_color(cx: &AppContext) -> Color { + cx.global::() + .theme + .workspace + .drop_target_overlay_color +} From f5795ffc6fe3d2dfe9c6741b26cd663969edae5e Mon Sep 17 00:00:00 2001 From: K Simmons Date: Sat, 22 Oct 2022 21:58:14 -0700 Subject: [PATCH 07/12] roll back mouse position in mouse_state struct in favor of using the dragged element position --- crates/gpui/src/app.rs | 15 --------------- crates/gpui/src/app/test_app_context.rs | 1 - crates/gpui/src/elements/mouse_event_handler.rs | 4 ---- crates/gpui/src/presenter.rs | 3 --- crates/gpui/src/scene/mouse_region.rs | 5 ----- .../workspace/src/pane/dragged_item_receiver.rs | 11 ++++++++++- 6 files changed, 10 insertions(+), 29 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index e7ce384f163b2b05a5c5a9f96e8c8abcb75d085c..3a2e850239285677d671910852dee63bd3b27756 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -3908,7 +3908,6 @@ pub struct RenderContext<'a, T: View> { pub(crate) window_id: usize, pub(crate) view_id: usize, pub(crate) view_type: PhantomData, - pub(crate) mouse_position: Vector2F, pub(crate) hovered_region_ids: HashSet, pub(crate) clicked_region_ids: Option<(HashSet, MouseButton)>, pub app: &'a mut MutableAppContext, @@ -3920,19 +3919,12 @@ pub struct RenderContext<'a, T: View> { #[derive(Clone, Default)] pub struct MouseState { hovered: bool, - mouse_position: Vector2F, clicked: Option, - accessed_mouse_position: bool, accessed_hovered: bool, accessed_clicked: bool, } impl MouseState { - pub fn mouse_position(&mut self) -> Vector2F { - self.accessed_mouse_position = true; - self.mouse_position - } - pub fn hovered(&mut self) -> bool { self.accessed_hovered = true; self.hovered @@ -3943,10 +3935,6 @@ impl MouseState { self.clicked } - pub fn accessed_mouse_position(&self) -> bool { - self.accessed_mouse_position - } - pub fn accessed_hovered(&self) -> bool { self.accessed_hovered } @@ -3964,7 +3952,6 @@ impl<'a, V: View> RenderContext<'a, V> { view_id: params.view_id, view_type: PhantomData, titlebar_height: params.titlebar_height, - mouse_position: params.mouse_position, hovered_region_ids: params.hovered_region_ids.clone(), clicked_region_ids: params.clicked_region_ids.clone(), refreshing: params.refreshing, @@ -3987,7 +3974,6 @@ impl<'a, V: View> RenderContext<'a, V> { pub fn mouse_state(&self, region_id: usize) -> MouseState { let region_id = MouseRegionId::new::(self.view_id, region_id); MouseState { - mouse_position: self.mouse_position.clone(), hovered: self.hovered_region_ids.contains(®ion_id), clicked: self.clicked_region_ids.as_ref().and_then(|(ids, button)| { if ids.contains(®ion_id) { @@ -3996,7 +3982,6 @@ impl<'a, V: View> RenderContext<'a, V> { None } }), - accessed_mouse_position: false, accessed_hovered: false, accessed_clicked: false, } diff --git a/crates/gpui/src/app/test_app_context.rs b/crates/gpui/src/app/test_app_context.rs index 0eebc6485d232f3e5153b508fb69bf27bdbd66c8..72f1f546fb8a32cb3c95fc932d2128fcbbc17cb7 100644 --- a/crates/gpui/src/app/test_app_context.rs +++ b/crates/gpui/src/app/test_app_context.rs @@ -186,7 +186,6 @@ impl TestAppContext { view_id: handle.id(), view_type: PhantomData, titlebar_height: 0., - mouse_position: Default::default(), hovered_region_ids: Default::default(), clicked_region_ids: None, refreshing: false, diff --git a/crates/gpui/src/elements/mouse_event_handler.rs b/crates/gpui/src/elements/mouse_event_handler.rs index 3548b1a38c3a895504a89f2d3d5fa87213f7298a..132a915f0b6f99313720b0b6e21ac134d862639f 100644 --- a/crates/gpui/src/elements/mouse_event_handler.rs +++ b/crates/gpui/src/elements/mouse_event_handler.rs @@ -21,7 +21,6 @@ pub struct MouseEventHandler { cursor_style: Option, handlers: HandlerSet, hoverable: bool, - notify_on_move: bool, notify_on_hover: bool, notify_on_click: bool, above: bool, @@ -39,7 +38,6 @@ impl MouseEventHandler { { let mut mouse_state = cx.mouse_state::(region_id); let child = render_child(&mut mouse_state, cx); - let notify_on_move = mouse_state.accessed_mouse_position(); let notify_on_hover = mouse_state.accessed_hovered(); let notify_on_click = mouse_state.accessed_clicked(); Self { @@ -47,7 +45,6 @@ impl MouseEventHandler { region_id, cursor_style: None, handlers: Default::default(), - notify_on_move, notify_on_hover, notify_on_click, hoverable: true, @@ -188,7 +185,6 @@ impl MouseEventHandler { self.handlers.clone(), ) .with_hoverable(self.hoverable) - .with_notify_on_move(self.notify_on_move) .with_notify_on_hover(self.notify_on_hover) .with_notify_on_click(self.notify_on_click), ); diff --git a/crates/gpui/src/presenter.rs b/crates/gpui/src/presenter.rs index b04dde7e063ae9dd4cc33aa2be7a45aadbc79cd2..f8c4482a06401128905248317dd339c9af8006b6 100644 --- a/crates/gpui/src/presenter.rs +++ b/crates/gpui/src/presenter.rs @@ -185,7 +185,6 @@ impl Presenter { asset_cache: &self.asset_cache, view_stack: Vec::new(), refreshing, - mouse_position: self.mouse_position.clone(), hovered_region_ids: self.hovered_region_ids.clone(), clicked_region_ids: self .clicked_button @@ -568,7 +567,6 @@ pub struct LayoutContext<'a> { pub window_size: Vector2F, titlebar_height: f32, appearance: Appearance, - mouse_position: Vector2F, hovered_region_ids: HashSet, clicked_region_ids: Option<(HashSet, MouseButton)>, } @@ -640,7 +638,6 @@ impl<'a> LayoutContext<'a> { view_id: handle.id(), view_type: PhantomData, titlebar_height: self.titlebar_height, - mouse_position: self.mouse_position.clone(), hovered_region_ids: self.hovered_region_ids.clone(), clicked_region_ids: self.clicked_region_ids.clone(), refreshing: self.refreshing, diff --git a/crates/gpui/src/scene/mouse_region.rs b/crates/gpui/src/scene/mouse_region.rs index f567fb7fe0a5ac50e006ec4c3c198405a83bd515..64c75153e4ee30ef02985ca4c450b6d8147566a6 100644 --- a/crates/gpui/src/scene/mouse_region.rs +++ b/crates/gpui/src/scene/mouse_region.rs @@ -138,11 +138,6 @@ impl MouseRegion { self } - pub fn with_notify_on_move(mut self, notify: bool) -> Self { - self.notify_on_move = notify; - self - } - pub fn with_notify_on_hover(mut self, notify: bool) -> Self { self.notify_on_hover = notify; self diff --git a/crates/workspace/src/pane/dragged_item_receiver.rs b/crates/workspace/src/pane/dragged_item_receiver.rs index afcb6b0fb9a086dfb5b09f1dab0af5aefb1b2c06..a3f1285e51303feb4961e522a4deb3a051ee6ae7 100644 --- a/crates/workspace/src/pane/dragged_item_receiver.rs +++ b/crates/workspace/src/pane/dragged_item_receiver.rs @@ -33,7 +33,7 @@ where .global::>() .currently_dragged::(cx.window_id()) .filter(|_| hovered) - .map(|_| state.mouse_position()); + .map(|(drag_position, _)| drag_position); Stack::new() .with_child(render_child(state, cx)) @@ -69,6 +69,15 @@ where cx.notify(); } }) + .on_move(|_, cx| { + if cx + .global::>() + .currently_dragged::(cx.window_id()) + .is_some() + { + cx.notify(); + } + }) } pub fn handle_dropped_item( From 4520227e98fdf3645fc7150bc55a0aeda09e95e8 Mon Sep 17 00:00:00 2001 From: K Simmons Date: Sat, 22 Oct 2022 22:02:22 -0700 Subject: [PATCH 08/12] remove mouse position from render params --- crates/gpui/src/app.rs | 3 --- crates/gpui/src/presenter.rs | 6 ------ 2 files changed, 9 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 3a2e850239285677d671910852dee63bd3b27756..5cbc786b725adce8ab4dc9997990b58c4e1a5197 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -21,7 +21,6 @@ use std::{ use anyhow::{anyhow, Context, Result}; use lazy_static::lazy_static; use parking_lot::Mutex; -use pathfinder_geometry::vector::Vector2F; use postage::oneshot; use smallvec::SmallVec; use smol::prelude::*; @@ -940,7 +939,6 @@ impl MutableAppContext { window_id, view_id, titlebar_height, - mouse_position: Default::default(), hovered_region_ids: Default::default(), clicked_region_ids: None, refreshing: false, @@ -3897,7 +3895,6 @@ pub struct RenderParams { pub window_id: usize, pub view_id: usize, pub titlebar_height: f32, - pub mouse_position: Vector2F, pub hovered_region_ids: HashSet, pub clicked_region_ids: Option<(HashSet, MouseButton)>, pub refreshing: bool, diff --git a/crates/gpui/src/presenter.rs b/crates/gpui/src/presenter.rs index f8c4482a06401128905248317dd339c9af8006b6..264e5af581a8824852c7ac6d008bcc4d3650694c 100644 --- a/crates/gpui/src/presenter.rs +++ b/crates/gpui/src/presenter.rs @@ -90,7 +90,6 @@ impl Presenter { window_id: self.window_id, view_id: *view_id, titlebar_height: self.titlebar_height, - mouse_position: self.mouse_position.clone(), hovered_region_ids: self.hovered_region_ids.clone(), clicked_region_ids: self .clicked_button @@ -117,7 +116,6 @@ impl Presenter { window_id: self.window_id, view_id: *view_id, titlebar_height: self.titlebar_height, - mouse_position: self.mouse_position.clone(), hovered_region_ids: self.hovered_region_ids.clone(), clicked_region_ids: self .clicked_button @@ -233,10 +231,6 @@ impl Presenter { let mut mouse_events = SmallVec::<[_; 2]>::new(); let mut notified_views: HashSet = Default::default(); - if let Some(mouse_position) = event.position() { - self.mouse_position = mouse_position; - } - // 1. Handle platform event. Keyboard events get dispatched immediately, while mouse events // get mapped into the mouse-specific MouseEvent type. // -> These are usually small: [Mouse Down] or [Mouse up, Click] or [Mouse Moved, Mouse Dragged?] From 345544646ac642ee7098f63a2f543a1f660c96a0 Mon Sep 17 00:00:00 2001 From: K Simmons Date: Sat, 22 Oct 2022 22:16:00 -0700 Subject: [PATCH 09/12] remove more notify on moves --- crates/gpui/src/presenter.rs | 7 ++----- crates/gpui/src/scene/mouse_region.rs | 2 -- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/crates/gpui/src/presenter.rs b/crates/gpui/src/presenter.rs index 264e5af581a8824852c7ac6d008bcc4d3650694c..43446394793f4468a7e4cd777105e67a664938e7 100644 --- a/crates/gpui/src/presenter.rs +++ b/crates/gpui/src/presenter.rs @@ -384,7 +384,7 @@ impl Presenter { //Ensure that hover entrance events aren't sent twice if self.hovered_region_ids.insert(region.id()) { valid_regions.push(region.clone()); - if region.notify_on_hover || region.notify_on_move { + if region.notify_on_hover { notified_views.insert(region.id().view_id()); } } @@ -392,7 +392,7 @@ impl Presenter { // Ensure that hover exit events aren't sent twice if self.hovered_region_ids.remove(®ion.id()) { valid_regions.push(region.clone()); - if region.notify_on_hover || region.notify_on_move { + if region.notify_on_hover { notified_views.insert(region.id().view_id()); } } @@ -451,9 +451,6 @@ impl Presenter { for (mouse_region, _) in self.mouse_regions.iter().rev() { if mouse_region.bounds.contains_point(self.mouse_position) { valid_regions.push(mouse_region.clone()); - if mouse_region.notify_on_move { - notified_views.insert(mouse_region.id().view_id()); - } } } } diff --git a/crates/gpui/src/scene/mouse_region.rs b/crates/gpui/src/scene/mouse_region.rs index 64c75153e4ee30ef02985ca4c450b6d8147566a6..4b5217cc2dec1355d350f921fa66233d8be39591 100644 --- a/crates/gpui/src/scene/mouse_region.rs +++ b/crates/gpui/src/scene/mouse_region.rs @@ -20,7 +20,6 @@ pub struct MouseRegion { pub bounds: RectF, pub handlers: HandlerSet, pub hoverable: bool, - pub notify_on_move: bool, pub notify_on_hover: bool, pub notify_on_click: bool, } @@ -55,7 +54,6 @@ impl MouseRegion { bounds, handlers, hoverable: true, - notify_on_move: false, notify_on_hover: false, notify_on_click: false, } From aed085b1689e8364e531a151497ff5036bce8be9 Mon Sep 17 00:00:00 2001 From: K Simmons Date: Sat, 22 Oct 2022 22:17:16 -0700 Subject: [PATCH 10/12] remove unnecessary Move branch in dispatch_events --- crates/gpui/src/presenter.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/crates/gpui/src/presenter.rs b/crates/gpui/src/presenter.rs index 43446394793f4468a7e4cd777105e67a664938e7..71803a4cf0862ac9101a64bdb0d2fd29cfeb6d6b 100644 --- a/crates/gpui/src/presenter.rs +++ b/crates/gpui/src/presenter.rs @@ -447,13 +447,6 @@ impl Presenter { } } } - MouseEvent::Move(_) => { - for (mouse_region, _) in self.mouse_regions.iter().rev() { - if mouse_region.bounds.contains_point(self.mouse_position) { - valid_regions.push(mouse_region.clone()); - } - } - } _ => { for (mouse_region, _) in self.mouse_regions.iter().rev() { // Contains From 113b7f6f97c375bc0a3399f740c3598eac55e865 Mon Sep 17 00:00:00 2001 From: K Simmons Date: Mon, 24 Oct 2022 23:47:43 -0700 Subject: [PATCH 11/12] tweak drop target overlay color and make stack fully constraint children by the first child 's size --- crates/gpui/src/elements/stack.rs | 14 +++++++++++--- crates/workspace/src/workspace.rs | 1 + styles/src/styleTree/workspace.ts | 4 ++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/crates/gpui/src/elements/stack.rs b/crates/gpui/src/elements/stack.rs index b81749787e10c44fcf0f1f680275a1bcc0241259..3b5c19505d1087c1878eead30a20a62daca9a10f 100644 --- a/crates/gpui/src/elements/stack.rs +++ b/crates/gpui/src/elements/stack.rs @@ -7,6 +7,8 @@ use crate::{ DebugContext, Element, ElementBox, LayoutContext, PaintContext, SizeConstraint, }; +/// Element which renders it's children in a stack on top of each other. +/// The first child determines the size of the others. #[derive(Default)] pub struct Stack { children: Vec, @@ -28,10 +30,16 @@ impl Element for Stack { cx: &mut LayoutContext, ) -> (Vector2F, Self::LayoutState) { let mut size = constraint.min; - for child in &mut self.children { - size = size.max(child.layout(constraint, cx)); - constraint.min = size; + let mut children = self.children.iter_mut(); + if let Some(bottom_child) = children.next() { + size = bottom_child.layout(constraint, cx); + constraint = SizeConstraint::strict(size); + } + + for child in children { + child.layout(constraint, cx); } + (size, ()) } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 4263f83ef106bcba0f473641e6555f3a9c0d66ae..349217985cb7801e3cadb7af35ccd38dcdaf393a 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -126,6 +126,7 @@ pub struct OpenSharedScreen { pub peer_id: PeerId, } +#[derive(Clone, PartialEq)] pub struct SplitWithItem { from: WeakViewHandle, pane_to_split: WeakViewHandle, diff --git a/styles/src/styleTree/workspace.ts b/styles/src/styleTree/workspace.ts index fea7df431339209611c3d407898937ae5fe413ff..50ee0c26da747be4e4c405d0390112957c7eca23 100644 --- a/styles/src/styleTree/workspace.ts +++ b/styles/src/styleTree/workspace.ts @@ -228,8 +228,8 @@ export default function workspace(colorScheme: ColorScheme) { }, }, dropTargetOverlayColor: withOpacity( - foreground(layer), - 0.6 + foreground(layer, "variant"), + 0.5 ), }; } From 2ac537393df968b110923b8a6eb326313503894a Mon Sep 17 00:00:00 2001 From: K Simmons Date: Tue, 25 Oct 2022 00:11:59 -0700 Subject: [PATCH 12/12] fix failing test --- Cargo.lock | 1 + crates/editor/Cargo.toml | 1 + crates/editor/src/editor_tests.rs | 2 ++ 3 files changed, 4 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 65b562c8edbf903a80d078f6d1e4c1c68e0b01ab..0c65493e26adbd2bc6be4cd0ffab89c731d71c77 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1736,6 +1736,7 @@ dependencies = [ "collections", "context_menu", "ctor", + "drag_and_drop", "env_logger", "futures 0.3.24", "fuzzy", diff --git a/crates/editor/Cargo.toml b/crates/editor/Cargo.toml index db634376d076cbca8d4e9a62651cc8f7eaefdcde..f56ed36f75076311ebbd3a778c7896f783b8268d 100644 --- a/crates/editor/Cargo.toml +++ b/crates/editor/Cargo.toml @@ -20,6 +20,7 @@ test-support = [ ] [dependencies] +drag_and_drop = { path = "../drag_and_drop" } text = { path = "../text" } clock = { path = "../clock" } collections = { path = "../collections" } diff --git a/crates/editor/src/editor_tests.rs b/crates/editor/src/editor_tests.rs index 58978c51f0d388bdcf9a2f0f8700777a34f34311..839f804ec30a31590f3c20c5c9900eea4e0191b6 100644 --- a/crates/editor/src/editor_tests.rs +++ b/crates/editor/src/editor_tests.rs @@ -1,5 +1,6 @@ use std::{cell::RefCell, rc::Rc, time::Instant}; +use drag_and_drop::DragAndDrop; use futures::StreamExt; use indoc::indoc; use unindent::Unindent; @@ -472,6 +473,7 @@ fn test_clone(cx: &mut gpui::MutableAppContext) { #[gpui::test] fn test_navigation_history(cx: &mut gpui::MutableAppContext) { cx.set_global(Settings::test(cx)); + cx.set_global(DragAndDrop::::default()); use workspace::Item; let (_, pane) = cx.add_window(Default::default(), |cx| Pane::new(None, cx)); let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);