feat: ctrl scroll modifier

Use the `ctrl` button to scroll by a factor of ten
This commit is contained in:
Jack Wills
2025-08-21 15:19:10 +00:00
parent 8939ac0345
commit c5bbffdb5f
18 changed files with 249 additions and 130 deletions
+1 -2
View File
@@ -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<String> {
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,
};
+9
View File
@@ -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
+99 -64
View File
@@ -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<String>, with the correct derived attributes
@@ -12,6 +12,7 @@ macro_rules! optional_config_struct {
$(
$key_name: Option<Vec<String>>,
)*
pub scroll_many: Option<Vec<String>>,
}
)*
};
@@ -24,9 +25,10 @@ macro_rules! config_struct {
$(
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct $struct_name {
$(
$(
pub $key_name: (KeyCode, Option<KeyCode>),
)*
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<Option<ConfigKeymap>> 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<Option<ConfigKeymap>> for Keymap {
}
impl Keymap {
// Allowable key modifiers are only `shift`, `control`, `alt`
fn try_parse_modifier(input: Option<Vec<String>>) -> Option<KeyModifiers> {
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);
}
}
+1 -1
View File
@@ -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)),
}
+53 -24
View File
@@ -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(),
}
}
+41 -16
View File
@@ -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'))),
@@ -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 │ "
@@ -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 │ "
@@ -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 │ "
" │ │ "
" ╰────────────────────────────────────────────────────────────────────────────────────╯ "
@@ -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 │ "
" │ │ "
" ╰────────────────────────────────────────────────────────────────────────────────────────────────────╯ "
@@ -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 │ "
" │ │ "
" ╰────────────────────────────────────────────────────────────────────────────────────────────╯ "
@@ -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 │ "
" │ │ "
" │ │ "
" ╰───────────────────────────────────────────────────────────────────────────────────╯ "
@@ -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 │ ││ │"
"│ │ •• • • │ │ ││ │"
"│ │• •• ╰────────────────────────────────────────────────────────────────────────────────────╯ ││ │"
"│ │• • ││ │• • ││ │"
"│ │• •• │ │ ││ │"
"│ │• • ╰────────────────────────────────────────────────────────────────────────────────────╯ ││ │"
"│ │ ││ │ ││ │"
"╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰────────────────────────────╯"
+4 -1
View File
@@ -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();
}
_ => (),
}