read only deleted completely
This commit is contained in:
@@ -1,9 +1,7 @@
|
|||||||
// src/functions/modes.rs
|
// src/functions/modes.rs
|
||||||
|
|
||||||
pub mod read_only;
|
|
||||||
pub mod edit;
|
pub mod edit;
|
||||||
pub mod navigation;
|
pub mod navigation;
|
||||||
|
|
||||||
pub use read_only::*;
|
|
||||||
pub use edit::*;
|
pub use edit::*;
|
||||||
pub use navigation::*;
|
pub use navigation::*;
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
// src/functions/modes/read_only.rs
|
|
||||||
|
|
||||||
pub mod add_table_ro;
|
|
||||||
pub mod add_logic_ro;
|
|
||||||
@@ -1,235 +0,0 @@
|
|||||||
// src/functions/modes/read_only/add_logic_ro.rs
|
|
||||||
use crate::config::binds::key_sequences::KeySequenceTracker;
|
|
||||||
use crate::state::pages::add_logic::AddLogicState; // Changed
|
|
||||||
use crate::state::app::state::AppState;
|
|
||||||
use canvas::canvas::CanvasState;
|
|
||||||
use anyhow::Result;
|
|
||||||
|
|
||||||
// Word navigation helpers (get_char_type, find_next_word_start, etc.)
|
|
||||||
// can be kept as they are generic.
|
|
||||||
#[derive(PartialEq)]
|
|
||||||
enum CharType {
|
|
||||||
Whitespace,
|
|
||||||
Alphanumeric,
|
|
||||||
Punctuation,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_char_type(c: char) -> CharType {
|
|
||||||
if c.is_whitespace() { CharType::Whitespace }
|
|
||||||
else if c.is_alphanumeric() { CharType::Alphanumeric }
|
|
||||||
else { CharType::Punctuation }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_next_word_start(text: &str, current_pos: usize) -> usize {
|
|
||||||
let chars: Vec<char> = text.chars().collect();
|
|
||||||
let len = chars.len();
|
|
||||||
if len == 0 || current_pos >= len { return len; }
|
|
||||||
let mut pos = current_pos;
|
|
||||||
let initial_type = get_char_type(chars[pos]);
|
|
||||||
while pos < len && get_char_type(chars[pos]) == initial_type { pos += 1; }
|
|
||||||
while pos < len && get_char_type(chars[pos]) == CharType::Whitespace { pos += 1; }
|
|
||||||
pos
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_word_end(text: &str, current_pos: usize) -> usize {
|
|
||||||
let chars: Vec<char> = text.chars().collect();
|
|
||||||
let len = chars.len();
|
|
||||||
if len == 0 { return 0; }
|
|
||||||
let mut pos = current_pos.min(len - 1);
|
|
||||||
if get_char_type(chars[pos]) == CharType::Whitespace {
|
|
||||||
pos = find_next_word_start(text, pos);
|
|
||||||
}
|
|
||||||
if pos >= len { return len.saturating_sub(1); }
|
|
||||||
let word_type = get_char_type(chars[pos]);
|
|
||||||
while pos < len && get_char_type(chars[pos]) == word_type { pos += 1; }
|
|
||||||
pos.saturating_sub(1).min(len.saturating_sub(1))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_prev_word_start(text: &str, current_pos: usize) -> usize {
|
|
||||||
let chars: Vec<char> = text.chars().collect();
|
|
||||||
if chars.is_empty() || current_pos == 0 { return 0; }
|
|
||||||
let mut pos = current_pos.saturating_sub(1);
|
|
||||||
while pos > 0 && get_char_type(chars[pos]) == CharType::Whitespace { pos -= 1; }
|
|
||||||
if pos == 0 && get_char_type(chars[pos]) == CharType::Whitespace { return 0; }
|
|
||||||
let word_type = get_char_type(chars[pos]);
|
|
||||||
while pos > 0 && get_char_type(chars[pos - 1]) == word_type { pos -= 1; }
|
|
||||||
pos
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_prev_word_end(text: &str, current_pos: usize) -> usize {
|
|
||||||
let prev_start = find_prev_word_start(text, current_pos);
|
|
||||||
if prev_start == 0 { return 0; }
|
|
||||||
find_word_end(text, prev_start.saturating_sub(1))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Executes read-only actions for the AddLogic view canvas.
|
|
||||||
pub async fn execute_action(
|
|
||||||
action: &str,
|
|
||||||
app_state: &mut AppState,
|
|
||||||
state: &mut AddLogicState,
|
|
||||||
ideal_cursor_column: &mut usize,
|
|
||||||
key_sequence_tracker: &mut KeySequenceTracker,
|
|
||||||
command_message: &mut String,
|
|
||||||
) -> Result<String> {
|
|
||||||
match action {
|
|
||||||
"move_up" => {
|
|
||||||
key_sequence_tracker.reset();
|
|
||||||
let num_fields = AddLogicState::INPUT_FIELD_COUNT;
|
|
||||||
if num_fields == 0 { return Ok("No fields.".to_string()); }
|
|
||||||
let current_field = state.current_field();
|
|
||||||
|
|
||||||
if current_field > 0 {
|
|
||||||
let new_field = current_field - 1;
|
|
||||||
state.set_current_field(new_field);
|
|
||||||
let current_input = state.get_current_input();
|
|
||||||
let max_cursor_pos = if current_input.is_empty() { 0 } else { current_input.len().saturating_sub(1) };
|
|
||||||
let new_pos = (*ideal_cursor_column).min(max_cursor_pos);
|
|
||||||
state.set_current_cursor_pos(new_pos);
|
|
||||||
} else {
|
|
||||||
*command_message = "At top of form.".to_string();
|
|
||||||
}
|
|
||||||
Ok(command_message.clone())
|
|
||||||
}
|
|
||||||
"move_down" => {
|
|
||||||
key_sequence_tracker.reset();
|
|
||||||
let num_fields = AddLogicState::INPUT_FIELD_COUNT;
|
|
||||||
if num_fields == 0 { return Ok("No fields.".to_string()); }
|
|
||||||
let current_field = state.current_field();
|
|
||||||
let last_field_index = num_fields - 1;
|
|
||||||
|
|
||||||
if current_field < last_field_index {
|
|
||||||
let new_field = current_field + 1;
|
|
||||||
state.set_current_field(new_field);
|
|
||||||
let current_input = state.get_current_input();
|
|
||||||
let max_cursor_pos = if current_input.is_empty() { 0 } else { current_input.len().saturating_sub(1) };
|
|
||||||
let new_pos = (*ideal_cursor_column).min(max_cursor_pos);
|
|
||||||
state.set_current_cursor_pos(new_pos);
|
|
||||||
} else {
|
|
||||||
// Move focus outside canvas when moving down from the last field
|
|
||||||
// FIX: Go to ScriptContentPreview instead of SaveButton
|
|
||||||
app_state.ui.focus_outside_canvas = true;
|
|
||||||
state.last_canvas_field = 2;
|
|
||||||
state.current_focus = crate::state::pages::add_logic::AddLogicFocus::ScriptContentPreview; // FIXED!
|
|
||||||
*command_message = "Focus moved to script preview".to_string();
|
|
||||||
}
|
|
||||||
Ok(command_message.clone())
|
|
||||||
}
|
|
||||||
// ... (rest of the actions remain the same) ...
|
|
||||||
"move_first_line" => {
|
|
||||||
key_sequence_tracker.reset();
|
|
||||||
if AddLogicState::INPUT_FIELD_COUNT > 0 {
|
|
||||||
state.set_current_field(0);
|
|
||||||
let current_input = state.get_current_input();
|
|
||||||
let max_cursor_pos = if current_input.is_empty() { 0 } else { current_input.len().saturating_sub(1) };
|
|
||||||
let new_pos = (*ideal_cursor_column).min(max_cursor_pos);
|
|
||||||
state.set_current_cursor_pos(new_pos);
|
|
||||||
*ideal_cursor_column = new_pos;
|
|
||||||
}
|
|
||||||
Ok("".to_string())
|
|
||||||
}
|
|
||||||
"move_last_line" => {
|
|
||||||
key_sequence_tracker.reset();
|
|
||||||
let num_fields = AddLogicState::INPUT_FIELD_COUNT;
|
|
||||||
if num_fields > 0 {
|
|
||||||
let last_field_index = num_fields - 1;
|
|
||||||
state.set_current_field(last_field_index);
|
|
||||||
let current_input = state.get_current_input();
|
|
||||||
let max_cursor_pos = if current_input.is_empty() { 0 } else { current_input.len().saturating_sub(1) };
|
|
||||||
let new_pos = (*ideal_cursor_column).min(max_cursor_pos);
|
|
||||||
state.set_current_cursor_pos(new_pos);
|
|
||||||
*ideal_cursor_column = new_pos;
|
|
||||||
}
|
|
||||||
Ok("".to_string())
|
|
||||||
}
|
|
||||||
"move_left" => {
|
|
||||||
let current_pos = state.current_cursor_pos();
|
|
||||||
let new_pos = current_pos.saturating_sub(1);
|
|
||||||
state.set_current_cursor_pos(new_pos);
|
|
||||||
*ideal_cursor_column = new_pos;
|
|
||||||
Ok("".to_string())
|
|
||||||
}
|
|
||||||
"move_right" => {
|
|
||||||
let current_input = state.get_current_input();
|
|
||||||
let current_pos = state.current_cursor_pos();
|
|
||||||
if !current_input.is_empty() && current_pos < current_input.len().saturating_sub(1) {
|
|
||||||
let new_pos = current_pos + 1;
|
|
||||||
state.set_current_cursor_pos(new_pos);
|
|
||||||
*ideal_cursor_column = new_pos;
|
|
||||||
}
|
|
||||||
Ok("".to_string())
|
|
||||||
}
|
|
||||||
"move_word_next" => {
|
|
||||||
let current_input = state.get_current_input();
|
|
||||||
if !current_input.is_empty() {
|
|
||||||
let new_pos = find_next_word_start(current_input, state.current_cursor_pos());
|
|
||||||
let final_pos = new_pos.min(current_input.len().saturating_sub(1));
|
|
||||||
state.set_current_cursor_pos(final_pos);
|
|
||||||
*ideal_cursor_column = final_pos;
|
|
||||||
}
|
|
||||||
Ok("".to_string())
|
|
||||||
}
|
|
||||||
"move_word_end" => {
|
|
||||||
let current_input = state.get_current_input();
|
|
||||||
if !current_input.is_empty() {
|
|
||||||
let current_pos = state.current_cursor_pos();
|
|
||||||
let new_pos = find_word_end(current_input, current_pos);
|
|
||||||
let final_pos = if new_pos == current_pos && current_pos < current_input.len().saturating_sub(1) {
|
|
||||||
find_word_end(current_input, current_pos + 1)
|
|
||||||
} else {
|
|
||||||
new_pos
|
|
||||||
};
|
|
||||||
let max_valid_index = current_input.len().saturating_sub(1);
|
|
||||||
let clamped_pos = final_pos.min(max_valid_index);
|
|
||||||
state.set_current_cursor_pos(clamped_pos);
|
|
||||||
*ideal_cursor_column = clamped_pos;
|
|
||||||
}
|
|
||||||
Ok("".to_string())
|
|
||||||
}
|
|
||||||
"move_word_prev" => {
|
|
||||||
let current_input = state.get_current_input();
|
|
||||||
if !current_input.is_empty() {
|
|
||||||
let new_pos = find_prev_word_start(current_input, state.current_cursor_pos());
|
|
||||||
state.set_current_cursor_pos(new_pos);
|
|
||||||
*ideal_cursor_column = new_pos;
|
|
||||||
}
|
|
||||||
Ok("".to_string())
|
|
||||||
}
|
|
||||||
"move_word_end_prev" => {
|
|
||||||
let current_input = state.get_current_input();
|
|
||||||
if !current_input.is_empty() {
|
|
||||||
let new_pos = find_prev_word_end(current_input, state.current_cursor_pos());
|
|
||||||
state.set_current_cursor_pos(new_pos);
|
|
||||||
*ideal_cursor_column = new_pos;
|
|
||||||
}
|
|
||||||
Ok("".to_string())
|
|
||||||
}
|
|
||||||
"move_line_start" => {
|
|
||||||
state.set_current_cursor_pos(0);
|
|
||||||
*ideal_cursor_column = 0;
|
|
||||||
Ok("".to_string())
|
|
||||||
}
|
|
||||||
"move_line_end" => {
|
|
||||||
let current_input = state.get_current_input();
|
|
||||||
if !current_input.is_empty() {
|
|
||||||
let new_pos = current_input.len().saturating_sub(1);
|
|
||||||
state.set_current_cursor_pos(new_pos);
|
|
||||||
*ideal_cursor_column = new_pos;
|
|
||||||
} else {
|
|
||||||
state.set_current_cursor_pos(0);
|
|
||||||
*ideal_cursor_column = 0;
|
|
||||||
}
|
|
||||||
Ok("".to_string())
|
|
||||||
}
|
|
||||||
"enter_edit_mode_before" | "enter_edit_mode_after" | "enter_command_mode" | "exit_highlight_mode" => {
|
|
||||||
key_sequence_tracker.reset();
|
|
||||||
Ok("Mode change handled by main loop".to_string())
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
key_sequence_tracker.reset();
|
|
||||||
command_message.clear();
|
|
||||||
Ok(format!("Unknown read-only action: {}", action))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,267 +0,0 @@
|
|||||||
// src/functions/modes/read_only/add_table_ro.rs
|
|
||||||
use crate::config::binds::key_sequences::KeySequenceTracker;
|
|
||||||
use crate::state::pages::add_table::AddTableState;
|
|
||||||
use crate::state::app::state::AppState;
|
|
||||||
use canvas::canvas::CanvasState;
|
|
||||||
use anyhow::Result;
|
|
||||||
|
|
||||||
// Re-use word navigation helpers if they are public or move them to a common module
|
|
||||||
// For now, duplicating them here for simplicity. Consider refactoring later.
|
|
||||||
#[derive(PartialEq)]
|
|
||||||
enum CharType {
|
|
||||||
Whitespace,
|
|
||||||
Alphanumeric,
|
|
||||||
Punctuation,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_char_type(c: char) -> CharType {
|
|
||||||
if c.is_whitespace() { CharType::Whitespace }
|
|
||||||
else if c.is_alphanumeric() { CharType::Alphanumeric }
|
|
||||||
else { CharType::Punctuation }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_next_word_start(text: &str, current_pos: usize) -> usize {
|
|
||||||
let chars: Vec<char> = text.chars().collect();
|
|
||||||
let len = chars.len();
|
|
||||||
if len == 0 || current_pos >= len { return len; }
|
|
||||||
let mut pos = current_pos;
|
|
||||||
let initial_type = get_char_type(chars[pos]);
|
|
||||||
while pos < len && get_char_type(chars[pos]) == initial_type { pos += 1; }
|
|
||||||
while pos < len && get_char_type(chars[pos]) == CharType::Whitespace { pos += 1; }
|
|
||||||
pos
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_word_end(text: &str, current_pos: usize) -> usize {
|
|
||||||
let chars: Vec<char> = text.chars().collect();
|
|
||||||
let len = chars.len();
|
|
||||||
if len == 0 { return 0; }
|
|
||||||
let mut pos = current_pos.min(len - 1);
|
|
||||||
if get_char_type(chars[pos]) == CharType::Whitespace {
|
|
||||||
pos = find_next_word_start(text, pos);
|
|
||||||
}
|
|
||||||
if pos >= len { return len.saturating_sub(1); }
|
|
||||||
let word_type = get_char_type(chars[pos]);
|
|
||||||
while pos < len && get_char_type(chars[pos]) == word_type { pos += 1; }
|
|
||||||
pos.saturating_sub(1).min(len.saturating_sub(1))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_prev_word_start(text: &str, current_pos: usize) -> usize {
|
|
||||||
let chars: Vec<char> = text.chars().collect();
|
|
||||||
if chars.is_empty() || current_pos == 0 { return 0; }
|
|
||||||
let mut pos = current_pos.saturating_sub(1);
|
|
||||||
while pos > 0 && get_char_type(chars[pos]) == CharType::Whitespace { pos -= 1; }
|
|
||||||
if pos == 0 && get_char_type(chars[pos]) == CharType::Whitespace { return 0; }
|
|
||||||
let word_type = get_char_type(chars[pos]);
|
|
||||||
while pos > 0 && get_char_type(chars[pos - 1]) == word_type { pos -= 1; }
|
|
||||||
pos
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: find_prev_word_end might need adjustments based on desired behavior.
|
|
||||||
// This version finds the end of the word *before* the previous word start.
|
|
||||||
fn find_prev_word_end(text: &str, current_pos: usize) -> usize {
|
|
||||||
let prev_start = find_prev_word_start(text, current_pos);
|
|
||||||
if prev_start == 0 { return 0; }
|
|
||||||
// Find the end of the word that starts at prev_start - 1
|
|
||||||
find_word_end(text, prev_start.saturating_sub(1))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// Executes read-only actions for the AddTable view canvas.
|
|
||||||
pub async fn execute_action(
|
|
||||||
action: &str,
|
|
||||||
app_state: &mut AppState, // Needed for focus_outside_canvas
|
|
||||||
state: &mut AddTableState,
|
|
||||||
ideal_cursor_column: &mut usize,
|
|
||||||
key_sequence_tracker: &mut KeySequenceTracker,
|
|
||||||
command_message: &mut String, // Keep for potential messages
|
|
||||||
) -> Result<String> {
|
|
||||||
// Use the CanvasState trait methods implemented for AddTableState
|
|
||||||
match action {
|
|
||||||
"move_up" => {
|
|
||||||
key_sequence_tracker.reset();
|
|
||||||
let num_fields = AddTableState::INPUT_FIELD_COUNT;
|
|
||||||
if num_fields == 0 {
|
|
||||||
*command_message = "No fields.".to_string();
|
|
||||||
return Ok(command_message.clone());
|
|
||||||
}
|
|
||||||
let current_field = state.current_field(); // Gets the index (0, 1, or 2)
|
|
||||||
|
|
||||||
if current_field > 0 {
|
|
||||||
// This handles moving from field 2 -> 1, or 1 -> 0
|
|
||||||
let new_field = current_field - 1;
|
|
||||||
state.set_current_field(new_field);
|
|
||||||
let current_input = state.get_current_input();
|
|
||||||
let max_cursor_pos = current_input.len(); // Allow cursor at end
|
|
||||||
let new_pos = (*ideal_cursor_column).min(max_cursor_pos);
|
|
||||||
state.set_current_cursor_pos(new_pos);
|
|
||||||
*ideal_cursor_column = new_pos; // Update ideal column as cursor moved
|
|
||||||
*command_message = "".to_string(); // Clear message for successful internal navigation
|
|
||||||
} else {
|
|
||||||
// current_field is 0 (InputTableName), and user pressed Up.
|
|
||||||
// Forbid moving up. Do not change focus or cursor.
|
|
||||||
*command_message = "At top of form.".to_string();
|
|
||||||
}
|
|
||||||
Ok(command_message.clone())
|
|
||||||
}
|
|
||||||
"move_down" => {
|
|
||||||
key_sequence_tracker.reset();
|
|
||||||
let num_fields = AddTableState::INPUT_FIELD_COUNT;
|
|
||||||
if num_fields == 0 {
|
|
||||||
*command_message = "No fields.".to_string();
|
|
||||||
return Ok(command_message.clone());
|
|
||||||
}
|
|
||||||
let current_field = state.current_field();
|
|
||||||
let last_field_index = num_fields - 1;
|
|
||||||
|
|
||||||
if current_field < last_field_index {
|
|
||||||
let new_field = current_field + 1;
|
|
||||||
state.set_current_field(new_field);
|
|
||||||
let current_input = state.get_current_input();
|
|
||||||
let max_cursor_pos = current_input.len(); // Allow cursor at end
|
|
||||||
let new_pos = (*ideal_cursor_column).min(max_cursor_pos);
|
|
||||||
state.set_current_cursor_pos(new_pos);
|
|
||||||
*ideal_cursor_column = new_pos; // Update ideal column
|
|
||||||
*command_message = "".to_string();
|
|
||||||
} else {
|
|
||||||
// Move focus outside canvas when moving down from the last field
|
|
||||||
app_state.ui.focus_outside_canvas = true;
|
|
||||||
// Set focus to the first element outside canvas (AddColumnButton)
|
|
||||||
state.current_focus =
|
|
||||||
crate::state::pages::add_table::AddTableFocus::AddColumnButton;
|
|
||||||
*command_message = "Focus moved below canvas".to_string();
|
|
||||||
}
|
|
||||||
Ok(command_message.clone())
|
|
||||||
}
|
|
||||||
// ... (other actions like "move_first_line", "move_left", etc. remain the same) ...
|
|
||||||
"move_first_line" => {
|
|
||||||
key_sequence_tracker.reset();
|
|
||||||
if AddTableState::INPUT_FIELD_COUNT > 0 {
|
|
||||||
state.set_current_field(0);
|
|
||||||
let current_input = state.get_current_input();
|
|
||||||
let max_cursor_pos = current_input.len();
|
|
||||||
let new_pos = (*ideal_cursor_column).min(max_cursor_pos);
|
|
||||||
state.set_current_cursor_pos(new_pos);
|
|
||||||
*ideal_cursor_column = new_pos; // Update ideal column
|
|
||||||
}
|
|
||||||
*command_message = "".to_string();
|
|
||||||
Ok(command_message.clone())
|
|
||||||
}
|
|
||||||
"move_last_line" => {
|
|
||||||
key_sequence_tracker.reset();
|
|
||||||
let num_fields = AddTableState::INPUT_FIELD_COUNT;
|
|
||||||
if num_fields > 0 {
|
|
||||||
let last_field_index = num_fields - 1;
|
|
||||||
state.set_current_field(last_field_index);
|
|
||||||
let current_input = state.get_current_input();
|
|
||||||
let max_cursor_pos = current_input.len();
|
|
||||||
let new_pos = (*ideal_cursor_column).min(max_cursor_pos);
|
|
||||||
state.set_current_cursor_pos(new_pos);
|
|
||||||
*ideal_cursor_column = new_pos; // Update ideal column
|
|
||||||
}
|
|
||||||
*command_message = "".to_string();
|
|
||||||
Ok(command_message.clone())
|
|
||||||
}
|
|
||||||
"move_left" => {
|
|
||||||
let current_pos = state.current_cursor_pos();
|
|
||||||
let new_pos = current_pos.saturating_sub(1);
|
|
||||||
state.set_current_cursor_pos(new_pos);
|
|
||||||
*ideal_cursor_column = new_pos;
|
|
||||||
*command_message = "".to_string();
|
|
||||||
Ok(command_message.clone())
|
|
||||||
}
|
|
||||||
"move_right" => {
|
|
||||||
let current_input = state.get_current_input();
|
|
||||||
let current_pos = state.current_cursor_pos();
|
|
||||||
// Allow moving cursor one position past the end
|
|
||||||
if current_pos < current_input.len() {
|
|
||||||
let new_pos = current_pos + 1;
|
|
||||||
state.set_current_cursor_pos(new_pos);
|
|
||||||
*ideal_cursor_column = new_pos;
|
|
||||||
}
|
|
||||||
*command_message = "".to_string();
|
|
||||||
Ok(command_message.clone())
|
|
||||||
}
|
|
||||||
"move_word_next" => {
|
|
||||||
let current_input = state.get_current_input();
|
|
||||||
let new_pos = find_next_word_start(
|
|
||||||
current_input,
|
|
||||||
state.current_cursor_pos(),
|
|
||||||
);
|
|
||||||
let final_pos = new_pos.min(current_input.len()); // Allow cursor at end
|
|
||||||
state.set_current_cursor_pos(final_pos);
|
|
||||||
*ideal_cursor_column = final_pos;
|
|
||||||
*command_message = "".to_string();
|
|
||||||
Ok(command_message.clone())
|
|
||||||
}
|
|
||||||
"move_word_end" => {
|
|
||||||
let current_input = state.get_current_input();
|
|
||||||
let current_pos = state.current_cursor_pos();
|
|
||||||
let new_pos = find_word_end(current_input, current_pos);
|
|
||||||
// If find_word_end returns current_pos, try starting search from next char
|
|
||||||
let final_pos =
|
|
||||||
if new_pos == current_pos && current_pos < current_input.len() {
|
|
||||||
find_word_end(current_input, current_pos + 1)
|
|
||||||
} else {
|
|
||||||
new_pos
|
|
||||||
};
|
|
||||||
let max_valid_index = current_input.len(); // Allow cursor at end
|
|
||||||
let clamped_pos = final_pos.min(max_valid_index);
|
|
||||||
state.set_current_cursor_pos(clamped_pos);
|
|
||||||
*ideal_cursor_column = clamped_pos;
|
|
||||||
*command_message = "".to_string();
|
|
||||||
Ok(command_message.clone())
|
|
||||||
}
|
|
||||||
"move_word_prev" => {
|
|
||||||
let current_input = state.get_current_input();
|
|
||||||
let new_pos = find_prev_word_start(
|
|
||||||
current_input,
|
|
||||||
state.current_cursor_pos(),
|
|
||||||
);
|
|
||||||
state.set_current_cursor_pos(new_pos);
|
|
||||||
*ideal_cursor_column = new_pos;
|
|
||||||
*command_message = "".to_string();
|
|
||||||
Ok(command_message.clone())
|
|
||||||
}
|
|
||||||
"move_word_end_prev" => {
|
|
||||||
let current_input = state.get_current_input();
|
|
||||||
let new_pos = find_prev_word_end(
|
|
||||||
current_input,
|
|
||||||
state.current_cursor_pos(),
|
|
||||||
);
|
|
||||||
state.set_current_cursor_pos(new_pos);
|
|
||||||
*ideal_cursor_column = new_pos;
|
|
||||||
*command_message = "".to_string();
|
|
||||||
Ok(command_message.clone())
|
|
||||||
}
|
|
||||||
"move_line_start" => {
|
|
||||||
state.set_current_cursor_pos(0);
|
|
||||||
*ideal_cursor_column = 0;
|
|
||||||
*command_message = "".to_string();
|
|
||||||
Ok(command_message.clone())
|
|
||||||
}
|
|
||||||
"move_line_end" => {
|
|
||||||
let current_input = state.get_current_input();
|
|
||||||
let new_pos = current_input.len(); // Allow cursor at end
|
|
||||||
state.set_current_cursor_pos(new_pos);
|
|
||||||
*ideal_cursor_column = new_pos;
|
|
||||||
*command_message = "".to_string();
|
|
||||||
Ok(command_message.clone())
|
|
||||||
}
|
|
||||||
// Actions handled by main event loop (mode changes)
|
|
||||||
"enter_edit_mode_before" | "enter_edit_mode_after"
|
|
||||||
| "enter_command_mode" | "exit_highlight_mode" => {
|
|
||||||
key_sequence_tracker.reset();
|
|
||||||
// These actions are primarily mode changes handled by the main event loop.
|
|
||||||
// The message here might be overridden by the main loop's message for mode change.
|
|
||||||
*command_message = "Mode change initiated".to_string();
|
|
||||||
Ok(command_message.clone())
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
key_sequence_tracker.reset();
|
|
||||||
*command_message =
|
|
||||||
format!("Unknown read-only action: {}", action);
|
|
||||||
Ok(command_message.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -9,11 +9,50 @@ use crate::state::pages::form::FormState;
|
|||||||
use crate::state::pages::add_logic::AddLogicState;
|
use crate::state::pages::add_logic::AddLogicState;
|
||||||
use crate::state::pages::add_table::AddTableState;
|
use crate::state::pages::add_table::AddTableState;
|
||||||
use crate::state::app::state::AppState;
|
use crate::state::app::state::AppState;
|
||||||
use crate::functions::modes::read_only::{add_logic_ro, add_table_ro};
|
|
||||||
use canvas::{canvas::{CanvasAction, CanvasState, ActionResult}, dispatcher::ActionDispatcher};
|
use canvas::{canvas::{CanvasAction, CanvasState, ActionResult}, dispatcher::ActionDispatcher};
|
||||||
use crossterm::event::KeyEvent;
|
use crossterm::event::KeyEvent;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
|
/// Helper function to dispatch canvas action for any CanvasState
|
||||||
|
async fn dispatch_canvas_action<S: CanvasState>(
|
||||||
|
action: &str,
|
||||||
|
state: &mut S,
|
||||||
|
ideal_cursor_column: &mut usize,
|
||||||
|
) -> String {
|
||||||
|
let canvas_action = CanvasAction::from_string(action);
|
||||||
|
match ActionDispatcher::dispatch(canvas_action, state, ideal_cursor_column).await {
|
||||||
|
Ok(ActionResult::Success(msg)) => msg.unwrap_or_default(),
|
||||||
|
Ok(ActionResult::HandledByFeature(msg)) => msg,
|
||||||
|
Ok(ActionResult::Error(msg)) => format!("Error: {}", msg),
|
||||||
|
Ok(ActionResult::RequiresContext(msg)) => format!("Context needed: {}", msg),
|
||||||
|
Err(e) => format!("Action failed: {}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function to dispatch canvas action to the appropriate state based on UI
|
||||||
|
async fn dispatch_to_active_state(
|
||||||
|
action: &str,
|
||||||
|
app_state: &AppState,
|
||||||
|
form_state: &mut FormState,
|
||||||
|
login_state: &mut LoginState,
|
||||||
|
register_state: &mut RegisterState,
|
||||||
|
add_table_state: &mut AddTableState,
|
||||||
|
add_logic_state: &mut AddLogicState,
|
||||||
|
ideal_cursor_column: &mut usize,
|
||||||
|
) -> String {
|
||||||
|
if app_state.ui.show_add_table {
|
||||||
|
dispatch_canvas_action(action, add_table_state, ideal_cursor_column).await
|
||||||
|
} else if app_state.ui.show_add_logic {
|
||||||
|
dispatch_canvas_action(action, add_logic_state, ideal_cursor_column).await
|
||||||
|
} else if app_state.ui.show_register {
|
||||||
|
dispatch_canvas_action(action, register_state, ideal_cursor_column).await
|
||||||
|
} else if app_state.ui.show_login {
|
||||||
|
dispatch_canvas_action(action, login_state, ideal_cursor_column).await
|
||||||
|
} else {
|
||||||
|
dispatch_canvas_action(action, form_state, ideal_cursor_column).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn handle_form_readonly_with_canvas(
|
pub async fn handle_form_readonly_with_canvas(
|
||||||
key_event: KeyEvent,
|
key_event: KeyEvent,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
@@ -88,8 +127,7 @@ pub async fn handle_read_only_event(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if config.is_enter_edit_mode_after(key.code, key.modifiers) {
|
if config.is_enter_edit_mode_after(key.code, key.modifiers) {
|
||||||
// Determine target state to adjust cursor
|
// Determine target state to adjust cursor - all states now use CanvasState trait
|
||||||
|
|
||||||
if app_state.ui.show_login {
|
if app_state.ui.show_login {
|
||||||
let current_input = login_state.get_current_input();
|
let current_input = login_state.get_current_input();
|
||||||
let current_pos = login_state.current_cursor_pos();
|
let current_pos = login_state.current_cursor_pos();
|
||||||
@@ -119,8 +157,7 @@ pub async fn handle_read_only_event(
|
|||||||
*ideal_cursor_column = add_table_state.current_cursor_pos();
|
*ideal_cursor_column = add_table_state.current_cursor_pos();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Handle FormState (uses library CanvasState)
|
// Handle FormState
|
||||||
use canvas::canvas::CanvasState as LibraryCanvasState; // Import at the top of the function
|
|
||||||
let current_input = form_state.get_current_input();
|
let current_input = form_state.get_current_input();
|
||||||
let current_pos = form_state.current_cursor_pos();
|
let current_pos = form_state.current_cursor_pos();
|
||||||
if !current_input.is_empty() && current_pos < current_input.len() {
|
if !current_input.is_empty() && current_pos < current_input.len() {
|
||||||
@@ -158,45 +195,18 @@ pub async fn handle_read_only_event(
|
|||||||
.await?
|
.await?
|
||||||
} else if app_state.ui.show_login && CONTEXT_ACTIONS_LOGIN.contains(&action) {
|
} else if app_state.ui.show_login && CONTEXT_ACTIONS_LOGIN.contains(&action) {
|
||||||
crate::tui::functions::login::handle_action(action).await?
|
crate::tui::functions::login::handle_action(action).await?
|
||||||
} else if app_state.ui.show_add_table {
|
} else {
|
||||||
add_table_ro::execute_action(
|
// All canvas states handled uniformly
|
||||||
|
dispatch_to_active_state(
|
||||||
action,
|
action,
|
||||||
app_state,
|
app_state,
|
||||||
|
form_state,
|
||||||
|
login_state,
|
||||||
|
register_state,
|
||||||
add_table_state,
|
add_table_state,
|
||||||
ideal_cursor_column,
|
|
||||||
key_sequence_tracker,
|
|
||||||
command_message,
|
|
||||||
).await?
|
|
||||||
} else if app_state.ui.show_add_logic {
|
|
||||||
add_logic_ro::execute_action(
|
|
||||||
action,
|
|
||||||
app_state,
|
|
||||||
add_logic_state,
|
add_logic_state,
|
||||||
ideal_cursor_column,
|
ideal_cursor_column,
|
||||||
key_sequence_tracker,
|
).await
|
||||||
command_message,
|
|
||||||
).await?
|
|
||||||
} else if app_state.ui.show_register{
|
|
||||||
auth_ro::execute_action(
|
|
||||||
action,
|
|
||||||
app_state,
|
|
||||||
register_state,
|
|
||||||
ideal_cursor_column,
|
|
||||||
key_sequence_tracker,
|
|
||||||
command_message,
|
|
||||||
).await?
|
|
||||||
} else if app_state.ui.show_login {
|
|
||||||
auth_ro::execute_action(
|
|
||||||
action,
|
|
||||||
app_state,
|
|
||||||
login_state,
|
|
||||||
ideal_cursor_column,
|
|
||||||
key_sequence_tracker,
|
|
||||||
command_message,
|
|
||||||
)
|
|
||||||
.await?
|
|
||||||
} else {
|
|
||||||
String::new()
|
|
||||||
};
|
};
|
||||||
key_sequence_tracker.reset();
|
key_sequence_tracker.reset();
|
||||||
return Ok((false, result));
|
return Ok((false, result));
|
||||||
@@ -218,45 +228,18 @@ pub async fn handle_read_only_event(
|
|||||||
.await?
|
.await?
|
||||||
} else if app_state.ui.show_login && CONTEXT_ACTIONS_LOGIN.contains(&action) {
|
} else if app_state.ui.show_login && CONTEXT_ACTIONS_LOGIN.contains(&action) {
|
||||||
crate::tui::functions::login::handle_action(action).await?
|
crate::tui::functions::login::handle_action(action).await?
|
||||||
} else if app_state.ui.show_add_table {
|
} else {
|
||||||
add_table_ro::execute_action(
|
// All canvas states handled uniformly
|
||||||
|
dispatch_to_active_state(
|
||||||
action,
|
action,
|
||||||
app_state,
|
app_state,
|
||||||
|
form_state,
|
||||||
|
login_state,
|
||||||
|
register_state,
|
||||||
add_table_state,
|
add_table_state,
|
||||||
ideal_cursor_column,
|
|
||||||
key_sequence_tracker,
|
|
||||||
command_message,
|
|
||||||
).await?
|
|
||||||
} else if app_state.ui.show_add_logic {
|
|
||||||
add_logic_ro::execute_action(
|
|
||||||
action,
|
|
||||||
app_state,
|
|
||||||
add_logic_state,
|
add_logic_state,
|
||||||
ideal_cursor_column,
|
ideal_cursor_column,
|
||||||
key_sequence_tracker,
|
).await
|
||||||
command_message,
|
|
||||||
).await?
|
|
||||||
} else if app_state.ui.show_register {
|
|
||||||
auth_ro::execute_action(
|
|
||||||
action,
|
|
||||||
app_state,
|
|
||||||
register_state,
|
|
||||||
ideal_cursor_column,
|
|
||||||
key_sequence_tracker,
|
|
||||||
command_message,
|
|
||||||
).await?
|
|
||||||
} else if app_state.ui.show_login {
|
|
||||||
auth_ro::execute_action(
|
|
||||||
action,
|
|
||||||
app_state,
|
|
||||||
login_state,
|
|
||||||
ideal_cursor_column,
|
|
||||||
key_sequence_tracker,
|
|
||||||
command_message,
|
|
||||||
)
|
|
||||||
.await?
|
|
||||||
} else {
|
|
||||||
String::new()
|
|
||||||
};
|
};
|
||||||
key_sequence_tracker.reset();
|
key_sequence_tracker.reset();
|
||||||
return Ok((false, result));
|
return Ok((false, result));
|
||||||
@@ -277,45 +260,18 @@ pub async fn handle_read_only_event(
|
|||||||
.await?
|
.await?
|
||||||
} else if app_state.ui.show_login && CONTEXT_ACTIONS_LOGIN.contains(&action) {
|
} else if app_state.ui.show_login && CONTEXT_ACTIONS_LOGIN.contains(&action) {
|
||||||
crate::tui::functions::login::handle_action(action).await?
|
crate::tui::functions::login::handle_action(action).await?
|
||||||
} else if app_state.ui.show_add_table {
|
} else {
|
||||||
add_table_ro::execute_action(
|
// All canvas states handled uniformly
|
||||||
|
dispatch_to_active_state(
|
||||||
action,
|
action,
|
||||||
app_state,
|
app_state,
|
||||||
|
form_state,
|
||||||
|
login_state,
|
||||||
|
register_state,
|
||||||
add_table_state,
|
add_table_state,
|
||||||
ideal_cursor_column,
|
|
||||||
key_sequence_tracker,
|
|
||||||
command_message,
|
|
||||||
).await?
|
|
||||||
} else if app_state.ui.show_add_logic {
|
|
||||||
add_logic_ro::execute_action(
|
|
||||||
action,
|
|
||||||
app_state,
|
|
||||||
add_logic_state,
|
add_logic_state,
|
||||||
ideal_cursor_column,
|
ideal_cursor_column,
|
||||||
key_sequence_tracker,
|
).await
|
||||||
command_message,
|
|
||||||
).await?
|
|
||||||
} else if app_state.ui.show_register {
|
|
||||||
auth_ro::execute_action(
|
|
||||||
action,
|
|
||||||
app_state,
|
|
||||||
register_state,
|
|
||||||
ideal_cursor_column,
|
|
||||||
key_sequence_tracker,
|
|
||||||
command_message,
|
|
||||||
).await?
|
|
||||||
} else if app_state.ui.show_login {
|
|
||||||
auth_ro::execute_action(
|
|
||||||
action,
|
|
||||||
app_state,
|
|
||||||
login_state,
|
|
||||||
ideal_cursor_column,
|
|
||||||
key_sequence_tracker,
|
|
||||||
command_message,
|
|
||||||
)
|
|
||||||
.await?
|
|
||||||
} else {
|
|
||||||
String::new()
|
|
||||||
};
|
};
|
||||||
return Ok((false, result));
|
return Ok((false, result));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user