mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-07-27 07:34:27 +02:00
Sorting
This commit is contained in:
parent
b0d4fd041a
commit
8f790d0b75
@ -262,7 +262,9 @@ impl Application for AppState {
|
||||
)),
|
||||
FlexElement::new(TextTable::build(
|
||||
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,
|
||||
|
@ -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<'_> {
|
||||
fn from(row: DataRow) -> Self {
|
||||
if let Some(style) = row.style {
|
||||
|
@ -59,40 +59,6 @@ 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;
|
||||
@ -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> {
|
||||
fn draw<B>(
|
||||
&mut self, state_ctx: &mut StateContext<'_>, draw_ctx: &DrawContext<'_>,
|
||||
@ -150,7 +150,7 @@ impl<Message> TmpComponent<Message> for TextTable<Message> {
|
||||
) where
|
||||
B: Backend,
|
||||
{
|
||||
let rect = draw_ctx.rect();
|
||||
let rect = draw_ctx.global_rect();
|
||||
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.
|
||||
|
||||
@ -182,7 +182,6 @@ impl<Message> TmpComponent<Message> for TextTable<Message> {
|
||||
.display_start_index(rect, 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| {
|
||||
let r: Row<'_> = row.into();
|
||||
r
|
||||
@ -212,7 +211,7 @@ impl<Message> TmpComponent<Message> for TextTable<Message> {
|
||||
use crate::tuine::MouseBoundIntersect;
|
||||
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);
|
||||
|
||||
match event {
|
||||
@ -230,24 +229,69 @@ impl<Message> TmpComponent<Message> for TextTable<Message> {
|
||||
match mouse_event.kind {
|
||||
MouseEventKind::Down(MouseButton::Left) => {
|
||||
let y = mouse_event.row - rect.top();
|
||||
|
||||
if y == 0 {
|
||||
let x = mouse_event.column - rect.left();
|
||||
match state.sort {
|
||||
SortType::Unsortable => Status::Ignored,
|
||||
SortType::Ascending(column) => {
|
||||
// Sort by the clicked column! If already using column, reverse!
|
||||
// self.sort_data();
|
||||
todo!()
|
||||
SortType::Ascending(column) | SortType::Descending(column) => {
|
||||
let mut cursor = 0;
|
||||
for (selected_column, width) in
|
||||
self.column_widths.iter().enumerate()
|
||||
{
|
||||
let end = cursor + width;
|
||||
|
||||
if x >= cursor && x <= end {
|
||||
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(column) => {
|
||||
// Sort by the clicked column! If already using column, reverse!
|
||||
// self.sort_data();
|
||||
todo!()
|
||||
}
|
||||
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 {
|
||||
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 {
|
||||
Status::Ignored
|
||||
}
|
||||
|
@ -39,8 +39,8 @@ impl<Message> TextTableProps<Message> {
|
||||
/// Sets the row to display in the table.
|
||||
///
|
||||
/// Defaults to displaying no data if not set.
|
||||
pub fn rows(mut self, rows: Vec<DataRow>) -> Self {
|
||||
self.rows = rows;
|
||||
pub fn rows<R: Into<DataRow>>(mut self, rows: Vec<R>) -> Self {
|
||||
self.rows = rows.into_iter().map(Into::into).collect();
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,6 @@ use std::borrow::Cow;
|
||||
pub struct TextColumn {
|
||||
pub name: Cow<'static, str>,
|
||||
pub width_constraint: TextColumnConstraint,
|
||||
x_bounds: Option<(u16, u16)>,
|
||||
}
|
||||
|
||||
pub enum TextColumnConstraint {
|
||||
@ -29,7 +28,6 @@ impl TextColumn {
|
||||
Self {
|
||||
name: name.into(),
|
||||
width_constraint: TextColumnConstraint::Fill,
|
||||
x_bounds: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,14 +35,4 @@ impl TextColumn {
|
||||
self.width_constraint = width_constraint;
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
rect.x += self.current_offset.0;
|
||||
rect.y += self.current_offset.1;
|
||||
|
Loading…
x
Reference in New Issue
Block a user