// 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, handle_save_table_action}; use crate::ui::handlers::context::DialogPurpose; use crate::services::GrpcClient; use tokio::sync::mpsc; use anyhow::Result; pub type SaveTableResultSender = mpsc::Sender>; 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 } else { false } } None => { table_state.select(Some(0)); true } } } 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 } else { false } } None => { table_state.select(Some(0)); true } } } pub fn handle_add_table_navigation( key: KeyEvent, config: &Config, app_state: &mut AppState, add_table_state: &mut AddTableState, grpc_client: GrpcClient, save_result_sender: SaveTableResultSender, 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; let mut new_focus = current_focus; if matches!(current_focus, AddTableFocus::InsideColumnsTable | AddTableFocus::InsideIndexesTable | AddTableFocus::InsideLinksTable) { if matches!(action.as_deref(), Some("next_option") | Some("previous_option")) { *command_message = "Press Esc to exit table item navigation first.".to_string(); return true; } } match action.as_deref() { 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(); // Minimal change: remove message } 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(); } _ => handled = false, } } Some("move_up") => { match current_focus { AddTableFocus::InputTableName => { // MINIMAL CHANGE: Do nothing, new_focus remains current_focus // *command_message = "At top of form.".to_string(); // Remove message } AddTableFocus::InputColumnName => new_focus = AddTableFocus::InputTableName, AddTableFocus::InputColumnType => new_focus = AddTableFocus::InputColumnName, AddTableFocus::AddColumnButton => new_focus = AddTableFocus::InputColumnType, AddTableFocus::ColumnsTable => new_focus = AddTableFocus::AddColumnButton, AddTableFocus::IndexesTable => new_focus = AddTableFocus::ColumnsTable, AddTableFocus::LinksTable => new_focus = AddTableFocus::IndexesTable, AddTableFocus::InsideColumnsTable => { navigate_table_up(&mut add_table_state.column_table_state, add_table_state.columns.len()); } AddTableFocus::InsideIndexesTable => { navigate_table_up(&mut add_table_state.index_table_state, add_table_state.indexes.len()); } AddTableFocus::InsideLinksTable => { navigate_table_up(&mut add_table_state.link_table_state, add_table_state.links.len()); } 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 => { add_table_state.last_canvas_field = 2; new_focus = AddTableFocus::AddColumnButton; }, AddTableFocus::AddColumnButton => new_focus = AddTableFocus::ColumnsTable, AddTableFocus::ColumnsTable => new_focus = AddTableFocus::IndexesTable, AddTableFocus::IndexesTable => new_focus = AddTableFocus::LinksTable, AddTableFocus::LinksTable => new_focus = AddTableFocus::SaveButton, AddTableFocus::InsideColumnsTable => { navigate_table_down(&mut add_table_state.column_table_state, add_table_state.columns.len()); } AddTableFocus::InsideIndexesTable => { navigate_table_down(&mut add_table_state.index_table_state, add_table_state.indexes.len()); } AddTableFocus::InsideLinksTable => { navigate_table_down(&mut add_table_state.link_table_state, add_table_state.links.len()); } AddTableFocus::SaveButton => new_focus = AddTableFocus::DeleteSelectedButton, AddTableFocus::DeleteSelectedButton => new_focus = AddTableFocus::CancelButton, AddTableFocus::CancelButton => { // MINIMAL CHANGE: Do nothing, new_focus remains current_focus // *command_message = "At bottom of form.".to_string(); // Remove message } } } Some("next_option") => { // This logic should already be non-wrapping match current_focus { AddTableFocus::InputTableName | AddTableFocus::InputColumnName | AddTableFocus::InputColumnType => { new_focus = AddTableFocus::AddColumnButton; } AddTableFocus::AddColumnButton => new_focus = AddTableFocus::ColumnsTable, AddTableFocus::ColumnsTable => new_focus = AddTableFocus::IndexesTable, AddTableFocus::IndexesTable => new_focus = AddTableFocus::LinksTable, AddTableFocus::LinksTable => new_focus = AddTableFocus::SaveButton, AddTableFocus::SaveButton => new_focus = AddTableFocus::DeleteSelectedButton, AddTableFocus::DeleteSelectedButton => new_focus = AddTableFocus::CancelButton, AddTableFocus::CancelButton => { /* *command_message = "At last focusable area.".to_string(); */ } // No change in focus _ => handled = false, } } Some("previous_option") => { // This logic should already be non-wrapping match current_focus { AddTableFocus::InputTableName | AddTableFocus::InputColumnName | AddTableFocus::InputColumnType => { /* *command_message = "At first focusable area.".to_string(); */ } // No change in focus AddTableFocus::AddColumnButton => new_focus = AddTableFocus::InputColumnType, AddTableFocus::ColumnsTable => new_focus = AddTableFocus::AddColumnButton, AddTableFocus::IndexesTable => new_focus = AddTableFocus::ColumnsTable, AddTableFocus::LinksTable => new_focus = AddTableFocus::IndexesTable, AddTableFocus::SaveButton => new_focus = AddTableFocus::LinksTable, AddTableFocus::DeleteSelectedButton => new_focus = AddTableFocus::SaveButton, AddTableFocus::CancelButton => new_focus = AddTableFocus::DeleteSelectedButton, _ => handled = false, } } Some("next_field") => { new_focus = match current_focus { AddTableFocus::InputTableName => AddTableFocus::InputColumnName, AddTableFocus::InputColumnName => AddTableFocus::InputColumnType, AddTableFocus::InputColumnType => AddTableFocus::AddColumnButton, AddTableFocus::AddColumnButton => AddTableFocus::ColumnsTable, 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, }; } Some("prev_field") => { new_focus = match current_focus { AddTableFocus::InputTableName => AddTableFocus::CancelButton, AddTableFocus::InputColumnName => AddTableFocus::InputTableName, AddTableFocus::InputColumnType => AddTableFocus::InputColumnName, AddTableFocus::AddColumnButton => AddTableFocus::InputColumnType, 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, }; } Some("select") => { match current_focus { AddTableFocus::ColumnsTable => { new_focus = AddTableFocus::InsideColumnsTable; 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)); } /* Message removed */ } 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)); } /* Message removed */ } 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)); } /* Message removed */ } AddTableFocus::InsideColumnsTable => { 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; /* Message removed */ }} /* else { Message removed } */ } AddTableFocus::InsideIndexesTable => { 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; /* Message removed */ }} /* else { Message removed } */ } AddTableFocus::InsideLinksTable => { 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; add_table_state.has_unsaved_changes = true; /* Message removed */ }} /* else { Message removed } */ } AddTableFocus::AddColumnButton => { if let Some(focus_after_add) = handle_add_column_action(add_table_state, command_message) { new_focus = focus_after_add; } else { /* Message already set by handle_add_column_action */ }} AddTableFocus::SaveButton => { if add_table_state.table_name.is_empty() { *command_message = "Cannot save: Table name is empty.".to_string(); } else if add_table_state.columns.is_empty() { *command_message = "Cannot save: No columns defined.".to_string(); } else { *command_message = "Saving table...".to_string(); app_state.show_loading_dialog("Saving", "Please wait..."); let mut client_clone = grpc_client.clone(); let state_clone = add_table_state.clone(); let sender_clone = save_result_sender.clone(); tokio::spawn(async move { let result = handle_save_table_action(&mut client_clone, &state_clone).await; let _ = sender_clone.send(result).await; }); }} AddTableFocus::DeleteSelectedButton => { let columns_to_delete: Vec<(usize, String, String)> = add_table_state.columns.iter().enumerate().filter(|(_, col)| col.selected).map(|(index, col)| (index, col.name.clone(), col.data_type.clone())).collect(); if columns_to_delete.is_empty() { *command_message = "No columns selected for deletion.".to_string(); } else { let column_details: String = columns_to_delete.iter().map(|(index, name, dtype)| format!("{}. {} ({})", index + 1, name, dtype)).collect::>().join("\n"); let message = format!("Delete the following columns?\n\n{}", column_details); app_state.show_dialog("Confirm Deletion", &message, vec!["Confirm".to_string(), "Cancel".to_string()], DialogPurpose::ConfirmDeleteColumns); }} AddTableFocus::CancelButton => { *command_message = "Action: Cancel Add Table (Not Implemented)".to_string(); } _ => { handled = false; } } } _ => handled = false, } if handled && current_focus != new_focus { add_table_state.current_focus = new_focus; // Minimal change: Command message update logic can be simplified or removed if not desired // For now, let's keep it minimal and only update if it was truly a focus change, // and not a boundary message. if !command_message.starts_with("At ") && current_focus != new_focus { // Avoid overwriting boundary messages // *command_message = format!("Focus: {:?}", add_table_state.current_focus); // Optional: restore if needed } let new_is_canvas_input_focus = matches!(new_focus, AddTableFocus::InputTableName | AddTableFocus::InputColumnName | AddTableFocus::InputColumnType ); app_state.ui.focus_outside_canvas = !new_is_canvas_input_focus; } // If not handled, command_message remains as it was (e.g., from a deeper function call or previous event) // or can be cleared if that's the desired default. For minimal change, we leave it. handled }