use std::borrow::Cow;
use egui::{Key, KeyboardShortcut, Modifiers};
pub use egui_extras::Column as TableColumnConfig;
use tap::prelude::Pipe;
pub trait RowViewer<R>: 'static {
fn num_columns(&mut self) -> usize;
fn column_name(&mut self, column: usize) -> Cow<'static, str> {
Cow::Borrowed(
&" 0 1 2 3 4 5 6 7 8 91011121314151617181920212223242526272829303132"
[(column % 10 * 2).pipe(|x| x..x + 2)],
)
}
fn column_render_config(&mut self, column: usize) -> TableColumnConfig {
let _ = column;
TableColumnConfig::auto().resizable(true)
}
fn is_sortable_column(&mut self, column: usize) -> bool {
let _ = column;
false
}
fn create_cell_comparator(&mut self) -> impl Fn(&R, &R, usize) -> std::cmp::Ordering {
|_, _, _| std::cmp::Ordering::Equal
}
fn row_filter_hash(&mut self) -> &impl std::hash::Hash {
&()
}
fn create_row_filter(&mut self) -> impl Fn(&R) -> bool {
|_| true
}
fn show_cell_view(&mut self, ui: &mut egui::Ui, row: &R, column: usize);
fn on_cell_view_response(
&mut self,
row: &R,
column: usize,
resp: &egui::Response,
) -> Option<Box<R>> {
let _ = (row, column, resp);
None
}
fn show_cell_editor(
&mut self,
ui: &mut egui::Ui,
row: &mut R,
column: usize,
) -> Option<egui::Response>;
fn set_cell_value(&mut self, src: &R, dst: &mut R, column: usize);
fn confirm_cell_write_by_ui(
&mut self,
current: &R,
next: &R,
column: usize,
context: CellWriteContext,
) -> bool {
let _ = (current, next, column, context);
true
}
fn confirm_row_deletion_by_ui(&mut self, row: &R) -> bool {
let _ = row;
true
}
fn new_empty_row(&mut self) -> R;
fn new_empty_row_for(&mut self, context: EmptyRowCreateContext) -> R {
let _ = context;
self.new_empty_row()
}
fn clone_row(&mut self, row: &R) -> R {
let mut dst = self.new_empty_row();
for i in 0..self.num_columns() {
self.set_cell_value(row, &mut dst, i);
}
dst
}
fn clone_row_for_insertion(&mut self, row: &R) -> R {
self.clone_row(row)
}
fn on_highlight_cell(&mut self, row: &R, column: usize) {
let _ = (row, column);
}
fn hotkeys(&mut self, context: &UiActionContext) -> Vec<(egui::KeyboardShortcut, UiAction)> {
self::default_hotkeys(context)
}
fn trivial_config(&mut self) -> TrivialConfig {
Default::default()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum CellWriteContext {
Paste,
Clear,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum EmptyRowCreateContext {
Default,
DeletionDefault,
InsertNewLine,
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct UiActionContext {
pub cursor: UiCursorState,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UiCursorState {
Idle,
Editing,
SelectOne,
SelectMany,
}
impl UiCursorState {
pub fn is_idle(&self) -> bool {
matches!(self, Self::Idle)
}
pub fn is_editing(&self) -> bool {
matches!(self, Self::Editing)
}
pub fn is_selecting(&self) -> bool {
matches!(self, Self::SelectOne | Self::SelectMany)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum UiAction {
SelectionStartEditing,
CancelEdition,
CommitEdition,
CommitEditionAndMove(MoveDirection),
Undo,
Redo,
MoveSelection(MoveDirection),
CopySelection,
CutSelection,
PasteInPlace,
PasteInsert,
DuplicateRow,
DeleteSelection,
DeleteRow,
NavPageDown,
NavPageUp,
NavTop,
NavBottom,
SelectionDuplicateValues,
SelectAll,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MoveDirection {
Up,
Down,
Left,
Right,
}
pub fn default_hotkeys(context: &UiActionContext) -> Vec<(KeyboardShortcut, UiAction)> {
let c = context.cursor;
fn shortcut(actions: &[(Modifiers, Key, UiAction)]) -> Vec<(egui::KeyboardShortcut, UiAction)> {
actions
.iter()
.map(|(m, k, a)| (egui::KeyboardShortcut::new(*m, *k), *a))
.collect()
}
let none = Modifiers::NONE;
let ctrl = Modifiers::CTRL;
let alt = Modifiers::ALT;
let shift = Modifiers::SHIFT;
use UiAction::CommitEditionAndMove;
type MD = MoveDirection;
if c.is_editing() {
shortcut(&[
(none, Key::Escape, UiAction::CommitEdition),
(ctrl, Key::Escape, UiAction::CancelEdition),
(shift, Key::Enter, CommitEditionAndMove(MD::Up)),
(ctrl, Key::Enter, CommitEditionAndMove(MD::Down)),
(shift, Key::Tab, CommitEditionAndMove(MD::Left)),
(none, Key::Tab, CommitEditionAndMove(MD::Right)),
])
} else {
shortcut(&[
(ctrl, Key::X, UiAction::CutSelection),
(ctrl, Key::C, UiAction::CopySelection),
(ctrl | shift, Key::V, UiAction::PasteInsert),
(ctrl, Key::V, UiAction::PasteInPlace),
(ctrl, Key::Y, UiAction::Redo),
(ctrl, Key::Z, UiAction::Undo),
(none, Key::Enter, UiAction::SelectionStartEditing),
(none, Key::ArrowUp, UiAction::MoveSelection(MD::Up)),
(none, Key::ArrowDown, UiAction::MoveSelection(MD::Down)),
(none, Key::ArrowLeft, UiAction::MoveSelection(MD::Left)),
(none, Key::ArrowRight, UiAction::MoveSelection(MD::Right)),
(shift, Key::V, UiAction::PasteInsert),
(alt, Key::V, UiAction::PasteInsert),
(ctrl | shift, Key::D, UiAction::DuplicateRow),
(ctrl, Key::D, UiAction::SelectionDuplicateValues),
(ctrl, Key::A, UiAction::SelectAll),
(ctrl, Key::Delete, UiAction::DeleteRow),
(none, Key::Delete, UiAction::DeleteSelection),
(none, Key::Backspace, UiAction::DeleteSelection),
(none, Key::PageUp, UiAction::NavPageUp),
(none, Key::PageDown, UiAction::NavPageDown),
(none, Key::Home, UiAction::NavTop),
(none, Key::End, UiAction::NavBottom),
])
}
}
#[derive(Clone, Debug)]
pub struct TrivialConfig {
pub table_row_height: Option<f32>,
pub max_undo_history: usize,
}
impl Default for TrivialConfig {
fn default() -> Self {
Self {
table_row_height: None,
max_undo_history: 100,
}
}
}