diff --git a/client/src/components/admin/add_table.rs b/client/src/components/admin/add_table.rs index b0997fc..5b4b664 100644 --- a/client/src/components/admin/add_table.rs +++ b/client/src/components/admin/add_table.rs @@ -186,8 +186,7 @@ pub fn render_add_table( // --- Common Widget Rendering (Uses calculated areas) --- // --- Columns Table Rendering --- - let columns_focused = - add_table_state.current_focus == AddTableFocus::ColumnsTable; + let columns_focused = matches!(add_table_state.current_focus, AddTableFocus::ColumnsTable | AddTableFocus::InsideColumnsTable); let columns_border_style = if columns_focused { Style::default().fg(theme.highlight) } else { @@ -204,11 +203,11 @@ pub fn render_add_table( .style(Style::default().fg(theme.fg)) }) .collect(); - // Use different headers/constraints based on layout? For now, keep consistent. let header_cells = ["Name", "Type"] .iter() .map(|h| Cell::from(*h).style(Style::default().fg(theme.accent))); let header = Row::new(header_cells).height(1).bottom_margin(1); + let columns_highlight_symbol = if add_table_state.current_focus == AddTableFocus::InsideColumnsTable { " > " } else { " " }; let columns_table = Table::new( column_rows, [Constraint::Percentage(60), Constraint::Percentage(40)], @@ -218,7 +217,7 @@ pub fn render_add_table( Block::default() .title(Span::styled(" Columns ", theme.fg)) .title_alignment(Alignment::Center) - .borders(Borders::ALL) // Use ALL borders for consistency + .borders(Borders::ALL) .border_type(BorderType::Rounded) .border_style(columns_border_style), ) @@ -287,11 +286,10 @@ pub fn render_add_table( add_table_state.current_focus, )), ); - f.render_widget(add_button, add_button_area); // Render into the calculated area + f.render_widget(add_button, add_button_area); // --- Indexes Table Rendering --- - let indexes_focused = - add_table_state.current_focus == AddTableFocus::IndexesTable; + let indexes_focused = matches!(add_table_state.current_focus, AddTableFocus::IndexesTable | AddTableFocus::InsideIndexesTable); let indexes_border_style = if indexes_focused { Style::default().fg(theme.highlight) } else { @@ -309,6 +307,7 @@ pub fn render_add_table( .iter() .map(|h| Cell::from(*h).style(Style::default().fg(theme.accent))); let index_header = Row::new(index_header_cells).height(1).bottom_margin(1); + let indexes_highlight_symbol = if add_table_state.current_focus == AddTableFocus::InsideIndexesTable { " > " } else { " " }; let indexes_table = Table::new(index_rows, [Constraint::Percentage(100)]) .header(index_header) @@ -333,7 +332,7 @@ pub fn render_add_table( ); // --- Links Table Rendering --- - let links_focused = add_table_state.current_focus == AddTableFocus::LinksTable; + let links_focused = matches!(add_table_state.current_focus, AddTableFocus::LinksTable | AddTableFocus::InsideLinksTable); let links_border_style = if links_focused { Style::default().fg(theme.highlight) } else { @@ -350,10 +349,11 @@ pub fn render_add_table( .style(Style::default().fg(theme.fg)) }) .collect(); - let link_header_cells = ["Linked Table", "Req"] + let link_header_cells = ["Linked Table", "Selected"] .iter() .map(|h| Cell::from(*h).style(Style::default().fg(theme.accent))); let link_header = Row::new(link_header_cells).height(1).bottom_margin(1); + let links_highlight_symbol = if add_table_state.current_focus == AddTableFocus::InsideLinksTable { " > " } else { " " }; let links_table = Table::new(link_rows, [Constraint::Percentage(80), Constraint::Min(5)]) .header(link_header) @@ -381,9 +381,9 @@ pub fn render_add_table( let bottom_button_chunks = Layout::default() .direction(Direction::Horizontal) .constraints([ - Constraint::Percentage(33), // Save Button - Constraint::Percentage(34), // Delete Button - Constraint::Percentage(33), // Cancel Button + Constraint::Percentage(33), // Save Button + Constraint::Percentage(34), // Delete Button + Constraint::Percentage(33), // Cancel Button ]) .split(bottom_buttons_area); @@ -404,22 +404,22 @@ pub fn render_add_table( ); f.render_widget(save_button, bottom_button_chunks[0]); - let delete_button = Paragraph::new(" Delete Selected ") - .style(get_button_style( - AddTableFocus::DeleteSelectedButton, - add_table_state.current_focus, - )) - .alignment(Alignment::Center) - .block( - Block::default() - .borders(Borders::ALL) - .border_type(BorderType::Rounded) - .border_style(get_button_border_style( - AddTableFocus::DeleteSelectedButton, - add_table_state.current_focus, - )), - ); - f.render_widget(delete_button, bottom_button_chunks[1]); + let delete_button = Paragraph::new(" Delete Selected ") + .style(get_button_style( + AddTableFocus::DeleteSelectedButton, + add_table_state.current_focus, + )) + .alignment(Alignment::Center) + .block( + Block::default() + .borders(Borders::ALL) + .border_type(BorderType::Rounded) + .border_style(get_button_border_style( + AddTableFocus::DeleteSelectedButton, + add_table_state.current_focus, + )), + ); + f.render_widget(delete_button, bottom_button_chunks[1]); let cancel_button = Paragraph::new(" Cancel ") .style(get_button_style( diff --git a/client/src/functions/modes/navigation/add_table_nav.rs b/client/src/functions/modes/navigation/add_table_nav.rs index 1670bc5..1977a2a 100644 --- a/client/src/functions/modes/navigation/add_table_nav.rs +++ b/client/src/functions/modes/navigation/add_table_nav.rs @@ -23,11 +23,14 @@ pub fn handle_add_table_navigation( let mut new_focus = current_focus; // Initialize new_focus // Define focus groups for horizontal navigation - let is_left_pane_focus = matches!(current_focus, + let is_left_pane_block_focus = matches!(current_focus, // Focus on the table blocks AddTableFocus::ColumnsTable | AddTableFocus::IndexesTable | AddTableFocus::LinksTable ); + let is_inside_table_focus = matches!(current_focus, // Focus inside for scrolling + AddTableFocus::InsideColumnsTable | AddTableFocus::InsideIndexesTable | AddTableFocus::InsideLinksTable + ); let is_right_pane_general_focus = matches!(current_focus, // Non-canvas elements in right pane - AddTableFocus::AddColumnButton | AddTableFocus::SaveButton | AddTableFocus::CancelButton + AddTableFocus::AddColumnButton | AddTableFocus::SaveButton | AddTableFocus::DeleteSelectedButton | AddTableFocus::CancelButton ); let is_canvas_input_focus = matches!(current_focus, AddTableFocus::InputTableName | AddTableFocus::InputColumnName | AddTableFocus::InputColumnType @@ -37,31 +40,30 @@ pub fn handle_add_table_navigation( // --- Vertical Navigation (Up/Down) --- Some("move_up") => { match current_focus { - AddTableFocus::InputTableName => new_focus = AddTableFocus::CancelButton, // Wrap top (right pane) + AddTableFocus::InputTableName => new_focus = AddTableFocus::CancelButton, AddTableFocus::InputColumnName => new_focus = AddTableFocus::InputTableName, AddTableFocus::InputColumnType => new_focus = AddTableFocus::InputColumnName, AddTableFocus::AddColumnButton => new_focus = AddTableFocus::InputColumnType, - AddTableFocus::ColumnsTable => { // Left pane navigation - if !navigate_table_up(&mut add_table_state.column_table_state, add_table_state.columns.len()) { - // If at top of columns, potentially wrap to bottom of left pane (LinksTable) or stay? Let's stay for now. - // Or maybe move to AddColumnButton? Let's try moving up from right pane instead. - new_focus = AddTableFocus::AddColumnButton; // Tentative: move focus up from right pane - } + // Navigate between blocks when focus is on the table block itself + AddTableFocus::ColumnsTable => new_focus = AddTableFocus::AddColumnButton, // Move up to right pane + AddTableFocus::IndexesTable => new_focus = AddTableFocus::ColumnsTable, + AddTableFocus::LinksTable => new_focus = AddTableFocus::IndexesTable, + // Scroll inside the table when focus is internal + AddTableFocus::InsideColumnsTable => { + navigate_table_up(&mut add_table_state.column_table_state, add_table_state.columns.len()); + // Stay inside the table, don't change new_focus } - AddTableFocus::IndexesTable => { - if !navigate_table_up(&mut add_table_state.index_table_state, add_table_state.indexes.len()) { - new_focus = AddTableFocus::ColumnsTable; - } + AddTableFocus::InsideIndexesTable => { + navigate_table_up(&mut add_table_state.index_table_state, add_table_state.indexes.len()); + // Stay inside the table } - AddTableFocus::LinksTable => { - if !navigate_table_up(&mut add_table_state.link_table_state, add_table_state.links.len()) { - new_focus = AddTableFocus::IndexesTable; - } + AddTableFocus::InsideLinksTable => { + navigate_table_up(&mut add_table_state.link_table_state, add_table_state.links.len()); + // Stay inside the table } - AddTableFocus::SaveButton => new_focus = AddTableFocus::LinksTable, // Move up to left pane bottom + AddTableFocus::SaveButton => new_focus = AddTableFocus::LinksTable, AddTableFocus::DeleteSelectedButton => new_focus = AddTableFocus::SaveButton, AddTableFocus::CancelButton => new_focus = AddTableFocus::DeleteSelectedButton, - } } Some("move_down") => { @@ -69,55 +71,56 @@ pub fn handle_add_table_navigation( AddTableFocus::InputTableName => new_focus = AddTableFocus::InputColumnName, AddTableFocus::InputColumnName => new_focus = AddTableFocus::InputColumnType, AddTableFocus::InputColumnType => new_focus = AddTableFocus::AddColumnButton, - AddTableFocus::AddColumnButton => new_focus = AddTableFocus::ColumnsTable, // Move down to left pane top - AddTableFocus::ColumnsTable => { // Left pane navigation - if !navigate_table_down(&mut add_table_state.column_table_state, add_table_state.columns.len()) { - new_focus = AddTableFocus::IndexesTable; // Move to next left pane item - } + AddTableFocus::AddColumnButton => new_focus = AddTableFocus::ColumnsTable, + // Navigate between blocks when focus is on the table block itself + AddTableFocus::ColumnsTable => new_focus = AddTableFocus::IndexesTable, + AddTableFocus::IndexesTable => new_focus = AddTableFocus::LinksTable, + AddTableFocus::LinksTable => new_focus = AddTableFocus::SaveButton, // Move down to right pane + // Scroll inside the table when focus is internal + AddTableFocus::InsideColumnsTable => { + navigate_table_down(&mut add_table_state.column_table_state, add_table_state.columns.len()); + // Stay inside the table } - AddTableFocus::IndexesTable => { - if !navigate_table_down(&mut add_table_state.index_table_state, add_table_state.indexes.len()) { - new_focus = AddTableFocus::LinksTable; - } + AddTableFocus::InsideIndexesTable => { + navigate_table_down(&mut add_table_state.index_table_state, add_table_state.indexes.len()); + // Stay inside the table } - AddTableFocus::LinksTable => { - if !navigate_table_down(&mut add_table_state.link_table_state, add_table_state.links.len()) { - new_focus = AddTableFocus::SaveButton; // Move down to right pane bottom - } + AddTableFocus::InsideLinksTable => { + navigate_table_down(&mut add_table_state.link_table_state, add_table_state.links.len()); + // Stay inside the table } AddTableFocus::SaveButton => new_focus = AddTableFocus::DeleteSelectedButton, AddTableFocus::DeleteSelectedButton => new_focus = AddTableFocus::CancelButton, - AddTableFocus::CancelButton => new_focus = AddTableFocus::InputTableName, // Wrap bottom (right pane) + AddTableFocus::CancelButton => new_focus = AddTableFocus::InputTableName, } } // --- Horizontal Navigation (Left/Right) --- Some("next_option") => { // 'l' or Right: Move from Left Pane to Right Pane - if is_left_pane_focus { + if is_left_pane_block_focus || is_inside_table_focus { // Treat inside same as block for exiting left new_focus = match current_focus { // Map left pane items to corresponding right pane items (approximate vertical alignment) - AddTableFocus::ColumnsTable => AddTableFocus::InputTableName, - AddTableFocus::IndexesTable => AddTableFocus::InputColumnName, // Or AddColumnButton? - AddTableFocus::LinksTable => AddTableFocus::SaveButton, - _ => current_focus, // Should not happen based on is_left_pane_focus + AddTableFocus::ColumnsTable | AddTableFocus::InsideColumnsTable => AddTableFocus::InputTableName, + AddTableFocus::IndexesTable | AddTableFocus::InsideIndexesTable => AddTableFocus::InputColumnName, + AddTableFocus::LinksTable | AddTableFocus::InsideLinksTable => AddTableFocus::SaveButton, + _ => current_focus, // Should not happen }; } else if is_right_pane_general_focus || is_canvas_input_focus { - // If already in right pane, maybe wrap Save -> Cancel or stay? Let's handle Save->Cancel only. - if current_focus == AddTableFocus::SaveButton { - new_focus = AddTableFocus::CancelButton; - } + // Horizontal nav within bottom buttons + if current_focus == AddTableFocus::SaveButton { new_focus = AddTableFocus::DeleteSelectedButton; } + else if current_focus == AddTableFocus::DeleteSelectedButton { new_focus = AddTableFocus::CancelButton; } + else if current_focus == AddTableFocus::CancelButton { /* Stay */ } } } Some("previous_option") => { // 'h' or Left: Move from Right Pane to Left Pane - if is_right_pane_general_focus { + if is_right_pane_general_focus || is_canvas_input_focus { // Treat canvas inputs same as right pane general for moving left new_focus = match current_focus { // Map right pane items back to left pane items (approximate vertical alignment) - AddTableFocus::InputTableName | AddTableFocus::InputColumnName | AddTableFocus::InputColumnType | AddTableFocus::AddColumnButton => AddTableFocus::ColumnsTable, // Go to top of left pane - AddTableFocus::SaveButton | AddTableFocus::CancelButton => AddTableFocus::LinksTable, // Go to bottom of left pane + AddTableFocus::InputTableName | AddTableFocus::InputColumnName | AddTableFocus::InputColumnType | AddTableFocus::AddColumnButton => AddTableFocus::ColumnsTable, + AddTableFocus::SaveButton | AddTableFocus::DeleteSelectedButton | AddTableFocus::CancelButton => AddTableFocus::LinksTable, _ => current_focus, // Should not happen }; - } else if is_left_pane_focus { - // If already in left pane, pressing 'h' could wrap to Cancel button? + } else if is_left_pane_block_focus || is_inside_table_focus { // Treat inside same as block for wrapping left new_focus = AddTableFocus::CancelButton; // Wrap left-to-right bottom } } @@ -129,9 +132,10 @@ pub fn handle_add_table_navigation( AddTableFocus::InputColumnName => AddTableFocus::InputColumnType, AddTableFocus::InputColumnType => AddTableFocus::AddColumnButton, AddTableFocus::AddColumnButton => AddTableFocus::ColumnsTable, - AddTableFocus::ColumnsTable => AddTableFocus::IndexesTable, - AddTableFocus::IndexesTable => AddTableFocus::LinksTable, - AddTableFocus::LinksTable => AddTableFocus::SaveButton, + // Treat Inside* same as block focus for tabbing out + AddTableFocus::ColumnsTable | AddTableFocus::InsideColumnsTable => AddTableFocus::IndexesTable, + AddTableFocus::IndexesTable | AddTableFocus::InsideIndexesTable => AddTableFocus::LinksTable, + AddTableFocus::LinksTable | AddTableFocus::InsideLinksTable => AddTableFocus::SaveButton, AddTableFocus::SaveButton => AddTableFocus::DeleteSelectedButton, AddTableFocus::DeleteSelectedButton => AddTableFocus::CancelButton, AddTableFocus::CancelButton => AddTableFocus::InputTableName, // Wrap @@ -143,9 +147,10 @@ pub fn handle_add_table_navigation( AddTableFocus::InputColumnName => AddTableFocus::InputTableName, AddTableFocus::InputColumnType => AddTableFocus::InputColumnName, AddTableFocus::AddColumnButton => AddTableFocus::InputColumnType, - AddTableFocus::ColumnsTable => AddTableFocus::AddColumnButton, - AddTableFocus::IndexesTable => AddTableFocus::ColumnsTable, - AddTableFocus::LinksTable => AddTableFocus::IndexesTable, + // Treat Inside* same as block focus for tabbing out + AddTableFocus::ColumnsTable | AddTableFocus::InsideColumnsTable => AddTableFocus::AddColumnButton, + AddTableFocus::IndexesTable | AddTableFocus::InsideIndexesTable => AddTableFocus::ColumnsTable, + AddTableFocus::LinksTable | AddTableFocus::InsideLinksTable => AddTableFocus::IndexesTable, AddTableFocus::SaveButton => AddTableFocus::LinksTable, AddTableFocus::DeleteSelectedButton => AddTableFocus::SaveButton, AddTableFocus::CancelButton => AddTableFocus::DeleteSelectedButton, @@ -155,6 +160,60 @@ pub fn handle_add_table_navigation( // --- Selection --- Some("select") => { match current_focus { + // --- Enter/Exit Table Focus --- + AddTableFocus::ColumnsTable => { + new_focus = AddTableFocus::InsideColumnsTable; + // Select first item if none selected when entering + 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)); + } + *command_message = "Entered Columns Table (Scroll with Up/Down, Select to exit)".to_string(); + } + AddTableFocus::IndexesTable => { + new_focus = AddTableFocus::InsideIndexesTable; + 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)); + } + *command_message = "Entered Indexes Table (Scroll with Up/Down, Select to exit)".to_string(); + } + AddTableFocus::LinksTable => { + new_focus = AddTableFocus::InsideLinksTable; + 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)); + } + *command_message = "Entered Links Table (Scroll with Up/Down, Select to toggle/exit)".to_string(); + } + AddTableFocus::InsideColumnsTable => { + new_focus = AddTableFocus::ColumnsTable; // Exit back to block focus + *command_message = "Exited Columns Table".to_string(); + } + AddTableFocus::InsideIndexesTable => { + new_focus = AddTableFocus::IndexesTable; // Exit back to block focus + *command_message = "Exited Indexes Table".to_string(); + } + AddTableFocus::InsideLinksTable => { + // Toggle selection when pressing select *inside* the links table + if let Some(index) = add_table_state.link_table_state.selected() { + if let Some(link) = add_table_state.links.get_mut(index) { + link.selected = !link.selected; // Toggle the selected state + add_table_state.has_unsaved_changes = true; // Mark changes + *command_message = format!( + "Toggled selection for link: {} to {}", + link.linked_table_name, link.selected + ); + } else { + *command_message = "Error: Selected link index out of bounds".to_string(); + } + } else { + *command_message = "No link selected to toggle".to_string(); + } + // Stay inside the links table after toggling + new_focus = AddTableFocus::InsideLinksTable; + // Alternative: Exit after toggle: + // new_focus = AddTableFocus::LinksTable; + // *command_message = format!("{} - Exited Links Table", command_message); + } + // --- Other Select Actions --- AddTableFocus::AddColumnButton => { if let Some(focus_after_add) = handle_add_column_action(add_table_state, command_message) { new_focus = focus_after_add; @@ -172,21 +231,6 @@ pub fn handle_add_table_navigation( *command_message = "Action: Cancel Add Table".to_string(); // TODO: Implement logic } - AddTableFocus::ColumnsTable => { - if let Some(index) = add_table_state.column_table_state.selected() { - *command_message = format!("Selected column index {}", index); - } 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); - } 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); - } else { *command_message = "No link selected".to_string(); } - } _ => { // Input fields *command_message = format!("Select on {:?}", current_focus); handled = false; // Let main loop handle edit mode toggle maybe @@ -207,19 +251,22 @@ pub fn handle_add_table_navigation( // Update focus state if it changed and was handled if handled && current_focus != new_focus { add_table_state.current_focus = new_focus; - *command_message = format!("Focus set to {:?}", add_table_state.current_focus); + // Avoid overwriting specific messages set during 'select' handling + if command_message.is_empty() || command_message.starts_with("Focus set to") { + *command_message = format!("Focus set to {:?}", add_table_state.current_focus); + } - // --- THIS IS THE KEY PART --- // Check if the *new* focus target is one of the canvas input fields let new_is_canvas_input_focus = matches!(new_focus, AddTableFocus::InputTableName | AddTableFocus::InputColumnName | AddTableFocus::InputColumnType ); - // Set focus_outside_canvas based on whether the new focus is NOT an input field - app_state.ui.focus_outside_canvas = !new_is_canvas_input_focus; // <--- Sets the flag correctly - // --- END KEY PART --- + // Focus is outside canvas if it's not an input field + app_state.ui.focus_outside_canvas = !new_is_canvas_input_focus; - // Select first item when focusing a table + // Select first item when focusing a table block (but not when entering 'Inside') + // This might be redundant now due to the 'select' logic selecting first item on entry. + // Consider removing this block if it causes issues. 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)); @@ -232,7 +279,7 @@ pub fn handle_add_table_navigation( }_ => {} } } else if !handled { - // ... + // command_message.clear(); // Optional: Clear message if not handled here } handled @@ -248,14 +295,14 @@ fn navigate_table_up(table_state: &mut TableState, item_count: usize) -> bool { Some(index) => { if index > 0 { table_state.select(Some(index - 1)); - true + true // Navigation happened } else { false // Was at the top } } - None => { - table_state.select(Some(item_count - 1)); // Select last item - true + None => { // No item selected, select the last one + table_state.select(Some(item_count - 1)); + true // Navigation happened (selection set) } } } @@ -269,14 +316,14 @@ fn navigate_table_down(table_state: &mut TableState, item_count: usize) -> bool Some(index) => { if index < item_count - 1 { table_state.select(Some(index + 1)); - true + true // Navigation happened } else { false // Was at the bottom } } - None => { - table_state.select(Some(0)); // Select first item - true + None => { // No item selected, select the first one + table_state.select(Some(0)); + true // Navigation happened (selection set) } } } diff --git a/client/src/state/pages/add_table.rs b/client/src/state/pages/add_table.rs index 67d35df..9657d9c 100644 --- a/client/src/state/pages/add_table.rs +++ b/client/src/state/pages/add_table.rs @@ -12,6 +12,7 @@ pub struct ColumnDefinition { pub struct LinkDefinition { pub linked_table_name: String, pub is_required: bool, + pub selected: bool, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] @@ -25,6 +26,10 @@ pub enum AddTableFocus { ColumnsTable, IndexesTable, LinksTable, + // Inside Tables (Scrolling Focus) + InsideColumnsTable, + InsideIndexesTable, + InsideLinksTable, // Buttons SaveButton, DeleteSelectedButton,