Cleaned up some code with some macros
This commit is contained in:
@@ -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::{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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use std::io;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
|
||||
+81
-147
@@ -27,172 +27,106 @@ pub trait Scrollable {
|
||||
fn scroll_to_bottom(&mut self);
|
||||
}
|
||||
|
||||
pub struct StatefulList<T> {
|
||||
pub state: ListState,
|
||||
pub items: Vec<T>,
|
||||
}
|
||||
|
||||
impl<T> Default for StatefulList<T> {
|
||||
fn default() -> StatefulList<T> {
|
||||
StatefulList {
|
||||
state: ListState::default(),
|
||||
items: Vec::new(),
|
||||
macro_rules! stateful_iterable {
|
||||
($name:ident, $state:ty) => {
|
||||
pub struct $name<T> {
|
||||
pub state: $state,
|
||||
pub items: Vec<T>,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Scrollable for StatefulList<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
|
||||
impl<T> Default for $name<T> {
|
||||
fn default() -> $name<T> {
|
||||
$name {
|
||||
state: <$state>::default(),
|
||||
items: Vec::new(),
|
||||
}
|
||||
}
|
||||
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) {
|
||||
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<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) {
|
||||
self.state.select(Some(0));
|
||||
}
|
||||
|
||||
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_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<T> {
|
||||
pub state: 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()
|
||||
}
|
||||
stateful_iterable!(StatefulList, ListState);
|
||||
stateful_iterable!(StatefulTable, TableState);
|
||||
|
||||
impl<T> StatefulTable<T>
|
||||
where
|
||||
T: Clone + PartialEq + Eq + Debug,
|
||||
{
|
||||
pub fn select_index(&mut self, index: Option<usize>) {
|
||||
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)]
|
||||
pub struct ScrollableText {
|
||||
pub items: Vec<String>,
|
||||
|
||||
+6
-10
@@ -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<B: Backend>(
|
||||
)
|
||||
};
|
||||
|
||||
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(
|
||||
|
||||
@@ -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<B: Backend>(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<B: Backend>(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,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
+9
-1
@@ -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<Constraint>, size: Rect) -> Vec<Rect> {
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user