refactor: Simplified both the table_handler macro and the stateful_table implementation

This commit is contained in:
2025-12-04 11:34:45 -07:00
parent 659023d561
commit 71240373c0
14 changed files with 192 additions and 419 deletions
@@ -38,7 +38,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for BlocklistHandler<'a,
let blocklist_table_handling_config =
TableHandlingConfig::new(ActiveRadarrBlock::Blocklist.into())
.sorting_block(ActiveRadarrBlock::BlocklistSortPrompt.into())
.sort_by_fn(|a: &BlocklistItem, b: &BlocklistItem| a.id.cmp(&b.id))
.sort_options(blocklist_sorting_options());
if !self.handle_blocklist_table_events(blocklist_table_handling_config) {
@@ -42,7 +42,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for CollectionsHandler<'
let collections_table_handling_config =
TableHandlingConfig::new(ActiveRadarrBlock::Collections.into())
.sorting_block(ActiveRadarrBlock::CollectionsSortPrompt.into())
.sort_by_fn(|a: &Collection, b: &Collection| a.id.cmp(&b.id))
.sort_options(collections_sorting_options())
.searching_block(ActiveRadarrBlock::SearchCollection.into())
.search_error_block(ActiveRadarrBlock::SearchCollectionError.into())
@@ -7,7 +7,6 @@ use crate::models::servarr_data::radarr::modals::AddMovieModal;
use crate::models::servarr_data::radarr::radarr_data::{
ADD_MOVIE_BLOCKS, ADD_MOVIE_SELECTION_BLOCKS, ActiveRadarrBlock,
};
use crate::models::stateful_table::StatefulTable;
use crate::models::{BlockSelectionState, Scrollable};
use crate::network::radarr_network::RadarrEvent;
use crate::{
@@ -35,7 +34,7 @@ impl AddMovieHandler<'_, '_> {
.radarr_data
.add_searched_movies
.as_mut()
.unwrap_or(&mut StatefulTable::default()),
.expect("add_searched_movies should be initialized"),
AddMovieSearchResult
);
@@ -44,7 +44,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveRadarrBlock> for LibraryHandler<'a, '
fn handle(&mut self) {
let movie_table_handling_config = TableHandlingConfig::new(ActiveRadarrBlock::Movies.into())
.sorting_block(ActiveRadarrBlock::MoviesSortPrompt.into())
.sort_by_fn(|a: &Movie, b: &Movie| a.id.cmp(&b.id))
.sort_options(movies_sorting_options())
.searching_block(ActiveRadarrBlock::SearchMovie.into())
.search_error_block(ActiveRadarrBlock::SearchMovieError.into())
@@ -38,7 +38,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for BlocklistHandler<'a,
let blocklist_table_handling_config =
TableHandlingConfig::new(ActiveSonarrBlock::Blocklist.into())
.sorting_block(ActiveSonarrBlock::BlocklistSortPrompt.into())
.sort_by_fn(|a: &BlocklistItem, b: &BlocklistItem| a.id.cmp(&b.id))
.sort_options(blocklist_sorting_options());
if !self.handle_blocklist_table_events(blocklist_table_handling_config) {
@@ -33,7 +33,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for HistoryHandler<'a, '
fn handle(&mut self) {
let history_table_handling_config = TableHandlingConfig::new(ActiveSonarrBlock::History.into())
.sorting_block(ActiveSonarrBlock::HistorySortPrompt.into())
.sort_by_fn(|a: &SonarrHistoryItem, b: &SonarrHistoryItem| a.id.cmp(&b.id))
.sort_options(history_sorting_options())
.searching_block(ActiveSonarrBlock::SearchHistory.into())
.search_error_block(ActiveSonarrBlock::SearchHistoryError.into())
@@ -5,7 +5,6 @@ use crate::models::servarr_data::sonarr::sonarr_data::{
ADD_SERIES_BLOCKS, ADD_SERIES_SELECTION_BLOCKS, ActiveSonarrBlock,
};
use crate::models::sonarr_models::{AddSeriesBody, AddSeriesOptions, AddSeriesSearchResult};
use crate::models::stateful_table::StatefulTable;
use crate::models::{BlockSelectionState, Scrollable};
use crate::network::sonarr_network::SonarrEvent;
use crate::{
@@ -33,7 +32,7 @@ impl AddSeriesHandler<'_, '_> {
.sonarr_data
.add_searched_series
.as_mut()
.unwrap_or(&mut StatefulTable::default()),
.expect("add_searched_series should be initialized"),
AddSeriesSearchResult
);
@@ -55,7 +55,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for LibraryHandler<'a, '
fn handle(&mut self) {
let series_table_handling_config = TableHandlingConfig::new(ActiveSonarrBlock::Series.into())
.sorting_block(ActiveSonarrBlock::SeriesSortPrompt.into())
.sort_by_fn(|a: &Series, b: &Series| a.id.cmp(&b.id))
.sort_options(series_sorting_options())
.searching_block(ActiveSonarrBlock::SearchSeries.into())
.search_error_block(ActiveSonarrBlock::SearchSeriesError.into())
@@ -115,7 +115,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for SeasonDetailsHandler
TableHandlingConfig::new(ActiveSonarrBlock::SeasonHistory.into())
.sorting_block(ActiveSonarrBlock::SeasonHistorySortPrompt.into())
.sort_options(history_sorting_options())
.sort_by_fn(|a: &SonarrHistoryItem, b: &SonarrHistoryItem| a.id.cmp(&b.id))
.searching_block(ActiveSonarrBlock::SearchSeasonHistory.into())
.search_error_block(ActiveSonarrBlock::SearchSeasonHistoryError.into())
.search_field_fn(|history_item: &SonarrHistoryItem| &history_item.source_title.text)
@@ -71,7 +71,6 @@ impl<'a, 'b> KeyEventHandler<'a, 'b, ActiveSonarrBlock> for SeriesDetailsHandler
TableHandlingConfig::new(ActiveSonarrBlock::SeriesHistory.into())
.sorting_block(ActiveSonarrBlock::SeriesHistorySortPrompt.into())
.sort_options(history_sorting_options())
.sort_by_fn(|a: &SonarrHistoryItem, b: &SonarrHistoryItem| a.id.cmp(&b.id))
.searching_block(ActiveSonarrBlock::SearchSeriesHistory.into())
.search_error_block(ActiveSonarrBlock::SearchSeriesHistoryError.into())
.search_field_fn(|history_item: &SonarrHistoryItem| &history_item.source_title.text)
+126 -193
View File
@@ -1,7 +1,6 @@
use crate::models::Route;
use crate::models::stateful_table::SortOption;
use derive_setters::Setters;
use std::cmp::Ordering;
use std::fmt::Debug;
#[cfg(test)]
@@ -18,8 +17,6 @@ where
#[setters(strip_option)]
pub sort_options: Option<Vec<SortOption<T>>>,
#[setters(strip_option)]
pub sort_by_fn: Option<fn(&T, &T) -> Ordering>,
#[setters(strip_option)]
pub searching_block: Option<Route>,
#[setters(strip_option)]
pub search_error_block: Option<Route>,
@@ -56,16 +53,12 @@ macro_rules! handle_table_events {
_ if $crate::matches_key!(submit, $self.key) => $self.[<handle_ $name _table_submit>](config),
_ if $crate::matches_key!(esc, $self.key) => $self.[<handle_ $name _table_esc>](config),
_ if config.searching_block.is_some()
&& $self.app.get_current_route() == *config.searching_block
.as_ref()
.expect("searching_block must be configured for this table") =>
&& $self.app.get_current_route() == *config.searching_block.as_ref().unwrap() =>
{
$self.[<handle_ $name _table_search_box_input>]()
}
_ if config.filtering_block.is_some()
&& $self.app.get_current_route() == *config.filtering_block
.as_ref()
.expect("filtering_block must be configured for this table") =>
&& $self.app.get_current_route() == *config.filtering_block.as_ref().unwrap() =>
{
$self.[<handle_ $name _table_filter_box_input>]()
}
@@ -91,11 +84,11 @@ macro_rules! handle_table_events {
true
}
_ if config.sorting_block.is_some()
&& $self.app.get_current_route() == *config.sorting_block
.as_ref()
.expect("sorting_block must be configured for this table") =>
&& $self.app.get_current_route() == *config.sorting_block.as_ref().unwrap() =>
{
$table.sort.as_mut().unwrap().scroll_up();
if let Some(ref mut sort) = $table.sort {
sort.scroll_up();
}
true
}
_ => false,
@@ -111,27 +104,12 @@ macro_rules! handle_table_events {
true
}
_ if config.sorting_block.is_some()
&& $self.app.get_current_route() == *config.sorting_block
.as_ref()
.expect("sorting_block must be configured for this table") =>
&& $self.app.get_current_route() == *config.sorting_block.as_ref().unwrap() =>
{
$table
.sort
.as_mut()
.unwrap()
.scroll_down();
true
}
_ => false,
}
}
fn [<handle_ $name _table_page_down>](&mut $self, config: $crate::handlers::table_handler::TableHandlingConfig<$row>) -> bool {
use $crate::models::Paginated;
match $self.app.get_current_route() {
_ if config.table_block == $self.app.get_current_route() => {
$table.page_down();
if let Some(ref mut sort) = $table.sort {
sort
.scroll_down();
}
true
}
_ => false,
@@ -141,12 +119,22 @@ macro_rules! handle_table_events {
fn [<handle_ $name _table_page_up>](&mut $self, config: $crate::handlers::table_handler::TableHandlingConfig<$row>) -> bool {
use $crate::models::Paginated;
match $self.app.get_current_route() {
_ if config.table_block == $self.app.get_current_route() => {
$table.page_up();
true
}
_ => false,
if config.table_block == $self.app.get_current_route() {
$table.page_up();
true
} else {
false
}
}
fn [<handle_ $name _table_page_down>](&mut $self, config: $crate::handlers::table_handler::TableHandlingConfig<$row>) -> bool {
use $crate::models::Paginated;
if config.table_block == $self.app.get_current_route() {
$table.page_down();
true
} else {
false
}
}
@@ -159,39 +147,27 @@ macro_rules! handle_table_events {
true
}
_ if config.sorting_block.is_some()
&& $self.app.get_current_route() == *config.sorting_block
.as_ref()
.expect("sorting_block must be configured for this table") =>
&& $self.app.get_current_route() == *config.sorting_block.as_ref().unwrap() =>
{
$table
.sort
.as_mut()
.unwrap()
.scroll_to_top();
if let Some(ref mut sort) = $table.sort {
sort.scroll_to_top();
}
true
}
_ if config.searching_block.is_some()
&& $self.app.get_current_route() == *config.searching_block
.as_ref()
.expect("searching_block must be configured for this table") =>
&& $self.app.get_current_route() == *config.searching_block.as_ref().unwrap() =>
{
$table
.search
.as_mut()
.unwrap()
.scroll_home();
if let Some(ref mut search) = $table.search {
search.scroll_home();
}
true
}
_ if config.filtering_block.is_some()
&& $self.app.get_current_route() == *config.filtering_block
.as_ref()
.expect("filtering_block must be configured for this table") =>
&& $self.app.get_current_route() == *config.filtering_block.as_ref().unwrap() =>
{
$table
.filter
.as_mut()
.unwrap()
.scroll_home();
if let Some(ref mut filter) = $table.filter {
filter.scroll_home();
}
true
}
_ => false,
@@ -207,39 +183,27 @@ macro_rules! handle_table_events {
true
}
_ if config.sorting_block.is_some()
&& $self.app.get_current_route() == *config.sorting_block
.as_ref()
.expect("sorting_block must be configured for this table") =>
&& $self.app.get_current_route() == *config.sorting_block.as_ref().unwrap() =>
{
$table
.sort
.as_mut()
.unwrap()
.scroll_to_bottom();
if let Some(ref mut sort) = $table.sort {
sort.scroll_to_bottom();
}
true
}
_ if config.searching_block.is_some()
&& $self.app.get_current_route() == *config.searching_block
.as_ref()
.expect("searching_block must be configured for this table") =>
&& $self.app.get_current_route() == *config.searching_block.as_ref().unwrap() =>
{
$table
.search
.as_mut()
.unwrap()
.reset_offset();
if let Some(ref mut search) = $table.search {
search.reset_offset();
}
true
}
_ if config.filtering_block.is_some()
&& $self.app.get_current_route() == *config.filtering_block
.as_ref()
.expect("filtering_block must be configured for this table") =>
&& $self.app.get_current_route() == *config.filtering_block.as_ref().unwrap() =>
{
$table
.filter
.as_mut()
.unwrap()
.reset_offset();
if let Some(ref mut filter) = $table.filter {
filter.reset_offset();
}
true
}
_ => false,
@@ -249,53 +213,36 @@ macro_rules! handle_table_events {
fn [<handle_ $name _table_left_right>](&mut $self, config: $crate::handlers::table_handler::TableHandlingConfig<$row>) -> bool {
match $self.app.get_current_route() {
_ if config.searching_block.is_some()
&& $self.app.get_current_route() == *config.searching_block
.as_ref()
.expect("searching_block must be configured for this table") =>
&& $self.app.get_current_route() == *config.searching_block.as_ref().unwrap() =>
{
$crate::handle_text_box_left_right_keys!(
$self,
$self.key,
$table.search.as_mut().unwrap()
);
if let Some(ref mut search) = $table.search {
$crate::handle_text_box_left_right_keys!($self, $self.key, search);
}
true
}
_ if config.filtering_block.is_some()
&& $self.app.get_current_route() == *config.filtering_block
.as_ref()
.expect("filtering_block must be configured for this table") =>
&& $self.app.get_current_route() == *config.filtering_block.as_ref().unwrap() =>
{
$crate::handle_text_box_left_right_keys!(
$self,
$self.key,
$table.filter.as_mut().unwrap()
);
if let Some(ref mut filter) = $table.filter {
$crate::handle_text_box_left_right_keys!($self, $self.key, filter);
}
true
}
_ => false,
}
}
fn [<handle _$name _table_submit>](&mut $self, config: $crate::handlers::table_handler::TableHandlingConfig<$row>) -> bool {
fn [<handle_ $name _table_submit>](&mut $self, config: $crate::handlers::table_handler::TableHandlingConfig<$row>) -> bool {
match $self.app.get_current_route() {
_ if config.sorting_block.is_some()
&& $self.app.get_current_route() == *config.sorting_block
.as_ref()
.expect("sorting_block must be configured for this table") =>
&& $self.app.get_current_route() == *config.sorting_block.as_ref().unwrap() =>
{
if let Some(sort_by_fn) = config.sort_by_fn {
$table.items.sort_by(sort_by_fn);
}
$table.apply_sorting();
$self.app.pop_navigation_stack();
true
}
_ if config.searching_block.is_some()
&& $self.app.get_current_route() == *config.searching_block
.as_ref()
.expect("searching_block must be configured for this table") =>
&& $self.app.get_current_route() == *config.searching_block.as_ref().unwrap() =>
{
$self.app.pop_navigation_stack();
$self.app.ignore_special_keys_for_textbox_input = false;
@@ -318,9 +265,7 @@ macro_rules! handle_table_events {
true
}
_ if config.filtering_block.is_some()
&& $self.app.get_current_route() == *config.filtering_block
.as_ref()
.expect("filtering_block must be configured for this table") =>
&& $self.app.get_current_route() == *config.filtering_block.as_ref().unwrap() =>
{
$self.app.pop_navigation_stack();
$self.app.ignore_special_keys_for_textbox_input = false;
@@ -349,21 +294,15 @@ macro_rules! handle_table_events {
fn [<handle_ $name _table_esc>](&mut $self, config: $crate::handlers::table_handler::TableHandlingConfig<$row>) -> bool {
match $self.app.get_current_route() {
_ if config.sorting_block.is_some()
&& $self.app.get_current_route() == *config.sorting_block
.as_ref()
.expect("sorting_block must be configured for this table") =>
&& $self.app.get_current_route() == *config.sorting_block.as_ref().unwrap() =>
{
$self.app.pop_navigation_stack();
true
}
_ if (config.searching_block.is_some()
&& $self.app.get_current_route() == *config.searching_block
.as_ref()
.expect("searching_block must be configured for this table"))
&& $self.app.get_current_route() == *config.searching_block.as_ref().unwrap())
|| (config.search_error_block.is_some()
&& $self.app.get_current_route() == *config.search_error_block
.as_ref()
.expect("search_error_block must be configured for this table")) =>
&& $self.app.get_current_route() == *config.search_error_block.as_ref().unwrap()) =>
{
$self.app.pop_navigation_stack();
$table.reset_search();
@@ -371,13 +310,9 @@ macro_rules! handle_table_events {
true
}
_ if (config.filtering_block.is_some()
&& $self.app.get_current_route() == *config.filtering_block
.as_ref()
.expect("filtering_block must be configured for this table"))
&& $self.app.get_current_route() == *config.filtering_block.as_ref().unwrap())
|| (config.filter_error_block.is_some()
&& $self.app.get_current_route() == *config.filter_error_block
.as_ref()
.expect("filter_error_block must be configured for this table")) =>
&& $self.app.get_current_route() == *config.filter_error_block.as_ref().unwrap()) =>
{
$self.app.pop_navigation_stack();
$table.reset_filter();
@@ -394,68 +329,67 @@ macro_rules! handle_table_events {
}
}
fn [<handle_ $name _table_filter_key>](&mut $self, config: $crate::handlers::table_handler::TableHandlingConfig<$row>) -> bool {
if matches!($self.app.get_current_route(), _ if config.table_block == $self.app.get_current_route()) {
$self
.app
.push_navigation_stack(config.filtering_block.expect("Filtering block is undefined").into());
$table.reset_filter();
$table.filter = Some($crate::models::HorizontallyScrollableText::default());
$self.app.ignore_special_keys_for_textbox_input = true;
true
} else {
false
}
}
fn [<handle_ $name _table_search_key>](&mut $self, config: $crate::handlers::table_handler::TableHandlingConfig<$row>) -> bool {
if matches!($self.app.get_current_route(), _ if config.table_block == $self.app.get_current_route()) {
$self
.app
.push_navigation_stack(config.searching_block.expect("Searching block is undefined"));
$table.search = Some($crate::models::HorizontallyScrollableText::default());
$self.app.ignore_special_keys_for_textbox_input = true;
true
} else {
false
}
}
fn [<handle_ $name _table_sort_key>](&mut $self, config: $crate::handlers::table_handler::TableHandlingConfig<$row>) -> bool {
if matches!($self.app.get_current_route(), _ if config.table_block == $self.app.get_current_route()) {
$table.sorting(
config
.sort_options
.as_ref()
.expect("Sort options are undefined")
.clone(),
);
$self
.app
.push_navigation_stack(config.sorting_block.expect("Sorting block is undefined"));
true
} else {
false
}
}
fn [<handle_ $name _table_search_box_input>](&mut $self) -> bool {
$crate::handle_text_box_keys!(
$self,
$self.key,
$table.search.as_mut().unwrap()
);
let Some(ref mut search) = $table.search else {
return false;
};
$crate::handle_text_box_keys!($self, $self.key, search);
true
}
fn [<handle_ $name _table_filter_box_input>](&mut $self) -> bool {
$crate::handle_text_box_keys!(
$self,
$self.key,
$table.filter.as_mut().unwrap()
);
let Some(ref mut filter) = $table.filter else {
return false;
};
$crate::handle_text_box_keys!($self, $self.key, filter);
true
}
fn [<handle_ $name _table_filter_key>](&mut $self, config: $crate::handlers::table_handler::TableHandlingConfig<$row>) -> bool {
if $self.app.get_current_route() != config.table_block {
return false;
}
let Some(ref filtering_block) = config.filtering_block else {
return false;
};
let filter = $crate::models::HorizontallyScrollableText::default();
$table.filter = Some(filter);
$self.app.push_navigation_stack(*filtering_block);
$self.app.ignore_special_keys_for_textbox_input = true;
true
}
fn [<handle_ $name _table_search_key>](&mut $self, config: $crate::handlers::table_handler::TableHandlingConfig<$row>) -> bool {
if $self.app.get_current_route() != config.table_block {
return false;
}
let Some(ref searching_block) = config.searching_block else {
return false;
};
let search = $crate::models::HorizontallyScrollableText::default();
$table.search = Some(search);
$self.app.push_navigation_stack(*searching_block);
$self.app.ignore_special_keys_for_textbox_input = true;
true
}
fn [<handle_ $name _table_sort_key>](&mut $self, config: $crate::handlers::table_handler::TableHandlingConfig<$row>) -> bool {
if $self.app.get_current_route() != config.table_block {
return false;
}
let (Some(ref sorting_block), Some(sort_options)) = (config.sorting_block, config.sort_options.as_ref()) else {
return false;
};
$table.sorting(sort_options.clone());
$self.app.push_navigation_stack(*sorting_block);
true
}
}
@@ -467,17 +401,16 @@ where
T: Clone + PartialEq + Eq + Debug + Default,
{
pub fn new(table_block: Route) -> Self {
TableHandlingConfig {
Self {
table_block,
sorting_block: None,
sort_options: None,
sort_by_fn: None,
searching_block: None,
search_error_block: None,
search_field_fn: None,
filtering_block: None,
filter_error_block: None,
filter_field_fn: None,
table_block,
}
}
}
-1
View File
@@ -23,7 +23,6 @@ mod tests {
fn handle(&mut self) {
let movie_table_handling_config = TableHandlingConfig::new(ActiveRadarrBlock::Movies.into())
.sorting_block(ActiveRadarrBlock::MoviesSortPrompt.into())
.sort_by_fn(|a: &Movie, b: &Movie| a.id.cmp(&b.id))
.sort_options(sort_options())
.searching_block(ActiveRadarrBlock::SearchMovie.into())
.search_error_block(ActiveRadarrBlock::SearchMovieError.into())
+61 -210
View File
@@ -50,151 +50,46 @@ where
T: Clone + PartialEq + Eq + Debug,
{
fn scroll_down(&mut self) {
if let Some(filtered_items) = self.filtered_items.as_ref() {
if filtered_items.is_empty() {
return;
}
match self
.filtered_state
.as_ref()
.expect("filtered_state must exist when filtered_items exists")
.selected()
{
Some(i) => {
if i >= filtered_items.len() - 1 {
self
.filtered_state
.as_mut()
.expect("filtered_state must exist when filtered_items exists")
.select_first();
} else {
self
.filtered_state
.as_mut()
.expect("filtered_state must exist when filtered_items exists")
.select_next();
}
}
None => self
.filtered_state
.as_mut()
.expect("filtered_state must exist when filtered_items exists")
.select_first(),
};
let items_len = self.active_items().len();
if items_len == 0 {
return;
}
if self.items.is_empty() {
return;
let state = self.active_state_mut();
match state.selected() {
Some(i) if i >= items_len - 1 => state.select_first(),
Some(_) => state.select_next(),
None => state.select_first(),
}
match self.state.selected() {
Some(i) => {
if i >= self.items.len() - 1 {
self.state.select_first();
} else {
self.state.select_next();
}
}
None => self.state.select_first(),
};
}
fn scroll_up(&mut self) {
if let Some(filtered_items) = self.filtered_items.as_ref() {
if filtered_items.is_empty() {
return;
}
match self
.filtered_state
.as_ref()
.expect("filtered_state must exist when filtered_items exists")
.selected()
{
Some(i) => {
if i == 0 {
self
.filtered_state
.as_mut()
.expect("filtered_state must exist when filtered_items exists")
.select(Some(filtered_items.len() - 1));
} else {
self
.filtered_state
.as_mut()
.expect("filtered_state must exist when filtered_items exists")
.select_previous();
}
}
None => self
.filtered_state
.as_mut()
.expect("filtered_state must exist when filtered_items exists")
.select_first(),
};
let items_len = self.active_items().len();
if items_len == 0 {
return;
}
if self.items.is_empty() {
return;
let state = self.active_state_mut();
match state.selected() {
Some(0) => state.select(Some(items_len - 1)),
Some(_) => state.select_previous(),
None => state.select_first(),
}
match self.state.selected() {
Some(i) => {
if i == 0 {
self.state.select(Some(self.items.len() - 1));
} else {
self.state.select_previous();
}
}
None => self.state.select_first(),
};
}
fn scroll_to_top(&mut self) {
if let Some(filtered_items) = self.filtered_items.as_ref() {
if filtered_items.is_empty() {
return;
}
self
.filtered_state
.as_mut()
.expect("filtered_state must exist when filtered_items exists")
.select_first();
if self.active_items().is_empty() {
return;
}
if self.items.is_empty() {
return;
}
self.state.select_first();
self.active_state_mut().select_first();
}
fn scroll_to_bottom(&mut self) {
if let Some(filtered_items) = self.filtered_items.as_ref() {
if filtered_items.is_empty() {
return;
}
self
.filtered_state
.as_mut()
.expect("filtered_state must exist when filtered_items exists")
.select(Some(filtered_items.len() - 1));
let items_len = self.active_items().len();
if items_len == 0 {
return;
}
if self.items.is_empty() {
return;
}
self.state.select(Some(self.items.len() - 1));
self.active_state_mut().select(Some(items_len - 1));
}
}
@@ -203,89 +98,56 @@ where
T: Clone + PartialEq + Eq + Debug,
{
fn page_down(&mut self) {
if let Some(filtered_items) = self.filtered_items.as_ref() {
if filtered_items.is_empty() {
return;
}
match self
.filtered_state
.as_ref()
.expect("filtered_state must exist when filtered_items exists")
.selected()
{
Some(i) => {
self
.filtered_state
.as_mut()
.expect("filtered_state must exist when filtered_items exists")
.select(Some(i.saturating_add(20) % (filtered_items.len() - 1)));
}
None => self
.filtered_state
.as_mut()
.expect("filtered_state must exist when filtered_items exists")
.select_first(),
};
let items_len = self.active_items().len();
if items_len == 0 {
return;
}
if self.items.is_empty() {
return;
let state = self.active_state_mut();
match state.selected() {
Some(i) => state.select(Some(i.saturating_add(20) % (items_len - 1))),
None => state.select_first(),
}
match self.state.selected() {
Some(i) => {
self
.state
.select(Some(i.saturating_add(20) % (self.items.len() - 1)));
}
None => self.state.select_first(),
};
}
fn page_up(&mut self) {
if let Some(filtered_items) = self.filtered_items.as_ref() {
if filtered_items.is_empty() {
return;
}
match self
.filtered_state
.as_ref()
.expect("filtered_state must exist when filtered_items exists")
.selected()
{
Some(i) => {
let len = filtered_items.len() - 1;
self
.filtered_state
.as_mut()
.expect("filtered_state must exist when filtered_items exists")
.select(Some((i + len - (20 % len)) % len));
}
None => self
.filtered_state
.as_mut()
.expect("filtered_state must exist when filtered_items exists")
.select_last(),
};
let items_len = self.active_items().len();
if items_len == 0 {
return;
}
if self.items.is_empty() {
return;
}
match self.state.selected() {
let state = self.active_state_mut();
match state.selected() {
Some(i) => {
let len = self.items.len() - 1;
self.state.select(Some((i + len - (20 % len)) % len));
let len = items_len - 1;
state.select(Some((i + len - (20 % len)) % len));
}
None => self.state.select_last(),
};
None => state.select_last(),
}
}
}
impl<T> StatefulTable<T>
where
T: Clone + PartialEq + Eq + Debug,
{
fn active_items(&self) -> &[T] {
self
.filtered_items
.as_ref()
.map_or(&self.items[..], |items| &items[..])
}
fn active_state_mut(&mut self) -> &mut TableState {
if let Some(ref mut filtered_state) = self.filtered_state {
filtered_state
} else {
&mut self.state
}
}
fn active_state(&self) -> &TableState {
self.filtered_state.as_ref().unwrap_or(&self.state)
}
}
@@ -318,24 +180,13 @@ where
}
pub fn select_index(&mut self, index: Option<usize>) {
if let Some(filtered_state) = &mut self.filtered_state {
filtered_state.select(index);
} else {
self.state.select(index);
}
self.active_state_mut().select(index);
}
pub fn current_selection(&self) -> &T {
if let Some(filtered_items) = &self.filtered_items {
&filtered_items[self
.filtered_state
.as_ref()
.expect("filtered_state must exist when filtered_items exists")
.selected()
.unwrap_or(0)]
} else {
&self.items[self.state.selected().unwrap_or(0)]
}
let items = self.active_items();
let index = self.active_state().selected().unwrap_or(0);
&items[index]
}
pub fn sorting(&mut self, sort_options: Vec<SortOption<T>>) {
+3 -3
View File
@@ -6,10 +6,10 @@ use std::sync::Arc;
use std::time::Duration;
use anyhow::Result;
use anyhow::{anyhow, Context};
use anyhow::{Context, anyhow};
use colored::Colorize;
use indicatif::{ProgressBar, ProgressStyle};
use log::{error, LevelFilter};
use log::{LevelFilter, error};
use log4rs::append::file::FileAppender;
use log4rs::config::{Appender, Root};
use log4rs::encode::pattern::PatternEncoder;
@@ -18,7 +18,7 @@ use reqwest::{Certificate, Client};
use tokio::sync::Mutex;
use tokio_util::sync::CancellationToken;
use crate::app::{log_and_print_error, App, AppConfig};
use crate::app::{App, AppConfig, log_and_print_error};
use crate::cli::{self, Command};
use crate::network::Network;
use crate::ui::theme::ThemeDefinitionsWrapper;