Cleaned up some code with some macros

This commit is contained in:
2023-08-08 10:50:05 -06:00
parent cab3d1d50d
commit 0f9dc639a8
10 changed files with 132 additions and 204 deletions
+15
View File
@@ -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);
}
_ => (),
}
};
}
@@ -4,7 +4,7 @@ use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
use crate::models::radarr_models::{MinimumAvailability, Monitor}; use crate::models::radarr_models::{MinimumAvailability, Monitor};
use crate::models::{Scrollable, StatefulTable}; use crate::models::{Scrollable, StatefulTable};
use crate::network::radarr_network::RadarrEvent; use crate::network::radarr_network::RadarrEvent;
use crate::{App, Key}; use crate::{handle_text_box_keys, App, Key};
pub(super) struct AddMovieHandler<'a> { pub(super) struct AddMovieHandler<'a> {
key: &'a Key, key: &'a Key,
@@ -259,15 +259,7 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for AddMovieHandler<'a> {
fn handle_char_key_event(&mut self) { fn handle_char_key_event(&mut self) {
let key = self.key; let key = self.key;
if self.active_radarr_block == &ActiveRadarrBlock::AddMovieSearchInput { if self.active_radarr_block == &ActiveRadarrBlock::AddMovieSearchInput {
match self.key { handle_text_box_keys!(self, key, self.app.data.radarr_data.search)
_ if *key == DEFAULT_KEYBINDINGS.backspace.key => {
self.app.data.radarr_data.search.pop();
}
Key::Char(character) => {
self.app.data.radarr_data.search.push(*character);
}
_ => (),
}
} }
} }
} }
+7 -19
View File
@@ -7,7 +7,7 @@ use crate::handlers::{handle_clear_errors, handle_prompt_toggle, KeyEventHandler
use crate::models::Scrollable; use crate::models::Scrollable;
use crate::network::radarr_network::RadarrEvent; use crate::network::radarr_network::RadarrEvent;
use crate::utils::strip_non_alphanumeric_characters; use crate::utils::strip_non_alphanumeric_characters;
use crate::{App, Key}; use crate::{handle_text_box_keys, App, Key};
mod add_movie_handler; mod add_movie_handler;
mod collection_details_handler; mod collection_details_handler;
@@ -425,24 +425,12 @@ impl<'a> KeyEventHandler<'a, ActiveRadarrBlock> for RadarrHandler<'a> {
} }
_ => (), _ => (),
}, },
ActiveRadarrBlock::SearchMovie | ActiveRadarrBlock::SearchCollection => match self.key { ActiveRadarrBlock::SearchMovie | ActiveRadarrBlock::SearchCollection => {
_ if *key == DEFAULT_KEYBINDINGS.backspace.key => { handle_text_box_keys!(self, key, self.app.data.radarr_data.search)
self.app.data.radarr_data.search.pop(); }
} ActiveRadarrBlock::FilterMovies | ActiveRadarrBlock::FilterCollections => {
Key::Char(character) => { handle_text_box_keys!(self, key, self.app.data.radarr_data.filter)
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);
}
_ => (),
},
_ => (), _ => (),
} }
} }
@@ -3,7 +3,7 @@ use crate::app::radarr::ActiveRadarrBlock;
use crate::app::App; use crate::app::App;
use crate::event::Key; use crate::event::Key;
use crate::handlers::{handle_prompt_toggle, KeyEventHandler}; use crate::handlers::{handle_prompt_toggle, KeyEventHandler};
use crate::models::{Scrollable, StatefulTable}; use crate::models::Scrollable;
use crate::network::radarr_network::RadarrEvent; use crate::network::radarr_network::RadarrEvent;
pub(super) struct MovieDetailsHandler<'a> { pub(super) struct MovieDetailsHandler<'a> {
-1
View File
@@ -1,6 +1,5 @@
use std::io; use std::io;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration;
use anyhow::Result; use anyhow::Result;
use clap::Parser; use clap::Parser;
+81 -147
View File
@@ -27,172 +27,106 @@ pub trait Scrollable {
fn scroll_to_bottom(&mut self); fn scroll_to_bottom(&mut self);
} }
pub struct StatefulList<T> { macro_rules! stateful_iterable {
pub state: ListState, ($name:ident, $state:ty) => {
pub items: Vec<T>, pub struct $name<T> {
} pub state: $state,
pub items: Vec<T>,
impl<T> Default for StatefulList<T> {
fn default() -> StatefulList<T> {
StatefulList {
state: ListState::default(),
items: Vec::new(),
} }
}
}
impl<T> Scrollable for StatefulList<T> { impl<T> Default for $name<T> {
fn scroll_down(&mut self) { fn default() -> $name<T> {
let selected_row = match self.state.selected() { $name {
Some(i) => { state: <$state>::default(),
if i >= self.items.len() - 1 { items: Vec::new(),
0
} else {
i + 1
} }
} }
None => 0, }
};
self.state.select(Some(selected_row)); impl<T> Scrollable for $name<T> {
} 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) { self.state.select(Some(selected_row));
let selected_row = match self.state.selected() { }
Some(i) => {
if i == 0 { fn scroll_up(&mut self) {
self.items.len() - 1 let selected_row = match self.state.selected() {
} else { Some(i) => {
i - 1 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<T> $name<T>
where
T: Clone + PartialEq + Eq + Debug,
{
pub fn set_items(&mut self, items: Vec<T>) {
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) { pub fn current_selection_clone(&self) -> T {
self.state.select(Some(0)); self.items[self.state.selected().unwrap_or(0)].clone()
} }
fn scroll_to_bottom(&mut self) {
self.state.select(Some(self.items.len() - 1));
}
}
impl<T: Clone + PartialEq + Eq + Debug> StatefulList<T> {
pub fn set_items(&mut self, items: Vec<T>) {
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()
}
} }
pub struct StatefulTable<T> { stateful_iterable!(StatefulList, ListState);
pub state: TableState, stateful_iterable!(StatefulTable, TableState);
pub items: Vec<T>,
}
impl<T> Default for StatefulTable<T> {
fn default() -> StatefulTable<T> {
StatefulTable {
state: TableState::default(),
items: Vec::new(),
}
}
}
impl<T: Clone + PartialEq + Eq + Debug> StatefulTable<T> {
pub fn set_items(&mut self, items: Vec<T>) {
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()
}
impl<T> StatefulTable<T>
where
T: Clone + PartialEq + Eq + Debug,
{
pub fn select_index(&mut self, index: Option<usize>) { pub fn select_index(&mut self, index: Option<usize>) {
self.state.select(index); self.state.select(index);
} }
} }
impl<T> Scrollable for StatefulTable<T> {
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)] #[derive(Default)]
pub struct ScrollableText { pub struct ScrollableText {
pub items: Vec<String>, pub items: Vec<String>,
+6 -10
View File
@@ -1,6 +1,5 @@
use tui::backend::Backend; use tui::backend::Backend;
use tui::layout::{Alignment, Constraint, Rect}; use tui::layout::{Alignment, Constraint, Rect};
use tui::style::Modifier;
use tui::text::{Span, Spans, Text}; use tui::text::{Span, Spans, Text};
use tui::widgets::Paragraph; use tui::widgets::Paragraph;
use tui::widgets::Row; use tui::widgets::Row;
@@ -14,10 +13,11 @@ use crate::app::App;
use crate::models::{Route, StatefulList, StatefulTable, TabState}; use crate::models::{Route, StatefulList, StatefulTable, TabState};
use crate::ui::utils::{ use crate::ui::utils::{
borderless_block, centered_rect, horizontal_chunks, horizontal_chunks_with_margin, layout_block, 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, layout_block_top_border, layout_button_paragraph, layout_button_paragraph_borderless,
style_button_highlight, style_default_bold, style_failure, style_help, style_highlight, layout_paragraph_borderless, logo_block, style_button_highlight, style_default_bold,
style_primary, style_secondary, style_system_function, title_block, title_block_centered, style_failure, style_help, style_highlight, style_primary, style_secondary,
vertical_chunks, vertical_chunks_with_margin, style_system_function, title_block, title_block_centered, vertical_chunks,
vertical_chunks_with_margin,
}; };
mod radarr_ui; mod radarr_ui;
@@ -338,11 +338,7 @@ pub fn draw_prompt_box_with_content<B: Backend>(
) )
}; };
let prompt_paragraph = Paragraph::new(Text::from(prompt)) let prompt_paragraph = layout_paragraph_borderless(prompt);
.block(borderless_block())
.style(style_primary().add_modifier(Modifier::BOLD))
.wrap(Wrap { trim: false })
.alignment(Alignment::Center);
f.render_widget(prompt_paragraph, chunks[0]); f.render_widget(prompt_paragraph, chunks[0]);
let horizontal_chunks = horizontal_chunks( let horizontal_chunks = horizontal_chunks(
+7 -11
View File
@@ -1,16 +1,16 @@
use tui::backend::Backend; use tui::backend::Backend;
use tui::layout::{Alignment, Constraint, Rect}; use tui::layout::{Alignment, Constraint, Rect};
use tui::style::Modifier;
use tui::text::Text; use tui::text::Text;
use tui::widgets::{Cell, ListItem, Paragraph, Row, Wrap}; use tui::widgets::{Cell, ListItem, Paragraph, Row};
use tui::Frame; use tui::Frame;
use crate::app::radarr::ActiveRadarrBlock; use crate::app::radarr::ActiveRadarrBlock;
use crate::models::radarr_models::AddMovieSearchResult; use crate::models::radarr_models::AddMovieSearchResult;
use crate::models::Route; use crate::models::Route;
use crate::ui::utils::{ use crate::ui::utils::{
borderless_block, get_width, horizontal_chunks, layout_block, show_cursor, style_default, borderless_block, get_width, horizontal_chunks, layout_block, layout_paragraph_borderless,
style_help, style_primary, title_block_centered, vertical_chunks_with_margin, show_cursor, style_default, style_help, style_primary, title_block_centered,
vertical_chunks_with_margin,
}; };
use crate::ui::{ use crate::ui::{
draw_button, draw_drop_down_list, draw_drop_down_menu_button, draw_drop_down_popup, draw_button, draw_drop_down_list, draw_drop_down_menu_button, draw_drop_down_popup,
@@ -293,11 +293,7 @@ fn draw_confirmation_prompt<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, pro
1, 1,
); );
let prompt_paragraph = Paragraph::new(Text::from(prompt)) let prompt_paragraph = layout_paragraph_borderless(&prompt);
.block(borderless_block())
.style(style_primary().add_modifier(Modifier::BOLD))
.wrap(Wrap { trim: false })
.alignment(Alignment::Center);
f.render_widget(prompt_paragraph, chunks[0]); f.render_widget(prompt_paragraph, chunks[0]);
let horizontal_chunks = horizontal_chunks( let horizontal_chunks = horizontal_chunks(
@@ -331,13 +327,13 @@ fn draw_confirmation_prompt<B: Backend>(f: &mut Frame<'_, B>, app: &mut App, pro
draw_button( draw_button(
f, f,
horizontal_chunks[0], horizontal_chunks[0],
"Yes", "Add",
*yes_no_value && highlight_yes_no, *yes_no_value && highlight_yes_no,
); );
draw_button( draw_button(
f, f,
horizontal_chunks[1], horizontal_chunks[1],
"No", "Cancel",
!*yes_no_value && highlight_yes_no, !*yes_no_value && highlight_yes_no,
); );
} }
+4 -4
View File
@@ -13,12 +13,12 @@ use crate::models::radarr_models::{Credit, MovieHistoryItem, Release};
use crate::models::Route; use crate::models::Route;
use crate::ui::utils::{ use crate::ui::utils::{
borderless_block, get_width, layout_block_bottom_border, layout_block_top_border, 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, spans_info_default, style_bold, style_default, style_failure, style_primary, style_success,
style_success, style_warning, vertical_chunks, style_warning, vertical_chunks,
}; };
use crate::ui::{ use crate::ui::{
draw_medium_popup_over, draw_prompt_box, draw_prompt_box_with_content, draw_prompt_popup_over, draw_prompt_box, draw_prompt_box_with_content, draw_prompt_popup_over, draw_small_popup_over,
draw_small_popup_over, draw_table, draw_tabs, loading, TableProps, draw_table, draw_tabs, loading, TableProps,
}; };
use crate::utils::convert_to_gb; use crate::utils::convert_to_gb;
+9 -1
View File
@@ -2,7 +2,7 @@ use tui::backend::Backend;
use tui::layout::{Alignment, Constraint, Direction, Layout, Rect}; use tui::layout::{Alignment, Constraint, Direction, Layout, Rect};
use tui::style::{Color, Modifier, Style}; use tui::style::{Color, Modifier, Style};
use tui::text::{Span, Spans, Text}; 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}; use tui::{symbols, Frame};
pub fn horizontal_chunks(constraints: Vec<Constraint>, size: Rect) -> Vec<Rect> { pub fn horizontal_chunks(constraints: Vec<Constraint>, size: Rect) -> Vec<Rect> {
@@ -83,6 +83,14 @@ pub fn layout_button_paragraph_borderless(
.style(style_button_highlight(is_selected)) .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> { pub fn borderless_block<'a>() -> Block<'a> {
Block::default() Block::default()
} }