This commit is contained in:
ClementTsang 2021-12-26 18:05:56 -05:00
parent b0d4fd041a
commit 8f790d0b75
6 changed files with 107 additions and 64 deletions

View File

@ -262,7 +262,9 @@ impl Application for AppState {
)), )),
FlexElement::new(TextTable::build( FlexElement::new(TextTable::build(
ctx, ctx,
TextTableProps::new(vec!["L", "EM", "NO", "PQ"]), TextTableProps::new(vec!["L", "EM", "NO", "PQ"])
.rows(vec![vec![1, 2, 3, 4], vec![4, 3, 2, 1]])
.default_sort(crate::tuine::SortType::Descending(0)),
)), )),
]), ]),
2, 2,

View File

@ -35,6 +35,15 @@ impl DataRow {
} }
} }
impl<D: Into<DataCell>> From<Vec<D>> for DataRow {
fn from(vec: Vec<D>) -> Self {
Self {
cells: vec.into_iter().map(Into::into).collect(),
style: None,
}
}
}
impl From<DataRow> for Row<'_> { impl From<DataRow> for Row<'_> {
fn from(row: DataRow) -> Self { fn from(row: DataRow) -> Self {
if let Some(style) = row.style { if let Some(style) = row.style {

View File

@ -59,40 +59,6 @@ pub struct TextTable<Message> {
on_selected_click: Option<Box<dyn Fn(usize) -> 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> { impl<Message> TextTable<Message> {
fn update_column_widths(&mut self, bounds: Rect) { fn update_column_widths(&mut self, bounds: Rect) {
let total_width = bounds.width; let total_width = bounds.width;
@ -143,6 +109,40 @@ impl<Message> TextTable<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> TmpComponent<Message> for TextTable<Message> { impl<Message> TmpComponent<Message> for TextTable<Message> {
fn draw<B>( fn draw<B>(
&mut self, state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>, &mut self, state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>,
@ -150,7 +150,7 @@ impl<Message> TmpComponent<Message> for TextTable<Message> {
) where ) where
B: Backend, B: Backend,
{ {
let rect = draw_ctx.rect(); let rect = draw_ctx.global_rect();
let state = state_ctx.mut_state::<TextTableState>(self.key); let state = state_ctx.mut_state::<TextTableState>(self.key);
state.scroll.set_num_items(self.rows.len()); // FIXME: Not a fan of this system like this - should be easier to do. state.scroll.set_num_items(self.rows.len()); // FIXME: Not a fan of this system like this - should be easier to do.
@ -182,7 +182,6 @@ impl<Message> TmpComponent<Message> for TextTable<Message> {
.display_start_index(rect, scrollable_height as usize); .display_start_index(rect, scrollable_height as usize);
let end = min(state.scroll.num_items(), start + scrollable_height as usize); let end = min(state.scroll.num_items(), start + scrollable_height as usize);
debug!("Start: {}, end: {}", start, end);
self.rows.drain(start..end).into_iter().map(|row| { self.rows.drain(start..end).into_iter().map(|row| {
let r: Row<'_> = row.into(); let r: Row<'_> = row.into();
r r
@ -212,7 +211,7 @@ impl<Message> TmpComponent<Message> for TextTable<Message> {
use crate::tuine::MouseBoundIntersect; use crate::tuine::MouseBoundIntersect;
use crossterm::event::{MouseButton, MouseEventKind}; use crossterm::event::{MouseButton, MouseEventKind};
let rect = draw_ctx.rect(); let rect = draw_ctx.global_rect();
let state = state_ctx.mut_state::<TextTableState>(self.key); let state = state_ctx.mut_state::<TextTableState>(self.key);
match event { match event {
@ -230,24 +229,69 @@ impl<Message> TmpComponent<Message> for TextTable<Message> {
match mouse_event.kind { match mouse_event.kind {
MouseEventKind::Down(MouseButton::Left) => { MouseEventKind::Down(MouseButton::Left) => {
let y = mouse_event.row - rect.top(); let y = mouse_event.row - rect.top();
if y == 0 { if y == 0 {
let x = mouse_event.column - rect.left();
match state.sort { match state.sort {
SortType::Unsortable => Status::Ignored, SortType::Unsortable => Status::Ignored,
SortType::Ascending(column) => { SortType::Ascending(column) | SortType::Descending(column) => {
// Sort by the clicked column! If already using column, reverse! let mut cursor = 0;
// self.sort_data(); for (selected_column, width) in
todo!() self.column_widths.iter().enumerate()
} {
SortType::Descending(column) => { let end = cursor + width;
// Sort by the clicked column! If already using column, reverse!
// self.sort_data(); if x >= cursor && x <= end {
todo!() match state.sort {
SortType::Ascending(_) => {
if selected_column == column {
// FIXME: This should handle default sorting orders...
state.sort = SortType::Descending(
selected_column,
);
} else {
state.sort = SortType::Ascending(
selected_column,
);
}
}
SortType::Descending(_) => {
if selected_column == column {
// FIXME: This should handle default sorting orders...
state.sort = SortType::Ascending(
selected_column,
);
} else {
state.sort = SortType::Descending(
selected_column,
);
}
}
SortType::Unsortable => unreachable!(), // Should be impossible by above check.
}
return Status::Captured;
} else {
cursor += width;
}
}
Status::Ignored
} }
} }
} else if y > self.table_gap { } else if y > self.table_gap {
let visual_index = usize::from(y - self.table_gap); let visual_index = usize::from(y - self.table_gap);
state.scroll.set_visual_index(visual_index) match state.scroll.set_visual_index(visual_index) {
Status::Captured => Status::Captured,
Status::Ignored => {
if let Some(on_selected_click) = &self.on_selected_click {
messages.push(on_selected_click(
state.scroll.current_index(),
));
Status::Captured
} else {
Status::Ignored
}
}
}
} else { } else {
Status::Ignored Status::Ignored
} }

View File

@ -39,8 +39,8 @@ impl<Message> TextTableProps<Message> {
/// Sets the row to display in the table. /// Sets the row to display in the table.
/// ///
/// Defaults to displaying no data if not set. /// Defaults to displaying no data if not set.
pub fn rows(mut self, rows: Vec<DataRow>) -> Self { pub fn rows<R: Into<DataRow>>(mut self, rows: Vec<R>) -> Self {
self.rows = rows; self.rows = rows.into_iter().map(Into::into).collect();
self self
} }

View File

@ -3,7 +3,6 @@ use std::borrow::Cow;
pub struct TextColumn { pub struct TextColumn {
pub name: Cow<'static, str>, pub name: Cow<'static, str>,
pub width_constraint: TextColumnConstraint, pub width_constraint: TextColumnConstraint,
x_bounds: Option<(u16, u16)>,
} }
pub enum TextColumnConstraint { pub enum TextColumnConstraint {
@ -29,7 +28,6 @@ impl TextColumn {
Self { Self {
name: name.into(), name: name.into(),
width_constraint: TextColumnConstraint::Fill, width_constraint: TextColumnConstraint::Fill,
x_bounds: None,
} }
} }
@ -37,14 +35,4 @@ impl TextColumn {
self.width_constraint = width_constraint; self.width_constraint = width_constraint;
self self
} }
/// Set the text column's x bounds.
pub(crate) fn set_x_bounds(&mut self, x_bounds: Option<(u16, u16)>) {
self.x_bounds = x_bounds;
}
/// Get the text column's x-coordinates.
pub fn x_bounds(&self) -> Option<(u16, u16)> {
self.x_bounds
}
} }

View File

@ -16,7 +16,7 @@ impl<'a> DrawContext<'a> {
} }
} }
pub(crate) fn rect(&self) -> Rect { pub(crate) fn global_rect(&self) -> Rect {
let mut rect = self.current_node.rect; let mut rect = self.current_node.rect;
rect.x += self.current_offset.0; rect.x += self.current_offset.0;
rect.y += self.current_offset.1; rect.y += self.current_offset.1;