From 57f789290dc50d13fe80442da70de9bdb2d946b8 Mon Sep 17 00:00:00 2001 From: filipriec Date: Thu, 17 Apr 2025 11:53:31 +0200 Subject: [PATCH] needs improvements but at least it looks like it exists --- client/src/components/admin/add_table.rs | 80 +++--- .../modes/navigation/add_table_nav.rs | 259 ++++++++++++------ client/src/modes/handlers/event.rs | 10 +- client/src/state/pages/add_table.rs | 112 +++++++- client/src/ui/handlers/render.rs | 2 + 5 files changed, 323 insertions(+), 140 deletions(-) diff --git a/client/src/components/admin/add_table.rs b/client/src/components/admin/add_table.rs index f0d7930..33d1d2a 100644 --- a/client/src/components/admin/add_table.rs +++ b/client/src/components/admin/add_table.rs @@ -8,6 +8,8 @@ use ratatui::{ Frame, }; use crate::state::app::state::AppState; +use crate::state::app::highlight::HighlightState; +use crate::state::pages::canvas_state::CanvasState; use crate::components::handlers::canvas::render_canvas; use crate::state::pages::add_table::AddTableState; @@ -16,15 +18,17 @@ pub fn render_add_table( f: &mut Frame, area: Rect, theme: &Theme, - app_state: &AppState, + _app_state: &AppState, add_table_state: &mut AddTableState, + is_edit_mode: bool, + highlight_state: &HighlightState, ) { // Main block for the whole page let main_block = Block::default() .title(" Add New Table ") .borders(Borders::ALL) .border_type(BorderType::Rounded) - .border_style(Style::default().fg(theme.border)) // Use theme border color + .border_style(Style::default().fg(theme.border)) .style(Style::default().bg(theme.bg)); let inner_area = main_block.inner(area); f.render_widget(main_block, area); @@ -69,7 +73,6 @@ pub fn render_add_table( // Columns section let columns_text = Paragraph::new(vec![ Line::from(Span::styled("Name Type", theme.accent)), // Header - // Add column lines here later from state ]) .block(Block::default().title(Span::styled(" Columns ", theme.fg))); f.render_widget(columns_text, left_vertical_chunks[1]); @@ -77,7 +80,6 @@ pub fn render_add_table( // Indexes section let indexes_text = Paragraph::new(vec![ Line::from(Span::styled("Column name", theme.accent)), // Header - // Add index lines here later from state ]) .block( Block::default() @@ -90,7 +92,6 @@ pub fn render_add_table( // Links section let links_text = Paragraph::new(vec![ Line::from(Span::styled("Linked table Required", theme.accent)), // Header - // Add link lines here later from state ]) .block( Block::default() @@ -104,58 +105,45 @@ pub fn render_add_table( let right_vertical_chunks = Layout::default() .direction(Direction::Vertical) .constraints([ - Constraint::Length(7), // Input fields + Add button area + Constraint::Length(5), // Area for render_canvas (3 fields + 2 border) + Constraint::Length(3), // Add Button Area Constraint::Min(1), // Spacer Constraint::Length(3), // Save/Cancel buttons area ].as_ref()) .split(right_pane); - let input_area = right_vertical_chunks[0]; - let bottom_buttons_area = right_vertical_chunks[2]; + let canvas_area = right_vertical_chunks[0]; + let add_button_area = right_vertical_chunks[1]; + let bottom_buttons_area = right_vertical_chunks[3]; - // Input Area (Name, Type, Add Button) - let input_vertical_chunks = Layout::default() - .direction(Direction::Vertical) - .constraints([ - Constraint::Length(3), // Name Input - Constraint::Length(3), // Type Input - Constraint::Length(1), // Add Button - ].as_ref()) - .split(input_area); - - // Table Name Input (Placeholder) - let name_input_block = Block::default() - .borders(Borders::ALL) - .border_type(BorderType::Rounded) - .title(" Table name ") - .border_style(Style::default().fg(theme.secondary)); // Default style - let name_input_text = Paragraph::new("Name") // Placeholder text - .style(Style::default().fg(theme.fg)) - .block(name_input_block); - f.render_widget(name_input_text, input_vertical_chunks[0]); - - // Type Input (Placeholder) - let type_input_block = Block::default() - .borders(Borders::ALL) - .border_type(BorderType::Rounded) - .title(" Type ") - .border_style(Style::default().fg(theme.secondary)); // Default style - let type_input_text = Paragraph::new("Type") // Placeholder text - .style(Style::default().fg(theme.fg)) - .block(type_input_block); - f.render_widget(type_input_text, input_vertical_chunks[1]); + // --- Use render_canvas for Inputs --- + let _active_field_rect = render_canvas( + f, + canvas_area, + add_table_state, + &[ + "Table name", + "Name", + "Type", + ], + &add_table_state.current_field(), + &add_table_state.inputs().iter().map(|s| *s).collect::>(), + theme, + is_edit_mode, + highlight_state, + ); // Add Button (Placeholder) let add_button = Paragraph::new(" Add ") - .style(Style::default().fg(theme.secondary)) // Default style + .style(Style::default().fg(theme.secondary)) .alignment(Alignment::Center) .block( Block::default() .borders(Borders::ALL) .border_type(BorderType::Rounded) - .border_style(Style::default().fg(theme.secondary)), // Default style + .border_style(Style::default().fg(theme.secondary)), ); - f.render_widget(add_button, input_vertical_chunks[2]); + f.render_widget(add_button, add_button_area); // Bottom Buttons Area (Save, Cancel) let bottom_button_chunks = Layout::default() @@ -168,25 +156,25 @@ pub fn render_add_table( // Save Button (Placeholder) let save_button = Paragraph::new(" Save table ") - .style(Style::default().fg(theme.secondary)) // Default style + .style(Style::default().fg(theme.secondary)) .alignment(Alignment::Center) .block( Block::default() .borders(Borders::ALL) .border_type(BorderType::Rounded) - .border_style(Style::default().fg(theme.secondary)), // Default style + .border_style(Style::default().fg(theme.secondary)), ); f.render_widget(save_button, bottom_button_chunks[0]); // Cancel Button (Placeholder) let cancel_button = Paragraph::new(" Cancel ") - .style(Style::default().fg(theme.secondary)) // Default style + .style(Style::default().fg(theme.secondary)) .alignment(Alignment::Center) .block( Block::default() .borders(Borders::ALL) .border_type(BorderType::Rounded) - .border_style(Style::default().fg(theme.secondary)), // Default style + .border_style(Style::default().fg(theme.secondary)), ); f.render_widget(cancel_button, bottom_button_chunks[1]); } diff --git a/client/src/functions/modes/navigation/add_table_nav.rs b/client/src/functions/modes/navigation/add_table_nav.rs index 51c675a..e4d3609 100644 --- a/client/src/functions/modes/navigation/add_table_nav.rs +++ b/client/src/functions/modes/navigation/add_table_nav.rs @@ -1,134 +1,222 @@ // src/functions/modes/navigation/add_table_nav.rs -use crate::state::pages::add_table::{AddTableFocus, AddTableState}; -use crate::state::app::state::AppState; // If needed for list lengths later +use crate::config::binds::config::Config; +use crate::state::{ + app::state::AppState, + pages::add_table::{AddTableFocus, AddTableState}, +}; +use crossterm::event::{KeyEvent}; +use ratatui::widgets::TableState; // Import TableState -/// Handles navigation events specifically for the Add Table page. +/// Handles navigation events specifically for the Add Table view. /// Returns true if the event was handled, false otherwise. -pub fn handle_navigation( - state: &mut AddTableState, - action: &str, - // app_state: &AppState, // Add if needed for list lengths +pub fn handle_add_table_navigation( + key: KeyEvent, + config: &Config, + _app_state: &AppState, // Keep for potential future use (e.g., checking permissions) + add_table_state: &mut AddTableState, + command_message: &mut String, ) -> bool { - let current_focus = state.current_focus; - let mut handled = true; // Assume handled unless proven otherwise + let action = config.get_general_action(key.code, key.modifiers); + let current_focus = add_table_state.current_focus; + let mut handled = true; // Assume handled unless logic determines otherwise - match action { - "move_up" => { - state.current_focus = match current_focus { - AddTableFocus::TableName => AddTableFocus::CancelButton, // Wrap around top - AddTableFocus::ColumnInput => AddTableFocus::TableName, + match action.as_deref() { + // --- Vertical Navigation (Up/Down) --- + Some("move_up") => { + let mut new_focus = current_focus; // Start with current focus + match current_focus { + AddTableFocus::InputTableName => new_focus = AddTableFocus::CancelButton, // Wrap top + AddTableFocus::InputColumnName => new_focus = AddTableFocus::InputTableName, + AddTableFocus::InputColumnType => new_focus = AddTableFocus::InputColumnName, + AddTableFocus::AddColumnButton => new_focus = AddTableFocus::InputColumnType, AddTableFocus::ColumnsTable => { - // Navigate within table or move focus up - if !navigate_table_up(&mut state.column_table_state, state.columns.len()) { - state.current_focus = AddTableFocus::ColumnInput; + if !navigate_table_up(&mut add_table_state.column_table_state, add_table_state.columns.len()) { + new_focus = AddTableFocus::AddColumnButton; // Move focus up if at table top } - AddTableFocus::ColumnsTable // Keep focus here while navigating table + // Keep focus on table while navigating within it } - AddTableFocus::IndexInput => AddTableFocus::ColumnsTable, AddTableFocus::IndexesTable => { - if !navigate_table_up(&mut state.index_table_state, state.indexes.len()) { - state.current_focus = AddTableFocus::IndexInput; + if !navigate_table_up(&mut add_table_state.index_table_state, add_table_state.indexes.len()) { + new_focus = AddTableFocus::ColumnsTable; // Move focus up } - AddTableFocus::IndexesTable } - AddTableFocus::LinkInput => AddTableFocus::IndexesTable, AddTableFocus::LinksTable => { - if !navigate_table_up(&mut state.link_table_state, state.links.len()) { - state.current_focus = AddTableFocus::LinkInput; + if !navigate_table_up(&mut add_table_state.link_table_state, add_table_state.links.len()) { + new_focus = AddTableFocus::IndexesTable; // Move focus up } - AddTableFocus::LinksTable } - AddTableFocus::SaveButton | AddTableFocus::CancelButton => AddTableFocus::LinksTable, + AddTableFocus::SaveButton => new_focus = AddTableFocus::LinksTable, + AddTableFocus::CancelButton => new_focus = AddTableFocus::SaveButton, } + add_table_state.current_focus = new_focus; + *command_message = format!("Focus set to {:?}", add_table_state.current_focus); } - "move_down" => { - state.current_focus = match current_focus { - AddTableFocus::TableName => AddTableFocus::ColumnInput, - AddTableFocus::ColumnInput => AddTableFocus::ColumnsTable, + Some("move_down") => { + let mut new_focus = current_focus; // Start with current focus + match current_focus { + AddTableFocus::InputTableName => new_focus = AddTableFocus::InputColumnName, + AddTableFocus::InputColumnName => new_focus = AddTableFocus::InputColumnType, + AddTableFocus::InputColumnType => new_focus = AddTableFocus::AddColumnButton, + AddTableFocus::AddColumnButton => new_focus = AddTableFocus::ColumnsTable, AddTableFocus::ColumnsTable => { - if !navigate_table_down(&mut state.column_table_state, state.columns.len()) { - state.current_focus = AddTableFocus::IndexInput; + if !navigate_table_down(&mut add_table_state.column_table_state, add_table_state.columns.len()) { + new_focus = AddTableFocus::IndexesTable; // Move focus down if at table bottom } - AddTableFocus::ColumnsTable + // Keep focus on table while navigating within it } - AddTableFocus::IndexInput => AddTableFocus::IndexesTable, AddTableFocus::IndexesTable => { - if !navigate_table_down(&mut state.index_table_state, state.indexes.len()) { - state.current_focus = AddTableFocus::LinkInput; + if !navigate_table_down(&mut add_table_state.index_table_state, add_table_state.indexes.len()) { + new_focus = AddTableFocus::LinksTable; // Move focus down } - AddTableFocus::IndexesTable } - AddTableFocus::LinkInput => AddTableFocus::LinksTable, AddTableFocus::LinksTable => { - if !navigate_table_down(&mut state.link_table_state, state.links.len()) { - // Move to buttons after table - state.current_focus = AddTableFocus::SaveButton; + if !navigate_table_down(&mut add_table_state.link_table_state, add_table_state.links.len()) { + new_focus = AddTableFocus::SaveButton; // Move focus down } - AddTableFocus::LinksTable } - AddTableFocus::SaveButton | AddTableFocus::CancelButton => AddTableFocus::TableName, // Wrap around bottom + AddTableFocus::SaveButton => new_focus = AddTableFocus::CancelButton, + AddTableFocus::CancelButton => new_focus = AddTableFocus::InputTableName, // Wrap bottom } + add_table_state.current_focus = new_focus; + *command_message = format!("Focus set to {:?}", add_table_state.current_focus); } - "next_option" => { // Typically Tab or Right arrow - state.current_focus = match current_focus { - // Simple vertical flow for now, like move_down - AddTableFocus::TableName => AddTableFocus::ColumnInput, - AddTableFocus::ColumnInput => AddTableFocus::ColumnsTable, - AddTableFocus::ColumnsTable => AddTableFocus::IndexInput, - AddTableFocus::IndexInput => AddTableFocus::IndexesTable, - AddTableFocus::IndexesTable => AddTableFocus::LinkInput, - AddTableFocus::LinkInput => AddTableFocus::LinksTable, + + // --- Horizontal Navigation (Left/Right) --- + Some("next_option") => { // 'l' or Right + add_table_state.current_focus = match current_focus { + AddTableFocus::SaveButton => AddTableFocus::CancelButton, + _ => current_focus, // No change for others yet + }; + *command_message = format!("Focus set to {:?}", add_table_state.current_focus); + } + Some("previous_option") => { // 'h' or Left + add_table_state.current_focus = match current_focus { + AddTableFocus::CancelButton => AddTableFocus::SaveButton, + _ => current_focus, // No change for others yet + }; + *command_message = format!("Focus set to {:?}", add_table_state.current_focus); + } + + // --- Tab / Shift+Tab Navigation --- + Some("next_field") => { // Tab + add_table_state.current_focus = match current_focus { + AddTableFocus::InputTableName => AddTableFocus::InputColumnName, + AddTableFocus::InputColumnName => AddTableFocus::InputColumnType, + AddTableFocus::InputColumnType => AddTableFocus::AddColumnButton, + AddTableFocus::AddColumnButton => AddTableFocus::ColumnsTable, + AddTableFocus::ColumnsTable => AddTableFocus::IndexesTable, + AddTableFocus::IndexesTable => AddTableFocus::LinksTable, AddTableFocus::LinksTable => AddTableFocus::SaveButton, AddTableFocus::SaveButton => AddTableFocus::CancelButton, - AddTableFocus::CancelButton => AddTableFocus::TableName, // Wrap - } + AddTableFocus::CancelButton => AddTableFocus::InputTableName, // Wrap + }; + *command_message = format!("Focus set to {:?}", add_table_state.current_focus); } - "previous_option" => { // Typically Shift+Tab or Left arrow - state.current_focus = match current_focus { - // Simple vertical flow upwards - AddTableFocus::TableName => AddTableFocus::CancelButton, // Wrap - AddTableFocus::ColumnInput => AddTableFocus::TableName, - AddTableFocus::ColumnsTable => AddTableFocus::ColumnInput, - AddTableFocus::IndexInput => AddTableFocus::ColumnsTable, - AddTableFocus::IndexesTable => AddTableFocus::IndexInput, - AddTableFocus::LinkInput => AddTableFocus::IndexesTable, - AddTableFocus::LinksTable => AddTableFocus::LinkInput, + Some("prev_field") => { // Shift+Tab + add_table_state.current_focus = match current_focus { + AddTableFocus::InputTableName => AddTableFocus::CancelButton, // Wrap + AddTableFocus::InputColumnName => AddTableFocus::InputTableName, + AddTableFocus::InputColumnType => AddTableFocus::InputColumnName, + AddTableFocus::AddColumnButton => AddTableFocus::InputColumnType, + AddTableFocus::ColumnsTable => AddTableFocus::AddColumnButton, + AddTableFocus::IndexesTable => AddTableFocus::ColumnsTable, + AddTableFocus::LinksTable => AddTableFocus::IndexesTable, AddTableFocus::SaveButton => AddTableFocus::LinksTable, AddTableFocus::CancelButton => AddTableFocus::SaveButton, + }; + *command_message = format!("Focus set to {:?}", add_table_state.current_focus); + } + + // --- Selection --- + Some("select") => { + match current_focus { + AddTableFocus::AddColumnButton => { + *command_message = "Action: Add Column (Not Implemented)".to_string(); + // TODO: Implement logic to add column based on inputs + // Clear input fields, add to columns list, mark unsaved changes + // add_table_state.add_column(); // Example method call + } + AddTableFocus::SaveButton => { + *command_message = "Action: Save Table (Not Implemented)".to_string(); + // TODO: Implement logic to save table (e.g., call API) + // Mark changes as saved + // add_table_state.save_table(); // Example method call + } + AddTableFocus::CancelButton => { + *command_message = "Action: Cancel Add Table".to_string(); + // TODO: Implement logic to navigate back (e.g., update AppView history) + // Maybe show a confirmation dialog if there are unsaved changes + // buffer_state.go_back(); // Example call + } + // Selecting input fields usually means entering Edit mode (handled elsewhere) + // Selecting tables might mean focusing on them for editing/deletion (TODO) + AddTableFocus::ColumnsTable => { + if let Some(index) = add_table_state.column_table_state.selected() { + *command_message = format!("Selected column index {}", index); + // TODO: Add logic for editing/deleting selected column + } else { + *command_message = "No column selected".to_string(); + } + } + AddTableFocus::IndexesTable => { + if let Some(index) = add_table_state.index_table_state.selected() { + *command_message = format!("Selected index index {}", index); + // TODO: Add logic for editing/deleting selected index + } else { + *command_message = "No index selected".to_string(); + } + } + AddTableFocus::LinksTable => { + if let Some(index) = add_table_state.link_table_state.selected() { + *command_message = format!("Selected link index {}", index); + // TODO: Add logic for editing/deleting selected link + } else { + *command_message = "No link selected".to_string(); + } + } + _ => { + // For InputTableName, InputColumnName, InputColumnType, + // the main event loop should handle 'select' by potentially + // switching to Edit mode if not already in it. + // We don't need specific logic here for that. + *command_message = format!("Select on {:?}", current_focus); + handled = false; // Let main loop handle edit mode toggle maybe + } } } - "select" => { - // TODO: Implement select action based on current_focus - // e.g., Enter edit mode for TableName/Inputs, toggle link required, trigger save/cancel - handled = false; // Mark as not handled for now + + // --- Other General Keys (Ignore for add_table nav) --- + Some("toggle_sidebar") | Some("toggle_buffer_list") => { + handled = false; // Let global handler manage these } - _ => handled = false, // Action not relevant for this navigation + + // --- No matching action --- + _ => handled = false, // Event not handled by add_table navigation } - // If focus changed to a table, select the first row if nothing is selected - if handled && current_focus != state.current_focus { - match state.current_focus { - AddTableFocus::ColumnsTable if state.column_table_state.selected().is_none() && !state.columns.is_empty() => { - state.column_table_state.select(Some(0)); + // If focus changed TO a table, select the first row if nothing is selected + if handled && current_focus != add_table_state.current_focus { + match add_table_state.current_focus { + AddTableFocus::ColumnsTable if add_table_state.column_table_state.selected().is_none() && !add_table_state.columns.is_empty() => { + add_table_state.column_table_state.select(Some(0)); } - AddTableFocus::IndexesTable if state.index_table_state.selected().is_none() && !state.indexes.is_empty() => { - state.index_table_state.select(Some(0)); + AddTableFocus::IndexesTable if add_table_state.index_table_state.selected().is_none() && !add_table_state.indexes.is_empty() => { + add_table_state.index_table_state.select(Some(0)); } - AddTableFocus::LinksTable if state.link_table_state.selected().is_none() && !state.links.is_empty() => { - state.link_table_state.select(Some(0)); + AddTableFocus::LinksTable if add_table_state.link_table_state.selected().is_none() && !add_table_state.links.is_empty() => { + add_table_state.link_table_state.select(Some(0)); } _ => {} // No action needed for other focus states } } - handled } // Helper function for navigating up within a table state // Returns true if navigation happened within the table, false if it reached the top -fn navigate_table_up(table_state: &mut ratatui::widgets::TableState, item_count: usize) -> bool { +fn navigate_table_up(table_state: &mut TableState, item_count: usize) -> bool { if item_count == 0 { return false; } // Cannot navigate empty table let current_selection = table_state.selected(); match current_selection { @@ -141,7 +229,8 @@ fn navigate_table_up(table_state: &mut ratatui::widgets::TableState, item_count: } } None => { - table_state.select(Some(item_count - 1)); // Select last item if nothing selected + // If nothing selected, moving up could select the last item + table_state.select(Some(item_count - 1)); true } } @@ -149,7 +238,7 @@ fn navigate_table_up(table_state: &mut ratatui::widgets::TableState, item_count: // Helper function for navigating down within a table state // Returns true if navigation happened within the table, false if it reached the bottom -fn navigate_table_down(table_state: &mut ratatui::widgets::TableState, item_count: usize) -> bool { +fn navigate_table_down(table_state: &mut TableState, item_count: usize) -> bool { if item_count == 0 { return false; } // Cannot navigate empty table let current_selection = table_state.selected(); match current_selection { @@ -162,8 +251,10 @@ fn navigate_table_down(table_state: &mut ratatui::widgets::TableState, item_coun } } None => { - table_state.select(Some(0)); // Select first item if nothing selected + // If nothing selected, moving down could select the first item + table_state.select(Some(0)); true } } } + diff --git a/client/src/modes/handlers/event.rs b/client/src/modes/handlers/event.rs index 1e79aaf..a65eee5 100644 --- a/client/src/modes/handlers/event.rs +++ b/client/src/modes/handlers/event.rs @@ -178,11 +178,15 @@ impl EventHandler { // --- Add Table Page Navigation --- if app_state.ui.show_add_table { if let Some(action) = config.get_general_action(key.code, key.modifiers) { - if add_table_nav::handle_navigation( + if add_table_nav::handle_add_table_navigation( + key, + config, + app_state, &mut admin_state.add_table_state, - action, + &mut self.command_message, + ) { - return Ok(EventOutcome::Ok(String::new())); + return Ok(EventOutcome::Ok(self.command_message.clone())); } } } diff --git a/client/src/state/pages/add_table.rs b/client/src/state/pages/add_table.rs index cca12ce..fc661dd 100644 --- a/client/src/state/pages/add_table.rs +++ b/client/src/state/pages/add_table.rs @@ -1,4 +1,5 @@ // src/state/pages/add_table.rs +use crate::state::pages::canvas_state::CanvasState; use ratatui::widgets::TableState; #[derive(Debug, Clone, PartialEq, Eq)] @@ -16,11 +17,10 @@ pub struct LinkDefinition { #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub enum AddTableFocus { #[default] - TableName, - // Input areas (placeholders for now) - ColumnInput, - IndexInput, - LinkInput, + InputTableName, // Field 0 for CanvasState + InputColumnName, // Field 1 for CanvasState + InputColumnType, // Field 2 for CanvasState + AddColumnButton, // Result Tables ColumnsTable, IndexesTable, @@ -34,6 +34,8 @@ pub enum AddTableFocus { pub struct AddTableState { pub profile_name: String, pub table_name: String, + pub column_name_input: String, + pub column_type_input: String, pub columns: Vec, pub indexes: Vec, pub links: Vec, @@ -42,6 +44,9 @@ pub struct AddTableState { pub index_table_state: TableState, pub link_table_state: TableState, pub table_name_cursor_pos: usize, + pub column_name_cursor_pos: usize, + pub column_type_cursor_pos: usize, + pub has_unsaved_changes: bool, } impl Default for AddTableState { @@ -49,7 +54,9 @@ impl Default for AddTableState { // Initialize with some dummy data for demonstration AddTableState { profile_name: "default".to_string(), // Should be set dynamically - table_name: "new_table".to_string(), + table_name: String::new(), // Start empty + column_name_input: String::new(), + column_type_input: String::new(), columns: vec![ ColumnDefinition { name: "id".to_string(), data_type: "INTEGER".to_string() }, ColumnDefinition { name: "name".to_string(), data_type: "TEXT".to_string() }, @@ -59,16 +66,107 @@ impl Default for AddTableState { LinkDefinition { linked_table_name: "related_table".to_string(), is_required: true }, LinkDefinition { linked_table_name: "another_table".to_string(), is_required: false }, ], - current_focus: AddTableFocus::TableName, + current_focus: AddTableFocus::InputTableName, column_table_state: TableState::default().with_selected(0), index_table_state: TableState::default().with_selected(0), link_table_state: TableState::default().with_selected(0), table_name_cursor_pos: 0, + column_name_cursor_pos: 0, + column_type_cursor_pos: 0, + has_unsaved_changes: false, } } } impl AddTableState { + const INPUT_FIELD_COUNT: usize = 3; } +// Implement CanvasState for the input fields +impl CanvasState for AddTableState { + fn current_field(&self) -> usize { + match self.current_focus { + AddTableFocus::InputTableName => 0, + AddTableFocus::InputColumnName => 1, + AddTableFocus::InputColumnType => 2, + // If focus is elsewhere, default to the first field for canvas rendering logic + _ => 0, + } + } + + fn current_cursor_pos(&self) -> usize { + match self.current_focus { + AddTableFocus::InputTableName => self.table_name_cursor_pos, + AddTableFocus::InputColumnName => self.column_name_cursor_pos, + AddTableFocus::InputColumnType => self.column_type_cursor_pos, + _ => 0, // Default if focus is not on an input field + } + } + + fn has_unsaved_changes(&self) -> bool { + self.has_unsaved_changes + } + + fn inputs(&self) -> Vec<&String> { + vec![&self.table_name, &self.column_name_input, &self.column_type_input] + } + + fn get_current_input(&self) -> &str { + match self.current_focus { + AddTableFocus::InputTableName => &self.table_name, + AddTableFocus::InputColumnName => &self.column_name_input, + AddTableFocus::InputColumnType => &self.column_type_input, + _ => "", // Should not happen if called correctly + } + } + + fn get_current_input_mut(&mut self) -> &mut String { + match self.current_focus { + AddTableFocus::InputTableName => &mut self.table_name, + AddTableFocus::InputColumnName => &mut self.column_name_input, + AddTableFocus::InputColumnType => &mut self.column_type_input, + // This case needs careful handling. If focus isn't on an input, + // which mutable string should we return? Returning the first one + // might be unexpected. Consider panicking or returning Option if this state is invalid. + // For now, returning the first field to avoid panics during rendering. + _ => &mut self.table_name, + } + } + + fn fields(&self) -> Vec<&str> { + // These must match the order used in render_add_table + vec!["Table name", "Name", "Type"] + } + + fn set_current_field(&mut self, index: usize) { + self.current_focus = match index { + 0 => AddTableFocus::InputTableName, + 1 => AddTableFocus::InputColumnName, + 2 => AddTableFocus::InputColumnType, + _ => self.current_focus, // Stay on current focus if index is out of bounds + }; + } + + fn set_current_cursor_pos(&mut self, pos: usize) { + match self.current_focus { + AddTableFocus::InputTableName => self.table_name_cursor_pos = pos, + AddTableFocus::InputColumnName => self.column_name_cursor_pos = pos, + AddTableFocus::InputColumnType => self.column_type_cursor_pos = pos, + _ => {} // Do nothing if focus is not on an input field + } + } + + fn set_has_unsaved_changes(&mut self, changed: bool) { + self.has_unsaved_changes = changed; + } + + // --- Autocomplete Support (Not needed for this form yet) --- + fn get_suggestions(&self) -> Option<&[String]> { + None + } + + fn get_selected_suggestion_index(&self) -> Option { + None + } +} diff --git a/client/src/ui/handlers/render.rs b/client/src/ui/handlers/render.rs index 67378d3..a339ba7 100644 --- a/client/src/ui/handlers/render.rs +++ b/client/src/ui/handlers/render.rs @@ -104,6 +104,8 @@ pub fn render_ui( theme, app_state, &mut admin_state.add_table_state, + login_state.current_field < 3, + highlight_state, ); } else if app_state.ui.show_login { render_login(