move out of canvas properly fixed, now working everyhing properly well
This commit is contained in:
@@ -332,6 +332,52 @@ impl EventHandler {
|
|||||||
if !outcome.get_message_if_ok().is_empty() {
|
if !outcome.get_message_if_ok().is_empty() {
|
||||||
return Ok(outcome);
|
return Ok(outcome);
|
||||||
}
|
}
|
||||||
|
} else if let Page::AddLogic(add_logic_page) = &mut router.current {
|
||||||
|
// Allow ":" (enter_command_mode) even when inside AddLogic canvas
|
||||||
|
if let Some(action) =
|
||||||
|
config.get_general_action(key_event.code, key_event.modifiers)
|
||||||
|
{
|
||||||
|
if action == "enter_command_mode"
|
||||||
|
&& !self.command_mode
|
||||||
|
&& !app_state.ui.show_search_palette
|
||||||
|
&& !self.navigation_state.active
|
||||||
|
{
|
||||||
|
self.command_mode = true;
|
||||||
|
self.command_input.clear();
|
||||||
|
self.command_message.clear();
|
||||||
|
self.key_sequence_tracker.reset();
|
||||||
|
app_state.ui.focus_outside_canvas = true;
|
||||||
|
return Ok(EventOutcome::Ok(String::new()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let movement_action_early = if let Some(act) =
|
||||||
|
config.get_general_action(key_event.code, key_event.modifiers)
|
||||||
|
{
|
||||||
|
match act {
|
||||||
|
"up" => Some(MovementAction::Up),
|
||||||
|
"down" => Some(MovementAction::Down),
|
||||||
|
"left" => Some(MovementAction::Left),
|
||||||
|
"right" => Some(MovementAction::Right),
|
||||||
|
"next" => Some(MovementAction::Next),
|
||||||
|
"previous" => Some(MovementAction::Previous),
|
||||||
|
"select" => Some(MovementAction::Select),
|
||||||
|
"esc" => Some(MovementAction::Esc),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
} else { None };
|
||||||
|
|
||||||
|
let outcome = add_logic::event::handle_add_logic_event(
|
||||||
|
key_event,
|
||||||
|
movement_action_early,
|
||||||
|
config,
|
||||||
|
app_state,
|
||||||
|
add_logic_page,
|
||||||
|
self.grpc_client.clone(),
|
||||||
|
self.save_logic_result_sender.clone(),
|
||||||
|
)?;
|
||||||
|
if !outcome.get_message_if_ok().is_empty() {
|
||||||
|
return Ok(outcome);
|
||||||
|
}
|
||||||
} else if let Page::Admin(admin_state) = &mut router.current {
|
} else if let Page::Admin(admin_state) = &mut router.current {
|
||||||
if matches!(auth_state.role, Some(UserRole::Admin)) {
|
if matches!(auth_state.role, Some(UserRole::Admin)) {
|
||||||
if let Event::Key(key_event) = event {
|
if let Event::Key(key_event) = event {
|
||||||
@@ -432,6 +478,22 @@ impl EventHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Allow ":" / ctrl+; to enter command mode only when outside canvas.
|
||||||
|
if action == "enter_command_mode" {
|
||||||
|
if app_state.ui.focus_outside_canvas
|
||||||
|
&& !self.command_mode
|
||||||
|
&& !app_state.ui.show_search_palette
|
||||||
|
&& !self.navigation_state.active
|
||||||
|
{
|
||||||
|
self.command_mode = true;
|
||||||
|
self.command_input.clear();
|
||||||
|
self.command_message.clear();
|
||||||
|
self.key_sequence_tracker.reset();
|
||||||
|
// Keep focus outside so canvas won't receive keys
|
||||||
|
app_state.ui.focus_outside_canvas = true;
|
||||||
|
return Ok(EventOutcome::Ok(String::new()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -485,22 +547,6 @@ impl EventHandler {
|
|||||||
return Ok(EventOutcome::Ok(self.command_message.clone()));
|
return Ok(EventOutcome::Ok(self.command_message.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Page::AddLogic(add_logic_page) = &mut router.current {
|
|
||||||
let client_clone = self.grpc_client.clone();
|
|
||||||
let sender_clone = self.save_logic_result_sender.clone();
|
|
||||||
if add_logic::event::handle_add_logic_event(
|
|
||||||
key_event,
|
|
||||||
movement_action,
|
|
||||||
config,
|
|
||||||
app_state,
|
|
||||||
add_logic_page,
|
|
||||||
client_clone,
|
|
||||||
sender_clone,
|
|
||||||
&mut self.command_message,
|
|
||||||
) {
|
|
||||||
return Ok(EventOutcome::Ok(self.command_message.clone()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generic navigation for the rest (Intro/Login/Register/Form)
|
// Generic navigation for the rest (Intro/Login/Register/Form)
|
||||||
let nav_outcome = if matches!(&router.current, Page::AddTable(_) | Page::AddLogic(_)) {
|
let nav_outcome = if matches!(&router.current, Page::AddTable(_) | Page::AddLogic(_)) {
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
// src/pages/admin_panel/add_logic/event.rs
|
// src/pages/admin_panel/add_logic/event.rs
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
use crate::config::binds::config::Config;
|
use crate::config::binds::config::Config;
|
||||||
use crate::movement::{move_focus, MovementAction};
|
use crate::movement::{move_focus, MovementAction};
|
||||||
use crate::pages::admin_panel::add_logic::nav::SaveLogicResultSender;
|
use crate::pages::admin_panel::add_logic::nav::SaveLogicResultSender;
|
||||||
@@ -6,7 +8,8 @@ use crate::pages::admin_panel::add_logic::state::{AddLogicFocus, AddLogicFormSta
|
|||||||
use crate::components::common::text_editor::TextEditor;
|
use crate::components::common::text_editor::TextEditor;
|
||||||
use crate::services::grpc_client::GrpcClient;
|
use crate::services::grpc_client::GrpcClient;
|
||||||
use crate::state::app::state::AppState;
|
use crate::state::app::state::AppState;
|
||||||
use canvas::DataProvider;
|
use crate::modes::handlers::event::EventOutcome;
|
||||||
|
use canvas::{AppMode as CanvasMode, DataProvider};
|
||||||
use crossterm::event::KeyEvent;
|
use crossterm::event::KeyEvent;
|
||||||
|
|
||||||
/// Focus traversal order for non-canvas navigation
|
/// Focus traversal order for non-canvas navigation
|
||||||
@@ -19,7 +22,9 @@ const ADD_LOGIC_FOCUS_ORDER: [AddLogicFocus; 6] = [
|
|||||||
AddLogicFocus::CancelButton,
|
AddLogicFocus::CancelButton,
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Return true if the event was handled and UI should be redrawn.
|
/// 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(
|
pub fn handle_add_logic_event(
|
||||||
key_event: KeyEvent,
|
key_event: KeyEvent,
|
||||||
movement: Option<MovementAction>,
|
movement: Option<MovementAction>,
|
||||||
@@ -28,16 +33,14 @@ pub fn handle_add_logic_event(
|
|||||||
add_logic_page: &mut AddLogicFormState,
|
add_logic_page: &mut AddLogicFormState,
|
||||||
grpc_client: GrpcClient,
|
grpc_client: GrpcClient,
|
||||||
save_logic_sender: SaveLogicResultSender,
|
save_logic_sender: SaveLogicResultSender,
|
||||||
command_message: &mut String,
|
) -> Result<EventOutcome> {
|
||||||
) -> bool {
|
|
||||||
// 1) Script editor fullscreen mode
|
// 1) Script editor fullscreen mode
|
||||||
if add_logic_page.state.current_focus == AddLogicFocus::InsideScriptContent {
|
if add_logic_page.state.current_focus == AddLogicFocus::InsideScriptContent {
|
||||||
match key_event.code {
|
match key_event.code {
|
||||||
crossterm::event::KeyCode::Esc => {
|
crossterm::event::KeyCode::Esc => {
|
||||||
add_logic_page.state.current_focus = AddLogicFocus::ScriptContentPreview;
|
add_logic_page.state.current_focus = AddLogicFocus::ScriptContentPreview;
|
||||||
app_state.ui.focus_outside_canvas = true;
|
app_state.ui.focus_outside_canvas = true;
|
||||||
*command_message = "Exited script editing.".to_string();
|
return Ok(EventOutcome::Ok("Exited script editing.".to_string()));
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let changed = {
|
let changed = {
|
||||||
@@ -52,9 +55,9 @@ pub fn handle_add_logic_event(
|
|||||||
};
|
};
|
||||||
if changed {
|
if changed {
|
||||||
add_logic_page.state.has_unsaved_changes = true;
|
add_logic_page.state.has_unsaved_changes = true;
|
||||||
*command_message = "Script updated".to_string();
|
return Ok(EventOutcome::Ok("Script updated".to_string()));
|
||||||
}
|
}
|
||||||
return true;
|
return Ok(EventOutcome::Ok(String::new()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -68,32 +71,41 @@ pub fn handle_add_logic_event(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if inside_canvas_inputs {
|
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 {
|
if let Some(ma) = movement {
|
||||||
let last_idx = add_logic_page.editor.data_provider().field_count().saturating_sub(1);
|
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;
|
let at_last = add_logic_page.editor.current_field() >= last_idx;
|
||||||
if at_last && matches!(ma, MovementAction::Down | MovementAction::Next) {
|
if at_last && matches!(ma, MovementAction::Down | MovementAction::Next) {
|
||||||
add_logic_page.state.last_canvas_field = last_idx;
|
add_logic_page.state.last_canvas_field = last_idx;
|
||||||
add_logic_page.state.current_focus = AddLogicFocus::ScriptContentPreview;
|
add_logic_page.state.current_focus = AddLogicFocus::ScriptContentPreview;
|
||||||
app_state.ui.focus_outside_canvas = true;
|
app_state.ui.focus_outside_canvas = true;
|
||||||
*command_message = "Moved to Script Preview".to_string();
|
return Ok(EventOutcome::Ok("Moved to Script Preview".to_string()));
|
||||||
return true;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match add_logic_page.handle_key_event(key_event) {
|
match add_logic_page.handle_key_event(key_event) {
|
||||||
canvas::keymap::KeyEventOutcome::Consumed(Some(msg)) => {
|
canvas::keymap::KeyEventOutcome::Consumed(Some(msg)) => {
|
||||||
add_logic_page.sync_from_editor();
|
add_logic_page.sync_from_editor();
|
||||||
if !msg.is_empty() {
|
return Ok(EventOutcome::Ok(msg));
|
||||||
*command_message = msg;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
canvas::keymap::KeyEventOutcome::Consumed(None) => {
|
canvas::keymap::KeyEventOutcome::Consumed(None) => {
|
||||||
add_logic_page.sync_from_editor();
|
add_logic_page.sync_from_editor();
|
||||||
return true;
|
return Ok(EventOutcome::Ok("Input updated".into()));
|
||||||
|
}
|
||||||
|
canvas::keymap::KeyEventOutcome::Pending => {
|
||||||
|
return Ok(EventOutcome::Ok(String::new()));
|
||||||
|
}
|
||||||
|
canvas::keymap::KeyEventOutcome::NotMatched => {
|
||||||
|
// fall through
|
||||||
}
|
}
|
||||||
canvas::keymap::KeyEventOutcome::Pending => return true,
|
|
||||||
canvas::keymap::KeyEventOutcome::NotMatched => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +120,7 @@ pub fn handle_add_logic_event(
|
|||||||
| AddLogicFocus::InputTargetColumn
|
| AddLogicFocus::InputTargetColumn
|
||||||
| AddLogicFocus::InputDescription
|
| AddLogicFocus::InputDescription
|
||||||
);
|
);
|
||||||
return true;
|
return Ok(EventOutcome::Ok(String::new()));
|
||||||
}
|
}
|
||||||
|
|
||||||
match ma {
|
match ma {
|
||||||
@@ -116,20 +128,19 @@ pub fn handle_add_logic_event(
|
|||||||
AddLogicFocus::ScriptContentPreview => {
|
AddLogicFocus::ScriptContentPreview => {
|
||||||
add_logic_page.state.current_focus = AddLogicFocus::InsideScriptContent;
|
add_logic_page.state.current_focus = AddLogicFocus::InsideScriptContent;
|
||||||
app_state.ui.focus_outside_canvas = false;
|
app_state.ui.focus_outside_canvas = false;
|
||||||
*command_message = "Fullscreen script editing. Esc to exit.".to_string();
|
return Ok(EventOutcome::Ok(
|
||||||
return true;
|
"Fullscreen script editing. Esc to exit.".to_string(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
AddLogicFocus::SaveButton => {
|
AddLogicFocus::SaveButton => {
|
||||||
if let Some(msg) = add_logic_page.state.save_logic() {
|
if let Some(msg) = add_logic_page.state.save_logic() {
|
||||||
*command_message = msg;
|
return Ok(EventOutcome::Ok(msg));
|
||||||
} else {
|
} else {
|
||||||
*command_message = "Saved (no changes)".to_string();
|
return Ok(EventOutcome::Ok("Saved (no changes)".to_string()));
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
AddLogicFocus::CancelButton => {
|
AddLogicFocus::CancelButton => {
|
||||||
*command_message = "Cancelled Add Logic".to_string();
|
return Ok(EventOutcome::Ok("Cancelled Add Logic".to_string()));
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
@@ -137,13 +148,12 @@ pub fn handle_add_logic_event(
|
|||||||
if add_logic_page.state.current_focus == AddLogicFocus::ScriptContentPreview {
|
if add_logic_page.state.current_focus == AddLogicFocus::ScriptContentPreview {
|
||||||
add_logic_page.state.current_focus = AddLogicFocus::InputDescription;
|
add_logic_page.state.current_focus = AddLogicFocus::InputDescription;
|
||||||
app_state.ui.focus_outside_canvas = false;
|
app_state.ui.focus_outside_canvas = false;
|
||||||
*command_message = "Back to Description".to_string();
|
return Ok(EventOutcome::Ok("Back to Description".to_string()));
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
false
|
Ok(EventOutcome::Ok(String::new()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ pub fn handle_login_event(
|
|||||||
if !login_page.focus_outside_canvas {
|
if !login_page.focus_outside_canvas {
|
||||||
let last_idx = login_page.editor.data_provider().field_count().saturating_sub(1);
|
let last_idx = login_page.editor.data_provider().field_count().saturating_sub(1);
|
||||||
let at_last = login_page.editor.current_field() >= last_idx;
|
let at_last = login_page.editor.current_field() >= last_idx;
|
||||||
if at_last
|
if login_page.editor.mode() == CanvasMode::ReadOnly
|
||||||
|
&& at_last
|
||||||
&& matches!(
|
&& matches!(
|
||||||
(key_code, modifiers),
|
(key_code, modifiers),
|
||||||
(KeyCode::Char('j'), KeyModifiers::NONE) | (KeyCode::Down, _)
|
(KeyCode::Char('j'), KeyModifiers::NONE) | (KeyCode::Down, _)
|
||||||
|
|||||||
@@ -38,7 +38,8 @@ pub fn handle_register_event(
|
|||||||
if !register_page.focus_outside_canvas {
|
if !register_page.focus_outside_canvas {
|
||||||
let last_idx = register_page.editor.data_provider().field_count().saturating_sub(1);
|
let last_idx = register_page.editor.data_provider().field_count().saturating_sub(1);
|
||||||
let at_last = register_page.editor.current_field() >= last_idx;
|
let at_last = register_page.editor.current_field() >= last_idx;
|
||||||
if at_last
|
if register_page.editor.mode() == CanvasMode::ReadOnly
|
||||||
|
&& at_last
|
||||||
&& matches!(
|
&& matches!(
|
||||||
(key_code, modifiers),
|
(key_code, modifiers),
|
||||||
(KeyCode::Char('j'), KeyModifiers::NONE) | (KeyCode::Down, _)
|
(KeyCode::Char('j'), KeyModifiers::NONE) | (KeyCode::Down, _)
|
||||||
|
|||||||
Reference in New Issue
Block a user