373 lines
19 KiB
Rust
373 lines
19 KiB
Rust
// 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::<Vec<String>>()
|
|
.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)
|
|
}
|
|
}
|
|
}
|
|
|