368 lines
16 KiB
Rust
368 lines
16 KiB
Rust
// src/modes/handlers/event.rs
|
|
use crossterm::event::Event;
|
|
use crossterm::cursor::SetCursorStyle;
|
|
use crate::tui::terminal::core::TerminalCore;
|
|
use crate::services::grpc_client::GrpcClient;
|
|
use crate::services::auth::AuthClient;
|
|
use crate::modes::common::commands::CommandHandler;
|
|
use crate::config::binds::config::Config;
|
|
use crate::state::pages::form::FormState;
|
|
use crate::state::pages::auth::AuthState;
|
|
use crate::state::pages::auth::LoginState;
|
|
use crate::state::pages::auth::RegisterState;
|
|
use crate::state::pages::admin::AdminState;
|
|
use crate::state::app::state::AppState;
|
|
use crate::state::pages::canvas_state::CanvasState;
|
|
use crate::ui::handlers::rat_state::UiStateHandler;
|
|
use crate::ui::handlers::context::UiContext;
|
|
use crate::tui::functions::{intro, admin};
|
|
use crate::tui::functions::common::{login, register};
|
|
use crate::modes::{
|
|
common::command_mode,
|
|
canvas::{edit, read_only, common_mode},
|
|
general::{navigation, dialog},
|
|
};
|
|
use crate::config::binds::key_sequences::KeySequenceTracker;
|
|
use crate::modes::handlers::mode_manager::{ModeManager, AppMode};
|
|
use crate::tui::functions::common::form::SaveOutcome;
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
pub enum EventOutcome {
|
|
Ok(String),
|
|
Exit(String),
|
|
DataSaved(SaveOutcome, String),
|
|
ButtonSelected { context: UiContext, index: usize },
|
|
}
|
|
|
|
pub struct EventHandler {
|
|
pub command_mode: bool,
|
|
pub command_input: String,
|
|
pub command_message: String,
|
|
pub is_edit_mode: bool,
|
|
pub edit_mode_cooldown: bool,
|
|
pub ideal_cursor_column: usize,
|
|
pub key_sequence_tracker: KeySequenceTracker,
|
|
pub auth_client: AuthClient,
|
|
}
|
|
|
|
impl EventHandler {
|
|
pub async fn new() -> Result<Self, Box<dyn std::error::Error>> {
|
|
Ok(EventHandler {
|
|
command_mode: false,
|
|
command_input: String::new(),
|
|
command_message: String::new(),
|
|
is_edit_mode: false,
|
|
edit_mode_cooldown: false,
|
|
ideal_cursor_column: 0,
|
|
key_sequence_tracker: KeySequenceTracker::new(800),
|
|
auth_client: AuthClient::new().await?,
|
|
})
|
|
}
|
|
|
|
pub async fn handle_event(
|
|
&mut self,
|
|
event: Event,
|
|
config: &Config,
|
|
terminal: &mut TerminalCore,
|
|
grpc_client: &mut GrpcClient,
|
|
command_handler: &mut CommandHandler,
|
|
form_state: &mut FormState,
|
|
auth_state: &mut AuthState,
|
|
login_state: &mut LoginState,
|
|
register_state: &mut RegisterState,
|
|
admin_state: &mut AdminState,
|
|
app_state: &mut AppState,
|
|
total_count: u64,
|
|
current_position: &mut u64,
|
|
) -> Result<EventOutcome, Box<dyn std::error::Error>> {
|
|
let current_mode = ModeManager::derive_mode(app_state, self);
|
|
app_state.update_mode(current_mode);
|
|
|
|
// --- DIALOG MODALITY ---
|
|
if app_state.ui.dialog.dialog_show {
|
|
if let Some(dialog_result) = dialog::handle_dialog_event(
|
|
&event, config, app_state, auth_state, login_state, register_state
|
|
).await {
|
|
return dialog_result;
|
|
}
|
|
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;
|
|
|
|
if UiStateHandler::toggle_sidebar(&mut app_state.ui, config, key_code, modifiers) {
|
|
let message = format!("Sidebar {}",
|
|
if app_state.ui.show_sidebar { "shown" } else { "hidden" }
|
|
);
|
|
return Ok(EventOutcome::Ok(message));
|
|
}
|
|
|
|
match current_mode {
|
|
AppMode::General => {
|
|
let nav_outcome = navigation::handle_navigation_event(
|
|
key,
|
|
config,
|
|
form_state,
|
|
app_state,
|
|
login_state,
|
|
register_state,
|
|
admin_state,
|
|
&mut self.command_mode,
|
|
&mut self.command_input,
|
|
&mut self.command_message,
|
|
).await;
|
|
match nav_outcome {
|
|
Ok(EventOutcome::ButtonSelected { context, index }) => {
|
|
let mut message = String::from("Selected"); // Default message
|
|
match context {
|
|
UiContext::Intro => {
|
|
intro::handle_intro_selection(app_state, index);
|
|
if app_state.ui.show_admin {
|
|
let profile_names = app_state.profile_tree.profiles.iter()
|
|
.map(|p| p.name.clone())
|
|
.collect();
|
|
admin_state.set_profiles(profile_names);
|
|
}
|
|
message = format!("Intro Option {} selected", index);
|
|
}
|
|
UiContext::Login => {
|
|
message = match index {
|
|
0 => login::save(auth_state, login_state, &mut self.auth_client, app_state).await?,
|
|
1 => login::back_to_main(login_state, app_state).await,
|
|
_ => "Invalid Login Option".to_string(),
|
|
};
|
|
}
|
|
UiContext::Register => {
|
|
message = match index {
|
|
0 => register::save(register_state, &mut self.auth_client, app_state).await?,
|
|
1 => register::back_to_main(register_state, app_state).await,
|
|
_ => "Invalid Login Option".to_string(),
|
|
};
|
|
}
|
|
UiContext::Admin => {
|
|
admin::handle_admin_selection(app_state, admin_state);
|
|
|
|
message = format!("Admin Option {} selected", index);
|
|
}
|
|
UiContext::Dialog => {
|
|
message = "Internal error: Unexpected dialog state".to_string();
|
|
}
|
|
}
|
|
return Ok(EventOutcome::Ok(message)); // Return Ok with message
|
|
}
|
|
other => return other, // Pass through Ok, Err, DataSaved directly
|
|
}
|
|
},
|
|
|
|
AppMode::ReadOnly => {
|
|
if config.is_enter_edit_mode_before(key_code, modifiers) &&
|
|
ModeManager::can_enter_edit_mode(current_mode) {
|
|
self.is_edit_mode = true;
|
|
self.edit_mode_cooldown = true;
|
|
self.command_message = "Edit mode".to_string();
|
|
terminal.set_cursor_style(SetCursorStyle::BlinkingBar)?;
|
|
return Ok(EventOutcome::Ok(self.command_message.clone()));
|
|
}
|
|
|
|
if config.is_enter_edit_mode_after(key_code, modifiers) &&
|
|
ModeManager::can_enter_edit_mode(current_mode) {
|
|
let current_input = if app_state.ui.show_login || app_state.ui.show_register{
|
|
login_state.get_current_input()
|
|
} else {
|
|
form_state.get_current_input()
|
|
};
|
|
let current_cursor_pos = if app_state.ui.show_login || app_state.ui.show_register{
|
|
login_state.current_cursor_pos()
|
|
} else {
|
|
form_state.current_cursor_pos()
|
|
};
|
|
|
|
if !current_input.is_empty() && current_cursor_pos < current_input.len() {
|
|
if app_state.ui.show_login || app_state.ui.show_register{
|
|
login_state.set_current_cursor_pos(current_cursor_pos + 1);
|
|
self.ideal_cursor_column = login_state.current_cursor_pos();
|
|
} else {
|
|
form_state.set_current_cursor_pos(current_cursor_pos + 1);
|
|
self.ideal_cursor_column = form_state.current_cursor_pos();
|
|
}
|
|
}
|
|
self.is_edit_mode = true;
|
|
self.edit_mode_cooldown = true;
|
|
self.command_message = "Edit mode (after cursor)".to_string();
|
|
terminal.set_cursor_style(SetCursorStyle::BlinkingBar)?;
|
|
return Ok(EventOutcome::Ok(self.command_message.clone()));
|
|
}
|
|
|
|
if let Some(action) = config.get_read_only_action_for_key(key_code, modifiers) {
|
|
if action == "enter_command_mode" && ModeManager::can_enter_command_mode(current_mode) {
|
|
self.command_mode = true;
|
|
self.command_input.clear();
|
|
self.command_message.clear();
|
|
return Ok(EventOutcome::Ok(String::new()));
|
|
}
|
|
}
|
|
|
|
if let Some(action) = config.get_action_for_key_in_mode(
|
|
&config.keybindings.common,
|
|
key_code,
|
|
modifiers
|
|
) {
|
|
match action {
|
|
"save" | "force_quit" | "save_and_quit" | "revert" => {
|
|
return common_mode::handle_core_action(
|
|
action,
|
|
form_state,
|
|
auth_state,
|
|
login_state,
|
|
register_state,
|
|
grpc_client,
|
|
&mut self.auth_client,
|
|
terminal,
|
|
app_state,
|
|
current_position,
|
|
total_count,
|
|
).await;
|
|
},
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
let (_should_exit, message) = read_only::handle_read_only_event(
|
|
app_state,
|
|
key,
|
|
config,
|
|
form_state,
|
|
login_state,
|
|
register_state,
|
|
&mut self.key_sequence_tracker,
|
|
current_position,
|
|
total_count,
|
|
grpc_client,
|
|
&mut self.command_message,
|
|
&mut self.edit_mode_cooldown,
|
|
&mut self.ideal_cursor_column,
|
|
).await?;
|
|
return Ok(EventOutcome::Ok(message));
|
|
},
|
|
|
|
AppMode::Edit => {
|
|
if config.is_exit_edit_mode(key_code, modifiers) {
|
|
self.is_edit_mode = false;
|
|
self.edit_mode_cooldown = true;
|
|
|
|
let has_changes = if app_state.ui.show_login || app_state.ui.show_register{
|
|
login_state.has_unsaved_changes()
|
|
} else {
|
|
form_state.has_unsaved_changes()
|
|
};
|
|
|
|
self.command_message = if has_changes {
|
|
"Exited edit mode (unsaved changes remain)".to_string()
|
|
} else {
|
|
"Read-only mode".to_string()
|
|
};
|
|
|
|
terminal.set_cursor_style(SetCursorStyle::SteadyBlock)?;
|
|
|
|
let current_input = if app_state.ui.show_login || app_state.ui.show_register{
|
|
login_state.get_current_input()
|
|
} else {
|
|
form_state.get_current_input()
|
|
};
|
|
let current_cursor_pos = if app_state.ui.show_login || app_state.ui.show_register{
|
|
login_state.current_cursor_pos()
|
|
} else {
|
|
form_state.current_cursor_pos()
|
|
};
|
|
|
|
if !current_input.is_empty() && current_cursor_pos >= current_input.len() {
|
|
let new_pos = current_input.len() - 1;
|
|
if app_state.ui.show_login || app_state.ui.show_register{
|
|
login_state.set_current_cursor_pos(new_pos);
|
|
self.ideal_cursor_column = login_state.current_cursor_pos();
|
|
} else {
|
|
form_state.set_current_cursor_pos(new_pos);
|
|
self.ideal_cursor_column = form_state.current_cursor_pos();
|
|
}
|
|
}
|
|
return Ok(EventOutcome::Ok(self.command_message.clone()));
|
|
}
|
|
|
|
if let Some(action) = config.get_action_for_key_in_mode(
|
|
&config.keybindings.common,
|
|
key_code,
|
|
modifiers
|
|
) {
|
|
match action {
|
|
"save" | "force_quit" | "save_and_quit" | "revert" => {
|
|
return common_mode::handle_core_action(
|
|
action,
|
|
form_state,
|
|
auth_state,
|
|
login_state,
|
|
register_state,
|
|
grpc_client,
|
|
&mut self.auth_client,
|
|
terminal,
|
|
app_state,
|
|
current_position,
|
|
total_count,
|
|
).await;
|
|
},
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
let message = edit::handle_edit_event(
|
|
key,
|
|
config,
|
|
form_state,
|
|
login_state,
|
|
register_state,
|
|
&mut self.ideal_cursor_column,
|
|
&mut self.command_message,
|
|
current_position,
|
|
total_count,
|
|
grpc_client,
|
|
app_state,
|
|
).await?;
|
|
|
|
self.key_sequence_tracker.reset();
|
|
return Ok(EventOutcome::Ok(message));
|
|
},
|
|
|
|
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;
|
|
}
|
|
}
|
|
return Ok(outcome);
|
|
}
|
|
}
|
|
}
|
|
|
|
self.edit_mode_cooldown = false;
|
|
Ok(EventOutcome::Ok(self.command_message.clone()))
|
|
}
|
|
}
|