use crate::_private::NonExhaustive;
use crate::event::{FocusKeys, HandleEvent, MouseOnly, Outcome};
#[allow(unused_imports)]
use log::debug;
use rat_focus::{FocusFlag, HasFocusFlag};
use rat_ftable::selection::{CellSelection, NoSelection, RowSelection, RowSetSelection};
use rat_ftable::textdata::Row;
use rat_scrolled::{ScrollingState, ScrollingWidget};
use ratatui::buffer::Buffer;
use ratatui::layout::{Constraint, Flex, Rect};
use ratatui::style::Style;
use ratatui::widgets::{Block, StatefulWidget, StatefulWidgetRef};
use std::collections::HashSet;
use rat_ftable::event::{DoubleClick, DoubleClickOutcome, EditKeys, EditOutcome};
pub use rat_ftable::{FTableContext, FTableStyle, TableData, TableDataIter, TableSelection};
pub mod selection {
pub use rat_ftable::selection::{CellSelection, NoSelection, RowSelection, RowSetSelection};
}
pub mod textdata {
pub use rat_ftable::textdata::{Cell, Row};
}
#[derive(Debug, Default)]
pub struct RTable<'a, Selection> {
widget: rat_ftable::FTable<'a, Selection>,
}
#[derive(Debug, Clone)]
pub struct RTableState<Selection> {
pub widget: rat_ftable::FTableState<Selection>,
pub non_exhaustive: NonExhaustive,
}
impl<'a, Selection> RTable<'a, Selection> {
pub fn new() -> Self
where
Selection: Default,
{
Self::default()
}
pub fn new_ratatui<R, C>(rows: R, widths: C) -> Self
where
R: IntoIterator,
R::Item: Into<Row<'a>>,
C: IntoIterator,
C::Item: Into<Constraint>,
Selection: Default,
{
Self {
widget: rat_ftable::FTable::new_ratatui(rows, widths),
}
}
pub fn rows<T>(mut self, rows: T) -> Self
where
T: IntoIterator<Item = Row<'a>>,
{
self.widget = self.widget.rows(rows);
self
}
#[inline]
pub fn data(mut self, data: impl TableData<'a> + 'a) -> Self {
self.widget = self.widget.data(data);
self
}
#[inline]
pub fn iter(mut self, data: impl TableDataIter<'a> + 'a) -> Self {
self.widget = self.widget.iter(data);
self
}
#[inline]
pub fn header(mut self, header: Row<'a>) -> Self {
self.widget = self.widget.header(header);
self
}
#[inline]
pub fn footer(mut self, footer: Row<'a>) -> Self {
self.widget = self.widget.footer(footer);
self
}
pub fn widths<I>(mut self, widths: I) -> Self
where
I: IntoIterator,
I::Item: Into<Constraint>,
{
self.widget = self.widget.widths(widths);
self
}
#[inline]
pub fn flex(mut self, flex: Flex) -> Self {
self.widget = self.widget.flex(flex);
self
}
#[inline]
pub fn column_spacing(mut self, spacing: u16) -> Self {
self.widget = self.widget.column_spacing(spacing);
self
}
#[inline]
pub fn layout_width(mut self, width: u16) -> Self {
self.widget = self.widget.layout_width(width);
self
}
#[inline]
pub fn block(mut self, block: Block<'a>) -> Self {
self.widget = self.widget.block(block);
self
}
#[inline]
pub fn styles(mut self, styles: FTableStyle) -> Self {
self.widget = self.widget.styles(styles);
self
}
#[inline]
pub fn style(mut self, style: Style) -> Self {
self.widget = self.widget.style(style);
self
}
#[inline]
pub fn header_style(mut self, style: Option<Style>) -> Self {
self.widget = self.widget.header_style(style);
self
}
#[inline]
pub fn footer_style(mut self, style: Option<Style>) -> Self {
self.widget = self.widget.footer_style(style);
self
}
#[inline]
pub fn select_row_style(mut self, select_style: Option<Style>) -> Self {
self.widget = self.widget.select_row_style(select_style);
self
}
#[inline]
pub fn show_row_focus(mut self, show: bool) -> Self {
self.widget = self.widget.show_row_focus(show);
self
}
#[inline]
pub fn select_column_style(mut self, select_style: Option<Style>) -> Self {
self.widget = self.widget.select_column_style(select_style);
self
}
#[inline]
pub fn show_column_focus(mut self, show: bool) -> Self {
self.widget = self.widget.show_column_focus(show);
self
}
#[inline]
pub fn select_cell_style(mut self, select_style: Option<Style>) -> Self {
self.widget = self.widget.select_cell_style(select_style);
self
}
#[inline]
pub fn show_cell_focus(mut self, show: bool) -> Self {
self.widget = self.widget.show_cell_focus(show);
self
}
#[inline]
pub fn select_header_style(mut self, select_style: Option<Style>) -> Self {
self.widget = self.widget.select_header_style(select_style);
self
}
#[inline]
pub fn show_header_focus(mut self, show: bool) -> Self {
self.widget = self.widget.show_header_focus(show);
self
}
#[inline]
pub fn select_footer_style(mut self, select_style: Option<Style>) -> Self {
self.widget = self.widget.select_footer_style(select_style);
self
}
#[inline]
pub fn show_footer_focus(mut self, show: bool) -> Self {
self.widget = self.widget.show_footer_focus(show);
self
}
#[inline]
pub fn focus_style(mut self, focus_style: Option<Style>) -> Self {
self.widget = self.widget.focus_style(focus_style);
self
}
#[inline]
pub fn debug(mut self, debug: bool) -> Self {
self.widget = self.widget.debug(debug);
self
}
}
impl<'a, Selection> ScrollingWidget<RTableState<Selection>> for RTable<'a, Selection> {
#[inline]
fn need_scroll(&self, area: Rect, _state: &mut RTableState<Selection>) -> (bool, bool) {
self.widget.need_scroll(area)
}
}
impl<'a, Selection> StatefulWidgetRef for RTable<'a, Selection>
where
Selection: TableSelection,
{
type State = RTableState<Selection>;
fn render_ref(&self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
self.widget.render_ref(area, buf, &mut state.widget);
}
}
impl<'a, Selection> StatefulWidget for RTable<'a, Selection>
where
Selection: TableSelection,
{
type State = RTableState<Selection>;
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
self.widget.render(area, buf, &mut state.widget);
}
}
impl<Selection: Default> Default for RTableState<Selection> {
fn default() -> Self {
Self {
widget: rat_ftable::FTableState::default(),
non_exhaustive: NonExhaustive,
}
}
}
impl<Selection> RTableState<Selection> {
#[inline]
pub fn rows(&self) -> usize {
self.widget.rows()
}
#[inline]
pub fn columns(&self) -> usize {
self.widget.columns()
}
#[inline]
pub fn row_cells(&self, row: usize) -> Option<(Rect, Vec<Rect>)> {
self.widget.row_cells(row)
}
#[inline]
pub fn cell_at_clicked(&self, pos: (u16, u16)) -> Option<(usize, usize)> {
self.widget.cell_at_clicked(pos)
}
#[inline]
pub fn column_at_clicked(&self, pos: (u16, u16)) -> Option<usize> {
self.widget.column_at_clicked(pos)
}
#[inline]
pub fn row_at_clicked(&self, pos: (u16, u16)) -> Option<usize> {
self.widget.row_at_clicked(pos)
}
#[inline]
pub fn cell_at_drag(&self, pos: (u16, u16)) -> (usize, usize) {
self.widget.cell_at_drag(pos)
}
#[inline]
pub fn row_at_drag(&self, pos: (u16, u16)) -> usize {
self.widget.row_at_drag(pos)
}
#[inline]
pub fn column_at_drag(&self, pos: (u16, u16)) -> usize {
self.widget.column_at_drag(pos)
}
#[inline]
pub fn clear_offset(&mut self) {
self.widget.clear_offset();
}
}
impl<Selection: TableSelection> RTableState<Selection> {
#[inline]
pub fn scroll_to_selected(&mut self) {
self.widget.scroll_to_selected()
}
#[inline]
pub fn scroll_to(&mut self, pos: (usize, usize)) {
self.widget.scroll_to(pos);
}
}
impl<Selection: TableSelection> ScrollingState for RTableState<Selection> {
#[inline]
fn vertical_max_offset(&self) -> usize {
self.widget.vertical_max_offset()
}
#[inline]
fn vertical_offset(&self) -> usize {
self.widget.vertical_offset()
}
#[inline]
fn vertical_page(&self) -> usize {
self.widget.vertical_page()
}
#[inline]
fn vertical_scroll(&self) -> usize {
self.widget.vertical_scroll()
}
#[inline]
fn horizontal_max_offset(&self) -> usize {
self.widget.horizontal_max_offset()
}
#[inline]
fn horizontal_offset(&self) -> usize {
self.widget.horizontal_offset()
}
#[inline]
fn horizontal_page(&self) -> usize {
self.widget.horizontal_page()
}
#[inline]
fn horizontal_scroll(&self) -> usize {
self.widget.horizontal_scroll()
}
#[inline]
fn set_vertical_offset(&mut self, offset: usize) -> bool {
self.widget.set_vertical_offset(offset)
}
#[inline]
fn set_horizontal_offset(&mut self, offset: usize) -> bool {
self.widget.set_horizontal_offset(offset)
}
#[inline]
fn scroll_up(&mut self, n: usize) -> bool {
self.widget.scroll_up(n)
}
#[inline]
fn scroll_down(&mut self, n: usize) -> bool {
self.widget.scroll_down(n)
}
#[inline]
fn scroll_left(&mut self, n: usize) -> bool {
self.widget.scroll_left(n)
}
#[inline]
fn scroll_right(&mut self, n: usize) -> bool {
self.widget.scroll_right(n)
}
}
impl HandleEvent<crossterm::event::Event, FocusKeys, Outcome> for RTableState<NoSelection> {
fn handle(&mut self, event: &crossterm::event::Event, _keymap: FocusKeys) -> Outcome {
if self.is_focused() {
self.widget.handle(event, FocusKeys)
} else {
self.widget.handle(event, MouseOnly)
}
}
}
impl HandleEvent<crossterm::event::Event, MouseOnly, Outcome> for RTableState<NoSelection> {
fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> Outcome {
self.widget.handle(event, MouseOnly)
}
}
impl RTableState<RowSelection> {
#[inline]
pub fn set_scroll_selection(&mut self, scroll: bool) {
self.widget.set_scroll_selection(scroll);
}
#[inline]
pub fn scroll_selection(&self) -> bool {
self.widget.scroll_selection()
}
#[inline]
pub fn clear(&mut self) {
self.widget.clear();
}
#[inline]
pub fn clear_selection(&mut self) {
self.widget.clear_selection();
}
#[inline]
pub fn has_selection(&mut self) -> bool {
self.widget.has_selection()
}
#[inline]
pub fn selected(&self) -> Option<usize> {
self.widget.selected()
}
#[inline]
pub fn select(&mut self, row: Option<usize>) {
self.widget.select(row);
}
#[inline]
pub fn select_clamped(&mut self, select: usize, maximum: usize) {
self.widget.select_clamped(select, maximum);
}
}
impl HandleEvent<crossterm::event::Event, FocusKeys, Outcome> for RTableState<RowSelection> {
fn handle(&mut self, event: &crossterm::event::Event, _keymap: FocusKeys) -> Outcome {
if self.is_focused() {
self.widget.handle(event, FocusKeys)
} else {
self.widget.handle(event, MouseOnly)
}
}
}
impl HandleEvent<crossterm::event::Event, MouseOnly, Outcome> for RTableState<RowSelection> {
fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> Outcome {
self.widget.handle(event, MouseOnly)
}
}
impl RTableState<RowSetSelection> {
#[inline]
pub fn clear(&mut self) {
self.widget.clear();
}
#[inline]
pub fn clear_selection(&mut self) {
self.widget.clear_selection();
}
#[inline]
pub fn has_selection(&mut self) -> bool {
self.widget.has_selection()
}
#[inline]
pub fn selected(&self) -> HashSet<usize> {
self.widget.selected()
}
#[inline]
pub fn set_lead(&mut self, lead: Option<usize>, extend: bool) {
self.widget.set_lead(lead, extend);
}
#[inline]
pub fn set_lead_clamped(&mut self, lead: usize, max: usize, extend: bool) {
self.widget.set_lead_clamped(lead, max, extend);
}
#[inline]
pub fn lead(&self) -> Option<usize> {
self.widget.lead()
}
#[inline]
pub fn anchor(&self) -> Option<usize> {
self.widget.anchor()
}
#[inline]
pub fn add_selected(&mut self, idx: usize) {
self.widget.add_selected(idx);
}
#[inline]
pub fn remove_selected(&mut self, idx: usize) {
self.widget.remove_selected(idx);
}
}
impl HandleEvent<crossterm::event::Event, FocusKeys, Outcome> for RTableState<RowSetSelection> {
fn handle(&mut self, event: &crossterm::event::Event, _: FocusKeys) -> Outcome {
if self.is_focused() {
self.widget.handle(event, FocusKeys)
} else {
self.widget.handle(event, MouseOnly)
}
}
}
impl HandleEvent<crossterm::event::Event, MouseOnly, Outcome> for RTableState<RowSetSelection> {
fn handle(&mut self, event: &crossterm::event::Event, _: MouseOnly) -> Outcome {
self.widget.handle(event, MouseOnly)
}
}
impl RTableState<CellSelection> {
#[inline]
pub fn clear(&mut self) {
self.widget.clear();
}
#[inline]
pub fn clear_selection(&mut self) {
self.widget.clear_selection();
}
#[inline]
pub fn selected(&self) -> Option<(usize, usize)> {
self.widget.selected()
}
#[inline]
pub fn has_selection(&mut self) -> bool {
self.widget.has_selection()
}
#[inline]
pub fn select_cell(&mut self, select: Option<(usize, usize)>) {
self.widget.select_cell(select);
}
#[inline]
pub fn select_row(&mut self, select: Option<usize>) {
self.widget.select_row(select);
}
#[inline]
pub fn select_column(&mut self, select: Option<usize>) {
self.widget.select_column(select);
}
#[inline]
pub fn select_clamped(&mut self, select: (usize, usize), maximum: (usize, usize)) {
self.widget.select_clamped(select, maximum);
}
}
impl HandleEvent<crossterm::event::Event, FocusKeys, Outcome> for RTableState<CellSelection> {
fn handle(&mut self, event: &crossterm::event::Event, _keymap: FocusKeys) -> Outcome {
if self.is_focused() {
self.widget.handle(event, FocusKeys)
} else {
self.widget.handle(event, MouseOnly)
}
}
}
impl HandleEvent<crossterm::event::Event, MouseOnly, Outcome> for RTableState<CellSelection> {
fn handle(&mut self, event: &crossterm::event::Event, _keymap: MouseOnly) -> Outcome {
self.widget.handle(event, MouseOnly)
}
}
impl<Selection> HandleEvent<crossterm::event::Event, DoubleClick, DoubleClickOutcome>
for RTableState<Selection>
where
rat_ftable::FTableState<Selection>:
HandleEvent<crossterm::event::Event, DoubleClick, DoubleClickOutcome>,
{
fn handle(
&mut self,
event: &crossterm::event::Event,
_keymap: DoubleClick,
) -> DoubleClickOutcome {
self.widget.handle(event, DoubleClick)
}
}
impl<Selection> HandleEvent<crossterm::event::Event, EditKeys, EditOutcome>
for RTableState<Selection>
where
rat_ftable::FTableState<Selection>: HandleEvent<crossterm::event::Event, MouseOnly, Outcome>,
rat_ftable::FTableState<Selection>: HandleEvent<crossterm::event::Event, FocusKeys, Outcome>,
rat_ftable::FTableState<Selection>: HandleEvent<crossterm::event::Event, EditKeys, EditOutcome>,
{
fn handle(&mut self, event: &crossterm::event::Event, _keymap: EditKeys) -> EditOutcome {
if self.is_focused() {
self.widget.handle(event, EditKeys)
} else {
self.widget.handle(event, MouseOnly).into()
}
}
}
impl<Selection> HasFocusFlag for RTableState<Selection> {
#[inline]
fn focus(&self) -> &FocusFlag {
&self.widget.focus
}
#[inline]
fn area(&self) -> Rect {
self.widget.area
}
}