rat_ftable/lib.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347
#![doc = include_str!("../readme.md")]
mod cellselection;
pub mod edit;
mod noselection;
mod rowselection;
mod rowsetselection;
mod table;
pub mod textdata;
mod util;
use crate::textdata::Row;
use ratatui::buffer::Buffer;
use ratatui::layout::{Constraint, Rect};
use ratatui::style::Style;
/// Render-context for rendering a table-cell.
#[derive(Debug)]
pub struct TableContext {
/// Focus flag is set.
pub focus: bool,
/// Cell is selected.
pub selected_cell: bool,
/// Row of the cell is selected.
pub selected_row: bool,
/// Column of the cell is selected.
pub selected_column: bool,
/// Base style
pub style: Style,
/// Row style if any.
pub row_style: Option<Style>,
/// Selection style if any.
pub select_style: Option<Style>,
/// Spacing after the cell. It's guaranteed that this
/// is writeable in the buffer given to render_cell.
pub space_area: Rect,
/// Total area for the current row.
pub row_area: Rect,
/// Construct with `..Default::default()`
pub non_exhaustive: NonExhaustive,
}
///
/// Trait for accessing the table-data by the Table.
///
/// This trait is suitable if the underlying data is some sort
/// of vec/slice.
pub trait TableData<'a> {
/// Size of the data.
fn rows(&self) -> usize;
/// Header can be obtained from here.
/// Alternative to setting on Table.
fn header(&self) -> Option<Row<'a>> {
None
}
/// Footer can be obtained from here.
/// Alternative to setting on Table.
fn footer(&self) -> Option<Row<'a>> {
None
}
/// Row height.
#[allow(unused_variables)]
fn row_height(&self, row: usize) -> u16 {
1
}
/// Row style.
#[allow(unused_variables)]
fn row_style(&self, row: usize) -> Option<Style> {
None
}
/// Column constraints.
fn widths(&self) -> Vec<Constraint> {
Vec::default()
}
/// Render the cell given by column/row.
/// * ctx - a lot of context data.
fn render_cell(
&self,
ctx: &TableContext,
column: usize,
row: usize,
area: Rect,
buf: &mut Buffer,
);
}
impl<'a> TableData<'a> for Box<dyn TableData<'a> + 'a> {
fn rows(&self) -> usize {
(**self).rows()
}
fn header(&self) -> Option<Row<'a>> {
(**self).header()
}
fn footer(&self) -> Option<Row<'a>> {
(**self).footer()
}
fn row_height(&self, row: usize) -> u16 {
(**self).row_height(row)
}
fn row_style(&self, row: usize) -> Option<Style> {
(**self).row_style(row)
}
fn widths(&self) -> Vec<Constraint> {
(**self).widths()
}
fn render_cell(
&self,
ctx: &TableContext,
column: usize,
row: usize,
area: Rect,
buf: &mut Buffer,
) {
(**self).render_cell(ctx, column, row, area, buf)
}
}
/// Trait for accessing the table-data by the Table.
///
/// This trait is suitable if the underlying data is an iterator.
pub trait TableDataIter<'a> {
/// StatefulWidgetRef needs a clone of the iterator for every render.
/// For StatefulWidget this is not needed at all. So this defaults to
/// None and warns at runtime.
fn cloned(&self) -> Option<Box<dyn TableDataIter<'a> + 'a>> {
None
}
/// Returns the number of rows, if known.
///
/// If they are not known, all items will be iterated to
/// calculate things as the length of the table. Which will
/// be slower if you have many items.
///
/// See [Table::no_row_count]
fn rows(&self) -> Option<usize>;
/// Header can be obtained from here.
/// Alternative to setting on Table.
fn header(&self) -> Option<Row<'a>> {
None
}
/// Footer can be obtained from here.
/// Alternative to setting on Table.
fn footer(&self) -> Option<Row<'a>> {
None
}
/// Skips to the nth item, returns true if such an item exists.
/// nth(0) == next()
fn nth(&mut self, n: usize) -> bool;
/// Row height for the current item.
fn row_height(&self) -> u16 {
1
}
/// Row style for the current line.
fn row_style(&self) -> Option<Style> {
None
}
/// Column constraints.
fn widths(&self) -> Vec<Constraint> {
Vec::default()
}
/// Render the cell for the current line.
/// * ctx - a lot of context data.
fn render_cell(&self, ctx: &TableContext, column: usize, area: Rect, buf: &mut Buffer);
}
/// Trait for the different selection models used by Table.
pub trait TableSelection {
/// Row is selected. This can be separate from `is_selected_cell`.
fn is_selected_row(&self, row: usize) -> bool;
/// Column is selected. This can be separate from `is_selected_cell`.
fn is_selected_column(&self, column: usize) -> bool;
/// Specific cell is selected.
fn is_selected_cell(&self, column: usize, row: usize) -> bool;
/// Selection lead, or the sole selected index.
fn lead_selection(&self) -> Option<(usize, usize)>;
}
use crate::_private::NonExhaustive;
pub use table::{handle_doubleclick_events, Table, TableState, TableStyle};
/// Different selection models for Table.
pub mod selection {
pub use crate::cellselection::CellSelection;
pub mod cellselection {
pub use crate::cellselection::{handle_events, handle_mouse_events};
}
pub use crate::noselection::NoSelection;
pub mod noselection {
pub use crate::noselection::{handle_events, handle_mouse_events};
}
pub use crate::rowselection::RowSelection;
pub mod rowselection {
pub use crate::rowselection::{handle_events, handle_mouse_events};
}
pub use crate::rowsetselection::RowSetSelection;
pub mod rowsetselection {
pub use crate::rowsetselection::{handle_events, handle_mouse_events};
}
}
/// Eventhandling.
pub mod event {
pub use rat_event::*;
/// Result type for double-click event-handling.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum DoubleClickOutcome {
/// The given event has not been used at all.
Continue,
/// The event has been recognized, but the result was nil.
/// Further processing for this event may stop.
Unchanged,
/// The event has been recognized and there is some change
/// due to it.
/// Further processing for this event may stop.
/// Rendering the ui is advised.
Changed,
/// Double click occurred. Contains (column, row)
ClickClick(usize, usize),
}
impl From<DoubleClickOutcome> for Outcome {
fn from(value: DoubleClickOutcome) -> Self {
match value {
DoubleClickOutcome::Continue => Outcome::Continue,
DoubleClickOutcome::Unchanged => Outcome::Unchanged,
DoubleClickOutcome::Changed => Outcome::Changed,
DoubleClickOutcome::ClickClick(_, _) => Outcome::Changed,
}
}
}
impl From<Outcome> for DoubleClickOutcome {
fn from(value: Outcome) -> Self {
match value {
Outcome::Continue => DoubleClickOutcome::Continue,
Outcome::Unchanged => DoubleClickOutcome::Unchanged,
Outcome::Changed => DoubleClickOutcome::Changed,
}
}
}
impl ConsumedEvent for DoubleClickOutcome {
fn is_consumed(&self) -> bool {
!matches!(self, DoubleClickOutcome::Continue)
}
}
/// Result type for the [edit](crate::edit) widgets.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum EditOutcome {
/// The given event has not been used at all.
Continue,
/// The event has been recognized, but the result was nil.
/// Further processing for this event may stop.
Unchanged,
/// The event has been recognized and there is some change
/// due to it.
/// Further processing for this event may stop.
/// Rendering the ui is advised.
Changed,
/// Cancel an ongoing edit.
Cancel,
/// Commit the current edit.
Commit,
/// Commit the edit, append a new line.
CommitAndAppend,
/// Commit the edit, edit next line.
CommitAndEdit,
/// Insert an item at the selection.
Insert,
/// Remove the item at the selection.
Remove,
/// Edit the item at the selection.
Edit,
/// Append an item after last row.
/// Might want to start the edit too.
Append,
}
impl From<Outcome> for EditOutcome {
fn from(value: Outcome) -> Self {
match value {
Outcome::Continue => EditOutcome::Continue,
Outcome::Unchanged => EditOutcome::Unchanged,
Outcome::Changed => EditOutcome::Changed,
}
}
}
impl From<EditOutcome> for Outcome {
fn from(value: EditOutcome) -> Self {
match value {
EditOutcome::Continue => Outcome::Continue,
EditOutcome::Unchanged => Outcome::Unchanged,
EditOutcome::Changed => Outcome::Changed,
EditOutcome::Insert => Outcome::Unchanged,
EditOutcome::Remove => Outcome::Unchanged,
EditOutcome::Edit => Outcome::Unchanged,
EditOutcome::Append => Outcome::Unchanged,
EditOutcome::Cancel => Outcome::Unchanged,
EditOutcome::Commit => Outcome::Unchanged,
EditOutcome::CommitAndAppend => Outcome::Unchanged,
EditOutcome::CommitAndEdit => Outcome::Unchanged,
}
}
}
impl ConsumedEvent for EditOutcome {
fn is_consumed(&self) -> bool {
!matches!(self, EditOutcome::Continue)
}
}
}
mod _private {
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct NonExhaustive;
}