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;
}