we compiled multi canvas, needs so much work to do now
This commit is contained in:
@@ -3,16 +3,21 @@ use crate::config::colors::themes::Theme;
|
||||
use crate::state::app::highlight::HighlightState;
|
||||
use crate::state::app::state::AppState;
|
||||
use crate::state::pages::add_logic::{AddLogicFocus, AddLogicState};
|
||||
use crate::state::pages::canvas_state::CanvasState;
|
||||
use crate::state::pages::canvas_state::CanvasState; // For the top 3 fields
|
||||
use ratatui::{
|
||||
layout::{Alignment, Constraint, Direction, Layout, Rect},
|
||||
style::{Modifier, Style},
|
||||
text::{Line, Span, Text},
|
||||
text::{Line, Span},
|
||||
widgets::{Block, BorderType, Borders, Paragraph},
|
||||
Frame,
|
||||
};
|
||||
use crate::components::handlers::canvas::render_canvas;
|
||||
use crate::components::handlers::canvas::render_canvas as render_single_line_canvas; // Alias for clarity
|
||||
use crate::components::common::dialog;
|
||||
// Use your existing multiline renderer
|
||||
use crate::components::handlers::multi_canvas::render_multiline_editor;
|
||||
// We also need the trait for the renderer's signature
|
||||
use crate::state::pages::multi_canvas_state::MultilineEditorState;
|
||||
|
||||
|
||||
pub fn render_add_logic(
|
||||
f: &mut Frame,
|
||||
@@ -33,7 +38,6 @@ pub fn render_add_logic(
|
||||
let inner_area = main_block.inner(area);
|
||||
f.render_widget(main_block, area);
|
||||
|
||||
// Calculate areas dynamically like add_table
|
||||
let main_chunks = Layout::default()
|
||||
.direction(Direction::Vertical)
|
||||
.constraints([
|
||||
@@ -49,19 +53,21 @@ pub fn render_add_logic(
|
||||
let script_content_area = main_chunks[2];
|
||||
let buttons_area = main_chunks[3];
|
||||
|
||||
// Top Info Rendering (like add_table)
|
||||
// Top Info Rendering
|
||||
let profile_text = Paragraph::new(vec![
|
||||
Line::from(Span::styled(
|
||||
format!("Profile: {}", add_logic_state.profile_name),
|
||||
theme.fg,
|
||||
Style::default().fg(theme.fg),
|
||||
)),
|
||||
Line::from(Span::styled(
|
||||
format!("Table: {}",
|
||||
add_logic_state.selected_table_id
|
||||
format!(
|
||||
"Table: {}",
|
||||
add_logic_state
|
||||
.selected_table_id
|
||||
.map(|id| format!("ID {}", id))
|
||||
.unwrap_or_else(|| "Global".to_string())
|
||||
),
|
||||
theme.fg,
|
||||
Style::default().fg(theme.fg),
|
||||
)),
|
||||
])
|
||||
.block(
|
||||
@@ -71,47 +77,43 @@ pub fn render_add_logic(
|
||||
);
|
||||
f.render_widget(profile_text, top_info_area);
|
||||
|
||||
// Canvas rendering for input fields (like add_table)
|
||||
let focus_on_canvas_inputs = matches!(
|
||||
// Single-line canvas rendering for top input fields
|
||||
let focus_on_single_line_canvas_inputs = matches!(
|
||||
add_logic_state.current_focus,
|
||||
AddLogicFocus::InputLogicName
|
||||
| AddLogicFocus::InputTargetColumn
|
||||
| AddLogicFocus::InputDescription
|
||||
);
|
||||
|
||||
render_canvas(
|
||||
render_single_line_canvas(
|
||||
f,
|
||||
canvas_area,
|
||||
add_logic_state,
|
||||
add_logic_state, // Implements CanvasState for these fields
|
||||
&add_logic_state.fields(),
|
||||
&add_logic_state.current_field(),
|
||||
&add_logic_state.inputs(),
|
||||
theme,
|
||||
is_edit_mode && focus_on_canvas_inputs,
|
||||
is_edit_mode && focus_on_single_line_canvas_inputs,
|
||||
highlight_state,
|
||||
);
|
||||
|
||||
// Script Content Area
|
||||
let script_block_border_style = if add_logic_state.current_focus == AddLogicFocus::InputScriptContent {
|
||||
Style::default().fg(theme.highlight)
|
||||
} else {
|
||||
Style::default().fg(theme.secondary)
|
||||
};
|
||||
// --- Script Content Area using your MultilineEditor ---
|
||||
let is_script_editor_focused =
|
||||
add_logic_state.current_focus == AddLogicFocus::InputScriptContent;
|
||||
|
||||
let script_block = Block::default()
|
||||
.title(" Steel Script Content ")
|
||||
.borders(Borders::ALL)
|
||||
.border_type(BorderType::Rounded)
|
||||
.border_style(script_block_border_style);
|
||||
// Pass the script_editor field which implements MultilineEditorState
|
||||
// The `render_multiline_editor` from canvas_multi.rs takes `&impl MultilineEditorState`
|
||||
render_multiline_editor(
|
||||
f,
|
||||
script_content_area,
|
||||
&add_logic_state.script_editor, // Pass your BasicMultilineEditor instance
|
||||
theme,
|
||||
is_script_editor_focused && is_edit_mode, // Editor is active for input
|
||||
highlight_state, // Pass the global highlight state
|
||||
);
|
||||
|
||||
let script_text = Text::from(add_logic_state.script_content_input.as_str());
|
||||
let script_paragraph = Paragraph::new(script_text)
|
||||
.block(script_block)
|
||||
.scroll(add_logic_state.script_content_scroll)
|
||||
.style(Style::default().fg(theme.fg));
|
||||
f.render_widget(script_paragraph, script_content_area);
|
||||
|
||||
// Button Style Helpers (same as add_table)
|
||||
// Button Style Helpers
|
||||
let get_button_style = |button_focus: AddLogicFocus, current_focus| {
|
||||
let is_focused = current_focus == button_focus;
|
||||
let base_style = Style::default().fg(if is_focused {
|
||||
@@ -134,12 +136,12 @@ pub fn render_add_logic(
|
||||
}
|
||||
};
|
||||
|
||||
// Bottom Buttons (same style as add_table)
|
||||
// Bottom Buttons
|
||||
let button_chunks = Layout::default()
|
||||
.direction(Direction::Horizontal)
|
||||
.constraints([
|
||||
Constraint::Percentage(50), // Save Button
|
||||
Constraint::Percentage(50), // Cancel Button
|
||||
Constraint::Percentage(50),
|
||||
Constraint::Percentage(50),
|
||||
])
|
||||
.split(buttons_area);
|
||||
|
||||
@@ -177,7 +179,7 @@ pub fn render_add_logic(
|
||||
);
|
||||
f.render_widget(cancel_button, button_chunks[1]);
|
||||
|
||||
// Dialog rendering (same as add_table)
|
||||
// Dialog rendering
|
||||
if app_state.ui.dialog.dialog_show {
|
||||
dialog::render_dialog(
|
||||
f,
|
||||
@@ -191,4 +193,3 @@ pub fn render_add_logic(
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
pub mod canvas;
|
||||
pub mod sidebar;
|
||||
pub mod buffer_list;
|
||||
pub mod multi_canvas;
|
||||
|
||||
pub use canvas::*;
|
||||
pub use sidebar::*;
|
||||
pub use buffer_list::*;
|
||||
pub use multi_canvas::*;
|
||||
|
||||
@@ -9,7 +9,7 @@ use ratatui::{
|
||||
};
|
||||
use crate::config::colors::themes::Theme;
|
||||
// Import the new trait
|
||||
use crate::state::pages::multiline_editor_state::MultilineEditorState;
|
||||
use crate::state::pages::multi_canvas_state::MultilineEditorState;
|
||||
use crate::state::app::highlight::HighlightState; // Assuming this is your global highlight state
|
||||
use std::cmp::{min, max};
|
||||
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
// src/functions/modes/edit/add_logic_e.rs
|
||||
use crate::state::pages::add_logic::AddLogicState; // Changed
|
||||
use crate::state::pages::add_logic::{AddLogicFocus, AddLogicState}; // Added AddLogicFocus
|
||||
use crate::state::pages::canvas_state::CanvasState;
|
||||
use crossterm::event::{KeyCode, KeyEvent};
|
||||
use anyhow::Result;
|
||||
// Import the trait your BasicMultilineEditor implements
|
||||
use crate::state::pages::multi_canvas_state::MultilineEditorState;
|
||||
|
||||
|
||||
// Word navigation helpers (get_char_type, find_next_word_start, etc.)
|
||||
// can be kept as they are generic.
|
||||
@@ -69,209 +72,279 @@ fn find_prev_word_end(text: &str, current_pos: usize) -> usize {
|
||||
if pos > 0 { pos - 1 } else { 0 }
|
||||
}
|
||||
|
||||
/// Executes edit actions for the AddLogic view canvas.
|
||||
|
||||
pub async fn execute_edit_action(
|
||||
action: &str,
|
||||
key: KeyEvent,
|
||||
state: &mut AddLogicState, // Changed
|
||||
ideal_cursor_column: &mut usize,
|
||||
action: &str, // This 'action' string comes from keybindings for edit mode
|
||||
key: KeyEvent, // The raw key event, useful for Char input
|
||||
state: &mut AddLogicState,
|
||||
ideal_cursor_column: &mut usize, // Used for single-line canvas fields
|
||||
) -> Result<String> {
|
||||
match action {
|
||||
"insert_char" => {
|
||||
if let KeyCode::Char(c) = key.code {
|
||||
// Check if the current focus is on the script content editor
|
||||
if state.current_focus == AddLogicFocus::InputScriptContent {
|
||||
// Delegate to script editor's methods based on key or mapped action
|
||||
match key.code {
|
||||
KeyCode::Char(c) => {
|
||||
// If action is "insert_char" or if no specific action mapped, treat as char input
|
||||
if action == "insert_char" || action.is_empty() { // Assuming unmapped keys might default to char insert
|
||||
state.script_editor.insert_char_at_cursor(c);
|
||||
state.script_editor.set_has_unsaved_changes(true);
|
||||
return Ok("".to_string());
|
||||
}
|
||||
}
|
||||
KeyCode::Enter => {
|
||||
state.script_editor.insert_newline_at_cursor();
|
||||
state.script_editor.set_has_unsaved_changes(true);
|
||||
return Ok("".to_string());
|
||||
}
|
||||
KeyCode::Backspace => {
|
||||
state.script_editor.delete_char_before_cursor();
|
||||
state.script_editor.set_has_unsaved_changes(true);
|
||||
return Ok("".to_string());
|
||||
}
|
||||
KeyCode::Delete => {
|
||||
state.script_editor.delete_char_at_cursor();
|
||||
state.script_editor.set_has_unsaved_changes(true);
|
||||
return Ok("".to_string());
|
||||
}
|
||||
KeyCode::Left => {
|
||||
state.script_editor.move_cursor_left();
|
||||
return Ok("".to_string());
|
||||
}
|
||||
KeyCode::Right => {
|
||||
state.script_editor.move_cursor_right();
|
||||
return Ok("".to_string());
|
||||
}
|
||||
KeyCode::Up => {
|
||||
state.script_editor.move_cursor_up();
|
||||
return Ok("".to_string());
|
||||
}
|
||||
KeyCode::Down => {
|
||||
state.script_editor.move_cursor_down();
|
||||
return Ok("".to_string());
|
||||
}
|
||||
// Home, End, PageUp, PageDown could be added here for the script_editor
|
||||
// KeyCode::Home => { state.script_editor.move_cursor_to_line_start(); Ok("".to_string()) }
|
||||
// KeyCode::End => { state.script_editor.move_cursor_to_line_end(); Ok("".to_string()) }
|
||||
_ => {} // Let it fall through to action string matching if needed
|
||||
}
|
||||
// Handle actions specific to multiline editor that might be bound to other keys
|
||||
match action {
|
||||
"move_left" => { state.script_editor.move_cursor_left(); Ok("".to_string()) }
|
||||
"move_right" => { state.script_editor.move_cursor_right(); Ok("".to_string()) }
|
||||
"move_up" => { state.script_editor.move_cursor_up(); Ok("".to_string()) }
|
||||
"move_down" => { state.script_editor.move_cursor_down(); Ok("".to_string()) }
|
||||
// "next_field" and "prev_field" in script editor context might mean nothing,
|
||||
// or could exit the editor focus. This is handled by the navigation logic.
|
||||
// "exit_edit_mode" is also typically handled by the caller (mode_manager).
|
||||
"exit_edit_mode" | "save" | "revert" | "next_field" | "prev_field" => {
|
||||
Ok(format!("Action '{}' not directly handled by script editor, caller should manage.", action))
|
||||
}
|
||||
_ => Ok(format!("Unknown/unhandled edit action for script: {}", action)),
|
||||
}
|
||||
} else {
|
||||
// Original logic for single-line canvas fields
|
||||
match action {
|
||||
"insert_char" => {
|
||||
if let KeyCode::Char(c) = key.code {
|
||||
let cursor_pos = state.current_cursor_pos();
|
||||
let field_value = state.get_current_input_mut(); // This uses CanvasState
|
||||
let mut chars: Vec<char> = field_value.chars().collect();
|
||||
if cursor_pos <= chars.len() {
|
||||
chars.insert(cursor_pos, c);
|
||||
*field_value = chars.into_iter().collect();
|
||||
state.set_current_cursor_pos(cursor_pos + 1);
|
||||
state.set_has_unsaved_changes(true); // For single-line canvas
|
||||
*ideal_cursor_column = state.current_cursor_pos();
|
||||
}
|
||||
} else {
|
||||
return Ok("Error: insert_char called without a char key.".to_string());
|
||||
}
|
||||
Ok("".to_string())
|
||||
}
|
||||
"delete_char_backward" => {
|
||||
if state.current_cursor_pos() > 0 {
|
||||
let cursor_pos = state.current_cursor_pos();
|
||||
let field_value = state.get_current_input_mut();
|
||||
let mut chars: Vec<char> = field_value.chars().collect();
|
||||
if cursor_pos <= chars.len() {
|
||||
chars.remove(cursor_pos - 1);
|
||||
*field_value = chars.into_iter().collect();
|
||||
let new_pos = cursor_pos - 1;
|
||||
state.set_current_cursor_pos(new_pos);
|
||||
state.set_has_unsaved_changes(true);
|
||||
*ideal_cursor_column = new_pos;
|
||||
}
|
||||
}
|
||||
Ok("".to_string())
|
||||
}
|
||||
"delete_char_forward" => {
|
||||
let cursor_pos = state.current_cursor_pos();
|
||||
let field_value = state.get_current_input_mut();
|
||||
let mut chars: Vec<char> = field_value.chars().collect();
|
||||
if cursor_pos <= chars.len() {
|
||||
chars.insert(cursor_pos, c);
|
||||
if cursor_pos < chars.len() {
|
||||
chars.remove(cursor_pos);
|
||||
*field_value = chars.into_iter().collect();
|
||||
state.set_current_cursor_pos(cursor_pos + 1);
|
||||
state.set_has_unsaved_changes(true);
|
||||
*ideal_cursor_column = state.current_cursor_pos();
|
||||
*ideal_cursor_column = cursor_pos;
|
||||
}
|
||||
} else {
|
||||
return Ok("Error: insert_char called without a char key.".to_string());
|
||||
Ok("".to_string())
|
||||
}
|
||||
Ok("".to_string())
|
||||
}
|
||||
"delete_char_backward" => {
|
||||
if state.current_cursor_pos() > 0 {
|
||||
let cursor_pos = state.current_cursor_pos();
|
||||
let field_value = state.get_current_input_mut();
|
||||
let mut chars: Vec<char> = field_value.chars().collect();
|
||||
if cursor_pos <= chars.len() {
|
||||
chars.remove(cursor_pos - 1);
|
||||
*field_value = chars.into_iter().collect();
|
||||
let new_pos = cursor_pos - 1;
|
||||
state.set_current_cursor_pos(new_pos);
|
||||
state.set_has_unsaved_changes(true);
|
||||
*ideal_cursor_column = new_pos;
|
||||
"next_field" => { // For single-line canvas
|
||||
let num_fields = AddLogicState::INPUT_FIELD_COUNT;
|
||||
if num_fields > 0 {
|
||||
let current_field = state.current_field();
|
||||
let last_field_index = num_fields - 1;
|
||||
if current_field < last_field_index {
|
||||
state.set_current_field(current_field + 1);
|
||||
}
|
||||
// else { // Optionally, if at last field, Tab could move to InputScriptContent
|
||||
// state.current_focus = AddLogicFocus::InputScriptContent;
|
||||
// }
|
||||
let current_input = state.get_current_input();
|
||||
let max_pos = current_input.len();
|
||||
state.set_current_cursor_pos((*ideal_cursor_column).min(max_pos));
|
||||
}
|
||||
Ok("".to_string())
|
||||
}
|
||||
Ok("".to_string())
|
||||
}
|
||||
"delete_char_forward" => {
|
||||
let cursor_pos = state.current_cursor_pos();
|
||||
let field_value = state.get_current_input_mut();
|
||||
let mut chars: Vec<char> = field_value.chars().collect();
|
||||
if cursor_pos < chars.len() {
|
||||
chars.remove(cursor_pos);
|
||||
*field_value = chars.into_iter().collect();
|
||||
state.set_has_unsaved_changes(true);
|
||||
*ideal_cursor_column = cursor_pos;
|
||||
}
|
||||
Ok("".to_string())
|
||||
}
|
||||
"next_field" => {
|
||||
let num_fields = AddLogicState::INPUT_FIELD_COUNT; // Changed
|
||||
if num_fields > 0 {
|
||||
let current_field = state.current_field();
|
||||
let last_field_index = num_fields - 1;
|
||||
if current_field < last_field_index { // Prevent cycling
|
||||
state.set_current_field(current_field + 1);
|
||||
"prev_field" => { // For single-line canvas
|
||||
let num_fields = AddLogicState::INPUT_FIELD_COUNT;
|
||||
if num_fields > 0 {
|
||||
let current_field = state.current_field();
|
||||
if current_field > 0 {
|
||||
state.set_current_field(current_field - 1);
|
||||
}
|
||||
// else { // Optionally, if at first field, Shift-Tab could move to CancelButton or similar
|
||||
// state.current_focus = AddLogicFocus::CancelButton; // Example
|
||||
// }
|
||||
let current_input = state.get_current_input();
|
||||
let max_pos = current_input.len();
|
||||
state.set_current_cursor_pos((*ideal_cursor_column).min(max_pos));
|
||||
}
|
||||
let current_input = state.get_current_input();
|
||||
let max_pos = current_input.len();
|
||||
state.set_current_cursor_pos((*ideal_cursor_column).min(max_pos));
|
||||
Ok("".to_string())
|
||||
}
|
||||
Ok("".to_string())
|
||||
}
|
||||
"prev_field" => {
|
||||
let num_fields = AddLogicState::INPUT_FIELD_COUNT; // Changed
|
||||
if num_fields > 0 {
|
||||
let current_field = state.current_field();
|
||||
if current_field > 0 { // Prevent cycling
|
||||
state.set_current_field(current_field - 1);
|
||||
}
|
||||
let current_input = state.get_current_input();
|
||||
let max_pos = current_input.len();
|
||||
state.set_current_cursor_pos((*ideal_cursor_column).min(max_pos));
|
||||
}
|
||||
Ok("".to_string())
|
||||
}
|
||||
"move_left" => {
|
||||
let new_pos = state.current_cursor_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_pos < current_input.len() {
|
||||
let new_pos = current_pos + 1;
|
||||
"move_left" => {
|
||||
let new_pos = state.current_cursor_pos().saturating_sub(1);
|
||||
state.set_current_cursor_pos(new_pos);
|
||||
*ideal_cursor_column = new_pos;
|
||||
Ok("".to_string())
|
||||
}
|
||||
Ok("".to_string())
|
||||
}
|
||||
"move_up" => { // In edit mode, up/down usually means prev/next field
|
||||
let current_field = state.current_field();
|
||||
if current_field > 0 {
|
||||
let new_field = current_field - 1;
|
||||
state.set_current_field(new_field);
|
||||
"move_right" => {
|
||||
let current_input = state.get_current_input();
|
||||
let max_pos = current_input.len();
|
||||
state.set_current_cursor_pos((*ideal_cursor_column).min(max_pos));
|
||||
let current_pos = state.current_cursor_pos();
|
||||
if current_pos < current_input.len() {
|
||||
let new_pos = current_pos + 1;
|
||||
state.set_current_cursor_pos(new_pos);
|
||||
*ideal_cursor_column = new_pos;
|
||||
}
|
||||
Ok("".to_string())
|
||||
}
|
||||
Ok("".to_string())
|
||||
}
|
||||
"move_down" => { // In edit mode, up/down usually means prev/next field
|
||||
let num_fields = AddLogicState::INPUT_FIELD_COUNT; // Changed
|
||||
if num_fields > 0 {
|
||||
"move_up" => { // In edit mode for single-line canvas, up/down usually means prev/next field
|
||||
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;
|
||||
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_pos = current_input.len();
|
||||
state.set_current_cursor_pos((*ideal_cursor_column).min(max_pos));
|
||||
}
|
||||
Ok("".to_string())
|
||||
}
|
||||
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();
|
||||
let new_pos = current_input.len();
|
||||
state.set_current_cursor_pos(new_pos);
|
||||
*ideal_cursor_column = new_pos;
|
||||
Ok("".to_string())
|
||||
}
|
||||
"move_first_line" => {
|
||||
if AddLogicState::INPUT_FIELD_COUNT > 0 { // Changed
|
||||
state.set_current_field(0);
|
||||
"move_down" => { // In edit mode for single-line canvas, up/down usually means prev/next field
|
||||
let num_fields = AddLogicState::INPUT_FIELD_COUNT;
|
||||
if num_fields > 0 {
|
||||
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_pos = current_input.len();
|
||||
state.set_current_cursor_pos((*ideal_cursor_column).min(max_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();
|
||||
let max_pos = current_input.len();
|
||||
state.set_current_cursor_pos((*ideal_cursor_column).min(max_pos));
|
||||
}
|
||||
Ok("".to_string())
|
||||
}
|
||||
"move_last_line" => {
|
||||
let num_fields = AddLogicState::INPUT_FIELD_COUNT; // Changed
|
||||
if num_fields > 0 {
|
||||
let new_field = num_fields - 1;
|
||||
state.set_current_field(new_field);
|
||||
let current_input = state.get_current_input();
|
||||
let max_pos = current_input.len();
|
||||
state.set_current_cursor_pos((*ideal_cursor_column).min(max_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());
|
||||
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() { // Ensure not to go past end
|
||||
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;
|
||||
}
|
||||
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());
|
||||
let new_pos = current_input.len();
|
||||
state.set_current_cursor_pos(new_pos);
|
||||
*ideal_cursor_column = new_pos;
|
||||
Ok("".to_string())
|
||||
}
|
||||
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;
|
||||
"move_first_line" => { // For single-line canvas, effectively "first field"
|
||||
if AddLogicState::INPUT_FIELD_COUNT > 0 {
|
||||
state.set_current_field(0);
|
||||
let current_input = state.get_current_input();
|
||||
let max_pos = current_input.len();
|
||||
state.set_current_cursor_pos((*ideal_cursor_column).min(max_pos));
|
||||
}
|
||||
Ok("".to_string())
|
||||
}
|
||||
Ok("".to_string())
|
||||
"move_last_line" => { // For single-line canvas, effectively "last field"
|
||||
let num_fields = AddLogicState::INPUT_FIELD_COUNT;
|
||||
if num_fields > 0 {
|
||||
let new_field = num_fields - 1;
|
||||
state.set_current_field(new_field);
|
||||
let current_input = state.get_current_input();
|
||||
let max_pos = current_input.len();
|
||||
state.set_current_cursor_pos((*ideal_cursor_column).min(max_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());
|
||||
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() {
|
||||
find_word_end(current_input, current_pos + 1)
|
||||
} else {
|
||||
new_pos
|
||||
};
|
||||
let max_valid_index = current_input.len();
|
||||
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())
|
||||
}
|
||||
"exit_edit_mode" | "save" | "revert" => {
|
||||
Ok("Action handled by main loop".to_string())
|
||||
}
|
||||
_ => Ok(format!("Unknown or unhandled edit action for single-line: {}", action)),
|
||||
}
|
||||
"exit_edit_mode" | "save" | "revert" => {
|
||||
Ok("Action handled by main loop".to_string())
|
||||
}
|
||||
_ => Ok(format!("Unknown or unhandled edit action: {}", action)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,11 +7,13 @@ use crate::state::{
|
||||
app::buffer::BufferState,
|
||||
};
|
||||
use crate::state::pages::canvas_state::CanvasState;
|
||||
use crossterm::event::{KeyEvent};
|
||||
use crossterm::event::{KeyEvent, KeyCode}; // Added KeyCode here
|
||||
use crate::services::GrpcClient;
|
||||
use tokio::sync::mpsc;
|
||||
use anyhow::Result;
|
||||
use common::proto::multieko2::table_script::{PostTableScriptRequest};
|
||||
// Import the trait your BasicMultilineEditor implements
|
||||
use crate::state::pages::multi_canvas_state::MultilineEditorState;
|
||||
|
||||
pub type SaveLogicResultSender = mpsc::Sender<Result<String>>;
|
||||
|
||||
@@ -29,57 +31,81 @@ pub fn handle_add_logic_navigation(
|
||||
let action = config.get_general_action(key.code, key.modifiers).map(String::from);
|
||||
let mut handled = false;
|
||||
|
||||
// Check if focus is on canvas input fields
|
||||
let focus_on_canvas_inputs = matches!(
|
||||
// Check if focus is on canvas input fields (single-line ones)
|
||||
let _focus_on_canvas_inputs = matches!( // Renamed as it's not directly used below for multiline
|
||||
add_logic_state.current_focus,
|
||||
AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription
|
||||
);
|
||||
|
||||
// Handle script content editing separately (multiline)
|
||||
if *is_edit_mode && add_logic_state.current_focus == AddLogicFocus::InputScriptContent {
|
||||
// Use methods from MultilineEditorState trait
|
||||
match key.code {
|
||||
crossterm::event::KeyCode::Char(c) => {
|
||||
add_logic_state.script_content_input.push(c);
|
||||
add_logic_state.has_unsaved_changes = true;
|
||||
KeyCode::Char(c) => {
|
||||
add_logic_state.script_editor.insert_char_at_cursor(c);
|
||||
add_logic_state.script_editor.set_has_unsaved_changes(true); // Mark editor as changed
|
||||
// add_logic_state.has_unsaved_changes = true; // Optionally mark the whole page state
|
||||
handled = true;
|
||||
}
|
||||
crossterm::event::KeyCode::Enter => {
|
||||
add_logic_state.script_content_input.push('\n');
|
||||
add_logic_state.has_unsaved_changes = true;
|
||||
add_logic_state.script_content_scroll.0 = add_logic_state.script_content_scroll.0.saturating_add(1);
|
||||
KeyCode::Enter => {
|
||||
add_logic_state.script_editor.insert_newline_at_cursor();
|
||||
add_logic_state.script_editor.set_has_unsaved_changes(true);
|
||||
// Scrolling is handled internally by the editor's state or renderer,
|
||||
// or by dedicated scroll commands if you add them.
|
||||
// We don't directly manipulate scroll here anymore.
|
||||
handled = true;
|
||||
}
|
||||
crossterm::event::KeyCode::Backspace => {
|
||||
if !add_logic_state.script_content_input.is_empty() {
|
||||
add_logic_state.script_content_input.pop();
|
||||
add_logic_state.has_unsaved_changes = true;
|
||||
handled = true;
|
||||
}
|
||||
KeyCode::Backspace => {
|
||||
add_logic_state.script_editor.delete_char_before_cursor();
|
||||
add_logic_state.script_editor.set_has_unsaved_changes(true);
|
||||
handled = true;
|
||||
}
|
||||
_ => {}
|
||||
// Add other key handling for the multiline editor as needed:
|
||||
// KeyCode::Delete => {
|
||||
// add_logic_state.script_editor.delete_char_at_cursor();
|
||||
// add_logic_state.script_editor.set_has_unsaved_changes(true);
|
||||
// handled = true;
|
||||
// }
|
||||
// KeyCode::Left => {
|
||||
// add_logic_state.script_editor.move_cursor_left();
|
||||
// handled = true;
|
||||
// }
|
||||
// KeyCode::Right => {
|
||||
// add_logic_state.script_editor.move_cursor_right();
|
||||
// handled = true;
|
||||
// }
|
||||
KeyCode::Up => { // In edit mode, up in multiline editor moves cursor up a line
|
||||
add_logic_state.script_editor.move_cursor_up();
|
||||
handled = true;
|
||||
}
|
||||
KeyCode::Down => { // In edit mode, down in multiline editor moves cursor down a line
|
||||
add_logic_state.script_editor.move_cursor_down();
|
||||
handled = true;
|
||||
}
|
||||
// Potentially PageUp, PageDown, Home, End etc.
|
||||
_ => {} // Let other handlers or default behavior take over
|
||||
}
|
||||
}
|
||||
|
||||
if !handled {
|
||||
match action.as_deref() {
|
||||
Some("exit_view") | Some("cancel_action") => {
|
||||
buffer_state.update_history(AppView::Admin); // Fixed: was AdminPanel
|
||||
buffer_state.update_history(AppView::Admin);
|
||||
app_state.ui.focus_outside_canvas = true;
|
||||
*command_message = "Exited Add Logic".to_string();
|
||||
handled = true;
|
||||
}
|
||||
Some("next_field") => {
|
||||
Some("next_field") => { // Tab
|
||||
let previous_focus = add_logic_state.current_focus;
|
||||
add_logic_state.current_focus = match add_logic_state.current_focus {
|
||||
AddLogicFocus::InputLogicName => AddLogicFocus::InputTargetColumn,
|
||||
AddLogicFocus::InputTargetColumn => AddLogicFocus::InputDescription,
|
||||
AddLogicFocus::InputDescription => AddLogicFocus::InputScriptContent,
|
||||
AddLogicFocus::InputScriptContent => AddLogicFocus::SaveButton,
|
||||
AddLogicFocus::InputScriptContent => AddLogicFocus::SaveButton, // Move from script editor to Save
|
||||
AddLogicFocus::SaveButton => AddLogicFocus::CancelButton,
|
||||
AddLogicFocus::CancelButton => AddLogicFocus::InputLogicName,
|
||||
};
|
||||
|
||||
// Update canvas field index only when moving between canvas inputs
|
||||
if matches!(previous_focus, AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn) {
|
||||
if matches!(add_logic_state.current_focus, AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription) {
|
||||
let new_field = match add_logic_state.current_focus {
|
||||
@@ -91,10 +117,9 @@ pub fn handle_add_logic_navigation(
|
||||
}
|
||||
}
|
||||
|
||||
// Update focus outside canvas flag
|
||||
app_state.ui.focus_outside_canvas = !matches!(
|
||||
add_logic_state.current_focus,
|
||||
AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription
|
||||
AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription | AddLogicFocus::InputScriptContent
|
||||
);
|
||||
|
||||
*command_message = format!("Focus: {:?}", add_logic_state.current_focus);
|
||||
@@ -103,108 +128,125 @@ pub fn handle_add_logic_navigation(
|
||||
AddLogicFocus::InputDescription | AddLogicFocus::InputScriptContent);
|
||||
handled = true;
|
||||
}
|
||||
Some("prev_field") => {
|
||||
let previous_focus = add_logic_state.current_focus;
|
||||
Some("prev_field") => { // Shift-Tab
|
||||
let _previous_focus = add_logic_state.current_focus; // Keep for potential future use
|
||||
add_logic_state.current_focus = match add_logic_state.current_focus {
|
||||
AddLogicFocus::InputLogicName => AddLogicFocus::CancelButton,
|
||||
AddLogicFocus::InputTargetColumn => AddLogicFocus::InputLogicName,
|
||||
AddLogicFocus::InputDescription => AddLogicFocus::InputTargetColumn,
|
||||
AddLogicFocus::InputScriptContent => AddLogicFocus::InputDescription,
|
||||
AddLogicFocus::InputScriptContent => AddLogicFocus::InputDescription, // Move from script editor to Description
|
||||
AddLogicFocus::SaveButton => AddLogicFocus::InputScriptContent,
|
||||
AddLogicFocus::CancelButton => AddLogicFocus::SaveButton,
|
||||
};
|
||||
|
||||
// Update canvas field index only when moving between canvas inputs
|
||||
if matches!(previous_focus, AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription) {
|
||||
if matches!(add_logic_state.current_focus, AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn) {
|
||||
// This logic for setting single-line canvas field might need adjustment
|
||||
// if previous_focus was InputScriptContent
|
||||
if matches!(add_logic_state.current_focus, AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription) {
|
||||
let new_field_index = match add_logic_state.current_focus {
|
||||
AddLogicFocus::InputLogicName => 0,
|
||||
AddLogicFocus::InputTargetColumn => 1,
|
||||
AddLogicFocus::InputDescription => 2,
|
||||
_ => add_logic_state.current_field(), // Should not happen
|
||||
};
|
||||
add_logic_state.set_current_field(new_field_index);
|
||||
}
|
||||
|
||||
|
||||
app_state.ui.focus_outside_canvas = !matches!(
|
||||
add_logic_state.current_focus,
|
||||
AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription | AddLogicFocus::InputScriptContent
|
||||
);
|
||||
|
||||
*command_message = format!("Focus: {:?}", add_logic_state.current_focus);
|
||||
*is_edit_mode = matches!(add_logic_state.current_focus,
|
||||
AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn |
|
||||
AddLogicFocus::InputDescription | AddLogicFocus::InputScriptContent);
|
||||
handled = true;
|
||||
}
|
||||
// "next_option" and "previous_option" (likely right/left arrow in non-edit mode)
|
||||
// These typically navigate between focusable elements like buttons or cycle through fields.
|
||||
// If current focus is InputScriptContent and not in edit mode, these might scroll the content.
|
||||
Some("next_option") => {
|
||||
if !*is_edit_mode && add_logic_state.current_focus == AddLogicFocus::InputScriptContent {
|
||||
// TODO: Implement horizontal scroll for BasicMultilineEditor if needed
|
||||
// add_logic_state.script_editor.scroll_right();
|
||||
*command_message = "Horizontal scroll right (not implemented for script editor)".to_string();
|
||||
handled = true;
|
||||
} else {
|
||||
// Default horizontal navigation logic (copied from next_field for now, adjust as needed)
|
||||
add_logic_state.current_focus = match add_logic_state.current_focus {
|
||||
AddLogicFocus::InputLogicName => AddLogicFocus::InputTargetColumn,
|
||||
AddLogicFocus::InputTargetColumn => AddLogicFocus::InputDescription,
|
||||
AddLogicFocus::InputDescription => AddLogicFocus::InputScriptContent,
|
||||
AddLogicFocus::InputScriptContent => AddLogicFocus::SaveButton,
|
||||
AddLogicFocus::SaveButton => AddLogicFocus::CancelButton,
|
||||
AddLogicFocus::CancelButton => AddLogicFocus::InputLogicName,
|
||||
};
|
||||
if matches!(add_logic_state.current_focus, AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription) {
|
||||
let new_field = match add_logic_state.current_focus {
|
||||
AddLogicFocus::InputLogicName => 0,
|
||||
AddLogicFocus::InputTargetColumn => 1,
|
||||
_ => 0,
|
||||
AddLogicFocus::InputDescription => 2,
|
||||
_ => add_logic_state.current_field(),
|
||||
};
|
||||
add_logic_state.set_current_field(new_field);
|
||||
}
|
||||
app_state.ui.focus_outside_canvas = !matches!(
|
||||
add_logic_state.current_focus,
|
||||
AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription | AddLogicFocus::InputScriptContent
|
||||
);
|
||||
*command_message = format!("Focus: {:?}", add_logic_state.current_focus);
|
||||
*is_edit_mode = matches!(add_logic_state.current_focus,
|
||||
AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn |
|
||||
AddLogicFocus::InputDescription | AddLogicFocus::InputScriptContent);
|
||||
handled = true;
|
||||
}
|
||||
|
||||
// Update focus outside canvas flag
|
||||
app_state.ui.focus_outside_canvas = !matches!(
|
||||
add_logic_state.current_focus,
|
||||
AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription
|
||||
);
|
||||
|
||||
*command_message = format!("Focus: {:?}", add_logic_state.current_focus);
|
||||
*is_edit_mode = matches!(add_logic_state.current_focus,
|
||||
AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn |
|
||||
AddLogicFocus::InputDescription | AddLogicFocus::InputScriptContent);
|
||||
handled = true;
|
||||
}
|
||||
Some("next_option") => { // Horizontal next
|
||||
let previous_focus = add_logic_state.current_focus;
|
||||
add_logic_state.current_focus = match add_logic_state.current_focus {
|
||||
AddLogicFocus::InputLogicName => AddLogicFocus::InputTargetColumn,
|
||||
AddLogicFocus::InputTargetColumn => AddLogicFocus::InputDescription,
|
||||
AddLogicFocus::InputDescription => AddLogicFocus::InputScriptContent,
|
||||
AddLogicFocus::InputScriptContent => AddLogicFocus::SaveButton,
|
||||
AddLogicFocus::SaveButton => AddLogicFocus::CancelButton,
|
||||
AddLogicFocus::CancelButton => AddLogicFocus::InputLogicName, // Cycle back
|
||||
};
|
||||
// Update canvas field index if moving within canvas inputs
|
||||
if matches!(add_logic_state.current_focus, AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription) {
|
||||
let new_field = match add_logic_state.current_focus {
|
||||
AddLogicFocus::InputLogicName => 0,
|
||||
AddLogicFocus::InputTargetColumn => 1,
|
||||
AddLogicFocus::InputDescription => 2,
|
||||
_ => add_logic_state.current_field(), // Should not happen
|
||||
Some("previous_option") => {
|
||||
if !*is_edit_mode && add_logic_state.current_focus == AddLogicFocus::InputScriptContent {
|
||||
// TODO: Implement horizontal scroll for BasicMultilineEditor if needed
|
||||
// add_logic_state.script_editor.scroll_left();
|
||||
*command_message = "Horizontal scroll left (not implemented for script editor)".to_string();
|
||||
handled = true;
|
||||
} else {
|
||||
// Default horizontal navigation logic (copied from prev_field for now, adjust as needed)
|
||||
add_logic_state.current_focus = match add_logic_state.current_focus {
|
||||
AddLogicFocus::InputLogicName => AddLogicFocus::CancelButton,
|
||||
AddLogicFocus::InputTargetColumn => AddLogicFocus::InputLogicName,
|
||||
AddLogicFocus::InputDescription => AddLogicFocus::InputTargetColumn,
|
||||
AddLogicFocus::InputScriptContent => AddLogicFocus::InputDescription,
|
||||
AddLogicFocus::SaveButton => AddLogicFocus::InputScriptContent,
|
||||
AddLogicFocus::CancelButton => AddLogicFocus::SaveButton,
|
||||
};
|
||||
add_logic_state.set_current_field(new_field);
|
||||
if matches!(add_logic_state.current_focus, AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription) {
|
||||
let new_field = match add_logic_state.current_focus {
|
||||
AddLogicFocus::InputLogicName => 0,
|
||||
AddLogicFocus::InputTargetColumn => 1,
|
||||
AddLogicFocus::InputDescription => 2,
|
||||
_ => add_logic_state.current_field(),
|
||||
};
|
||||
add_logic_state.set_current_field(new_field);
|
||||
}
|
||||
app_state.ui.focus_outside_canvas = !matches!(
|
||||
add_logic_state.current_focus,
|
||||
AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription | AddLogicFocus::InputScriptContent
|
||||
);
|
||||
*command_message = format!("Focus: {:?}", add_logic_state.current_focus);
|
||||
*is_edit_mode = matches!(add_logic_state.current_focus,
|
||||
AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn |
|
||||
AddLogicFocus::InputDescription | AddLogicFocus::InputScriptContent);
|
||||
handled = true;
|
||||
}
|
||||
app_state.ui.focus_outside_canvas = !matches!(
|
||||
add_logic_state.current_focus,
|
||||
AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription
|
||||
);
|
||||
*command_message = format!("Focus: {:?}", add_logic_state.current_focus);
|
||||
*is_edit_mode = matches!(add_logic_state.current_focus,
|
||||
AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn |
|
||||
AddLogicFocus::InputDescription | AddLogicFocus::InputScriptContent);
|
||||
handled = true;
|
||||
}
|
||||
Some("previous_option") => { // Horizontal previous
|
||||
let previous_focus = add_logic_state.current_focus;
|
||||
add_logic_state.current_focus = match add_logic_state.current_focus {
|
||||
AddLogicFocus::InputLogicName => AddLogicFocus::CancelButton, // Cycle back
|
||||
AddLogicFocus::InputTargetColumn => AddLogicFocus::InputLogicName,
|
||||
AddLogicFocus::InputDescription => AddLogicFocus::InputTargetColumn,
|
||||
AddLogicFocus::InputScriptContent => AddLogicFocus::InputDescription,
|
||||
AddLogicFocus::SaveButton => AddLogicFocus::InputScriptContent,
|
||||
AddLogicFocus::CancelButton => AddLogicFocus::SaveButton,
|
||||
};
|
||||
// Update canvas field index if moving within canvas inputs
|
||||
if matches!(add_logic_state.current_focus, AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription) {
|
||||
let new_field = match add_logic_state.current_focus {
|
||||
AddLogicFocus::InputLogicName => 0,
|
||||
AddLogicFocus::InputTargetColumn => 1,
|
||||
AddLogicFocus::InputDescription => 2,
|
||||
_ => add_logic_state.current_field(), // Should not happen
|
||||
};
|
||||
add_logic_state.set_current_field(new_field);
|
||||
}
|
||||
app_state.ui.focus_outside_canvas = !matches!(
|
||||
add_logic_state.current_focus,
|
||||
AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn | AddLogicFocus::InputDescription
|
||||
);
|
||||
*command_message = format!("Focus: {:?}", add_logic_state.current_focus);
|
||||
*is_edit_mode = matches!(add_logic_state.current_focus,
|
||||
AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn |
|
||||
AddLogicFocus::InputDescription | AddLogicFocus::InputScriptContent);
|
||||
handled = true;
|
||||
}
|
||||
Some("select") => {
|
||||
Some("select") => { // Enter key in navigation mode
|
||||
match add_logic_state.current_focus {
|
||||
AddLogicFocus::SaveButton => {
|
||||
if let Some(table_def_id) = add_logic_state.selected_table_id {
|
||||
// Use the new get_script_content method
|
||||
let script_content = add_logic_state.get_script_content();
|
||||
if add_logic_state.target_column_input.trim().is_empty() {
|
||||
*command_message = "Cannot save: Target Column cannot be empty.".to_string();
|
||||
} else if add_logic_state.script_content_input.trim().is_empty() {
|
||||
} else if script_content.trim().is_empty() { // Check content from editor
|
||||
*command_message = "Cannot save: Script Content cannot be empty.".to_string();
|
||||
} else {
|
||||
*command_message = "Saving logic script...".to_string();
|
||||
@@ -213,7 +255,7 @@ pub fn handle_add_logic_navigation(
|
||||
let request = PostTableScriptRequest {
|
||||
table_definition_id: table_def_id,
|
||||
target_column: add_logic_state.target_column_input.trim().to_string(),
|
||||
script: add_logic_state.script_content_input.trim().to_string(),
|
||||
script: script_content.trim().to_string(), // Use content from editor
|
||||
description: add_logic_state.description_input.trim().to_string(),
|
||||
};
|
||||
|
||||
@@ -233,16 +275,19 @@ pub fn handle_add_logic_navigation(
|
||||
handled = true;
|
||||
}
|
||||
AddLogicFocus::CancelButton => {
|
||||
buffer_state.update_history(AppView::Admin); // Fixed: was AdminPanel
|
||||
buffer_state.update_history(AppView::Admin);
|
||||
app_state.ui.focus_outside_canvas = true;
|
||||
*command_message = "Cancelled Add Logic".to_string();
|
||||
handled = true;
|
||||
}
|
||||
// If Enter is pressed on an input field (including script content), switch to edit mode
|
||||
AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn |
|
||||
AddLogicFocus::InputDescription | AddLogicFocus::InputScriptContent => {
|
||||
if !*is_edit_mode {
|
||||
*is_edit_mode = true;
|
||||
*command_message = "Edit mode: ON".to_string();
|
||||
// Ensure focus_outside_canvas is false when entering edit mode for any input
|
||||
app_state.ui.focus_outside_canvas = false;
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
@@ -251,17 +296,34 @@ pub fn handle_add_logic_navigation(
|
||||
Some("toggle_edit_mode") => {
|
||||
*is_edit_mode = !*is_edit_mode;
|
||||
*command_message = format!("Edit mode: {}", if *is_edit_mode { "ON" } else { "OFF" });
|
||||
// When toggling edit mode, adjust focus_outside_canvas
|
||||
if *is_edit_mode {
|
||||
// If entering edit mode, and focus is on an input, ensure canvas has focus
|
||||
if matches!(add_logic_state.current_focus,
|
||||
AddLogicFocus::InputLogicName | AddLogicFocus::InputTargetColumn |
|
||||
AddLogicFocus::InputDescription | AddLogicFocus::InputScriptContent) {
|
||||
app_state.ui.focus_outside_canvas = false;
|
||||
}
|
||||
} else {
|
||||
// If exiting edit mode, and focus was on an input, it might now be considered "outside"
|
||||
// This depends on your desired navigation flow after exiting edit mode.
|
||||
// For now, let's assume it stays on the element but is no longer editable.
|
||||
// If you want focus to jump to a button, handle that explicitly.
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
// Handle script content scrolling when not in edit mode
|
||||
// Handle script content scrolling when not in edit mode using arrow keys
|
||||
_ if !*is_edit_mode && add_logic_state.current_focus == AddLogicFocus::InputScriptContent => {
|
||||
// This specific block for action.as_deref() might be redundant if
|
||||
// KeyCode::Up/Down are handled directly above for edit mode.
|
||||
// However, if "move_up"/"move_down" are distinct actions in your config for non-edit mode scrolling:
|
||||
match action.as_deref() {
|
||||
Some("move_up") => {
|
||||
add_logic_state.script_content_scroll.0 = add_logic_state.script_content_scroll.0.saturating_sub(1);
|
||||
add_logic_state.script_editor.move_cursor_up(); // Or a dedicated scroll_up method
|
||||
handled = true;
|
||||
}
|
||||
Some("move_down") => {
|
||||
add_logic_state.script_content_scroll.0 = add_logic_state.script_content_scroll.0.saturating_add(1);
|
||||
add_logic_state.script_editor.move_cursor_down(); // Or a dedicated scroll_down method
|
||||
handled = true;
|
||||
}
|
||||
_ => {}
|
||||
@@ -272,4 +334,3 @@ pub fn handle_add_logic_navigation(
|
||||
}
|
||||
handled
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
// src/state/pages/add_logic.rs
|
||||
use crate::state::pages::canvas_state::CanvasState;
|
||||
// Use your existing BasicMultilineEditor
|
||||
use crate::state::pages::multi_canvas_state::BasicMultilineEditor;
|
||||
// You might also need the trait if you pass it around as dyn MultilineEditorState
|
||||
use crate::state::pages::multi_canvas_state::MultilineEditorState;
|
||||
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub enum AddLogicFocus {
|
||||
#[default]
|
||||
InputLogicName,
|
||||
InputTargetColumn,
|
||||
InputScriptContent,
|
||||
InputScriptContent, // This will now refer to the BasicMultilineEditor
|
||||
InputDescription,
|
||||
SaveButton,
|
||||
CancelButton,
|
||||
@@ -19,14 +24,16 @@ pub struct AddLogicState {
|
||||
pub selected_table_name: Option<String>,
|
||||
pub logic_name_input: String,
|
||||
pub target_column_input: String,
|
||||
pub script_content_input: String,
|
||||
// pub script_content_input: String, // Remove this
|
||||
pub description_input: String,
|
||||
pub current_focus: AddLogicFocus,
|
||||
pub logic_name_cursor_pos: usize,
|
||||
pub target_column_cursor_pos: usize,
|
||||
pub script_content_scroll: (u16, u16), // (vertical, horizontal)
|
||||
// pub script_content_scroll: (u16, u16), // Remove this
|
||||
pub description_cursor_pos: usize,
|
||||
pub has_unsaved_changes: bool,
|
||||
pub has_unsaved_changes: bool, // For the single-line canvas fields
|
||||
// Add this field using your existing struct
|
||||
pub script_editor: BasicMultilineEditor,
|
||||
}
|
||||
|
||||
impl Default for AddLogicState {
|
||||
@@ -37,30 +44,49 @@ impl Default for AddLogicState {
|
||||
selected_table_name: None,
|
||||
logic_name_input: String::new(),
|
||||
target_column_input: String::new(),
|
||||
script_content_input: String::new(),
|
||||
description_input: String::new(),
|
||||
current_focus: AddLogicFocus::InputLogicName,
|
||||
logic_name_cursor_pos: 0,
|
||||
target_column_cursor_pos: 0,
|
||||
script_content_scroll: (0, 0),
|
||||
description_cursor_pos: 0,
|
||||
has_unsaved_changes: false,
|
||||
// Initialize your BasicMultilineEditor
|
||||
script_editor: BasicMultilineEditor::new(
|
||||
None, // No initial content
|
||||
Some("Steel Script Content".to_string()), // Optional label
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AddLogicState {
|
||||
// Number of canvas-editable fields
|
||||
// Number of canvas-editable fields (for the top single-line inputs)
|
||||
pub const INPUT_FIELD_COUNT: usize = 3; // Logic Name, Target Column, Description
|
||||
|
||||
// Method to get script content from the editor
|
||||
pub fn get_script_content(&self) -> String {
|
||||
// Assuming BasicMultilineEditor will have a method to get all lines as a single string
|
||||
// If not, we'll need to add it to BasicMultilineEditor or implement it here.
|
||||
// For now, let's assume it's:
|
||||
self.script_editor.lines().join("\n")
|
||||
}
|
||||
|
||||
// Method to set script content in the editor (e.g., when loading to edit)
|
||||
pub fn set_script_content(&mut self, content: &str) {
|
||||
let lines: Vec<String> = content.lines().map(String::from).collect();
|
||||
self.script_editor.set_lines(lines);
|
||||
// self.script_editor.set_has_unsaved_changes(false); // If this is initial population
|
||||
}
|
||||
}
|
||||
|
||||
// The CanvasState implementation remains for the single-line inputs.
|
||||
impl CanvasState for AddLogicState {
|
||||
fn current_field(&self) -> usize {
|
||||
match self.current_focus {
|
||||
AddLogicFocus::InputLogicName => 0,
|
||||
AddLogicFocus::InputTargetColumn => 1,
|
||||
AddLogicFocus::InputDescription => 2,
|
||||
_ => 0, // Default or non-input focus
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,6 +100,10 @@ impl CanvasState for AddLogicState {
|
||||
}
|
||||
|
||||
fn has_unsaved_changes(&self) -> bool {
|
||||
// This refers to the single-line fields.
|
||||
// The BasicMultilineEditor has its own `has_unsaved_changes`.
|
||||
// You might want a combined status:
|
||||
// self.has_unsaved_changes || self.script_editor.has_unsaved_changes()
|
||||
self.has_unsaved_changes
|
||||
}
|
||||
|
||||
@@ -95,11 +125,12 @@ impl CanvasState for AddLogicState {
|
||||
}
|
||||
|
||||
fn get_current_input_mut(&mut self) -> &mut String {
|
||||
self.has_unsaved_changes = true;
|
||||
match self.current_focus {
|
||||
AddLogicFocus::InputLogicName => &mut self.logic_name_input,
|
||||
AddLogicFocus::InputTargetColumn => &mut self.target_column_input,
|
||||
AddLogicFocus::InputDescription => &mut self.description_input,
|
||||
_ => &mut self.logic_name_input, // Placeholder, should not be hit if focus is correct
|
||||
_ => &mut self.logic_name_input,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,20 +143,23 @@ impl CanvasState for AddLogicState {
|
||||
0 => AddLogicFocus::InputLogicName,
|
||||
1 => AddLogicFocus::InputTargetColumn,
|
||||
2 => AddLogicFocus::InputDescription,
|
||||
_ => self.current_focus, // Stay if out of bounds
|
||||
_ => self.current_focus,
|
||||
};
|
||||
}
|
||||
|
||||
fn set_current_cursor_pos(&mut self, pos: usize) {
|
||||
match self.current_focus {
|
||||
AddLogicFocus::InputLogicName => {
|
||||
self.logic_name_cursor_pos = pos.min(self.logic_name_input.len());
|
||||
self.logic_name_cursor_pos =
|
||||
pos.min(self.logic_name_input.chars().count());
|
||||
}
|
||||
AddLogicFocus::InputTargetColumn => {
|
||||
self.target_column_cursor_pos = pos.min(self.target_column_input.len());
|
||||
self.target_column_cursor_pos =
|
||||
pos.min(self.target_column_input.chars().count());
|
||||
}
|
||||
AddLogicFocus::InputDescription => {
|
||||
self.description_cursor_pos = pos.min(self.description_input.len());
|
||||
self.description_cursor_pos =
|
||||
pos.min(self.description_input.chars().count());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user