1use std::borrow::Cow;
2
3use egui::{Key, KeyboardShortcut, Modifiers};
4pub use egui_extras::Column as TableColumnConfig;
5use tap::prelude::Pipe;
6
7#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
8pub enum DecodeErrorBehavior {
9 SkipCell,
11
12 SkipRow,
14
15 #[default]
17 Abort,
18}
19
20pub trait RowCodec<R> {
24 type DeserializeError;
25
26 fn create_empty_decoded_row(&mut self) -> R;
28
29 fn encode_column(&mut self, src_row: &R, column: usize, dst: &mut String);
33
34 fn decode_column(
36 &mut self,
37 src_data: &str,
38 column: usize,
39 dst_row: &mut R,
40 ) -> Result<(), DecodeErrorBehavior>;
41}
42
43impl<R> RowCodec<R> for () {
45 type DeserializeError = ();
46
47 fn create_empty_decoded_row(&mut self) -> R {
48 unimplemented!()
49 }
50
51 fn encode_column(&mut self, src_row: &R, column: usize, dst: &mut String) {
52 let _ = (src_row, column, dst);
53 unimplemented!()
54 }
55
56 fn decode_column(
57 &mut self,
58 src_data: &str,
59 column: usize,
60 dst_row: &mut R,
61 ) -> Result<(), DecodeErrorBehavior> {
62 let _ = (src_data, column, dst_row);
63 unimplemented!()
64 }
65}
66
67pub trait RowViewer<R>: 'static {
70 fn num_columns(&mut self) -> usize;
73
74 fn column_name(&mut self, column: usize) -> Cow<'static, str> {
76 Cow::Borrowed(
77 &" 0 1 2 3 4 5 6 7 8 91011121314151617181920212223242526272829303132"
78 [(column % 10 * 2).pipe(|x| x..x + 2)],
79 )
80 }
81
82 fn try_create_codec(&mut self, is_encoding: bool) -> Option<impl RowCodec<R>> {
92 let _ = is_encoding;
93 None::<()>
94 }
95
96 fn column_render_config(
98 &mut self,
99 column: usize,
100 is_last_visible_column: bool,
101 ) -> TableColumnConfig {
102 let _ = column;
103 if is_last_visible_column {
104 TableColumnConfig::remainder().at_least(24.0)
105 } else {
106 TableColumnConfig::auto().resizable(true)
107 }
108 }
109
110 fn is_sortable_column(&mut self, column: usize) -> bool {
112 let _ = column;
113 false
114 }
115
116 fn is_editable_cell(&mut self, column: usize, row: usize, row_value: &R) -> bool {
122 let _ = column;
123 let _ = row;
124 let _ = row_value;
125 true
126 }
127
128 fn allow_row_insertions(&mut self) -> bool {
130 true
131 }
132
133 fn allow_row_deletions(&mut self) -> bool {
135 true
136 }
137
138 fn compare_cell(&self, row_a: &R, row_b: &R, column: usize) -> std::cmp::Ordering {
140 let _ = (row_a, row_b, column);
141 std::cmp::Ordering::Equal
142 }
143
144 fn row_filter_hash(&mut self) -> &impl std::hash::Hash {
146 &()
147 }
148
149 fn filter_row(&mut self, row: &R) -> bool {
151 let _ = row;
152 true
153 }
154
155 fn show_cell_view(&mut self, ui: &mut egui::Ui, row: &R, column: usize);
162
163 fn on_cell_view_response(
166 &mut self,
167 row: &R,
168 column: usize,
169 resp: &egui::Response,
170 ) -> Option<Box<R>> {
171 let _ = (row, column, resp);
172 None
173 }
174
175 fn show_cell_editor(
177 &mut self,
178 ui: &mut egui::Ui,
179 row: &mut R,
180 column: usize,
181 ) -> Option<egui::Response>;
182
183 fn set_cell_value(&mut self, src: &R, dst: &mut R, column: usize);
185
186 fn confirm_cell_write_by_ui(
189 &mut self,
190 current: &R,
191 next: &R,
192 column: usize,
193 context: CellWriteContext,
194 ) -> bool {
195 let _ = (current, next, column, context);
196 true
197 }
198
199 fn confirm_row_deletion_by_ui(&mut self, row: &R) -> bool {
202 let _ = row;
203 true
204 }
205
206 fn new_empty_row(&mut self) -> R;
208
209 fn new_empty_row_for(&mut self, context: EmptyRowCreateContext) -> R {
211 let _ = context;
212 self.new_empty_row()
213 }
214
215 fn clone_row(&mut self, row: &R) -> R {
219 let mut dst = self.new_empty_row();
220 for i in 0..self.num_columns() {
221 self.set_cell_value(row, &mut dst, i);
222 }
223 dst
224 }
225
226 fn clone_row_for_insertion(&mut self, row: &R) -> R {
228 self.clone_row(row)
229 }
230
231 fn clone_row_as_copied_base(&mut self, row: &R) -> R {
234 self.clone_row(row)
235 }
236
237 fn on_highlight_cell(&mut self, row: &R, column: usize) {
239 let _ = (row, column);
240 }
241
242 fn on_highlight_change(&mut self, highlighted: &[&R], unhighlighted: &[&R]) {
244 let (_, _) = (highlighted, unhighlighted);
245 }
246
247 fn on_row_updated(&mut self, row_index: usize, new_row: &R, old_row: &R) {
249 let (_, _, _) = (row_index, new_row, old_row);
250 }
251
252 fn on_row_inserted(&mut self, row_index: usize, row: &R) {
254 let (_, _) = (row_index, row);
255 }
256
257 fn on_row_removed(&mut self, row_index: usize, row: &R) {
259 let (_, _) = (row_index, row);
260 }
261
262 fn hotkeys(&mut self, context: &UiActionContext) -> Vec<(egui::KeyboardShortcut, UiAction)> {
264 self::default_hotkeys(context)
265 }
266
267 #[cfg(feature = "persistency")]
270 fn persist_ui_state(&self) -> bool {
271 false
272 }
273}
274
275#[derive(Debug, Clone, Copy, PartialEq, Eq)]
278#[non_exhaustive]
279pub enum CellWriteContext {
280 Paste,
282
283 Clear,
285}
286
287#[derive(Debug, Clone, Copy, PartialEq, Eq)]
288#[non_exhaustive]
289pub enum EmptyRowCreateContext {
290 Default,
292
293 DeletionDefault,
295
296 InsertNewLine,
298}
299
300#[derive(Debug, Clone)]
304#[non_exhaustive]
305pub struct UiActionContext {
306 pub cursor: UiCursorState,
307}
308
309#[derive(Debug, Clone, Copy, PartialEq, Eq)]
310pub enum UiCursorState {
311 Idle,
312 Editing,
313 SelectOne,
314 SelectMany,
315}
316
317impl UiCursorState {
318 pub fn is_idle(&self) -> bool {
319 matches!(self, Self::Idle)
320 }
321
322 pub fn is_editing(&self) -> bool {
323 matches!(self, Self::Editing)
324 }
325
326 pub fn is_selecting(&self) -> bool {
327 matches!(self, Self::SelectOne | Self::SelectMany)
328 }
329}
330
331#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
335#[non_exhaustive]
336pub enum UiAction {
337 SelectionStartEditing,
338
339 CancelEdition,
340 CommitEdition,
341
342 CommitEditionAndMove(MoveDirection),
343
344 Undo,
345 Redo,
346
347 MoveSelection(MoveDirection),
348 CopySelection,
349 CutSelection,
350
351 PasteInPlace,
352 PasteInsert,
353
354 DuplicateRow,
355 DeleteSelection,
356 DeleteRow,
357
358 NavPageDown,
359 NavPageUp,
360 NavTop,
361 NavBottom,
362
363 SelectionDuplicateValues,
364 SelectAll,
365}
366
367#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
368pub enum MoveDirection {
369 Up,
370 Down,
371 Left,
372 Right,
373}
374
375pub fn default_hotkeys(context: &UiActionContext) -> Vec<(KeyboardShortcut, UiAction)> {
376 let c = context.cursor;
377
378 fn shortcut(actions: &[(Modifiers, Key, UiAction)]) -> Vec<(egui::KeyboardShortcut, UiAction)> {
379 actions
380 .iter()
381 .map(|(m, k, a)| (egui::KeyboardShortcut::new(*m, *k), *a))
382 .collect()
383 }
384
385 let none = Modifiers::NONE;
386 let ctrl = Modifiers::CTRL;
387 let alt = Modifiers::ALT;
388 let shift = Modifiers::SHIFT;
389
390 use UiAction::CommitEditionAndMove;
391 type MD = MoveDirection;
392
393 if c.is_editing() {
394 shortcut(&[
395 (none, Key::Escape, UiAction::CommitEdition),
396 (ctrl, Key::Escape, UiAction::CancelEdition),
397 (shift, Key::Enter, CommitEditionAndMove(MD::Up)),
398 (ctrl, Key::Enter, CommitEditionAndMove(MD::Down)),
399 (shift, Key::Tab, CommitEditionAndMove(MD::Left)),
400 (none, Key::Tab, CommitEditionAndMove(MD::Right)),
401 ])
402 } else {
403 shortcut(&[
404 (ctrl, Key::X, UiAction::CutSelection),
405 (ctrl, Key::C, UiAction::CopySelection),
406 (ctrl | shift, Key::V, UiAction::PasteInsert),
407 (ctrl, Key::V, UiAction::PasteInPlace),
408 (ctrl, Key::Y, UiAction::Redo),
409 (ctrl, Key::Z, UiAction::Undo),
410 (none, Key::Enter, UiAction::SelectionStartEditing),
411 (none, Key::ArrowUp, UiAction::MoveSelection(MD::Up)),
412 (none, Key::ArrowDown, UiAction::MoveSelection(MD::Down)),
413 (none, Key::ArrowLeft, UiAction::MoveSelection(MD::Left)),
414 (none, Key::ArrowRight, UiAction::MoveSelection(MD::Right)),
415 (shift, Key::V, UiAction::PasteInsert),
416 (alt, Key::V, UiAction::PasteInsert),
417 (ctrl | shift, Key::D, UiAction::DuplicateRow),
418 (ctrl, Key::D, UiAction::SelectionDuplicateValues),
419 (ctrl, Key::A, UiAction::SelectAll),
420 (ctrl, Key::Delete, UiAction::DeleteRow),
421 (none, Key::Delete, UiAction::DeleteSelection),
422 (none, Key::Backspace, UiAction::DeleteSelection),
423 (none, Key::PageUp, UiAction::NavPageUp),
424 (none, Key::PageDown, UiAction::NavPageDown),
425 (none, Key::Home, UiAction::NavTop),
426 (none, Key::End, UiAction::NavBottom),
427 ])
428 }
429}