aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2021-04-08 11:36:49 +0200
committerGitHub <noreply@github.com>2021-04-08 11:36:49 +0200
commit65c75ebb9593dc2a3b2ae5a82db03dad41bd1b10 (patch)
treebfd6caeeddb2b0f3b46baaeb59423414ded8a932 /src
parentfix(tabs): do not crash if closing pane in fullscreen (#259) (diff)
downloadsl0ppij-65c75ebb9593dc2a3b2ae5a82db03dad41bd1b10.tar.gz
sl0ppij-65c75ebb9593dc2a3b2ae5a82db03dad41bd1b10.tar.xz
sl0ppij-65c75ebb9593dc2a3b2ae5a82db03dad41bd1b10.zip
feat(controls): add quick navigation (#260)
* feat(input): quick navigation * feat(ui): quick navigation * style(fmt): rustfmt
Diffstat (limited to 'src')
-rw-r--r--src/client/boundaries.rs4
-rw-r--r--src/client/tab.rs56
-rw-r--r--src/common/errors.rs8
-rw-r--r--src/common/input/actions.rs4
-rw-r--r--src/common/input/handler.rs55
-rw-r--r--src/common/input/keybinds.rs49
-rw-r--r--src/common/mod.rs10
-rw-r--r--src/common/screen.rs4
8 files changed, 163 insertions, 27 deletions
diff --git a/src/client/boundaries.rs b/src/client/boundaries.rs
index e498da89..28e01a11 100644
--- a/src/client/boundaries.rs
+++ b/src/client/boundaries.rs
@@ -21,9 +21,9 @@ pub mod boundary_type {
pub mod colors {
use ansi_term::Colour::{self, Fixed};
- pub const WHITE: Colour = Fixed(255);
pub const GREEN: Colour = Fixed(154);
pub const GRAY: Colour = Fixed(238);
+ pub const ORANGE: Colour = Fixed(166);
}
pub type BoundaryType = &'static str; // easy way to refer to boundary_type above
@@ -768,7 +768,7 @@ impl Boundaries {
let color = match color.is_some() {
true => match input_mode {
InputMode::Normal | InputMode::Locked => Some(colors::GREEN),
- _ => Some(colors::WHITE),
+ _ => Some(colors::ORANGE),
},
false => None,
};
diff --git a/src/client/tab.rs b/src/client/tab.rs
index 82c2639f..3a12656d 100644
--- a/src/client/tab.rs
+++ b/src/client/tab.rs
@@ -1809,6 +1809,62 @@ impl Tab {
}
self.render();
}
+ pub fn focus_next_pane(&mut self) {
+ if !self.has_selectable_panes() {
+ return;
+ }
+ if self.fullscreen_is_active {
+ return;
+ }
+ let active_pane_id = self.get_active_pane_id().unwrap();
+ let mut panes: Vec<(&PaneId, &Box<dyn Pane>)> = self.get_selectable_panes().collect();
+ panes.sort_by(|(_a_id, a_pane), (_b_id, b_pane)| {
+ if a_pane.y() == b_pane.y() {
+ a_pane.x().cmp(&b_pane.x())
+ } else {
+ a_pane.y().cmp(&b_pane.y())
+ }
+ });
+ let first_pane = panes.get(0).unwrap();
+ let active_pane_position = panes
+ .iter()
+ .position(|(id, _)| *id == &active_pane_id) // TODO: better
+ .unwrap();
+ if let Some(next_pane) = panes.get(active_pane_position + 1) {
+ self.active_terminal = Some(*next_pane.0);
+ } else {
+ self.active_terminal = Some(*first_pane.0);
+ }
+ self.render();
+ }
+ pub fn focus_previous_pane(&mut self) {
+ if !self.has_selectable_panes() {
+ return;
+ }
+ if self.fullscreen_is_active {
+ return;
+ }
+ let active_pane_id = self.get_active_pane_id().unwrap();
+ let mut panes: Vec<(&PaneId, &Box<dyn Pane>)> = self.get_selectable_panes().collect();
+ panes.sort_by(|(_a_id, a_pane), (_b_id, b_pane)| {
+ if a_pane.y() == b_pane.y() {
+ a_pane.x().cmp(&b_pane.x())
+ } else {
+ a_pane.y().cmp(&b_pane.y())
+ }
+ });
+ let last_pane = panes.last().unwrap();
+ let active_pane_position = panes
+ .iter()
+ .position(|(id, _)| *id == &active_pane_id) // TODO: better
+ .unwrap();
+ if active_pane_position == 0 {
+ self.active_terminal = Some(*last_pane.0);
+ } else {
+ self.active_terminal = Some(*panes.get(active_pane_position - 1).unwrap().0);
+ }
+ self.render();
+ }
pub fn move_focus_left(&mut self) {
if !self.has_selectable_panes() {
return;
diff --git a/src/common/errors.rs b/src/common/errors.rs
index 00284cd4..1548aec0 100644
--- a/src/common/errors.rs
+++ b/src/common/errors.rs
@@ -178,7 +178,9 @@ pub enum ScreenContext {
ResizeRight,
ResizeDown,
ResizeUp,
- MoveFocus,
+ SwitchFocus,
+ FocusNextPane,
+ FocusPreviousPane,
MoveFocusLeft,
MoveFocusDown,
MoveFocusUp,
@@ -218,7 +220,9 @@ impl From<&ScreenInstruction> for ScreenContext {
ScreenInstruction::ResizeRight => ScreenContext::ResizeRight,
ScreenInstruction::ResizeDown => ScreenContext::ResizeDown,
ScreenInstruction::ResizeUp => ScreenContext::ResizeUp,
- ScreenInstruction::MoveFocus => ScreenContext::MoveFocus,
+ ScreenInstruction::SwitchFocus => ScreenContext::SwitchFocus,
+ ScreenInstruction::FocusNextPane => ScreenContext::FocusNextPane,
+ ScreenInstruction::FocusPreviousPane => ScreenContext::FocusPreviousPane,
ScreenInstruction::MoveFocusLeft => ScreenContext::MoveFocusLeft,
ScreenInstruction::MoveFocusDown => ScreenContext::MoveFocusDown,
ScreenInstruction::MoveFocusUp => ScreenContext::MoveFocusUp,
diff --git a/src/common/input/actions.rs b/src/common/input/actions.rs
index ac923f7d..47a5f763 100644
--- a/src/common/input/actions.rs
+++ b/src/common/input/actions.rs
@@ -24,8 +24,10 @@ pub enum Action {
/// Resize focus pane in specified direction.
Resize(Direction),
/// Switch focus to next pane in specified direction.
- SwitchFocus(Direction),
+ FocusNextPane,
+ FocusPreviousPane,
/// Move the focus pane in specified direction.
+ SwitchFocus,
MoveFocus(Direction),
/// Scroll up in focus pane.
ScrollUp,
diff --git a/src/common/input/handler.rs b/src/common/input/handler.rs
index 2020fc20..44065c67 100644
--- a/src/common/input/handler.rs
+++ b/src/common/input/handler.rs
@@ -26,6 +26,7 @@ struct InputHandler {
send_pty_instructions: SenderWithContext<PtyInstruction>,
send_plugin_instructions: SenderWithContext<PluginInstruction>,
send_app_instructions: SenderWithContext<AppInstruction>,
+ should_exit: bool,
}
impl InputHandler {
@@ -48,6 +49,7 @@ impl InputHandler {
send_pty_instructions,
send_plugin_instructions,
send_app_instructions,
+ should_exit: false,
}
}
@@ -60,30 +62,29 @@ impl InputHandler {
self.send_app_instructions.update(err_ctx);
self.send_screen_instructions.update(err_ctx);
let keybinds = self.config.keybinds.clone();
- 'input_loop: loop {
- //@@@ I think this should actually just iterate over stdin directly
+ let alt_left_bracket = vec![27, 91];
+ loop {
+ if self.should_exit {
+ break;
+ }
let stdin_buffer = self.os_input.read_from_stdin();
for key_result in stdin_buffer.events_and_raw() {
match key_result {
Ok((event, raw_bytes)) => match event {
termion::event::Event::Key(key) => {
let key = cast_termion_key(key);
- // FIXME this explicit break is needed because the current test
- // framework relies on it to not create dead threads that loop
- // and eat up CPUs. Do not remove until the test framework has
- // been revised. Sorry about this (@categorille)
- let mut should_break = false;
- for action in
- Keybinds::key_to_actions(&key, raw_bytes, &self.mode, &keybinds)
- {
- should_break |= self.dispatch_action(action);
- }
- if should_break {
- break 'input_loop;
+ self.handle_key(&key, raw_bytes, &keybinds);
+ }
+ termion::event::Event::Unsupported(unsupported_key) => {
+ // we have to do this because of a bug in termion
+ // this should be a key event and not an unsupported event
+ if unsupported_key == alt_left_bracket {
+ let key = Key::Alt('[');
+ self.handle_key(&key, raw_bytes, &keybinds);
}
}
- termion::event::Event::Mouse(_) | termion::event::Event::Unsupported(_) => {
- // Mouse and unsupported events aren't implemented yet,
+ termion::event::Event::Mouse(_) => {
+ // Mouse events aren't implemented yet,
// use a NoOp untill then.
}
},
@@ -92,6 +93,14 @@ impl InputHandler {
}
}
}
+ fn handle_key(&mut self, key: &Key, raw_bytes: Vec<u8>, keybinds: &Keybinds) {
+ for action in Keybinds::key_to_actions(&key, raw_bytes, &self.mode, &keybinds) {
+ let should_exit = self.dispatch_action(action);
+ if should_exit {
+ self.should_exit = true;
+ }
+ }
+ }
/// Dispatches an [`Action`].
///
@@ -144,9 +153,19 @@ impl InputHandler {
};
self.send_screen_instructions.send(screen_instr).unwrap();
}
- Action::SwitchFocus(_) => {
+ Action::SwitchFocus => {
+ self.send_screen_instructions
+ .send(ScreenInstruction::SwitchFocus)
+ .unwrap();
+ }
+ Action::FocusNextPane => {
+ self.send_screen_instructions
+ .send(ScreenInstruction::FocusNextPane)
+ .unwrap();
+ }
+ Action::FocusPreviousPane => {
self.send_screen_instructions
- .send(ScreenInstruction::MoveFocus)
+ .send(ScreenInstruction::FocusPreviousPane)
.unwrap();
}
Action::MoveFocus(direction) => {
diff --git a/src/common/input/keybinds.rs b/src/common/input/keybinds.rs
index 6a14a182..8a064970 100644
--- a/src/common/input/keybinds.rs
+++ b/src/common/input/keybinds.rs
@@ -91,6 +91,14 @@ impl Keybinds {
vec![Action::SwitchToMode(InputMode::Scroll)],
);
defaults.insert(Key::Ctrl('q'), vec![Action::Quit]);
+
+ defaults.insert(Key::Alt('n'), vec![Action::NewPane(None)]);
+ defaults.insert(Key::Alt('h'), vec![Action::MoveFocus(Direction::Left)]);
+ defaults.insert(Key::Alt('j'), vec![Action::MoveFocus(Direction::Down)]);
+ defaults.insert(Key::Alt('k'), vec![Action::MoveFocus(Direction::Up)]);
+ defaults.insert(Key::Alt('l'), vec![Action::MoveFocus(Direction::Right)]);
+ defaults.insert(Key::Alt('['), vec![Action::FocusPreviousPane]);
+ defaults.insert(Key::Alt(']'), vec![Action::FocusNextPane]);
}
InputMode::Locked => {
defaults.insert(
@@ -133,6 +141,14 @@ impl Keybinds {
defaults.insert(Key::Down, vec![Action::Resize(Direction::Down)]);
defaults.insert(Key::Up, vec![Action::Resize(Direction::Up)]);
defaults.insert(Key::Right, vec![Action::Resize(Direction::Right)]);
+
+ defaults.insert(Key::Alt('n'), vec![Action::NewPane(None)]);
+ defaults.insert(Key::Alt('h'), vec![Action::MoveFocus(Direction::Left)]);
+ defaults.insert(Key::Alt('j'), vec![Action::MoveFocus(Direction::Down)]);
+ defaults.insert(Key::Alt('k'), vec![Action::MoveFocus(Direction::Up)]);
+ defaults.insert(Key::Alt('l'), vec![Action::MoveFocus(Direction::Right)]);
+ defaults.insert(Key::Alt('['), vec![Action::FocusPreviousPane]);
+ defaults.insert(Key::Alt(']'), vec![Action::FocusNextPane]);
}
InputMode::Pane => {
defaults.insert(
@@ -173,7 +189,7 @@ impl Keybinds {
defaults.insert(Key::Up, vec![Action::MoveFocus(Direction::Up)]);
defaults.insert(Key::Right, vec![Action::MoveFocus(Direction::Right)]);
- defaults.insert(Key::Char('p'), vec![Action::SwitchFocus(Direction::Right)]);
+ defaults.insert(Key::Char('p'), vec![Action::SwitchFocus]);
defaults.insert(Key::Char('n'), vec![Action::NewPane(None)]);
defaults.insert(Key::Char('d'), vec![Action::NewPane(Some(Direction::Down))]);
defaults.insert(
@@ -182,6 +198,14 @@ impl Keybinds {
);
defaults.insert(Key::Char('x'), vec![Action::CloseFocus]);
defaults.insert(Key::Char('f'), vec![Action::ToggleFocusFullscreen]);
+
+ defaults.insert(Key::Alt('n'), vec![Action::NewPane(None)]);
+ defaults.insert(Key::Alt('h'), vec![Action::MoveFocus(Direction::Left)]);
+ defaults.insert(Key::Alt('j'), vec![Action::MoveFocus(Direction::Down)]);
+ defaults.insert(Key::Alt('k'), vec![Action::MoveFocus(Direction::Up)]);
+ defaults.insert(Key::Alt('l'), vec![Action::MoveFocus(Direction::Right)]);
+ defaults.insert(Key::Alt('['), vec![Action::FocusPreviousPane]);
+ defaults.insert(Key::Alt(']'), vec![Action::FocusNextPane]);
}
InputMode::Tab => {
defaults.insert(
@@ -240,6 +264,13 @@ impl Keybinds {
for i in '1'..='9' {
defaults.insert(Key::Char(i), vec![Action::GoToTab(i.to_digit(10).unwrap())]);
}
+ defaults.insert(Key::Alt('n'), vec![Action::NewPane(None)]);
+ defaults.insert(Key::Alt('h'), vec![Action::MoveFocus(Direction::Left)]);
+ defaults.insert(Key::Alt('j'), vec![Action::MoveFocus(Direction::Down)]);
+ defaults.insert(Key::Alt('k'), vec![Action::MoveFocus(Direction::Up)]);
+ defaults.insert(Key::Alt('l'), vec![Action::MoveFocus(Direction::Right)]);
+ defaults.insert(Key::Alt('['), vec![Action::FocusPreviousPane]);
+ defaults.insert(Key::Alt(']'), vec![Action::FocusNextPane]);
}
InputMode::Scroll => {
defaults.insert(
@@ -272,6 +303,14 @@ impl Keybinds {
defaults.insert(Key::Down, vec![Action::ScrollDown]);
defaults.insert(Key::Up, vec![Action::ScrollUp]);
+
+ defaults.insert(Key::Alt('n'), vec![Action::NewPane(None)]);
+ defaults.insert(Key::Alt('h'), vec![Action::MoveFocus(Direction::Left)]);
+ defaults.insert(Key::Alt('j'), vec![Action::MoveFocus(Direction::Down)]);
+ defaults.insert(Key::Alt('k'), vec![Action::MoveFocus(Direction::Up)]);
+ defaults.insert(Key::Alt('l'), vec![Action::MoveFocus(Direction::Right)]);
+ defaults.insert(Key::Alt('['), vec![Action::FocusPreviousPane]);
+ defaults.insert(Key::Alt(']'), vec![Action::FocusNextPane]);
}
InputMode::RenameTab => {
defaults.insert(Key::Char('\n'), vec![Action::SwitchToMode(InputMode::Tab)]);
@@ -286,6 +325,14 @@ impl Keybinds {
Action::SwitchToMode(InputMode::Tab),
],
);
+
+ defaults.insert(Key::Alt('n'), vec![Action::NewPane(None)]);
+ defaults.insert(Key::Alt('h'), vec![Action::MoveFocus(Direction::Left)]);
+ defaults.insert(Key::Alt('j'), vec![Action::MoveFocus(Direction::Down)]);
+ defaults.insert(Key::Alt('k'), vec![Action::MoveFocus(Direction::Up)]);
+ defaults.insert(Key::Alt('l'), vec![Action::MoveFocus(Direction::Right)]);
+ defaults.insert(Key::Alt('['), vec![Action::FocusPreviousPane]);
+ defaults.insert(Key::Alt(']'), vec![Action::FocusNextPane]);
}
}
ModeKeybinds(defaults)
diff --git a/src/common/mod.rs b/src/common/mod.rs
index 8180a724..7651905c 100644
--- a/src/common/mod.rs
+++ b/src/common/mod.rs
@@ -326,9 +326,15 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
ScreenInstruction::ResizeUp => {
screen.get_active_tab_mut().unwrap().resize_up();
}
- ScreenInstruction::MoveFocus => {
+ ScreenInstruction::SwitchFocus => {
screen.get_active_tab_mut().unwrap().move_focus();
}
+ ScreenInstruction::FocusNextPane => {
+ screen.get_active_tab_mut().unwrap().focus_next_pane();
+ }
+ ScreenInstruction::FocusPreviousPane => {
+ screen.get_active_tab_mut().unwrap().focus_previous_pane();
+ }
ScreenInstruction::MoveFocusLeft => {
screen.get_active_tab_mut().unwrap().move_focus_left();
}
@@ -589,7 +595,7 @@ pub fn start(mut os_input: Box<dyn OsApi>, opts: CliArgs) {
}
ApiCommand::MoveFocus => {
send_screen_instructions
- .send(ScreenInstruction::MoveFocus)
+ .send(ScreenInstruction::FocusNextPane)
.unwrap();
}
}
diff --git a/src/common/screen.rs b/src/common/screen.rs
index 121f21bd..41e934cb 100644
--- a/src/common/screen.rs
+++ b/src/common/screen.rs
@@ -28,7 +28,9 @@ pub enum ScreenInstruction {
ResizeRight,
ResizeDown,
ResizeUp,
- MoveFocus,
+ SwitchFocus,
+ FocusNextPane,
+ FocusPreviousPane,
MoveFocusLeft,
MoveFocusDown,
MoveFocusUp,