@@ -831,10 +831,10 @@
"ctrl-w shift-right": "workspace::SwapPaneRight",
"ctrl-w shift-up": "workspace::SwapPaneUp",
"ctrl-w shift-down": "workspace::SwapPaneDown",
- "ctrl-w shift-h": "workspace::SwapPaneLeft",
- "ctrl-w shift-l": "workspace::SwapPaneRight",
- "ctrl-w shift-k": "workspace::SwapPaneUp",
- "ctrl-w shift-j": "workspace::SwapPaneDown",
+ "ctrl-w shift-h": "workspace::MovePaneLeft",
+ "ctrl-w shift-l": "workspace::MovePaneRight",
+ "ctrl-w shift-k": "workspace::MovePaneUp",
+ "ctrl-w shift-j": "workspace::MovePaneDown",
"ctrl-w >": "vim::ResizePaneRight",
"ctrl-w <": "vim::ResizePaneLeft",
"ctrl-w -": "vim::ResizePaneDown",
@@ -29,9 +29,9 @@ use util::{ResultExt, TryFutureExt};
use workspace::{
ActivateNextPane, ActivatePane, ActivatePaneDown, ActivatePaneLeft, ActivatePaneRight,
ActivatePaneUp, ActivatePreviousPane, DraggedSelection, DraggedTab, ItemId, MoveItemToPane,
- MoveItemToPaneInDirection, NewTerminal, Pane, PaneGroup, SplitDirection, SplitDown, SplitLeft,
- SplitRight, SplitUp, SwapPaneDown, SwapPaneLeft, SwapPaneRight, SwapPaneUp, ToggleZoom,
- Workspace,
+ MoveItemToPaneInDirection, MovePaneDown, MovePaneLeft, MovePaneRight, MovePaneUp, NewTerminal,
+ Pane, PaneGroup, SplitDirection, SplitDown, SplitLeft, SplitRight, SplitUp, SwapPaneDown,
+ SwapPaneLeft, SwapPaneRight, SwapPaneUp, ToggleZoom, Workspace,
dock::{DockPosition, Panel, PanelEvent, PanelHandle},
item::SerializableItem,
move_active_item, move_item, pane,
@@ -1055,6 +1055,16 @@ impl TerminalPanel {
cx.notify();
}
}
+
+ fn move_pane_to_border(&mut self, direction: SplitDirection, cx: &mut Context<Self>) {
+ if self
+ .center
+ .move_to_border(&self.active_pane, direction)
+ .unwrap()
+ {
+ cx.notify();
+ }
+ }
}
fn is_enabled_in_workspace(workspace: &Workspace, cx: &App) -> bool {
@@ -1404,6 +1414,18 @@ impl Render for TerminalPanel {
.on_action(cx.listener(|terminal_panel, _: &SwapPaneDown, _, cx| {
terminal_panel.swap_pane_in_direction(SplitDirection::Down, cx);
}))
+ .on_action(cx.listener(|terminal_panel, _: &MovePaneLeft, _, cx| {
+ terminal_panel.move_pane_to_border(SplitDirection::Left, cx);
+ }))
+ .on_action(cx.listener(|terminal_panel, _: &MovePaneRight, _, cx| {
+ terminal_panel.move_pane_to_border(SplitDirection::Right, cx);
+ }))
+ .on_action(cx.listener(|terminal_panel, _: &MovePaneUp, _, cx| {
+ terminal_panel.move_pane_to_border(SplitDirection::Up, cx);
+ }))
+ .on_action(cx.listener(|terminal_panel, _: &MovePaneDown, _, cx| {
+ terminal_panel.move_pane_to_border(SplitDirection::Down, cx);
+ }))
.on_action(
cx.listener(|terminal_panel, action: &MoveItemToPane, window, cx| {
let Some(&target_pane) =
@@ -79,6 +79,56 @@ impl PaneGroup {
}
}
+ /// Moves active pane to span the entire border in the given direction,
+ /// similar to Vim ctrl+w shift-[hjkl] motion.
+ ///
+ /// Returns:
+ /// - Ok(true) if it found and moved a pane
+ /// - Ok(false) if it found but did not move the pane
+ /// - Err(_) if it did not find the pane
+ pub fn move_to_border(
+ &mut self,
+ active_pane: &Entity<Pane>,
+ direction: SplitDirection,
+ ) -> Result<bool> {
+ if let Some(pane) = self.find_pane_at_border(direction)
+ && pane == active_pane
+ {
+ return Ok(false);
+ }
+
+ if !self.remove(active_pane)? {
+ return Ok(false);
+ }
+
+ if let Member::Axis(root) = &mut self.root
+ && direction.axis() == root.axis
+ {
+ let idx = if direction.increasing() {
+ root.members.len()
+ } else {
+ 0
+ };
+ root.insert_pane(idx, active_pane);
+ return Ok(true);
+ }
+
+ let members = if direction.increasing() {
+ vec![self.root.clone(), Member::Pane(active_pane.clone())]
+ } else {
+ vec![Member::Pane(active_pane.clone()), self.root.clone()]
+ };
+ self.root = Member::Axis(PaneAxis::new(direction.axis(), members));
+ Ok(true)
+ }
+
+ fn find_pane_at_border(&self, direction: SplitDirection) -> Option<&Entity<Pane>> {
+ match &self.root {
+ Member::Pane(pane) => Some(pane),
+ Member::Axis(axis) => axis.find_pane_at_border(direction),
+ }
+ }
+
/// Returns:
/// - Ok(true) if it found and removed a pane
/// - Ok(false) if it found but did not remove the pane
@@ -526,9 +576,7 @@ impl PaneAxis {
if direction.increasing() {
idx += 1;
}
-
- self.members.insert(idx, Member::Pane(new_pane.clone()));
- *self.flexes.lock() = vec![1.; self.members.len()];
+ self.insert_pane(idx, new_pane);
} else {
*member =
Member::new_axis(old_pane.clone(), new_pane.clone(), direction);
@@ -541,6 +589,26 @@ impl PaneAxis {
anyhow::bail!("Pane not found");
}
+ fn insert_pane(&mut self, idx: usize, new_pane: &Entity<Pane>) {
+ self.members.insert(idx, Member::Pane(new_pane.clone()));
+ *self.flexes.lock() = vec![1.; self.members.len()];
+ }
+
+ fn find_pane_at_border(&self, direction: SplitDirection) -> Option<&Entity<Pane>> {
+ if self.axis != direction.axis() {
+ return None;
+ }
+ let member = if direction.increasing() {
+ self.members.last()
+ } else {
+ self.members.first()
+ };
+ member.and_then(|e| match e {
+ Member::Pane(pane) => Some(pane),
+ Member::Axis(_) => None,
+ })
+ }
+
fn remove(&mut self, pane_to_remove: &Entity<Pane>) -> Result<Option<Member>> {
let mut found_pane = false;
let mut remove_member = None;
@@ -421,6 +421,14 @@ actions!(
SwapPaneUp,
/// Swaps the current pane with the one below.
SwapPaneDown,
+ /// Move the current pane to be at the far left.
+ MovePaneLeft,
+ /// Move the current pane to be at the far right.
+ MovePaneRight,
+ /// Move the current pane to be at the very top.
+ MovePaneUp,
+ /// Move the current pane to be at the very bottom.
+ MovePaneDown,
]
);
@@ -3866,6 +3874,16 @@ impl Workspace {
}
}
+ pub fn move_pane_to_border(&mut self, direction: SplitDirection, cx: &mut Context<Self>) {
+ if self
+ .center
+ .move_to_border(&self.active_pane, direction)
+ .unwrap()
+ {
+ cx.notify();
+ }
+ }
+
pub fn resize_pane(
&mut self,
axis: gpui::Axis,
@@ -5674,6 +5692,18 @@ impl Workspace {
.on_action(cx.listener(|workspace, _: &SwapPaneDown, _, cx| {
workspace.swap_pane_in_direction(SplitDirection::Down, cx)
}))
+ .on_action(cx.listener(|workspace, _: &MovePaneLeft, _, cx| {
+ workspace.move_pane_to_border(SplitDirection::Left, cx)
+ }))
+ .on_action(cx.listener(|workspace, _: &MovePaneRight, _, cx| {
+ workspace.move_pane_to_border(SplitDirection::Right, cx)
+ }))
+ .on_action(cx.listener(|workspace, _: &MovePaneUp, _, cx| {
+ workspace.move_pane_to_border(SplitDirection::Up, cx)
+ }))
+ .on_action(cx.listener(|workspace, _: &MovePaneDown, _, cx| {
+ workspace.move_pane_to_border(SplitDirection::Down, cx)
+ }))
.on_action(cx.listener(|this, _: &ToggleLeftDock, window, cx| {
this.toggle_dock(DockPosition::Left, window, cx);
}))