Move to splits more ergonomically (#31449)
Part of https://github.com/zed-industries/zed/discussions/24889 Release Notes: - Made `workspace::MoveItemToPaneInDirection` and `workspace::MoveItemToPane` to create non-existing panes
This commit is contained in:
parent
2c8049270a
commit
f8365c5375
1 changed files with 175 additions and 15 deletions
|
@ -3344,17 +3344,38 @@ impl Workspace {
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
let Some(&target_pane) = self.center.panes().get(action.destination) else {
|
let panes = self.center.panes();
|
||||||
return;
|
let destination = match panes.get(action.destination) {
|
||||||
|
Some(&destination) => destination.clone(),
|
||||||
|
None => {
|
||||||
|
if self.active_pane.read(cx).items_len() < 2 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let direction = SplitDirection::Right;
|
||||||
|
let split_off_pane = self
|
||||||
|
.find_pane_in_direction(direction, cx)
|
||||||
|
.unwrap_or_else(|| self.active_pane.clone());
|
||||||
|
let new_pane = self.add_pane(window, cx);
|
||||||
|
if self
|
||||||
|
.center
|
||||||
|
.split(&split_off_pane, &new_pane, direction)
|
||||||
|
.log_err()
|
||||||
|
.is_none()
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
new_pane
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
move_active_item(
|
move_active_item(
|
||||||
&self.active_pane,
|
&self.active_pane,
|
||||||
target_pane,
|
&destination,
|
||||||
action.focus,
|
action.focus,
|
||||||
true,
|
true,
|
||||||
window,
|
window,
|
||||||
cx,
|
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) {
|
||||||
|
@ -3486,18 +3507,35 @@ impl Workspace {
|
||||||
&mut self,
|
&mut self,
|
||||||
action: &MoveItemToPaneInDirection,
|
action: &MoveItemToPaneInDirection,
|
||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut App,
|
cx: &mut Context<Self>,
|
||||||
) {
|
) {
|
||||||
if let Some(destination) = self.find_pane_in_direction(action.direction, cx) {
|
let destination = match self.find_pane_in_direction(action.direction, cx) {
|
||||||
move_active_item(
|
Some(destination) => destination,
|
||||||
&self.active_pane,
|
None => {
|
||||||
&destination,
|
if self.active_pane.read(cx).items_len() < 2 {
|
||||||
action.focus,
|
return;
|
||||||
true,
|
}
|
||||||
window,
|
let new_pane = self.add_pane(window, cx);
|
||||||
cx,
|
if self
|
||||||
);
|
.center
|
||||||
}
|
.split(&self.active_pane, &new_pane, action.direction)
|
||||||
|
.log_err()
|
||||||
|
.is_none()
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
new_pane
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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>> {
|
||||||
|
@ -9333,6 +9371,117 @@ mod tests {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[gpui::test]
|
||||||
|
async fn test_moving_items_create_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,
|
||||||
|
},
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
workspace.move_item_to_pane_at_index(
|
||||||
|
&MoveItemToPane {
|
||||||
|
destination: 3,
|
||||||
|
focus: true,
|
||||||
|
},
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(workspace.panes.len(), 1, "No new panes were created");
|
||||||
|
assert_eq!(
|
||||||
|
pane_items_paths(&workspace.active_pane, cx),
|
||||||
|
vec!["first.txt".to_string()],
|
||||||
|
"Single item was not moved anywhere"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
let item_2 = cx.new(|cx| {
|
||||||
|
TestItem::new(cx).with_project_items(&[TestProjectItem::new(2, "second.txt", cx)])
|
||||||
|
});
|
||||||
|
workspace.update_in(cx, |workspace, window, cx| {
|
||||||
|
workspace.add_item_to_active_pane(Box::new(item_2), None, true, window, cx);
|
||||||
|
assert_eq!(
|
||||||
|
pane_items_paths(&workspace.panes[0], cx),
|
||||||
|
vec!["first.txt".to_string(), "second.txt".to_string()],
|
||||||
|
);
|
||||||
|
workspace.move_item_to_pane_in_direction(
|
||||||
|
&MoveItemToPaneInDirection {
|
||||||
|
direction: SplitDirection::Right,
|
||||||
|
focus: true,
|
||||||
|
},
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(workspace.panes.len(), 2, "A new pane should be created");
|
||||||
|
assert_eq!(
|
||||||
|
pane_items_paths(&workspace.panes[0], cx),
|
||||||
|
vec!["first.txt".to_string()],
|
||||||
|
"After moving, one item should be left in the original pane"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
pane_items_paths(&workspace.panes[1], cx),
|
||||||
|
vec!["second.txt".to_string()],
|
||||||
|
"New item should have been moved to the new pane"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
let item_3 = cx.new(|cx| {
|
||||||
|
TestItem::new(cx).with_project_items(&[TestProjectItem::new(3, "third.txt", cx)])
|
||||||
|
});
|
||||||
|
workspace.update_in(cx, |workspace, window, cx| {
|
||||||
|
let original_pane = workspace.panes[0].clone();
|
||||||
|
workspace.set_active_pane(&original_pane, window, cx);
|
||||||
|
workspace.add_item_to_active_pane(Box::new(item_3), None, true, window, cx);
|
||||||
|
assert_eq!(workspace.panes.len(), 2, "No new panes were created");
|
||||||
|
assert_eq!(
|
||||||
|
pane_items_paths(&workspace.active_pane, cx),
|
||||||
|
vec!["first.txt".to_string(), "third.txt".to_string()],
|
||||||
|
"New pane should be ready to move one item out"
|
||||||
|
);
|
||||||
|
|
||||||
|
workspace.move_item_to_pane_at_index(
|
||||||
|
&MoveItemToPane {
|
||||||
|
destination: 3,
|
||||||
|
focus: true,
|
||||||
|
},
|
||||||
|
window,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
assert_eq!(workspace.panes.len(), 3, "A new pane should be created");
|
||||||
|
assert_eq!(
|
||||||
|
pane_items_paths(&workspace.active_pane, cx),
|
||||||
|
vec!["first.txt".to_string()],
|
||||||
|
"After moving, one item should be left in the original pane"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
pane_items_paths(&workspace.panes[1], cx),
|
||||||
|
vec!["second.txt".to_string()],
|
||||||
|
"Previously created pane should be unchanged"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
pane_items_paths(&workspace.panes[2], cx),
|
||||||
|
vec!["third.txt".to_string()],
|
||||||
|
"New item should have been moved to the new pane"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
mod register_project_item_tests {
|
mod register_project_item_tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -9648,6 +9797,17 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pane_items_paths(pane: &Entity<Pane>, cx: &App) -> Vec<String> {
|
||||||
|
pane.read(cx)
|
||||||
|
.items()
|
||||||
|
.flat_map(|item| {
|
||||||
|
item.project_paths(cx)
|
||||||
|
.into_iter()
|
||||||
|
.map(|path| path.path.to_string_lossy().to_string())
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn init_test(cx: &mut TestAppContext) {
|
pub fn init_test(cx: &mut TestAppContext) {
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
let settings_store = SettingsStore::test(cx);
|
let settings_store = SettingsStore::test(cx);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue