fix: Fixed UI bugs introduced as part of the hotkey refactor

This commit is contained in:
2025-09-02 15:49:36 -06:00
parent fedec79a95
commit 97ce258a8d
9 changed files with 63 additions and 62 deletions
@@ -1,4 +1,4 @@
use ratatui::layout::{Alignment, Constraint, Flex, Layout, Rect}; use ratatui::layout::{Constraint, Flex, Layout, Rect};
use ratatui::style::Stylize; use ratatui::style::Stylize;
use ratatui::text::{Line, Text}; use ratatui::text::{Line, Text};
use ratatui::widgets::{Cell, Paragraph, Row, Wrap}; use ratatui::widgets::{Cell, Paragraph, Row, Wrap};
@@ -184,7 +184,6 @@ pub fn draw_collection_details(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect)
) )
.block(layout_block_top_border_with_title(title_style("Movies"))) .block(layout_block_top_border_with_title(title_style("Movies")))
.loading(app.is_loading) .loading(app.is_loading)
.footer_alignment(Alignment::Center)
.headers([ .headers([
"", "",
"Title", "Title",
@@ -7,7 +7,7 @@ use crate::ui::utils::{get_width_from_percentage, title_block};
use crate::ui::widgets::managarr_table::ManagarrTable; use crate::ui::widgets::managarr_table::ManagarrTable;
use crate::ui::widgets::popup::Size; use crate::ui::widgets::popup::Size;
use crate::ui::{draw_popup, DrawUi}; use crate::ui::{draw_popup, DrawUi};
use ratatui::layout::{Alignment, Constraint, Rect}; use ratatui::layout::{Constraint, Rect};
use ratatui::widgets::{Cell, Row}; use ratatui::widgets::{Cell, Row};
use ratatui::Frame; use ratatui::Frame;
@@ -67,7 +67,6 @@ fn draw_test_all_indexers_test_results(f: &mut Frame<'_>, app: &mut App<'_>, are
test_results_row_mapping, test_results_row_mapping,
) )
.loading(is_loading) .loading(is_loading)
.footer_alignment(Alignment::Center)
.margin(1) .margin(1)
.headers(["Indexer", "Pass/Fail", "Failure Messages"]) .headers(["Indexer", "Pass/Fail", "Failure Messages"])
.constraints([ .constraints([
+11 -6
View File
@@ -1,4 +1,4 @@
use ratatui::layout::{Alignment, Rect}; use ratatui::layout::Rect;
use ratatui::text::{Span, Text}; use ratatui::text::{Span, Text};
use ratatui::widgets::{Cell, ListItem, Paragraph, Row}; use ratatui::widgets::{Cell, ListItem, Paragraph, Row};
use ratatui::Frame; use ratatui::Frame;
@@ -60,7 +60,7 @@ fn draw_logs_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
if app.data.radarr_data.log_details.items.is_empty() { if app.data.radarr_data.log_details.items.is_empty() {
let loading = LoadingBlock::new(app.is_loading, borderless_block()); let loading = LoadingBlock::new(app.is_loading, borderless_block());
let popup = Popup::new(loading).size(Size::Large).block(block); let popup = Popup::new(loading).size(Size::Large).block(block).margin(1);
f.render_widget(popup, f.area()); f.render_widget(popup, f.area());
return; return;
@@ -73,7 +73,10 @@ fn draw_logs_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
style_log_list_item(ListItem::new(Text::from(Span::raw(log_line))), level) style_log_list_item(ListItem::new(Text::from(Span::raw(log_line))), level)
}) })
.block(borderless_block()); .block(borderless_block());
let popup = Popup::new(logs_list).size(Size::Large).block(block); let popup = Popup::new(logs_list)
.size(Size::Large)
.block(block)
.margin(1);
f.render_widget(popup, f.area()); f.render_widget(popup, f.area());
} }
@@ -94,7 +97,6 @@ fn draw_tasks_popup(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
let tasks_table = ManagarrTable::new(Some(&mut app.data.radarr_data.tasks), tasks_row_mapping) let tasks_table = ManagarrTable::new(Some(&mut app.data.radarr_data.tasks), tasks_row_mapping)
.loading(app.is_loading) .loading(app.is_loading)
.margin(1) .margin(1)
.footer_alignment(Alignment::Center)
.headers(TASK_TABLE_HEADERS) .headers(TASK_TABLE_HEADERS)
.constraints(TASK_TABLE_CONSTRAINTS); .constraints(TASK_TABLE_CONSTRAINTS);
@@ -129,12 +131,15 @@ fn draw_updates_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
let updates_paragraph = Paragraph::new(Text::from(updates)) let updates_paragraph = Paragraph::new(Text::from(updates))
.block(borderless_block()) .block(borderless_block())
.scroll((app.data.radarr_data.updates.offset, 0)); .scroll((app.data.radarr_data.updates.offset, 0));
let popup = Popup::new(updates_paragraph).size(Size::Large).block(block); let popup = Popup::new(updates_paragraph)
.size(Size::Large)
.block(block)
.margin(1);
f.render_widget(popup, f.area()); f.render_widget(popup, f.area());
} else { } else {
let loading = LoadingBlock::new(app.is_loading, borderless_block()); let loading = LoadingBlock::new(app.is_loading, borderless_block());
let popup = Popup::new(loading).size(Size::Large).block(block); let popup = Popup::new(loading).size(Size::Large).block(block).margin(1);
f.render_widget(popup, f.area()); f.render_widget(popup, f.area());
} }
@@ -7,7 +7,7 @@ use crate::ui::utils::{get_width_from_percentage, title_block};
use crate::ui::widgets::managarr_table::ManagarrTable; use crate::ui::widgets::managarr_table::ManagarrTable;
use crate::ui::widgets::popup::Size; use crate::ui::widgets::popup::Size;
use crate::ui::{draw_popup, DrawUi}; use crate::ui::{draw_popup, DrawUi};
use ratatui::layout::{Alignment, Constraint, Rect}; use ratatui::layout::{Constraint, Rect};
use ratatui::widgets::{Cell, Row}; use ratatui::widgets::{Cell, Row};
use ratatui::Frame; use ratatui::Frame;
@@ -65,7 +65,6 @@ fn draw_test_all_indexers_test_results(f: &mut Frame<'_>, app: &mut App<'_>, are
test_results_row_mapping, test_results_row_mapping,
) )
.loading(is_loading) .loading(is_loading)
.footer_alignment(Alignment::Center)
.margin(1) .margin(1)
.headers(["Indexer", "Pass/Fail", "Failure Messages"]) .headers(["Indexer", "Pass/Fail", "Failure Messages"])
.constraints([ .constraints([
+11 -6
View File
@@ -1,4 +1,4 @@
use ratatui::layout::{Alignment, Rect}; use ratatui::layout::Rect;
use ratatui::text::{Span, Text}; use ratatui::text::{Span, Text};
use ratatui::widgets::{Cell, ListItem, Paragraph, Row}; use ratatui::widgets::{Cell, ListItem, Paragraph, Row};
use ratatui::Frame; use ratatui::Frame;
@@ -60,7 +60,7 @@ fn draw_logs_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
if app.data.sonarr_data.log_details.items.is_empty() { if app.data.sonarr_data.log_details.items.is_empty() {
let loading = LoadingBlock::new(app.is_loading, borderless_block()); let loading = LoadingBlock::new(app.is_loading, borderless_block());
let popup = Popup::new(loading).size(Size::Large).block(block); let popup = Popup::new(loading).size(Size::Large).block(block).margin(1);
f.render_widget(popup, f.area()); f.render_widget(popup, f.area());
return; return;
@@ -73,7 +73,10 @@ fn draw_logs_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
style_log_list_item(ListItem::new(Text::from(Span::raw(log_line))), level) style_log_list_item(ListItem::new(Text::from(Span::raw(log_line))), level)
}) })
.block(borderless_block()); .block(borderless_block());
let popup = Popup::new(logs_list).size(Size::Large).block(block); let popup = Popup::new(logs_list)
.size(Size::Large)
.block(block)
.margin(1);
f.render_widget(popup, f.area()); f.render_widget(popup, f.area());
} }
@@ -93,7 +96,6 @@ fn draw_tasks_popup(f: &mut Frame<'_>, app: &mut App<'_>, area: Rect) {
let tasks_table = ManagarrTable::new(Some(&mut app.data.sonarr_data.tasks), tasks_row_mapping) let tasks_table = ManagarrTable::new(Some(&mut app.data.sonarr_data.tasks), tasks_row_mapping)
.loading(app.is_loading) .loading(app.is_loading)
.margin(1) .margin(1)
.footer_alignment(Alignment::Center)
.headers(TASK_TABLE_HEADERS) .headers(TASK_TABLE_HEADERS)
.constraints(TASK_TABLE_CONSTRAINTS); .constraints(TASK_TABLE_CONSTRAINTS);
@@ -128,12 +130,15 @@ fn draw_updates_popup(f: &mut Frame<'_>, app: &mut App<'_>) {
let updates_paragraph = Paragraph::new(Text::from(updates)) let updates_paragraph = Paragraph::new(Text::from(updates))
.block(borderless_block()) .block(borderless_block())
.scroll((app.data.sonarr_data.updates.offset, 0)); .scroll((app.data.sonarr_data.updates.offset, 0));
let popup = Popup::new(updates_paragraph).size(Size::Large).block(block); let popup = Popup::new(updates_paragraph)
.size(Size::Large)
.block(block)
.margin(1);
f.render_widget(popup, f.area()); f.render_widget(popup, f.area());
} else { } else {
let loading = LoadingBlock::new(app.is_loading, borderless_block()); let loading = LoadingBlock::new(app.is_loading, borderless_block());
let popup = Popup::new(loading).size(Size::Large).block(block); let popup = Popup::new(loading).size(Size::Large).block(block).margin(1);
f.render_widget(popup, f.area()); f.render_widget(popup, f.area());
} }
+9 -31
View File
@@ -3,18 +3,16 @@ use super::message::Message;
use super::popup::Size; use super::popup::Size;
use crate::models::stateful_table::StatefulTable; use crate::models::stateful_table::StatefulTable;
use crate::ui::styles::ManagarrStyle; use crate::ui::styles::ManagarrStyle;
use crate::ui::utils::{ use crate::ui::utils::{borderless_block, centered_rect, title_block_centered};
borderless_block, centered_rect, layout_block_top_border, title_block_centered,
};
use crate::ui::widgets::loading_block::LoadingBlock; use crate::ui::widgets::loading_block::LoadingBlock;
use crate::ui::widgets::popup::Popup; use crate::ui::widgets::popup::Popup;
use crate::ui::widgets::selectable_list::SelectableList; use crate::ui::widgets::selectable_list::SelectableList;
use crate::ui::HIGHLIGHT_SYMBOL; use crate::ui::HIGHLIGHT_SYMBOL;
use derive_setters::Setters; use derive_setters::Setters;
use ratatui::buffer::Buffer; use ratatui::buffer::Buffer;
use ratatui::layout::{Alignment, Constraint, Layout, Position, Rect}; use ratatui::layout::{Constraint, Layout, Position, Rect};
use ratatui::prelude::{Style, Stylize, Text}; use ratatui::prelude::{Style, Stylize, Text};
use ratatui::widgets::{Block, ListItem, Paragraph, Row, StatefulWidget, Table, Widget, WidgetRef}; use ratatui::widgets::{Block, ListItem, Row, StatefulWidget, Table, Widget, WidgetRef};
use ratatui::Frame; use ratatui::Frame;
use std::fmt::Debug; use std::fmt::Debug;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
@@ -36,8 +34,6 @@ where
#[setters(skip)] #[setters(skip)]
constraints: Vec<Constraint>, constraints: Vec<Constraint>,
row_mapper: F, row_mapper: F,
footer: Option<String>,
footer_alignment: Alignment,
block: Block<'a>, block: Block<'a>,
margin: u16, margin: u16,
#[setters(rename = "loading")] #[setters(rename = "loading")]
@@ -68,8 +64,6 @@ where
table_headers: Vec::new(), table_headers: Vec::new(),
constraints: Vec::new(), constraints: Vec::new(),
row_mapper, row_mapper,
footer: None,
footer_alignment: Alignment::Left,
block: borderless_block(), block: borderless_block(),
margin: 0, margin: 0,
is_loading: false, is_loading: false,
@@ -119,20 +113,12 @@ where
fn render_table(self, area: Rect, buf: &mut Buffer) { fn render_table(self, area: Rect, buf: &mut Buffer) {
let table_headers = self.parse_headers(); let table_headers = self.parse_headers();
let table_area = if let Some(ref footer) = self.footer { let table_area = {
let [content_area, footer_area] = let [content_area, _] = Layout::vertical([Constraint::Fill(1), Constraint::Fill(0)])
Layout::vertical([Constraint::Fill(0), Constraint::Length(2)]) .margin(self.margin)
.margin(self.margin) .areas(area);
.areas(area);
Paragraph::new(Text::from(format!(" {footer}").help()))
.block(layout_block_top_border())
.alignment(self.footer_alignment)
.render(footer_area, buf);
content_area content_area
} else {
area
}; };
let loading_block = LoadingBlock::new(self.is_loading, self.block.clone()); let loading_block = LoadingBlock::new(self.is_loading, self.block.clone());
@@ -230,19 +216,11 @@ where
pub fn show_cursor(&self, f: &mut Frame<'_>, area: Rect) { pub fn show_cursor(&self, f: &mut Frame<'_>, area: Rect) {
let mut draw_cursor = |length: usize, offset: usize| { let mut draw_cursor = |length: usize, offset: usize| {
let table_area = if self.footer.is_some() {
let [content_area, _] = Layout::vertical([Constraint::Fill(0), Constraint::Length(2)])
.margin(self.margin)
.areas(area);
content_area
} else {
area
};
let popup_area = Rect { let popup_area = Rect {
height: 7, height: 7,
..centered_rect(30, 20, table_area) ..centered_rect(30, 20, area)
}; };
let [text_box_area, _] = Layout::vertical([Constraint::Length(3), Constraint::Length(1)]) let [text_box_area, _] = Layout::vertical([Constraint::Fill(1), Constraint::Fill(0)])
.margin(1) .margin(1)
.areas(popup_area); .areas(popup_area);
f.set_cursor_position(Position { f.set_cursor_position(Position {
+1 -11
View File
@@ -6,7 +6,7 @@ mod tests {
use crate::ui::utils::borderless_block; use crate::ui::utils::borderless_block;
use crate::ui::widgets::managarr_table::ManagarrTable; use crate::ui::widgets::managarr_table::ManagarrTable;
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use ratatui::layout::{Alignment, Constraint}; use ratatui::layout::Constraint;
use ratatui::text::Text; use ratatui::text::Text;
use ratatui::widgets::{Cell, Row}; use ratatui::widgets::{Cell, Row};
use std::sync::atomic::AtomicUsize; use std::sync::atomic::AtomicUsize;
@@ -25,8 +25,6 @@ mod tests {
assert_eq!(row_mapper(&"item1"), Row::new(vec![Cell::new("item1")])); assert_eq!(row_mapper(&"item1"), Row::new(vec![Cell::new("item1")]));
assert_eq!(managarr_table.table_headers, Vec::<String>::new()); assert_eq!(managarr_table.table_headers, Vec::<String>::new());
assert_eq!(managarr_table.constraints, Vec::new()); assert_eq!(managarr_table.constraints, Vec::new());
assert_eq!(managarr_table.footer, None);
assert_eq!(managarr_table.footer_alignment, Alignment::Left);
assert_eq!(managarr_table.block, borderless_block()); assert_eq!(managarr_table.block, borderless_block());
assert_eq!(managarr_table.margin, 0); assert_eq!(managarr_table.margin, 0);
assert!(!managarr_table.is_loading); assert!(!managarr_table.is_loading);
@@ -61,8 +59,6 @@ mod tests {
assert_eq!(row_mapper(&"item1"), Row::new(vec![Cell::new("item1")])); assert_eq!(row_mapper(&"item1"), Row::new(vec![Cell::new("item1")]));
assert_eq!(managarr_table.table_headers, Vec::<String>::new()); assert_eq!(managarr_table.table_headers, Vec::<String>::new());
assert_eq!(managarr_table.constraints, Vec::new()); assert_eq!(managarr_table.constraints, Vec::new());
assert_eq!(managarr_table.footer, None);
assert_eq!(managarr_table.footer_alignment, Alignment::Left);
assert_eq!(managarr_table.block, borderless_block()); assert_eq!(managarr_table.block, borderless_block());
assert_eq!(managarr_table.margin, 0); assert_eq!(managarr_table.margin, 0);
assert!(!managarr_table.is_loading); assert!(!managarr_table.is_loading);
@@ -97,8 +93,6 @@ mod tests {
assert_eq!(row_mapper(&"item1"), Row::new(vec![Cell::new("item1")])); assert_eq!(row_mapper(&"item1"), Row::new(vec![Cell::new("item1")]));
assert_eq!(managarr_table.table_headers, Vec::<String>::new()); assert_eq!(managarr_table.table_headers, Vec::<String>::new());
assert_eq!(managarr_table.constraints, Vec::new()); assert_eq!(managarr_table.constraints, Vec::new());
assert_eq!(managarr_table.footer, None);
assert_eq!(managarr_table.footer_alignment, Alignment::Left);
assert_eq!(managarr_table.block, borderless_block()); assert_eq!(managarr_table.block, borderless_block());
assert_eq!(managarr_table.margin, 0); assert_eq!(managarr_table.margin, 0);
assert!(!managarr_table.is_loading); assert!(!managarr_table.is_loading);
@@ -130,8 +124,6 @@ mod tests {
assert_eq!(managarr_table.content.unwrap().items, items); assert_eq!(managarr_table.content.unwrap().items, items);
assert_eq!(row_mapper(&"item1"), Row::new(vec![Cell::new("item1")])); assert_eq!(row_mapper(&"item1"), Row::new(vec![Cell::new("item1")]));
assert_eq!(managarr_table.constraints, Vec::new()); assert_eq!(managarr_table.constraints, Vec::new());
assert_eq!(managarr_table.footer, None);
assert_eq!(managarr_table.footer_alignment, Alignment::Left);
assert_eq!(managarr_table.block, borderless_block()); assert_eq!(managarr_table.block, borderless_block());
assert_eq!(managarr_table.margin, 0); assert_eq!(managarr_table.margin, 0);
assert!(!managarr_table.is_loading); assert!(!managarr_table.is_loading);
@@ -163,8 +155,6 @@ mod tests {
assert_eq!(managarr_table.content.unwrap().items, items); assert_eq!(managarr_table.content.unwrap().items, items);
assert_eq!(row_mapper(&"item1"), Row::new(vec![Cell::new("item1")])); assert_eq!(row_mapper(&"item1"), Row::new(vec![Cell::new("item1")]));
assert_eq!(managarr_table.table_headers, Vec::<String>::new()); assert_eq!(managarr_table.table_headers, Vec::<String>::new());
assert_eq!(managarr_table.footer, None);
assert_eq!(managarr_table.footer_alignment, Alignment::Left);
assert_eq!(managarr_table.block, borderless_block()); assert_eq!(managarr_table.block, borderless_block());
assert_eq!(managarr_table.margin, 0); assert_eq!(managarr_table.margin, 0);
assert!(!managarr_table.is_loading); assert!(!managarr_table.is_loading);
+13 -2
View File
@@ -1,6 +1,6 @@
use crate::ui::utils::{background_block, centered_rect}; use crate::ui::utils::{background_block, centered_rect};
use ratatui::buffer::Buffer; use ratatui::buffer::Buffer;
use ratatui::layout::Rect; use ratatui::layout::{Constraint, Layout, Rect};
use ratatui::widgets::{Block, Clear, Widget}; use ratatui::widgets::{Block, Clear, Widget};
#[cfg(test)] #[cfg(test)]
@@ -51,6 +51,7 @@ impl Size {
pub struct Popup<'a, T: Widget> { pub struct Popup<'a, T: Widget> {
widget: T, widget: T,
margin: u16,
percent_x: u16, percent_x: u16,
percent_y: u16, percent_y: u16,
block: Option<Block<'a>>, block: Option<Block<'a>>,
@@ -62,6 +63,7 @@ impl<'a, T: Widget> Popup<'a, T> {
widget, widget,
percent_x: 0, percent_x: 0,
percent_y: 0, percent_y: 0,
margin: 0,
block: None, block: None,
} }
} }
@@ -84,6 +86,11 @@ impl<'a, T: Widget> Popup<'a, T> {
self self
} }
pub fn margin(mut self, margin: u16) -> Self {
self.margin = margin;
self
}
fn render_popup(self, area: Rect, buf: &mut Buffer) { fn render_popup(self, area: Rect, buf: &mut Buffer) {
let mut popup_area = centered_rect(self.percent_x, self.percent_y, area); let mut popup_area = centered_rect(self.percent_x, self.percent_y, area);
let height = if popup_area.height < 3 { let height = if popup_area.height < 3 {
@@ -102,7 +109,11 @@ impl<'a, T: Widget> Popup<'a, T> {
block.render(popup_area, buf); block.render(popup_area, buf);
} }
self.widget.render(popup_area, buf); let [content_area, _] = Layout::vertical([Constraint::Fill(1), Constraint::Fill(0)])
.margin(self.margin)
.areas(popup_area);
self.widget.render(content_area, buf);
} }
} }
+15
View File
@@ -32,6 +32,7 @@ mod tests {
assert_eq!(popup.percent_x, 0); assert_eq!(popup.percent_x, 0);
assert_eq!(popup.percent_y, 0); assert_eq!(popup.percent_y, 0);
assert_eq!(popup.block, None); assert_eq!(popup.block, None);
assert_eq!(popup.margin, 0);
} }
#[test] #[test]
@@ -42,6 +43,7 @@ mod tests {
assert_eq!(popup.percent_y, 40); assert_eq!(popup.percent_y, 40);
assert_eq!(popup.widget, Block::new()); assert_eq!(popup.widget, Block::new());
assert_eq!(popup.block, None); assert_eq!(popup.block, None);
assert_eq!(popup.margin, 0);
} }
#[test] #[test]
@@ -52,6 +54,7 @@ mod tests {
assert_eq!(popup.percent_y, 50); assert_eq!(popup.percent_y, 50);
assert_eq!(popup.widget, Block::new()); assert_eq!(popup.widget, Block::new());
assert_eq!(popup.block, None); assert_eq!(popup.block, None);
assert_eq!(popup.margin, 0);
} }
#[test] #[test]
@@ -62,5 +65,17 @@ mod tests {
assert_eq!(popup.widget, Block::new()); assert_eq!(popup.widget, Block::new());
assert_eq!(popup.percent_x, 0); assert_eq!(popup.percent_x, 0);
assert_eq!(popup.percent_y, 0); assert_eq!(popup.percent_y, 0);
assert_eq!(popup.margin, 0);
}
#[test]
fn test_popup_margin() {
let popup = Popup::new(Block::new()).margin(5);
assert_eq!(popup.margin, 5);
assert_eq!(popup.widget, Block::new());
assert_eq!(popup.percent_x, 0);
assert_eq!(popup.percent_y, 0);
assert_eq!(popup.block, None);
} }
} }