From 0f9dc639a8a71d53003cf2b5b9f64ae22aefe25f Mon Sep 17 00:00:00 2001 From: Dark-Alex-17 Date: Tue, 8 Aug 2023 10:50:05 -0600 Subject: [PATCH] Cleaned up some code with some macros --- src/handlers/mod.rs | 15 ++ .../radarr_handlers/add_movie_handler.rs | 12 +- src/handlers/radarr_handlers/mod.rs | 26 +- .../radarr_handlers/movie_details_handler.rs | 2 +- src/main.rs | 1 - src/models/mod.rs | 228 +++++++----------- src/ui/mod.rs | 16 +- src/ui/radarr_ui/add_movie_ui.rs | 18 +- src/ui/radarr_ui/movie_details_ui.rs | 8 +- src/ui/utils.rs | 10 +- 10 files changed, 132 insertions(+), 204 deletions(-) diff --git a/src/handlers/mod.rs b/src/handlers/mod.rs index 28a057b..7c677a6 100644 --- a/src/handlers/mod.rs +++ b/src/handlers/mod.rs @@ -62,3 +62,18 @@ fn handle_prompt_toggle(app: &mut App, key: &Key) { _ => (), } } + +#[macro_export] +macro_rules! handle_text_box_keys { + ($self:expr, $key:expr, $input:expr) => { + match $self.key { + _ if *$key == DEFAULT_KEYBINDINGS.backspace.key => { + $input.pop(); + } + Key::Char(character) => { + $input.push(*character); + } + _ => (), + } + }; +} diff --git a/src/handlers/radarr_handlers/add_movie_handler.rs b/src/handlers/radarr_handlers/add_movie_handler.rs index 71eb1f9..9774cbc 100644 --- a/src/handlers/radarr_handlers/add_movie_handler.rs +++ b/src/handlers/radarr_handlers/add_movie_handler.rs @@ -4,7 +4,7 @@ use crate::handlers::{handle_prompt_toggle, KeyEventHandler}; use crate::models::radarr_models::{MinimumAvailability, Monitor}; use crate::models::{Scrollable, StatefulTable}; use crate::network::radarr_network::RadarrEvent; -use crate::{App, Key}; +use crate::{handle_text_box_keys, App, Key}; pub(super) struct AddMovieHandler<'a> { key: &'a Key, @@ -259,15 +259,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> { fn handle_char_key_event(&mut self) { let key = self.key; if self.active_radarr_block == &ActiveRadarrBlock::AddMovieSearchInput { - match self.key { - _ if *key == DEFAULT_KEYBINDINGS.backspace.key => { - self.app.data.radarr_data.search.pop(); - } - Key::Char(character) => { - self.app.data.radarr_data.search.push(*character); - } - _ => (), - } + handle_text_box_keys!(self, key, self.app.data.radarr_data.search) } } } diff --git a/src/handlers/radarr_handlers/mod.rs b/src/handlers/radarr_handlers/mod.rs index e9a1af1..ef78741 100644 --- a/src/handlers/radarr_handlers/mod.rs +++ b/src/handlers/radarr_handlers/mod.rs @@ -7,7 +7,7 @@ use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler use crate::models::Scrollable; use crate::network::radarr_network::RadarrEvent; use crate::utils::strip_non_alphanumeric_characters; -use crate::{App, Key}; +use crate::{handle_text_box_keys, App, Key}; mod add_movie_handler; mod collection_details_handler; @@ -425,24 +425,12 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> { } _ => (), }, - ActiveRadarrBlock::SearchMovie | ActiveRadarrBlock::SearchCollection => match self.key { - _ if *key == DEFAULT_KEYBINDINGS.backspace.key => { - self.app.data.radarr_data.search.pop(); - } - Key::Char(character) => { - self.app.data.radarr_data.search.push(*character); - } - _ => (), - }, - ActiveRadarrBlock::FilterMovies | ActiveRadarrBlock::FilterCollections => match self.key { - _ if *key == DEFAULT_KEYBINDINGS.backspace.key => { - self.app.data.radarr_data.filter.pop(); - } - Key::Char(character) => { - self.app.data.radarr_data.filter.push(*character); - } - _ => (), - }, + ActiveRadarrBlock::SearchMovie | ActiveRadarrBlock::SearchCollection => { + handle_text_box_keys!(self, key, self.app.data.radarr_data.search) + } + ActiveRadarrBlock::FilterMovies | ActiveRadarrBlock::FilterCollections => { + handle_text_box_keys!(self, key, self.app.data.radarr_data.filter) + } _ => (), } } diff --git a/src/handlers/radarr_handlers/movie_details_handler.rs b/src/handlers/radarr_handlers/movie_details_handler.rs index 21ee7df..885186f 100644 --- a/src/handlers/radarr_handlers/movie_details_handler.rs +++ b/src/handlers/radarr_handlers/movie_details_handler.rs @@ -3,7 +3,7 @@ use crate::app::radarr::ActiveRadarrBlock; use crate::app::App; use crate::event::Key; use crate::handlers::{handle_prompt_toggle, KeyEventHandler}; -use crate::models::{Scrollable, StatefulTable}; +use crate::models::Scrollable; use crate::network::radarr_network::RadarrEvent; pub(super) struct MovieDetailsHandler<'a> { diff --git a/src/main.rs b/src/main.rs index 83d129d..02e3882 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,5 @@ use std::io; use std::sync::Arc; -use std::time::Duration; use anyhow::Result; use clap::Parser; diff --git a/src/models/mod.rs b/src/models/mod.rs index 39b9b74..bee6116 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -27,172 +27,106 @@ pub trait Scrollable { fn scroll_to_bottom(&mut self); } -pub struct StatefulList { - pub state: ListState, - pub items: Vec, -} - -impl Default for StatefulList { - fn default() -> StatefulList { - StatefulList { - state: ListState::default(), - items: Vec::new(), +macro_rules! stateful_iterable { + ($name:ident, $state:ty) => { + pub struct $name { + pub state: $state, + pub items: Vec, } - } -} -impl Scrollable for StatefulList { - fn scroll_down(&mut self) { - let selected_row = match self.state.selected() { - Some(i) => { - if i >= self.items.len() - 1 { - 0 - } else { - i + 1 + impl Default for $name { + fn default() -> $name { + $name { + state: <$state>::default(), + items: Vec::new(), } } - None => 0, - }; + } - self.state.select(Some(selected_row)); - } + impl Scrollable for $name { + fn scroll_down(&mut self) { + let selected_row = match self.state.selected() { + Some(i) => { + if i >= self.items.len() - 1 { + 0 + } else { + i + 1 + } + } + None => 0, + }; - fn scroll_up(&mut self) { - let selected_row = match self.state.selected() { - Some(i) => { - if i == 0 { - self.items.len() - 1 - } else { - i - 1 + self.state.select(Some(selected_row)); + } + + fn scroll_up(&mut self) { + let selected_row = match self.state.selected() { + Some(i) => { + if i == 0 { + self.items.len() - 1 + } else { + i - 1 + } + } + None => 0, + }; + + self.state.select(Some(selected_row)); + } + + fn scroll_to_top(&mut self) { + self.state.select(Some(0)); + } + + fn scroll_to_bottom(&mut self) { + self.state.select(Some(self.items.len() - 1)); + } + } + + impl $name + where + T: Clone + PartialEq + Eq + Debug, + { + pub fn set_items(&mut self, items: Vec) { + let items_len = items.len(); + self.items = items; + if !self.items.is_empty() { + let selected_row = self.state.selected().map_or(0, |i| { + if i > 0 && i < items_len { + i + } else if i >= items_len { + items_len - 1 + } else { + 0 + } + }); + self.state.select(Some(selected_row)); } } - None => 0, - }; - self.state.select(Some(selected_row)); - } + pub fn current_selection(&self) -> &T { + &self.items[self.state.selected().unwrap_or(0)] + } - fn scroll_to_top(&mut self) { - self.state.select(Some(0)); - } - - fn scroll_to_bottom(&mut self) { - self.state.select(Some(self.items.len() - 1)); - } -} - -impl StatefulList { - pub fn set_items(&mut self, items: Vec) { - let items_len = items.len(); - self.items = items; - if !self.items.is_empty() { - let selected_row = self.state.selected().map_or(0, |i| { - if i > 0 && i < items_len { - i - } else if i >= items_len { - items_len - 1 - } else { - 0 - } - }); - self.state.select(Some(selected_row)); + pub fn current_selection_clone(&self) -> T { + self.items[self.state.selected().unwrap_or(0)].clone() + } } - } - - pub fn current_selection(&self) -> &T { - &self.items[self.state.selected().unwrap_or(0)] - } - - pub fn current_selection_clone(&self) -> T { - self.items[self.state.selected().unwrap_or(0)].clone() - } + }; } -pub struct StatefulTable { - pub state: TableState, - pub items: Vec, -} - -impl Default for StatefulTable { - fn default() -> StatefulTable { - StatefulTable { - state: TableState::default(), - items: Vec::new(), - } - } -} - -impl StatefulTable { - pub fn set_items(&mut self, items: Vec) { - let items_len = items.len(); - self.items = items; - if !self.items.is_empty() { - let selected_row = self.state.selected().map_or(0, |i| { - if i > 0 && i < items_len { - i - } else if i >= items_len { - items_len - 1 - } else { - 0 - } - }); - self.state.select(Some(selected_row)); - } - } - - pub fn current_selection(&self) -> &T { - &self.items[self.state.selected().unwrap_or(0)] - } - - pub fn current_selection_clone(&self) -> T { - self.items[self.state.selected().unwrap_or(0)].clone() - } +stateful_iterable!(StatefulList, ListState); +stateful_iterable!(StatefulTable, TableState); +impl StatefulTable +where + T: Clone + PartialEq + Eq + Debug, +{ pub fn select_index(&mut self, index: Option) { self.state.select(index); } } -impl Scrollable for StatefulTable { - fn scroll_down(&mut self) { - let selected_row = match self.state.selected() { - Some(i) => { - if i >= self.items.len() - 1 { - 0 - } else { - i + 1 - } - } - None => 0, - }; - - self.state.select(Some(selected_row)); - } - - fn scroll_up(&mut self) { - let selected_row = match self.state.selected() { - Some(i) => { - if i == 0 { - self.items.len() - 1 - } else { - i - 1 - } - } - None => 0, - }; - - self.state.select(Some(selected_row)); - } - - fn scroll_to_top(&mut self) { - self.state.select(Some(0)); - } - - fn scroll_to_bottom(&mut self) { - self.state.select(Some(self.items.len() - 1)); - } -} - #[derive(Default)] pub struct ScrollableText { pub items: Vec, diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 4b04b5a..6a6ef49 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -1,6 +1,5 @@ use tui::backend::Backend; use tui::layout::{Alignment, Constraint, Rect}; -use tui::style::Modifier; use tui::text::{Span, Spans, Text}; use tui::widgets::Paragraph; use tui::widgets::Row; @@ -14,10 +13,11 @@ use crate::app::App; use crate::models::{Route, StatefulList, StatefulTable, TabState}; use crate::ui::utils::{ borderless_block, centered_rect, horizontal_chunks, horizontal_chunks_with_margin, layout_block, - layout_block_top_border, layout_button_paragraph, layout_button_paragraph_borderless, logo_block, - style_button_highlight, style_default_bold, style_failure, style_help, style_highlight, - style_primary, style_secondary, style_system_function, title_block, title_block_centered, - vertical_chunks, vertical_chunks_with_margin, + layout_block_top_border, layout_button_paragraph, layout_button_paragraph_borderless, + layout_paragraph_borderless, logo_block, style_button_highlight, style_default_bold, + style_failure, style_help, style_highlight, style_primary, style_secondary, + style_system_function, title_block, title_block_centered, vertical_chunks, + vertical_chunks_with_margin, }; mod radarr_ui; @@ -338,11 +338,7 @@ pub fn draw_prompt_box_with_content( ) }; - let prompt_paragraph = Paragraph::new(Text::from(prompt)) - .block(borderless_block()) - .style(style_primary().add_modifier(Modifier::BOLD)) - .wrap(Wrap { trim: false }) - .alignment(Alignment::Center); + let prompt_paragraph = layout_paragraph_borderless(prompt); f.render_widget(prompt_paragraph, chunks[0]); let horizontal_chunks = horizontal_chunks( diff --git a/src/ui/radarr_ui/add_movie_ui.rs b/src/ui/radarr_ui/add_movie_ui.rs index 685e50a..c94642d 100644 --- a/src/ui/radarr_ui/add_movie_ui.rs +++ b/src/ui/radarr_ui/add_movie_ui.rs @@ -1,16 +1,16 @@ use tui::backend::Backend; use tui::layout::{Alignment, Constraint, Rect}; -use tui::style::Modifier; use tui::text::Text; -use tui::widgets::{Cell, ListItem, Paragraph, Row, Wrap}; +use tui::widgets::{Cell, ListItem, Paragraph, Row}; use tui::Frame; use crate::app::radarr::ActiveRadarrBlock; use crate::models::radarr_models::AddMovieSearchResult; use crate::models::Route; use crate::ui::utils::{ - borderless_block, get_width, horizontal_chunks, layout_block, show_cursor, style_default, - style_help, style_primary, title_block_centered, vertical_chunks_with_margin, + borderless_block, get_width, horizontal_chunks, layout_block, layout_paragraph_borderless, + show_cursor, style_default, style_help, style_primary, title_block_centered, + vertical_chunks_with_margin, }; use crate::ui::{ draw_button, draw_drop_down_list, draw_drop_down_menu_button, draw_drop_down_popup, @@ -293,11 +293,7 @@ fn draw_confirmation_prompt(f: &mut Frame<'_, B>, app: &mut App, pro 1, ); - let prompt_paragraph = Paragraph::new(Text::from(prompt)) - .block(borderless_block()) - .style(style_primary().add_modifier(Modifier::BOLD)) - .wrap(Wrap { trim: false }) - .alignment(Alignment::Center); + let prompt_paragraph = layout_paragraph_borderless(&prompt); f.render_widget(prompt_paragraph, chunks[0]); let horizontal_chunks = horizontal_chunks( @@ -331,13 +327,13 @@ fn draw_confirmation_prompt(f: &mut Frame<'_, B>, app: &mut App, pro draw_button( f, horizontal_chunks[0], - "Yes", + "Add", *yes_no_value && highlight_yes_no, ); draw_button( f, horizontal_chunks[1], - "No", + "Cancel", !*yes_no_value && highlight_yes_no, ); } diff --git a/src/ui/radarr_ui/movie_details_ui.rs b/src/ui/radarr_ui/movie_details_ui.rs index cfb43e5..e659a85 100644 --- a/src/ui/radarr_ui/movie_details_ui.rs +++ b/src/ui/radarr_ui/movie_details_ui.rs @@ -13,12 +13,12 @@ use crate::models::radarr_models::{Credit, MovieHistoryItem, Release}; use crate::models::Route; use crate::ui::utils::{ borderless_block, get_width, layout_block_bottom_border, layout_block_top_border, - spans_info_default, spans_info_primary, style_bold, style_default, style_failure, style_primary, - style_success, style_warning, vertical_chunks, + spans_info_default, style_bold, style_default, style_failure, style_primary, style_success, + style_warning, vertical_chunks, }; use crate::ui::{ - draw_medium_popup_over, draw_prompt_box, draw_prompt_box_with_content, draw_prompt_popup_over, - draw_small_popup_over, draw_table, draw_tabs, loading, TableProps, + draw_prompt_box, draw_prompt_box_with_content, draw_prompt_popup_over, draw_small_popup_over, + draw_table, draw_tabs, loading, TableProps, }; use crate::utils::convert_to_gb; diff --git a/src/ui/utils.rs b/src/ui/utils.rs index 823863b..9a0c293 100644 --- a/src/ui/utils.rs +++ b/src/ui/utils.rs @@ -2,7 +2,7 @@ use tui::backend::Backend; use tui::layout::{Alignment, Constraint, Direction, Layout, Rect}; use tui::style::{Color, Modifier, Style}; use tui::text::{Span, Spans, Text}; -use tui::widgets::{Block, Borders, LineGauge, Paragraph}; +use tui::widgets::{Block, Borders, LineGauge, Paragraph, Wrap}; use tui::{symbols, Frame}; pub fn horizontal_chunks(constraints: Vec, size: Rect) -> Vec { @@ -83,6 +83,14 @@ pub fn layout_button_paragraph_borderless( .style(style_button_highlight(is_selected)) } +pub fn layout_paragraph_borderless(string: &str) -> Paragraph { + Paragraph::new(Text::from(string)) + .block(borderless_block()) + .style(style_primary().add_modifier(Modifier::BOLD)) + .wrap(Wrap { trim: false }) + .alignment(Alignment::Center) +} + pub fn borderless_block<'a>() -> Block<'a> { Block::default() }