Upgraded to Ratatui 0.26 and did a partial refactor to clean up the UI module. Created the ManagarrStyle trait to make it cleaner to use consistent styles across the project. Still need to update the layouts to be consistent with the newer and nicer format. That's a tomorrow problem
This commit is contained in:
+54
-74
@@ -1,9 +1,9 @@
|
||||
use std::iter;
|
||||
use std::rc::Rc;
|
||||
|
||||
use ratatui::layout::{Alignment, Constraint, Rect};
|
||||
use ratatui::style::Modifier;
|
||||
use ratatui::text::{Line, Span, Text};
|
||||
use ratatui::layout::{Alignment, Constraint, Layout, Rect};
|
||||
use ratatui::style::{Modifier, Style, Stylize};
|
||||
use ratatui::text::{Line, Text};
|
||||
use ratatui::widgets::Paragraph;
|
||||
use ratatui::widgets::Row;
|
||||
use ratatui::widgets::Table;
|
||||
@@ -15,16 +15,16 @@ use ratatui::Frame;
|
||||
use crate::app::App;
|
||||
use crate::models::{HorizontallyScrollableText, Route, StatefulList, StatefulTable, TabState};
|
||||
use crate::ui::radarr_ui::RadarrUi;
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::utils::{
|
||||
background_block, borderless_block, centered_rect, horizontal_chunks,
|
||||
horizontal_chunks_with_margin, layout_block, layout_block_top_border, layout_button_paragraph,
|
||||
layout_button_paragraph_borderless, layout_paragraph_borderless, logo_block, show_cursor,
|
||||
style_block_highlight, style_default, style_default_bold, style_failure, style_help,
|
||||
style_highlight, style_primary, style_secondary, style_system_function, title_block,
|
||||
title_block_centered, vertical_chunks_with_margin,
|
||||
style_block_highlight, title_block, title_block_centered, vertical_chunks_with_margin,
|
||||
};
|
||||
|
||||
mod radarr_ui;
|
||||
mod styles;
|
||||
mod utils;
|
||||
|
||||
static HIGHLIGHT_SYMBOL: &str = "=> ";
|
||||
@@ -37,59 +37,53 @@ pub trait DrawUi {
|
||||
|
||||
pub fn ui(f: &mut Frame<'_>, app: &mut App<'_>) {
|
||||
f.render_widget(background_block(), f.size());
|
||||
let main_chunks = if !app.error.text.is_empty() {
|
||||
let chunks = vertical_chunks_with_margin(
|
||||
vec![
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(10),
|
||||
Constraint::Length(0),
|
||||
],
|
||||
f.size(),
|
||||
1,
|
||||
);
|
||||
let [header, context, table] = if !app.error.text.is_empty() {
|
||||
let [header, error, context, table] = Layout::vertical([
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(10),
|
||||
Constraint::Fill(1),
|
||||
])
|
||||
.margin(1)
|
||||
.areas(f.size());
|
||||
|
||||
draw_error(f, app, chunks[1]);
|
||||
draw_error(f, app, error);
|
||||
|
||||
Rc::new([chunks[0], chunks[2], chunks[3]])
|
||||
[header, context, table]
|
||||
} else {
|
||||
vertical_chunks_with_margin(
|
||||
vec![
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(10),
|
||||
Constraint::Length(0),
|
||||
],
|
||||
f.size(),
|
||||
1,
|
||||
)
|
||||
Layout::vertical([
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(10),
|
||||
Constraint::Fill(1),
|
||||
])
|
||||
.margin(1)
|
||||
.areas(f.size())
|
||||
};
|
||||
|
||||
draw_header_row(f, app, main_chunks[0]);
|
||||
draw_header_row(f, app, header);
|
||||
|
||||
if RadarrUi::accepts(*app.get_current_route()) {
|
||||
RadarrUi::draw_context_row(f, app, main_chunks[1]);
|
||||
RadarrUi::draw(f, app, main_chunks[2]);
|
||||
RadarrUi::draw_context_row(f, app, context);
|
||||
RadarrUi::draw(f, app, table);
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_header_row(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
let chunks =
|
||||
horizontal_chunks_with_margin(vec![Constraint::Length(75), Constraint::Min(0)], area, 1);
|
||||
let help_text = Text::from(app.server_tabs.get_active_tab_help());
|
||||
let help_text = Text::from(app.server_tabs.get_active_tab_help().help());
|
||||
|
||||
let titles = app
|
||||
.server_tabs
|
||||
.tabs
|
||||
.iter()
|
||||
.map(|tab| Line::from(Span::styled(tab.title, style_default_bold())))
|
||||
.collect();
|
||||
.map(|tab| Line::from(tab.title.bold()));
|
||||
let tabs = Tabs::new(titles)
|
||||
.block(logo_block())
|
||||
.highlight_style(style_secondary())
|
||||
.highlight_style(Style::new().secondary())
|
||||
.select(app.server_tabs.index);
|
||||
let help = Paragraph::new(help_text)
|
||||
.block(borderless_block())
|
||||
.style(style_help())
|
||||
.alignment(Alignment::Right);
|
||||
|
||||
f.render_widget(tabs, area);
|
||||
@@ -97,8 +91,7 @@ fn draw_header_row(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
}
|
||||
|
||||
fn draw_error(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
let block =
|
||||
title_block("Error | <esc> to close").style(style_failure().add_modifier(Modifier::BOLD));
|
||||
let block = title_block("Error | <esc> to close").failure().bold();
|
||||
|
||||
app.error.scroll_left_or_reset(
|
||||
area.width as usize,
|
||||
@@ -106,13 +99,10 @@ fn draw_error(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
app.tick_count % app.ticks_until_scroll == 0,
|
||||
);
|
||||
|
||||
let mut text = Text::from(app.error.to_string());
|
||||
text.patch_style(style_failure());
|
||||
|
||||
let paragraph = Paragraph::new(text)
|
||||
let paragraph = Paragraph::new(Text::from(app.error.to_string()).failure())
|
||||
.block(block)
|
||||
.wrap(Wrap { trim: true })
|
||||
.style(style_primary());
|
||||
.primary();
|
||||
|
||||
f.render_widget(paragraph, area);
|
||||
}
|
||||
@@ -245,8 +235,9 @@ pub fn draw_error_popup(f: &mut Frame<'_>, message: &str) {
|
||||
f.render_widget(background_block(), prompt_area);
|
||||
|
||||
let error_message = Paragraph::new(Text::from(message))
|
||||
.block(title_block_centered("Error").style(style_failure()))
|
||||
.style(style_failure().add_modifier(Modifier::BOLD))
|
||||
.block(title_block_centered("Error").failure())
|
||||
.failure()
|
||||
.bold()
|
||||
.wrap(Wrap { trim: false })
|
||||
.alignment(Alignment::Center);
|
||||
|
||||
@@ -262,24 +253,21 @@ fn draw_tabs<'a>(
|
||||
let chunks =
|
||||
vertical_chunks_with_margin(vec![Constraint::Length(2), Constraint::Min(0)], area, 1);
|
||||
let horizontal_chunks = horizontal_chunks_with_margin(
|
||||
vec![Constraint::Percentage(10), Constraint::Min(0)],
|
||||
vec![Constraint::Percentage(10), Constraint::Fill(1)],
|
||||
area,
|
||||
1,
|
||||
);
|
||||
let block = title_block(title);
|
||||
let mut help_text = Text::from(tab_state.get_active_tab_help());
|
||||
help_text.patch_style(style_help());
|
||||
|
||||
let titles = tab_state
|
||||
.tabs
|
||||
.iter()
|
||||
.map(|tab_route| Line::from(Span::styled(tab_route.title, style_default_bold())))
|
||||
.collect();
|
||||
.map(|tab_route| Line::from(tab_route.title.bold()));
|
||||
let tabs = Tabs::new(titles)
|
||||
.block(block)
|
||||
.highlight_style(style_secondary())
|
||||
.highlight_style(Style::new().secondary())
|
||||
.select(tab_state.index);
|
||||
let help = Paragraph::new(help_text)
|
||||
let help = Paragraph::new(Text::from(tab_state.get_active_tab_help().help()))
|
||||
.block(borderless_block())
|
||||
.alignment(Alignment::Right);
|
||||
|
||||
@@ -369,15 +357,13 @@ fn draw_table_contents<'a, T, F>(
|
||||
{
|
||||
let rows = content.items.iter().map(row_mapper);
|
||||
|
||||
let headers = Row::new(table_headers)
|
||||
.style(style_default_bold())
|
||||
.bottom_margin(0);
|
||||
let headers = Row::new(table_headers).default().bold().bottom_margin(0);
|
||||
|
||||
let mut table = Table::new(rows, &constraints).header(headers).block(block);
|
||||
|
||||
if highlight {
|
||||
table = table
|
||||
.highlight_style(style_highlight())
|
||||
.highlight_style(Style::new().highlight())
|
||||
.highlight_symbol(HIGHLIGHT_SYMBOL);
|
||||
}
|
||||
|
||||
@@ -386,12 +372,8 @@ fn draw_table_contents<'a, T, F>(
|
||||
|
||||
pub fn loading(f: &mut Frame<'_>, block: Block<'_>, area: Rect, is_loading: bool) {
|
||||
if is_loading {
|
||||
let text = "\n\n Loading ...\n\n".to_owned();
|
||||
let mut text = Text::from(text);
|
||||
text.patch_style(style_system_function());
|
||||
|
||||
let paragraph = Paragraph::new(text)
|
||||
.style(style_system_function())
|
||||
let paragraph = Paragraph::new(Text::from("\n\n Loading ...\n\n"))
|
||||
.system_function()
|
||||
.block(block);
|
||||
f.render_widget(paragraph, area);
|
||||
} else {
|
||||
@@ -538,7 +520,7 @@ pub fn draw_checkbox_with_label(
|
||||
let label_paragraph = Paragraph::new(Text::from(format!("\n{label}: ")))
|
||||
.block(borderless_block())
|
||||
.alignment(Alignment::Right)
|
||||
.style(style_primary());
|
||||
.primary();
|
||||
|
||||
f.render_widget(label_paragraph, horizontal_chunks[0]);
|
||||
|
||||
@@ -598,7 +580,7 @@ pub fn draw_drop_down_menu_button(
|
||||
let description_paragraph = Paragraph::new(Text::from(format!("\n{description}: ")))
|
||||
.block(borderless_block())
|
||||
.alignment(Alignment::Right)
|
||||
.style(style_primary());
|
||||
.primary();
|
||||
|
||||
f.render_widget(description_paragraph, horizontal_chunks[0]);
|
||||
|
||||
@@ -614,7 +596,7 @@ pub fn draw_selectable_list<'a, T>(
|
||||
let items: Vec<ListItem<'_>> = content.items.iter().map(item_mapper).collect();
|
||||
let list = List::new(items)
|
||||
.block(layout_block())
|
||||
.highlight_style(style_highlight());
|
||||
.highlight_style(Style::new().highlight());
|
||||
|
||||
f.render_stateful_widget(list, area, &mut content.state);
|
||||
}
|
||||
@@ -648,7 +630,7 @@ pub fn draw_list_box<'a, T>(
|
||||
let mut list = List::new(items).block(block);
|
||||
|
||||
if is_popup {
|
||||
list = list.highlight_style(style_highlight());
|
||||
list = list.highlight_style(Style::new().highlight());
|
||||
}
|
||||
|
||||
f.render_stateful_widget(list, content_area, &mut content.state);
|
||||
@@ -662,9 +644,7 @@ fn draw_help_and_get_content_rect(f: &mut Frame<'_>, area: Rect, help: Option<St
|
||||
let chunks =
|
||||
vertical_chunks_with_margin(vec![Constraint::Min(0), Constraint::Length(2)], area, 1);
|
||||
|
||||
let mut help_test = Text::from(format!(" {help_string}"));
|
||||
help_test.patch_style(style_help());
|
||||
let help_paragraph = Paragraph::new(help_test)
|
||||
let help_paragraph = Paragraph::new(Text::from(format!(" {help_string}").help()))
|
||||
.block(layout_block_top_border())
|
||||
.alignment(Alignment::Left);
|
||||
|
||||
@@ -697,12 +677,12 @@ pub fn draw_text_box(f: &mut Frame<'_>, text_box_props: TextBoxProps<'_>) {
|
||||
cursor_after_string,
|
||||
} = text_box_props;
|
||||
let (block, style) = if let Some(title) = block_title {
|
||||
(title_block_centered(title), style_default())
|
||||
(title_block_centered(title), Style::new().default())
|
||||
} else {
|
||||
(
|
||||
layout_block(),
|
||||
if should_show_cursor {
|
||||
style_default()
|
||||
Style::new().default()
|
||||
} else {
|
||||
style_block_highlight(is_selected)
|
||||
},
|
||||
@@ -753,7 +733,7 @@ pub fn draw_text_box_with_label(
|
||||
let label_paragraph = Paragraph::new(Text::from(format!("\n{label}: ")))
|
||||
.block(borderless_block())
|
||||
.alignment(Alignment::Right)
|
||||
.style(style_primary());
|
||||
.primary();
|
||||
|
||||
f.render_widget(label_paragraph, horizontal_chunks[0]);
|
||||
|
||||
@@ -801,7 +781,7 @@ pub fn draw_input_box_popup(
|
||||
);
|
||||
|
||||
let help = Paragraph::new("<esc> cancel")
|
||||
.style(style_help())
|
||||
.help()
|
||||
.alignment(Alignment::Center)
|
||||
.block(borderless_block());
|
||||
f.render_widget(help, chunks[1]);
|
||||
@@ -809,7 +789,7 @@ pub fn draw_input_box_popup(
|
||||
|
||||
pub fn draw_error_message_popup(f: &mut Frame<'_>, error_message_area: Rect, error_msg: &str) {
|
||||
let input = Paragraph::new(error_msg)
|
||||
.style(style_failure())
|
||||
.failure()
|
||||
.alignment(Alignment::Center)
|
||||
.block(layout_block());
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use ratatui::layout::{Alignment, Constraint, Rect};
|
||||
use ratatui::text::Text;
|
||||
use ratatui::style::Stylize;
|
||||
use ratatui::text::{Line, Text};
|
||||
use ratatui::widgets::{Cell, Paragraph, Row, Wrap};
|
||||
use ratatui::Frame;
|
||||
|
||||
@@ -12,10 +13,10 @@ use crate::models::servarr_data::radarr::radarr_data::{
|
||||
};
|
||||
use crate::models::Route;
|
||||
use crate::ui::radarr_ui::collections::draw_collections;
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::utils::{
|
||||
borderless_block, get_width_from_percentage, layout_block_top_border_with_title,
|
||||
line_info_primary, style_default, style_help, style_primary, title_block, title_style,
|
||||
vertical_chunks_with_margin,
|
||||
borderless_block, get_width_from_percentage, layout_block_top_border_with_title, title_block,
|
||||
title_style, vertical_chunks_with_margin,
|
||||
};
|
||||
use crate::ui::{draw_large_popup_over, draw_small_popup_over, draw_table, DrawUi, TableProps};
|
||||
use crate::utils::convert_runtime;
|
||||
@@ -98,11 +99,13 @@ pub fn draw_collection_details(f: &mut Frame<'_>, app: &mut App<'_>, content_are
|
||||
.current_selection()
|
||||
.clone()
|
||||
};
|
||||
let mut help_text = Text::from(format!(
|
||||
"<↑↓> scroll table | {}",
|
||||
build_context_clue_string(&COLLECTION_DETAILS_CONTEXT_CLUES)
|
||||
));
|
||||
help_text.patch_style(style_help());
|
||||
let help_text = Text::from(
|
||||
format!(
|
||||
"<↑↓> scroll table | {}",
|
||||
build_context_clue_string(&COLLECTION_DETAILS_CONTEXT_CLUES)
|
||||
)
|
||||
.help(),
|
||||
);
|
||||
let monitored = if collection_selection.monitored {
|
||||
"Yes"
|
||||
} else {
|
||||
@@ -116,24 +119,35 @@ pub fn draw_collection_details(f: &mut Frame<'_>, app: &mut App<'_>, content_are
|
||||
let minimum_availability = collection_selection.minimum_availability.to_display_str();
|
||||
|
||||
let collection_description = Text::from(vec![
|
||||
line_info_primary(
|
||||
"Overview: ".to_owned(),
|
||||
collection_selection.overview.clone().unwrap_or_default(),
|
||||
),
|
||||
line_info_primary(
|
||||
"Root Folder Path: ".to_owned(),
|
||||
Line::from(vec![
|
||||
"Overview ".primary().bold(),
|
||||
collection_selection
|
||||
.overview
|
||||
.clone()
|
||||
.unwrap_or_default()
|
||||
.default(),
|
||||
]),
|
||||
Line::from(vec![
|
||||
"Root Folder Path: ".primary().bold(),
|
||||
collection_selection
|
||||
.root_folder_path
|
||||
.clone()
|
||||
.unwrap_or_default(),
|
||||
),
|
||||
line_info_primary("Quality Profile: ".to_owned(), quality_profile),
|
||||
line_info_primary(
|
||||
"Minimum Availability: ".to_owned(),
|
||||
minimum_availability.to_owned(),
|
||||
),
|
||||
line_info_primary("Monitored: ".to_owned(), monitored.to_owned()),
|
||||
line_info_primary("Search on Add: ".to_owned(), search_on_add.to_owned()),
|
||||
.unwrap_or_default()
|
||||
.default(),
|
||||
]),
|
||||
Line::from(vec![
|
||||
"Quality Profile: ".primary().bold(),
|
||||
quality_profile.default(),
|
||||
]),
|
||||
Line::from(vec![
|
||||
"Minimum Availability: ".primary().bold(),
|
||||
minimum_availability.default(),
|
||||
]),
|
||||
Line::from(vec!["Monitored: ".primary().bold(), monitored.default()]),
|
||||
Line::from(vec![
|
||||
"Search on Add: ".primary().bold(),
|
||||
search_on_add.default(),
|
||||
]),
|
||||
]);
|
||||
|
||||
let description_paragraph = Paragraph::new(collection_description)
|
||||
@@ -230,7 +244,7 @@ pub fn draw_collection_details(f: &mut Frame<'_>, app: &mut App<'_>, content_are
|
||||
Cell::from(rotten_tomatoes_rating),
|
||||
Cell::from(movie.genres.join(", ")),
|
||||
])
|
||||
.style(style_primary())
|
||||
.primary()
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
@@ -246,7 +260,7 @@ fn draw_movie_overview(f: &mut Frame<'_>, app: &mut App<'_>, content_area: Rect)
|
||||
content_area,
|
||||
1,
|
||||
);
|
||||
let mut overview = Text::from(
|
||||
let overview = Text::from(
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
@@ -254,10 +268,9 @@ fn draw_movie_overview(f: &mut Frame<'_>, app: &mut App<'_>, content_area: Rect)
|
||||
.current_selection()
|
||||
.clone()
|
||||
.overview,
|
||||
);
|
||||
overview.patch_style(style_default());
|
||||
let mut help_text = Text::from(build_context_clue_string(&BARE_POPUP_CONTEXT_CLUES));
|
||||
help_text.patch_style(style_help());
|
||||
)
|
||||
.default();
|
||||
let help_text = Text::from(build_context_clue_string(&BARE_POPUP_CONTEXT_CLUES).help());
|
||||
|
||||
let paragraph = Paragraph::new(overview)
|
||||
.block(borderless_block())
|
||||
|
||||
@@ -10,11 +10,12 @@ use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, COLLEC
|
||||
use crate::models::Route;
|
||||
use crate::ui::radarr_ui::collections::collection_details_ui::CollectionDetailsUi;
|
||||
use crate::ui::radarr_ui::collections::edit_collection_ui::EditCollectionUi;
|
||||
use crate::ui::utils::{get_width_from_percentage, layout_block_top_border, style_primary};
|
||||
use crate::ui::utils::{get_width_from_percentage, layout_block_top_border};
|
||||
use crate::ui::{
|
||||
draw_error_message_popup, draw_input_box_popup, draw_popup_over, draw_prompt_box,
|
||||
draw_prompt_popup_over, draw_table, DrawUi, TableProps,
|
||||
};
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
|
||||
mod collection_details_ui;
|
||||
#[cfg(test)]
|
||||
@@ -167,8 +168,7 @@ pub(super) fn draw_collections(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect)
|
||||
),
|
||||
Cell::from(search_on_add),
|
||||
Cell::from(monitored),
|
||||
])
|
||||
.style(style_primary())
|
||||
]).primary()
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
|
||||
@@ -6,8 +6,9 @@ use crate::app::App;
|
||||
use crate::models::radarr_models::DownloadRecord;
|
||||
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, DOWNLOADS_BLOCKS};
|
||||
use crate::models::{HorizontallyScrollableText, Route};
|
||||
use crate::ui::utils::{get_width_from_percentage, layout_block_top_border, style_primary};
|
||||
use crate::ui::utils::{get_width_from_percentage, layout_block_top_border};
|
||||
use crate::ui::{draw_prompt_box, draw_prompt_popup_over, draw_table, DrawUi, TableProps};
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::utils::convert_to_gb;
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -119,8 +120,7 @@ fn draw_downloads(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
),
|
||||
Cell::from(indexer.to_owned()),
|
||||
Cell::from(download_client.to_owned()),
|
||||
])
|
||||
.style(style_primary())
|
||||
]).primary()
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::ui::{
|
||||
draw_button, draw_checkbox_with_label, draw_popup_over, draw_text_box_with_label, loading,
|
||||
DrawUi, LabeledTextBoxProps,
|
||||
};
|
||||
use ratatui::layout::{Constraint, Rect};
|
||||
use ratatui::layout::{Constraint, Layout, Rect};
|
||||
use ratatui::Frame;
|
||||
use std::iter;
|
||||
|
||||
@@ -66,16 +66,14 @@ fn draw_edit_indexer_prompt(f: &mut Frame<'_>, app: &mut App<'_>, prompt_area: R
|
||||
1,
|
||||
);
|
||||
|
||||
let left_chunks = vertical_chunks(
|
||||
vec![
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(3),
|
||||
Constraint::Min(0),
|
||||
],
|
||||
split_chunks[0],
|
||||
);
|
||||
let [name, rss, auto_search, interactive_search, _] = Layout::vertical([
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(3),
|
||||
Constraint::Length(3),
|
||||
Constraint::Min(0),
|
||||
])
|
||||
.areas(split_chunks[0]);
|
||||
let right_chunks = vertical_chunks(
|
||||
vec![
|
||||
Constraint::Length(3),
|
||||
@@ -91,7 +89,7 @@ fn draw_edit_indexer_prompt(f: &mut Frame<'_>, app: &mut App<'_>, prompt_area: R
|
||||
draw_text_box_with_label(
|
||||
f,
|
||||
LabeledTextBoxProps {
|
||||
area: left_chunks[0],
|
||||
area: name,
|
||||
label: "Name",
|
||||
text: &edit_indexer_modal.name.text,
|
||||
offset: *edit_indexer_modal.name.offset.borrow(),
|
||||
@@ -166,14 +164,14 @@ fn draw_edit_indexer_prompt(f: &mut Frame<'_>, app: &mut App<'_>, prompt_area: R
|
||||
|
||||
draw_checkbox_with_label(
|
||||
f,
|
||||
left_chunks[1],
|
||||
rss,
|
||||
"Enable RSS",
|
||||
edit_indexer_modal.enable_rss.unwrap_or_default(),
|
||||
selected_block == &ActiveRadarrBlock::EditIndexerToggleEnableRss,
|
||||
);
|
||||
draw_checkbox_with_label(
|
||||
f,
|
||||
left_chunks[2],
|
||||
auto_search,
|
||||
"Enable Automatic Search",
|
||||
edit_indexer_modal
|
||||
.enable_automatic_search
|
||||
@@ -182,7 +180,7 @@ fn draw_edit_indexer_prompt(f: &mut Frame<'_>, app: &mut App<'_>, prompt_area: R
|
||||
);
|
||||
draw_checkbox_with_label(
|
||||
f,
|
||||
left_chunks[3],
|
||||
interactive_search,
|
||||
"Enable Interactive Search",
|
||||
edit_indexer_modal
|
||||
.enable_interactive_search
|
||||
|
||||
@@ -10,7 +10,8 @@ use crate::models::Route;
|
||||
use crate::ui::radarr_ui::indexers::edit_indexer_ui::EditIndexerUi;
|
||||
use crate::ui::radarr_ui::indexers::indexer_settings_ui::IndexerSettingsUi;
|
||||
use crate::ui::radarr_ui::indexers::test_all_indexers_ui::TestAllIndexersUi;
|
||||
use crate::ui::utils::{layout_block_top_border, style_failure, style_primary, style_success};
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::utils::layout_block_top_border;
|
||||
use crate::ui::{draw_prompt_box, draw_prompt_popup_over, draw_table, DrawUi, TableProps};
|
||||
|
||||
mod edit_indexer_ui;
|
||||
@@ -103,24 +104,15 @@ fn draw_indexers(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
} = indexer;
|
||||
let bool_to_text = |flag: bool| {
|
||||
if flag {
|
||||
return ("Enabled", style_success());
|
||||
return Text::from("Enabled").success();
|
||||
}
|
||||
|
||||
("Disabled", style_failure())
|
||||
Text::from("Disabled").failure()
|
||||
};
|
||||
|
||||
let (rss_text, rss_style) = bool_to_text(*enable_rss);
|
||||
let mut rss = Text::from(rss_text);
|
||||
rss.patch_style(rss_style);
|
||||
|
||||
let (auto_search_text, auto_search_style) = bool_to_text(*enable_automatic_search);
|
||||
let mut automatic_search = Text::from(auto_search_text);
|
||||
automatic_search.patch_style(auto_search_style);
|
||||
|
||||
let (interactive_search_text, interactive_search_style) =
|
||||
bool_to_text(*enable_interactive_search);
|
||||
let mut interactive_search = Text::from(interactive_search_text);
|
||||
interactive_search.patch_style(interactive_search_style);
|
||||
let rss = bool_to_text(*enable_rss);
|
||||
let automatic_search = bool_to_text(*enable_automatic_search);
|
||||
let interactive_search = bool_to_text(*enable_interactive_search);
|
||||
let tags: String = tags
|
||||
.iter()
|
||||
.map(|tag_id| {
|
||||
@@ -143,7 +135,7 @@ fn draw_indexers(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
Cell::from(priority.to_string()),
|
||||
Cell::from(tags),
|
||||
])
|
||||
.style(style_primary())
|
||||
.primary()
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
|
||||
@@ -4,9 +4,8 @@ use crate::models::servarr_data::radarr::modals::IndexerTestResultModalItem;
|
||||
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
||||
use crate::models::Route;
|
||||
use crate::ui::radarr_ui::indexers::draw_indexers;
|
||||
use crate::ui::utils::{
|
||||
borderless_block, get_width_from_percentage, style_failure, style_success, title_block,
|
||||
};
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::utils::{borderless_block, get_width_from_percentage, title_block};
|
||||
use crate::ui::{
|
||||
draw_help_and_get_content_rect, draw_large_popup_over, draw_table, DrawUi, TableProps,
|
||||
};
|
||||
@@ -76,18 +75,17 @@ fn draw_test_all_indexers_test_results(f: &mut Frame<'_>, app: &mut App<'_>, are
|
||||
app.tick_count % app.ticks_until_scroll == 0,
|
||||
);
|
||||
let pass_fail = if result.is_valid { "✔" } else { "❌" };
|
||||
let row_style = if result.is_valid {
|
||||
style_success()
|
||||
} else {
|
||||
style_failure()
|
||||
};
|
||||
|
||||
Row::new(vec![
|
||||
let row = Row::new(vec![
|
||||
Cell::from(result.name.to_owned()),
|
||||
Cell::from(pass_fail.to_owned()),
|
||||
Cell::from(result.validation_failures.to_string()),
|
||||
])
|
||||
.style(row_style)
|
||||
]);
|
||||
|
||||
if result.is_valid {
|
||||
row.success()
|
||||
} else {
|
||||
row.failure()
|
||||
}
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
|
||||
@@ -11,10 +11,10 @@ use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, ADD_MO
|
||||
use crate::models::Route;
|
||||
use crate::ui::radarr_ui::collections::{draw_collection_details, draw_collections};
|
||||
use crate::ui::radarr_ui::library::draw_library;
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::utils::{
|
||||
borderless_block, get_width_from_percentage, horizontal_chunks, layout_block,
|
||||
layout_paragraph_borderless, style_help, style_primary, title_block_centered,
|
||||
vertical_chunks_with_margin,
|
||||
layout_paragraph_borderless, title_block_centered, vertical_chunks_with_margin,
|
||||
};
|
||||
use crate::ui::{
|
||||
draw_button, draw_drop_down_menu_button, draw_drop_down_popup, draw_error_popup,
|
||||
@@ -148,8 +148,7 @@ fn draw_add_movie_search(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
);
|
||||
f.render_widget(layout_block(), chunks[1]);
|
||||
|
||||
let mut help_text = Text::from(build_context_clue_string(&BARE_POPUP_CONTEXT_CLUES));
|
||||
help_text.patch_style(style_help());
|
||||
let help_text = Text::from(build_context_clue_string(&BARE_POPUP_CONTEXT_CLUES).help());
|
||||
let help_paragraph = Paragraph::new(help_text)
|
||||
.block(borderless_block())
|
||||
.alignment(Alignment::Center);
|
||||
@@ -167,10 +166,8 @@ fn draw_add_movie_search(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
| ActiveRadarrBlock::AddMovieSelectRootFolder
|
||||
| ActiveRadarrBlock::AddMovieAlreadyInLibrary
|
||||
| ActiveRadarrBlock::AddMovieTagsInput => {
|
||||
let mut help_text = Text::from(build_context_clue_string(
|
||||
&ADD_MOVIE_SEARCH_RESULTS_CONTEXT_CLUES,
|
||||
));
|
||||
help_text.patch_style(style_help());
|
||||
let help_text =
|
||||
Text::from(build_context_clue_string(&ADD_MOVIE_SEARCH_RESULTS_CONTEXT_CLUES).help());
|
||||
let help_paragraph = Paragraph::new(help_text)
|
||||
.block(borderless_block())
|
||||
.alignment(Alignment::Center);
|
||||
@@ -259,7 +256,7 @@ fn draw_add_movie_search(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
Cell::from(rotten_tomatoes_rating),
|
||||
Cell::from(movie.genres.join(", ")),
|
||||
])
|
||||
.style(style_primary())
|
||||
.primary()
|
||||
},
|
||||
is_loading,
|
||||
true,
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::app::App;
|
||||
use crate::models::radarr_models::Movie;
|
||||
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, LIBRARY_BLOCKS};
|
||||
use crate::models::Route;
|
||||
use crate::ui::radarr_ui::determine_row_style;
|
||||
use crate::ui::radarr_ui::decorate_with_row_style;
|
||||
use crate::ui::radarr_ui::library::add_movie_ui::AddMovieUi;
|
||||
use crate::ui::radarr_ui::library::delete_movie_ui::DeleteMovieUi;
|
||||
use crate::ui::radarr_ui::library::edit_movie_ui::EditMovieUi;
|
||||
@@ -186,19 +186,22 @@ pub(super) fn draw_library(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ");
|
||||
|
||||
Row::new(vec![
|
||||
Cell::from(movie.title.to_string()),
|
||||
Cell::from(movie.year.to_string()),
|
||||
Cell::from(movie.studio.to_string()),
|
||||
Cell::from(format!("{hours}h {minutes}m")),
|
||||
Cell::from(certification),
|
||||
Cell::from(movie.original_language.name.to_owned()),
|
||||
Cell::from(format!("{file_size:.2} GB")),
|
||||
Cell::from(quality_profile),
|
||||
Cell::from(monitored.to_owned()),
|
||||
Cell::from(tags),
|
||||
])
|
||||
.style(determine_row_style(downloads_vec, movie))
|
||||
decorate_with_row_style(
|
||||
downloads_vec,
|
||||
movie,
|
||||
Row::new(vec![
|
||||
Cell::from(movie.title.to_string()),
|
||||
Cell::from(movie.year.to_string()),
|
||||
Cell::from(movie.studio.to_string()),
|
||||
Cell::from(format!("{hours}h {minutes}m")),
|
||||
Cell::from(certification),
|
||||
Cell::from(movie.original_language.name.to_owned()),
|
||||
Cell::from(format!("{file_size:.2} GB")),
|
||||
Cell::from(quality_profile),
|
||||
Cell::from(monitored.to_owned()),
|
||||
Cell::from(tags),
|
||||
]),
|
||||
)
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::iter;
|
||||
|
||||
use ratatui::layout::{Alignment, Constraint, Rect};
|
||||
use ratatui::style::{Modifier, Style};
|
||||
use ratatui::style::{Style, Stylize};
|
||||
use ratatui::text::{Line, Span, Text};
|
||||
use ratatui::widgets::{Cell, ListItem, Paragraph, Row, Wrap};
|
||||
use ratatui::Frame;
|
||||
@@ -11,10 +11,10 @@ use crate::models::radarr_models::{Credit, MovieHistoryItem, Release, ReleaseFie
|
||||
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, MOVIE_DETAILS_BLOCKS};
|
||||
use crate::models::Route;
|
||||
use crate::ui::radarr_ui::library::draw_library;
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::utils::{
|
||||
borderless_block, get_width_from_percentage, layout_block_bottom_border, layout_block_top_border,
|
||||
line_info_default, style_awaiting_import, style_bold, style_failure, style_primary,
|
||||
style_success, style_warning, vertical_chunks,
|
||||
vertical_chunks,
|
||||
};
|
||||
use crate::ui::{
|
||||
draw_drop_down_popup, draw_large_popup_over, draw_prompt_box, draw_prompt_box_with_content,
|
||||
@@ -162,19 +162,13 @@ fn draw_file_info(f: &mut Frame<'_>, app: &App<'_>, content_area: Rect) {
|
||||
],
|
||||
content_area,
|
||||
);
|
||||
let mut file_details_title = Text::from("File Details");
|
||||
let mut audio_details_title = Text::from("Audio Details");
|
||||
let mut video_details_title = Text::from("Video Details");
|
||||
file_details_title.patch_style(style_bold());
|
||||
audio_details_title.patch_style(style_bold());
|
||||
video_details_title.patch_style(style_bold());
|
||||
|
||||
let file_details_title_paragraph =
|
||||
Paragraph::new(file_details_title).block(layout_block_top_border());
|
||||
Paragraph::new("File Details".bold()).block(layout_block_top_border());
|
||||
let audio_details_title_paragraph =
|
||||
Paragraph::new(audio_details_title).block(borderless_block());
|
||||
Paragraph::new("Audio Details".bold()).block(borderless_block());
|
||||
let video_details_title_paragraph =
|
||||
Paragraph::new(video_details_title).block(borderless_block());
|
||||
Paragraph::new("Video Details".bold()).block(borderless_block());
|
||||
|
||||
let file_details = Text::from(file_info);
|
||||
let audio_details = Text::from(audio_details);
|
||||
@@ -214,19 +208,22 @@ fn draw_movie_details(f: &mut Frame<'_>, app: &App<'_>, content_area: Rect) {
|
||||
.unwrap()
|
||||
.split(": ")
|
||||
.collect::<Vec<&str>>()[1];
|
||||
let mut text = Text::from(
|
||||
let text = Text::from(
|
||||
movie_details
|
||||
.items
|
||||
.iter()
|
||||
.map(|line| {
|
||||
let split = line.split(':').collect::<Vec<&str>>();
|
||||
let title = format!("{}:", split[0]);
|
||||
let style = style_from_download_status(download_status);
|
||||
|
||||
line_info_default(title, split[1..].join(":"))
|
||||
Line::from(vec![
|
||||
title.bold().style(style),
|
||||
Span::styled(split[1..].join(":"), style),
|
||||
])
|
||||
})
|
||||
.collect::<Vec<Line<'_>>>(),
|
||||
);
|
||||
text.patch_style(determine_style_from_download_status(download_status));
|
||||
|
||||
let paragraph = Paragraph::new(text)
|
||||
.block(block)
|
||||
@@ -304,7 +301,7 @@ fn draw_movie_history(f: &mut Frame<'_>, app: &mut App<'_>, content_area: Rect)
|
||||
Cell::from(quality.quality.name.to_owned()),
|
||||
Cell::from(date.to_string()),
|
||||
])
|
||||
.style(style_success())
|
||||
.success()
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
@@ -347,7 +344,7 @@ fn draw_movie_cast(f: &mut Frame<'_>, app: &mut App<'_>, content_area: Rect) {
|
||||
Cell::from(person_name.to_owned()),
|
||||
Cell::from(character.clone().unwrap_or_default()),
|
||||
])
|
||||
.style(style_success())
|
||||
.success()
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
@@ -391,7 +388,7 @@ fn draw_movie_crew(f: &mut Frame<'_>, app: &mut App<'_>, content_area: Rect) {
|
||||
Cell::from(job.clone().unwrap_or_default()),
|
||||
Cell::from(department.clone().unwrap_or_default()),
|
||||
])
|
||||
.style(style_success())
|
||||
.success()
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
@@ -505,14 +502,16 @@ fn draw_movie_releases(f: &mut Frame<'_>, app: &mut App<'_>, content_area: Rect)
|
||||
let size = convert_to_gb(*size);
|
||||
let rejected_str = if *rejected { "⛔" } else { "" };
|
||||
let peers = if seeders.is_none() || leechers.is_none() {
|
||||
Text::default()
|
||||
Text::from("")
|
||||
} else {
|
||||
let seeders = seeders.clone().unwrap().as_u64().unwrap();
|
||||
let leechers = leechers.clone().unwrap().as_u64().unwrap();
|
||||
let mut text = Text::from(format!("{seeders} / {leechers}"));
|
||||
text.patch_style(determine_peer_style(seeders, leechers));
|
||||
|
||||
text
|
||||
decorate_peer_style(
|
||||
seeders,
|
||||
leechers,
|
||||
Text::from(format!("{seeders} / {leechers}")),
|
||||
)
|
||||
};
|
||||
|
||||
let language = if languages.is_some() {
|
||||
@@ -533,7 +532,7 @@ fn draw_movie_releases(f: &mut Frame<'_>, app: &mut App<'_>, content_area: Rect)
|
||||
Cell::from(language),
|
||||
Cell::from(quality),
|
||||
])
|
||||
.style(style_primary())
|
||||
.primary()
|
||||
},
|
||||
app.is_loading || is_empty,
|
||||
true,
|
||||
@@ -567,16 +566,13 @@ fn draw_manual_search_confirm_prompt(f: &mut Frame<'_>, app: &mut App<'_>, promp
|
||||
};
|
||||
|
||||
if current_selection.rejected {
|
||||
let mut lines_vec = vec![Line::from(vec![Span::styled(
|
||||
"Rejection reasons: ",
|
||||
style_primary().add_modifier(Modifier::BOLD),
|
||||
)])];
|
||||
let mut lines_vec = vec![Line::from("Rejection reasons: ".primary().bold())];
|
||||
let mut rejections_spans = current_selection
|
||||
.rejections
|
||||
.clone()
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.map(|item| Line::from(vec![Span::styled(format!("• {item}"), style_primary())]))
|
||||
.map(|item| Line::from(format!("• {item}").primary().bold()))
|
||||
.collect::<Vec<Line<'_>>>();
|
||||
lines_vec.append(&mut rejections_spans);
|
||||
|
||||
@@ -604,22 +600,22 @@ fn draw_manual_search_confirm_prompt(f: &mut Frame<'_>, app: &mut App<'_>, promp
|
||||
}
|
||||
}
|
||||
|
||||
fn determine_style_from_download_status(download_status: &str) -> Style {
|
||||
fn style_from_download_status(download_status: &str) -> Style {
|
||||
match download_status {
|
||||
"Downloaded" => style_success(),
|
||||
"Awaiting Import" => style_awaiting_import(),
|
||||
"Downloading" => style_warning(),
|
||||
"Missing" => style_failure(),
|
||||
_ => style_success(),
|
||||
"Downloaded" => Style::new().success(),
|
||||
"Awaiting Import" => Style::new().awaiting_import(),
|
||||
"Downloading" => Style::new().warning(),
|
||||
"Missing" => Style::new().failure(),
|
||||
_ => Style::new().success(),
|
||||
}
|
||||
}
|
||||
|
||||
fn determine_peer_style(seeders: u64, leechers: u64) -> Style {
|
||||
fn decorate_peer_style(seeders: u64, leechers: u64, text: Text<'_>) -> Text<'_> {
|
||||
if seeders == 0 {
|
||||
style_failure()
|
||||
text.failure()
|
||||
} else if seeders < leechers {
|
||||
style_warning()
|
||||
text.warning()
|
||||
} else {
|
||||
style_success()
|
||||
text.success()
|
||||
}
|
||||
}
|
||||
|
||||
+30
-30
@@ -1,10 +1,10 @@
|
||||
use std::iter;
|
||||
|
||||
use chrono::{Duration, Utc};
|
||||
use ratatui::layout::{Alignment, Constraint, Rect};
|
||||
use ratatui::style::{Color, Style};
|
||||
use ratatui::layout::{Alignment, Constraint, Layout, Rect};
|
||||
use ratatui::prelude::Stylize;
|
||||
use ratatui::text::Text;
|
||||
use ratatui::widgets::Paragraph;
|
||||
use ratatui::widgets::{Paragraph, Row};
|
||||
use ratatui::Frame;
|
||||
|
||||
use crate::app::App;
|
||||
@@ -20,10 +20,10 @@ use crate::ui::radarr_ui::indexers::IndexersUi;
|
||||
use crate::ui::radarr_ui::library::LibraryUi;
|
||||
use crate::ui::radarr_ui::root_folders::RootFoldersUi;
|
||||
use crate::ui::radarr_ui::system::SystemUi;
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::utils::{
|
||||
borderless_block, horizontal_chunks, layout_block, line_gauge_with_label, line_gauge_with_title,
|
||||
style_awaiting_import, style_bold, style_default, style_failure, style_success,
|
||||
style_unmonitored, style_warning, title_block, vertical_chunks_with_margin,
|
||||
borderless_block, layout_block, line_gauge_with_label, line_gauge_with_title, title_block,
|
||||
vertical_chunks_with_margin,
|
||||
};
|
||||
use crate::ui::DrawUi;
|
||||
use crate::utils::convert_to_gb;
|
||||
@@ -63,16 +63,14 @@ impl DrawUi for RadarrUi {
|
||||
}
|
||||
|
||||
fn draw_context_row(f: &mut Frame<'_>, app: &App<'_>, area: Rect) {
|
||||
let chunks = horizontal_chunks(vec![Constraint::Min(0), Constraint::Length(20)], area);
|
||||
let [main, logo] = Layout::horizontal([Constraint::Min(0), Constraint::Length(20)]).areas(area);
|
||||
|
||||
let context_chunks = horizontal_chunks(
|
||||
vec![Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)],
|
||||
chunks[0],
|
||||
);
|
||||
let [stats, downloads] =
|
||||
Layout::horizontal([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)]).areas(main);
|
||||
|
||||
draw_stats_context(f, app, context_chunks[0]);
|
||||
draw_downloads_context(f, app, context_chunks[1]);
|
||||
draw_radarr_logo(f, chunks[1]);
|
||||
draw_stats_context(f, app, stats);
|
||||
draw_downloads_context(f, app, downloads);
|
||||
draw_radarr_logo(f, logo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +105,7 @@ fn draw_stats_context(f: &mut Frame<'_>, app: &App<'_>, area: Rect) {
|
||||
app.data.radarr_data.version
|
||||
)))
|
||||
.block(borderless_block())
|
||||
.style(style_bold());
|
||||
.bold();
|
||||
|
||||
let uptime = Utc::now() - start_time.to_owned();
|
||||
let days = uptime.num_days();
|
||||
@@ -122,12 +120,10 @@ fn draw_stats_context(f: &mut Frame<'_>, app: &App<'_>, area: Rect) {
|
||||
width = 2
|
||||
)))
|
||||
.block(borderless_block())
|
||||
.style(style_bold());
|
||||
.bold();
|
||||
|
||||
let storage =
|
||||
Paragraph::new(Text::from("Storage:")).block(borderless_block().style(style_bold()));
|
||||
let folders =
|
||||
Paragraph::new(Text::from("Root Folders:")).block(borderless_block().style(style_bold()));
|
||||
let storage = Paragraph::new(Text::from("Storage:")).block(borderless_block().bold());
|
||||
let folders = Paragraph::new(Text::from("Root Folders:")).block(borderless_block().bold());
|
||||
|
||||
f.render_widget(version_paragraph, chunks[0]);
|
||||
f.render_widget(uptime_paragraph, chunks[1]);
|
||||
@@ -159,7 +155,7 @@ fn draw_stats_context(f: &mut Frame<'_>, app: &App<'_>, area: Rect) {
|
||||
let space: f64 = convert_to_gb(*free_space);
|
||||
let root_folder_space = Paragraph::new(format!("{path}: {space:.2} GB free"))
|
||||
.block(borderless_block())
|
||||
.style(style_default());
|
||||
.default();
|
||||
|
||||
f.render_widget(root_folder_space, chunks[i + disk_space_vec.len() + 4])
|
||||
}
|
||||
@@ -198,36 +194,40 @@ fn draw_downloads_context(f: &mut Frame<'_>, app: &App<'_>, area: Rect) {
|
||||
}
|
||||
}
|
||||
|
||||
fn determine_row_style(downloads_vec: &[DownloadRecord], movie: &Movie) -> Style {
|
||||
fn decorate_with_row_style<'a>(
|
||||
downloads_vec: &[DownloadRecord],
|
||||
movie: &Movie,
|
||||
row: Row<'a>,
|
||||
) -> Row<'a> {
|
||||
if !movie.has_file {
|
||||
if let Some(download) = downloads_vec
|
||||
.iter()
|
||||
.find(|&download| download.movie_id == movie.id)
|
||||
{
|
||||
if download.status == "downloading" {
|
||||
return style_warning();
|
||||
return row.warning();
|
||||
}
|
||||
|
||||
if download.status == "completed" {
|
||||
return style_awaiting_import();
|
||||
return row.awaiting_import();
|
||||
}
|
||||
}
|
||||
|
||||
return style_failure();
|
||||
return row.failure();
|
||||
}
|
||||
|
||||
if !movie.monitored {
|
||||
style_unmonitored()
|
||||
row.unmonitored()
|
||||
} else {
|
||||
style_success()
|
||||
row.success()
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_radarr_logo(f: &mut Frame<'_>, area: Rect) {
|
||||
let mut logo_text = Text::from(RADARR_LOGO);
|
||||
logo_text.patch_style(Style::default().fg(Color::LightYellow));
|
||||
let logo_text = Text::from(RADARR_LOGO);
|
||||
let logo = Paragraph::new(logo_text)
|
||||
.block(layout_block())
|
||||
.light_yellow()
|
||||
.block(layout_block().default())
|
||||
.alignment(Alignment::Center);
|
||||
f.render_widget(logo, area);
|
||||
}
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
use crate::ui::utils::{style_default, style_failure, style_secondary};
|
||||
use ratatui::style::{Color, Modifier, Style};
|
||||
use ratatui::style::{Style, Stylize};
|
||||
use ratatui::widgets::ListItem;
|
||||
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "radarr_ui_utils_tests.rs"]
|
||||
mod radarr_ui_utils_tests;
|
||||
|
||||
pub(super) fn determine_log_style_by_level(level: &str) -> Style {
|
||||
pub(super) fn style_log_list_item(list_item: ListItem<'_>, level: String) -> ListItem<'_> {
|
||||
match level.to_lowercase().as_str() {
|
||||
"trace" => Style::default().fg(Color::Gray),
|
||||
"debug" => Style::default().fg(Color::Blue),
|
||||
"info" => style_default(),
|
||||
"warn" => style_secondary(),
|
||||
"error" => style_failure(),
|
||||
"fatal" => style_failure().add_modifier(Modifier::BOLD),
|
||||
_ => style_default(),
|
||||
"trace" => list_item.gray(),
|
||||
"debug" => list_item.blue(),
|
||||
"info" => list_item.style(Style::new().default()),
|
||||
"warn" => list_item.style(Style::new().secondary()),
|
||||
"error" => list_item.style(Style::new().failure()),
|
||||
"fatal" => list_item.style(Style::new().failure().bold()),
|
||||
_ => list_item.style(Style::new().default()),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,32 +2,50 @@
|
||||
mod tests {
|
||||
use super::super::*;
|
||||
use pretty_assertions::assert_str_eq;
|
||||
use ratatui::prelude::Text;
|
||||
use ratatui::text::Span;
|
||||
|
||||
#[test]
|
||||
fn test_determine_log_style_by_level() {
|
||||
let list_item = ListItem::new(Text::from(Span::raw("test")));
|
||||
|
||||
assert_eq!(
|
||||
determine_log_style_by_level("trace"),
|
||||
Style::default().fg(Color::Gray)
|
||||
style_log_list_item(list_item.clone(), "trace".to_string()),
|
||||
list_item.clone().gray()
|
||||
);
|
||||
assert_eq!(
|
||||
determine_log_style_by_level("debug"),
|
||||
Style::default().fg(Color::Blue)
|
||||
style_log_list_item(list_item.clone(), "debug".to_string()),
|
||||
list_item.clone().blue()
|
||||
);
|
||||
assert_eq!(determine_log_style_by_level("info"), style_default());
|
||||
assert_eq!(determine_log_style_by_level("warn"), style_secondary());
|
||||
assert_eq!(determine_log_style_by_level("error"), style_failure());
|
||||
assert_eq!(
|
||||
determine_log_style_by_level("fatal"),
|
||||
style_failure().add_modifier(Modifier::BOLD)
|
||||
style_log_list_item(list_item.clone(), "info".to_string()),
|
||||
list_item.clone().style(Style::new().default())
|
||||
);
|
||||
assert_eq!(
|
||||
style_log_list_item(list_item.clone(), "warn".to_string()),
|
||||
list_item.clone().style(Style::new().secondary())
|
||||
);
|
||||
assert_eq!(
|
||||
style_log_list_item(list_item.clone(), "error".to_string()),
|
||||
list_item.clone().style(Style::new().failure())
|
||||
);
|
||||
assert_eq!(
|
||||
style_log_list_item(list_item.clone(), "fatal".to_string()),
|
||||
list_item.clone().style(Style::new().failure().bold())
|
||||
);
|
||||
assert_eq!(
|
||||
style_log_list_item(list_item.clone(), "".to_string()),
|
||||
list_item.style(Style::new().default())
|
||||
);
|
||||
assert_eq!(determine_log_style_by_level(""), style_default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_determine_log_style_by_level_case_insensitive() {
|
||||
let list_item = ListItem::new(Text::from(Span::raw("test")));
|
||||
|
||||
assert_eq!(
|
||||
determine_log_style_by_level("TrAcE"),
|
||||
Style::default().fg(Color::Gray)
|
||||
style_log_list_item(list_item.clone(), "TrAcE".to_string()),
|
||||
list_item.gray()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,11 +6,12 @@ use crate::app::App;
|
||||
use crate::models::radarr_models::RootFolder;
|
||||
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, ROOT_FOLDERS_BLOCKS};
|
||||
use crate::models::Route;
|
||||
use crate::ui::utils::{layout_block_top_border, style_primary};
|
||||
use crate::ui::utils::{layout_block_top_border};
|
||||
use crate::ui::{
|
||||
draw_input_box_popup, draw_popup_over, draw_prompt_box, draw_prompt_popup_over, draw_table,
|
||||
DrawUi, TableProps,
|
||||
};
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::utils::convert_to_gb;
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -94,8 +95,7 @@ fn draw_root_folders(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
.len()
|
||||
.to_string(),
|
||||
),
|
||||
])
|
||||
.style(style_primary())
|
||||
]).primary()
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
|
||||
@@ -13,11 +13,10 @@ use ratatui::{
|
||||
use crate::app::App;
|
||||
use crate::models::radarr_models::Task;
|
||||
use crate::models::servarr_data::radarr::radarr_data::ActiveRadarrBlock;
|
||||
use crate::ui::radarr_ui::radarr_ui_utils::{
|
||||
convert_to_minutes_hours_days, determine_log_style_by_level,
|
||||
};
|
||||
use crate::ui::radarr_ui::radarr_ui_utils::{convert_to_minutes_hours_days, style_log_list_item};
|
||||
use crate::ui::radarr_ui::system::system_details_ui::SystemDetailsUi;
|
||||
use crate::ui::utils::{layout_block_top_border, style_help, style_primary};
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::utils::layout_block_top_border;
|
||||
use crate::ui::{draw_table, ListProps, TableProps};
|
||||
use crate::{
|
||||
models::Route,
|
||||
@@ -117,7 +116,7 @@ fn draw_tasks(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
Cell::from(task_props.last_duration),
|
||||
Cell::from(task_props.next_execution),
|
||||
])
|
||||
.style(style_primary())
|
||||
.primary()
|
||||
},
|
||||
app.is_loading,
|
||||
false,
|
||||
@@ -177,7 +176,7 @@ pub(super) fn draw_queued_events(f: &mut Frame<'_>, app: &mut App<'_>, area: Rec
|
||||
Cell::from(started_string),
|
||||
Cell::from(duration.to_owned()),
|
||||
])
|
||||
.style(style_primary())
|
||||
.primary()
|
||||
},
|
||||
app.is_loading,
|
||||
false,
|
||||
@@ -190,10 +189,9 @@ fn draw_logs(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
area,
|
||||
|log| {
|
||||
let log_line = log.to_string();
|
||||
let level = log_line.split('|').collect::<Vec<&str>>()[1];
|
||||
let style = determine_log_style_by_level(level);
|
||||
let level = log_line.split('|').collect::<Vec<&str>>()[1].to_string();
|
||||
|
||||
ListItem::new(Text::from(Span::raw(log_line))).style(style)
|
||||
style_log_list_item(ListItem::new(Text::from(Span::raw(log_line))), level)
|
||||
},
|
||||
ListProps {
|
||||
content: &mut app.data.radarr_data.logs,
|
||||
@@ -206,16 +204,18 @@ fn draw_logs(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
}
|
||||
|
||||
fn draw_help(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
let mut help_text = Text::from(format!(
|
||||
" {}",
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.main_tabs
|
||||
.get_active_tab_contextual_help()
|
||||
.unwrap()
|
||||
));
|
||||
help_text.patch_style(style_help());
|
||||
let help_text = Text::from(
|
||||
format!(
|
||||
" {}",
|
||||
app
|
||||
.data
|
||||
.radarr_data
|
||||
.main_tabs
|
||||
.get_active_tab_contextual_help()
|
||||
.unwrap()
|
||||
)
|
||||
.help(),
|
||||
);
|
||||
let help_paragraph = Paragraph::new(help_text)
|
||||
.block(layout_block_top_border())
|
||||
.alignment(Alignment::Left);
|
||||
|
||||
@@ -8,12 +8,13 @@ use crate::app::radarr::radarr_context_clues::SYSTEM_TASKS_CONTEXT_CLUES;
|
||||
use crate::app::App;
|
||||
use crate::models::servarr_data::radarr::radarr_data::{ActiveRadarrBlock, SYSTEM_DETAILS_BLOCKS};
|
||||
use crate::models::Route;
|
||||
use crate::ui::radarr_ui::radarr_ui_utils::determine_log_style_by_level;
|
||||
use crate::ui::radarr_ui::radarr_ui_utils::style_log_list_item;
|
||||
use crate::ui::radarr_ui::system::{
|
||||
draw_queued_events, draw_system_ui_layout, extract_task_props, TASK_TABLE_CONSTRAINTS,
|
||||
TASK_TABLE_HEADERS,
|
||||
};
|
||||
use crate::ui::utils::{borderless_block, style_primary, title_block};
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use crate::ui::utils::{borderless_block, title_block};
|
||||
use crate::ui::{
|
||||
draw_help_and_get_content_rect, draw_large_popup_over, draw_list_box, draw_medium_popup_over,
|
||||
draw_prompt_box, draw_prompt_popup_over, draw_table, loading, DrawUi, ListProps, TableProps,
|
||||
@@ -75,10 +76,9 @@ fn draw_logs_popup(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
area,
|
||||
|log| {
|
||||
let log_line = log.to_string();
|
||||
let level = log.text.split('|').collect::<Vec<&str>>()[1];
|
||||
let style = determine_log_style_by_level(level);
|
||||
let level = log.text.split('|').collect::<Vec<&str>>()[1].to_string();
|
||||
|
||||
ListItem::new(Text::from(Span::raw(log_line))).style(style)
|
||||
style_log_list_item(ListItem::new(Text::from(Span::raw(log_line))), level)
|
||||
},
|
||||
ListProps {
|
||||
content: &mut app.data.radarr_data.log_details,
|
||||
@@ -124,7 +124,7 @@ fn draw_tasks_popup(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
|
||||
Cell::from(task_props.last_duration),
|
||||
Cell::from(task_props.next_execution),
|
||||
])
|
||||
.style(style_primary())
|
||||
.primary()
|
||||
},
|
||||
app.is_loading,
|
||||
true,
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
use ratatui::prelude::{Color, Styled};
|
||||
use ratatui::style::Stylize;
|
||||
|
||||
pub const COLOR_ORANGE: Color = Color::Rgb(255, 170, 66);
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "styles_tests.rs"]
|
||||
mod styles_tests;
|
||||
|
||||
pub trait ManagarrStyle<'a, T>: Stylize<'a, T>
|
||||
where
|
||||
T: Default,
|
||||
{
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
fn new() -> T;
|
||||
fn awaiting_import(self) -> T;
|
||||
fn default(self) -> T;
|
||||
fn failure(self) -> T;
|
||||
fn help(self) -> T;
|
||||
fn highlight(self) -> T;
|
||||
fn primary(self) -> T;
|
||||
fn secondary(self) -> T;
|
||||
fn success(self) -> T;
|
||||
fn system_function(self) -> T;
|
||||
fn unmonitored(self) -> T;
|
||||
fn warning(self) -> T;
|
||||
}
|
||||
|
||||
impl<'a, T, U> ManagarrStyle<'a, T> for U
|
||||
where
|
||||
U: Styled<Item = T>,
|
||||
T: Default,
|
||||
{
|
||||
fn new() -> T {
|
||||
T::default()
|
||||
}
|
||||
|
||||
fn awaiting_import(self) -> T {
|
||||
self.fg(COLOR_ORANGE)
|
||||
}
|
||||
|
||||
fn default(self) -> T {
|
||||
self.white()
|
||||
}
|
||||
|
||||
fn failure(self) -> T {
|
||||
self.red()
|
||||
}
|
||||
|
||||
fn help(self) -> T {
|
||||
self.light_blue()
|
||||
}
|
||||
|
||||
fn highlight(self) -> T {
|
||||
self.reversed()
|
||||
}
|
||||
|
||||
fn primary(self) -> T {
|
||||
self.cyan()
|
||||
}
|
||||
|
||||
fn secondary(self) -> T {
|
||||
self.yellow()
|
||||
}
|
||||
|
||||
fn success(self) -> T {
|
||||
self.green()
|
||||
}
|
||||
|
||||
fn system_function(self) -> T {
|
||||
self.yellow()
|
||||
}
|
||||
|
||||
fn unmonitored(self) -> T {
|
||||
self.white()
|
||||
}
|
||||
|
||||
fn warning(self) -> T {
|
||||
self.magenta()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::ui::styles::{ManagarrStyle, COLOR_ORANGE};
|
||||
use pretty_assertions::assert_eq;
|
||||
use ratatui::prelude::Modifier;
|
||||
use ratatui::style::{Style, Stylize};
|
||||
|
||||
#[test]
|
||||
fn test_new() {
|
||||
assert_eq!(Style::new(), <Style as Default>::default())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_awaiting_import() {
|
||||
assert_eq!(
|
||||
Style::new().awaiting_import(),
|
||||
Style::new().fg(COLOR_ORANGE)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_default() {
|
||||
assert_eq!(Style::new().default(), Style::new().white());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_failure() {
|
||||
assert_eq!(Style::new().failure(), Style::new().red());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_help() {
|
||||
assert_eq!(Style::new().help(), Style::new().light_blue());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_highlight() {
|
||||
assert_eq!(
|
||||
Style::new().highlight(),
|
||||
Style::new().add_modifier(Modifier::REVERSED)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_primary() {
|
||||
assert_eq!(Style::new().primary(), Style::new().cyan());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_secondary() {
|
||||
assert_eq!(Style::new().secondary(), Style::new().yellow());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_success() {
|
||||
assert_eq!(Style::new().success(), Style::new().green());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_system_function() {
|
||||
assert_eq!(Style::new().system_function(), Style::new().yellow());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_unmonitored() {
|
||||
assert_eq!(Style::new().unmonitored(), Style::new().white());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_warning() {
|
||||
assert_eq!(Style::new().warning(), Style::new().magenta());
|
||||
}
|
||||
}
|
||||
+19
-105
@@ -1,19 +1,12 @@
|
||||
use crate::ui::styles::ManagarrStyle;
|
||||
use ratatui::layout::{Alignment, Constraint, Direction, Layout, Rect};
|
||||
use ratatui::style::{Color, Modifier, Style};
|
||||
use ratatui::style::{Color, Style, Stylize};
|
||||
use ratatui::text::{Line, Span, Text};
|
||||
use ratatui::widgets::{Block, BorderType, Borders, LineGauge, Paragraph, Wrap};
|
||||
use ratatui::{symbols, Frame};
|
||||
use std::rc::Rc;
|
||||
|
||||
pub const COLOR_TEAL: Color = Color::Rgb(35, 50, 55);
|
||||
pub const COLOR_CYAN: Color = Color::Cyan;
|
||||
pub const COLOR_LIGHT_BLUE: Color = Color::LightBlue;
|
||||
pub const COLOR_YELLOW: Color = Color::Yellow;
|
||||
pub const COLOR_GREEN: Color = Color::Green;
|
||||
pub const COLOR_RED: Color = Color::Red;
|
||||
pub const COLOR_ORANGE: Color = Color::Rgb(255, 170, 66);
|
||||
pub const COLOR_WHITE: Color = Color::White;
|
||||
pub const COLOR_MAGENTA: Color = Color::Magenta;
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "utils_tests.rs"]
|
||||
@@ -60,11 +53,11 @@ fn layout_with_constraints(constraints: Vec<Constraint>) -> Layout {
|
||||
}
|
||||
|
||||
pub fn background_block<'a>() -> Block<'a> {
|
||||
Block::default().style(Style::default().bg(COLOR_TEAL).fg(COLOR_WHITE))
|
||||
Block::new().white().bg(COLOR_TEAL)
|
||||
}
|
||||
|
||||
pub fn layout_block<'a>() -> Block<'a> {
|
||||
Block::default()
|
||||
Block::new()
|
||||
.borders(Borders::ALL)
|
||||
.border_type(BorderType::Rounded)
|
||||
}
|
||||
@@ -78,11 +71,11 @@ pub fn layout_block_top_border_with_title(title_span: Span<'_>) -> Block<'_> {
|
||||
}
|
||||
|
||||
pub fn layout_block_top_border<'a>() -> Block<'a> {
|
||||
Block::default().borders(Borders::TOP)
|
||||
Block::new().borders(Borders::TOP)
|
||||
}
|
||||
|
||||
pub fn layout_block_bottom_border<'a>() -> Block<'a> {
|
||||
Block::default().borders(Borders::BOTTOM)
|
||||
Block::new().borders(Borders::BOTTOM)
|
||||
}
|
||||
|
||||
pub fn layout_button_paragraph(
|
||||
@@ -110,102 +103,26 @@ pub fn layout_button_paragraph_borderless(
|
||||
pub fn layout_paragraph_borderless(string: &str) -> Paragraph<'_> {
|
||||
Paragraph::new(Text::from(string))
|
||||
.block(borderless_block())
|
||||
.style(style_primary().add_modifier(Modifier::BOLD))
|
||||
.primary()
|
||||
.bold()
|
||||
.wrap(Wrap { trim: false })
|
||||
.alignment(Alignment::Center)
|
||||
}
|
||||
|
||||
pub fn borderless_block<'a>() -> Block<'a> {
|
||||
Block::default()
|
||||
}
|
||||
|
||||
pub fn line_info_with_style<'a>(
|
||||
title: String,
|
||||
content: String,
|
||||
title_style: Style,
|
||||
content_style: Style,
|
||||
) -> Line<'a> {
|
||||
Line::from(vec![
|
||||
Span::styled(title, title_style),
|
||||
Span::styled(content, content_style),
|
||||
])
|
||||
}
|
||||
|
||||
pub fn line_info_default<'a>(title: String, content: String) -> Line<'a> {
|
||||
line_info_with_style(title, content, style_bold(), style_default())
|
||||
}
|
||||
|
||||
pub fn line_info_primary<'a>(title: String, content: String) -> Line<'a> {
|
||||
line_info_with_style(
|
||||
title,
|
||||
content,
|
||||
style_primary().add_modifier(Modifier::BOLD),
|
||||
style_default(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn style_bold() -> Style {
|
||||
Style::default().add_modifier(Modifier::BOLD)
|
||||
}
|
||||
|
||||
pub fn style_highlight() -> Style {
|
||||
Style::default().add_modifier(Modifier::REVERSED)
|
||||
}
|
||||
|
||||
pub fn style_default() -> Style {
|
||||
Style::default().fg(COLOR_WHITE)
|
||||
}
|
||||
|
||||
pub fn style_default_bold() -> Style {
|
||||
style_default().add_modifier(Modifier::BOLD)
|
||||
}
|
||||
|
||||
pub fn style_primary() -> Style {
|
||||
Style::default().fg(COLOR_CYAN)
|
||||
}
|
||||
|
||||
pub fn style_secondary() -> Style {
|
||||
Style::default().fg(COLOR_YELLOW)
|
||||
}
|
||||
|
||||
pub fn style_system_function() -> Style {
|
||||
Style::default().fg(COLOR_YELLOW)
|
||||
}
|
||||
|
||||
pub fn style_unmonitored() -> Style {
|
||||
Style::default().fg(COLOR_WHITE)
|
||||
}
|
||||
|
||||
pub fn style_success() -> Style {
|
||||
Style::default().fg(COLOR_GREEN)
|
||||
}
|
||||
|
||||
pub fn style_warning() -> Style {
|
||||
Style::default().fg(COLOR_MAGENTA)
|
||||
}
|
||||
|
||||
pub fn style_failure() -> Style {
|
||||
Style::default().fg(COLOR_RED)
|
||||
}
|
||||
|
||||
pub fn style_awaiting_import() -> Style {
|
||||
Style::default().fg(COLOR_ORANGE)
|
||||
}
|
||||
|
||||
pub fn style_help() -> Style {
|
||||
Style::default().fg(COLOR_LIGHT_BLUE)
|
||||
Block::new()
|
||||
}
|
||||
|
||||
pub fn style_block_highlight(is_selected: bool) -> Style {
|
||||
if is_selected {
|
||||
style_system_function().add_modifier(Modifier::BOLD)
|
||||
Style::new().system_function().bold()
|
||||
} else {
|
||||
style_default_bold()
|
||||
Style::new().default().bold()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn title_style(title: &str) -> Span<'_> {
|
||||
Span::styled(format!(" {title} "), style_bold())
|
||||
format!(" {title} ").bold()
|
||||
}
|
||||
|
||||
pub fn title_block(title: &str) -> Block<'_> {
|
||||
@@ -219,10 +136,7 @@ pub fn title_block_centered(title: &str) -> Block<'_> {
|
||||
pub fn logo_block<'a>() -> Block<'a> {
|
||||
layout_block().title(Span::styled(
|
||||
" Managarr - A Servarr management TUI ",
|
||||
Style::default()
|
||||
.fg(Color::Magenta)
|
||||
.add_modifier(Modifier::BOLD)
|
||||
.add_modifier(Modifier::ITALIC),
|
||||
Style::new().magenta().bold().italic(),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -250,18 +164,18 @@ pub fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect {
|
||||
}
|
||||
|
||||
pub fn line_gauge_with_title(title: &str, ratio: f64) -> LineGauge<'_> {
|
||||
LineGauge::default()
|
||||
.block(Block::default().title(title))
|
||||
.gauge_style(Style::default().fg(COLOR_CYAN))
|
||||
LineGauge::new()
|
||||
.block(Block::new().title(title))
|
||||
.gauge_style(Style::new().cyan())
|
||||
.line_set(symbols::line::THICK)
|
||||
.ratio(ratio)
|
||||
.label(Line::from(format!("{:.0}%", ratio * 100.0)))
|
||||
}
|
||||
|
||||
pub fn line_gauge_with_label(title: &str, ratio: f64) -> LineGauge<'_> {
|
||||
LineGauge::default()
|
||||
.block(Block::default())
|
||||
.gauge_style(Style::default().fg(COLOR_CYAN))
|
||||
LineGauge::new()
|
||||
.block(Block::new())
|
||||
.gauge_style(Style::new().cyan())
|
||||
.line_set(symbols::line::THICK)
|
||||
.ratio(ratio)
|
||||
.label(Line::from(format!("{title}: {:.0}%", ratio * 100.0)))
|
||||
|
||||
+4
-133
@@ -3,17 +3,14 @@ mod test {
|
||||
use pretty_assertions::assert_eq;
|
||||
use ratatui::layout::{Alignment, Constraint, Direction, Layout, Rect};
|
||||
use ratatui::style::{Color, Modifier, Style};
|
||||
use ratatui::text::{Line, Span};
|
||||
use ratatui::text::Span;
|
||||
use ratatui::widgets::{Block, BorderType, Borders};
|
||||
|
||||
use crate::ui::utils::{
|
||||
borderless_block, centered_rect, get_width_from_percentage, horizontal_chunks,
|
||||
horizontal_chunks_with_margin, layout_block, layout_block_bottom_border,
|
||||
layout_block_top_border, layout_block_top_border_with_title, layout_block_with_title,
|
||||
layout_with_constraints, line_info_default, line_info_primary, line_info_with_style,
|
||||
logo_block, style_block_highlight, style_bold, style_default, style_default_bold,
|
||||
style_failure, style_help, style_highlight, style_primary, style_secondary, style_success,
|
||||
style_system_function, style_unmonitored, style_warning, title_block, title_block_centered,
|
||||
layout_with_constraints, logo_block, style_block_highlight, title_block, title_block_centered,
|
||||
title_style, vertical_chunks, vertical_chunks_with_margin,
|
||||
};
|
||||
|
||||
@@ -176,132 +173,6 @@ mod test {
|
||||
assert_eq!(borderless_block(), Block::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_line_info_with_style() {
|
||||
let first_style = Style::default()
|
||||
.fg(Color::DarkGray)
|
||||
.add_modifier(Modifier::BOLD);
|
||||
let second_style = Style::default()
|
||||
.fg(Color::LightYellow)
|
||||
.add_modifier(Modifier::ITALIC);
|
||||
let expected_lines = Line::from(vec![
|
||||
Span::styled("title".to_owned(), first_style),
|
||||
Span::styled("content".to_owned(), second_style),
|
||||
]);
|
||||
|
||||
assert_eq!(
|
||||
line_info_with_style(
|
||||
"title".to_owned(),
|
||||
"content".to_owned(),
|
||||
first_style,
|
||||
second_style
|
||||
),
|
||||
expected_lines
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_line_info_default() {
|
||||
let expected_line = Line::from(vec![
|
||||
Span::styled(
|
||||
"title".to_owned(),
|
||||
Style::default().add_modifier(Modifier::BOLD),
|
||||
),
|
||||
Span::styled("content".to_owned(), Style::default().fg(Color::White)),
|
||||
]);
|
||||
|
||||
assert_eq!(
|
||||
line_info_default("title".to_owned(), "content".to_owned()),
|
||||
expected_line
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_line_info_primary() {
|
||||
let expected_line = Line::from(vec![
|
||||
Span::styled(
|
||||
"title".to_owned(),
|
||||
Style::default()
|
||||
.fg(Color::Cyan)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
),
|
||||
Span::styled("content".to_owned(), Style::default().fg(Color::White)),
|
||||
]);
|
||||
|
||||
assert_eq!(
|
||||
line_info_primary("title".to_owned(), "content".to_owned()),
|
||||
expected_line
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_bold() {
|
||||
assert_eq!(style_bold(), Style::default().add_modifier(Modifier::BOLD));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_highlight() {
|
||||
assert_eq!(
|
||||
style_highlight(),
|
||||
Style::default().add_modifier(Modifier::REVERSED)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_unmonitored() {
|
||||
assert_eq!(style_unmonitored(), Style::default().fg(Color::White));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_default() {
|
||||
assert_eq!(style_default(), Style::default().fg(Color::White));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_default_bold() {
|
||||
assert_eq!(
|
||||
style_default_bold(),
|
||||
Style::default()
|
||||
.fg(Color::White)
|
||||
.add_modifier(Modifier::BOLD)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_primary() {
|
||||
assert_eq!(style_primary(), Style::default().fg(Color::Cyan));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_secondary() {
|
||||
assert_eq!(style_secondary(), Style::default().fg(Color::Yellow));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_system_function() {
|
||||
assert_eq!(style_system_function(), Style::default().fg(Color::Yellow));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_success() {
|
||||
assert_eq!(style_success(), Style::default().fg(Color::Green));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_warning() {
|
||||
assert_eq!(style_warning(), Style::default().fg(Color::Magenta));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_failure() {
|
||||
assert_eq!(style_failure(), Style::default().fg(Color::Red));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_help() {
|
||||
assert_eq!(style_help(), Style::default().fg(Color::LightBlue));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_style_button_highlight_selected() {
|
||||
let expected_style = Style::default()
|
||||
@@ -390,9 +261,9 @@ mod test {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 100,
|
||||
height: 10
|
||||
height: 10,
|
||||
},
|
||||
30
|
||||
30,
|
||||
),
|
||||
30
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user