mirror of
https://github.com/zellij-org/zellij.git
synced 2024-12-25 02:06:19 +03:00
Feature: Move panes directionally (#762)
* Feature: Move panes directionally * change keybinds * Fix active pane after move * Add a separate 'Move' mode * Add tests * Add more tests * Send resize message to pty * wrap set_terminal_size_using_fd() in macro * change keybind for Move mode * cargo fmt * fix test * move render functions from tab.rs to screen.rs * undo wrong keybinds
This commit is contained in:
parent
76a96b538b
commit
d90e3d4cac
@ -23,6 +23,7 @@ enum CtrlKeyAction {
|
||||
Scroll,
|
||||
Quit,
|
||||
Session,
|
||||
Move,
|
||||
}
|
||||
|
||||
enum CtrlKeyMode {
|
||||
@ -41,6 +42,7 @@ impl CtrlKeyShortcut {
|
||||
CtrlKeyAction::Scroll => String::from("SCROLL"),
|
||||
CtrlKeyAction::Quit => String::from("QUIT"),
|
||||
CtrlKeyAction::Session => String::from("SESSION"),
|
||||
CtrlKeyAction::Move => String::from("MOVE"),
|
||||
}
|
||||
}
|
||||
pub fn letter_shortcut(&self) -> char {
|
||||
@ -52,6 +54,7 @@ impl CtrlKeyShortcut {
|
||||
CtrlKeyAction::Scroll => 's',
|
||||
CtrlKeyAction::Quit => 'q',
|
||||
CtrlKeyAction::Session => 'o',
|
||||
CtrlKeyAction::Move => 'h',
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -253,6 +256,7 @@ pub fn ctrl_keys(help: &ModeInfo, max_len: usize, separator: &str) -> LinePart {
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Disabled, CtrlKeyAction::Pane),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Disabled, CtrlKeyAction::Tab),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Disabled, CtrlKeyAction::Resize),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Disabled, CtrlKeyAction::Move),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Disabled, CtrlKeyAction::Scroll),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Disabled, CtrlKeyAction::Session),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Disabled, CtrlKeyAction::Quit),
|
||||
@ -267,6 +271,7 @@ pub fn ctrl_keys(help: &ModeInfo, max_len: usize, separator: &str) -> LinePart {
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Pane),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Tab),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Selected, CtrlKeyAction::Resize),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Move),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Scroll),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Session),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Quit),
|
||||
@ -281,6 +286,7 @@ pub fn ctrl_keys(help: &ModeInfo, max_len: usize, separator: &str) -> LinePart {
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Selected, CtrlKeyAction::Pane),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Tab),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Resize),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Move),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Scroll),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Session),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Quit),
|
||||
@ -295,6 +301,7 @@ pub fn ctrl_keys(help: &ModeInfo, max_len: usize, separator: &str) -> LinePart {
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Pane),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Selected, CtrlKeyAction::Tab),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Resize),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Move),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Scroll),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Session),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Quit),
|
||||
@ -309,6 +316,7 @@ pub fn ctrl_keys(help: &ModeInfo, max_len: usize, separator: &str) -> LinePart {
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Pane),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Tab),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Resize),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Move),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Selected, CtrlKeyAction::Scroll),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Session),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Quit),
|
||||
@ -316,6 +324,21 @@ pub fn ctrl_keys(help: &ModeInfo, max_len: usize, separator: &str) -> LinePart {
|
||||
colored_elements,
|
||||
separator,
|
||||
),
|
||||
InputMode::Move => key_indicators(
|
||||
max_len,
|
||||
&[
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Lock),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Pane),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Tab),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Resize),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Selected, CtrlKeyAction::Move),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Scroll),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Session),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Quit),
|
||||
],
|
||||
colored_elements,
|
||||
separator,
|
||||
),
|
||||
InputMode::Normal => key_indicators(
|
||||
max_len,
|
||||
&[
|
||||
@ -323,6 +346,7 @@ pub fn ctrl_keys(help: &ModeInfo, max_len: usize, separator: &str) -> LinePart {
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Pane),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Tab),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Resize),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Move),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Scroll),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Session),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Quit),
|
||||
@ -337,6 +361,7 @@ pub fn ctrl_keys(help: &ModeInfo, max_len: usize, separator: &str) -> LinePart {
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Pane),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Tab),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Resize),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Move),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Scroll),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Selected, CtrlKeyAction::Session),
|
||||
CtrlKeyShortcut::new(CtrlKeyMode::Unselected, CtrlKeyAction::Quit),
|
||||
|
@ -116,6 +116,15 @@ fn route_action(
|
||||
};
|
||||
session.senders.send_to_screen(screen_instr).unwrap();
|
||||
}
|
||||
Action::MovePane(direction) => {
|
||||
let screen_instr = match direction {
|
||||
Direction::Left => ScreenInstruction::MovePaneLeft,
|
||||
Direction::Right => ScreenInstruction::MovePaneRight,
|
||||
Direction::Up => ScreenInstruction::MovePaneUp,
|
||||
Direction::Down => ScreenInstruction::MovePaneDown,
|
||||
};
|
||||
session.senders.send_to_screen(screen_instr).unwrap();
|
||||
}
|
||||
Action::ScrollUp => {
|
||||
session
|
||||
.senders
|
||||
|
@ -44,6 +44,10 @@ pub(crate) enum ScreenInstruction {
|
||||
MoveFocusUp,
|
||||
MoveFocusRight,
|
||||
MoveFocusRightOrNextTab,
|
||||
MovePaneUp,
|
||||
MovePaneDown,
|
||||
MovePaneRight,
|
||||
MovePaneLeft,
|
||||
Exit,
|
||||
ScrollUp,
|
||||
ScrollUpAt(Position),
|
||||
@ -100,6 +104,10 @@ impl From<&ScreenInstruction> for ScreenContext {
|
||||
ScreenInstruction::MoveFocusUp => ScreenContext::MoveFocusUp,
|
||||
ScreenInstruction::MoveFocusRight => ScreenContext::MoveFocusRight,
|
||||
ScreenInstruction::MoveFocusRightOrNextTab => ScreenContext::MoveFocusRightOrNextTab,
|
||||
ScreenInstruction::MovePaneDown => ScreenContext::MovePaneDown,
|
||||
ScreenInstruction::MovePaneUp => ScreenContext::MovePaneUp,
|
||||
ScreenInstruction::MovePaneRight => ScreenContext::MovePaneRight,
|
||||
ScreenInstruction::MovePaneLeft => ScreenContext::MovePaneLeft,
|
||||
ScreenInstruction::Exit => ScreenContext::Exit,
|
||||
ScreenInstruction::ScrollUp => ScreenContext::ScrollUp,
|
||||
ScreenInstruction::ScrollDown => ScreenContext::ScrollDown,
|
||||
@ -637,6 +645,29 @@ pub(crate) fn screen_thread_main(
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::MovePaneDown => {
|
||||
screen.get_active_tab_mut().unwrap().move_active_pane_down();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::MovePaneUp => {
|
||||
screen.get_active_tab_mut().unwrap().move_active_pane_up();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::MovePaneRight => {
|
||||
screen
|
||||
.get_active_tab_mut()
|
||||
.unwrap()
|
||||
.move_active_pane_right();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::MovePaneLeft => {
|
||||
screen.get_active_tab_mut().unwrap().move_active_pane_left();
|
||||
|
||||
screen.render();
|
||||
}
|
||||
ScreenInstruction::ScrollUp => {
|
||||
screen
|
||||
.get_active_tab_mut()
|
||||
|
@ -275,6 +275,20 @@ pub trait Pane {
|
||||
fn borderless(&self) -> bool;
|
||||
}
|
||||
|
||||
macro_rules! resize_pty {
|
||||
($pane:expr, $os_input:expr) => {
|
||||
if let PaneId::Terminal(ref pid) = $pane.pid() {
|
||||
// FIXME: This `set_terminal_size_using_fd` call would be best in
|
||||
// `TerminalPane::reflow_lines`
|
||||
$os_input.set_terminal_size_using_fd(
|
||||
*pid,
|
||||
$pane.get_content_columns() as u16,
|
||||
$pane.get_content_rows() as u16,
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl Tab {
|
||||
// FIXME: Still too many arguments for clippy to be happy...
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
@ -756,7 +770,7 @@ impl Tab {
|
||||
self.draw_pane_frames = draw_pane_frames;
|
||||
self.should_clear_display_before_rendering = true;
|
||||
let viewport = self.viewport;
|
||||
for (pane_id, pane) in self.panes.iter_mut() {
|
||||
for pane in self.panes.values_mut() {
|
||||
if !pane.borderless() {
|
||||
pane.set_frame(draw_pane_frames);
|
||||
}
|
||||
@ -783,15 +797,7 @@ impl Tab {
|
||||
pane.set_content_offset(Offset::shift(pane_rows_offset, pane_columns_offset));
|
||||
}
|
||||
|
||||
// FIXME: This, and all other `set_terminal_size_using_fd` calls, would be best in
|
||||
// `TerminalPane::reflow_lines`
|
||||
if let PaneId::Terminal(pid) = pane_id {
|
||||
self.os_api.set_terminal_size_using_fd(
|
||||
*pid,
|
||||
pane.get_content_columns() as u16,
|
||||
pane.get_content_rows() as u16,
|
||||
);
|
||||
}
|
||||
resize_pty!(pane, self.os_api);
|
||||
}
|
||||
}
|
||||
pub fn render(&mut self) -> Option<Output> {
|
||||
@ -2031,6 +2037,170 @@ impl Tab {
|
||||
self.active_terminal = updated_active_terminal;
|
||||
false
|
||||
}
|
||||
pub fn move_active_pane_down(&mut self) {
|
||||
if !self.has_selectable_panes() {
|
||||
return;
|
||||
}
|
||||
if self.fullscreen_is_active {
|
||||
return;
|
||||
}
|
||||
if let Some(active) = self.get_active_pane() {
|
||||
let terminals = self.get_selectable_panes();
|
||||
let next_index = terminals
|
||||
.enumerate()
|
||||
.filter(|(_, (_, c))| {
|
||||
c.is_directly_below(active) && c.vertically_overlaps_with(active)
|
||||
})
|
||||
.max_by_key(|(_, (_, c))| c.active_at())
|
||||
.map(|(_, (pid, _))| pid);
|
||||
if let Some(&p) = next_index {
|
||||
let current_position = self.panes.get(&self.active_terminal.unwrap()).unwrap();
|
||||
let prev_geom = current_position.position_and_size();
|
||||
let prev_geom_override = current_position.geom_override();
|
||||
|
||||
let new_position = self.panes.get_mut(&p).unwrap();
|
||||
let next_geom = new_position.position_and_size();
|
||||
let next_geom_override = new_position.geom_override();
|
||||
new_position.set_geom(prev_geom);
|
||||
if let Some(geom) = prev_geom_override {
|
||||
new_position.get_geom_override(geom);
|
||||
}
|
||||
resize_pty!(new_position, self.os_api);
|
||||
new_position.set_should_render(true);
|
||||
|
||||
let current_position = self.panes.get_mut(&self.active_terminal.unwrap()).unwrap();
|
||||
current_position.set_geom(next_geom);
|
||||
if let Some(geom) = next_geom_override {
|
||||
current_position.get_geom_override(geom);
|
||||
}
|
||||
resize_pty!(current_position, self.os_api);
|
||||
current_position.set_should_render(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn move_active_pane_up(&mut self) {
|
||||
if !self.has_selectable_panes() {
|
||||
return;
|
||||
}
|
||||
if self.fullscreen_is_active {
|
||||
return;
|
||||
}
|
||||
if let Some(active) = self.get_active_pane() {
|
||||
let terminals = self.get_selectable_panes();
|
||||
let next_index = terminals
|
||||
.enumerate()
|
||||
.filter(|(_, (_, c))| {
|
||||
c.is_directly_above(active) && c.vertically_overlaps_with(active)
|
||||
})
|
||||
.max_by_key(|(_, (_, c))| c.active_at())
|
||||
.map(|(_, (pid, _))| pid);
|
||||
if let Some(&p) = next_index {
|
||||
let current_position = self.panes.get(&self.active_terminal.unwrap()).unwrap();
|
||||
let prev_geom = current_position.position_and_size();
|
||||
let prev_geom_override = current_position.geom_override();
|
||||
|
||||
let new_position = self.panes.get_mut(&p).unwrap();
|
||||
let next_geom = new_position.position_and_size();
|
||||
let next_geom_override = new_position.geom_override();
|
||||
new_position.set_geom(prev_geom);
|
||||
if let Some(geom) = prev_geom_override {
|
||||
new_position.get_geom_override(geom);
|
||||
}
|
||||
resize_pty!(new_position, self.os_api);
|
||||
new_position.set_should_render(true);
|
||||
|
||||
let current_position = self.panes.get_mut(&self.active_terminal.unwrap()).unwrap();
|
||||
current_position.set_geom(next_geom);
|
||||
if let Some(geom) = next_geom_override {
|
||||
current_position.get_geom_override(geom);
|
||||
}
|
||||
resize_pty!(current_position, self.os_api);
|
||||
current_position.set_should_render(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn move_active_pane_right(&mut self) {
|
||||
if !self.has_selectable_panes() {
|
||||
return;
|
||||
}
|
||||
if self.fullscreen_is_active {
|
||||
return;
|
||||
}
|
||||
if let Some(active) = self.get_active_pane() {
|
||||
let terminals = self.get_selectable_panes();
|
||||
let next_index = terminals
|
||||
.enumerate()
|
||||
.filter(|(_, (_, c))| {
|
||||
c.is_directly_right_of(active) && c.horizontally_overlaps_with(active)
|
||||
})
|
||||
.max_by_key(|(_, (_, c))| c.active_at())
|
||||
.map(|(_, (pid, _))| pid);
|
||||
if let Some(&p) = next_index {
|
||||
let current_position = self.panes.get(&self.active_terminal.unwrap()).unwrap();
|
||||
let prev_geom = current_position.position_and_size();
|
||||
let prev_geom_override = current_position.geom_override();
|
||||
|
||||
let new_position = self.panes.get_mut(&p).unwrap();
|
||||
let next_geom = new_position.position_and_size();
|
||||
let next_geom_override = new_position.geom_override();
|
||||
new_position.set_geom(prev_geom);
|
||||
if let Some(geom) = prev_geom_override {
|
||||
new_position.get_geom_override(geom);
|
||||
}
|
||||
resize_pty!(new_position, self.os_api);
|
||||
new_position.set_should_render(true);
|
||||
|
||||
let current_position = self.panes.get_mut(&self.active_terminal.unwrap()).unwrap();
|
||||
current_position.set_geom(next_geom);
|
||||
if let Some(geom) = next_geom_override {
|
||||
current_position.get_geom_override(geom);
|
||||
}
|
||||
resize_pty!(current_position, self.os_api);
|
||||
current_position.set_should_render(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn move_active_pane_left(&mut self) {
|
||||
if !self.has_selectable_panes() {
|
||||
return;
|
||||
}
|
||||
if self.fullscreen_is_active {
|
||||
return;
|
||||
}
|
||||
if let Some(active) = self.get_active_pane() {
|
||||
let terminals = self.get_selectable_panes();
|
||||
let next_index = terminals
|
||||
.enumerate()
|
||||
.filter(|(_, (_, c))| {
|
||||
c.is_directly_left_of(active) && c.horizontally_overlaps_with(active)
|
||||
})
|
||||
.max_by_key(|(_, (_, c))| c.active_at())
|
||||
.map(|(_, (pid, _))| pid);
|
||||
if let Some(&p) = next_index {
|
||||
let current_position = self.panes.get(&self.active_terminal.unwrap()).unwrap();
|
||||
let prev_geom = current_position.position_and_size();
|
||||
let prev_geom_override = current_position.geom_override();
|
||||
|
||||
let new_position = self.panes.get_mut(&p).unwrap();
|
||||
let next_geom = new_position.position_and_size();
|
||||
let next_geom_override = new_position.geom_override();
|
||||
new_position.set_geom(prev_geom);
|
||||
if let Some(geom) = prev_geom_override {
|
||||
new_position.get_geom_override(geom);
|
||||
}
|
||||
resize_pty!(new_position, self.os_api);
|
||||
new_position.set_should_render(true);
|
||||
|
||||
let current_position = self.panes.get_mut(&self.active_terminal.unwrap()).unwrap();
|
||||
current_position.set_geom(next_geom);
|
||||
if let Some(geom) = next_geom_override {
|
||||
current_position.get_geom_override(geom);
|
||||
}
|
||||
resize_pty!(current_position, self.os_api);
|
||||
current_position.set_should_render(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn horizontal_borders(&self, terminals: &[PaneId]) -> HashSet<usize> {
|
||||
terminals.iter().fold(HashSet::new(), |mut borders, t| {
|
||||
let terminal = self.panes.get(t).unwrap();
|
||||
|
@ -2525,6 +2525,244 @@ pub fn move_focus_right_to_the_most_recently_used_pane() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn move_active_pane_down() {
|
||||
let size = Size {
|
||||
cols: 121,
|
||||
rows: 20,
|
||||
};
|
||||
let mut tab = create_new_tab(size);
|
||||
let new_pane_id = PaneId::Terminal(2);
|
||||
|
||||
tab.horizontal_split(new_pane_id);
|
||||
tab.move_focus_up();
|
||||
tab.move_active_pane_down();
|
||||
|
||||
assert_eq!(
|
||||
tab.get_active_pane().unwrap().y(),
|
||||
10,
|
||||
"Active pane is the bottom one"
|
||||
);
|
||||
assert_eq!(
|
||||
tab.get_active_pane().unwrap().pid(),
|
||||
PaneId::Terminal(1),
|
||||
"Active pane is the bottom one"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn move_active_pane_down_to_the_most_recently_used_position() {
|
||||
let size = Size {
|
||||
cols: 121,
|
||||
rows: 20,
|
||||
};
|
||||
let mut tab = create_new_tab(size);
|
||||
let new_pane_id_1 = PaneId::Terminal(2);
|
||||
let new_pane_id_2 = PaneId::Terminal(3);
|
||||
let new_pane_id_3 = PaneId::Terminal(4);
|
||||
|
||||
tab.horizontal_split(new_pane_id_1);
|
||||
tab.vertical_split(new_pane_id_2);
|
||||
tab.vertical_split(new_pane_id_3);
|
||||
tab.move_focus_up();
|
||||
tab.move_active_pane_down();
|
||||
|
||||
assert_eq!(
|
||||
tab.get_active_pane().unwrap().y(),
|
||||
10,
|
||||
"Active pane y position"
|
||||
);
|
||||
assert_eq!(
|
||||
tab.get_active_pane().unwrap().x(),
|
||||
91,
|
||||
"Active pane x position"
|
||||
);
|
||||
assert_eq!(
|
||||
tab.get_active_pane().unwrap().pid(),
|
||||
PaneId::Terminal(1),
|
||||
"Active pane PaneId"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn move_active_pane_up() {
|
||||
let size = Size {
|
||||
cols: 121,
|
||||
rows: 20,
|
||||
};
|
||||
let mut tab = create_new_tab(size);
|
||||
let new_pane_id = PaneId::Terminal(2);
|
||||
|
||||
tab.horizontal_split(new_pane_id);
|
||||
tab.move_active_pane_up();
|
||||
|
||||
assert_eq!(
|
||||
tab.get_active_pane().unwrap().y(),
|
||||
0,
|
||||
"Active pane is the top one"
|
||||
);
|
||||
assert_eq!(
|
||||
tab.get_active_pane().unwrap().pid(),
|
||||
PaneId::Terminal(2),
|
||||
"Active pane is the top one"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn move_active_pane_up_to_the_most_recently_used_position() {
|
||||
let size = Size {
|
||||
cols: 121,
|
||||
rows: 20,
|
||||
};
|
||||
let mut tab = create_new_tab(size);
|
||||
let new_pane_id_1 = PaneId::Terminal(2);
|
||||
let new_pane_id_2 = PaneId::Terminal(3);
|
||||
let new_pane_id_3 = PaneId::Terminal(4);
|
||||
|
||||
tab.horizontal_split(new_pane_id_1);
|
||||
tab.move_focus_up();
|
||||
tab.vertical_split(new_pane_id_2);
|
||||
tab.vertical_split(new_pane_id_3);
|
||||
tab.move_focus_down();
|
||||
tab.move_active_pane_up();
|
||||
|
||||
assert_eq!(
|
||||
tab.get_active_pane().unwrap().y(),
|
||||
0,
|
||||
"Active pane y position"
|
||||
);
|
||||
assert_eq!(
|
||||
tab.get_active_pane().unwrap().x(),
|
||||
91,
|
||||
"Active pane x position"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tab.get_active_pane().unwrap().pid(),
|
||||
PaneId::Terminal(2),
|
||||
"Active pane PaneId"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn move_active_pane_left() {
|
||||
let size = Size {
|
||||
cols: 121,
|
||||
rows: 20,
|
||||
};
|
||||
let mut tab = create_new_tab(size);
|
||||
let new_pane_id = PaneId::Terminal(2);
|
||||
|
||||
tab.vertical_split(new_pane_id);
|
||||
tab.move_active_pane_left();
|
||||
|
||||
assert_eq!(
|
||||
tab.get_active_pane().unwrap().x(),
|
||||
0,
|
||||
"Active pane is the left one"
|
||||
);
|
||||
assert_eq!(
|
||||
tab.get_active_pane().unwrap().pid(),
|
||||
PaneId::Terminal(2),
|
||||
"Active pane is the left one"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn move_active_pane_left_to_the_most_recently_used_position() {
|
||||
let size = Size {
|
||||
cols: 121,
|
||||
rows: 20,
|
||||
};
|
||||
let mut tab = create_new_tab(size);
|
||||
let new_pane_id_1 = PaneId::Terminal(2);
|
||||
let new_pane_id_2 = PaneId::Terminal(3);
|
||||
let new_pane_id_3 = PaneId::Terminal(4);
|
||||
|
||||
tab.vertical_split(new_pane_id_1);
|
||||
tab.move_focus_left();
|
||||
tab.horizontal_split(new_pane_id_2);
|
||||
tab.horizontal_split(new_pane_id_3);
|
||||
tab.move_focus_right();
|
||||
tab.move_active_pane_left();
|
||||
|
||||
assert_eq!(
|
||||
tab.get_active_pane().unwrap().y(),
|
||||
15,
|
||||
"Active pane y position"
|
||||
);
|
||||
assert_eq!(
|
||||
tab.get_active_pane().unwrap().x(),
|
||||
0,
|
||||
"Active pane x position"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tab.get_active_pane().unwrap().pid(),
|
||||
PaneId::Terminal(2),
|
||||
"Active pane PaneId"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn move_active_pane_right() {
|
||||
let size = Size {
|
||||
cols: 121,
|
||||
rows: 20,
|
||||
};
|
||||
let mut tab = create_new_tab(size);
|
||||
let new_pane_id = PaneId::Terminal(2);
|
||||
|
||||
tab.vertical_split(new_pane_id);
|
||||
tab.move_focus_left();
|
||||
tab.move_active_pane_right();
|
||||
|
||||
assert_eq!(
|
||||
tab.get_active_pane().unwrap().x(),
|
||||
61,
|
||||
"Active pane is the right one"
|
||||
);
|
||||
assert_eq!(
|
||||
tab.get_active_pane().unwrap().pid(),
|
||||
PaneId::Terminal(1),
|
||||
"Active pane is the right one"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn move_active_pane_right_to_the_most_recently_used_position() {
|
||||
let size = Size {
|
||||
cols: 121,
|
||||
rows: 20,
|
||||
};
|
||||
let mut tab = create_new_tab(size);
|
||||
let new_pane_id_1 = PaneId::Terminal(2);
|
||||
let new_pane_id_2 = PaneId::Terminal(3);
|
||||
let new_pane_id_3 = PaneId::Terminal(4);
|
||||
|
||||
tab.vertical_split(new_pane_id_1);
|
||||
tab.horizontal_split(new_pane_id_2);
|
||||
tab.horizontal_split(new_pane_id_3);
|
||||
tab.move_focus_left();
|
||||
tab.move_active_pane_right();
|
||||
|
||||
assert_eq!(
|
||||
tab.get_active_pane().unwrap().y(),
|
||||
15,
|
||||
"Active pane y position"
|
||||
);
|
||||
assert_eq!(
|
||||
tab.get_active_pane().unwrap().x(),
|
||||
61,
|
||||
"Active pane x position"
|
||||
);
|
||||
assert_eq!(
|
||||
tab.get_active_pane().unwrap().pid(),
|
||||
PaneId::Terminal(1),
|
||||
"Active pane Paneid"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn resize_down_with_pane_above() {
|
||||
// ┌───────────┐ ┌───────────┐
|
||||
|
@ -81,6 +81,9 @@ pub enum InputMode {
|
||||
/// `Session` mode allows detaching sessions
|
||||
#[serde(alias = "session")]
|
||||
Session,
|
||||
/// `Move` mode allows moving the different existing panes within a tab
|
||||
#[serde(alias = "move")]
|
||||
Move,
|
||||
}
|
||||
|
||||
impl Default for InputMode {
|
||||
@ -124,6 +127,7 @@ impl FromStr for InputMode {
|
||||
"scroll" => Ok(InputMode::Scroll),
|
||||
"renametab" => Ok(InputMode::RenameTab),
|
||||
"session" => Ok(InputMode::Session),
|
||||
"move" => Ok(InputMode::Move),
|
||||
e => Err(e.to_string().into()),
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ keybinds:
|
||||
key: [Ctrl: 's',]
|
||||
- action: [SwitchToMode: Session,]
|
||||
key: [Ctrl: 'o',]
|
||||
- action: [SwitchToMode: Move,]
|
||||
key: [Ctrl: 'h',]
|
||||
- action: [Quit,]
|
||||
key: [Ctrl: 'q',]
|
||||
- action: [NewPane: ]
|
||||
@ -52,6 +54,8 @@ keybinds:
|
||||
key: [Ctrl: 's']
|
||||
- action: [SwitchToMode: Session,]
|
||||
key: [Ctrl: 'o',]
|
||||
- action: [SwitchToMode: Move,]
|
||||
key: [Ctrl: 'h',]
|
||||
- action: [Quit]
|
||||
key: [Ctrl: 'q']
|
||||
- action: [Resize: Left,]
|
||||
@ -89,6 +93,8 @@ keybinds:
|
||||
key: [Ctrl: 's']
|
||||
- action: [SwitchToMode: Session,]
|
||||
key: [Ctrl: 'o',]
|
||||
- action: [SwitchToMode: Move,]
|
||||
key: [Ctrl: 'h',]
|
||||
- action: [Quit,]
|
||||
key: [Ctrl: 'q',]
|
||||
- action: [MoveFocus: Left,]
|
||||
@ -117,6 +123,45 @@ keybinds:
|
||||
key: [ Alt: '[',]
|
||||
- action: [FocusNextPane,]
|
||||
key: [ Alt: ']',]
|
||||
move:
|
||||
- action: [SwitchToMode: Locked,]
|
||||
key: [Ctrl: 'g']
|
||||
- action: [SwitchToMode: Pane,]
|
||||
key: [Ctrl: 'p',]
|
||||
- action: [SwitchToMode: Tab,]
|
||||
key: [Ctrl: 't',]
|
||||
- action: [SwitchToMode: Resize,]
|
||||
key: [Ctrl: 'n',]
|
||||
- action: [SwitchToMode: Normal,]
|
||||
key: [Ctrl: 'h', Char: "\n", Char: ' ',]
|
||||
- action: [SwitchToMode: Scroll,]
|
||||
key: [Ctrl: 's']
|
||||
- action: [SwitchToMode: Session,]
|
||||
key: [Ctrl: 'o',]
|
||||
- action: [Quit]
|
||||
key: [Ctrl: 'q']
|
||||
- action: [MovePane: Left,]
|
||||
key: [Char: 'h', Left,]
|
||||
- action: [MovePane: Down,]
|
||||
key: [Char: 'j', Down,]
|
||||
- action: [MovePane: Up,]
|
||||
key: [Char: 'k', Up, ]
|
||||
- action: [MovePane: Right,]
|
||||
key: [Char: 'l', Right,]
|
||||
- action: [NewPane: ,]
|
||||
key: [ Alt: 'n',]
|
||||
- action: [MoveFocus: Left,]
|
||||
key: [ Alt: 'h',]
|
||||
- action: [MoveFocus: Right,]
|
||||
key: [ Alt: 'l',]
|
||||
- action: [MoveFocus: Down,]
|
||||
key: [ Alt: 'j',]
|
||||
- action: [MoveFocus: Up,]
|
||||
key: [ Alt: 'k',]
|
||||
- action: [FocusPreviousPane,]
|
||||
key: [ Alt: '[',]
|
||||
- action: [FocusNextPane,]
|
||||
key: [ Alt: ']',]
|
||||
tab:
|
||||
- action: [SwitchToMode: Locked,]
|
||||
key: [Ctrl: 'g']
|
||||
@ -128,6 +173,8 @@ keybinds:
|
||||
key: [Ctrl: 't', Char: "\n", Char: ' ',]
|
||||
- action: [SwitchToMode: Scroll,]
|
||||
key: [Ctrl: 's']
|
||||
- action: [SwitchToMode: Move,]
|
||||
key: [Ctrl: 'h',]
|
||||
- action: [SwitchToMode: Session,]
|
||||
key: [Ctrl: 'o',]
|
||||
- action: [SwitchToMode: RenameTab, TabNameInput: [0],]
|
||||
@ -186,6 +233,8 @@ keybinds:
|
||||
key: [Ctrl: 'g',]
|
||||
- action: [SwitchToMode: Pane,]
|
||||
key: [Ctrl: 'p',]
|
||||
- action: [SwitchToMode: Move,]
|
||||
key: [Ctrl: 'h',]
|
||||
- action: [SwitchToMode: Session,]
|
||||
key: [Ctrl: 'o',]
|
||||
- action: [SwitchToMode: Resize,]
|
||||
@ -244,6 +293,8 @@ keybinds:
|
||||
key: [Ctrl: 'n',]
|
||||
- action: [SwitchToMode: Pane,]
|
||||
key: [Ctrl: 'p',]
|
||||
- action: [SwitchToMode: Move,]
|
||||
key: [Ctrl: 'h',]
|
||||
- action: [SwitchToMode: Tab,]
|
||||
key: [Ctrl: 't',]
|
||||
- action: [SwitchToMode: Normal,]
|
||||
|
@ -226,6 +226,10 @@ pub enum ScreenContext {
|
||||
MoveFocusUp,
|
||||
MoveFocusRight,
|
||||
MoveFocusRightOrNextTab,
|
||||
MovePaneDown,
|
||||
MovePaneUp,
|
||||
MovePaneRight,
|
||||
MovePaneLeft,
|
||||
Exit,
|
||||
ScrollUp,
|
||||
ScrollUpAt,
|
||||
|
@ -41,6 +41,7 @@ pub enum Action {
|
||||
/// Tries to move the focus pane in specified direction.
|
||||
/// If there is no pane in the direction, move to previous/next Tab.
|
||||
MoveFocusOrTab(Direction),
|
||||
MovePane(Direction),
|
||||
/// Scroll up in focus pane.
|
||||
ScrollUp,
|
||||
/// Scroll up at point
|
||||
|
@ -23,6 +23,7 @@ pub fn get_mode_info(
|
||||
let keybinds = match mode {
|
||||
InputMode::Normal | InputMode::Locked => Vec::new(),
|
||||
InputMode::Resize => vec![("←↓↑→".to_string(), "Resize".to_string())],
|
||||
InputMode::Move => vec![("←↓↑→".to_string(), "Move".to_string())],
|
||||
InputMode::Pane => vec![
|
||||
("←↓↑→".to_string(), "Move focus".to_string()),
|
||||
("p".to_string(), "Next".to_string()),
|
||||
|
@ -378,7 +378,7 @@ fn unbind_multiple_keybinds_all_modes() {
|
||||
let result_normal_2 = mode_keybinds_normal
|
||||
.expect("ModeKeybinds shouldn't be empty")
|
||||
.0
|
||||
.get(&Key::Ctrl('h'));
|
||||
.get(&Key::Ctrl('f'));
|
||||
let result_resize_1 = mode_keybinds_resize
|
||||
.expect("ModeKeybinds shouldn't be empty")
|
||||
.0
|
||||
@ -386,7 +386,7 @@ fn unbind_multiple_keybinds_all_modes() {
|
||||
let result_resize_2 = mode_keybinds_resize
|
||||
.expect("ModeKeybinds shouldn't be empty")
|
||||
.0
|
||||
.get(&Key::Ctrl('h'));
|
||||
.get(&Key::Ctrl('f'));
|
||||
assert!(result_normal_1.is_none());
|
||||
assert!(result_resize_1.is_none());
|
||||
assert!(result_normal_2.is_none());
|
||||
|
Loading…
Reference in New Issue
Block a user