diff --git a/.github/screenshot_01.png b/.github/screenshot_01.png index 567dd3a..f39df48 100644 Binary files a/.github/screenshot_01.png and b/.github/screenshot_01.png differ diff --git a/README.md b/README.md index fb9ef37..1c85e30 100644 --- a/README.md +++ b/README.md @@ -105,8 +105,9 @@ In application controls, these, amongst many other settings, can be customized w | button| result| |--|--| | ```( tab )``` or ```( shift+tab )``` | Change panel, clicking on a panel also changes the selected panel.| -| ```( ↑ ↓ )``` or ```( j k )``` or ```( PgUp PgDown )``` or ```( Home End )```| Change selected line in selected panel, mouse scroll also changes selected line.| +| ```( ↑ ↓ )``` or ```( j k )``` or ```( PgUp PgDown )``` or ```( Home End )```| Scroll line in selected panel - mouse wheel will also scroll.| | ```( ← → )``` | When logs panel selected, scroll horizontally across the text of the logs.| +| ```( ctrl )``` | Increase scroll speed, used in conjuction scroll keys.| | ```( enter )```| Run selected docker command.| | ```( 1-9 )``` | Sort containers by heading, clicking on headings also sorts the selected column. | | ```( 0 )``` | Stop sorting.| diff --git a/example_config/example.config.jsonc b/example_config/example.config.jsonc index bf0436a..8abb678 100644 --- a/example_config/example.config.jsonc +++ b/example_config/example.config.jsonc @@ -42,6 +42,7 @@ // 3) F1-F12 // 4) backspace, tab, backtab, delete, end, esc, home, insert, pagedown, pageup, left, right, up, down // Each definition can have two keys associated with it + // WARNING "scroll_many" only accepts control, alt, shift, with no secondary option // If any key clashes are found, oxker will revert to it's default keymap "keymap": { // Clear any popup boxes, filter panel, or help panel @@ -74,10 +75,12 @@ "save_logs": [ "s" ], + // TODO "scroll_down_many" will be removed in the next release // Scroll down a list by many "scroll_down_many": [ "pagedown" ], + // TODO rename in next release // Scroll down a list by one item "scroll_down_one": [ "down", @@ -87,14 +90,18 @@ "scroll_end": [ "end" ], + // Modifier to scroll by 10 lines isntead of one, used in conjunction with scroll_up_x/scroll_down_x + "scroll_many": ["control"], // Scroll up to the start of a list "scroll_start": [ "home" ], + // TODO "scroll_up_many" will be removed in the next release // Scroll up a list by many "scroll_up_many": [ "pageup" ], + // TODO rename in next release // Scroll up a list by one item "scroll_up_one": [ "up", diff --git a/example_config/example.config.toml b/example_config/example.config.toml index a7132ac..182e7c5 100644 --- a/example_config/example.config.toml +++ b/example_config/example.config.toml @@ -55,8 +55,10 @@ show_logs = true # 3) F1-F12 # 4) backspace, tab, backtab, delete, end, esc, home, insert, pagedown, pageup, left, right, up, down - # Each definition can have two keys associated with it + +# WARNING "scroll_many" only accepts control, alt, shift, with no secondary option + # If any key clashes are found, oxker will revert to it's default keymap [keymap] @@ -74,16 +76,23 @@ filter_mode = ["/", "F1"] quit = ["q"] # Save logs of selected container to file on disk save_logs = ["s"] +# TODO "scroll_down_many" will be removed in the next release # scroll down a list by many scroll_down_many = ["pagedown"] +# TODO rename in next release # scroll down a list by one item scroll_down_one = ["down", "j"] + # scroll down to the end of a list scroll_end = ["end"] +# Modifier to scroll by 10 lines isntead of one, used in conjunction with scroll_up_x/scroll_down_x +scroll_many = ["control"] # scroll up to the start of a list scroll_start = ["home"] +# TODO "scroll_up_many" will be removed in the next release # scroll up a list by many scroll_up_many = ["pageup"] +# TODO rename in next release # scroll up a list by one item scroll_up_one = ["up", "k"] # Horizontal scroll of the logs @@ -111,7 +120,6 @@ toggle_help = ["h"] toggle_mouse_capture = ["m"] # Reduce the height of the logs list section log_section_height_decrease = ["-"] -# Increase the height of the logs list section log_section_height_increase = ["+"] # Toggle visibility of the log section log_section_toggle = ["\\"] @@ -191,6 +199,7 @@ selected_filter_text = "black" # Highlighted text color highlight = "magenta" + # The color the of Docker commands available for each container [colors.commands] # Background color of panel diff --git a/src/app_data/container_state.rs b/src/app_data/container_state.rs index d5a4ff6..d31550e 100644 --- a/src/app_data/container_state.rs +++ b/src/app_data/container_state.rs @@ -630,7 +630,6 @@ impl Logs { } } - // TODO test me! /// If scrolling horiztonally along the logs, display a counter of the position in the in the scroll, `x/y` pub fn get_scroll_title(&mut self, width: u16) -> Option { if self.horizontal_scroll_able(width) { @@ -938,7 +937,7 @@ mod tests { text::{Line, Text}, }; - use crate::{ + use crate::{ app_data::{ContainerImage, Logs, LogsTz, RunningState}, ui::log_sanitizer, }; diff --git a/src/config/config.toml b/src/config/config.toml index 25ee594..182e7c5 100644 --- a/src/config/config.toml +++ b/src/config/config.toml @@ -56,6 +56,9 @@ show_logs = true # 4) backspace, tab, backtab, delete, end, esc, home, insert, pagedown, pageup, left, right, up, down # Each definition can have two keys associated with it + +# WARNING "scroll_many" only accepts control, alt, shift, with no secondary option + # If any key clashes are found, oxker will revert to it's default keymap [keymap] @@ -73,17 +76,23 @@ filter_mode = ["/", "F1"] quit = ["q"] # Save logs of selected container to file on disk save_logs = ["s"] +# TODO "scroll_down_many" will be removed in the next release # scroll down a list by many scroll_down_many = ["pagedown"] +# TODO rename in next release # scroll down a list by one item scroll_down_one = ["down", "j"] # scroll down to the end of a list scroll_end = ["end"] +# Modifier to scroll by 10 lines isntead of one, used in conjunction with scroll_up_x/scroll_down_x +scroll_many = ["control"] # scroll up to the start of a list scroll_start = ["home"] +# TODO "scroll_up_many" will be removed in the next release # scroll up a list by many scroll_up_many = ["pageup"] +# TODO rename in next release # scroll up a list by one item scroll_up_one = ["up", "k"] # Horizontal scroll of the logs diff --git a/src/config/keymap_parser.rs b/src/config/keymap_parser.rs index 808cc18..3f62e44 100644 --- a/src/config/keymap_parser.rs +++ b/src/config/keymap_parser.rs @@ -1,6 +1,6 @@ use std::collections::HashSet; -use crossterm::event::KeyCode; +use crossterm::event::{KeyCode, KeyModifiers}; /// The macro accepts a list of struct names with key names /// Returns a struct where every key name is an Option, with the correct derived attributes @@ -12,6 +12,7 @@ macro_rules! optional_config_struct { $( $key_name: Option>, )* + pub scroll_many: Option>, } )* }; @@ -24,9 +25,10 @@ macro_rules! config_struct { $( #[derive(Debug, Clone, PartialEq, Eq)] pub struct $struct_name { - $( + $( pub $key_name: (KeyCode, Option), )* + pub scroll_many: KeyModifiers, } )* }; @@ -47,11 +49,15 @@ optional_config_struct!( log_scroll_back, quit, save_logs, + // TODO remove in next release scroll_down_many, + // TODO rename in next release scroll_down_one, scroll_end, scroll_start, + // TODO remove in next release scroll_up_many, + // TODO rename in next release scroll_up_one, select_next_panel, select_previous_panel, @@ -84,11 +90,15 @@ config_struct!( log_scroll_back, quit, save_logs, + // TODO remove in next release scroll_down_many, + // TODO rename in next release scroll_down_one, scroll_end, scroll_start, + // TODO remove in next release scroll_up_many, + // TODO rename in next release scroll_up_one, select_next_panel, select_previous_panel, @@ -122,11 +132,16 @@ impl Keymap { log_scroll_forward: (KeyCode::Right, None), quit: (KeyCode::Char('q'), None), save_logs: (KeyCode::Char('s'), None), + // TODO remove in next release scroll_down_many: (KeyCode::PageDown, None), + // TODO rename in next release scroll_down_one: (KeyCode::Down, Some(KeyCode::Char('j'))), scroll_end: (KeyCode::End, None), scroll_start: (KeyCode::Home, None), + scroll_many: KeyModifiers::CONTROL, + // TODO remove in next release scroll_up_many: (KeyCode::PageUp, None), + // TODO rename in next release scroll_up_one: (KeyCode::Up, Some(KeyCode::Char('k'))), select_next_panel: (KeyCode::Tab, None), select_previous_panel: (KeyCode::BackTab, None), @@ -243,6 +258,10 @@ impl From> for Keymap { &mut keymap.toggle_mouse_capture, &mut clash, ); + // TODO need to check for clashes when using additional modifiers + if let Some(scroll_many) = Self::try_parse_modifier(ck.scroll_many) { + keymap.scroll_many = scroll_many; + } } // A very basic clash check, every key has been inserted into a hashset, and a counter has been increased // if the counter and hashet length don't match, then there's a clash, and we just return the default keymap @@ -255,6 +274,20 @@ impl From> for Keymap { } impl Keymap { + // Allowable key modifiers are only `shift`, `control`, `alt` + fn try_parse_modifier(input: Option>) -> Option { + input.and_then(|input| { + input + .first() + .and_then(|input| match input.to_lowercase().trim() { + "control" => Some(KeyModifiers::CONTROL), + "alt" => Some(KeyModifiers::ALT), + "shift" => Some(KeyModifiers::SHIFT), + _ => None, + }) + }) + } + /// Try to parse a &[String] into a Vec of keycodes, at most the output will have 2 entries /// This might fail on MacOS due to Backspace and Delete working in a different manner as to how they work on Linux & Windows /// I think that on MacOS `Del` becomes `Fwd Del`, and `Backspace` becomes `Delete` @@ -326,7 +359,7 @@ impl Keymap { #[cfg(test)] #[allow(clippy::unwrap_used)] mod tests { - use crossterm::event::KeyCode; + use crossterm::event::{KeyCode, KeyModifiers}; use crate::config::keymap_parser::ConfigKeymap; @@ -393,6 +426,7 @@ mod tests { scroll_down_one: None, scroll_end: None, scroll_start: None, + scroll_many: None, scroll_up_many: None, scroll_up_one: None, select_next_panel: None, @@ -423,78 +457,79 @@ mod tests { let input = ConfigKeymap { clear: gen_v(("a", "b")), - delete_confirm: gen_v(("e", "f")), - delete_deny: gen_v(("c", "d")), + delete_confirm: gen_v(("c", "d")), + delete_deny: gen_v(("e", "fd")), exec: gen_v(("g", "h")), filter_mode: gen_v(("i", "j")), - force_redraw: gen_v(("F1", "F2")), - log_section_height_decrease: gen_v(("-", "Z")), - log_section_height_increase: gen_v(("=", "X")), - log_scroll_forward: gen_v(("right", "R")), - log_scroll_back: gen_v(("left", "L")), - log_section_toggle: gen_v(("Y", "W")), - quit: gen_v(("k", "l")), - save_logs: gen_v(("m", "n")), - scroll_down_many: gen_v(("o", "p")), - scroll_down_one: gen_v(("q", "r")), - scroll_end: gen_v(("s", "t")), - scroll_start: gen_v(("u", "v")), - scroll_up_many: gen_v(("w", "x")), - scroll_up_one: gen_v(("y", "z")), - select_next_panel: gen_v(("0", "1")), - select_previous_panel: gen_v(("2", "3")), - sort_by_cpu: gen_v(("F11", "F12")), - sort_by_id: gen_v(("[", "]")), - sort_by_image: gen_v(("A", "B")), - sort_by_memory: gen_v(("/", "\\")), - sort_by_name: gen_v(("4", "5")), - sort_by_rx: gen_v(("C", "D")), - sort_by_state: gen_v(("6", "7")), - sort_by_status: gen_v(("8", "9")), - sort_by_tx: gen_v(("insert", "TAB")), - sort_reset: gen_v(("up", "down")), - toggle_help: gen_v(("home", "end")), - toggle_mouse_capture: gen_v(("pagedown", "PAGEUP")), + force_redraw: gen_v(("k", "l")), + log_section_height_decrease: gen_v(("m", "n")), + log_section_height_increase: gen_v(("o", "p")), + log_scroll_forward: gen_v(("q", "r")), + log_scroll_back: gen_v(("s", "t")), + log_section_toggle: gen_v(("u", "v")), + quit: gen_v(("w", "x")), + save_logs: gen_v(("y", "z")), + scroll_down_many: gen_v(("1", "2")), + scroll_down_one: gen_v(("3", "4")), + scroll_end: gen_v(("5", "6")), + scroll_many: Some(vec!["alt".to_owned()]), + scroll_start: gen_v(("7", "8")), + scroll_up_many: gen_v(("9", "0")), + scroll_up_one: gen_v(("F1", "F2")), + select_next_panel: gen_v(("F3", "F4")), + select_previous_panel: gen_v(("F5", "F6")), + sort_by_cpu: gen_v(("F7", "F8")), + sort_by_id: gen_v(("F9", "F10")), + sort_by_image: gen_v(("F11", "F12")), + sort_by_memory: gen_v(("HOME", "END")), + sort_by_name: gen_v(("UP", "DOWN")), + sort_by_rx: gen_v(("LEFT", "RIGHT")), + sort_by_state: gen_v(("[", "]")), + sort_by_status: gen_v(("INSERTt", "TAB")), + sort_by_tx: gen_v(("PAGEDOWN", "PAGEUP")), + sort_reset: gen_v((",", ".")), + toggle_help: gen_v(("-", "=")), + toggle_mouse_capture: gen_v(("\\", "/")), }; let result = Keymap::from(Some(input)); let expected = Keymap { clear: (KeyCode::Char('a'), Some(KeyCode::Char('b'))), - delete_deny: (KeyCode::Char('c'), Some(KeyCode::Char('d'))), - delete_confirm: (KeyCode::Char('e'), Some(KeyCode::Char('f'))), - force_redraw: (KeyCode::F(1), Some(KeyCode::F(2))), - log_section_height_decrease: (KeyCode::Char('-'), Some(KeyCode::Char('Z'))), - log_section_height_increase: (KeyCode::Char('='), Some(KeyCode::Char('X'))), - log_section_toggle: (KeyCode::Char('Y'), Some(KeyCode::Char('W'))), - log_scroll_forward: (KeyCode::Right, Some(KeyCode::Char('R'))), - log_scroll_back: (KeyCode::Left, Some(KeyCode::Char('L'))), + delete_deny: (KeyCode::Char('e'), None), + delete_confirm: (KeyCode::Char('c'), Some(KeyCode::Char('d'))), exec: (KeyCode::Char('g'), Some(KeyCode::Char('h'))), filter_mode: (KeyCode::Char('i'), Some(KeyCode::Char('j'))), - quit: (KeyCode::Char('k'), Some(KeyCode::Char('l'))), - save_logs: (KeyCode::Char('m'), Some(KeyCode::Char('n'))), - scroll_down_many: (KeyCode::Char('o'), Some(KeyCode::Char('p'))), - scroll_down_one: (KeyCode::Char('q'), Some(KeyCode::Char('r'))), - scroll_end: (KeyCode::Char('s'), Some(KeyCode::Char('t'))), - scroll_start: (KeyCode::Char('u'), Some(KeyCode::Char('v'))), - scroll_up_many: (KeyCode::Char('w'), Some(KeyCode::Char('x'))), - scroll_up_one: (KeyCode::Char('y'), Some(KeyCode::Char('z'))), - select_next_panel: (KeyCode::Char('0'), Some(KeyCode::Char('1'))), - select_previous_panel: (KeyCode::Char('2'), Some(KeyCode::Char('3'))), - sort_by_name: (KeyCode::Char('4'), Some(KeyCode::Char('5'))), - sort_by_state: (KeyCode::Char('6'), Some(KeyCode::Char('7'))), - sort_by_status: (KeyCode::Char('8'), Some(KeyCode::Char('9'))), - sort_by_cpu: (KeyCode::F(11), Some(KeyCode::F(12))), - sort_by_memory: (KeyCode::Char('/'), Some(KeyCode::Char('\\'))), - sort_by_id: (KeyCode::Char('['), Some(KeyCode::Char(']'))), - sort_by_image: (KeyCode::Char('A'), Some(KeyCode::Char('B'))), - sort_by_rx: (KeyCode::Char('C'), Some(KeyCode::Char('D'))), - sort_by_tx: (KeyCode::Insert, Some(KeyCode::Tab)), - sort_reset: (KeyCode::Up, Some(KeyCode::Down)), - toggle_help: (KeyCode::Home, Some(KeyCode::End)), - toggle_mouse_capture: (KeyCode::PageDown, Some(KeyCode::PageUp)), + force_redraw: (KeyCode::Char('k'), Some(KeyCode::Char('l'))), + log_section_height_increase: (KeyCode::Char('o'), Some(KeyCode::Char('p'))), + log_section_height_decrease: (KeyCode::Char('m'), Some(KeyCode::Char('n'))), + log_section_toggle: (KeyCode::Char('u'), Some(KeyCode::Char('v'))), + log_scroll_forward: (KeyCode::Char('q'), Some(KeyCode::Char('r'))), + log_scroll_back: (KeyCode::Char('s'), Some(KeyCode::Char('t'))), + quit: (KeyCode::Char('w'), Some(KeyCode::Char('x'))), + save_logs: (KeyCode::Char('y'), Some(KeyCode::Char('z'))), + scroll_down_many: (KeyCode::Char('1'), Some(KeyCode::Char('2'))), + scroll_down_one: (KeyCode::Char('3'), Some(KeyCode::Char('4'))), + scroll_end: (KeyCode::Char('5'), Some(KeyCode::Char('6'))), + scroll_start: (KeyCode::Char('7'), Some(KeyCode::Char('8'))), + scroll_up_many: (KeyCode::Char('9'), Some(KeyCode::Char('0'))), + scroll_up_one: (KeyCode::F(1), Some(KeyCode::F(2))), + select_next_panel: (KeyCode::F(3), Some(KeyCode::F(4))), + select_previous_panel: (KeyCode::F(5), Some(KeyCode::F(6))), + sort_by_name: (KeyCode::Up, Some(KeyCode::Down)), + sort_by_state: (KeyCode::Char('['), Some(KeyCode::Char(']'))), + sort_by_status: (KeyCode::Tab, None), + sort_by_cpu: (KeyCode::F(7), Some(KeyCode::F(8))), + sort_by_memory: (KeyCode::Home, Some(KeyCode::End)), + sort_by_id: (KeyCode::F(9), Some(KeyCode::F(10))), + sort_by_image: (KeyCode::F(11), Some(KeyCode::F(12))), + sort_by_rx: (KeyCode::Left, Some(KeyCode::Right)), + sort_by_tx: (KeyCode::PageDown, Some(KeyCode::PageUp)), + sort_reset: (KeyCode::Char(','), Some(KeyCode::Char('.'))), + toggle_help: (KeyCode::Char('-'), Some(KeyCode::Char('='))), + toggle_mouse_capture: (KeyCode::Char('\\'), Some(KeyCode::Char('/'))), + scroll_many: KeyModifiers::ALT, }; - assert_eq!(expected, result); } } diff --git a/src/input_handler/message.rs b/src/input_handler/message.rs index ba50101..1d5ab4a 100644 --- a/src/input_handler/message.rs +++ b/src/input_handler/message.rs @@ -3,5 +3,5 @@ use crossterm::event::{KeyCode, KeyModifiers, MouseEvent}; #[derive(Debug, Clone, Copy)] pub enum InputMessages { ButtonPress((KeyCode, KeyModifiers)), - MouseEvent(MouseEvent), + MouseEvent((MouseEvent, KeyModifiers)), } diff --git a/src/input_handler/mod.rs b/src/input_handler/mod.rs index faa2d03..fcf098c 100644 --- a/src/input_handler/mod.rs +++ b/src/input_handler/mod.rs @@ -67,7 +67,7 @@ impl InputHandler { while let Some(message) = self.rx.recv().await { match message { InputMessages::ButtonPress(key) => self.button_press(key.0, key.1).await, - InputMessages::MouseEvent(mouse_event) => { + InputMessages::MouseEvent((mouse_event, modifider)) => { let status = self.gui_state.lock().get_status(); let contains = |s: Status| status.contains(&s); @@ -78,7 +78,7 @@ impl InputHandler { | !contains(Status::DeleteConfirm) | !contains(Status::Filter) { - self.mouse_press(mouse_event); + self.mouse_press(mouse_event, modifider); } } } @@ -285,20 +285,33 @@ impl InputHandler { } } + /// If keymap.scroll_modifier is pressed, return 10, else return 1, to speed up scrolling + fn get_modifier_total(&self, modifier: KeyModifiers) -> u8 { + if modifier == self.keymap.scroll_many { + 10 + } else { + 1 + } + } + /// Advance the "cursor" along the logs - fn logs_forward(&self) { + fn logs_forward(&self, modifier: KeyModifiers) { let panel = self.gui_state.lock().get_selected_panel(); if panel == SelectablePanel::Logs { - let width = self.gui_state.lock().get_screen_width(); - self.app_data.lock().log_forward(width); + for _ in 0..self.get_modifier_total(modifier) { + let width = self.gui_state.lock().get_screen_width(); + self.app_data.lock().log_forward(width); + } } } /// Retreat the "cursor" along the logs - fn logs_back(&self) { + fn logs_back(&self, modifier: KeyModifiers) { let panel = self.gui_state.lock().get_selected_panel(); if panel == SelectablePanel::Logs { - self.app_data.lock().log_back(); + for _ in 0..self.get_modifier_total(modifier) { + self.app_data.lock().log_back(); + } } } @@ -489,7 +502,7 @@ impl InputHandler { /// Handle button presses in all other scenarios #[allow(clippy::cognitive_complexity)] - async fn handle_others(&mut self, key_code: KeyCode) { + async fn handle_others(&mut self, key_code: KeyCode, modifier: KeyModifiers) { self.handle_sort(key_code); // shift key plus arrows match key_code { @@ -559,28 +572,28 @@ impl InputHandler { _ if self.keymap.scroll_up_one.0 == key_code || self.keymap.scroll_up_one.1 == Some(key_code) => { - self.scroll_up(); + self.scroll_up(modifier); } _ if self.keymap.scroll_up_many.0 == key_code || self.keymap.scroll_up_many.1 == Some(key_code) => { for _ in 0..=6 { - self.scroll_up(); + self.scroll_up(modifier); } } _ if self.keymap.scroll_down_one.0 == key_code || self.keymap.scroll_down_one.1 == Some(key_code) => { - self.scroll_down(); + self.scroll_down(modifier); } _ if self.keymap.scroll_down_many.0 == key_code || self.keymap.scroll_down_many.1 == Some(key_code) => { for _ in 0..=6 { - self.scroll_down(); + self.scroll_down(modifier); } } @@ -594,13 +607,13 @@ impl InputHandler { _ if self.keymap.log_scroll_back.0 == key_code || self.keymap.log_scroll_back.1 == Some(key_code) => { - self.logs_back(); + self.logs_back(modifier); } _ if self.keymap.log_scroll_forward.0 == key_code || self.keymap.log_scroll_forward.1 == Some(key_code) => { - self.logs_forward(); + self.logs_forward(modifier); } KeyCode::Enter => self.enter_key().await, @@ -637,7 +650,7 @@ impl InputHandler { } else if contains_delete { self.handle_delete(key_code).await; } else { - self.handle_others(key_code).await; + self.handle_others(key_code, key_modifier).await; } } } @@ -662,7 +675,7 @@ impl InputHandler { } /// Handle mouse button events - fn mouse_press(&self, mouse_event: MouseEvent) { + fn mouse_press(&self, mouse_event: MouseEvent, modifier: KeyModifiers) { let status = self.gui_state.lock().get_status(); if status.contains(&Status::Help) { let mouse_point = Rect::new(mouse_event.column, mouse_event.row, 1, 1); @@ -672,8 +685,8 @@ impl InputHandler { } } else { match mouse_event.kind { - MouseEventKind::ScrollUp => self.scroll_up(), - MouseEventKind::ScrollDown => self.scroll_down(), + MouseEventKind::ScrollUp => self.scroll_up(modifier), + MouseEventKind::ScrollDown => self.scroll_down(modifier), MouseEventKind::Down(MouseButton::Left) => { let mouse_point = Rect::new(mouse_event.column, mouse_event.row, 1, 1); let header = self.gui_state.lock().get_intersect_header(mouse_point); @@ -693,21 +706,37 @@ impl InputHandler { } /// Change state to next, depending which panel is currently in focus - fn scroll_down(&self) { + fn scroll_down(&self, modifier: KeyModifiers) { let selected_panel = self.gui_state.lock().get_selected_panel(); match selected_panel { - SelectablePanel::Containers => self.app_data.lock().containers_next(), - SelectablePanel::Logs => self.app_data.lock().log_next(), + SelectablePanel::Containers => { + for _ in 0..self.get_modifier_total(modifier) { + self.app_data.lock().containers_next(); + } + } + SelectablePanel::Logs => { + for _ in 0..self.get_modifier_total(modifier) { + self.app_data.lock().log_next(); + } + } SelectablePanel::Commands => self.app_data.lock().docker_controls_next(), } } /// Change state to previous, depending which panel is currently in focus - fn scroll_up(&self) { + fn scroll_up(&self, modifier: KeyModifiers) { let selected_panel = self.gui_state.lock().get_selected_panel(); match selected_panel { - SelectablePanel::Containers => self.app_data.lock().containers_previous(), - SelectablePanel::Logs => self.app_data.lock().log_previous(), + SelectablePanel::Containers => { + for _ in 0..self.get_modifier_total(modifier) { + self.app_data.lock().containers_previous(); + } + } + SelectablePanel::Logs => { + for _ in 0..self.get_modifier_total(modifier) { + self.app_data.lock().log_previous(); + } + } SelectablePanel::Commands => self.app_data.lock().docker_controls_previous(), } } diff --git a/src/ui/draw_blocks/help.rs b/src/ui/draw_blocks/help.rs index 0694ddd..83a3051 100644 --- a/src/ui/draw_blocks/help.rs +++ b/src/ui/draw_blocks/help.rs @@ -109,13 +109,18 @@ impl HelpInfo { button_item("PgUp PgDown"), or(), button_item("Home End"), - button_desc("change selected line"), + button_desc("scroll vertically"), ]), Line::from(vec![ space(), button_item("← →"), button_desc("horizontal scroll across logs"), ]), + Line::from(vec![ + space(), + button_item("ctrl"), + button_desc("increase scroll speed, used in conjuction scroll keys"), + ]), Line::from(vec![ space(), button_item("enter"), @@ -280,6 +285,11 @@ impl HelpInfo { or_secondary(km.scroll_start, "scroll list to start"), or_secondary(km.log_scroll_forward, "horizontal scroll logs right"), or_secondary(km.log_scroll_back, "horizontal scroll logs left"), + Line::from(vec![ + space(), + button_item(km.scroll_many.to_string().as_str()), + button_desc("increase scroll speed, used in conjuction scroll keys"), + ]), Line::from(vec![ space(), button_item("enter"), @@ -440,7 +450,7 @@ pub fn draw( #[allow(clippy::unwrap_used, clippy::too_many_lines)] mod tests { use crate::config::{AppColors, Keymap}; - use crossterm::event::KeyCode; + use crossterm::event::{KeyCode, KeyModifiers}; use insta::assert_snapshot; use jiff::tz::TimeZone; use ratatui::style::{Color, Modifier}; @@ -449,9 +459,10 @@ mod tests { #[test] /// This will cause issues once the version has more than the current 5 chars (0.5.0) + /// This test is incredibly annoying /// println!("{} {} {} {} {}", row_index, result_cell_index, result_cell.symbol(), result_cell.bg, result_cell.fg); fn test_draw_blocks_help() { - let mut setup = test_setup(87, 36, true, true); + let mut setup = test_setup(87, 37, true, true); let tz = setup.app_data.lock().config.timezone.clone(); setup @@ -471,9 +482,17 @@ mod tests { for (row_index, result_row) in get_result(&setup) { for (result_cell_index, result_cell) in result_row.iter().enumerate() { + println!( + "{} {} {} {} {}", + row_index, + result_cell_index, + result_cell.symbol(), + result_cell.bg, + result_cell.fg + ); match (row_index, result_cell_index) { // first & last row, and first & last char on each row, is reset/reset, making sure that the help info is centered in the given area - (0 | 35, _) | (0..=34, 0 | 86) => { + (0 | 36, _) | (0..=35, 0 | 86) => { assert_eq!(result_cell.bg, Color::Reset); assert_eq!(result_cell.fg, Color::Reset); } @@ -487,15 +506,16 @@ mod tests { | (12, 19..=66) | (14, 2..=10 | 13..=27) | (15, 2..=10 | 13..=21 | 24..=40 | 43..=56) - | (16 | 26 | 28, 2..=10) - | (17 | 25, 2..=12) - | (18 | 19 | 20 | 21 | 22 | 24 | 27 | 29, 2..=8) - | (23, 2..=9 | 12..=18) => { + | (16 | 27 | 29, 2..=10) + | (17, 2..=11) + | (18 | 26, 2..=12) + | (19 | 20 | 21 | 22 | 24 | 25 | 28 | 23 | 30, 2..=8) + | (24, 2..=9 | 12..=18) => { assert_eq!(result_cell.bg, Color::Magenta); assert_eq!(result_cell.fg, Color::White); } // The URL is yellow and underlined - (32, 25..=60) => { + (33, 25..=60) => { assert_eq!(result_cell.bg, Color::Magenta); assert_eq!(result_cell.fg, Color::White); assert_eq!(result_cell.modifier, Modifier::UNDERLINED); @@ -512,9 +532,10 @@ mod tests { #[test] /// Test that the help panel gets drawn with custom colors + /// This test is incredibly annoying /// println!("{} {} {} {} {}", row_index, result_cell_index, result_cell.symbol(), result_cell.bg, result_cell.fg); fn test_draw_blocks_help_custom_colors() { - let mut setup = test_setup(87, 36, true, true); + let mut setup = test_setup(87, 37, true, true); let mut colors = AppColors::new(); let tz = setup.app_data.lock().config.timezone.clone(); @@ -540,7 +561,7 @@ mod tests { for (result_cell_index, result_cell) in result_row.iter().enumerate() { match (row_index, result_cell_index) { // first & last row, and first & last char on each row, is reset/reset, making sure that the help info is centered in the given area - (0 | 35, _) | (0..=34, 0 | 86) => { + (0 | 36, _) | (0..=35, 0 | 86) => { assert_eq!(result_cell.bg, Color::Reset); assert_eq!(result_cell.fg, Color::Reset); } @@ -554,15 +575,16 @@ mod tests { | (12, 19..=66) | (14, 2..=10 | 13..=27) | (15, 2..=10 | 13..=21 | 24..=40 | 43..=56) - | (16 | 26 | 28, 2..=10) - | (17 | 25, 2..=12) - | (18 | 19 | 20 | 21 | 22 | 24 | 27 | 29, 2..=8) - | (23, 2..=9 | 12..=18) => { + | (16 | 27 | 29, 2..=10) + | (17, 2..=11) + | (18 | 26, 2..=12) + | (19 | 20 | 21 | 22 | 24 | 25 | 28 | 23 | 30, 2..=8) + | (24, 2..=9 | 12..=18) => { assert_eq!(result_cell.bg, Color::Black); assert_eq!(result_cell.fg, Color::Yellow); } // The URL is yellow and underlined - (32, 25..=60) => { + (33, 25..=60) => { assert_eq!(result_cell.bg, Color::Black); assert_eq!(result_cell.fg, Color::Yellow); assert_eq!(result_cell.modifier, Modifier::UNDERLINED); @@ -599,6 +621,7 @@ mod tests { scroll_down_many: (KeyCode::Char('n'), None), scroll_down_one: (KeyCode::Char('o'), None), scroll_end: (KeyCode::Char('p'), None), + scroll_many: KeyModifiers::ALT, scroll_start: (KeyCode::Char('q'), None), scroll_up_many: (KeyCode::Char('r'), None), scroll_up_one: (KeyCode::Char('s'), None), @@ -650,6 +673,7 @@ mod tests { scroll_down_many: (KeyCode::Char('m'), Some(KeyCode::Char('M'))), scroll_down_one: (KeyCode::Char('n'), Some(KeyCode::Char('N'))), scroll_end: (KeyCode::Char('o'), Some(KeyCode::Char('O'))), + scroll_many: KeyModifiers::ALT, scroll_start: (KeyCode::Char('p'), Some(KeyCode::Char('P'))), scroll_up_many: (KeyCode::Char('q'), Some(KeyCode::Char('Q'))), scroll_up_one: (KeyCode::Char('r'), Some(KeyCode::Char('R'))), @@ -701,6 +725,7 @@ mod tests { scroll_down_many: (KeyCode::Char('n'), None), scroll_down_one: (KeyCode::Char('o'), Some(KeyCode::Char('O'))), scroll_end: (KeyCode::Char('p'), None), + scroll_many: KeyModifiers::ALT, scroll_start: (KeyCode::Char('q'), Some(KeyCode::Char('Q'))), scroll_up_many: (KeyCode::Char('r'), None), scroll_up_one: (KeyCode::Char('s'), Some(KeyCode::Char('S'))), diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help.snap index de059d4..ad841ca 100644 --- a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help.snap +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help.snap @@ -17,8 +17,9 @@ expression: setup.terminal.backend() " │ A simple tui to view & control docker containers │ " " │ │ " " │ ( tab ) or ( shift+tab ) change panels │ " -" │ ( ↑ ↓ ) or ( j k ) or ( PgUp PgDown ) or ( Home End ) change selected line │ " +" │ ( ↑ ↓ ) or ( j k ) or ( PgUp PgDown ) or ( Home End ) scroll vertically │ " " │ ( ← → ) horizontal scroll across logs │ " +" │ ( ctrl ) increase scroll speed, used in conjuction scroll keys │ " " │ ( enter ) send docker container command │ " " │ ( e ) exec into a container │ " " │ ( f ) force clear the screen & redraw the gui │ " diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_colors.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_colors.snap index de059d4..ad841ca 100644 --- a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_colors.snap +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_colors.snap @@ -17,8 +17,9 @@ expression: setup.terminal.backend() " │ A simple tui to view & control docker containers │ " " │ │ " " │ ( tab ) or ( shift+tab ) change panels │ " -" │ ( ↑ ↓ ) or ( j k ) or ( PgUp PgDown ) or ( Home End ) change selected line │ " +" │ ( ↑ ↓ ) or ( j k ) or ( PgUp PgDown ) or ( Home End ) scroll vertically │ " " │ ( ← → ) horizontal scroll across logs │ " +" │ ( ctrl ) increase scroll speed, used in conjuction scroll keys │ " " │ ( enter ) send docker container command │ " " │ ( e ) exec into a container │ " " │ ( f ) force clear the screen & redraw the gui │ " diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_keymap_one_definition.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_keymap_one_definition.snap index eee704c..f8933a4 100644 --- a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_keymap_one_definition.snap +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_keymap_one_definition.snap @@ -25,6 +25,7 @@ expression: setup.terminal.backend() " │ ( q ) scroll list to start │ " " │ ( h ) horizontal scroll logs right │ " " │ ( g ) horizontal scroll logs left │ " +" │ ( Alt ) increase scroll speed, used in conjuction scroll keys │ " " │ ( enter ) send docker container command │ " " │ ( d ) exec into a container │ " " │ ( f ) force clear the screen & redraw the gui │ " @@ -50,5 +51,4 @@ expression: setup.terminal.backend() " │ │ " " │ currently an early work in progress, all and any input appreciated │ " " │ https://github.com/mrjackwills/oxker │ " -" │ │ " " ╰────────────────────────────────────────────────────────────────────────────────────╯ " diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_keymap_two_definitions.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_keymap_two_definitions.snap index 418e684..40866fb 100644 --- a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_keymap_two_definitions.snap +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_custom_keymap_two_definitions.snap @@ -25,6 +25,7 @@ expression: setup.terminal.backend() " │ ( p ) or ( P ) scroll list to start │ " " │ ( g ) or ( G ) horizontal scroll logs right │ " " │ ( f ) or ( F ) horizontal scroll logs left │ " +" │ ( Alt ) increase scroll speed, used in conjuction scroll keys │ " " │ ( enter ) send docker container command │ " " │ ( d ) or ( D ) exec into a container │ " " │ ( f ) or ( F ) force clear the screen & redraw the gui │ " @@ -50,5 +51,4 @@ expression: setup.terminal.backend() " │ │ " " │ currently an early work in progress, all and any input appreciated │ " " │ https://github.com/mrjackwills/oxker │ " -" │ │ " " ╰────────────────────────────────────────────────────────────────────────────────────────────────────╯ " diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_one_and_two_definitions.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_one_and_two_definitions.snap index 15f2de0..3694cac 100644 --- a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_one_and_two_definitions.snap +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_one_and_two_definitions.snap @@ -25,6 +25,7 @@ expression: setup.terminal.backend() " │ ( q ) or ( Q ) scroll list to start │ " " │ ( h ) horizontal scroll logs right │ " " │ ( g ) or ( G ) horizontal scroll logs left │ " +" │ ( Alt ) increase scroll speed, used in conjuction scroll keys │ " " │ ( enter ) send docker container command │ " " │ ( d ) exec into a container │ " " │ ( f ) force clear the screen & redraw the gui │ " @@ -50,5 +51,4 @@ expression: setup.terminal.backend() " │ │ " " │ currently an early work in progress, all and any input appreciated │ " " │ https://github.com/mrjackwills/oxker │ " -" │ │ " " ╰────────────────────────────────────────────────────────────────────────────────────────────╯ " diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_show_timezone.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_show_timezone.snap index 82193cb..67b6242 100644 --- a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_show_timezone.snap +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__help__tests__draw_blocks_help_show_timezone.snap @@ -18,8 +18,9 @@ expression: setup.terminal.backend() " │ logs timezone: Asia/Tokyo │ " " │ │ " " │ ( tab ) or ( shift+tab ) change panels │ " -" │ ( ↑ ↓ ) or ( j k ) or ( PgUp PgDown ) or ( Home End ) change selected line │ " +" │ ( ↑ ↓ ) or ( j k ) or ( PgUp PgDown ) or ( Home End ) scroll vertically │ " " │ ( ← → ) horizontal scroll across logs │ " +" │ ( ctrl ) increase scroll speed, used in conjuction scroll keys │ " " │ ( enter ) send docker container command │ " " │ ( e ) exec into a container │ " " │ ( f ) force clear the screen & redraw the gui │ " @@ -37,5 +38,4 @@ expression: setup.terminal.backend() " │ currently an early work in progress, all and any input appreciated │ " " │ https://github.com/mrjackwills/oxker │ " " │ │ " -" │ │ " " ╰───────────────────────────────────────────────────────────────────────────────────╯ " diff --git a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_help_panel.snap b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_help_panel.snap index 65aed11..f6603f1 100644 --- a/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_help_panel.snap +++ b/src/ui/draw_blocks/snapshots/oxker__ui__draw_blocks__tests__draw_blocks_whole_layout_help_panel.snap @@ -4,23 +4,23 @@ expression: setup.terminal.backend() --- " name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) exit help " "╭ Containers 1/3 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮╭──────────────╮" -"│⚪ container_1 ✓ running Up 1 hour 03.00% 30.00 kB / 30.00 kB 1 image_1 0.00 kB 0.00 kB ││▶ pause │" Hidden by multi-width symbols: [(2, " ")] -"│ container_2 ✓ running Up 2 ho╭ 0.00.000 ──────────────────────────────────────────────────────────────────────────╮ ││ restart │" -"│ container_3 ✓ running Up 3 ho│ │ ││ stop │" +"│⚪ container_1 ✓ running Up 1 ho╭ 0.00.000 ──────────────────────────────────────────────────────────────────────────╮ ││▶ pause │" Hidden by multi-width symbols: [(2, " ")] +"│ container_2 ✓ running Up 2 ho│ │ ││ restart │" +"│ container_3 ✓ running Up 3 ho│ 88 │ ││ stop │" "│ │ 88 │ ││ delete │" "│ │ 88 │ ││ │" -"╰────────────────────────────────────│ 88 │────────────────────╯╰──────────────╯" -"╭ Logs 3/3 - container_1 - image_1 ──│ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba, │────────────────────────────────────╮" -"│ line 1 │ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y8 │ │" -"│ line 2 │ 8b d8 )888( 8888[ 8PP""""""" 88 │ │" -"│▶ line 3 │ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 │ │" -"│ │ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 │ │" +"╰────────────────────────────────────│ ,adPPYba, 8b, ,d8 88 ,d8 ,adPPYba, 8b,dPPYba, │────────────────────╯╰──────────────╯" +"╭ Logs 3/3 - container_1 - image_1 ──│ a8" "8a `Y8, ,8P' 88 ,a8" a8P_____88 88P' "Y8 │────────────────────────────────────╮" +"│ line 1 │ 8b d8 )888( 8888[ 8PP""""""" 88 │ │" +"│ line 2 │ "8a, ,a8" ,d8" "8b, 88`"Yba, "8b, ,aa 88 │ │" +"│▶ line 3 │ `"YbbdP"' 8P' `Y8 88 `Y8a `"Ybbd8"' 88 │ │" "│ │ │ │" "│ │ A simple tui to view & control docker containers │ │" "│ │ │ │" "│ │ ( tab ) or ( shift+tab ) change panels │ │" -"│ │ ( ↑ ↓ ) or ( j k ) or ( PgUp PgDown ) or ( Home End ) change selected line │ │" +"│ │ ( ↑ ↓ ) or ( j k ) or ( PgUp PgDown ) or ( Home End ) scroll vertically │ │" "│ │ ( ← → ) horizontal scroll across logs │ │" +"│ │ ( ctrl ) increase scroll speed, used in conjuction scroll keys │ │" "│ │ ( enter ) send docker container command │ │" "│ │ ( e ) exec into a container │ │" "│ │ ( f ) force clear the screen & redraw the gui │ │" @@ -38,7 +38,7 @@ expression: setup.terminal.backend() "│ │ •• • │ currently an early work in progress, all and any input appreciated │ ││127.0.0.1 8003 8003│" "│ │ • • │ https://github.com/mrjackwills/oxker │ ││ │" "│ │ •• • • │ │ ││ │" -"│ │• •• ╰────────────────────────────────────────────────────────────────────────────────────╯ ││ │" -"│ │• • ││ │• • ││ │" +"│ │• •• │ │ ││ │" +"│ │• • ╰────────────────────────────────────────────────────────────────────────────────────╯ ││ │" "│ │ ││ │ ││ │" "╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰────────────────────────────╯" diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 3ee8d15..c46dacf 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -255,7 +255,10 @@ impl Ui { event::MouseEventKind::Down(_) | event::MouseEventKind::ScrollDown | event::MouseEventKind::ScrollUp => { - self.input_tx.send(InputMessages::MouseEvent(m)).await.ok(); + self.input_tx + .send(InputMessages::MouseEvent((m, m.modifiers))) + .await + .ok(); } _ => (), }