160 lines
6.5 KiB
Rust
160 lines
6.5 KiB
Rust
// src/pages/admin_panel/add_logic/event.rs
|
|
|
|
use anyhow::Result;
|
|
use crate::config::binds::config::Config;
|
|
use crate::movement::{move_focus, MovementAction};
|
|
use crate::pages::admin_panel::add_logic::nav::SaveLogicResultSender;
|
|
use crate::pages::admin_panel::add_logic::state::{AddLogicFocus, AddLogicFormState};
|
|
use crate::components::common::text_editor::TextEditor;
|
|
use crate::services::grpc_client::GrpcClient;
|
|
use crate::state::app::state::AppState;
|
|
use crate::modes::handlers::event::EventOutcome;
|
|
use canvas::{AppMode as CanvasMode, DataProvider};
|
|
use crossterm::event::KeyEvent;
|
|
|
|
/// Focus traversal order for non-canvas navigation
|
|
const ADD_LOGIC_FOCUS_ORDER: [AddLogicFocus; 6] = [
|
|
AddLogicFocus::InputLogicName,
|
|
AddLogicFocus::InputTargetColumn,
|
|
AddLogicFocus::InputDescription,
|
|
AddLogicFocus::ScriptContentPreview,
|
|
AddLogicFocus::SaveButton,
|
|
AddLogicFocus::CancelButton,
|
|
];
|
|
|
|
/// Handles all AddLogic page-specific events.
|
|
/// Return a non-empty Ok(message) only when the page actually consumed the key,
|
|
/// otherwise return Ok("") to let global handling proceed.
|
|
pub fn handle_add_logic_event(
|
|
key_event: KeyEvent,
|
|
movement: Option<MovementAction>,
|
|
config: &Config,
|
|
app_state: &mut AppState,
|
|
add_logic_page: &mut AddLogicFormState,
|
|
grpc_client: GrpcClient,
|
|
save_logic_sender: SaveLogicResultSender,
|
|
) -> Result<EventOutcome> {
|
|
// 1) Script editor fullscreen mode
|
|
if add_logic_page.state.current_focus == AddLogicFocus::InsideScriptContent {
|
|
match key_event.code {
|
|
crossterm::event::KeyCode::Esc => {
|
|
add_logic_page.state.current_focus = AddLogicFocus::ScriptContentPreview;
|
|
add_logic_page.focus_outside_canvas = true;
|
|
return Ok(EventOutcome::Ok("Exited script editing.".to_string()));
|
|
}
|
|
_ => {
|
|
let changed = {
|
|
let mut editor_borrow =
|
|
add_logic_page.state.script_content_editor.borrow_mut();
|
|
TextEditor::handle_input(
|
|
&mut editor_borrow,
|
|
key_event,
|
|
&add_logic_page.state.editor_keybinding_mode,
|
|
&mut add_logic_page.state.vim_state,
|
|
)
|
|
};
|
|
if changed {
|
|
add_logic_page.state.has_unsaved_changes = true;
|
|
return Ok(EventOutcome::Ok("Script updated".to_string()));
|
|
}
|
|
return Ok(EventOutcome::Ok(String::new()));
|
|
}
|
|
}
|
|
}
|
|
|
|
// 2) Inside canvas: forward to FormEditor
|
|
let inside_canvas_inputs = matches!(
|
|
add_logic_page.state.current_focus,
|
|
AddLogicFocus::InputLogicName
|
|
| AddLogicFocus::InputTargetColumn
|
|
| AddLogicFocus::InputDescription
|
|
);
|
|
|
|
if inside_canvas_inputs {
|
|
// Only allow leaving the canvas with Down/Next when the form editor
|
|
// is in ReadOnly mode. In Edit mode, keep focus inside the canvas.
|
|
let in_edit_mode = add_logic_page.editor.mode() == CanvasMode::Edit;
|
|
if !in_edit_mode {
|
|
if let Some(ma) = movement {
|
|
let last_idx = add_logic_page
|
|
.editor
|
|
.data_provider()
|
|
.field_count()
|
|
.saturating_sub(1);
|
|
let at_last = add_logic_page.editor.current_field() >= last_idx;
|
|
if at_last && matches!(ma, MovementAction::Down | MovementAction::Next) {
|
|
add_logic_page.state.last_canvas_field = last_idx;
|
|
add_logic_page.state.current_focus = AddLogicFocus::ScriptContentPreview;
|
|
add_logic_page.focus_outside_canvas = true;
|
|
return Ok(EventOutcome::Ok("Moved to Script Preview".to_string()));
|
|
}
|
|
}
|
|
}
|
|
|
|
match add_logic_page.handle_key_event(key_event) {
|
|
canvas::keymap::KeyEventOutcome::Consumed(Some(msg)) => {
|
|
add_logic_page.sync_from_editor();
|
|
return Ok(EventOutcome::Ok(msg));
|
|
}
|
|
canvas::keymap::KeyEventOutcome::Consumed(None) => {
|
|
add_logic_page.sync_from_editor();
|
|
return Ok(EventOutcome::Ok("Input updated".into()));
|
|
}
|
|
canvas::keymap::KeyEventOutcome::Pending => {
|
|
return Ok(EventOutcome::Ok(String::new()));
|
|
}
|
|
canvas::keymap::KeyEventOutcome::NotMatched => {
|
|
// fall through
|
|
}
|
|
}
|
|
}
|
|
|
|
// 3) Outside canvas
|
|
if let Some(ma) = movement {
|
|
let mut current = add_logic_page.state.current_focus;
|
|
if move_focus(&ADD_LOGIC_FOCUS_ORDER, &mut current, ma) {
|
|
add_logic_page.state.current_focus = current;
|
|
add_logic_page.focus_outside_canvas = !matches!(
|
|
add_logic_page.state.current_focus,
|
|
AddLogicFocus::InputLogicName
|
|
| AddLogicFocus::InputTargetColumn
|
|
| AddLogicFocus::InputDescription
|
|
);
|
|
return Ok(EventOutcome::Ok(String::new()));
|
|
}
|
|
|
|
match ma {
|
|
MovementAction::Select => match add_logic_page.state.current_focus {
|
|
AddLogicFocus::ScriptContentPreview => {
|
|
add_logic_page.state.current_focus = AddLogicFocus::InsideScriptContent;
|
|
add_logic_page.focus_outside_canvas = false;
|
|
return Ok(EventOutcome::Ok(
|
|
"Fullscreen script editing. Esc to exit.".to_string(),
|
|
));
|
|
}
|
|
AddLogicFocus::SaveButton => {
|
|
if let Some(msg) = add_logic_page.state.save_logic() {
|
|
return Ok(EventOutcome::Ok(msg));
|
|
} else {
|
|
return Ok(EventOutcome::Ok("Saved (no changes)".to_string()));
|
|
}
|
|
}
|
|
AddLogicFocus::CancelButton => {
|
|
return Ok(EventOutcome::Ok("Cancelled Add Logic".to_string()));
|
|
}
|
|
_ => {}
|
|
},
|
|
MovementAction::Esc => {
|
|
if add_logic_page.state.current_focus == AddLogicFocus::ScriptContentPreview {
|
|
add_logic_page.state.current_focus = AddLogicFocus::InputDescription;
|
|
add_logic_page.focus_outside_canvas = false;
|
|
return Ok(EventOutcome::Ok("Back to Description".to_string()));
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
Ok(EventOutcome::Ok(String::new()))
|
|
}
|