Workspace move editor actions (#21760)
Closes #20205 Release Notes: - Added `MoveItemToPane` and `MoveItemToPaneInDirection` actions --------- Co-authored-by: Kirill Bulatov <kirill@zed.dev>
This commit is contained in:
parent
9082a006d6
commit
e1bc48c554
4 changed files with 150 additions and 20 deletions
|
@ -4,12 +4,32 @@
|
||||||
"ctrl-shift-[": "pane::ActivatePrevItem",
|
"ctrl-shift-[": "pane::ActivatePrevItem",
|
||||||
"ctrl-shift-]": "pane::ActivateNextItem",
|
"ctrl-shift-]": "pane::ActivateNextItem",
|
||||||
"ctrl-pageup": "pane::ActivatePrevItem",
|
"ctrl-pageup": "pane::ActivatePrevItem",
|
||||||
"ctrl-pagedown": "pane::ActivateNextItem"
|
"ctrl-pagedown": "pane::ActivateNextItem",
|
||||||
|
"ctrl-1": ["workspace::ActivatePane", 0],
|
||||||
|
"ctrl-2": ["workspace::ActivatePane", 1],
|
||||||
|
"ctrl-3": ["workspace::ActivatePane", 2],
|
||||||
|
"ctrl-4": ["workspace::ActivatePane", 3],
|
||||||
|
"ctrl-5": ["workspace::ActivatePane", 4],
|
||||||
|
"ctrl-6": ["workspace::ActivatePane", 5],
|
||||||
|
"ctrl-7": ["workspace::ActivatePane", 6],
|
||||||
|
"ctrl-8": ["workspace::ActivatePane", 7],
|
||||||
|
"ctrl-9": ["workspace::ActivatePane", 8],
|
||||||
|
"ctrl-shift-1": ["workspace::MoveItemToPane", { "destination": 0, "focus": true }],
|
||||||
|
"ctrl-shift-2": ["workspace::MoveItemToPane", { "destination": 1 }],
|
||||||
|
"ctrl-shift-3": ["workspace::MoveItemToPane", { "destination": 2 }],
|
||||||
|
"ctrl-shift-4": ["workspace::MoveItemToPane", { "destination": 3 }],
|
||||||
|
"ctrl-shift-5": ["workspace::MoveItemToPane", { "destination": 4 }],
|
||||||
|
"ctrl-shift-6": ["workspace::MoveItemToPane", { "destination": 5 }],
|
||||||
|
"ctrl-shift-7": ["workspace::MoveItemToPane", { "destination": 6 }],
|
||||||
|
"ctrl-shift-8": ["workspace::MoveItemToPane", { "destination": 7 }],
|
||||||
|
"ctrl-shift-9": ["workspace::MoveItemToPane", { "destination": 8 }]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"context": "Editor",
|
"context": "Editor",
|
||||||
"bindings": {
|
"bindings": {
|
||||||
|
"ctrl-alt-up": "editor::AddSelectionAbove",
|
||||||
|
"ctrl-alt-down": "editor::AddSelectionBelow",
|
||||||
"ctrl-shift-up": "editor::MoveLineUp",
|
"ctrl-shift-up": "editor::MoveLineUp",
|
||||||
"ctrl-shift-down": "editor::MoveLineDown",
|
"ctrl-shift-down": "editor::MoveLineDown",
|
||||||
"ctrl-shift-m": "editor::SelectLargerSyntaxNode",
|
"ctrl-shift-m": "editor::SelectLargerSyntaxNode",
|
||||||
|
@ -17,6 +37,8 @@
|
||||||
"ctrl-shift-a": "editor::SelectLargerSyntaxNode",
|
"ctrl-shift-a": "editor::SelectLargerSyntaxNode",
|
||||||
"ctrl-shift-d": "editor::DuplicateSelection",
|
"ctrl-shift-d": "editor::DuplicateSelection",
|
||||||
"alt-f3": "editor::SelectAllMatches", // find_all_under
|
"alt-f3": "editor::SelectAllMatches", // find_all_under
|
||||||
|
"f9": "editor::SortLinesCaseSensitive",
|
||||||
|
"ctrl-f9": "editor::SortLinesCaseInsensitive",
|
||||||
"f12": "editor::GoToDefinition",
|
"f12": "editor::GoToDefinition",
|
||||||
"ctrl-f12": "editor::GoToDefinitionSplit",
|
"ctrl-f12": "editor::GoToDefinitionSplit",
|
||||||
"shift-f12": "editor::FindAllReferences",
|
"shift-f12": "editor::FindAllReferences",
|
||||||
|
|
|
@ -4,7 +4,25 @@
|
||||||
"cmd-shift-[": "pane::ActivatePrevItem",
|
"cmd-shift-[": "pane::ActivatePrevItem",
|
||||||
"cmd-shift-]": "pane::ActivateNextItem",
|
"cmd-shift-]": "pane::ActivateNextItem",
|
||||||
"ctrl-pageup": "pane::ActivatePrevItem",
|
"ctrl-pageup": "pane::ActivatePrevItem",
|
||||||
"ctrl-pagedown": "pane::ActivateNextItem"
|
"ctrl-pagedown": "pane::ActivateNextItem",
|
||||||
|
"ctrl-1": ["workspace::ActivatePane", 0],
|
||||||
|
"ctrl-2": ["workspace::ActivatePane", 1],
|
||||||
|
"ctrl-3": ["workspace::ActivatePane", 2],
|
||||||
|
"ctrl-4": ["workspace::ActivatePane", 3],
|
||||||
|
"ctrl-5": ["workspace::ActivatePane", 4],
|
||||||
|
"ctrl-6": ["workspace::ActivatePane", 5],
|
||||||
|
"ctrl-7": ["workspace::ActivatePane", 6],
|
||||||
|
"ctrl-8": ["workspace::ActivatePane", 7],
|
||||||
|
"ctrl-9": ["workspace::ActivatePane", 8],
|
||||||
|
"ctrl-shift-1": ["workspace::MoveItemToPane", { "destination": 0, "focus": true }],
|
||||||
|
"ctrl-shift-2": ["workspace::MoveItemToPane", { "destination": 1 }],
|
||||||
|
"ctrl-shift-3": ["workspace::MoveItemToPane", { "destination": 2 }],
|
||||||
|
"ctrl-shift-4": ["workspace::MoveItemToPane", { "destination": 3 }],
|
||||||
|
"ctrl-shift-5": ["workspace::MoveItemToPane", { "destination": 4 }],
|
||||||
|
"ctrl-shift-6": ["workspace::MoveItemToPane", { "destination": 5 }],
|
||||||
|
"ctrl-shift-7": ["workspace::MoveItemToPane", { "destination": 6 }],
|
||||||
|
"ctrl-shift-8": ["workspace::MoveItemToPane", { "destination": 7 }],
|
||||||
|
"ctrl-shift-9": ["workspace::MoveItemToPane", { "destination": 8 }]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -20,6 +38,8 @@
|
||||||
"cmd-shift-a": "editor::SelectLargerSyntaxNode",
|
"cmd-shift-a": "editor::SelectLargerSyntaxNode",
|
||||||
"cmd-shift-d": "editor::DuplicateSelection",
|
"cmd-shift-d": "editor::DuplicateSelection",
|
||||||
"ctrl-cmd-g": "editor::SelectAllMatches", // find_all_under
|
"ctrl-cmd-g": "editor::SelectAllMatches", // find_all_under
|
||||||
|
"f5": "editor::SortLinesCaseSensitive",
|
||||||
|
"ctrl-f5": "editor::SortLinesCaseInsensitive",
|
||||||
"shift-f12": "editor::FindAllReferences",
|
"shift-f12": "editor::FindAllReferences",
|
||||||
"alt-cmd-down": "editor::GoToDefinition",
|
"alt-cmd-down": "editor::GoToDefinition",
|
||||||
"ctrl-alt-cmd-down": "editor::GoToDefinitionSplit",
|
"ctrl-alt-cmd-down": "editor::GoToDefinitionSplit",
|
||||||
|
|
|
@ -33,11 +33,12 @@ use util::{ResultExt, TryFutureExt};
|
||||||
use workspace::{
|
use workspace::{
|
||||||
dock::{DockPosition, Panel, PanelEvent},
|
dock::{DockPosition, Panel, PanelEvent},
|
||||||
item::SerializableItem,
|
item::SerializableItem,
|
||||||
move_item, pane,
|
move_active_item, move_item, pane,
|
||||||
ui::IconName,
|
ui::IconName,
|
||||||
ActivateNextPane, ActivatePane, ActivatePaneInDirection, ActivatePreviousPane, DraggedTab,
|
ActivateNextPane, ActivatePane, ActivatePaneInDirection, ActivatePreviousPane, DraggedTab,
|
||||||
ItemId, NewTerminal, Pane, PaneGroup, SplitDirection, SplitDown, SplitLeft, SplitRight,
|
ItemId, MoveItemToPane, MoveItemToPaneInDirection, NewTerminal, Pane, PaneGroup,
|
||||||
SplitUp, SwapPaneInDirection, ToggleZoom, Workspace,
|
SplitDirection, SplitDown, SplitLeft, SplitRight, SplitUp, SwapPaneInDirection, ToggleZoom,
|
||||||
|
Workspace,
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
|
@ -355,7 +356,7 @@ impl TerminalPanel {
|
||||||
&mut self,
|
&mut self,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Option<View<Pane>> {
|
) -> Option<View<Pane>> {
|
||||||
let workspace = self.workspace.clone().upgrade()?;
|
let workspace = self.workspace.upgrade()?;
|
||||||
let workspace = workspace.read(cx);
|
let workspace = workspace.read(cx);
|
||||||
let database_id = workspace.database_id();
|
let database_id = workspace.database_id();
|
||||||
let weak_workspace = self.workspace.clone();
|
let weak_workspace = self.workspace.clone();
|
||||||
|
@ -1181,8 +1182,7 @@ impl Render for TerminalPanel {
|
||||||
.position(|pane| **pane == terminal_panel.active_pane)
|
.position(|pane| **pane == terminal_panel.active_pane)
|
||||||
{
|
{
|
||||||
let next_ix = (ix + 1) % panes.len();
|
let next_ix = (ix + 1) % panes.len();
|
||||||
let next_pane = panes[next_ix].clone();
|
cx.focus_view(&panes[next_ix]);
|
||||||
cx.focus_view(&next_pane);
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
@ -1194,15 +1194,14 @@ impl Render for TerminalPanel {
|
||||||
.position(|pane| **pane == terminal_panel.active_pane)
|
.position(|pane| **pane == terminal_panel.active_pane)
|
||||||
{
|
{
|
||||||
let prev_ix = cmp::min(ix.wrapping_sub(1), panes.len() - 1);
|
let prev_ix = cmp::min(ix.wrapping_sub(1), panes.len() - 1);
|
||||||
let prev_pane = panes[prev_ix].clone();
|
cx.focus_view(&panes[prev_ix]);
|
||||||
cx.focus_view(&prev_pane);
|
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.on_action(cx.listener(|terminal_panel, action: &ActivatePane, cx| {
|
.on_action(cx.listener(|terminal_panel, action: &ActivatePane, cx| {
|
||||||
let panes = terminal_panel.center.panes();
|
let panes = terminal_panel.center.panes();
|
||||||
if let Some(pane) = panes.get(action.0).map(|p| (*p).clone()) {
|
if let Some(&pane) = panes.get(action.0) {
|
||||||
cx.focus_view(&pane);
|
cx.focus_view(pane);
|
||||||
} else {
|
} else {
|
||||||
if let Some(new_pane) =
|
if let Some(new_pane) =
|
||||||
terminal_panel.new_pane_with_cloned_active_terminal(cx)
|
terminal_panel.new_pane_with_cloned_active_terminal(cx)
|
||||||
|
@ -1219,18 +1218,40 @@ impl Render for TerminalPanel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
.on_action(cx.listener(
|
.on_action(
|
||||||
|terminal_panel, action: &SwapPaneInDirection, cx| {
|
cx.listener(|terminal_panel, action: &SwapPaneInDirection, cx| {
|
||||||
if let Some(to) = terminal_panel
|
if let Some(to) = terminal_panel
|
||||||
.center
|
.center
|
||||||
.find_pane_in_direction(&terminal_panel.active_pane, action.0, cx)
|
.find_pane_in_direction(&terminal_panel.active_pane, action.0, cx)
|
||||||
.cloned()
|
.cloned()
|
||||||
{
|
{
|
||||||
terminal_panel
|
terminal_panel.center.swap(&terminal_panel.active_pane, &to);
|
||||||
.center
|
|
||||||
.swap(&terminal_panel.active_pane.clone(), &to);
|
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.on_action(cx.listener(|terminal_panel, action: &MoveItemToPane, cx| {
|
||||||
|
let Some(&target_pane) = terminal_panel.center.panes().get(action.destination)
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
move_active_item(
|
||||||
|
&terminal_panel.active_pane,
|
||||||
|
target_pane,
|
||||||
|
action.focus,
|
||||||
|
true,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
}))
|
||||||
|
.on_action(cx.listener(
|
||||||
|
|terminal_panel, action: &MoveItemToPaneInDirection, cx| {
|
||||||
|
let source_pane = &terminal_panel.active_pane;
|
||||||
|
if let Some(destination_pane) = terminal_panel
|
||||||
|
.center
|
||||||
|
.find_pane_in_direction(source_pane, action.direction, cx)
|
||||||
|
{
|
||||||
|
move_active_item(source_pane, destination_pane, action.focus, true, cx);
|
||||||
|
};
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
|
|
|
@ -93,7 +93,7 @@ use theme::{ActiveTheme, SystemAppearance, ThemeSettings};
|
||||||
pub use toolbar::{Toolbar, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView};
|
pub use toolbar::{Toolbar, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView};
|
||||||
pub use ui;
|
pub use ui;
|
||||||
use ui::prelude::*;
|
use ui::prelude::*;
|
||||||
use util::{paths::SanitizedPath, ResultExt, TryFutureExt};
|
use util::{paths::SanitizedPath, serde::default_true, ResultExt, TryFutureExt};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
pub use workspace_settings::{
|
pub use workspace_settings::{
|
||||||
AutosaveSetting, RestoreOnStartupBehavior, TabBarSettings, WorkspaceSettings,
|
AutosaveSetting, RestoreOnStartupBehavior, TabBarSettings, WorkspaceSettings,
|
||||||
|
@ -173,6 +173,20 @@ pub struct ActivatePaneInDirection(pub SplitDirection);
|
||||||
#[derive(Clone, Deserialize, PartialEq)]
|
#[derive(Clone, Deserialize, PartialEq)]
|
||||||
pub struct SwapPaneInDirection(pub SplitDirection);
|
pub struct SwapPaneInDirection(pub SplitDirection);
|
||||||
|
|
||||||
|
#[derive(Clone, Deserialize, PartialEq)]
|
||||||
|
pub struct MoveItemToPane {
|
||||||
|
pub destination: usize,
|
||||||
|
#[serde(default = "default_true")]
|
||||||
|
pub focus: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Deserialize, PartialEq)]
|
||||||
|
pub struct MoveItemToPaneInDirection {
|
||||||
|
pub direction: SplitDirection,
|
||||||
|
#[serde(default = "default_true")]
|
||||||
|
pub focus: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Debug, Deserialize)]
|
#[derive(Clone, PartialEq, Debug, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct SaveAll {
|
pub struct SaveAll {
|
||||||
|
@ -222,6 +236,8 @@ impl_actions!(
|
||||||
ActivatePaneInDirection,
|
ActivatePaneInDirection,
|
||||||
CloseAllItemsAndPanes,
|
CloseAllItemsAndPanes,
|
||||||
CloseInactiveTabsAndPanes,
|
CloseInactiveTabsAndPanes,
|
||||||
|
MoveItemToPane,
|
||||||
|
MoveItemToPaneInDirection,
|
||||||
OpenTerminal,
|
OpenTerminal,
|
||||||
Reload,
|
Reload,
|
||||||
Save,
|
Save,
|
||||||
|
@ -2829,6 +2845,13 @@ impl Workspace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn move_item_to_pane_at_index(&mut self, action: &MoveItemToPane, cx: &mut ViewContext<Self>) {
|
||||||
|
let Some(&target_pane) = self.center.panes().get(action.destination) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
move_active_item(&self.active_pane, target_pane, action.focus, true, cx);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn activate_next_pane(&mut self, cx: &mut WindowContext) {
|
pub fn activate_next_pane(&mut self, cx: &mut WindowContext) {
|
||||||
let panes = self.center.panes();
|
let panes = self.center.panes();
|
||||||
if let Some(ix) = panes.iter().position(|pane| **pane == self.active_pane) {
|
if let Some(ix) = panes.iter().position(|pane| **pane == self.active_pane) {
|
||||||
|
@ -2947,6 +2970,16 @@ impl Workspace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn move_item_to_pane_in_direction(
|
||||||
|
&mut self,
|
||||||
|
action: &MoveItemToPaneInDirection,
|
||||||
|
cx: &mut WindowContext,
|
||||||
|
) {
|
||||||
|
if let Some(destination) = self.find_pane_in_direction(action.direction, cx) {
|
||||||
|
move_active_item(&self.active_pane, &destination, action.focus, true, cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn bounding_box_for_pane(&self, pane: &View<Pane>) -> Option<Bounds<Pixels>> {
|
pub fn bounding_box_for_pane(&self, pane: &View<Pane>) -> Option<Bounds<Pixels>> {
|
||||||
self.center.bounding_box_for_pane(pane)
|
self.center.bounding_box_for_pane(pane)
|
||||||
}
|
}
|
||||||
|
@ -2967,14 +3000,14 @@ impl Workspace {
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) {
|
) {
|
||||||
if let Some(to) = self.find_pane_in_direction(direction, cx) {
|
if let Some(to) = self.find_pane_in_direction(direction, cx) {
|
||||||
self.center.swap(&self.active_pane.clone(), &to);
|
self.center.swap(&self.active_pane, &to);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resize_pane(&mut self, axis: gpui::Axis, amount: Pixels, cx: &mut ViewContext<Self>) {
|
pub fn resize_pane(&mut self, axis: gpui::Axis, amount: Pixels, cx: &mut ViewContext<Self>) {
|
||||||
self.center
|
self.center
|
||||||
.resize(&self.active_pane.clone(), axis, amount, &self.bounds);
|
.resize(&self.active_pane, axis, amount, &self.bounds);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4408,6 +4441,7 @@ impl Workspace {
|
||||||
.on_action(cx.listener(Self::follow_next_collaborator))
|
.on_action(cx.listener(Self::follow_next_collaborator))
|
||||||
.on_action(cx.listener(Self::close_window))
|
.on_action(cx.listener(Self::close_window))
|
||||||
.on_action(cx.listener(Self::activate_pane_at_index))
|
.on_action(cx.listener(Self::activate_pane_at_index))
|
||||||
|
.on_action(cx.listener(Self::move_item_to_pane_at_index))
|
||||||
.on_action(cx.listener(|workspace, _: &Unfollow, cx| {
|
.on_action(cx.listener(|workspace, _: &Unfollow, cx| {
|
||||||
let pane = workspace.active_pane().clone();
|
let pane = workspace.active_pane().clone();
|
||||||
workspace.unfollow_in_pane(&pane, cx);
|
workspace.unfollow_in_pane(&pane, cx);
|
||||||
|
@ -4438,6 +4472,11 @@ impl Workspace {
|
||||||
workspace.activate_pane_in_direction(action.0, cx)
|
workspace.activate_pane_in_direction(action.0, cx)
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
.on_action(
|
||||||
|
cx.listener(|workspace, action: &MoveItemToPaneInDirection, cx| {
|
||||||
|
workspace.move_item_to_pane_in_direction(action, cx)
|
||||||
|
}),
|
||||||
|
)
|
||||||
.on_action(cx.listener(|workspace, action: &SwapPaneInDirection, cx| {
|
.on_action(cx.listener(|workspace, action: &SwapPaneInDirection, cx| {
|
||||||
workspace.swap_pane_in_direction(action.0, cx)
|
workspace.swap_pane_in_direction(action.0, cx)
|
||||||
}))
|
}))
|
||||||
|
@ -6181,6 +6220,34 @@ pub fn move_item(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn move_active_item(
|
||||||
|
source: &View<Pane>,
|
||||||
|
destination: &View<Pane>,
|
||||||
|
focus_destination: bool,
|
||||||
|
close_if_empty: bool,
|
||||||
|
cx: &mut WindowContext<'_>,
|
||||||
|
) {
|
||||||
|
if source == destination {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let Some(active_item) = source.read(cx).active_item() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
source.update(cx, |source_pane, cx| {
|
||||||
|
let item_id = active_item.item_id();
|
||||||
|
source_pane.remove_item(item_id, false, close_if_empty, cx);
|
||||||
|
destination.update(cx, |target_pane, cx| {
|
||||||
|
target_pane.add_item(
|
||||||
|
active_item,
|
||||||
|
focus_destination,
|
||||||
|
focus_destination,
|
||||||
|
Some(target_pane.items_len()),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue