Add ability to clone item when using workspace::MoveItemToPane
(#32895)
This PR adds an optional `clone: bool` argument to `workspace::MoveItemToPane` and `workspace::MoveItemToPaneInDirection` which causes the item to be cloned into the destination pane rather than moved. It provides similar functionality to `workbench.action.splitEditorToRightGroup` in vscode. This PR supercedes #25030. Closes #24889 Release Notes: - Add optional `clone: bool` (default: `false`) to `workspace::MoveItemToPane` and `workspace::MoveItemToPaneInDirection` which causes the item to be cloned into the destination pane rather than moved.
This commit is contained in:
parent
324cbecb74
commit
95f10fd187
1 changed files with 132 additions and 18 deletions
|
@ -222,6 +222,8 @@ pub struct MoveItemToPane {
|
||||||
pub destination: usize,
|
pub destination: usize,
|
||||||
#[serde(default = "default_true")]
|
#[serde(default = "default_true")]
|
||||||
pub focus: bool,
|
pub focus: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub clone: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, PartialEq, JsonSchema)]
|
#[derive(Clone, Deserialize, PartialEq, JsonSchema)]
|
||||||
|
@ -230,6 +232,8 @@ pub struct MoveItemToPaneInDirection {
|
||||||
pub direction: SplitDirection,
|
pub direction: SplitDirection,
|
||||||
#[serde(default = "default_true")]
|
#[serde(default = "default_true")]
|
||||||
pub focus: bool,
|
pub focus: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub clone: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema)]
|
#[derive(Clone, PartialEq, Debug, Deserialize, JsonSchema)]
|
||||||
|
@ -3355,7 +3359,7 @@ impl Workspace {
|
||||||
let destination = match panes.get(action.destination) {
|
let destination = match panes.get(action.destination) {
|
||||||
Some(&destination) => destination.clone(),
|
Some(&destination) => destination.clone(),
|
||||||
None => {
|
None => {
|
||||||
if self.active_pane.read(cx).items_len() < 2 {
|
if !action.clone && self.active_pane.read(cx).items_len() < 2 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let direction = SplitDirection::Right;
|
let direction = SplitDirection::Right;
|
||||||
|
@ -3375,14 +3379,25 @@ impl Workspace {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
move_active_item(
|
if action.clone {
|
||||||
&self.active_pane,
|
clone_active_item(
|
||||||
&destination,
|
self.database_id(),
|
||||||
action.focus,
|
&self.active_pane,
|
||||||
true,
|
&destination,
|
||||||
window,
|
action.focus,
|
||||||
cx,
|
window,
|
||||||
)
|
cx,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
move_active_item(
|
||||||
|
&self.active_pane,
|
||||||
|
&destination,
|
||||||
|
action.focus,
|
||||||
|
true,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn activate_next_pane(&mut self, window: &mut Window, cx: &mut App) {
|
pub fn activate_next_pane(&mut self, window: &mut Window, cx: &mut App) {
|
||||||
|
@ -3526,7 +3541,7 @@ impl Workspace {
|
||||||
let destination = match self.find_pane_in_direction(action.direction, cx) {
|
let destination = match self.find_pane_in_direction(action.direction, cx) {
|
||||||
Some(destination) => destination,
|
Some(destination) => destination,
|
||||||
None => {
|
None => {
|
||||||
if self.active_pane.read(cx).items_len() < 2 {
|
if !action.clone && self.active_pane.read(cx).items_len() < 2 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let new_pane = self.add_pane(window, cx);
|
let new_pane = self.add_pane(window, cx);
|
||||||
|
@ -3542,14 +3557,25 @@ impl Workspace {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
move_active_item(
|
if action.clone {
|
||||||
&self.active_pane,
|
clone_active_item(
|
||||||
&destination,
|
self.database_id(),
|
||||||
action.focus,
|
&self.active_pane,
|
||||||
true,
|
&destination,
|
||||||
window,
|
action.focus,
|
||||||
cx,
|
window,
|
||||||
);
|
cx,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
move_active_item(
|
||||||
|
&self.active_pane,
|
||||||
|
&destination,
|
||||||
|
action.focus,
|
||||||
|
true,
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bounding_box_for_pane(&self, pane: &Entity<Pane>) -> Option<Bounds<Pixels>> {
|
pub fn bounding_box_for_pane(&self, pane: &Entity<Pane>) -> Option<Bounds<Pixels>> {
|
||||||
|
@ -7631,6 +7657,35 @@ pub fn move_active_item(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn clone_active_item(
|
||||||
|
workspace_id: Option<WorkspaceId>,
|
||||||
|
source: &Entity<Pane>,
|
||||||
|
destination: &Entity<Pane>,
|
||||||
|
focus_destination: bool,
|
||||||
|
window: &mut Window,
|
||||||
|
cx: &mut App,
|
||||||
|
) {
|
||||||
|
if source == destination {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let Some(active_item) = source.read(cx).active_item() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
destination.update(cx, |target_pane, cx| {
|
||||||
|
let Some(clone) = active_item.clone_on_split(workspace_id, window, cx) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
target_pane.add_item(
|
||||||
|
clone,
|
||||||
|
focus_destination,
|
||||||
|
focus_destination,
|
||||||
|
Some(target_pane.items_len()),
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct WorkspacePosition {
|
pub struct WorkspacePosition {
|
||||||
pub window_bounds: Option<WindowBounds>,
|
pub window_bounds: Option<WindowBounds>,
|
||||||
|
@ -9814,6 +9869,7 @@ mod tests {
|
||||||
&MoveItemToPaneInDirection {
|
&MoveItemToPaneInDirection {
|
||||||
direction: SplitDirection::Right,
|
direction: SplitDirection::Right,
|
||||||
focus: true,
|
focus: true,
|
||||||
|
clone: false,
|
||||||
},
|
},
|
||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
|
@ -9822,6 +9878,7 @@ mod tests {
|
||||||
&MoveItemToPane {
|
&MoveItemToPane {
|
||||||
destination: 3,
|
destination: 3,
|
||||||
focus: true,
|
focus: true,
|
||||||
|
clone: false,
|
||||||
},
|
},
|
||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
|
@ -9848,6 +9905,7 @@ mod tests {
|
||||||
&MoveItemToPaneInDirection {
|
&MoveItemToPaneInDirection {
|
||||||
direction: SplitDirection::Right,
|
direction: SplitDirection::Right,
|
||||||
focus: true,
|
focus: true,
|
||||||
|
clone: false,
|
||||||
},
|
},
|
||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
|
@ -9884,6 +9942,7 @@ mod tests {
|
||||||
&MoveItemToPane {
|
&MoveItemToPane {
|
||||||
destination: 3,
|
destination: 3,
|
||||||
focus: true,
|
focus: true,
|
||||||
|
clone: false,
|
||||||
},
|
},
|
||||||
window,
|
window,
|
||||||
cx,
|
cx,
|
||||||
|
@ -9907,6 +9966,61 @@ mod tests {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_moving_items_can_clone_panes(cx: &mut TestAppContext) {
|
||||||
|
init_test(cx);
|
||||||
|
|
||||||
|
let fs = FakeFs::new(cx.executor());
|
||||||
|
let project = Project::test(fs, [], cx).await;
|
||||||
|
let (workspace, cx) =
|
||||||
|
cx.add_window_view(|window, cx| Workspace::test_new(project.clone(), window, cx));
|
||||||
|
|
||||||
|
let item_1 = cx.new(|cx| {
|
||||||
|
TestItem::new(cx).with_project_items(&[TestProjectItem::new(1, "first.txt", cx)])
|
||||||
|
});
|
||||||
|
workspace.update_in(cx, |workspace, window, cx| {
|
||||||
|
workspace.add_item_to_active_pane(Box::new(item_1), None, true, window, cx);
|
||||||
|
workspace.move_item_to_pane_in_direction(
|
||||||
|
&MoveItemToPaneInDirection {
|
||||||
|
direction: SplitDirection::Right,
|
||||||
|
focus: true,
|
||||||
|
clone: true,
|
||||||
|
},
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
workspace.move_item_to_pane_at_index(
|
||||||
|
&MoveItemToPane {
|
||||||
|
destination: 3,
|
||||||
|
focus: true,
|
||||||
|
clone: true,
|
||||||
|
},
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(workspace.panes.len(), 3, "Two new panes were created");
|
||||||
|
for pane in workspace.panes() {
|
||||||
|
assert_eq!(
|
||||||
|
pane_items_paths(pane, cx),
|
||||||
|
vec!["first.txt".to_string()],
|
||||||
|
"Single item exists in all panes"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// verify that the active pane has been updated after waiting for the
|
||||||
|
// pane focus event to fire and resolve
|
||||||
|
workspace.read_with(cx, |workspace, _app| {
|
||||||
|
assert_eq!(
|
||||||
|
workspace.active_pane(),
|
||||||
|
&workspace.panes[2],
|
||||||
|
"The third pane should be the active one: {:?}",
|
||||||
|
workspace.panes
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
mod register_project_item_tests {
|
mod register_project_item_tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue