feat: change log panel size, closes #50

This commit is contained in:
Jack Wills
2025-06-16 16:34:22 +00:00
parent d198398795
commit 6edf99e084
30 changed files with 738 additions and 342 deletions
Generated
+6 -6
View File
@@ -959,9 +959,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]] [[package]]
name = "jiff" name = "jiff"
version = "0.2.14" version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a194df1107f33c79f4f93d02c80798520551949d59dfad22b6157048a88cca93" checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49"
dependencies = [ dependencies = [
"jiff-static", "jiff-static",
"jiff-tzdb", "jiff-tzdb",
@@ -975,9 +975,9 @@ dependencies = [
[[package]] [[package]]
name = "jiff-static" name = "jiff-static"
version = "0.2.14" version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c6e1db7ed32c6c71b759497fae34bf7933636f75a251b9e736555da426f6442" checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -1017,9 +1017,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.172" version = "0.2.173"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" checksum = "d8cfeafaffdbc32176b64fb251369d52ea9f0a8fbc6f8759edffef7b525d64bb"
[[package]] [[package]]
name = "libredox" name = "libredox"
+4 -3
View File
@@ -211,6 +211,7 @@ cross_build_x86_linux() {
# Build, using cross-rs, for linux arm64 musl # Build, using cross-rs, for linux arm64 musl
cross_build_aarch64_linux() { cross_build_aarch64_linux() {
clear
check_cross check_cross
echo -e "${YELLOW}cross build --target aarch64-unknown-linux-musl --release${RESET}" echo -e "${YELLOW}cross build --target aarch64-unknown-linux-musl --release${RESET}"
cross build --target aarch64-unknown-linux-musl --release cross build --target aarch64-unknown-linux-musl --release
@@ -371,7 +372,7 @@ release_flow() {
} }
build_choice() { build_choice() {
cmd=(dialog --backtitle "Choose option" --radiolist "choose" 14 80 16) cmd=(dialog --backtitle "Choose option" --keep-tite --radiolist "choose" 14 80 16)
options=( options=(
1 "x86 musl linux" off 1 "x86 musl linux" off
2 "aarch64 musl linux" off 2 "aarch64 musl linux" off
@@ -415,7 +416,7 @@ build_choice() {
} }
build_container_choice() { build_container_choice() {
cmd=(dialog --backtitle "Choose option" --radiolist "choose" 14 80 16) cmd=(dialog --backtitle "Choose option" --keep-tite --radiolist "choose" 14 80 16)
options=( options=(
1 "x86 " off 1 "x86 " off
2 "aarch64" off 2 "aarch64" off
@@ -455,7 +456,7 @@ build_container_choice() {
} }
main() { main() {
cmd=(dialog --backtitle "Choose option" --radiolist "choose" 14 80 16) cmd=(dialog --backtitle "Choose option" --keep-tite --radiolist "choose" 14 80 16)
options=( options=(
1 "test" off 1 "test" off
2 "release" off 2 "release" off
+14
View File
@@ -29,6 +29,8 @@
// "save_dir": "$HOME", // "save_dir": "$HOME",
// Force use of docker cli when execing into containers, honestly mostly pointless // Force use of docker cli when execing into containers, honestly mostly pointless
"use_cli": false, "use_cli": false,
// Show the logs section - this can be changed during operation with the container_height_decrease / container_height_increase keys
"show_logs": true,
////////////////// //////////////////
// Custom Keymap // // Custom Keymap //
////////////////// //////////////////
@@ -144,6 +146,18 @@
// Toggle mouse capture // Toggle mouse capture
"toggle_mouse_capture": [ "toggle_mouse_capture": [
"m" "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": [
"\\"
] ]
}, },
//////////////////// ////////////////////
+9 -2
View File
@@ -40,6 +40,9 @@ timestamp_format="%Y-%m-%dT%H:%M:%S.%8f"
# Force use of docker cli when execing into containers, honestly mostly pointless # Force use of docker cli when execing into containers, honestly mostly pointless
use_cli = false use_cli = false
# Show the logs section - this can be changed during operation with the container_height_decrease / container_height_increase keys
show_logs = true
################# #################
# Custom Keymap # # Custom Keymap #
################# #################
@@ -101,7 +104,12 @@ sort_reset=["0"]
toggle_help = ["h"] toggle_help = ["h"]
# Toggle mouse capture # Toggle mouse capture
toggle_mouse_capture = ["m"] 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 = ["\\"]
################# #################
# Custom Colors # # Custom Colors #
@@ -176,7 +184,6 @@ selected_filter_text="black"
# Highlighted text color # Highlighted text color
highlight = "magenta" highlight = "magenta"
# The color the of Docker commands available for each container # The color the of Docker commands available for each container
[colors.commands] [colors.commands]
# Background color of panel # Background color of panel
+24
View File
@@ -30,6 +30,14 @@ impl From<&str> for ContainerId {
} }
impl ContainerId { impl ContainerId {
// TODO remove this once zigbuild uses Rust v1.87.0
#[cfg(target_os = "macos")]
#[allow(clippy::missing_const_for_fn)]
pub fn get(&self) -> &str {
self.0.as_str()
}
#[cfg(not(target_os = "macos"))]
pub const fn get(&self) -> &str { pub const fn get(&self) -> &str {
self.0.as_str() self.0.as_str()
} }
@@ -76,6 +84,14 @@ macro_rules! unit_struct {
} }
impl $name { impl $name {
#[cfg(target_os = "macos")]
#[allow(clippy::missing_const_for_fn)]
// TODO remove this once zigbuild uses Rust v1.87.0
pub fn get(&self) -> &str {
self.0.as_str()
}
#[cfg(not(target_os = "macos"))]
pub const fn get(&self) -> &str { pub const fn get(&self) -> &str {
self.0.as_str() self.0.as_str()
} }
@@ -594,6 +610,14 @@ impl Logs {
self.logs.start(); self.logs.start();
} }
// TODO remove this once zigbuild uses Rust v1.87.0
#[cfg(target_os = "macos")]
#[allow(clippy::missing_const_for_fn)]
pub fn len(&self) -> usize {
self.logs.items.len()
}
#[cfg(not(target_os = "macos"))]
pub const fn len(&self) -> usize { pub const fn len(&self) -> usize {
self.logs.items.len() self.logs.items.len()
} }
+32 -25
View File
@@ -14,7 +14,7 @@ use crate::{
ENTRY_POINT, ENTRY_POINT,
app_error::AppError, app_error::AppError,
config::Config, config::Config,
ui::{GuiState, Redraw, Status, log_sanitizer}, ui::{GuiState, Rerender, Status, log_sanitizer},
}; };
pub use container_state::*; pub use container_state::*;
@@ -122,7 +122,7 @@ pub struct AppData {
error: Option<AppError>, error: Option<AppError>,
filter: Filter, filter: Filter,
hidden_containers: Vec<ContainerItem>, hidden_containers: Vec<ContainerItem>,
redraw: Arc<Redraw>, redraw: Arc<Rerender>,
sorted_by: Option<(Header, SortedOrder)>, sorted_by: Option<(Header, SortedOrder)>,
current_sorted_id: Vec<ContainerId>, current_sorted_id: Vec<ContainerId>,
pub config: Config, pub config: Config,
@@ -137,13 +137,13 @@ pub struct AppData {
pub filter: Filter, pub filter: Filter,
pub hidden_containers: Vec<ContainerItem>, pub hidden_containers: Vec<ContainerItem>,
pub current_sorted_id: Vec<ContainerId>, pub current_sorted_id: Vec<ContainerId>,
pub redraw: Arc<Redraw>, pub redraw: Arc<Rerender>,
pub sorted_by: Option<(Header, SortedOrder)>, pub sorted_by: Option<(Header, SortedOrder)>,
} }
impl AppData { impl AppData {
/// Generate a default app_state /// Generate a default app_state
pub fn new(config: Config, redraw: &Arc<Redraw>) -> Self { pub fn new(config: Config, redraw: &Arc<Rerender>) -> Self {
Self { Self {
config, config,
containers: StatefulList::new(vec![]), containers: StatefulList::new(vec![]),
@@ -192,7 +192,7 @@ impl AppData {
/// sets the state to start if any filtering has occurred /// sets the state to start if any filtering has occurred
/// Also search in the "hidden" vec for items and insert back into the main containers vec /// Also search in the "hidden" vec for items and insert back into the main containers vec
fn filter_containers(&mut self) { fn filter_containers(&mut self) {
self.redraw.set_true(); self.redraw.update();
let pre_len = self.get_container_len(); let pre_len = self.get_container_len();
if !self.hidden_containers.is_empty() { if !self.hidden_containers.is_empty() {
@@ -296,7 +296,7 @@ impl AppData {
/// Remove the sorted header & order, and sort by default - created datetime /// Remove the sorted header & order, and sort by default - created datetime
pub fn reset_sorted(&mut self) { pub fn reset_sorted(&mut self) {
self.set_sorted(None); self.set_sorted(None);
self.redraw.set_true(); self.redraw.update();
} }
/// Sort containers based on a given header, if headings match, and already ascending, remove sorting /// Sort containers based on a given header, if headings match, and already ascending, remove sorting
@@ -392,7 +392,7 @@ impl AppData {
self.containers.items.sort_by(sort_closure); self.containers.items.sort_by(sort_closure);
if pre_order != self.get_current_ids() { if pre_order != self.get_current_ids() {
self.redraw.set_true(); self.redraw.update();
} }
} else if self.current_sorted_id != self.get_current_ids() { } else if self.current_sorted_id != self.get_current_ids() {
self.containers.items.sort_by(|a, b| { self.containers.items.sort_by(|a, b| {
@@ -400,13 +400,20 @@ impl AppData {
.cmp(&b.created) .cmp(&b.created)
.then_with(|| a.name.get().cmp(b.name.get())) .then_with(|| a.name.get().cmp(b.name.get()))
}); });
self.redraw.set_true(); self.redraw.update();
self.current_sorted_id = self.get_current_ids(); self.current_sorted_id = self.get_current_ids();
} }
} }
/// Container state methods /// Container state methods
/// Get the total number of none "hidden" containers /// Get the total number of none "hidden" containers
// TODO remove this once zigbuild uses Rust v1.87.0
#[cfg(target_os = "macos")]
pub fn get_container_len(&self) -> usize {
self.containers.items.len()
}
#[cfg(not(target_os = "macos"))]
pub const fn get_container_len(&self) -> usize { pub const fn get_container_len(&self) -> usize {
self.containers.items.len() self.containers.items.len()
} }
@@ -439,25 +446,25 @@ impl AppData {
/// Select the first container /// Select the first container
pub fn containers_start(&mut self) { pub fn containers_start(&mut self) {
self.containers.start(); self.containers.start();
self.redraw.set_true(); self.redraw.update();
} }
/// select the last container /// select the last container
pub fn containers_end(&mut self) { pub fn containers_end(&mut self) {
self.containers.end(); self.containers.end();
self.redraw.set_true(); self.redraw.update();
} }
/// Select the next container /// Select the next container
pub fn containers_next(&mut self) { pub fn containers_next(&mut self) {
self.containers.next(); self.containers.next();
self.redraw.set_true(); self.redraw.update();
} }
/// select the previous container /// select the previous container
pub fn containers_previous(&mut self) { pub fn containers_previous(&mut self) {
self.containers.previous(); self.containers.previous();
self.redraw.set_true(); self.redraw.update();
} }
/// Get ListState of containers /// Get ListState of containers
@@ -579,7 +586,7 @@ impl AppData {
pub fn docker_controls_next(&mut self) { pub fn docker_controls_next(&mut self) {
if let Some(i) = self.get_mut_selected_container() { if let Some(i) = self.get_mut_selected_container() {
i.docker_controls.next(); i.docker_controls.next();
self.redraw.set_true(); self.redraw.update();
} }
} }
@@ -587,7 +594,7 @@ impl AppData {
pub fn docker_controls_previous(&mut self) { pub fn docker_controls_previous(&mut self) {
if let Some(i) = self.get_mut_selected_container() { if let Some(i) = self.get_mut_selected_container() {
i.docker_controls.previous(); i.docker_controls.previous();
self.redraw.set_true(); self.redraw.update();
} }
} }
@@ -595,7 +602,7 @@ impl AppData {
pub fn docker_controls_start(&mut self) { pub fn docker_controls_start(&mut self) {
if let Some(i) = self.get_mut_selected_container() { if let Some(i) = self.get_mut_selected_container() {
i.docker_controls.start(); i.docker_controls.start();
self.redraw.set_true(); self.redraw.update();
} }
} }
@@ -603,7 +610,7 @@ impl AppData {
pub fn docker_controls_end(&mut self) { pub fn docker_controls_end(&mut self) {
if let Some(i) = self.get_mut_selected_container() { if let Some(i) = self.get_mut_selected_container() {
i.docker_controls.end(); i.docker_controls.end();
self.redraw.set_true(); self.redraw.update();
} }
} }
@@ -641,7 +648,7 @@ impl AppData {
pub fn log_next(&mut self) { pub fn log_next(&mut self) {
if let Some(i) = self.get_mut_selected_container() { if let Some(i) = self.get_mut_selected_container() {
i.logs.next(); i.logs.next();
self.redraw.set_true(); self.redraw.update();
} }
} }
@@ -649,7 +656,7 @@ impl AppData {
pub fn log_previous(&mut self) { pub fn log_previous(&mut self) {
if let Some(i) = self.get_mut_selected_container() { if let Some(i) = self.get_mut_selected_container() {
i.logs.previous(); i.logs.previous();
self.redraw.set_true(); self.redraw.update();
} }
} }
@@ -657,7 +664,7 @@ impl AppData {
pub fn log_end(&mut self) { pub fn log_end(&mut self) {
if let Some(i) = self.get_mut_selected_container() { if let Some(i) = self.get_mut_selected_container() {
i.logs.end(); i.logs.end();
self.redraw.set_true(); self.redraw.update();
} }
} }
@@ -665,7 +672,7 @@ impl AppData {
pub fn log_start(&mut self) { pub fn log_start(&mut self) {
if let Some(i) = self.get_mut_selected_container() { if let Some(i) = self.get_mut_selected_container() {
i.logs.start(); i.logs.start();
self.redraw.set_true(); self.redraw.update();
} }
} }
@@ -706,14 +713,14 @@ impl AppData {
/// Remove single app_state error /// Remove single app_state error
pub fn remove_error(&mut self) { pub fn remove_error(&mut self) {
self.error = None; self.error = None;
self.redraw.set_true(); self.redraw.update();
} }
/// Insert single app_state error /// Insert single app_state error
pub fn set_error(&mut self, error: AppError, gui_state: &Arc<Mutex<GuiState>>, status: Status) { pub fn set_error(&mut self, error: AppError, gui_state: &Arc<Mutex<GuiState>>, status: Status) {
gui_state.lock().status_push(status); gui_state.lock().status_push(status);
self.error = Some(error); self.error = Some(error);
self.redraw.set_true(); self.redraw.update();
} }
/// Check if the selected container is a dockerised version of oxker /// Check if the selected container is a dockerised version of oxker
@@ -803,7 +810,7 @@ impl AppData {
container.mem_limit.update(mem_limit); container.mem_limit.update(mem_limit);
} }
if self.is_selected_container(id) { if self.is_selected_container(id) {
self.redraw.set_true(); self.redraw.update();
} }
self.sort_containers(); self.sort_containers();
} }
@@ -841,7 +848,7 @@ impl AppData {
if self.containers.items.get(index).is_some() { if self.containers.items.get(index).is_some() {
self.containers.items.remove(index); self.containers.items.remove(index);
if self.is_selected_container(id) { if self.is_selected_container(id) {
self.redraw.set_true(); self.redraw.update();
} }
} }
} }
@@ -971,7 +978,7 @@ impl AppData {
} }
} }
if self.is_selected_container(id) { if self.is_selected_container(id) {
self.redraw.set_true(); self.redraw.update();
} }
} }
} }
+8 -1
View File
@@ -40,6 +40,9 @@ timestamp_format="%Y-%m-%dT%H:%M:%S.%8f"
# Force use of docker cli when execing into containers, honestly mostly pointless # Force use of docker cli when execing into containers, honestly mostly pointless
use_cli = false use_cli = false
# Show the logs section - this can be changed during operation with the container_height_decrease / container_height_increase keys
show_logs = true
################# #################
# Custom Keymap # # Custom Keymap #
################# #################
@@ -101,7 +104,11 @@ sort_reset=["0"]
toggle_help = ["h"] toggle_help = ["h"]
# Toggle mouse capture # Toggle mouse capture
toggle_mouse_capture = ["m"] toggle_mouse_capture = ["m"]
# Reduce the height of the logs list section
log_section_height_decrease = ["-"]
log_section_height_increase = ["+"]
# Toggle visibility of the log section
log_section_toggle = ["\\"]
################# #################
# Custom Colors # # Custom Colors #
+44 -10
View File
@@ -39,6 +39,9 @@ optional_config_struct!(
delete_confirm, delete_confirm,
exec, exec,
filter_mode, filter_mode,
log_section_height_increase,
log_section_height_decrease,
log_section_toggle,
quit, quit,
save_logs, save_logs,
scroll_down_many, scroll_down_many,
@@ -70,6 +73,9 @@ config_struct!(
delete_confirm, delete_confirm,
exec, exec,
filter_mode, filter_mode,
log_section_height_increase,
log_section_height_decrease,
log_section_toggle,
quit, quit,
save_logs, save_logs,
scroll_down_many, scroll_down_many,
@@ -98,10 +104,13 @@ impl Keymap {
pub const fn new() -> Self { pub const fn new() -> Self {
Self { Self {
clear: (KeyCode::Char('c'), Some(KeyCode::Esc)), clear: (KeyCode::Char('c'), Some(KeyCode::Esc)),
delete_deny: (KeyCode::Char('n'), None),
delete_confirm: (KeyCode::Char('y'), None), delete_confirm: (KeyCode::Char('y'), None),
delete_deny: (KeyCode::Char('n'), None),
exec: (KeyCode::Char('e'), None), exec: (KeyCode::Char('e'), None),
filter_mode: (KeyCode::Char('/'), Some(KeyCode::F(1))), filter_mode: (KeyCode::Char('/'), Some(KeyCode::F(1))),
log_section_height_decrease: (KeyCode::Char('-'), None),
log_section_height_increase: (KeyCode::Char('='), None),
log_section_toggle: (KeyCode::Char('\\'), None),
quit: (KeyCode::Char('q'), None), quit: (KeyCode::Char('q'), None),
save_logs: (KeyCode::Char('s'), None), save_logs: (KeyCode::Char('s'), None),
scroll_down_many: (KeyCode::PageDown, None), scroll_down_many: (KeyCode::PageDown, None),
@@ -112,14 +121,14 @@ impl Keymap {
scroll_up_one: (KeyCode::Up, Some(KeyCode::Char('k'))), scroll_up_one: (KeyCode::Up, Some(KeyCode::Char('k'))),
select_next_panel: (KeyCode::Tab, None), select_next_panel: (KeyCode::Tab, None),
select_previous_panel: (KeyCode::BackTab, None), select_previous_panel: (KeyCode::BackTab, None),
sort_by_name: (KeyCode::Char('1'), None),
sort_by_state: (KeyCode::Char('2'), None),
sort_by_status: (KeyCode::Char('3'), None),
sort_by_cpu: (KeyCode::Char('4'), None), sort_by_cpu: (KeyCode::Char('4'), None),
sort_by_memory: (KeyCode::Char('5'), None),
sort_by_id: (KeyCode::Char('6'), None), sort_by_id: (KeyCode::Char('6'), None),
sort_by_image: (KeyCode::Char('7'), None), sort_by_image: (KeyCode::Char('7'), None),
sort_by_memory: (KeyCode::Char('5'), None),
sort_by_name: (KeyCode::Char('1'), None),
sort_by_rx: (KeyCode::Char('8'), None), sort_by_rx: (KeyCode::Char('8'), None),
sort_by_state: (KeyCode::Char('2'), None),
sort_by_status: (KeyCode::Char('3'), None),
sort_by_tx: (KeyCode::Char('9'), None), sort_by_tx: (KeyCode::Char('9'), None),
sort_reset: (KeyCode::Char('0'), None), sort_reset: (KeyCode::Char('0'), None),
toggle_help: (KeyCode::Char('h'), None), toggle_help: (KeyCode::Char('h'), None),
@@ -162,6 +171,22 @@ impl From<Option<ConfigKeymap>> for Keymap {
update_keymap(ck.clear, &mut keymap.clear, &mut clash); update_keymap(ck.clear, &mut keymap.clear, &mut clash);
update_keymap(ck.delete_deny, &mut keymap.delete_deny, &mut clash); update_keymap(ck.delete_deny, &mut keymap.delete_deny, &mut clash);
update_keymap(ck.delete_confirm, &mut keymap.delete_confirm, &mut clash); update_keymap(ck.delete_confirm, &mut keymap.delete_confirm, &mut clash);
update_keymap(
ck.log_section_height_decrease,
&mut keymap.log_section_height_decrease,
&mut clash,
);
update_keymap(
ck.log_section_height_increase,
&mut keymap.log_section_height_increase,
&mut clash,
);
update_keymap(
ck.log_section_toggle,
&mut keymap.log_section_toggle,
&mut clash,
);
update_keymap(ck.exec, &mut keymap.exec, &mut clash); update_keymap(ck.exec, &mut keymap.exec, &mut clash);
update_keymap(ck.filter_mode, &mut keymap.filter_mode, &mut clash); update_keymap(ck.filter_mode, &mut keymap.filter_mode, &mut clash);
update_keymap(ck.quit, &mut keymap.quit, &mut clash); update_keymap(ck.quit, &mut keymap.quit, &mut clash);
@@ -339,6 +364,8 @@ mod tests {
delete_deny: Some(vec!["s".to_owned()]), delete_deny: Some(vec!["s".to_owned()]),
delete_confirm: None, delete_confirm: None,
exec: None, exec: None,
log_section_height_decrease: None,
log_section_height_increase: None,
filter_mode: None, filter_mode: None,
quit: None, quit: None,
save_logs: None, save_logs: None,
@@ -349,6 +376,7 @@ mod tests {
scroll_up_many: None, scroll_up_many: None,
scroll_up_one: None, scroll_up_one: None,
select_next_panel: None, select_next_panel: None,
log_section_toggle: None,
select_previous_panel: None, select_previous_panel: None,
sort_by_name: None, sort_by_name: None,
sort_by_state: None, sort_by_state: None,
@@ -376,10 +404,13 @@ mod tests {
let input = ConfigKeymap { let input = ConfigKeymap {
clear: gen_v(("a", "b")), clear: gen_v(("a", "b")),
delete_deny: gen_v(("c", "d")),
delete_confirm: gen_v(("e", "f")), delete_confirm: gen_v(("e", "f")),
delete_deny: gen_v(("c", "d")),
exec: gen_v(("g", "h")), exec: gen_v(("g", "h")),
filter_mode: gen_v(("i", "j")), filter_mode: gen_v(("i", "j")),
log_section_height_decrease: gen_v(("-", "Z")),
log_section_height_increase: gen_v(("=", "X")),
log_section_toggle: gen_v(("Y", "W")),
quit: gen_v(("k", "l")), quit: gen_v(("k", "l")),
save_logs: gen_v(("m", "n")), save_logs: gen_v(("m", "n")),
scroll_down_many: gen_v(("o", "p")), scroll_down_many: gen_v(("o", "p")),
@@ -390,14 +421,14 @@ mod tests {
scroll_up_one: gen_v(("y", "z")), scroll_up_one: gen_v(("y", "z")),
select_next_panel: gen_v(("0", "1")), select_next_panel: gen_v(("0", "1")),
select_previous_panel: gen_v(("2", "3")), select_previous_panel: gen_v(("2", "3")),
sort_by_name: gen_v(("4", "5")),
sort_by_state: gen_v(("6", "7")),
sort_by_status: gen_v(("8", "9")),
sort_by_cpu: gen_v(("F1", "F12")), sort_by_cpu: gen_v(("F1", "F12")),
sort_by_memory: gen_v(("/", "\\")),
sort_by_id: gen_v(("[", "]")), sort_by_id: gen_v(("[", "]")),
sort_by_image: gen_v(("A", "B")), 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_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_by_tx: gen_v(("insert", "TAB")),
sort_reset: gen_v(("up", "down")), sort_reset: gen_v(("up", "down")),
toggle_help: gen_v(("home", "end")), toggle_help: gen_v(("home", "end")),
@@ -410,6 +441,9 @@ mod tests {
clear: (KeyCode::Char('a'), Some(KeyCode::Char('b'))), clear: (KeyCode::Char('a'), Some(KeyCode::Char('b'))),
delete_deny: (KeyCode::Char('c'), Some(KeyCode::Char('d'))), delete_deny: (KeyCode::Char('c'), Some(KeyCode::Char('d'))),
delete_confirm: (KeyCode::Char('e'), Some(KeyCode::Char('f'))), delete_confirm: (KeyCode::Char('e'), Some(KeyCode::Char('f'))),
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'))),
exec: (KeyCode::Char('g'), Some(KeyCode::Char('h'))), exec: (KeyCode::Char('g'), Some(KeyCode::Char('h'))),
filter_mode: (KeyCode::Char('i'), Some(KeyCode::Char('j'))), filter_mode: (KeyCode::Char('i'), Some(KeyCode::Char('j'))),
quit: (KeyCode::Char('k'), Some(KeyCode::Char('l'))), quit: (KeyCode::Char('k'), Some(KeyCode::Char('l'))),
+3
View File
@@ -30,6 +30,7 @@ pub struct Config {
pub show_timestamp: bool, pub show_timestamp: bool,
pub timezone: Option<TimeZone>, pub timezone: Option<TimeZone>,
pub timestamp_format: String, pub timestamp_format: String,
pub show_logs: bool,
pub use_cli: bool, pub use_cli: bool,
} }
@@ -51,6 +52,7 @@ impl From<&Args> for Config {
timezone: Self::parse_timezone(args.timezone.clone()), timezone: Self::parse_timezone(args.timezone.clone()),
timestamp_format: Self::parse_timestamp_format(None), timestamp_format: Self::parse_timestamp_format(None),
use_cli: args.use_cli, use_cli: args.use_cli,
show_logs: true,
} }
} }
} }
@@ -73,6 +75,7 @@ impl From<ConfigFile> for Config {
timezone: Self::parse_timezone(config_file.timezone), timezone: Self::parse_timezone(config_file.timezone),
timestamp_format: Self::parse_timestamp_format(config_file.timestamp_format), timestamp_format: Self::parse_timestamp_format(config_file.timestamp_format),
use_cli: config_file.use_cli.unwrap_or(false), use_cli: config_file.use_cli.unwrap_or(false),
show_logs: config_file.show_logs.unwrap_or(true),
} }
} }
} }
+1
View File
@@ -75,6 +75,7 @@ pub struct ConfigFile {
pub timestamp_format: Option<String>, pub timestamp_format: Option<String>,
pub timezone: Option<String>, pub timezone: Option<String>,
pub use_cli: Option<bool>, pub use_cli: Option<bool>,
pub show_logs: Option<bool>,
} }
impl ConfigFile { impl ConfigFile {
+37 -12
View File
@@ -288,23 +288,15 @@ impl InputHandler {
/// Change the the "next" selectable panel /// Change the the "next" selectable panel
/// If no containers, and on Commands panel, skip to next panel, as Commands panel isn't visible in this state /// If no containers, and on Commands panel, skip to next panel, as Commands panel isn't visible in this state
fn next_panel_key(&self) { fn next_panel_key(&self) {
self.gui_state.lock().next_panel(); self.gui_state.lock().selectable_panel_next(&self.app_data);
if self.app_data.lock().get_container_len() == 0
&& self.gui_state.lock().get_selected_panel() == SelectablePanel::Commands
{
self.gui_state.lock().next_panel();
}
} }
/// Change to previously selected panel /// Change to previously selected panel
/// Need to skip the commands planel if there no are current containers running /// Need to skip the commands planel if there no are current containers running
fn previous_panel_key(&self) { fn previous_panel_key(&self) {
self.gui_state.lock().previous_panel(); self.gui_state
if self.app_data.lock().get_container_len() == 0 .lock()
&& self.gui_state.lock().get_selected_panel() == SelectablePanel::Commands .selectable_panel_previous(&self.app_data);
{
self.gui_state.lock().previous_panel();
}
} }
fn scroll_start_key(&self) { fn scroll_start_key(&self) {
@@ -458,9 +450,25 @@ impl InputHandler {
} }
} }
// Increase the log panel height
fn log_panel_height_increase(&self) {
self.gui_state.lock().log_height_increase();
}
// Decrease the log panel height
fn log_panel_height_decrease(&self) {
self.gui_state.lock().log_height_decrease();
}
// Toggle visibility of the log panel
fn log_panel_toggle(&self) {
self.gui_state.lock().toggle_show_logs();
}
/// Handle button presses in all other scenarios /// Handle button presses in all other scenarios
async fn handle_others(&mut self, key_code: KeyCode) { async fn handle_others(&mut self, key_code: KeyCode) {
self.handle_sort(key_code); self.handle_sort(key_code);
// shift key plus arrows
match key_code { match key_code {
_ if self.keymap.exec.0 == key_code || self.keymap.exec.1 == Some(key_code) => { _ if self.keymap.exec.0 == key_code || self.keymap.exec.1 == Some(key_code) => {
self.exec_key().await; self.exec_key().await;
@@ -477,6 +485,23 @@ impl InputHandler {
{ {
self.mouse_capture_key(); self.mouse_capture_key();
} }
_ if self.keymap.log_section_height_decrease.0 == key_code
|| self.keymap.log_section_height_decrease.1 == Some(key_code) =>
{
self.log_panel_height_decrease();
}
_ if self.keymap.log_section_height_increase.0 == key_code
|| self.keymap.log_section_height_increase.1 == Some(key_code) =>
{
self.log_panel_height_increase();
}
_ if self.keymap.log_section_toggle.0 == key_code
|| self.keymap.log_section_toggle.1 == Some(key_code) =>
{
self.log_panel_toggle();
}
_ if self.keymap.save_logs.0 == key_code _ if self.keymap.save_logs.0 == key_code
|| self.keymap.save_logs.1 == Some(key_code) => || self.keymap.save_logs.1 == Some(key_code) =>
+6 -5
View File
@@ -23,7 +23,7 @@ mod exec;
mod input_handler; mod input_handler;
mod ui; mod ui;
use ui::{GuiState, Redraw, Status, Ui}; use ui::{GuiState, Rerender, Status, Ui};
use crate::docker_data::DockerMessage; use crate::docker_data::DockerMessage;
@@ -98,10 +98,10 @@ fn handler_init(
async fn main() { async fn main() {
setup_tracing(); setup_tracing();
let config = config::Config::new(); let config = config::Config::new();
let redraw = Arc::new(Redraw::new()); let redraw = Arc::new(Rerender::new());
let app_data = Arc::new(Mutex::new(AppData::new(config.clone(), &redraw))); let app_data = Arc::new(Mutex::new(AppData::new(config.clone(), &redraw)));
let gui_state = Arc::new(Mutex::new(GuiState::new(&redraw))); let gui_state = Arc::new(Mutex::new(GuiState::new(&redraw, config.show_logs)));
let is_running = Arc::new(AtomicBool::new(true)); let is_running = Arc::new(AtomicBool::new(true));
let (docker_tx, docker_rx) = tokio::sync::mpsc::channel(32); let (docker_tx, docker_rx) = tokio::sync::mpsc::channel(32);
@@ -159,7 +159,7 @@ mod tests {
RunningState, State, StatefulList, RunningState, State, StatefulList,
}, },
config::{AppColors, Config, Keymap}, config::{AppColors, Config, Keymap},
ui::Redraw, ui::Rerender,
}; };
/// Default test config, has timestamps turned off /// Default test config, has timestamps turned off
@@ -179,6 +179,7 @@ mod tests {
timestamp_format: "HH:MM:SS.NNNNN dd-mm-yyyy".to_owned(), timestamp_format: "HH:MM:SS.NNNNN dd-mm-yyyy".to_owned(),
show_timestamp: false, show_timestamp: false,
use_cli: false, use_cli: false,
show_logs: true,
timezone: None, timezone: None,
} }
} }
@@ -207,7 +208,7 @@ mod tests {
current_sorted_id: vec![], current_sorted_id: vec![],
error: None, error: None,
sorted_by: None, sorted_by: None,
redraw: Arc::new(Redraw::new()), redraw: Arc::new(Rerender::new()),
filter: Filter::new(), filter: Filter::new(),
config: gen_config(), config: gen_config(),
} }
+4 -1
View File
@@ -241,7 +241,10 @@ mod tests {
} }
// Control panel now selected, should have a blue border // Control panel now selected, should have a blue border
setup.gui_state.lock().next_panel(); setup
.gui_state
.lock()
.selectable_panel_next(&setup.app_data);
let fd = FrameData::from((&setup.app_data, &setup.gui_state)); let fd = FrameData::from((&setup.app_data, &setup.gui_state));
setup setup
.terminal .terminal
+12 -3
View File
@@ -157,7 +157,10 @@ mod tests {
let mut setup = test_setup(40, 6, true, true); let mut setup = test_setup(40, 6, true, true);
setup.app_data.lock().containers = StatefulList::new(vec![]); setup.app_data.lock().containers = StatefulList::new(vec![]);
setup.gui_state.lock().next_panel(); setup
.gui_state
.lock()
.selectable_panel_next(&setup.app_data);
let fd = FrameData::from((&setup.app_data, &setup.gui_state)); let fd = FrameData::from((&setup.app_data, &setup.gui_state));
let colors = setup.app_data.lock().config.app_colors; let colors = setup.app_data.lock().config.app_colors;
@@ -184,7 +187,10 @@ mod tests {
} }
} }
setup.gui_state.lock().previous_panel(); setup
.gui_state
.lock()
.selectable_panel_previous(&setup.app_data);
let fd = FrameData::from((&setup.app_data, &setup.gui_state)); let fd = FrameData::from((&setup.app_data, &setup.gui_state));
setup setup
@@ -255,7 +261,10 @@ mod tests {
} }
// Change selected panel, border is now no longer blue // Change selected panel, border is now no longer blue
setup.gui_state.lock().next_panel(); setup
.gui_state
.lock()
.selectable_panel_next(&setup.app_data);
let fd = FrameData::from((&setup.app_data, &setup.gui_state)); let fd = FrameData::from((&setup.app_data, &setup.gui_state));
setup setup
.terminal .terminal
+47 -27
View File
@@ -85,6 +85,7 @@ impl HelpInfo {
} }
/// Generate the button information span + metadata /// Generate the button information span + metadata
#[allow(clippy::too_many_lines)]
fn gen_keymap_info(colors: AppColors, zone: Option<&TimeZone>, show_timestamp: bool) -> Self { fn gen_keymap_info(colors: AppColors, zone: Option<&TimeZone>, show_timestamp: bool) -> Self {
let button_item = |x: &str| Self::highlighted_text_span(&format!(" ( {x} ) "), colors); let button_item = |x: &str| Self::highlighted_text_span(&format!(" ( {x} ) "), colors);
let button_desc = |x: &str| Self::text_span(x, colors); let button_desc = |x: &str| Self::text_span(x, colors);
@@ -152,6 +153,16 @@ impl HelpInfo {
button_item("1 - 9"), button_item("1 - 9"),
button_desc("sort by header - or click header"), button_desc("sort by header - or click header"),
]), ]),
Line::from(vec![
space(),
button_item("- ="),
button_desc("change log section height"),
]),
Line::from(vec![
space(),
button_item("\\"),
button_desc("toggle log section visibility"),
]),
Line::from(vec![ Line::from(vec![
space(), space(),
button_item("esc"), button_item("esc"),
@@ -212,15 +223,6 @@ impl HelpInfo {
fn custom_text<'a>(colors: AppColors, _keymap: &Keymap, zone: Option<&TimeZone>) -> Line<'a> { fn custom_text<'a>(colors: AppColors, _keymap: &Keymap, zone: Option<&TimeZone>) -> Line<'a> {
let highlighted = |x: &str| Self::highlighted_text_span(x, colors); let highlighted = |x: &str| Self::highlighted_text_span(x, colors);
let text = |x: &str| Self::text_span(x, colors); let text = |x: &str| Self::text_span(x, colors);
// if keymap != &Keymap::new() {
// op.push(highlighted("customised keymap, "));
// }
// if colors != AppColors::new() {
// op.push(highlighted("customised app colors, "));
// };
let zone = zone.and_then(|i| i.iana_name()).unwrap_or("Etc/UTC"); let zone = zone.and_then(|i| i.iana_name()).unwrap_or("Etc/UTC");
Line::from(Vec::from([text("logs timezone: "), highlighted(zone)])).centered() Line::from(Vec::from([text("logs timezone: "), highlighted(zone)])).centered()
} }
@@ -295,6 +297,15 @@ impl HelpInfo {
or_secondary(km.sort_by_image, "sort containers by image"), or_secondary(km.sort_by_image, "sort containers by image"),
or_secondary(km.sort_by_rx, "sort containers by rx"), or_secondary(km.sort_by_rx, "sort containers by rx"),
or_secondary(km.sort_by_tx, "sort containers by tx"), or_secondary(km.sort_by_tx, "sort containers by tx"),
or_secondary(
km.log_section_height_decrease,
"decrease log section height",
),
or_secondary(
km.log_section_height_increase,
"increase log section height",
),
or_secondary(km.log_section_toggle, "toggle log section visibility"),
or_secondary(km.clear, "close dialog"), or_secondary(km.clear, "close dialog"),
or_secondary(km.quit, "quit at any time"), or_secondary(km.quit, "quit at any time"),
]; ];
@@ -426,7 +437,7 @@ mod tests {
#[test] #[test]
/// This will cause issues once the version has more than the current 5 chars (0.5.0) /// This will cause issues once the version has more than the current 5 chars (0.5.0)
fn test_draw_blocks_help() { fn test_draw_blocks_help() {
let mut setup = test_setup(87, 33, true, true); let mut setup = test_setup(87, 35, true, true);
let tz = setup.app_data.lock().config.timezone.clone(); let tz = setup.app_data.lock().config.timezone.clone();
setup setup
@@ -448,29 +459,29 @@ mod tests {
for (result_cell_index, result_cell) in result_row.iter().enumerate() { for (result_cell_index, result_cell) in result_row.iter().enumerate() {
match (row_index, result_cell_index) { 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 // 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 | 32, _) | (0..=33, 0 | 86) => { (0 | 34, _) | (0..=33, 0 | 86) => {
assert_eq!(result_cell.bg, Color::Reset); assert_eq!(result_cell.bg, Color::Reset);
assert_eq!(result_cell.fg, Color::Reset); assert_eq!(result_cell.fg, Color::Reset);
} }
// border is black on magenta // border is black on magenta
(1 | 31, _) | (1..=31, 1 | 85) => { (1 | 32, _) | (1..=31, 1 | 85) => {
assert_eq!(result_cell.bg, Color::Magenta); assert_eq!(result_cell.bg, Color::Magenta);
assert_eq!(result_cell.fg, Color::Black); assert_eq!(result_cell.fg, Color::Black);
} }
// oxker logo && description // oxker logo && description
(2..=10, 2..=85) | (12, 19..=66) (2..=10, 2..=85)
// button in the brackets | (12, 19..=66)
| (14, 2..=10 | 13..=27) | (14, 2..=10 | 13..=27)
| (15, 2..=10 | 13..=21 | 24..=40 | 43..=56) | (15, 2..=10 | 13..=21 | 24..=40 | 43..=56)
| (16 | 23, 2..=12) | (16 | 23, 2..=12)
| (17..=20 | 22 | 25, 2..=8) | (17..=20 | 22 | 25 | 27, 2..=8)
| (21, 2..=9 | 12..=18) | (21, 2..=9 | 12..=18)
| (24, 2..=10) => { | (24 | 26, 2..=10) => {
assert_eq!(result_cell.bg, Color::Magenta); assert_eq!(result_cell.bg, Color::Magenta);
assert_eq!(result_cell.fg, Color::White); assert_eq!(result_cell.fg, Color::White);
} }
// The URL is white and underlined // The URL is white and underlined
(28, 25..=60) => { (30, 25..=60) => {
assert_eq!(result_cell.bg, Color::Magenta); assert_eq!(result_cell.bg, Color::Magenta);
assert_eq!(result_cell.fg, Color::White); assert_eq!(result_cell.fg, Color::White);
assert_eq!(result_cell.modifier, Modifier::UNDERLINED); assert_eq!(result_cell.modifier, Modifier::UNDERLINED);
@@ -488,7 +499,7 @@ mod tests {
#[test] #[test]
/// Test that the help panel gets drawn with custom colors /// Test that the help panel gets drawn with custom colors
fn test_draw_blocks_help_custom_colors() { fn test_draw_blocks_help_custom_colors() {
let mut setup = test_setup(87, 33, true, true); let mut setup = test_setup(87, 35, true, true);
let mut colors = AppColors::new(); let mut colors = AppColors::new();
let tz = setup.app_data.lock().config.timezone.clone(); let tz = setup.app_data.lock().config.timezone.clone();
@@ -515,29 +526,29 @@ mod tests {
for (result_cell_index, result_cell) in result_row.iter().enumerate() { for (result_cell_index, result_cell) in result_row.iter().enumerate() {
match (row_index, result_cell_index) { 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 // 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 | 32, _) | (0..=33, 0 | 86) => { (0 | 34, _) | (0..=33, 0 | 86) => {
assert_eq!(result_cell.bg, Color::Reset); assert_eq!(result_cell.bg, Color::Reset);
assert_eq!(result_cell.fg, Color::Reset); assert_eq!(result_cell.fg, Color::Reset);
} }
// border is black on magenta // border is red on black
(1 | 31, _) | (1..=31, 1 | 85) => { (1 | 32, _) | (1..=31, 1 | 85) => {
assert_eq!(result_cell.bg, Color::Black); assert_eq!(result_cell.bg, Color::Black);
assert_eq!(result_cell.fg, Color::Red); assert_eq!(result_cell.fg, Color::Red);
} }
// oxker logo && description // oxker logo && description
(2..=10, 2..=85) | (12, 19..=66) (2..=10, 2..=85)
// button in the brackets | (12, 19..=66)
| (14, 2..=10 | 13..=27) | (14, 2..=10 | 13..=27)
| (15, 2..=10 | 13..=21 | 24..=40 | 43..=56) | (15, 2..=10 | 13..=21 | 24..=40 | 43..=56)
| (16 | 23, 2..=12) | (16 | 23, 2..=12)
| (17..=20 | 22 | 25, 2..=8) | (17..=20 | 22 | 25 | 27, 2..=8)
| (21, 2..=9 | 12..=18) | (21, 2..=9 | 12..=18)
| (24, 2..=10) => { | (24 | 26, 2..=10) => {
assert_eq!(result_cell.bg, Color::Black); assert_eq!(result_cell.bg, Color::Black);
assert_eq!(result_cell.fg, Color::Yellow); assert_eq!(result_cell.fg, Color::Yellow);
} }
// The URL is yellow and underlined // The URL is yellow and underlined
(28, 25..=60) => { (30, 25..=60) => {
assert_eq!(result_cell.bg, Color::Black); assert_eq!(result_cell.bg, Color::Black);
assert_eq!(result_cell.fg, Color::Yellow); assert_eq!(result_cell.fg, Color::Yellow);
assert_eq!(result_cell.modifier, Modifier::UNDERLINED); assert_eq!(result_cell.modifier, Modifier::UNDERLINED);
@@ -562,6 +573,9 @@ mod tests {
delete_deny: (KeyCode::Char('c'), None), delete_deny: (KeyCode::Char('c'), None),
delete_confirm: (KeyCode::Char('e'), None), delete_confirm: (KeyCode::Char('e'), None),
exec: (KeyCode::Char('g'), None), exec: (KeyCode::Char('g'), None),
log_section_height_decrease: (KeyCode::Char('z'), None),
log_section_height_increase: (KeyCode::Char('x'), None),
log_section_toggle: (KeyCode::Char('W'), None),
filter_mode: (KeyCode::Char('i'), None), filter_mode: (KeyCode::Char('i'), None),
quit: (KeyCode::Char('k'), None), quit: (KeyCode::Char('k'), None),
save_logs: (KeyCode::Char('m'), None), save_logs: (KeyCode::Char('m'), None),
@@ -607,6 +621,9 @@ mod tests {
delete_deny: (KeyCode::Char('c'), Some(KeyCode::Char('d'))), delete_deny: (KeyCode::Char('c'), Some(KeyCode::Char('d'))),
delete_confirm: (KeyCode::Char('e'), Some(KeyCode::Char('f'))), delete_confirm: (KeyCode::Char('e'), Some(KeyCode::Char('f'))),
exec: (KeyCode::Char('g'), Some(KeyCode::Char('h'))), exec: (KeyCode::Char('g'), Some(KeyCode::Char('h'))),
log_section_height_decrease: (KeyCode::Char('A'), Some(KeyCode::Char('Z'))),
log_section_height_increase: (KeyCode::Char('B'), Some(KeyCode::Char('X'))),
log_section_toggle: (KeyCode::Char('C'), Some(KeyCode::Char('W'))),
filter_mode: (KeyCode::Char('i'), Some(KeyCode::Char('j'))), filter_mode: (KeyCode::Char('i'), Some(KeyCode::Char('j'))),
quit: (KeyCode::Char('k'), Some(KeyCode::Char('l'))), quit: (KeyCode::Char('k'), Some(KeyCode::Char('l'))),
save_logs: (KeyCode::Char('m'), Some(KeyCode::Char('n'))), save_logs: (KeyCode::Char('m'), Some(KeyCode::Char('n'))),
@@ -653,6 +670,9 @@ mod tests {
delete_confirm: (KeyCode::Char('e'), Some(KeyCode::Char('f'))), delete_confirm: (KeyCode::Char('e'), Some(KeyCode::Char('f'))),
exec: (KeyCode::Char('g'), None), exec: (KeyCode::Char('g'), None),
filter_mode: (KeyCode::Char('i'), Some(KeyCode::Char('j'))), filter_mode: (KeyCode::Char('i'), Some(KeyCode::Char('j'))),
log_section_height_decrease: (KeyCode::Char('A'), Some(KeyCode::Char('Z'))),
log_section_height_increase: (KeyCode::Char('B'), Some(KeyCode::Char('X'))),
log_section_toggle: (KeyCode::Char('C'), Some(KeyCode::Char('W'))),
quit: (KeyCode::Char('k'), None), quit: (KeyCode::Char('k'), None),
save_logs: (KeyCode::Char('m'), Some(KeyCode::Char('n'))), save_logs: (KeyCode::Char('m'), Some(KeyCode::Char('n'))),
scroll_down_many: (KeyCode::Char('o'), None), scroll_down_many: (KeyCode::Char('o'), None),
@@ -691,7 +711,7 @@ mod tests {
#[test] #[test]
fn test_draw_blocks_help_show_timezone() { fn test_draw_blocks_help_show_timezone() {
let mut setup = test_setup(87, 35, true, true); let mut setup = test_setup(87, 37, true, true);
setup setup
.terminal .terminal
+8 -2
View File
@@ -124,8 +124,14 @@ mod tests {
} }
} }
setup.gui_state.lock().next_panel(); setup
setup.gui_state.lock().next_panel(); .gui_state
.lock()
.selectable_panel_next(&setup.app_data);
setup
.gui_state
.lock()
.selectable_panel_next(&setup.app_data);
let fd = FrameData::from((&setup.app_data, &setup.gui_state)); let fd = FrameData::from((&setup.app_data, &setup.gui_state));
// When selected, has a blue border // When selected, has a blue border
+76 -14
View File
@@ -128,7 +128,7 @@ pub mod tests {
use crate::{ use crate::{
app_data::{AppData, ContainerId, ContainerImage, ContainerName, ContainerPorts}, app_data::{AppData, ContainerId, ContainerImage, ContainerName, ContainerPorts},
tests::{gen_appdata, gen_containers}, tests::{gen_appdata, gen_containers},
ui::{GuiState, Redraw, draw_frame}, ui::{GuiState, Rerender, draw_frame},
}; };
use super::FrameData; use super::FrameData;
@@ -152,32 +152,33 @@ pub mod tests {
fn from(data: (&Arc<Mutex<AppData>>, &Arc<Mutex<GuiState>>)) -> Self { fn from(data: (&Arc<Mutex<AppData>>, &Arc<Mutex<GuiState>>)) -> Self {
let (app_data, gui_data) = (data.0.lock(), data.1.lock()); let (app_data, gui_data) = (data.0.lock(), data.1.lock());
// set max height for container section, needs +5 to deal with docker commands list and borders // let container_section_height = app_data.get_container_len();
let height = app_data.get_container_len(); // let container_section_height = if container_section_height < 12 {
let height = if height < 12 { // u16::try_from(container_section_height + 5).unwrap_or_default()
u16::try_from(height + 5).unwrap_or_default() // } else {
} else { // 12
12 // };
};
let (filter_by, filter_term) = app_data.get_filter(); let (filter_by, filter_term) = app_data.get_filter();
Self { Self {
chart_data: app_data.get_chart_data(), chart_data: app_data.get_chart_data(),
columns: app_data.get_width(),
color_logs: app_data.config.color_logs, color_logs: app_data.config.color_logs,
columns: app_data.get_width(),
// container_section_height,
container_title: app_data.get_container_title(), container_title: app_data.get_container_title(),
delete_confirm: gui_data.get_delete_container(), delete_confirm: gui_data.get_delete_container(),
filter_by, filter_by,
filter_term: filter_term.cloned(), filter_term: filter_term.cloned(),
has_containers: app_data.get_container_len() > 0, has_containers: app_data.get_container_len() > 0,
has_error: app_data.get_error(), has_error: app_data.get_error(),
height, show_logs: gui_data.get_show_logs(),
ports: app_data.get_selected_ports(),
port_max_lens: app_data.get_longest_port(),
info_text: gui_data.info_box_text.clone(), info_text: gui_data.info_box_text.clone(),
is_loading: gui_data.is_loading(), is_loading: gui_data.is_loading(),
loading_icon: gui_data.get_loading().to_string(), loading_icon: gui_data.get_loading().to_string(),
log_height: gui_data.get_log_height(),
log_title: app_data.get_log_title(), log_title: app_data.get_log_title(),
port_max_lens: app_data.get_longest_port(),
ports: app_data.get_selected_ports(),
selected_panel: gui_data.get_selected_panel(), selected_panel: gui_data.get_selected_panel(),
sorted_by: app_data.get_sorted(), sorted_by: app_data.get_sorted(),
status: gui_data.get_status(), status: gui_data.get_status(),
@@ -199,8 +200,8 @@ pub mod tests {
app_data.containers_start(); app_data.containers_start();
} }
let redraw = Arc::new(Redraw::new()); let redraw = Arc::new(Rerender::new());
let gui_state = GuiState::new(&redraw); let gui_state = GuiState::new(&redraw, app_data.config.show_logs);
let app_data = Arc::new(Mutex::new(app_data)); let app_data = Arc::new(Mutex::new(app_data));
let gui_state = Arc::new(Mutex::new(gui_state)); let gui_state = Arc::new(Mutex::new(gui_state));
@@ -360,4 +361,65 @@ pub mod tests {
assert_snapshot!(setup.terminal.backend()); assert_snapshot!(setup.terminal.backend());
} }
#[test]
/// Check that the whole layout is drawn correctly when the logs panel is removed
fn test_draw_blocks_whole_layout_no_logs() {
let mut setup = test_setup(160, 30, true, true);
insert_chart_data(&setup);
insert_logs(&setup);
setup.app_data.lock().containers.items[0]
.ports
.push(ContainerPorts {
ip: Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))),
private: 8003,
public: Some(8003),
});
let colors = setup.app_data.lock().config.app_colors;
let keymap = setup.app_data.lock().config.keymap.clone();
setup.gui_state.lock().log_height_zero();
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
setup
.terminal
.draw(|f| {
draw_frame(&setup.app_data, colors, &keymap, f, &fd, &setup.gui_state);
})
.unwrap();
assert_snapshot!(setup.terminal.backend());
}
#[test]
/// Check that the whole layout is drawn correctly when the logs panel height is ~4
fn test_draw_blocks_whole_layout_short_height_logs() {
let mut setup = test_setup(160, 30, true, true);
insert_chart_data(&setup);
insert_logs(&setup);
setup.app_data.lock().containers.items[0]
.ports
.push(ContainerPorts {
ip: Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))),
private: 8003,
public: Some(8003),
});
let colors = setup.app_data.lock().config.app_colors;
let keymap = setup.app_data.lock().config.keymap.clone();
setup.gui_state.lock().log_height_zero();
for _ in 0..=3 {
setup.gui_state.lock().log_height_increase();
}
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
setup
.terminal
.draw(|f| {
draw_frame(&setup.app_data, colors, &keymap, f, &fd, &setup.gui_state);
})
.unwrap();
assert_snapshot!(setup.terminal.backend());
}
} }
@@ -1,5 +1,6 @@
--- ---
source: src/ui/draw_blocks/help.rs source: src/ui/draw_blocks/help.rs
assertion_line: 456
expression: setup.terminal.backend() expression: setup.terminal.backend()
--- ---
" " " "
@@ -26,6 +27,8 @@ expression: setup.terminal.backend()
" │ ( F1 ) or ( / ) enter filter mode │ " " │ ( F1 ) or ( / ) enter filter mode │ "
" │ ( 0 ) stop sort │ " " │ ( 0 ) stop sort │ "
" │ ( 1 - 9 ) sort by header - or click header │ " " │ ( 1 - 9 ) sort by header - or click header │ "
" │ ( - = ) change log section height │ "
" │ ( \ ) toggle log section visibility │ "
" │ ( esc ) close dialog │ " " │ ( esc ) close dialog │ "
" │ ( q ) quit at any time │ " " │ ( q ) quit at any time │ "
" │ │ " " │ │ "
@@ -26,6 +26,8 @@ expression: setup.terminal.backend()
" │ ( F1 ) or ( / ) enter filter mode │ " " │ ( F1 ) or ( / ) enter filter mode │ "
" │ ( 0 ) stop sort │ " " │ ( 0 ) stop sort │ "
" │ ( 1 - 9 ) sort by header - or click header │ " " │ ( 1 - 9 ) sort by header - or click header │ "
" │ ( - = ) change log section height │ "
" │ ( \ ) toggle log section visibility │ "
" │ ( esc ) close dialog │ " " │ ( esc ) close dialog │ "
" │ ( q ) quit at any time │ " " │ ( q ) quit at any time │ "
" │ │ " " │ │ "
@@ -2,7 +2,6 @@
source: src/ui/draw_blocks/help.rs source: src/ui/draw_blocks/help.rs
expression: setup.terminal.backend() expression: setup.terminal.backend()
--- ---
" "
" ╭ 0.00.000 ──────────────────────────────────────────────────────────────────────────────────╮ " " ╭ 0.00.000 ──────────────────────────────────────────────────────────────────────────────────╮ "
" │ │ " " │ │ "
" │ 88 │ " " │ 88 │ "
@@ -40,12 +39,13 @@ expression: setup.terminal.backend()
" │ ( , ) sort containers by image │ " " │ ( , ) sort containers by image │ "
" │ ( . ) sort containers by rx │ " " │ ( . ) sort containers by rx │ "
" │ ( Insert ) sort containers by tx │ " " │ ( Insert ) sort containers by tx │ "
" │ ( z ) decrease log section height │ "
" │ ( x ) increase log section height │ "
" │ ( W ) toggle log section visibility │ "
" │ ( a ) close dialog │ " " │ ( a ) close dialog │ "
" │ ( k ) quit at any time │ " " │ ( k ) quit at any time │ "
" │ │ " " │ │ "
" │ currently an early work in progress, all and any input appreciated │ " " │ currently an early work in progress, all and any input appreciated │ "
" │ https://github.com/mrjackwills/oxker │ " " │ https://github.com/mrjackwills/oxker │ "
" │ │ " " │ │ "
" │ │ "
" ╰────────────────────────────────────────────────────────────────────────────────────────────╯ " " ╰────────────────────────────────────────────────────────────────────────────────────────────╯ "
" "
@@ -2,7 +2,6 @@
source: src/ui/draw_blocks/help.rs source: src/ui/draw_blocks/help.rs
expression: setup.terminal.backend() expression: setup.terminal.backend()
--- ---
" "
" ╭ 0.00.000 ────────────────────────────────────────────────────────────────────────────────────────────────╮ " " ╭ 0.00.000 ────────────────────────────────────────────────────────────────────────────────────────────────╮ "
" │ │ " " │ │ "
" │ 88 │ " " │ 88 │ "
@@ -40,12 +39,13 @@ expression: setup.terminal.backend()
" │ ( , ) or ( \ ) sort containers by image │ " " │ ( , ) or ( \ ) sort containers by image │ "
" │ ( . ) or ( ] ) sort containers by rx │ " " │ ( . ) or ( ] ) sort containers by rx │ "
" │ ( Insert ) or ( Back Tab ) sort containers by tx │ " " │ ( Insert ) or ( Back Tab ) sort containers by tx │ "
" │ ( A ) or ( Z ) decrease log section height │ "
" │ ( B ) or ( X ) increase log section height │ "
" │ ( C ) or ( W ) toggle log section visibility │ "
" │ ( a ) or ( b ) close dialog │ " " │ ( a ) or ( b ) close dialog │ "
" │ ( k ) or ( l ) quit at any time │ " " │ ( k ) or ( l ) quit at any time │ "
" │ │ " " │ │ "
" │ currently an early work in progress, all and any input appreciated │ " " │ currently an early work in progress, all and any input appreciated │ "
" │ https://github.com/mrjackwills/oxker │ " " │ https://github.com/mrjackwills/oxker │ "
" │ │ " " │ │ "
" │ │ "
" ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯ " " ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯ "
" "
@@ -2,7 +2,6 @@
source: src/ui/draw_blocks/help.rs source: src/ui/draw_blocks/help.rs
expression: setup.terminal.backend() expression: setup.terminal.backend()
--- ---
" "
" ╭ 0.00.000 ────────────────────────────────────────────────────────────────────────────────────────────────╮ " " ╭ 0.00.000 ────────────────────────────────────────────────────────────────────────────────────────────────╮ "
" │ │ " " │ │ "
" │ 88 │ " " │ 88 │ "
@@ -40,12 +39,13 @@ expression: setup.terminal.backend()
" │ ( , ) sort containers by image │ " " │ ( , ) sort containers by image │ "
" │ ( . ) or ( ] ) sort containers by rx │ " " │ ( . ) or ( ] ) sort containers by rx │ "
" │ ( Insert ) sort containers by tx │ " " │ ( Insert ) sort containers by tx │ "
" │ ( A ) or ( Z ) decrease log section height │ "
" │ ( B ) or ( X ) increase log section height │ "
" │ ( C ) or ( W ) toggle log section visibility │ "
" │ ( a ) or ( b ) close dialog │ " " │ ( a ) or ( b ) close dialog │ "
" │ ( k ) quit at any time │ " " │ ( k ) quit at any time │ "
" │ │ " " │ │ "
" │ currently an early work in progress, all and any input appreciated │ " " │ currently an early work in progress, all and any input appreciated │ "
" │ https://github.com/mrjackwills/oxker │ " " │ https://github.com/mrjackwills/oxker │ "
" │ │ " " │ │ "
" │ │ "
" ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯ " " ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────╯ "
" "
@@ -28,6 +28,8 @@ expression: setup.terminal.backend()
" │ ( F1 ) or ( / ) enter filter mode │ " " │ ( F1 ) or ( / ) enter filter mode │ "
" │ ( 0 ) stop sort │ " " │ ( 0 ) stop sort │ "
" │ ( 1 - 9 ) sort by header - or click header │ " " │ ( 1 - 9 ) sort by header - or click header │ "
" │ ( - = ) change log section height │ "
" │ ( \ ) toggle log section visibility │ "
" │ ( esc ) close dialog │ " " │ ( esc ) close dialog │ "
" │ ( q ) quit at any time │ " " │ ( q ) quit at any time │ "
" │ │ " " │ │ "
@@ -8,8 +8,6 @@ expression: setup.terminal.backend()
"│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB ││ restart │" "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB ││ restart │"
"│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB ││ stop │" "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB ││ stop │"
"│ ││ delete │" "│ ││ delete │"
"│ ││ │"
"│ ││ │"
"╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯╰──────────────╯" "╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯╰──────────────╯"
"╭ Logs 3/3 - container_1 - image_1 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮" "╭ Logs 3/3 - container_1 - image_1 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
"│ line 1 │" "│ line 1 │"
@@ -25,10 +23,12 @@ expression: setup.terminal.backend()
"│ │" "│ │"
"│ │" "│ │"
"│ │" "│ │"
"│ │"
"╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯" "╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
"╭───────────────────────── cpu 03.00% ──────────────────────────╮╭─────────────────────── memory 30.00 kB ───────────────────────╮╭────────── ports ───────────╮" "╭───────────────────────── cpu 03.00% ──────────────────────────╮╭─────────────────────── memory 30.00 kB ───────────────────────╮╭────────── ports ───────────╮"
"│10.00%│ ••• ││100.00 kB│ •• ││ ip private public│" "│10.00%│ ••• ││100.00 kB│ •• ││ ip private public│"
"│ │ •• • ││ │ •• • ││ 8001 │" "│ │ •• • ││ │ •• • ││ 8001 │"
"│ │ ••• • • ││ │ ••• • • ││127.0.0.1 8003 8003│" "│ │ ••• • • ││ │ ••• • • ││127.0.0.1 8003 8003│"
"│ │• •• ││ │• •• ││ │"
"│ │ ││ │ ││ │" "│ │ ││ │ ││ │"
"╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰────────────────────────────╯" "╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰────────────────────────────╯"
@@ -8,8 +8,6 @@ expression: setup.terminal.backend()
"│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB ││ restart │" "│ container_2 ✓ running Up 2 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB ││ restart │"
"│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB ││ stop │" "│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB ││ stop │"
"│ ││ delete │" "│ ││ delete │"
"│ ││ │"
"│ ││ │"
"╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯╰─────────────────╯" "╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯╰─────────────────╯"
"╭ Logs 3/3 - a_long_container_name_for_the_purposes_of_this_test - a_long_image_name_for_the_purposes_of_this_test ──────────────────────────────────────────────────────────────────────────╮" "╭ Logs 3/3 - a_long_container_name_for_the_purposes_of_this_test - a_long_image_name_for_the_purposes_of_this_test ──────────────────────────────────────────────────────────────────────────╮"
"│ line 1 │" "│ line 1 │"
@@ -25,10 +23,12 @@ expression: setup.terminal.backend()
"│ │" "│ │"
"│ │" "│ │"
"│ │" "│ │"
"│ │"
"╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯" "╰────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
"╭───────────────────────────────── cpu 03.00% ─────────────────────────────────╮╭────────────────────────────── memory 30.00 kB ───────────────────────────────╮╭────────── ports ───────────╮" "╭───────────────────────────────── cpu 03.00% ─────────────────────────────────╮╭────────────────────────────── memory 30.00 kB ───────────────────────────────╮╭────────── ports ───────────╮"
"│10.00%│ ••• ││100.00 kB│ •••• ││ ip private public│" "│10.00%│ ••• ││100.00 kB│ •••• ││ ip private public│"
"│ │ ••• • ││ │ •• • ││ 8001 │" "│ │ ••• • ││ │ •• • ││ 8001 │"
"│ │••• •••• ││ │••• •• ││127.0.0.1 8003 8003│" "│ │ ••• • ││ │ ••• • • ││127.0.0.1 8003 8003│"
"│ │•• ••• ││ │•• •• ││ │"
"│ │ ││ │ ││ │" "│ │ ││ │ ││ │"
"╰──────────────────────────────────────────────────────────────────────────────╯╰──────────────────────────────────────────────────────────────────────────────╯╰────────────────────────────╯" "╰──────────────────────────────────────────────────────────────────────────────╯╰──────────────────────────────────────────────────────────────────────────────╯╰────────────────────────────╯"
@@ -0,0 +1,34 @@
---
source: src/ui/draw_blocks/mod.rs
expression: setup.terminal.backend()
---
" name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show 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 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB ││ restart │"
"│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB ││ stop │"
"│ ││ delete │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯╰──────────────╯"
"╭───────────────────────── cpu 03.00% ──────────────────────────╮╭─────────────────────── memory 30.00 kB ───────────────────────╮╭────────── ports ───────────╮"
"│10.00%│ ••• ││100.00 kB│ •• ││ ip private public│"
"│ │ •• • ││ │ •• • ││ 8001 │"
"│ │ ••• • • ││ │ ••• • • ││127.0.0.1 8003 8003│"
"│ │• •• ││ │• •• ││ │"
"│ │ ││ │ ││ │"
"╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰────────────────────────────╯"
@@ -0,0 +1,34 @@
---
source: src/ui/draw_blocks/mod.rs
expression: setup.terminal.backend()
---
" name state status cpu memory/limit id image ↓ rx ↑ tx ( h ) show 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 hour 00.00% 0.00 kB / 0.00 kB 2 image_2 0.00 kB 0.00 kB ││ restart │"
"│ container_3 ✓ running Up 3 hour 00.00% 0.00 kB / 0.00 kB 3 image_3 0.00 kB 0.00 kB ││ stop │"
"│ ││ delete │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"│ ││ │"
"╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯╰──────────────╯"
"╭ Logs 3/3 - container_1 - image_1 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮"
"│ line 2 │"
"│▶ line 3 │"
"╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯"
"╭───────────────────────── cpu 03.00% ──────────────────────────╮╭─────────────────────── memory 30.00 kB ───────────────────────╮╭────────── ports ───────────╮"
"│10.00%│ ••• ││100.00 kB│ •• ││ ip private public│"
"│ │ •• • ││ │ •• • ││ 8001 │"
"│ │ ••• • • ││ │ ••• • • ││127.0.0.1 8003 8003│"
"│ │• •• ││ │• •• ││ │"
"│ │ ││ │ ││ │"
"╰───────────────────────────────────────────────────────────────╯╰───────────────────────────────────────────────────────────────╯╰────────────────────────────╯"
+82 -18
View File
@@ -9,11 +9,11 @@ use tokio::task::JoinHandle;
use uuid::Uuid; use uuid::Uuid;
use crate::{ use crate::{
app_data::{ContainerId, Header}, app_data::{AppData, ContainerId, Header},
exec::ExecMode, exec::ExecMode,
}; };
use super::Redraw; use super::Rerender;
#[derive(Debug, Default, Clone, Copy, Eq, Hash, PartialEq)] #[derive(Debug, Default, Clone, Copy, Eq, Hash, PartialEq)]
pub enum SelectablePanel { pub enum SelectablePanel {
@@ -184,13 +184,15 @@ pub struct GuiState {
loading_handle: Option<JoinHandle<()>>, loading_handle: Option<JoinHandle<()>>,
loading_index: u8, loading_index: u8,
loading_set: HashSet<Uuid>, loading_set: HashSet<Uuid>,
redraw: Arc<Redraw>, log_height: u16,
rerender: Arc<Rerender>,
selected_panel: SelectablePanel, selected_panel: SelectablePanel,
show_logs: bool,
status: HashSet<Status>, status: HashSet<Status>,
pub info_box_text: Option<(String, Instant)>, pub info_box_text: Option<(String, Instant)>,
} }
impl GuiState { impl GuiState {
pub fn new(redraw: &Arc<Redraw>) -> Self { pub fn new(redraw: &Arc<Rerender>, show_logs: bool) -> Self {
Self { Self {
delete_container: None, delete_container: None,
exec_mode: None, exec_mode: None,
@@ -202,11 +204,61 @@ impl GuiState {
loading_handle: None, loading_handle: None,
loading_index: 0, loading_index: 0,
loading_set: HashSet::new(), loading_set: HashSet::new(),
redraw: Arc::clone(redraw), log_height: 75,
rerender: Arc::clone(redraw),
selected_panel: SelectablePanel::default(), selected_panel: SelectablePanel::default(),
show_logs,
status: HashSet::new(), status: HashSet::new(),
} }
} }
/// Increase the height of the log panel, then rerender
pub fn log_height_increase(&mut self) {
if self.show_logs {
if self.log_height <= 75 {
self.log_height = self.log_height.saturating_add(5);
}
} else {
self.log_height = 5;
}
self.show_logs = true;
self.rerender.update();
}
/// Reduce the height of the logs panel, then rerender
/// Unselect logs panel if currently selected
pub fn log_height_decrease(&mut self) {
if self.show_logs {
self.log_height = self.log_height.saturating_sub(5);
if self.log_height == 0 && self.selected_panel == SelectablePanel::Logs {
self.show_logs = false;
self.selected_panel = SelectablePanel::Containers;
}
self.rerender.update();
}
}
pub const fn get_show_logs(&self) -> bool {
self.show_logs
}
pub fn toggle_show_logs(&mut self) {
self.show_logs = !self.show_logs;
if !self.show_logs && self.selected_panel == SelectablePanel::Logs {
self.selected_panel = SelectablePanel::Containers;
}
self.rerender.update();
}
/// Set the log_height to zero, used if show_logs=false in the config file
pub const fn log_height_zero(&mut self) {
self.log_height = 0;
}
/// Get the log height, *should* be a u8 between 0 and 80, essentially a percentage
pub const fn get_log_height(&self) -> u16 {
self.log_height
}
/// Clear panels hash map, so on resize can fix the sizes for mouse clicks /// Clear panels hash map, so on resize can fix the sizes for mouse clicks
pub fn clear_area_map(&mut self) { pub fn clear_area_map(&mut self) {
self.intersect_panel.clear(); self.intersect_panel.clear();
@@ -227,7 +279,7 @@ impl GuiState {
.first() .first()
{ {
self.selected_panel = *data.0; self.selected_panel = *data.0;
self.redraw.set_true(); self.rerender.update();
} }
} }
@@ -300,7 +352,7 @@ impl GuiState {
self.status_del(Status::DeleteConfirm); self.status_del(Status::DeleteConfirm);
} }
self.delete_container = id; self.delete_container = id;
self.redraw.set_true(); self.rerender.update();
} }
/// Return a copy of the Status HashSet /// Return a copy of the Status HashSet
@@ -321,7 +373,7 @@ impl GuiState {
} }
_ => (), _ => (),
} }
self.redraw.set_true(); self.rerender.update();
} }
/// Inset the ExecMode into self, and set the Status as exec /// Inset the ExecMode into self, and set the Status as exec
@@ -330,7 +382,7 @@ impl GuiState {
pub fn set_exec_mode(&mut self, mode: ExecMode) { pub fn set_exec_mode(&mut self, mode: ExecMode) {
self.exec_mode = Some(mode); self.exec_mode = Some(mode);
self.status.insert(Status::Exec); self.status.insert(Status::Exec);
self.redraw.set_true(); self.rerender.update();
} }
pub fn get_exec_mode(&self) -> Option<ExecMode> { pub fn get_exec_mode(&self) -> Option<ExecMode> {
@@ -342,20 +394,32 @@ impl GuiState {
pub fn status_push(&mut self, status: Status) { pub fn status_push(&mut self, status: Status) {
if status != Status::Exec { if status != Status::Exec {
self.status.insert(status); self.status.insert(status);
self.redraw.set_true(); self.rerender.update();
} }
} }
/// Change to next selectable panel /// Change to next selectable panel
pub fn next_panel(&mut self) { pub fn selectable_panel_next(&mut self, app_data: &Arc<Mutex<AppData>>) {
self.selected_panel = self.selected_panel.next(); self.selected_panel = self.selected_panel.next();
self.redraw.set_true(); if (app_data.lock().get_container_len() == 0
&& self.get_selected_panel() == SelectablePanel::Commands)
|| (self.log_height == 0 && self.get_selected_panel() == SelectablePanel::Logs)
{
self.selected_panel = self.selected_panel.next();
}
self.rerender.update();
} }
/// Change to previous selectable panel /// Change to previous selectable panel
pub fn previous_panel(&mut self) { pub fn selectable_panel_previous(&mut self, app_data: &Arc<Mutex<AppData>>) {
self.selected_panel = self.selected_panel.prev(); self.selected_panel = self.selected_panel.prev();
self.redraw.set_true(); if (app_data.lock().get_container_len() == 0
&& self.get_selected_panel() == SelectablePanel::Commands)
|| (self.log_height == 0 && self.get_selected_panel() == SelectablePanel::Logs)
{
self.selected_panel = self.selected_panel.prev();
}
self.rerender.update();
} }
/// Insert a new loading_uuid into HashSet, and advance the loading_index by one frame, or reset to 0 if at end of array /// Insert a new loading_uuid into HashSet, and advance the loading_index by one frame, or reset to 0 if at end of array
@@ -366,7 +430,7 @@ impl GuiState {
self.loading_index += 1; self.loading_index += 1;
} }
self.loading_set.insert(uuid); self.loading_set.insert(uuid);
self.redraw.set_true(); self.rerender.update();
} }
pub fn is_loading(&self) -> bool { pub fn is_loading(&self) -> bool {
@@ -399,7 +463,7 @@ impl GuiState {
/// Stop the loading_spin function, and reset gui loading status /// Stop the loading_spin function, and reset gui loading status
pub fn stop_loading_animation(&mut self, loading_uuid: Uuid) { pub fn stop_loading_animation(&mut self, loading_uuid: Uuid) {
self.loading_set.remove(&loading_uuid); self.loading_set.remove(&loading_uuid);
self.redraw.set_true(); self.rerender.update();
if self.loading_set.is_empty() { if self.loading_set.is_empty() {
self.loading_index = 0; self.loading_index = 0;
if let Some(h) = &self.loading_handle { if let Some(h) = &self.loading_handle {
@@ -412,12 +476,12 @@ impl GuiState {
/// Set info box content /// Set info box content
pub fn set_info_box(&mut self, text: &str) { pub fn set_info_box(&mut self, text: &str) {
self.info_box_text = Some((text.to_owned(), std::time::Instant::now())); self.info_box_text = Some((text.to_owned(), std::time::Instant::now()));
self.redraw.set_true(); self.rerender.update();
} }
/// Remove info box content /// Remove info box content
pub fn reset_info_box(&mut self) { pub fn reset_info_box(&mut self) {
self.info_box_text = None; self.info_box_text = None;
self.redraw.set_true(); self.rerender.update();
} }
} }
+72 -39
View File
@@ -24,7 +24,7 @@ mod color_match;
mod draw_blocks; mod draw_blocks;
mod gui_state; mod gui_state;
mod redraw; mod redraw;
pub use redraw::Redraw; pub use redraw::Rerender;
pub use self::color_match::*; pub use self::color_match::*;
pub use self::gui_state::{DeleteButton, GuiState, SelectablePanel, Status}; pub use self::gui_state::{DeleteButton, GuiState, SelectablePanel, Status};
@@ -50,7 +50,7 @@ pub struct Ui {
input_tx: Sender<InputMessages>, input_tx: Sender<InputMessages>,
is_running: Arc<AtomicBool>, is_running: Arc<AtomicBool>,
now: Instant, now: Instant,
redraw: Arc<Redraw>, redraw: Arc<Rerender>,
terminal: Terminal<CrosstermBackend<Stdout>>, terminal: Terminal<CrosstermBackend<Stdout>>,
} }
@@ -73,7 +73,7 @@ impl Ui {
gui_state: Arc<Mutex<GuiState>>, gui_state: Arc<Mutex<GuiState>>,
input_tx: Sender<InputMessages>, input_tx: Sender<InputMessages>,
is_running: Arc<AtomicBool>, is_running: Arc<AtomicBool>,
redraw: Arc<Redraw>, redraw: Arc<Rerender>,
) { ) {
match Self::setup_terminal() { match Self::setup_terminal() {
Ok(mut terminal) => { Ok(mut terminal) => {
@@ -205,6 +205,10 @@ impl Ui {
let docker_interval_ms = u128::from(self.app_data.lock().config.docker_interval_ms); let docker_interval_ms = u128::from(self.app_data.lock().config.docker_interval_ms);
let mut drawn_at = std::time::Instant::now(); let mut drawn_at = std::time::Instant::now();
if !self.app_data.lock().config.show_logs {
self.gui_state.lock().log_height_zero();
}
while self.is_running.load(Ordering::SeqCst) { while self.is_running.load(Ordering::SeqCst) {
if self.should_redraw(&mut drawn_at, docker_interval_ms) { if self.should_redraw(&mut drawn_at, docker_interval_ms) {
let fd = FrameData::from(&*self); let fd = FrameData::from(&*self);
@@ -245,6 +249,8 @@ impl Ui {
} }
} else if let Event::Resize(_, _) = event { } else if let Event::Resize(_, _) = event {
self.gui_state.lock().clear_area_map(); self.gui_state.lock().clear_area_map();
// self.gui_state.lock().set_window_height(row);
self.terminal.autoresize().ok(); self.terminal.autoresize().ok();
} }
} }
@@ -267,6 +273,7 @@ impl Ui {
/// Frequent data required by multiple frame drawing functions, can reduce mutex reads by placing it all in here /// Frequent data required by multiple frame drawing functions, can reduce mutex reads by placing it all in here
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
#[allow(clippy::struct_excessive_bools)]
pub struct FrameData { pub struct FrameData {
chart_data: Option<(CpuTuple, MemTuple)>, chart_data: Option<(CpuTuple, MemTuple)>,
color_logs: bool, color_logs: bool,
@@ -276,8 +283,10 @@ pub struct FrameData {
filter_by: FilterBy, filter_by: FilterBy,
filter_term: Option<String>, filter_term: Option<String>,
has_containers: bool, has_containers: bool,
// container_section_height: u16,
log_height: u16,
show_logs: bool,
has_error: Option<AppError>, has_error: Option<AppError>,
height: u16,
info_text: Option<(String, Instant)>, info_text: Option<(String, Instant)>,
is_loading: bool, is_loading: bool,
loading_icon: String, loading_icon: String,
@@ -293,29 +302,36 @@ impl From<&Ui> for FrameData {
fn from(ui: &Ui) -> Self { fn from(ui: &Ui) -> Self {
let (app_data, gui_data) = (ui.app_data.lock(), ui.gui_state.lock()); let (app_data, gui_data) = (ui.app_data.lock(), ui.gui_state.lock());
// set a flag if the - or = button has been pressed, and if so use that calc, else use this calc
// set max height for container section, needs +5 to deal with docker commands list and borders // set max height for container section, needs +5 to deal with docker commands list and borders
let height = app_data.get_container_len(); // TODO fix this
let height = if height < 12 { // let container_len = app_data.get_container_len();
u16::try_from(height + 5).unwrap_or_default() // let container_section_height = if container_len < 12 {
} else { // // u16::try_from(container_len + 5).unwrap_or_default()
12 // 8
}; // } else {
// 12
// };
// TODO work out what to do with this
// container_section_height = 8;
let (filter_by, filter_term) = app_data.get_filter(); let (filter_by, filter_term) = app_data.get_filter();
Self { Self {
chart_data: app_data.get_chart_data(), chart_data: app_data.get_chart_data(),
color_logs: app_data.config.color_logs, color_logs: app_data.config.color_logs,
columns: app_data.get_width(), columns: app_data.get_width(),
// container_section_height,
container_title: app_data.get_container_title(), container_title: app_data.get_container_title(),
delete_confirm: gui_data.get_delete_container(), delete_confirm: gui_data.get_delete_container(),
filter_by, filter_by,
filter_term: filter_term.cloned(), filter_term: filter_term.cloned(),
has_containers: app_data.get_container_len() > 0, has_containers: app_data.get_container_len() > 0,
has_error: app_data.get_error(), has_error: app_data.get_error(),
height,
info_text: gui_data.info_box_text.clone(), info_text: gui_data.info_box_text.clone(),
is_loading: gui_data.is_loading(), is_loading: gui_data.is_loading(),
show_logs: gui_data.get_show_logs(),
loading_icon: gui_data.get_loading().to_string(), loading_icon: gui_data.get_loading().to_string(),
log_height: gui_data.get_log_height(),
log_title: app_data.get_log_title(), log_title: app_data.get_log_title(),
port_max_lens: app_data.get_longest_port(), port_max_lens: app_data.get_longest_port(),
ports: app_data.get_selected_ports(), ports: app_data.get_selected_ports(),
@@ -346,44 +362,61 @@ fn draw_frame(
.constraints(whole_constraints) .constraints(whole_constraints)
.split(f.area()); .split(f.area());
// Split into 3, containers+controls, logs, then graphs draw_blocks::headers::draw(whole_layout[0], colors, f, fd, gui_state, keymap);
// If required, draw filter bar
if let Some(rect) = whole_layout.get(2) {
draw_blocks::filter::draw(*rect, colors, f, fd);
}
// What we should do is work out the container+logs size, and then set the percentage to height - 6
let container_logs_section_constraints = if fd.show_logs {
vec![Constraint::Min(6), Constraint::Percentage(fd.log_height)]
} else {
vec![Constraint::Percentage(100)]
};
let upper_main_constraints = if fd.has_containers {
vec![Constraint::Percentage(75), Constraint::Percentage(25)]
} else {
vec![Constraint::Percentage(100), Constraint::Percentage(0)]
};
let upper_main = Layout::default() let upper_main = Layout::default()
.direction(Direction::Vertical) .direction(Direction::Vertical)
.constraints([Constraint::Max(fd.height), Constraint::Min(1)].as_ref()) .constraints(upper_main_constraints)
.split(whole_layout[1]); .split(whole_layout[1]);
let top_split = if fd.has_containers { let containers_logs_section = Layout::default()
.direction(Direction::Vertical)
.constraints(container_logs_section_constraints)
.split(upper_main[0]);
// Containers & Command Horizontal split
let container_command_constraints = if fd.has_containers {
vec![Constraint::Percentage(90), Constraint::Percentage(10)] vec![Constraint::Percentage(90), Constraint::Percentage(10)]
} else { } else {
vec![Constraint::Percentage(100)] vec![Constraint::Percentage(100)]
}; };
// Containers + docker commands // Containers + docker commands
let top_panel = Layout::default() let containers_commands = Layout::default()
.direction(Direction::Horizontal) .direction(Direction::Horizontal)
.constraints(top_split) .constraints(container_command_constraints)
.split(upper_main[0]); .split(containers_logs_section[0]);
let lower_split = if fd.has_containers { draw_blocks::containers::draw(app_data, containers_commands[0], colors, f, fd, gui_state);
vec![Constraint::Percentage(70), Constraint::Percentage(30)]
} else {
vec![Constraint::Percentage(100)]
};
// Split into 2, logs and charts // TODO redraw logs
let lower_main = Layout::default() if fd.show_logs {
.direction(Direction::Vertical) draw_blocks::logs::draw(
.constraints(lower_split) app_data,
.split(upper_main[1]); containers_logs_section[1],
colors,
draw_blocks::containers::draw(app_data, top_panel[0], colors, f, fd, gui_state); f,
fd,
draw_blocks::logs::draw(app_data, lower_main[0], colors, f, fd, gui_state); gui_state,
);
draw_blocks::headers::draw(whole_layout[0], colors, f, fd, gui_state, keymap);
// Draw filter bar
if let Some(rect) = whole_layout.get(2) {
draw_blocks::filter::draw(*rect, colors, f, fd);
} }
if let Some(id) = fd.delete_confirm.as_ref() { if let Some(id) = fd.delete_confirm.as_ref() {
@@ -400,7 +433,7 @@ fn draw_frame(
} }
// only draw commands + charts if there are containers // only draw commands + charts if there are containers
if let Some(rect) = top_panel.get(1) { if let Some(rect) = containers_commands.get(1) {
draw_blocks::commands::draw(app_data, *rect, colors, f, fd, gui_state); draw_blocks::commands::draw(app_data, *rect, colors, f, fd, gui_state);
// Can calculate the max string length here, and then use that to keep the ports section as small as possible (+4 for some padding + border) // Can calculate the max string length here, and then use that to keep the ports section as small as possible (+4 for some padding + border)
@@ -411,7 +444,7 @@ fn draw_frame(
let lower = Layout::default() let lower = Layout::default()
.direction(Direction::Horizontal) .direction(Direction::Horizontal)
.constraints([Constraint::Min(1), Constraint::Max(ports_len)]) .constraints([Constraint::Min(1), Constraint::Max(ports_len)])
.split(lower_main[1]); .split(upper_main[1]);
draw_blocks::charts::draw(lower[0], colors, f, fd); draw_blocks::charts::draw(lower[0], colors, f, fd);
draw_blocks::ports::draw(lower[1], colors, f, fd); draw_blocks::ports::draw(lower[1], colors, f, fd);
+3 -3
View File
@@ -1,14 +1,14 @@
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
#[derive(Debug)] #[derive(Debug)]
pub struct Redraw(AtomicBool); pub struct Rerender(AtomicBool);
impl Redraw { impl Rerender {
pub const fn new() -> Self { pub const fn new() -> Self {
Self(AtomicBool::new(true)) Self(AtomicBool::new(true))
} }
pub fn set_true(&self) { pub fn update(&self) {
self.0.store(true, Ordering::SeqCst); self.0.store(true, Ordering::SeqCst);
} }