feat(tab): add keybind to go to last tab visited

Fixes #398.

Tab key is used as default for the `GoToLastTab` action.
This commit is contained in:
Sagittarius-a 2021-07-21 23:43:20 +02:00
parent 309f4a62bf
commit 5799ea4e5d
8 changed files with 70 additions and 0 deletions

View file

@ -188,6 +188,7 @@ impl InputHandler {
| Action::GoToPreviousTab | Action::GoToPreviousTab
| Action::CloseTab | Action::CloseTab
| Action::GoToTab(_) | Action::GoToTab(_)
| Action::GoToLastTab
| Action::MoveFocusOrTab(_) => { | Action::MoveFocusOrTab(_) => {
self.command_is_executing.blocking_input_thread(); self.command_is_executing.blocking_input_thread();
self.os_input self.os_input

View file

@ -24,6 +24,12 @@ fn route_action(
) -> bool { ) -> bool {
let mut should_break = false; let mut should_break = false;
match action { match action {
Action::GoToLastTab => {
session
.senders
.send_to_screen(ScreenInstruction::GoToLastTab)
.unwrap();
}
Action::Write(val) => { Action::Write(val) => {
session session
.senders .senders

View file

@ -68,6 +68,7 @@ pub(crate) enum ScreenInstruction {
ToggleActiveSyncTab, ToggleActiveSyncTab,
CloseTab, CloseTab,
GoToTab(u32), GoToTab(u32),
GoToLastTab,
UpdateTabName(Vec<u8>), UpdateTabName(Vec<u8>),
TerminalResize(PositionAndSize), TerminalResize(PositionAndSize),
ChangeMode(ModeInfo), ChangeMode(ModeInfo),
@ -133,6 +134,7 @@ impl From<&ScreenInstruction> for ScreenContext {
ScreenInstruction::MouseRelease(_) => ScreenContext::MouseRelease, ScreenInstruction::MouseRelease(_) => ScreenContext::MouseRelease,
ScreenInstruction::MouseHold(_) => ScreenContext::MouseHold, ScreenInstruction::MouseHold(_) => ScreenContext::MouseHold,
ScreenInstruction::Copy => ScreenContext::Copy, ScreenInstruction::Copy => ScreenContext::Copy,
ScreenInstruction::GoToLastTab => ScreenContext::GoToLastTab,
} }
} }
} }
@ -150,6 +152,7 @@ pub(crate) struct Screen {
position_and_size: PositionAndSize, position_and_size: PositionAndSize,
/// The index of this [`Screen`]'s active [`Tab`]. /// The index of this [`Screen`]'s active [`Tab`].
active_tab_index: Option<usize>, active_tab_index: Option<usize>,
last_active_tab_index: Option<usize>,
mode_info: ModeInfo, mode_info: ModeInfo,
colors: Palette, colors: Palette,
session_state: Arc<RwLock<SessionState>>, session_state: Arc<RwLock<SessionState>>,
@ -170,6 +173,7 @@ impl Screen {
position_and_size: client_attributes.position_and_size, position_and_size: client_attributes.position_and_size,
colors: client_attributes.palette, colors: client_attributes.palette,
active_tab_index: None, active_tab_index: None,
last_active_tab_index: None,
tabs: BTreeMap::new(), tabs: BTreeMap::new(),
mode_info, mode_info,
session_state, session_state,
@ -194,6 +198,7 @@ impl Screen {
self.colors, self.colors,
self.session_state.clone(), self.session_state.clone(),
); );
self.last_active_tab_index = self.active_tab_index;
self.active_tab_index = Some(tab_index); self.active_tab_index = Some(tab_index);
self.tabs.insert(tab_index, tab); self.tabs.insert(tab_index, tab);
self.update_tabs(); self.update_tabs();
@ -219,6 +224,7 @@ impl Screen {
for tab in self.tabs.values_mut() { for tab in self.tabs.values_mut() {
if tab.position == new_tab_pos { if tab.position == new_tab_pos {
tab.set_force_render(); tab.set_force_render();
self.last_active_tab_index = self.active_tab_index;
self.active_tab_index = Some(tab.index); self.active_tab_index = Some(tab.index);
break; break;
} }
@ -238,6 +244,7 @@ impl Screen {
for tab in self.tabs.values_mut() { for tab in self.tabs.values_mut() {
if tab.position == new_tab_pos { if tab.position == new_tab_pos {
tab.set_force_render(); tab.set_force_render();
self.last_active_tab_index = self.active_tab_index;
self.active_tab_index = Some(tab.index); self.active_tab_index = Some(tab.index);
break; break;
} }
@ -252,6 +259,7 @@ impl Screen {
if let Some(t) = self.tabs.values_mut().find(|t| t.position == tab_index) { if let Some(t) = self.tabs.values_mut().find(|t| t.position == tab_index) {
if t.index != active_tab_index { if t.index != active_tab_index {
t.set_force_render(); t.set_force_render();
self.last_active_tab_index = self.active_tab_index;
self.active_tab_index = Some(t.index); self.active_tab_index = Some(t.index);
self.update_tabs(); self.update_tabs();
self.render(); self.render();
@ -277,6 +285,7 @@ impl Screen {
.unwrap(); .unwrap();
if self.tabs.is_empty() { if self.tabs.is_empty() {
self.active_tab_index = None; self.active_tab_index = None;
self.last_active_tab_index = None;
if *self.session_state.read().unwrap() == SessionState::Attached { if *self.session_state.read().unwrap() == SessionState::Attached {
self.bus self.bus
.senders .senders
@ -361,6 +370,7 @@ impl Screen {
self.session_state.clone(), self.session_state.clone(),
); );
tab.apply_layout(layout, new_pids, tab_index); tab.apply_layout(layout, new_pids, tab_index);
self.last_active_tab_index = self.active_tab_index;
self.active_tab_index = Some(tab_index); self.active_tab_index = Some(tab_index);
self.tabs.insert(tab_index, tab); self.tabs.insert(tab_index, tab);
self.update_tabs(); self.update_tabs();
@ -417,6 +427,15 @@ impl Screen {
self.switch_tab_next(); self.switch_tab_next();
} }
} }
pub fn go_to_last_tab(&mut self) {
let active_tab_index = self.active_tab_index.unwrap();
if let Some(i) = self.last_active_tab_index {
self.go_to_tab(i + 1);
}
self.last_active_tab_index = Some(active_tab_index);
self.update_tabs();
self.render();
}
} }
// The box is here in order to make the // The box is here in order to make the
@ -751,6 +770,14 @@ pub(crate) fn screen_thread_main(
ScreenInstruction::Exit => { ScreenInstruction::Exit => {
break; break;
} }
ScreenInstruction::GoToLastTab => {
screen.go_to_last_tab();
screen
.bus
.senders
.send_to_server(ServerInstruction::UnblockInputThread)
.unwrap();
}
} }
} }
} }

View file

@ -247,3 +247,34 @@ fn move_focus_right_at_right_screen_edge_changes_tab() {
"Active tab switched to next" "Active tab switched to next"
); );
} }
#[test]
pub fn switch_to_last_tab() {
let position_and_size = PositionAndSize {
cols: 121,
rows: 20,
x: 0,
y: 0,
..Default::default()
};
let mut screen = create_new_screen(position_and_size);
screen.new_tab(1);
screen.new_tab(2);
screen.go_to_tab(1);
screen.go_to_tab(2);
screen.go_to_last_tab();
assert_eq!(
screen.get_active_tab().unwrap().position,
0,
"Active tab switched to last tab"
);
screen.go_to_last_tab();
assert_eq!(
screen.get_active_tab().unwrap().position,
1,
"Active tab switched to last tab"
);
}

View file

@ -166,6 +166,8 @@ keybinds:
key: [ Char: '8',] key: [ Char: '8',]
- action: [GoToTab: 9,] - action: [GoToTab: 9,]
key: [ Char: '9',] key: [ Char: '9',]
- action: [GoToLastTab]
key: [ Char: "\t" ]
scroll: scroll:
- action: [SwitchToMode: Normal,] - action: [SwitchToMode: Normal,]
key: [Ctrl: 'r', Ctrl: 's', Char: ' ', key: [Ctrl: 'r', Ctrl: 's', Char: ' ',

View file

@ -227,6 +227,7 @@ pub enum ScreenContext {
MouseRelease, MouseRelease,
MouseHold, MouseHold,
Copy, Copy,
GoToLastTab,
} }
/// Stack call representations corresponding to the different types of [`PtyInstruction`]s. /// Stack call representations corresponding to the different types of [`PtyInstruction`]s.

View file

@ -74,6 +74,7 @@ pub enum Action {
/// Close the current tab. /// Close the current tab.
CloseTab, CloseTab,
GoToTab(u32), GoToTab(u32),
GoToLastTab,
TabNameInput(Vec<u8>), TabNameInput(Vec<u8>),
/// Run speficied command in new pane. /// Run speficied command in new pane.
Run(RunCommandAction), Run(RunCommandAction),

View file

@ -37,6 +37,7 @@ pub fn get_mode_info(
("x".to_string(), "Close".to_string()), ("x".to_string(), "Close".to_string()),
("r".to_string(), "Rename".to_string()), ("r".to_string(), "Rename".to_string()),
("s".to_string(), "Sync".to_string()), ("s".to_string(), "Sync".to_string()),
("Tab".to_string(), "Last".to_string()),
], ],
InputMode::Scroll => vec![ InputMode::Scroll => vec![
("↓↑".to_string(), "Scroll".to_string()), ("↓↑".to_string(), "Scroll".to_string()),