diff --git a/client/src/modes/handlers/event.rs b/client/src/modes/handlers/event.rs index 461042c..09b59ca 100644 --- a/client/src/modes/handlers/event.rs +++ b/client/src/modes/handlers/event.rs @@ -1,5 +1,6 @@ // src/modes/handlers/event.rs use crossterm::event::Event; +use crossterm::event::KeyCode; use crossterm::cursor::SetCursorStyle; use crate::tui::terminal::core::TerminalCore; use crate::services::grpc_client::GrpcClient; @@ -71,6 +72,56 @@ impl EventHandler { let current_mode = ModeManager::derive_mode(app_state, self); app_state.update_mode(current_mode); + // --- DIALOG MODALITY CHECK --- + // If a dialog is showing, intercept and handle ONLY dialog inputs. + if app_state.ui.dialog.dialog_show { + if let Event::Key(key) = event { + // Always allow Esc to dismiss + if key.code == KeyCode::Esc { + app_state.hide_dialog(); + return Ok(EventOutcome::Ok("Dialog dismissed".to_string())); + } + + // Check general bindings for dialog actions + if let Some(action) = config.get_general_action(key.code, key.modifiers) { + match action { + "next_option" => { + app_state.next_dialog_button(); + return Ok(EventOutcome::Ok(String::new())); // Consume event + } + "previous_option" => { + app_state.previous_dialog_button(); + return Ok(EventOutcome::Ok(String::new())); // Consume event + } + "select" => { + let selected_index = app_state.ui.dialog.dialog_active_button_index; + let selected_label = app_state.get_active_dialog_button_label().unwrap_or("").to_string(); + let mut message = format!("Dialog '{}' selected", selected_label); + + // --- Add specific actions based on button index or label --- + if selected_label.eq_ignore_ascii_case("menu") { + app_state.ui.show_login = false; + app_state.ui.show_intro = true; + // focus_outside_canvas is handled by hide_dialog + message = "Returning to menu".to_string(); + } else if selected_label.eq_ignore_ascii_case("exit") { + app_state.hide_dialog(); + return Ok(EventOutcome::Exit("Exiting via dialog".to_string())); + } + // --- End specific actions --- + app_state.hide_dialog(); // Hide dialog after processing selection + return Ok(EventOutcome::Ok(message)); // Consume event + } + _ => {} // Ignore other general actions when dialog is shown + } + } + } + // If dialog is shown, consume any event not handled above + return Ok(EventOutcome::Ok(String::new())); + } + // --- END DIALOG MODALITY CHECK --- + + if let Event::Key(key) = event { let key_code = key.code; let modifiers = key.modifiers; diff --git a/client/src/state/state.rs b/client/src/state/state.rs index d4893f9..b9f9b2a 100644 --- a/client/src/state/state.rs +++ b/client/src/state/state.rs @@ -89,16 +89,18 @@ impl AppState { self.ui.dialog.dialog_message = message.to_string(); self.ui.dialog.dialog_buttons = buttons; self.ui.dialog.dialog_active_button_index = 0; // Default to first button - self.ui.dialog.dialog_show = true; // Use new name + self.ui.dialog.dialog_show = true; + self.ui.focus_outside_canvas = true; } /// Hides the dialog and clears its content. pub fn hide_dialog(&mut self) { - self.ui.dialog.dialog_show = false; // Use new name + self.ui.dialog.dialog_show = false; self.ui.dialog.dialog_title.clear(); self.ui.dialog.dialog_message.clear(); self.ui.dialog.dialog_buttons.clear(); self.ui.dialog.dialog_active_button_index = 0; + self.ui.focus_outside_canvas = false; } /// Sets the active button index, wrapping around if necessary.