diff --git a/docs/docs/key-bindings.md b/docs/docs/key-bindings.md index 5af8f0612c..5f910ced40 100644 --- a/docs/docs/key-bindings.md +++ b/docs/docs/key-bindings.md @@ -74,25 +74,28 @@ Execute a predefined action in Rio terminal. ### [Split Actions](#split-actions) -| Action | Description | -| :-------------- | :------------------------------------------------------------------------- | -| SplitRight | Create a split by right side | -| SplitDown | Create a split by under current pane | -| SelectNextSplit | Select next split | -| SelectPrevSplit | Select previous split | -| CloseSplitOrTab | Close split, if split is the last then will close the tab | +| Action | Description | +| :------------------- | :------------------------------------------------------------------------- | +| SplitRight | Create a split by right side | +| SplitDown | Create a split by under current pane | +| SelectNextSplit | Select next split | +| SelectPrevSplit | Select previous split | +| CloseSplitOrTab | Close split, if split is the last then will close the tab | +| SelectNextSplitOrTab | Select next split if available if not next tab | +| SelectPrevSplitOrTab | Select previous split if available if not previous tab | ### [Tab Actions](#tab-actions) | Action | Description | | :------------------- | :---------------------------------------------------------------------- | -| CreateTab | | -| CloseTab | | -| CloseUnfocusedTabs | | -| SelectPrevTab | | -| SelectNextTab | | -| SelectLastTab | | -| MoveCurrentTabToPrev | Move the current focused tab to the previous slot if any is available | +| CreateTab | Create new tab | +| CloseTab | Close current tab | +| CloseUnfocusedTabs | Close all tabs that are not currently focused | +| SelectNextTab | Select next tab | +| SelectPrevTab | Select pervious tab | +| SelectLastTab | Select last tab | +| MoveCurrentTabToNext | Move the current focused tab to the next slot, or first when last | +| MoveCurrentTabToPrev | Move the current focused tab to the previous slot, or last when first | | SelectTab(tab_index) | Example: Select first tab `SelectTab(0)`, second tab `SelectTab(1)` | ### [Scroll Actions](#scroll-actions) diff --git a/frontends/rioterm/src/bindings/mod.rs b/frontends/rioterm/src/bindings/mod.rs index 854c35943f..81862318ee 100644 --- a/frontends/rioterm/src/bindings/mod.rs +++ b/frontends/rioterm/src/bindings/mod.rs @@ -254,6 +254,8 @@ impl From for Action { "splitdown" => Some(Action::SplitDown), "selectnextsplit" => Some(Action::SelectNextSplit), "selectprevsplit" => Some(Action::SelectPrevSplit), + "selectnextsplitortab" => Some(Action::SelectNextSplitOrTab), + "selectprevsplitortab" => Some(Action::SelectPrevSplitOrTab), "togglevimode" => Some(Action::ToggleViMode), "togglefullscreen" => Some(Action::ToggleFullscreen), "none" => Some(Action::None), @@ -465,9 +467,18 @@ pub enum Action { /// Split vertically SplitDown, + /// Select next split SelectNextSplit, + + /// Select previous split SelectPrevSplit, + /// Select next split if available if not next tab + SelectNextSplitOrTab, + + /// Select previous split if available if not previous tab + SelectPrevSplitOrTab, + /// Allow receiving char input. ReceiveChar, diff --git a/frontends/rioterm/src/context/grid.rs b/frontends/rioterm/src/context/grid.rs index c17127104d..ebfa3f5a59 100644 --- a/frontends/rioterm/src/context/grid.rs +++ b/frontends/rioterm/src/context/grid.rs @@ -140,6 +140,21 @@ impl ContextGrid { } } + #[inline] + pub fn select_next_split_no_loop(&mut self) -> bool { + if self.inner.len() == 1 { + return false; + } + + if self.current >= self.inner.len() - 1 { + return false; + } else { + self.current += 1; + } + + return true; + } + #[inline] pub fn select_prev_split(&mut self) { if self.inner.len() == 1 { @@ -153,6 +168,20 @@ impl ContextGrid { } } + #[inline] + pub fn select_prev_split_no_loop(&mut self) -> bool { + if self.inner.len() == 1 { + return false; + } + + if self.current == 0 { + return false; + } else { + self.current -= 1; + } + return true; + } + #[inline] #[allow(unused)] pub fn current_index(&self) -> usize { diff --git a/frontends/rioterm/src/context/mod.rs b/frontends/rioterm/src/context/mod.rs index 66cf4b39f4..1487765426 100644 --- a/frontends/rioterm/src/context/mod.rs +++ b/frontends/rioterm/src/context/mod.rs @@ -570,6 +570,32 @@ impl ContextManager { self.current_route = self.current().route_id; } + #[inline] + pub fn switch_to_next_split_or_tab(&mut self) { + if self.contexts[self.current_index].select_next_split_no_loop() { + self.current_route = self.current().route_id; + return; + } + self.switch_to_next(); + // Make sure first split is selected + let current_tab = &mut self.contexts[self.current_index]; + current_tab.current = 0; + self.current_route = self.current().route_id; + } + + #[inline] + pub fn switch_to_prev_split_or_tab(&mut self) { + if self.contexts[self.current_index].select_prev_split_no_loop() { + self.current_route = self.current().route_id; + return; + } + self.switch_to_prev(); + // Make sure last split is selected + let current_tab = &mut self.contexts[self.current_index]; + current_tab.current = current_tab.len() - 1; + self.current_route = self.current().route_id; + } + #[inline] pub fn select_tab(&mut self, tab_index: usize) { if self.config.is_native { @@ -1229,6 +1255,194 @@ pub mod test { assert_eq!(context_manager.current_index, 1); } + #[test] + fn test_switch_to_next_split_or_tab() { + let window_id: WindowId = WindowId::from(0); + + let mut context_manager = + ContextManager::start_with_capacity(5, VoidListener {}, window_id).unwrap(); + let should_redirect = true; + let split_down = false; + + context_manager.add_context(should_redirect, 0); + context_manager.split(0, split_down); + context_manager.split(0, split_down); + context_manager.add_context(should_redirect, 0); + context_manager.add_context(should_redirect, 0); + context_manager.split(0, split_down); + context_manager.add_context(should_redirect, 0); + context_manager.set_current(0); + assert_eq!(context_manager.len(), 5); + assert_eq!(context_manager.current_index, 0); + + let mut current_index; + + context_manager.switch_to_next_split_or_tab(); + current_index = context_manager.current_index; + assert_eq!(current_index, 1); + assert_eq!(context_manager.contexts[current_index].current, 0); + + context_manager.switch_to_next_split_or_tab(); + current_index = context_manager.current_index; + assert_eq!(current_index, 1); + assert_eq!(context_manager.contexts[current_index].current, 1); + + context_manager.switch_to_next_split_or_tab(); + current_index = context_manager.current_index; + assert_eq!(current_index, 1); + assert_eq!(context_manager.contexts[current_index].current, 2); + + context_manager.switch_to_next_split_or_tab(); + current_index = context_manager.current_index; + assert_eq!(current_index, 2); + assert_eq!(context_manager.contexts[current_index].current, 0); + + context_manager.switch_to_next_split_or_tab(); + current_index = context_manager.current_index; + assert_eq!(current_index, 3); + assert_eq!(context_manager.contexts[current_index].current, 0); + + context_manager.switch_to_next_split_or_tab(); + current_index = context_manager.current_index; + assert_eq!(current_index, 3); + assert_eq!(context_manager.contexts[current_index].current, 1); + + context_manager.switch_to_next_split_or_tab(); + current_index = context_manager.current_index; + assert_eq!(current_index, 4); + assert_eq!(context_manager.contexts[current_index].current, 0); + + context_manager.switch_to_next_split_or_tab(); + current_index = context_manager.current_index; + assert_eq!(current_index, 0); + assert_eq!(context_manager.contexts[current_index].current, 0); + } + + #[test] + fn test_switch_to_prev_split_or_tab() { + let window_id: WindowId = WindowId::from(0); + + let mut context_manager = + ContextManager::start_with_capacity(5, VoidListener {}, window_id).unwrap(); + let should_redirect = true; + let split_down = false; + + context_manager.add_context(should_redirect, 0); + context_manager.split(0, split_down); + context_manager.split(0, split_down); + context_manager.add_context(should_redirect, 0); + context_manager.add_context(should_redirect, 0); + context_manager.split(0, split_down); + context_manager.add_context(should_redirect, 0); + context_manager.set_current(0); + assert_eq!(context_manager.len(), 5); + assert_eq!(context_manager.current_index, 0); + + let mut current_index; + + context_manager.switch_to_prev_split_or_tab(); + current_index = context_manager.current_index; + assert_eq!(current_index, 4); + assert_eq!(context_manager.contexts[current_index].current, 0); + + context_manager.switch_to_prev_split_or_tab(); + current_index = context_manager.current_index; + assert_eq!(current_index, 3); + assert_eq!(context_manager.contexts[current_index].current, 1); + + context_manager.switch_to_prev_split_or_tab(); + current_index = context_manager.current_index; + assert_eq!(current_index, 3); + assert_eq!(context_manager.contexts[current_index].current, 0); + + context_manager.switch_to_prev_split_or_tab(); + current_index = context_manager.current_index; + assert_eq!(current_index, 2); + assert_eq!(context_manager.contexts[current_index].current, 0); + + context_manager.switch_to_prev_split_or_tab(); + current_index = context_manager.current_index; + assert_eq!(current_index, 1); + assert_eq!(context_manager.contexts[current_index].current, 2); + + context_manager.switch_to_prev_split_or_tab(); + current_index = context_manager.current_index; + assert_eq!(current_index, 1); + assert_eq!(context_manager.contexts[current_index].current, 1); + + context_manager.switch_to_prev_split_or_tab(); + current_index = context_manager.current_index; + assert_eq!(current_index, 1); + assert_eq!(context_manager.contexts[current_index].current, 0); + + context_manager.switch_to_prev_split_or_tab(); + current_index = context_manager.current_index; + assert_eq!(current_index, 0); + assert_eq!(context_manager.contexts[current_index].current, 0); + } + + #[test] + fn test_switch_to_next_and_prev_split_or_tab() { + let window_id: WindowId = WindowId::from(0); + + let mut context_manager = + ContextManager::start_with_capacity(5, VoidListener {}, window_id).unwrap(); + let should_redirect = true; + let split_down = false; + + context_manager.add_context(should_redirect, 0); + context_manager.split(0, split_down); + context_manager.split(0, split_down); + context_manager.add_context(should_redirect, 0); + context_manager.set_current(0); + assert_eq!(context_manager.len(), 3); + assert_eq!(context_manager.current_index, 0); + + let mut current_index; + + // Next + context_manager.switch_to_next_split_or_tab(); + current_index = context_manager.current_index; + assert_eq!(current_index, 1); + assert_eq!(context_manager.contexts[current_index].current, 0); + + context_manager.switch_to_next_split_or_tab(); + current_index = context_manager.current_index; + assert_eq!(current_index, 1); + assert_eq!(context_manager.contexts[current_index].current, 1); + + context_manager.switch_to_next_split_or_tab(); + current_index = context_manager.current_index; + assert_eq!(current_index, 1); + assert_eq!(context_manager.contexts[current_index].current, 2); + + context_manager.switch_to_next_split_or_tab(); + current_index = context_manager.current_index; + assert_eq!(current_index, 2); + assert_eq!(context_manager.contexts[current_index].current, 0); + + // Prev + context_manager.switch_to_prev_split_or_tab(); + current_index = context_manager.current_index; + assert_eq!(current_index, 1); + assert_eq!(context_manager.contexts[current_index].current, 2); + + context_manager.switch_to_prev_split_or_tab(); + current_index = context_manager.current_index; + assert_eq!(current_index, 1); + assert_eq!(context_manager.contexts[current_index].current, 1); + + context_manager.switch_to_prev_split_or_tab(); + current_index = context_manager.current_index; + assert_eq!(current_index, 1); + assert_eq!(context_manager.contexts[current_index].current, 0); + + context_manager.switch_to_prev_split_or_tab(); + current_index = context_manager.current_index; + assert_eq!(current_index, 0); + assert_eq!(context_manager.contexts[current_index].current, 0); + } + #[test] fn test_move_current_to_next() { let window_id = WindowId::from(0); diff --git a/frontends/rioterm/src/screen/mod.rs b/frontends/rioterm/src/screen/mod.rs index f9cd0e0b0f..77b6cee7da 100644 --- a/frontends/rioterm/src/screen/mod.rs +++ b/frontends/rioterm/src/screen/mod.rs @@ -997,6 +997,16 @@ impl Screen<'_> { self.context_manager.select_prev_split(); self.render(); } + Act::SelectNextSplitOrTab => { + self.cancel_search(); + self.context_manager.switch_to_next_split_or_tab(); + self.render(); + } + Act::SelectPrevSplitOrTab => { + self.cancel_search(); + self.context_manager.switch_to_prev_split_or_tab(); + self.render(); + } Act::SelectTab(tab_index) => { self.context_manager.select_tab(*tab_index); self.cancel_search();