// src/functions/modes/navigation/add_table_nav.rs 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; use crate::tui::functions::common::add_table::handle_add_column_action; use crate::ui::handlers::context::DialogPurpose; /// Handles navigation events specifically for the Add Table view. /// Returns true if the event was handled, false otherwise. pub fn handle_add_table_navigation( key: KeyEvent, config: &Config, app_state: &mut AppState, add_table_state: &mut AddTableState, command_message: &mut String, ) -> bool { 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 let mut new_focus = current_focus; // Initialize new_focus match action.as_deref() { // --- Handle Exiting Table Scroll Mode --- Some("exit_table_scroll") => { match current_focus { AddTableFocus::InsideColumnsTable => { add_table_state.column_table_state.select(None); new_focus = AddTableFocus::ColumnsTable; *command_message = "Exited Columns Table".to_string(); } AddTableFocus::InsideIndexesTable => { add_table_state.index_table_state.select(None); new_focus = AddTableFocus::IndexesTable; *command_message = "Exited Indexes Table".to_string(); } AddTableFocus::InsideLinksTable => { add_table_state.link_table_state.select(None); new_focus = AddTableFocus::LinksTable; *command_message = "Exited Links Table".to_string(); } _ => { // Action triggered but not applicable in this focus state handled = false; } } // If handled (i.e., focus changed), handled remains true. // If not handled, handled becomes false. } // --- Vertical Navigation (Up/Down) --- Some("move_up") => { match current_focus { AddTableFocus::InputTableName => new_focus = AddTableFocus::CancelButton, AddTableFocus::InputColumnName => new_focus = AddTableFocus::InputTableName, AddTableFocus::InputColumnType => new_focus = AddTableFocus::InputColumnName, AddTableFocus::AddColumnButton => new_focus = AddTableFocus::InputColumnType, // 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::InsideIndexesTable => { navigate_table_up(&mut add_table_state.index_table_state, add_table_state.indexes.len()); // Stay inside the table } 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, AddTableFocus::DeleteSelectedButton => new_focus = AddTableFocus::SaveButton, AddTableFocus::CancelButton => new_focus = AddTableFocus::DeleteSelectedButton, } } Some("move_down") => { 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, // 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::InsideIndexesTable => { navigate_table_down(&mut add_table_state.index_table_state, add_table_state.indexes.len()); // Stay inside the table } 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, } } // --- Horizontal Navigation (Left/Right) --- Some("next_option") => { // 'l' or Right: Move from Left Pane to Right Pane // Horizontal nav within bottom buttons if current_focus == AddTableFocus::SaveButton { new_focus = AddTableFocus::DeleteSelectedButton; } else if current_focus == AddTableFocus::DeleteSelectedButton { new_focus = AddTableFocus::CancelButton; } } Some("previous_option") => { // 'h' or Left: Move from Right Pane to Left Pane // Horizontal nav within bottom buttons if current_focus == AddTableFocus::CancelButton { new_focus = AddTableFocus::DeleteSelectedButton; } else if current_focus == AddTableFocus::DeleteSelectedButton { new_focus = AddTableFocus::SaveButton; } } // --- Tab / Shift+Tab Navigation (Keep as vertical cycle) --- Some("next_field") => { // Tab new_focus = match current_focus { AddTableFocus::InputTableName => AddTableFocus::InputColumnName, AddTableFocus::InputColumnName => AddTableFocus::InputColumnType, AddTableFocus::InputColumnType => AddTableFocus::AddColumnButton, AddTableFocus::AddColumnButton => AddTableFocus::ColumnsTable, // 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 }; } Some("prev_field") => { // Shift+Tab new_focus = match current_focus { AddTableFocus::InputTableName => AddTableFocus::CancelButton, // Wrap AddTableFocus::InputColumnName => AddTableFocus::InputTableName, AddTableFocus::InputColumnType => AddTableFocus::InputColumnName, AddTableFocus::AddColumnButton => AddTableFocus::InputColumnType, // 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, }; } // --- 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 => { // Toggle selection when pressing select *inside* the columns table if let Some(index) = add_table_state.column_table_state.selected() { if let Some(col) = add_table_state.columns.get_mut(index) { col.selected = !col.selected; add_table_state.has_unsaved_changes = true; *command_message = format!( "Toggled selection for column: {} to {}", col.name, col.selected ); } } else { *command_message = "No column highlighted to toggle selection".to_string(); } } AddTableFocus::InsideIndexesTable => { // Select does nothing here anymore, only Esc exits. if let Some(index) = add_table_state.index_table_state.selected() { if let Some(idx_def) = add_table_state.indexes.get_mut(index) { idx_def.selected = !idx_def.selected; add_table_state.has_unsaved_changes = true; *command_message = format!( "Toggled selection for index: {} to {}", idx_def.name, idx_def.selected ); } else { *command_message = "Error: Selected index out of bounds".to_string(); } } else { *command_message = "No index selected (Press Esc to exit scroll mode)".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; } } AddTableFocus::SaveButton => { *command_message = "Action: Save Table (Not Implemented)".to_string(); // TODO: Implement logic } AddTableFocus::DeleteSelectedButton => { // --- Show Confirmation Dialog --- // Collect tuples of (index, name, type) for selected columns let columns_to_delete: Vec<(usize, String, String)> = add_table_state .columns .iter() .enumerate() // Get index along with the column .filter(|(_index, col)| col.selected) // Filter based on selection .map(|(index, col)| (index, col.name.clone(), col.data_type.clone())) // Map to (index, name, type) .collect(); if columns_to_delete.is_empty() { *command_message = "No columns selected for deletion.".to_string(); } else { // Format the message to include index, name, and type let column_details: String = columns_to_delete .iter() // Add 1 to index for 1-based numbering for user display .map(|(index, name, dtype)| format!("{}. {} ({})", index + 1, name, dtype)) .collect::>() .join("\n"); // Use the formatted column_details string in the message let message = format!( "Delete the following columns?\n\n{}", column_details ); let buttons = vec!["Confirm".to_string(), "Cancel".to_string()]; app_state.show_dialog( "Confirm Deletion", &message, buttons, DialogPurpose::ConfirmDeleteColumns, ); } } AddTableFocus::CancelButton => { *command_message = "Action: Cancel Add Table".to_string(); // TODO: Implement logic } _ => { // Input fields *command_message = format!("Select on {:?}", current_focus); handled = false; // Let main loop handle edit mode toggle maybe } } // Keep handled = true for select actions unless specifically set to false } // --- Other General Keys --- Some("toggle_sidebar") | Some("toggle_buffer_list") => { handled = false; } // --- No matching action --- _ => handled = false, } // Update focus state if it changed and was handled if handled && current_focus != new_focus { add_table_state.current_focus = new_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); } // 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 ); // Focus is outside canvas if it's not an input field app_state.ui.focus_outside_canvas = !new_is_canvas_input_focus; } else if !handled { // command_message.clear(); // Optional: Clear message if not handled here } 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 TableState, item_count: usize) -> bool { if item_count == 0 { return false; } let current_selection = table_state.selected(); match current_selection { Some(index) => { if index > 0 { table_state.select(Some(index - 1)); true // Navigation happened } else { false // Was at the top } } None => { // No item selected, select the last one table_state.select(Some(item_count - 1)); true // Navigation happened (selection set) } } } // 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 TableState, item_count: usize) -> bool { if item_count == 0 { return false; } let current_selection = table_state.selected(); match current_selection { Some(index) => { if index < item_count - 1 { table_state.select(Some(index + 1)); true // Navigation happened } else { false // Was at the bottom } } None => { // No item selected, select the first one table_state.select(Some(0)); true // Navigation happened (selection set) } } }