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