mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-04-08 17:05:59 +02:00
Move to semi-builder
This commit is contained in:
parent
8c3a44deba
commit
b0d4fd041a
20
src/app.rs
20
src/app.rs
@ -240,22 +240,30 @@ impl Application for AppState {
|
||||
|
||||
fn view<'b>(&mut self, ctx: &mut ViewContext<'_>) -> Element<Self::Message> {
|
||||
use crate::tuine::FlexElement;
|
||||
use crate::tuine::StatefulTemplate;
|
||||
use crate::tuine::TempTable;
|
||||
use crate::tuine::TextTableBuilder;
|
||||
use crate::tuine::StatefulComponent;
|
||||
use crate::tuine::{TempTable, TextTable, TextTableProps};
|
||||
|
||||
Flex::column()
|
||||
.with_flex_child(
|
||||
Flex::row_with_children(vec![
|
||||
FlexElement::new(TempTable::new(ctx)),
|
||||
FlexElement::new(TextTableBuilder::new(vec!["D", "E", "F"]).build(ctx)),
|
||||
FlexElement::new(TextTable::build(
|
||||
ctx,
|
||||
TextTableProps::new(vec!["D", "E", "F"]),
|
||||
)),
|
||||
]),
|
||||
1,
|
||||
)
|
||||
.with_flex_child(
|
||||
Flex::row_with_children(vec![
|
||||
FlexElement::new(TextTableBuilder::new(vec!["G", "H", "I", "J"]).build(ctx)),
|
||||
FlexElement::new(TextTableBuilder::new(vec!["K", "L", "M", "N"]).build(ctx)),
|
||||
FlexElement::new(TextTable::build(
|
||||
ctx,
|
||||
TextTableProps::new(vec!["G", "H", "I", "J"]),
|
||||
)),
|
||||
FlexElement::new(TextTable::build(
|
||||
ctx,
|
||||
TextTableProps::new(vec!["L", "EM", "NO", "PQ"]),
|
||||
)),
|
||||
]),
|
||||
2,
|
||||
)
|
||||
|
@ -1,5 +1,5 @@
|
||||
pub mod text_table;
|
||||
pub use text_table::{TextColumn, TextColumnConstraint, TextTable, TextTableBuilder};
|
||||
pub use text_table::*;
|
||||
|
||||
pub mod shortcut;
|
||||
pub use shortcut::Shortcut;
|
||||
|
@ -10,13 +10,13 @@ pub use data_row::DataRow;
|
||||
pub mod data_cell;
|
||||
pub use data_cell::DataCell;
|
||||
|
||||
pub mod builder;
|
||||
pub use builder::TextTableBuilder;
|
||||
|
||||
pub mod sort_type;
|
||||
pub use sort_type::SortType;
|
||||
|
||||
use std::cmp::min;
|
||||
pub mod props;
|
||||
pub use props::TextTableProps;
|
||||
|
||||
use std::{cmp::min, panic::Location};
|
||||
|
||||
use tui::{
|
||||
backend::Backend,
|
||||
@ -29,7 +29,7 @@ use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
use crate::{
|
||||
constants::TABLE_GAP_HEIGHT_LIMIT,
|
||||
tuine::{DrawContext, Event, Key, StateContext, Status, TmpComponent},
|
||||
tuine::{DrawContext, Event, Key, StateContext, StatefulComponent, Status, TmpComponent},
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
@ -59,6 +59,40 @@ pub struct TextTable<Message> {
|
||||
on_selected_click: Option<Box<dyn Fn(usize) -> Message>>,
|
||||
}
|
||||
|
||||
impl<Message> StatefulComponent<Message> for TextTable<Message> {
|
||||
type Properties = TextTableProps<Message>;
|
||||
|
||||
type ComponentState = TextTableState;
|
||||
|
||||
#[track_caller]
|
||||
fn build(ctx: &mut crate::tuine::ViewContext<'_>, mut props: Self::Properties) -> Self {
|
||||
let sort = props.sort;
|
||||
let (key, state) = ctx.register_and_mut_state_with_default::<_, Self::ComponentState, _>(
|
||||
Location::caller(),
|
||||
|| TextTableState {
|
||||
scroll: Default::default(),
|
||||
sort,
|
||||
},
|
||||
);
|
||||
|
||||
state.scroll.set_num_items(props.rows.len());
|
||||
props.try_sort_data(state.sort);
|
||||
|
||||
TextTable {
|
||||
key,
|
||||
column_widths: props.column_widths,
|
||||
columns: props.columns,
|
||||
show_gap: props.show_gap,
|
||||
show_selected_entry: props.show_selected_entry,
|
||||
rows: props.rows,
|
||||
style_sheet: props.style_sheet,
|
||||
table_gap: props.table_gap,
|
||||
on_select: props.on_select,
|
||||
on_selected_click: props.on_selected_click,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Message> TextTable<Message> {
|
||||
fn update_column_widths(&mut self, bounds: Rect) {
|
||||
let total_width = bounds.width;
|
||||
@ -245,8 +279,7 @@ impl<Message> TmpComponent<Message> for TextTable<Message> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tuine::{
|
||||
text_table::{SortType, TextTableBuilder},
|
||||
StateMap, StatefulTemplate, ViewContext,
|
||||
text_table::SortType, StateMap, StatefulComponent, TextTableProps, ViewContext,
|
||||
};
|
||||
|
||||
use super::{DataRow, TextTable};
|
||||
@ -268,10 +301,13 @@ mod tests {
|
||||
let index = 1;
|
||||
|
||||
let mut map = StateMap::default();
|
||||
let table: TextTable<Message> = TextTableBuilder::new(vec!["Sensor", "Temp"])
|
||||
.default_sort(SortType::Ascending(index))
|
||||
.rows(rows)
|
||||
.build(&mut ctx(&mut map));
|
||||
let ctx = &mut ctx(&mut map);
|
||||
let table: TextTable<Message> = TextTable::build(
|
||||
ctx,
|
||||
TextTableProps::new(vec!["Sensor", "Temp"])
|
||||
.default_sort(SortType::Ascending(index))
|
||||
.rows(rows),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
table.rows.len(),
|
||||
@ -301,11 +337,14 @@ mod tests {
|
||||
let new_index = 0;
|
||||
|
||||
let mut map = StateMap::default();
|
||||
let table: TextTable<Message> = TextTableBuilder::new(vec!["Sensor", "Temp"])
|
||||
.default_sort(SortType::Ascending(index))
|
||||
.rows(rows)
|
||||
.default_sort(SortType::Ascending(new_index))
|
||||
.build(&mut ctx(&mut map));
|
||||
let ctx = &mut ctx(&mut map);
|
||||
let table: TextTable<Message> = TextTable::build(
|
||||
ctx,
|
||||
TextTableProps::new(vec!["Sensor", "Temp"])
|
||||
.default_sort(SortType::Ascending(index))
|
||||
.rows(rows)
|
||||
.default_sort(SortType::Ascending(new_index)),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
table.rows.len(),
|
||||
@ -334,10 +373,13 @@ mod tests {
|
||||
let index = 1;
|
||||
|
||||
let mut map = StateMap::default();
|
||||
let table: TextTable<Message> = TextTableBuilder::new(vec!["Sensor", "Temp"])
|
||||
.default_sort(SortType::Descending(index))
|
||||
.rows(rows)
|
||||
.build(&mut ctx(&mut map));
|
||||
let ctx = &mut ctx(&mut map);
|
||||
let table: TextTable<Message> = TextTable::build(
|
||||
ctx,
|
||||
TextTableProps::new(vec!["Sensor", "Temp"])
|
||||
.default_sort(SortType::Descending(index))
|
||||
.rows(rows),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
table.rows.len(),
|
||||
@ -366,11 +408,14 @@ mod tests {
|
||||
let index = 1;
|
||||
|
||||
let mut map = StateMap::default();
|
||||
let table: TextTable<Message> = TextTableBuilder::new(vec!["Sensor", "Temp"])
|
||||
.rows(rows)
|
||||
.default_sort(SortType::Ascending(index))
|
||||
.row(DataRow::default().cell("X").cell(0))
|
||||
.build(&mut ctx(&mut map));
|
||||
let ctx = &mut ctx(&mut map);
|
||||
let table: TextTable<Message> = TextTable::build(
|
||||
ctx,
|
||||
TextTableProps::new(vec!["Sensor", "Temp"])
|
||||
.rows(rows)
|
||||
.default_sort(SortType::Ascending(index))
|
||||
.row(DataRow::default().cell("X").cell(0)),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
table.rows.len(),
|
||||
@ -400,10 +445,13 @@ mod tests {
|
||||
let row_length = original_rows.len();
|
||||
|
||||
let mut map = StateMap::default();
|
||||
let table: TextTable<Message> = TextTableBuilder::new(vec!["Sensor", "Temp"])
|
||||
.rows(rows)
|
||||
.row(original_rows[3].clone())
|
||||
.build(&mut ctx(&mut map));
|
||||
let ctx = &mut ctx(&mut map);
|
||||
let table: TextTable<Message> = TextTable::build(
|
||||
ctx,
|
||||
TextTableProps::new(vec!["Sensor", "Temp"])
|
||||
.rows(rows)
|
||||
.row(original_rows[3].clone()),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
table.rows.len(),
|
||||
|
@ -1,23 +1,23 @@
|
||||
use std::{borrow::Cow, panic::Location};
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::tuine::{StatefulTemplate, ViewContext};
|
||||
use crate::tuine::{DataRow, SortType, TextColumn};
|
||||
|
||||
use super::{DataRow, SortType, StyleSheet, TextColumn, TextTable, TextTableState};
|
||||
use super::StyleSheet;
|
||||
|
||||
pub struct TextTableBuilder<Message> {
|
||||
column_widths: Vec<u16>,
|
||||
columns: Vec<TextColumn>,
|
||||
show_gap: bool,
|
||||
show_selected_entry: bool,
|
||||
rows: Vec<DataRow>,
|
||||
style_sheet: StyleSheet,
|
||||
sort: SortType,
|
||||
table_gap: u16,
|
||||
on_select: Option<Box<dyn Fn(usize) -> Message>>,
|
||||
on_selected_click: Option<Box<dyn Fn(usize) -> Message>>,
|
||||
pub struct TextTableProps<Message> {
|
||||
pub(crate) column_widths: Vec<u16>,
|
||||
pub(crate) columns: Vec<TextColumn>,
|
||||
pub(crate) show_gap: bool,
|
||||
pub(crate) show_selected_entry: bool,
|
||||
pub(crate) rows: Vec<DataRow>,
|
||||
pub(crate) style_sheet: StyleSheet,
|
||||
pub(crate) sort: SortType,
|
||||
pub(crate) table_gap: u16,
|
||||
pub(crate) on_select: Option<Box<dyn Fn(usize) -> Message>>,
|
||||
pub(crate) on_selected_click: Option<Box<dyn Fn(usize) -> Message>>,
|
||||
}
|
||||
|
||||
impl<Message> TextTableBuilder<Message> {
|
||||
impl<Message> TextTableProps<Message> {
|
||||
pub fn new<S: Into<Cow<'static, str>>>(columns: Vec<S>) -> Self {
|
||||
Self {
|
||||
column_widths: vec![0; columns.len()],
|
||||
@ -94,7 +94,7 @@ impl<Message> TextTableBuilder<Message> {
|
||||
self
|
||||
}
|
||||
|
||||
fn try_sort_data(&mut self, sort_type: SortType) {
|
||||
pub(crate) fn try_sort_data(&mut self, sort_type: SortType) {
|
||||
use std::cmp::Ordering;
|
||||
|
||||
// TODO: We can avoid some annoying checks by using const generics - this is waiting on
|
||||
@ -121,36 +121,3 @@ impl<Message> TextTableBuilder<Message> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Message> StatefulTemplate<Message> for TextTableBuilder<Message> {
|
||||
type Component = TextTable<Message>;
|
||||
type ComponentState = TextTableState;
|
||||
|
||||
#[track_caller]
|
||||
fn build(mut self, ctx: &mut ViewContext<'_>) -> Self::Component {
|
||||
let sort = self.sort;
|
||||
let (key, state) = ctx.register_and_mut_state_with_default::<_, Self::ComponentState, _>(
|
||||
Location::caller(),
|
||||
|| TextTableState {
|
||||
scroll: Default::default(),
|
||||
sort,
|
||||
},
|
||||
);
|
||||
|
||||
state.scroll.set_num_items(self.rows.len());
|
||||
self.try_sort_data(state.sort);
|
||||
|
||||
TextTable {
|
||||
key,
|
||||
column_widths: self.column_widths,
|
||||
columns: self.columns,
|
||||
show_gap: self.show_gap,
|
||||
show_selected_entry: self.show_selected_entry,
|
||||
rows: self.rows,
|
||||
style_sheet: self.style_sheet,
|
||||
table_gap: self.table_gap,
|
||||
on_select: self.on_select,
|
||||
on_selected_click: self.on_selected_click,
|
||||
}
|
||||
}
|
||||
}
|
@ -2,13 +2,13 @@ use crate::tuine::{State, ViewContext};
|
||||
|
||||
use super::TmpComponent;
|
||||
|
||||
/// A [`StatefulTemplate`] is a builder-style pattern for building a stateful
|
||||
/// A [`StatefulComponent`] is a builder-style pattern for building a stateful
|
||||
/// [`Component`].
|
||||
///
|
||||
/// Inspired by Flutter's StatefulWidget interface.
|
||||
pub trait StatefulTemplate<Message> {
|
||||
type Component: TmpComponent<Message>;
|
||||
pub trait StatefulComponent<Message>: TmpComponent<Message> {
|
||||
type Properties;
|
||||
type ComponentState: State;
|
||||
|
||||
fn build(self, ctx: &mut ViewContext<'_>) -> Self::Component;
|
||||
fn build(ctx: &mut ViewContext<'_>, props: Self::Properties) -> Self;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::tuine::{
|
||||
text_table::{DataRow, SortType, TextTableBuilder},
|
||||
Shortcut, StatefulTemplate, TextTable, TmpComponent, ViewContext,
|
||||
text_table::{DataRow, SortType, TextTableProps},
|
||||
Shortcut, StatefulComponent, TextTable, TmpComponent, ViewContext,
|
||||
};
|
||||
|
||||
/// A [`TempTable`] is a text table that is meant to display temperature data.
|
||||
@ -12,16 +12,16 @@ impl<Message> TempTable<Message> {
|
||||
#[track_caller]
|
||||
pub fn new(ctx: &mut ViewContext<'_>) -> Self {
|
||||
Self {
|
||||
inner: Shortcut::with_child(
|
||||
TextTableBuilder::new(vec!["Sensor", "Temp"])
|
||||
inner: Shortcut::with_child(TextTable::build(
|
||||
ctx,
|
||||
TextTableProps::new(vec!["Sensor", "Temp"])
|
||||
.rows(vec![
|
||||
DataRow::default().cell("A").cell(2),
|
||||
DataRow::default().cell("B").cell(3),
|
||||
DataRow::default().cell("C").cell(1),
|
||||
])
|
||||
.default_sort(SortType::Ascending(1))
|
||||
.build(ctx),
|
||||
),
|
||||
.default_sort(SortType::Ascending(1)),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user