Replaced all list uses with the SelectableList widget and popup widget. Simplified more popups to use the widgets

This commit is contained in:
2024-02-13 16:16:31 -07:00
parent 649f4b5e3b
commit 4b734811f4
7 changed files with 221 additions and 141 deletions
+51 -8
View File
@@ -1,37 +1,80 @@
use crate::ui::utils::{background_block, centered_rect};
use crate::ui::styles::ManagarrStyle;
use crate::ui::utils::{background_block, centered_rect, layout_block_top_border};
use ratatui::buffer::Buffer;
use ratatui::layout::Rect;
use ratatui::widgets::{Clear, Widget};
use ratatui::layout::{Alignment, Constraint, Layout, Rect};
use ratatui::prelude::Text;
use ratatui::widgets::{Block, Clear, Paragraph, Widget};
#[cfg(test)]
#[path = "popup_tests.rs"]
mod popup_tests;
pub struct Popup<T: Widget> {
pub struct Popup<'a, T: Widget> {
widget: T,
percent_x: u16,
percent_y: u16,
block: Option<Block<'a>>,
footer: Option<&'a str>,
footer_alignment: Alignment,
}
impl<T: Widget> Popup<T> {
impl<'a, T: Widget> Popup<'a, T> {
pub fn new(widget: T, percent_x: u16, percent_y: u16) -> Self {
Self {
widget,
percent_x,
percent_y,
block: None,
footer: None,
footer_alignment: Alignment::Left,
}
}
pub fn block(mut self, block: Block<'a>) -> Self {
self.block = Some(block);
self
}
pub fn footer(mut self, footer: &'a str) -> Self {
self.footer = Some(footer);
self
}
pub fn footer_alignment(mut self, alignment: Alignment) -> Self {
self.footer_alignment = alignment;
self
}
fn render_popup(self, area: Rect, buf: &mut Buffer) {
let popup_area = centered_rect(self.percent_x, self.percent_y, area);
Clear.render(popup_area, buf);
background_block().render(popup_area, buf);
self.widget.render(popup_area, buf);
if let Some(block) = self.block {
block.render(popup_area, buf);
}
let content_area = if let Some(footer) = self.footer {
let [content_area, help_footer_area] =
Layout::vertical([Constraint::Fill(0), Constraint::Length(2)])
.margin(1)
.areas(popup_area);
Paragraph::new(Text::from(format!(" {footer}").help()))
.block(layout_block_top_border())
.alignment(self.footer_alignment)
.render(help_footer_area, buf);
content_area
} else {
popup_area
};
self.widget.render(content_area, buf);
}
}
impl<T: Widget> Widget for Popup<T> {
impl<'a, T: Widget> Widget for Popup<'a, T> {
fn render(self, area: Rect, buf: &mut Buffer) {
self.render_popup(area, buf);
}
+40
View File
@@ -2,6 +2,7 @@
mod tests {
use crate::ui::widgets::popup::Popup;
use pretty_assertions::assert_eq;
use ratatui::layout::Alignment;
use ratatui::widgets::Block;
#[test]
@@ -11,5 +12,44 @@ mod tests {
assert_eq!(popup.widget, Block::new());
assert_eq!(popup.percent_x, 50);
assert_eq!(popup.percent_y, 50);
assert_eq!(popup.block, None);
assert_eq!(popup.footer, None);
assert_eq!(popup.footer_alignment, Alignment::Left);
}
#[test]
fn test_popup_block() {
let popup = Popup::new(Block::new(), 50, 50).block(Block::new());
assert_eq!(popup.block, Some(Block::new()));
assert_eq!(popup.widget, Block::new());
assert_eq!(popup.percent_x, 50);
assert_eq!(popup.percent_y, 50);
assert_eq!(popup.footer, None);
assert_eq!(popup.footer_alignment, Alignment::Left);
}
#[test]
fn test_popup_footer() {
let popup = Popup::new(Block::new(), 50, 50).footer("footer");
assert_eq!(popup.footer, Some("footer"));
assert_eq!(popup.widget, Block::new());
assert_eq!(popup.percent_x, 50);
assert_eq!(popup.percent_y, 50);
assert_eq!(popup.block, None);
assert_eq!(popup.footer_alignment, Alignment::Left);
}
#[test]
fn test_popup_footer_alignment() {
let popup = Popup::new(Block::new(), 50, 50).footer_alignment(Alignment::Center);
assert_eq!(popup.footer_alignment, Alignment::Center);
assert_eq!(popup.widget, Block::new());
assert_eq!(popup.percent_x, 50);
assert_eq!(popup.percent_y, 50);
assert_eq!(popup.block, None);
assert_eq!(popup.footer, None);
}
}
+17 -3
View File
@@ -5,7 +5,7 @@ use ratatui::buffer::Buffer;
use ratatui::layout::Rect;
use ratatui::prelude::Widget;
use ratatui::style::Style;
use ratatui::widgets::{List, ListItem, StatefulWidget};
use ratatui::widgets::{Block, List, ListItem, StatefulWidget};
#[cfg(test)]
#[path = "selectable_list_tests.rs"]
@@ -17,6 +17,8 @@ where
{
content: &'a mut StatefulList<T>,
row_mapper: F,
highlight_style: Style,
block: Block<'a>,
}
impl<'a, T, F> SelectableList<'a, T, F>
@@ -27,15 +29,27 @@ where
Self {
content,
row_mapper,
highlight_style: Style::new().highlight(),
block: layout_block(),
}
}
pub fn highlight_style(mut self, style: Style) -> Self {
self.highlight_style = style;
self
}
pub fn block(mut self, block: Block<'a>) -> Self {
self.block = block;
self
}
fn render_list(self, area: Rect, buf: &mut Buffer) {
let items: Vec<ListItem<'_>> = self.content.items.iter().map(&self.row_mapper).collect();
let selectable_list = List::new(items)
.block(layout_block())
.highlight_style(Style::new().highlight());
.block(self.block)
.highlight_style(self.highlight_style);
StatefulWidget::render(selectable_list, area, buf, &mut self.content.state);
}
+39
View File
@@ -1,8 +1,11 @@
#[cfg(test)]
mod tests {
use crate::models::stateful_list::StatefulList;
use crate::ui::styles::ManagarrStyle;
use crate::ui::utils::{layout_block, title_block};
use crate::ui::widgets::selectable_list::SelectableList;
use pretty_assertions::assert_eq;
use ratatui::style::{Style, Stylize};
use ratatui::widgets::ListItem;
#[test]
@@ -17,5 +20,41 @@ mod tests {
let row_mapper = selectable_list.row_mapper;
assert_eq!(selectable_list.content.items, items);
assert_eq!(row_mapper(&"test"), ListItem::new("test"));
assert_eq!(selectable_list.highlight_style, Style::new().highlight());
assert_eq!(selectable_list.block, layout_block());
}
#[test]
fn test_selectable_list_highlight_style() {
let items = vec!["test"];
let mut stateful_list = StatefulList::default();
stateful_list.set_items(items.clone());
let selectable_list =
SelectableList::new(&mut stateful_list, |item| ListItem::new(item.to_string()))
.highlight_style(Style::new().bold());
let row_mapper = selectable_list.row_mapper;
assert_eq!(selectable_list.highlight_style, Style::new().bold());
assert_eq!(selectable_list.content.items, items);
assert_eq!(row_mapper(&"test"), ListItem::new("test"));
assert_eq!(selectable_list.block, layout_block());
}
#[test]
fn test_selectable_list_block() {
let items = vec!["test"];
let mut stateful_list = StatefulList::default();
stateful_list.set_items(items.clone());
let selectable_list =
SelectableList::new(&mut stateful_list, |item| ListItem::new(item.to_string()))
.block(title_block("test"));
let row_mapper = selectable_list.row_mapper;
assert_eq!(selectable_list.block, title_block("test"));
assert_eq!(selectable_list.content.items, items);
assert_eq!(row_mapper(&"test"), ListItem::new("test"));
assert_eq!(selectable_list.highlight_style, Style::new().highlight());
}
}