This commit is contained in:
K Simmons 2022-08-12 16:11:15 -07:00
parent 03a344a272
commit 0d6125889f
4 changed files with 191 additions and 105 deletions

View file

@ -1,7 +0,0 @@
[target.'cfg(all())']
rustflags = [
"-Dwarnings",
"-Aclippy::reversed_empty_ranges",
"-Aclippy::missing_safety_doc",
"-Aclippy::let_unit_value",
]

View file

@ -1,4 +1,4 @@
use std::{any::Any, sync::Arc}; use std::{any::Any, rc::Rc};
use gpui::{ use gpui::{
elements::{Container, MouseEventHandler}, elements::{Container, MouseEventHandler},
@ -10,8 +10,8 @@ use gpui::{
struct State<V: View> { struct State<V: View> {
position: Vector2F, position: Vector2F,
region_offset: Vector2F, region_offset: Vector2F,
payload: Arc<dyn Any>, payload: Rc<dyn Any + 'static>,
render: Arc<dyn Fn(Arc<dyn Any>, &mut RenderContext<V>) -> ElementBox>, render: Rc<dyn Fn(Rc<dyn Any>, &mut RenderContext<V>) -> ElementBox>,
} }
impl<V: View> Clone for State<V> { impl<V: View> Clone for State<V> {
@ -46,13 +46,15 @@ impl<V: View> DragAndDrop<V> {
} }
} }
pub fn currently_dragged<T: Any>(&self) -> Option<(Vector2F, &T)> { pub fn currently_dragged<T: Any>(&self) -> Option<(Vector2F, Rc<T>)> {
self.currently_dragged.as_ref().and_then( self.currently_dragged.as_ref().and_then(
|State { |State {
position, payload, .. position, payload, ..
}| { }| {
payload payload
.downcast_ref::<T>() .clone()
.downcast::<T>()
.ok()
.map(|payload| (position.clone(), payload)) .map(|payload| (position.clone(), payload))
}, },
) )
@ -61,9 +63,9 @@ impl<V: View> DragAndDrop<V> {
pub fn dragging<T: Any>( pub fn dragging<T: Any>(
relative_to: Option<RectF>, relative_to: Option<RectF>,
position: Vector2F, position: Vector2F,
payload: Arc<T>, payload: Rc<T>,
cx: &mut EventContext, cx: &mut EventContext,
render: Arc<impl 'static + Fn(&T, &mut RenderContext<V>) -> ElementBox>, render: Rc<impl 'static + Fn(&T, &mut RenderContext<V>) -> ElementBox>,
) { ) {
cx.update_global::<Self, _, _>(|this, cx| { cx.update_global::<Self, _, _>(|this, cx| {
let region_offset = if let Some(previous_state) = this.currently_dragged.as_ref() { let region_offset = if let Some(previous_state) = this.currently_dragged.as_ref() {
@ -80,7 +82,7 @@ impl<V: View> DragAndDrop<V> {
region_offset, region_offset,
position, position,
payload, payload,
render: Arc::new(move |payload, cx| { render: Rc::new(move |payload, cx| {
render(payload.downcast_ref::<T>().unwrap(), cx) render(payload.downcast_ref::<T>().unwrap(), cx)
}), }),
}); });
@ -143,8 +145,8 @@ impl Draggable for MouseEventHandler {
where where
Self: Sized, Self: Sized,
{ {
let payload = Arc::new(payload); let payload = Rc::new(payload);
let render = Arc::new(render); let render = Rc::new(render);
self.on_drag(MouseButton::Left, move |e, cx| { self.on_drag(MouseButton::Left, move |e, cx| {
let payload = payload.clone(); let payload = payload.clone();
let render = render.clone(); let render = render.clone();

View file

@ -224,6 +224,8 @@ impl Presenter {
Event::MouseDown(e @ MouseButtonEvent { position, .. }) => { Event::MouseDown(e @ MouseButtonEvent { position, .. }) => {
for (region, _) in self.mouse_regions.iter().rev() { for (region, _) in self.mouse_regions.iter().rev() {
if region.bounds.contains_point(*position) { if region.bounds.contains_point(*position) {
self.clicked_region = Some(region.clone());
self.prev_drag_position = Some(*position);
events_to_send.push(( events_to_send.push((
region.clone(), region.clone(),
MouseRegionEvent::Down(DownRegionEvent { MouseRegionEvent::Down(DownRegionEvent {
@ -277,19 +279,19 @@ impl Presenter {
} }
} }
Event::MouseMoved(e @ MouseMovedEvent { position, .. }) => { Event::MouseMoved(e @ MouseMovedEvent { position, .. }) => {
if let Some((clicked_region, prev_drag_position)) = self if let Some(clicked_region) = self.clicked_region.as_ref() {
.clicked_region if let Some(prev_drag_position) = self.prev_drag_position {
.as_ref() events_to_send.push((
.zip(self.prev_drag_position.as_mut()) clicked_region.clone(),
{ MouseRegionEvent::Drag(DragRegionEvent {
events_to_send.push(( region: clicked_region.bounds,
clicked_region.clone(), prev_drag_position,
MouseRegionEvent::Drag(DragRegionEvent { platform_event: e.clone(),
region: clicked_region.bounds, }),
prev_drag_position: *prev_drag_position, ));
platform_event: e.clone(), }
}),
)); self.prev_drag_position = Some(*position)
} }
for (region, _) in self.mouse_regions.iter().rev() { for (region, _) in self.mouse_regions.iter().rev() {
@ -417,8 +419,6 @@ impl Presenter {
view_stack: Default::default(), view_stack: Default::default(),
invalidated_views: Default::default(), invalidated_views: Default::default(),
notify_count: 0, notify_count: 0,
clicked_region: &mut self.clicked_region,
prev_drag_position: &mut self.prev_drag_position,
handled: false, handled: false,
window_id: self.window_id, window_id: self.window_id,
app: cx, app: cx,
@ -639,8 +639,6 @@ pub struct EventContext<'a> {
pub window_id: usize, pub window_id: usize,
pub notify_count: usize, pub notify_count: usize,
view_stack: Vec<usize>, view_stack: Vec<usize>,
clicked_region: &'a mut Option<MouseRegion>,
prev_drag_position: &'a mut Option<Vector2F>,
handled: bool, handled: bool,
invalidated_views: HashSet<usize>, invalidated_views: HashSet<usize>,
} }
@ -664,17 +662,6 @@ impl<'a> EventContext<'a> {
continue; continue;
} }
match &event {
MouseRegionEvent::Down(e) => {
*self.clicked_region = Some(region.clone());
*self.prev_drag_position = Some(e.position);
}
MouseRegionEvent::Drag(e) => {
*self.prev_drag_position = Some(e.position);
}
_ => {}
}
self.invalidated_views.insert(region.view_id); self.invalidated_views.insert(region.view_id);
} }

View file

@ -3,7 +3,7 @@ use crate::{toolbar::Toolbar, Item, NewFile, NewSearch, NewTerminal, WeakItemHan
use anyhow::Result; use anyhow::Result;
use collections::{HashMap, HashSet, VecDeque}; use collections::{HashMap, HashSet, VecDeque};
use context_menu::{ContextMenu, ContextMenuItem}; use context_menu::{ContextMenu, ContextMenuItem};
use drag_and_drop::Draggable; use drag_and_drop::{DragAndDrop, Draggable};
use futures::StreamExt; use futures::StreamExt;
use gpui::{ use gpui::{
actions, actions,
@ -49,6 +49,14 @@ pub struct CloseItem {
pub pane: WeakViewHandle<Pane>, pub pane: WeakViewHandle<Pane>,
} }
#[derive(Clone, PartialEq)]
pub struct MoveItem {
pub item_id: usize,
pub from: WeakViewHandle<Pane>,
pub to: WeakViewHandle<Pane>,
pub destination_index: usize,
}
#[derive(Clone, Deserialize, PartialEq)] #[derive(Clone, Deserialize, PartialEq)]
pub struct GoBack { pub struct GoBack {
#[serde(skip_deserializing)] #[serde(skip_deserializing)]
@ -72,7 +80,7 @@ pub struct DeployNewMenu {
} }
impl_actions!(pane, [GoBack, GoForward, ActivateItem]); impl_actions!(pane, [GoBack, GoForward, ActivateItem]);
impl_internal_actions!(pane, [CloseItem, DeploySplitMenu, DeployNewMenu]); impl_internal_actions!(pane, [CloseItem, DeploySplitMenu, DeployNewMenu, MoveItem]);
const MAX_NAVIGATION_HISTORY_LEN: usize = 1024; const MAX_NAVIGATION_HISTORY_LEN: usize = 1024;
@ -99,6 +107,47 @@ pub fn init(cx: &mut MutableAppContext) {
Ok(()) Ok(())
})) }))
}); });
cx.add_action(|workspace: &mut Workspace, action: &MoveItem, cx| {
// Get item handle to move
let from = if let Some(from) = action.from.upgrade(cx) {
from
} else {
return;
};
let (item_ix, item_handle) = from
.read(cx)
.items()
.enumerate()
.find(|(_, item_handle)| item_handle.id() == action.item_id)
.expect("Tried to move item handle which was not in from pane");
// Add item to new pane at given index
let to = if let Some(to) = action.to.upgrade(cx) {
to
} else {
return;
};
// This automatically removes duplicate items in the pane
Pane::add_item_at(
workspace,
to,
item_handle.clone(),
true,
true,
Some(action.destination_index),
cx,
);
if action.from != action.to {
// Close item from previous pane
from.update(cx, |from, cx| {
from.remove_item(item_ix, cx);
dbg!(from.items().collect::<Vec<_>>());
});
}
});
cx.add_action(|pane: &mut Pane, _: &SplitLeft, cx| pane.split(SplitDirection::Left, cx)); cx.add_action(|pane: &mut Pane, _: &SplitLeft, cx| pane.split(SplitDirection::Left, cx));
cx.add_action(|pane: &mut Pane, _: &SplitUp, cx| pane.split(SplitDirection::Up, cx)); cx.add_action(|pane: &mut Pane, _: &SplitUp, cx| pane.split(SplitDirection::Up, cx));
cx.add_action(|pane: &mut Pane, _: &SplitRight, cx| pane.split(SplitDirection::Right, cx)); cx.add_action(|pane: &mut Pane, _: &SplitRight, cx| pane.split(SplitDirection::Right, cx));
@ -408,12 +457,13 @@ impl Pane {
} }
} }
pub(crate) fn add_item( pub fn add_item_at(
workspace: &mut Workspace, workspace: &mut Workspace,
pane: ViewHandle<Pane>, pane: ViewHandle<Pane>,
item: Box<dyn ItemHandle>, item: Box<dyn ItemHandle>,
activate_pane: bool, activate_pane: bool,
focus_item: bool, focus_item: bool,
destination_index: Option<usize>,
cx: &mut ViewContext<Workspace>, cx: &mut ViewContext<Workspace>,
) { ) {
// Prevent adding the same item to the pane more than once. // Prevent adding the same item to the pane more than once.
@ -428,16 +478,20 @@ impl Pane {
item.added_to_pane(workspace, pane.clone(), cx); item.added_to_pane(workspace, pane.clone(), cx);
pane.update(cx, |pane, cx| { pane.update(cx, |pane, cx| {
// If there is already an active item, then insert the new item let item_ix = if let Some(destination_index) = destination_index {
// right after it. Otherwise, adjust the `active_item_index` field destination_index
// before activating the new item, so that in the `activate_item`
// method, we can detect that the active item is changing.
let item_ix;
if pane.active_item_index < pane.items.len() {
item_ix = pane.active_item_index + 1
} else { } else {
item_ix = pane.items.len(); // If there is already an active item, then insert the new item
pane.active_item_index = usize::MAX; // right after it. Otherwise, adjust the `active_item_index` field
// before activating the new item, so that in the `activate_item`
// method, we can detect that the active item is changing.
if pane.active_item_index < pane.items.len() {
pane.active_item_index + 1
} else {
let ix = pane.items.len();
pane.active_item_index = usize::MAX;
ix
}
}; };
cx.reparent(&item); cx.reparent(&item);
@ -447,6 +501,17 @@ impl Pane {
}); });
} }
pub(crate) fn add_item(
workspace: &mut Workspace,
pane: ViewHandle<Pane>,
item: Box<dyn ItemHandle>,
activate_pane: bool,
focus_item: bool,
cx: &mut ViewContext<Workspace>,
) {
Self::add_item_at(workspace, pane, item, activate_pane, focus_item, None, cx)
}
pub fn items(&self) -> impl Iterator<Item = &Box<dyn ItemHandle>> { pub fn items(&self) -> impl Iterator<Item = &Box<dyn ItemHandle>> {
self.items.iter() self.items.iter()
} }
@ -673,48 +738,7 @@ impl Pane {
// Remove the item from the pane. // Remove the item from the pane.
pane.update(&mut cx, |pane, cx| { pane.update(&mut cx, |pane, cx| {
if let Some(item_ix) = pane.items.iter().position(|i| i.id() == item.id()) { if let Some(item_ix) = pane.items.iter().position(|i| i.id() == item.id()) {
if item_ix == pane.active_item_index { pane.remove_item(item_ix, cx);
// Activate the previous item if possible.
// This returns the user to the previously opened tab if they closed
// a ne item they just navigated to.
if item_ix > 0 {
pane.activate_prev_item(cx);
} else if item_ix + 1 < pane.items.len() {
pane.activate_next_item(cx);
}
}
let item = pane.items.remove(item_ix);
cx.emit(Event::RemoveItem);
if pane.items.is_empty() {
item.deactivated(cx);
pane.update_toolbar(cx);
cx.emit(Event::Remove);
}
if item_ix < pane.active_item_index {
pane.active_item_index -= 1;
}
pane.nav_history
.borrow_mut()
.set_mode(NavigationMode::ClosingItem);
item.deactivated(cx);
pane.nav_history
.borrow_mut()
.set_mode(NavigationMode::Normal);
if let Some(path) = item.project_path(cx) {
pane.nav_history
.borrow_mut()
.paths_by_item
.insert(item.id(), path);
} else {
pane.nav_history
.borrow_mut()
.paths_by_item
.remove(&item.id());
}
} }
}); });
} }
@ -724,6 +748,53 @@ impl Pane {
}) })
} }
fn remove_item(&mut self, item_ix: usize, cx: &mut ViewContext<Self>) {
if item_ix == self.active_item_index {
// Activate the previous item if possible.
// This returns the user to the previously opened tab if they closed
// a new item they just navigated to.
if item_ix > 0 {
self.activate_prev_item(cx);
} else if item_ix + 1 < self.items.len() {
self.activate_next_item(cx);
}
}
let item = self.items.remove(item_ix);
cx.emit(Event::RemoveItem);
if self.items.is_empty() {
item.deactivated(cx);
self.update_toolbar(cx);
cx.emit(Event::Remove);
}
if item_ix < self.active_item_index {
self.active_item_index -= 1;
}
self.nav_history
.borrow_mut()
.set_mode(NavigationMode::ClosingItem);
item.deactivated(cx);
self.nav_history
.borrow_mut()
.set_mode(NavigationMode::Normal);
if let Some(path) = item.project_path(cx) {
self.nav_history
.borrow_mut()
.paths_by_item
.insert(item.id(), path);
} else {
self.nav_history
.borrow_mut()
.paths_by_item
.remove(&item.id());
}
cx.notify();
}
pub async fn save_item( pub async fn save_item(
project: ModelHandle<Project>, project: ModelHandle<Project>,
pane: &ViewHandle<Pane>, pane: &ViewHandle<Pane>,
@ -880,6 +951,11 @@ impl Pane {
fn render_tab_bar(&mut self, cx: &mut RenderContext<Self>) -> impl Element { fn render_tab_bar(&mut self, cx: &mut RenderContext<Self>) -> impl Element {
let theme = cx.global::<Settings>().theme.clone(); let theme = cx.global::<Settings>().theme.clone();
struct DraggedItem {
item: Box<dyn ItemHandle>,
pane: WeakViewHandle<Pane>,
}
enum Tabs {} enum Tabs {}
enum Tab {} enum Tab {}
let pane = cx.handle(); let pane = cx.handle();
@ -940,15 +1016,43 @@ impl Pane {
}) })
} }
}) })
.as_draggable(item, { .on_up(MouseButton::Left, {
let pane = pane.clone(); let pane = pane.clone();
let detail = detail.clone(); move |_, cx: &mut EventContext| {
if let Some((_, dragged_item)) = cx
move |item, cx: &mut RenderContext<Workspace>| { .global::<DragAndDrop<Workspace>>()
let pane = pane.clone(); .currently_dragged::<DraggedItem>()
Pane::render_tab(item, pane, detail, false, pane_active, tab_active, cx) {
cx.dispatch_action(MoveItem {
item_id: dragged_item.item.id(),
from: dragged_item.pane.clone(),
to: pane.clone(),
destination_index: ix,
})
}
cx.propogate_event();
} }
}) })
.as_draggable(
DraggedItem {
item,
pane: pane.clone(),
},
{
let detail = detail.clone();
move |dragged_item, cx: &mut RenderContext<Workspace>| {
Pane::render_tab(
&dragged_item.item,
dragged_item.pane.clone(),
detail,
false,
pane_active,
tab_active,
cx,
)
}
},
)
.boxed() .boxed()
}) })
} }