feat: filter panel colors

This commit is contained in:
Jack Wills
2025-02-21 22:00:12 +00:00
parent 893e6fcef4
commit f90e831239
7 changed files with 184 additions and 27 deletions
+7
View File
@@ -64,6 +64,13 @@
"text_rx": "#FFE9C1",
"text_tx": "#CD8C8C"
},
"filter": {
"background": "reset",
"text": "gray",
"selected_filter_background": "gray",
"selected_filter_text": "black",
"highlight": "magenta"
},
"headers_bar": {
"background": "magenta",
"loading_spinner": "white",
+13
View File
@@ -251,6 +251,19 @@
// Ports & IP listing text
"text": "white"
},
// The filter panel
"filter": {
// Background color of panel
"background": "reset",
// color of text
"text": "gray",
// background color of the selected filter by item (Name/Image/Status/All)
"selected_filter_background": "gray",
// text color of the selected filter by item (Name/Image/Status/All)
"selected_filter_text": "black",
// Highlighted text color
"highlight": "magenta"
},
// The logs panel, will only be applied if color_logs is false
"logs": {
// Background color of panel
+14
View File
@@ -163,6 +163,20 @@ running_healthy ="green"
running_unhealthy="#FFB224"
unknown="red"
# The filter panel
[colors.filter]
# Background color of panel
background = "reset"
# color of text
text="gray"
# background color of the selected filter by item (Name/Image/Status/All)
selected_filter_background="gray"
# text color of the selected filter by item (Name/Image/Status/All)
selected_filter_text="black"
# Highlighted text color
highlight="magenta"
# The color the of Docker commands available for each container
[colors.commands]
# Background color of panel
+34
View File
@@ -73,6 +73,22 @@ impl From<Option<ConfigColors>> for AppColors {
Self::map_color(ep.text.as_deref(), &mut app_colors.popup_error.text);
}
// Filter panel
if let Some(fc) = config_colors.filter {
Self::map_color(fc.background.as_deref(), &mut app_colors.filter.background);
Self::map_color(fc.highlight.as_deref(), &mut app_colors.filter.highlight);
Self::map_color(
fc.selected_filter_background.as_deref(),
&mut app_colors.filter.selected_filter_background,
);
Self::map_color(
fc.selected_filter_text.as_deref(),
&mut app_colors.filter.selected_filter_text,
);
Self::map_color(fc.text.as_deref(), &mut app_colors.filter.text);
}
// Help Popup
if let Some(hp) = config_colors.popup_help {
Self::map_color(
@@ -221,6 +237,7 @@ optional_config_struct!(
ConfigCommands, background, pause, restart, stop, delete, resume, start;
ConfigContainers, background, icon, text, text_rx, text_tx;
ConfigContainerState, background, dead, exited, paused, removing, restarting, running_healthy, running_unhealthy, unknown;
ConfigFilter, background, text, selected_filter_background, selected_filter_text, highlight;
ConfigHeadersBar, background, loading_spinner, text, text_selected;
ConfigLogs, background, text
);
@@ -233,6 +250,7 @@ config_struct!(
Commands, background, pause, restart, stop, delete, resume, start;
Containers, background, icon, text, text_rx, text_tx;
ContainerState, dead, exited, paused, removing, restarting, running_healthy, running_unhealthy, unknown;
Filter, background, text, selected_filter_background, selected_filter_text, highlight;
HeadersBar, background, text_selected, loading_spinner, text;
Logs, background, text;
PopupDelete, background, text, text_highlight;
@@ -250,6 +268,7 @@ pub struct ConfigColors {
commands: Option<ConfigCommands>,
container_state: Option<ConfigContainerState>,
containers: Option<ConfigContainers>,
filter: Option<ConfigFilter>,
headers_bar: Option<ConfigHeadersBar>,
logs: Option<ConfigLogs>,
popup_delete: Option<ConfigBackgroundTextHighlight>,
@@ -365,6 +384,19 @@ impl ContainerState {
}
}
/// Default colours for the filter panel
impl Filter {
const fn new() -> Self {
Self {
background: Color::Reset,
highlight: Color::Magenta,
selected_filter_background: Color::Gray,
selected_filter_text: Color::Black,
text: Color::Gray,
}
}
}
/// Default colours for the logs panel, only applied if color_logs is false
impl Logs {
const fn new() -> Self {
@@ -426,6 +458,7 @@ pub struct AppColors {
pub commands: Commands,
pub container_state: ContainerState,
pub containers: Containers,
pub filter: Filter,
pub headers_bar: HeadersBar,
pub logs: Logs,
pub popup_delete: PopupDelete,
@@ -444,6 +477,7 @@ impl AppColors {
commands: Commands::new(),
container_state: ContainerState::new(),
containers: Containers::new(),
filter: Filter::new(),
headers_bar: HeadersBar::new(),
logs: Logs::new(),
popup_delete: PopupDelete::new(),
+15 -1
View File
@@ -30,7 +30,7 @@ host = "/var/run/docker.sock"
# Display the container logs timestamp with a given timezone, if timezone is unknown, defaults to UTC
timezone = "Etc/UTC"
# Display the timestamp in a custom format, if given option is invalid, it will default to %Y-%m-%dT%H:%M:%S.%8f -> 2025-02-18T12:34:56.01234567
# Display the timestamp in a custom format, if given option is invalid, it will default to %Y-%m-%dT%H:%M:%S.%8f -> 2025-02-18T12:34:56.012345678Z
# *Should* accept any valid strftime string up to 32 chars
timestamp_format="%Y-%m-%dT%H:%M:%S.%8f"
@@ -163,6 +163,20 @@ running_healthy ="green"
running_unhealthy="#FFB224"
unknown="red"
# The filter panel
[colors.filter]
# Background color of panel
background = "reset"
# color of text
text="gray"
# background color of the selected filter by item (Name/Image/Status/All)
selected_filter_background="gray"
# text color of the selected filter by item (Name/Image/Status/All)
selected_filter_text="black"
# Highlighted text color
highlight="magenta"
# The color the of Docker commands available for each container
[colors.commands]
# Background color of panel
+96 -21
View File
@@ -1,16 +1,20 @@
use ratatui::{
layout::Rect,
style::{Color, Modifier, Style},
style::{Modifier, Style, Stylize},
text::{Line, Span},
Frame,
};
use crate::{app_data::FilterBy, ui::FrameData};
use crate::{app_data::FilterBy, config::AppColors, ui::FrameData};
/// Create the filter_by by spans, coloured dependant on which one is selected
fn filter_by_spans(fd: &FrameData) -> [Span; 4] {
let selected = Style::default().bg(Color::Gray).fg(Color::Black);
let not_selected = Style::default().bg(Color::Reset).fg(Color::Reset);
fn filter_by_spans(colors: AppColors, fd: &FrameData) -> [Span; 4] {
let selected = Style::default()
.bg(colors.filter.selected_filter_background)
.fg(colors.filter.selected_filter_text);
let not_selected = Style::default()
.bg(colors.filter.background)
.fg(colors.filter.text);
let name = [" Name ", " Image ", " Status ", " All "];
@@ -31,9 +35,13 @@ fn filter_by_spans(fd: &FrameData) -> [Span; 4] {
}
/// Draw the filter bar
pub fn draw(area: Rect, frame: &mut Frame, fd: &FrameData) {
let style_but = Style::default().fg(Color::Black).bg(Color::Magenta);
let style_desc = Style::default().fg(Color::Gray).bg(Color::Reset);
pub fn draw(area: Rect, colors: AppColors, frame: &mut Frame, fd: &FrameData) {
let style_but = Style::default()
.fg(colors.filter.selected_filter_text)
.bg(colors.filter.highlight);
let style_desc = Style::default()
.fg(colors.filter.text)
.bg(colors.filter.background);
let mut line = vec![
Span::styled(" Esc ", style_but),
@@ -41,22 +49,22 @@ pub fn draw(area: Rect, frame: &mut Frame, fd: &FrameData) {
Span::styled(" ← by → ", style_but),
Span::from(" "),
];
line.extend_from_slice(&filter_by_spans(fd));
line.extend_from_slice(&filter_by_spans(colors, fd));
line.extend_from_slice(&[
Span::styled(
" term: ",
Style::default()
.fg(Color::Magenta)
.fg(colors.filter.highlight)
.add_modifier(Modifier::BOLD),
),
Span::styled(
fd.filter_term
.as_ref()
.map_or(String::new(), std::clone::Clone::clone),
Style::default().fg(Color::Gray),
Style::default().fg(colors.filter.text),
),
]);
frame.render_widget(Line::from(line), area);
frame.render_widget(Line::from(line).bg(colors.filter.background), area);
}
#[cfg(test)]
@@ -65,9 +73,12 @@ mod tests {
use ratatui::style::{Color, Modifier};
use crate::ui::{
use crate::{
config::AppColors,
ui::{
draw_blocks::tests::{expected_to_vec, get_result, test_setup},
FrameData,
},
};
#[test]
@@ -85,7 +96,7 @@ mod tests {
setup
.terminal
.draw(|f| {
super::draw(setup.area, f, &setup.fd);
super::draw(setup.area, AppColors::new(), f, &setup.fd);
})
.unwrap();
@@ -97,12 +108,13 @@ mod tests {
let expected_row = expected_to_vec(&expected, row_index);
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
match result_cell_index {
0..=4 | 12..=19 => {
assert_eq!(result_cell.bg, Color::Magenta);
assert_eq!(result_cell.fg, Color::Black);
}
5..=11 => {
5..=11 | 27..=46 => {
assert_eq!(result_cell.bg, Color::Reset);
assert_eq!(result_cell.fg, Color::Gray);
}
@@ -131,7 +143,7 @@ mod tests {
setup
.terminal
.draw(|f| {
super::draw(setup.area, f, &fd);
super::draw(setup.area, AppColors::new(), f, &fd);
})
.unwrap();
@@ -149,7 +161,7 @@ mod tests {
assert_eq!(result_cell.bg, Color::Magenta);
assert_eq!(result_cell.fg, Color::Black);
}
5..=11 | 54..=55 => {
5..=11 | 27..=46 | 54..=55 => {
assert_eq!(result_cell.bg, Color::Reset);
assert_eq!(result_cell.fg, Color::Gray);
}
@@ -170,13 +182,13 @@ mod tests {
}
}
// Test when filter_by chances
// Test when filter_by changes
setup.app_data.lock().filter_by_next();
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
setup
.terminal
.draw(|f| {
super::draw(setup.area, f, &fd);
super::draw(setup.area, AppColors::new(), f, &fd);
})
.unwrap();
@@ -188,13 +200,12 @@ mod tests {
let expected_row = expected_to_vec(&expected, row_index);
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
match result_cell_index {
0..=4 | 12..=19 => {
assert_eq!(result_cell.bg, Color::Magenta);
assert_eq!(result_cell.fg, Color::Black);
}
5..=11 | 54..=55 => {
5..=11 | 21..=26 | 34..=46 | 54..=55 => {
assert_eq!(result_cell.bg, Color::Reset);
assert_eq!(result_cell.fg, Color::Gray);
}
@@ -215,4 +226,68 @@ mod tests {
}
}
}
#[test]
/// Make sure custom colors are applied
fn test_draw_blocks_filter_row_custom_colors() {
let (w, h) = (140, 1);
let mut setup = test_setup(w, h, true, true);
setup
.gui_state
.lock()
.status_push(crate::ui::Status::Filter);
setup.app_data.lock().filter_term_push('c');
setup.app_data.lock().filter_term_push('d');
let fd = FrameData::from((&setup.app_data, &setup.gui_state));
let mut colors = AppColors::new();
colors.filter.background = Color::White;
colors.filter.highlight = Color::Blue;
colors.filter.selected_filter_background = Color::Red;
colors.filter.selected_filter_text = Color::Yellow;
colors.filter.text = Color::Magenta;
setup
.terminal
.draw(|f| {
super::draw(setup.area, colors, f, &fd);
})
.unwrap();
let expected = [
" Esc clear ← by → Name Image Status All term: cd "
];
for (row_index, result_row) in get_result(&setup, w) {
let expected_row = expected_to_vec(&expected, row_index);
for (result_cell_index, result_cell) in result_row.iter().enumerate() {
assert_eq!(result_cell.symbol(), expected_row[result_cell_index]);
match result_cell_index {
0..=4 | 12..=19 => {
assert_eq!(result_cell.bg, Color::Blue);
assert_eq!(result_cell.fg, Color::Yellow);
}
5..=11 | 27..=46 | 54..=55 => {
assert_eq!(result_cell.bg, Color::White);
assert_eq!(result_cell.fg, Color::Magenta);
}
21..=26 => {
assert_eq!(result_cell.bg, Color::Red);
assert_eq!(result_cell.fg, Color::Yellow);
}
47..=53 => {
assert_eq!(result_cell.bg, Color::White);
assert_eq!(result_cell.fg, Color::Blue);
assert_eq!(result_cell.modifier, Modifier::BOLD);
}
_ => {
assert_eq!(result_cell.bg, Color::White);
assert_eq!(result_cell.fg, Color::Reset);
}
}
}
}
}
}
+1 -1
View File
@@ -380,7 +380,7 @@ fn draw_frame(
// Draw filter bar
if let Some(rect) = whole_layout.get(2) {
draw_blocks::filter::draw(*rect, f, fd);
draw_blocks::filter::draw(*rect, colors, f, fd);
}
if let Some(id) = fd.delete_confirm.as_ref() {