open menu in command mode now implemented

This commit is contained in:
filipriec
2025-05-28 19:09:55 +02:00
parent f77c16dec9
commit 799d8471c9
6 changed files with 336 additions and 214 deletions

View File

@@ -43,6 +43,7 @@ use crate::tui::functions::common::register::RegisterResult;
use crate::functions::modes::navigation::add_table_nav::SaveTableResultSender;
use crate::functions::modes::navigation::add_logic_nav::SaveLogicResultSender;
use crate::functions::modes::navigation::add_logic_nav;
use crossterm::event::{KeyCode, KeyModifiers};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum EventOutcome {
@@ -66,6 +67,10 @@ pub struct EventHandler {
pub register_result_sender: mpsc::Sender<RegisterResult>,
pub save_table_result_sender: SaveTableResultSender,
pub save_logic_result_sender: SaveLogicResultSender,
pub find_file_palette_active: bool,
pub find_file_options: Vec<String>,
pub find_file_selected_index: Option<usize>,
pub find_file_input: String,
}
impl EventHandler {
@@ -83,15 +88,30 @@ impl EventHandler {
highlight_state: HighlightState::Off,
edit_mode_cooldown: false,
ideal_cursor_column: 0,
key_sequence_tracker: KeySequenceTracker::new(800),
key_sequence_tracker: KeySequenceTracker::new(400),
auth_client: AuthClient::new().await?,
login_result_sender,
register_result_sender,
save_table_result_sender,
save_logic_result_sender,
find_file_palette_active: false,
find_file_options: vec![
"src/main.rs".to_string(),
"src/lib.rs".to_string(),
"Cargo.toml".to_string(),
"README.md".to_string(),
"config.toml".to_string(),
"src/ui/handlers/ui.rs".to_string(),
"src/modes/handlers/event.rs".to_string(),
"another_file.txt".to_string(),
"yet_another_one.md".to_string(),
],
find_file_selected_index: None,
find_file_input: String::new(),
})
}
#[allow(clippy::too_many_arguments)]
pub async fn handle_event(
&mut self,
event: Event,
@@ -110,6 +130,25 @@ impl EventHandler {
total_count: u64,
current_position: &mut u64,
) -> Result<EventOutcome> {
// Handle find file palette first
if self.find_file_palette_active {
if let Event::Key(key) = event {
if key.code == KeyCode::Esc {
self.find_file_palette_active = false;
self.find_file_input.clear();
self.find_file_selected_index = None;
self.command_message = "Find File palette closed".to_string();
return Ok(EventOutcome::Ok(self.command_message.clone()));
}
if let KeyCode::Char(c) = key.code {
self.find_file_input.push(c);
} else if key.code == KeyCode::Backspace {
self.find_file_input.pop();
}
return Ok(EventOutcome::Ok("Palette event consumed".to_string()));
}
}
let current_mode = ModeManager::derive_mode(app_state, self, admin_state);
app_state.update_mode(current_mode);
@@ -121,7 +160,7 @@ impl EventHandler {
else if ui.show_admin { AppView::Admin }
else if ui.show_add_logic { AppView::AddLogic }
else if ui.show_add_table { AppView::AddTable }
else if ui.show_form { AppView::Form } // Remove the dynamic name part
else if ui.show_form { AppView::Form }
else { AppView::Scratch }
};
buffer_state.update_history(current_view);
@@ -158,7 +197,6 @@ impl EventHandler {
return Ok(EventOutcome::Ok(message));
}
if !matches!(current_mode, AppMode::Edit | AppMode::Command) {
if let Some(action) = config.get_action_for_key_in_mode(
&config.keybindings.global, key_code, modifiers
@@ -174,13 +212,8 @@ impl EventHandler {
return Ok(EventOutcome::Ok("Switched to previous buffer".to_string()));
}
}
//"close_buffer" => {
// let message = buffer_state.close_buffer_with_intro_fallback();
// return Ok(EventOutcome::Ok(message));
//}
"close_buffer" => {
// TODO: Replace with actual table name from server response
let current_table_name = Some("2025_customer"); // Your hardcoded table name
let current_table_name = Some("2025_customer");
let message = buffer_state.close_buffer_with_intro_fallback(current_table_name);
return Ok(EventOutcome::Ok(message));
}
@@ -191,7 +224,6 @@ impl EventHandler {
match current_mode {
AppMode::General => {
// Prioritize Admin Panel navigation if it's visible
if app_state.ui.show_admin
&& auth_state.role.as_deref() == Some("admin") {
if admin_nav::handle_admin_navigation(
@@ -205,7 +237,7 @@ impl EventHandler {
return Ok(EventOutcome::Ok(self.command_message.clone()));
}
}
// --- Add Logic Page Navigation ---
if app_state.ui.show_add_logic {
let client_clone = grpc_client.clone();
let sender_clone = self.save_logic_result_sender.clone();
@@ -225,7 +257,6 @@ impl EventHandler {
}
}
// --- Add Table Page Navigation ---
if app_state.ui.show_add_table {
let client_clone = grpc_client.clone();
let sender_clone = self.save_table_result_sender.clone();
@@ -238,7 +269,6 @@ impl EventHandler {
client_clone,
sender_clone,
&mut self.command_message,
) {
return Ok(EventOutcome::Ok(self.command_message.clone()));
}
@@ -296,7 +326,7 @@ impl EventHandler {
UiContext::Dialog => {
"Internal error: Unexpected dialog state".to_string()
}
}; // Semicolon added here
};
return Ok(EventOutcome::Ok(message));
}
other => return other,
@@ -304,7 +334,6 @@ impl EventHandler {
},
AppMode::ReadOnly => {
// Check for Linewise highlight first
if config.get_read_only_action_for_key(key_code, modifiers) == Some("enter_highlight_mode_linewise")
&& ModeManager::can_enter_highlight_mode(current_mode)
{
@@ -315,7 +344,6 @@ impl EventHandler {
self.command_message = "-- LINE HIGHLIGHT --".to_string();
return Ok(EventOutcome::Ok(self.command_message.clone()));
}
// Check for Character-wise highlight
else if config.get_read_only_action_for_key(key_code, modifiers) == Some("enter_highlight_mode")
&& ModeManager::can_enter_highlight_mode(current_mode)
{
@@ -330,7 +358,6 @@ impl EventHandler {
self.command_message = "-- HIGHLIGHT --".to_string();
return Ok(EventOutcome::Ok(self.command_message.clone()));
}
// Check for entering edit mode (before cursor)
else if config.get_read_only_action_for_key(key_code, modifiers).as_deref() == Some("enter_edit_mode_before")
&& ModeManager::can_enter_edit_mode(current_mode) {
self.is_edit_mode = true;
@@ -339,7 +366,6 @@ impl EventHandler {
terminal.set_cursor_style(SetCursorStyle::BlinkingBar)?;
return Ok(EventOutcome::Ok(self.command_message.clone()));
}
// Check for entering edit mode (after cursor)
else if config.get_read_only_action_for_key(key_code, modifiers).as_deref() == Some("enter_edit_mode_after")
&& ModeManager::can_enter_edit_mode(current_mode) {
let current_input = if app_state.ui.show_login || app_state.ui.show_register{
@@ -369,7 +395,6 @@ impl EventHandler {
terminal.set_cursor_style(SetCursorStyle::BlinkingBar)?;
return Ok(EventOutcome::Ok(self.command_message.clone()));
}
// Check for entering command mode
else if config.get_read_only_action_for_key(key_code, modifiers) == Some("enter_command_mode")
&& ModeManager::can_enter_command_mode(current_mode) {
self.command_mode = true;
@@ -378,7 +403,6 @@ impl EventHandler {
return Ok(EventOutcome::Ok(String::new()));
}
// Check for common actions (save, quit, etc.) only if no mode change happened
if let Some(action) = config.get_common_action(key_code, modifiers) {
match action {
"save" | "force_quit" | "save_and_quit" | "revert" => {
@@ -400,7 +424,6 @@ impl EventHandler {
}
}
// If no mode change or specific common action handled, delegate to read_only handler
let (_should_exit, message) = read_only::handle_read_only_event(
app_state,
key,
@@ -418,22 +441,17 @@ impl EventHandler {
&mut self.edit_mode_cooldown,
&mut self.ideal_cursor_column,
).await?;
// Note: handle_read_only_event should ignore mode entry keys internally now
return Ok(EventOutcome::Ok(message));
}, // End AppMode::ReadOnly
},
AppMode::Highlight => {
// --- Handle Highlight Mode Specific Keys ---
// 1. Check for Exit first
if config.get_highlight_action_for_key(key_code, modifiers) == Some("exit_highlight_mode") {
self.highlight_state = HighlightState::Off;
self.command_message = "Exited highlight mode".to_string();
terminal.set_cursor_style(SetCursorStyle::SteadyBlock)?;
return Ok(EventOutcome::Ok(self.command_message.clone()));
}
// 2. Check for Switch to Linewise
else if config.get_highlight_action_for_key(key_code, modifiers) == Some("enter_highlight_mode_linewise") {
// Only switch if currently characterwise
if let HighlightState::Characterwise { anchor } = self.highlight_state {
self.highlight_state = HighlightState::Linewise { anchor_line: anchor.0 };
self.command_message = "-- LINE HIGHLIGHT --".to_string();
@@ -444,14 +462,14 @@ impl EventHandler {
let (_should_exit, message) = read_only::handle_read_only_event(
app_state, key, config, form_state, login_state,
register_state,
&mut admin_state.add_table_state,
register_state,
&mut admin_state.add_table_state,
&mut admin_state.add_logic_state,
&mut self.key_sequence_tracker,
current_position,
total_count,
current_position,
total_count,
grpc_client,
&mut self.command_message,
&mut self.command_message,
&mut self.edit_mode_cooldown,
&mut self.ideal_cursor_column,
)
@@ -460,14 +478,9 @@ impl EventHandler {
}
AppMode::Edit => {
// First, check for common actions (save, revert, etc.) that apply in Edit mode
// These might take precedence or have different behavior than the edit handler
if let Some(action) = config.get_common_action(key_code, modifiers) {
// Handle common actions like save, revert, force_quit, save_and_quit
// Ensure these actions return EventOutcome directly if they might exit the app
match action {
"save" | "force_quit" | "save_and_quit" | "revert" => {
// This call likely returns EventOutcome, handle it directly
return common_mode::handle_core_action(
action,
form_state,
@@ -482,16 +495,10 @@ impl EventHandler {
total_count,
).await;
},
// Handle other common actions if necessary
_ => {}
}
// If a common action was handled but didn't return/exit,
// we might want to stop further processing for this key event.
// Depending on the action, you might return Ok(EventOutcome::Ok(...)) here.
// For now, assume common actions either exit or don't prevent further processing.
}
// If no common action took precedence, delegate to the edit-specific handler
let edit_result = edit::handle_edit_event(
key,
config,
@@ -508,7 +515,6 @@ impl EventHandler {
match edit_result {
Ok(edit::EditEventOutcome::ExitEditMode) => {
// The edit handler signaled to exit the mode
self.is_edit_mode = false;
self.edit_mode_cooldown = true;
let has_changes = if app_state.ui.show_login { login_state.has_unsaved_changes() }
@@ -520,7 +526,6 @@ impl EventHandler {
"Read-only mode".to_string()
};
terminal.set_cursor_style(SetCursorStyle::SteadyBlock)?;
// Adjust cursor position if needed
let current_input = if app_state.ui.show_login { login_state.get_current_input() }
else if app_state.ui.show_register { register_state.get_current_input() }
else { form_state.get_current_input() };
@@ -536,43 +541,91 @@ impl EventHandler {
return Ok(EventOutcome::Ok(self.command_message.clone()));
}
Ok(edit::EditEventOutcome::Message(msg)) => {
// Stay in edit mode, update message if not empty
if !msg.is_empty() {
self.command_message = msg;
}
self.key_sequence_tracker.reset(); // Reset sequence tracker on successful edit action
self.key_sequence_tracker.reset();
return Ok(EventOutcome::Ok(self.command_message.clone()));
}
Err(e) => {
// Handle error from the edit handler
return Err(e.into());
}
}
}, // End AppMode::Edit
},
AppMode::Command => {
let outcome = command_mode::handle_command_event(
key,
config,
app_state,
login_state,
register_state,
form_state,
&mut self.command_input,
&mut self.command_message,
grpc_client,
command_handler,
terminal,
current_position,
total_count,
).await?;
if let EventOutcome::Ok(msg) = &outcome {
if msg == "Exited command mode" {
self.command_mode = false;
}
// Handle immediate command mode actions
if config.is_exit_command_mode(key_code, modifiers) {
self.command_input.clear();
self.command_message.clear();
self.command_mode = false;
self.key_sequence_tracker.reset();
return Ok(EventOutcome::Ok("Exited command mode".to_string()));
}
return Ok(outcome);
if config.is_command_execute(key_code, modifiers) {
let outcome = command_mode::handle_command_event(
key, config, app_state, login_state, register_state, form_state,
&mut self.command_input, &mut self.command_message,
grpc_client, command_handler, terminal, current_position, total_count,
).await?;
self.command_mode = false;
self.key_sequence_tracker.reset();
return Ok(outcome);
}
// Handle backspace
if key_code == KeyCode::Backspace {
self.command_input.pop();
self.key_sequence_tracker.reset();
return Ok(EventOutcome::Ok(String::new()));
}
// Handle character input and sequences
if let KeyCode::Char(c) = key_code {
if c == 'f' {
self.key_sequence_tracker.add_key(key_code);
let sequence = self.key_sequence_tracker.get_sequence();
if config.matches_key_sequence_generalized(&sequence) == Some("find_file_palette_toggle") {
if app_state.ui.show_form || app_state.ui.show_intro {
self.find_file_palette_active = true;
self.find_file_input.clear();
self.find_file_selected_index = if self.find_file_options.is_empty() {
None
} else {
Some(0)
};
self.command_mode = false;
self.command_input.clear();
self.command_message = "Find File:".to_string();
self.key_sequence_tracker.reset();
return Ok(EventOutcome::Ok("Find File palette activated".to_string()));
} else {
self.key_sequence_tracker.reset();
self.command_input.push('f');
self.command_input.push('f');
self.command_message = "Find File not available in this view.".to_string();
return Ok(EventOutcome::Ok(self.command_message.clone()));
}
}
if config.is_key_sequence_prefix(&sequence) {
return Ok(EventOutcome::Ok(String::new()));
}
}
if c != 'f' && !self.key_sequence_tracker.current_sequence.is_empty() {
self.key_sequence_tracker.reset();
}
self.command_input.push(c);
return Ok(EventOutcome::Ok(String::new()));
}
// Reset tracker for other keys
self.key_sequence_tracker.reset();
return Ok(EventOutcome::Ok(String::new()));
}
}
}
@@ -580,4 +633,8 @@ impl EventHandler {
self.edit_mode_cooldown = false;
Ok(EventOutcome::Ok(self.command_message.clone()))
}
fn is_processed_command(&self, command: &str) -> bool {
matches!(command, "w" | "q" | "q!" | "wq" | "r")
}
}

View File

@@ -23,6 +23,10 @@ impl ModeManager {
event_handler: &EventHandler,
admin_state: &AdminState,
) -> AppMode {
if event_handler.find_file_palette_active {
return AppMode::General;
}
if event_handler.command_mode {
return AppMode::Command;
}