1use crate::edit::{Mode, TableEditor, TableEditorState};
12use crate::rowselection::RowSelection;
13use crate::textdata::Row;
14use crate::{Table, TableContext, TableData, TableState};
15use log::warn;
16use rat_cursor::HasScreenCursor;
17use rat_event::util::MouseFlags;
18use rat_event::{ct_event, try_flow, HandleEvent, Outcome, Regular};
19use rat_focus::{FocusBuilder, FocusFlag, HasFocus, Navigation};
20use rat_reloc::RelocatableState;
21use ratatui::buffer::Buffer;
22use ratatui::layout::{Constraint, Rect};
23use ratatui::prelude::{StatefulWidget, Style};
24use std::cell::RefCell;
25use std::fmt::{Debug, Formatter};
26use std::rc::Rc;
27
28pub trait TableDataVec<D>: TableData<'static> {
36 fn set_data(&mut self, data: Rc<RefCell<Vec<D>>>);
38}
39
40pub struct EditableTableVec<'a, E>
47where
48 E: TableEditor + 'a,
49{
50 table: Table<'a, RowSelection>,
51 table_data: Box<dyn TableDataVec<<<E as TableEditor>::State as TableEditorState>::Value>>,
52 editor: E,
53}
54
55pub struct EditableTableVecState<S>
61where
62 S: TableEditorState,
63{
64 mode: Mode,
66
67 pub table: TableState<RowSelection>,
69 editor: S,
71 editor_data: Rc<RefCell<Vec<S::Value>>>,
73
74 mouse: MouseFlags,
75}
76
77impl<'a, E> EditableTableVec<'a, E>
78where
79 E: TableEditor + 'a,
80{
81 pub fn new(
82 table_data: impl TableDataVec<<<E as TableEditor>::State as TableEditorState>::Value> + 'static,
83 table: Table<'a, RowSelection>,
84 editor: E,
85 ) -> Self {
86 Self {
87 table,
88 table_data: Box::new(table_data),
89 editor,
90 }
91 }
92}
93
94impl<'a, D> TableData<'a> for Box<dyn TableDataVec<D> + 'a> {
95 fn rows(&self) -> usize {
96 (**self).rows()
97 }
98
99 fn header(&self) -> Option<Row<'a>> {
100 (**self).header()
101 }
102
103 fn footer(&self) -> Option<Row<'a>> {
104 (**self).footer()
105 }
106
107 fn row_height(&self, row: usize) -> u16 {
108 (**self).row_height(row)
109 }
110
111 fn row_style(&self, row: usize) -> Option<Style> {
112 (**self).row_style(row)
113 }
114
115 fn widths(&self) -> Vec<Constraint> {
116 (**self).widths()
117 }
118
119 fn render_cell(
120 &self,
121 ctx: &TableContext,
122 column: usize,
123 row: usize,
124 area: Rect,
125 buf: &mut Buffer,
126 ) {
127 (**self).render_cell(ctx, column, row, area, buf)
128 }
129}
130
131impl<'a, E> Debug for EditableTableVec<'a, E>
132where
133 E: Debug,
134 E: TableEditor + 'a,
135{
136 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
137 f.debug_struct("EditVec")
138 .field("table", &self.table)
139 .field("table_data", &"..dyn..")
140 .field("editor", &self.editor)
141 .finish()
142 }
143}
144
145impl<'a, E> StatefulWidget for EditableTableVec<'a, E>
146where
147 E: TableEditor + 'a,
148{
149 type State = EditableTableVecState<E::State>;
150
151 #[allow(clippy::collapsible_else_if)]
152 fn render(mut self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
153 self.table_data.set_data(state.editor_data.clone());
154 self.table
155 .data(self.table_data)
156 .render(area, buf, &mut state.table);
157
158 if state.mode == Mode::Insert || state.mode == Mode::Edit {
159 if let Some(row) = state.table.selected() {
160 if let Some((row_area, cell_areas)) = state.table.row_cells(row) {
162 self.editor
163 .render(row_area, &cell_areas, buf, &mut state.editor);
164 }
165 } else {
166 if cfg!(debug_assertions) {
167 warn!("no row selection, not rendering editor");
168 }
169 }
170 }
171 }
172}
173
174impl<S> Default for EditableTableVecState<S>
175where
176 S: TableEditorState + Default,
177{
178 fn default() -> Self {
179 Self {
180 mode: Mode::View,
181 table: Default::default(),
182 editor: S::default(),
183 editor_data: Rc::new(RefCell::new(Vec::default())),
184 mouse: Default::default(),
185 }
186 }
187}
188
189impl<S> Debug for EditableTableVecState<S>
190where
191 S: TableEditorState + Debug,
192 S::Value: Debug,
193{
194 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
195 f.debug_struct("EditVecState")
196 .field("mode", &self.mode)
197 .field("table", &self.table)
198 .field("editor", &self.editor)
199 .field("editor_data", &self.editor_data)
200 .field("mouse", &self.mouse)
201 .finish()
202 }
203}
204
205impl<S> HasFocus for EditableTableVecState<S>
206where
207 S: TableEditorState,
208{
209 fn build(&self, builder: &mut FocusBuilder) {
210 builder.leaf_widget(self);
211 }
212
213 fn focus(&self) -> FocusFlag {
214 self.table.focus()
215 }
216
217 fn area(&self) -> Rect {
218 self.table.area()
219 }
220
221 fn navigable(&self) -> Navigation {
222 match self.mode {
223 Mode::View => self.table.navigable(),
224 Mode::Edit | Mode::Insert => Navigation::Lock,
225 }
226 }
227
228 fn is_focused(&self) -> bool {
229 self.table.is_focused()
230 }
231
232 fn lost_focus(&self) -> bool {
233 self.table.lost_focus()
234 }
235
236 fn gained_focus(&self) -> bool {
237 self.table.gained_focus()
238 }
239}
240
241impl<S> HasScreenCursor for EditableTableVecState<S>
242where
243 S: TableEditorState + HasScreenCursor,
244{
245 fn screen_cursor(&self) -> Option<(u16, u16)> {
246 match self.mode {
247 Mode::View => None,
248 Mode::Edit | Mode::Insert => self.editor.screen_cursor(),
249 }
250 }
251}
252
253impl<S> RelocatableState for EditableTableVecState<S>
254where
255 S: TableEditorState + RelocatableState,
256{
257 fn relocate(&mut self, shift: (i16, i16), clip: Rect) {
258 match self.mode {
259 Mode::View => {}
260 Mode::Edit | Mode::Insert => {
261 self.editor.relocate(shift, clip);
262 }
263 }
264 }
265}
266
267impl<S> EditableTableVecState<S>
268where
269 S: TableEditorState,
270{
271 pub fn new(editor: S) -> Self {
272 Self {
273 mode: Mode::View,
274 table: TableState::new(),
275 editor,
276 editor_data: Rc::new(RefCell::new(vec![])),
277 mouse: Default::default(),
278 }
279 }
280
281 pub fn named(name: &str, editor: S) -> Self {
282 Self {
283 mode: Mode::View,
284 table: TableState::named(name),
285 editor,
286 editor_data: Rc::new(RefCell::new(vec![])),
287 mouse: Default::default(),
288 }
289 }
290}
291
292impl<S> EditableTableVecState<S>
293where
294 S: TableEditorState,
295{
296 pub fn set_value(&mut self, data: Vec<S::Value>) {
298 self.editor_data = Rc::new(RefCell::new(data));
299 }
300
301 pub fn value(&self) -> Vec<S::Value> {
303 self.editor_data.borrow().clone()
304 }
305
306 pub fn is_editing(&self) -> bool {
308 self.mode == Mode::Edit || self.mode == Mode::Insert
309 }
310
311 pub fn is_insert(&self) -> bool {
313 self.mode == Mode::Insert
314 }
315
316 pub fn remove(&mut self, row: usize) {
318 if self.mode != Mode::View {
319 return;
320 }
321 if row < self.editor_data.borrow().len() {
322 self.editor_data.borrow_mut().remove(row);
323 self.table.items_removed(row, 1);
324 if !self.table.scroll_to_row(row) {
325 self.table.scroll_to_row(row.saturating_sub(1));
326 }
327 }
328 }
329
330 pub fn edit_new(&mut self, row: usize, ctx: S::Context<'_>) -> Result<(), S::Err> {
332 if self.mode != Mode::View {
333 return Ok(());
334 }
335 let value = self.editor.create_value(ctx.clone())?;
336 self.editor.set_value(value.clone(), ctx.clone())?;
337 self.editor_data.borrow_mut().insert(row, value);
338 self._start(row, Mode::Insert);
339 Ok(())
340 }
341
342 pub fn edit(&mut self, row: usize, ctx: S::Context<'_>) -> Result<(), S::Err> {
344 if self.mode != Mode::View {
345 return Ok(());
346 }
347 {
348 let value = &self.editor_data.borrow()[row];
349 self.editor.set_value(value.clone(), ctx.clone())?;
350 }
351 self._start(row, Mode::Edit);
352 Ok(())
353 }
354
355 fn _start(&mut self, pos: usize, mode: Mode) {
356 if self.table.is_focused() {
357 FocusBuilder::build_for(&self.editor).first();
359 }
360
361 self.mode = mode;
362 if self.mode == Mode::Insert {
363 self.table.items_added(pos, 1);
364 }
365 self.table.move_to(pos);
366 self.table.scroll_to_col(0);
367 }
368
369 pub fn cancel(&mut self) {
373 if self.mode == Mode::View {
374 return;
375 }
376 let Some(row) = self.table.selected_checked() else {
377 return;
378 };
379 if self.mode == Mode::Insert {
380 self.editor_data.borrow_mut().remove(row);
381 self.table.items_removed(row, 1);
382 }
383 self._stop();
384 }
385
386 pub fn commit(&mut self, ctx: S::Context<'_>) -> Result<(), S::Err> {
388 if self.mode == Mode::View {
389 return Ok(());
390 }
391 let Some(row) = self.table.selected_checked() else {
392 return Ok(());
393 };
394 {
395 let value = self.editor.value(ctx.clone())?;
396 if let Some(value) = value {
397 self.editor_data.borrow_mut()[row] = value;
398 } else {
399 self.editor_data.borrow_mut().remove(row);
400 self.table.items_removed(row, 1);
401 }
402 }
403 self._stop();
404 Ok(())
405 }
406
407 pub fn commit_and_append(&mut self, ctx: S::Context<'_>) -> Result<(), S::Err> {
408 self.commit(ctx.clone())?;
409 if let Some(row) = self.table.selected_checked() {
410 self.edit_new(row + 1, ctx.clone())?;
411 }
412 Ok(())
413 }
414
415 pub fn commit_and_edit(&mut self, ctx: S::Context<'_>) -> Result<(), S::Err> {
416 let Some(row) = self.table.selected_checked() else {
417 return Ok(());
418 };
419
420 self.commit(ctx.clone())?;
421 self.table.select(Some(row + 1));
422 self.edit(row + 1, ctx.clone())?;
423 Ok(())
424 }
425
426 fn _stop(&mut self) {
427 self.mode = Mode::View;
428 self.table.scroll_to_col(0);
429 }
430}
431
432impl<'a, S> HandleEvent<crossterm::event::Event, S::Context<'a>, Result<Outcome, S::Err>>
433 for EditableTableVecState<S>
434where
435 S: HandleEvent<crossterm::event::Event, S::Context<'a>, Result<Outcome, S::Err>>,
436 S: TableEditorState,
437{
438 fn handle(
439 &mut self,
440 event: &crossterm::event::Event,
441 ctx: S::Context<'a>,
442 ) -> Result<Outcome, S::Err> {
443 if self.mode == Mode::Edit || self.mode == Mode::Insert {
444 try_flow!(match self.editor.handle(event, ctx.clone())? {
445 Outcome::Continue => Outcome::Continue,
446 Outcome::Unchanged => Outcome::Unchanged,
447 r => {
448 if let Some(col) = self.editor.focused_col() {
449 self.table.scroll_to_col(col);
450 }
451 r
452 }
453 });
454
455 try_flow!(match event {
456 ct_event!(keycode press Esc) => {
457 self.cancel();
458 Outcome::Changed
459 }
460 ct_event!(keycode press Enter) => {
461 if self.table.selected_checked() < Some(self.table.rows().saturating_sub(1)) {
462 self.commit_and_edit(ctx.clone())?;
463 Outcome::Changed
464 } else {
465 self.commit_and_append(ctx.clone())?;
466 Outcome::Changed
467 }
468 }
469 ct_event!(keycode press Up) => {
470 self.commit(ctx.clone())?;
471 Outcome::Changed
472 }
473 ct_event!(keycode press Down) => {
474 self.commit(ctx.clone())?;
475 Outcome::Changed
476 }
477 _ => Outcome::Continue,
478 });
479
480 Ok(Outcome::Continue)
481 } else {
482 try_flow!(match event {
483 ct_event!(mouse any for m) if self.mouse.doubleclick(self.table.table_area, m) => {
484 if let Some((_col, row)) = self.table.cell_at_clicked((m.column, m.row)) {
485 self.edit(row, ctx.clone())?;
486 Outcome::Changed
487 } else {
488 Outcome::Continue
489 }
490 }
491 _ => Outcome::Continue,
492 });
493
494 try_flow!(match event {
495 ct_event!(keycode press Insert) => {
496 if let Some(row) = self.table.selected_checked() {
497 self.edit_new(row, ctx.clone())?;
498 }
499 Outcome::Changed
500 }
501 ct_event!(keycode press Delete) => {
502 if let Some(row) = self.table.selected_checked() {
503 self.remove(row);
504 }
505 Outcome::Changed
506 }
507 ct_event!(keycode press Enter) | ct_event!(keycode press F(2)) => {
508 if let Some(row) = self.table.selected_checked() {
509 self.edit(row, ctx.clone())?;
510 Outcome::Changed
511 } else if self.table.rows() == 0 {
512 self.edit_new(0, ctx.clone())?;
513 Outcome::Changed
514 } else {
515 Outcome::Continue
516 }
517 }
518 ct_event!(keycode press Down) => {
519 if let Some(row) = self.table.selected_checked() {
520 if row == self.table.rows().saturating_sub(1) {
521 self.edit_new(row + 1, ctx.clone())?;
522 Outcome::Changed
523 } else {
524 Outcome::Continue
525 }
526 } else if self.table.rows() == 0 {
527 self.edit_new(0, ctx.clone())?;
528 Outcome::Changed
529 } else {
530 Outcome::Continue
531 }
532 }
533 _ => {
534 Outcome::Continue
535 }
536 });
537
538 try_flow!(self.table.handle(event, Regular));
539
540 Ok(Outcome::Continue)
541 }
542 }
543}