add_logic is now using canvas library
This commit is contained in:
@@ -3,7 +3,7 @@ use crate::config::colors::themes::Theme;
|
|||||||
use crate::state::app::highlight::HighlightState;
|
use crate::state::app::highlight::HighlightState;
|
||||||
use crate::state::app::state::AppState;
|
use crate::state::app::state::AppState;
|
||||||
use crate::state::pages::add_logic::{AddLogicFocus, AddLogicState};
|
use crate::state::pages::add_logic::{AddLogicFocus, AddLogicState};
|
||||||
use crate::state::pages::canvas_state::CanvasState;
|
use canvas::canvas::{render_canvas, CanvasState, HighlightState as CanvasHighlightState}; // Use canvas library
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
layout::{Alignment, Constraint, Direction, Layout, Rect},
|
layout::{Alignment, Constraint, Direction, Layout, Rect},
|
||||||
style::{Modifier, Style},
|
style::{Modifier, Style},
|
||||||
@@ -11,10 +11,18 @@ use ratatui::{
|
|||||||
widgets::{Block, BorderType, Borders, Paragraph},
|
widgets::{Block, BorderType, Borders, Paragraph},
|
||||||
Frame,
|
Frame,
|
||||||
};
|
};
|
||||||
use crate::components::handlers::canvas::render_canvas;
|
|
||||||
use crate::components::common::{dialog, autocomplete}; // Added autocomplete
|
use crate::components::common::{dialog, autocomplete}; // Added autocomplete
|
||||||
use crate::config::binds::config::EditorKeybindingMode;
|
use crate::config::binds::config::EditorKeybindingMode;
|
||||||
|
|
||||||
|
// Helper function to convert between HighlightState types
|
||||||
|
fn convert_highlight_state(local: &HighlightState) -> CanvasHighlightState {
|
||||||
|
match local {
|
||||||
|
HighlightState::Off => CanvasHighlightState::Off,
|
||||||
|
HighlightState::Characterwise { anchor } => CanvasHighlightState::Characterwise { anchor: *anchor },
|
||||||
|
HighlightState::Linewise { anchor_line } => CanvasHighlightState::Linewise { anchor_line: *anchor_line },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn render_add_logic(
|
pub fn render_add_logic(
|
||||||
f: &mut Frame,
|
f: &mut Frame,
|
||||||
area: Rect,
|
area: Rect,
|
||||||
@@ -152,40 +160,37 @@ pub fn render_add_logic(
|
|||||||
);
|
);
|
||||||
f.render_widget(profile_text, top_info_area);
|
f.render_widget(profile_text, top_info_area);
|
||||||
|
|
||||||
// Canvas
|
// Canvas - USING CANVAS LIBRARY
|
||||||
let focus_on_canvas_inputs = matches!(
|
let focus_on_canvas_inputs = matches!(
|
||||||
add_logic_state.current_focus,
|
add_logic_state.current_focus,
|
||||||
AddLogicFocus::InputLogicName
|
AddLogicFocus::InputLogicName
|
||||||
| AddLogicFocus::InputTargetColumn
|
| AddLogicFocus::InputTargetColumn
|
||||||
| AddLogicFocus::InputDescription
|
| AddLogicFocus::InputDescription
|
||||||
);
|
);
|
||||||
// Call render_canvas and get the active_field_rect
|
|
||||||
|
let canvas_highlight_state = convert_highlight_state(highlight_state);
|
||||||
let active_field_rect = render_canvas(
|
let active_field_rect = render_canvas(
|
||||||
f,
|
f,
|
||||||
canvas_area,
|
canvas_area,
|
||||||
add_logic_state, // Pass the whole state as it impl CanvasState
|
add_logic_state, // AddLogicState implements CanvasState
|
||||||
&add_logic_state.fields(),
|
theme, // Theme implements CanvasTheme
|
||||||
&add_logic_state.current_field(),
|
is_edit_mode && focus_on_canvas_inputs,
|
||||||
&add_logic_state.inputs(),
|
&canvas_highlight_state,
|
||||||
theme,
|
|
||||||
is_edit_mode && focus_on_canvas_inputs, // is_edit_mode for canvas fields
|
|
||||||
highlight_state,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// --- Render Autocomplete for Target Column ---
|
// --- Render Autocomplete for Target Column ---
|
||||||
// `is_edit_mode` here refers to the general edit mode of the EventHandler
|
// `is_edit_mode` here refers to the general edit mode of the EventHandler
|
||||||
if is_edit_mode && add_logic_state.current_field() == 1 { // Target Column field
|
if is_edit_mode && add_logic_state.current_field() == 1 { // Target Column field
|
||||||
if let Some(suggestions) = add_logic_state.get_suggestions() { // Uses CanvasState impl
|
if add_logic_state.in_target_column_suggestion_mode && add_logic_state.show_target_column_suggestions {
|
||||||
let selected = add_logic_state.get_selected_suggestion_index();
|
if !add_logic_state.target_column_suggestions.is_empty() {
|
||||||
if !suggestions.is_empty() { // Only render if there are suggestions to show
|
|
||||||
if let Some(input_rect) = active_field_rect {
|
if let Some(input_rect) = active_field_rect {
|
||||||
autocomplete::render_autocomplete_dropdown(
|
autocomplete::render_autocomplete_dropdown(
|
||||||
f,
|
f,
|
||||||
input_rect,
|
input_rect,
|
||||||
f.area(), // Full frame area for clamping
|
f.area(), // Full frame area for clamping
|
||||||
theme,
|
theme,
|
||||||
suggestions,
|
&add_logic_state.target_column_suggestions,
|
||||||
selected,
|
add_logic_state.selected_target_column_suggestion_index,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// src/functions/modes/edit/add_logic_e.rs
|
// src/functions/modes/edit/add_logic_e.rs
|
||||||
use crate::state::pages::add_logic::AddLogicState;
|
use crate::state::pages::add_logic::AddLogicState;
|
||||||
use crate::state::pages::canvas_state::CanvasState;
|
use canvas::canvas::CanvasState;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use crossterm::event::{KeyCode, KeyEvent};
|
use crossterm::event::{KeyCode, KeyEvent};
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// src/functions/modes/read_only/add_logic_ro.rs
|
// src/functions/modes/read_only/add_logic_ro.rs
|
||||||
use crate::config::binds::key_sequences::KeySequenceTracker;
|
use crate::config::binds::key_sequences::KeySequenceTracker;
|
||||||
use crate::state::pages::add_logic::AddLogicState; // Changed
|
use crate::state::pages::add_logic::AddLogicState; // Changed
|
||||||
use crate::state::pages::canvas_state::CanvasState;
|
|
||||||
use crate::state::app::state::AppState;
|
use crate::state::app::state::AppState;
|
||||||
|
use canvas::canvas::CanvasState;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
// Word navigation helpers (get_char_type, find_next_word_start, etc.)
|
// Word navigation helpers (get_char_type, find_next_word_start, etc.)
|
||||||
|
|||||||
@@ -357,10 +357,10 @@ pub async fn handle_edit_event(
|
|||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
} else if app_state.ui.show_add_logic {
|
} else if app_state.ui.show_add_logic {
|
||||||
// FIX: Pass &mut event_handler.ideal_cursor_column
|
// NEW: Use unified canvas handler instead of add_logic_e::execute_edit_action
|
||||||
add_logic_e::execute_edit_action(
|
handle_canvas_state_edit(
|
||||||
action_str,
|
|
||||||
key,
|
key,
|
||||||
|
config,
|
||||||
&mut admin_state.add_logic_state,
|
&mut admin_state.add_logic_state,
|
||||||
&mut event_handler.ideal_cursor_column,
|
&mut event_handler.ideal_cursor_column,
|
||||||
)
|
)
|
||||||
@@ -408,10 +408,10 @@ pub async fn handle_edit_event(
|
|||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
} else if app_state.ui.show_add_logic {
|
} else if app_state.ui.show_add_logic {
|
||||||
// FIX: Pass &mut event_handler.ideal_cursor_column
|
// NEW: Use unified canvas handler instead of add_logic_e::execute_edit_action
|
||||||
add_logic_e::execute_edit_action(
|
handle_canvas_state_edit(
|
||||||
"insert_char",
|
|
||||||
key,
|
key,
|
||||||
|
config,
|
||||||
&mut admin_state.add_logic_state,
|
&mut admin_state.add_logic_state,
|
||||||
&mut event_handler.ideal_cursor_column,
|
&mut event_handler.ideal_cursor_column,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// src/state/pages/add_logic.rs
|
// src/state/pages/add_logic.rs
|
||||||
use crate::config::binds::config::{EditorConfig, EditorKeybindingMode};
|
use crate::config::binds::config::{EditorConfig, EditorKeybindingMode};
|
||||||
use crate::state::pages::canvas_state::CanvasState;
|
use canvas::canvas::{CanvasState, ActionContext, CanvasAction}; // External library
|
||||||
use crate::components::common::text_editor::{TextEditor, VimState};
|
use crate::components::common::text_editor::{TextEditor, VimState};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
@@ -225,6 +225,46 @@ impl AddLogicState {
|
|||||||
self.script_editor_trigger_position = None;
|
self.script_editor_trigger_position = None;
|
||||||
self.script_editor_filter_text.clear();
|
self.script_editor_filter_text.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Helper method to validate and save logic
|
||||||
|
pub fn save_logic(&mut self) -> Option<String> {
|
||||||
|
if self.logic_name_input.trim().is_empty() {
|
||||||
|
return Some("Logic name is required".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.target_column_input.trim().is_empty() {
|
||||||
|
return Some("Target column is required".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
let script_content = {
|
||||||
|
let editor_borrow = self.script_content_editor.borrow();
|
||||||
|
editor_borrow.lines().join("\n")
|
||||||
|
};
|
||||||
|
|
||||||
|
if script_content.trim().is_empty() {
|
||||||
|
return Some("Script content is required".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here you would typically save to database/storage
|
||||||
|
// For now, just clear the form and mark as saved
|
||||||
|
self.has_unsaved_changes = false;
|
||||||
|
Some(format!("Logic '{}' saved successfully", self.logic_name_input.trim()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper method to clear the form
|
||||||
|
pub fn clear_form(&mut self) -> Option<String> {
|
||||||
|
let profile = self.profile_name.clone();
|
||||||
|
let table_id = self.selected_table_id;
|
||||||
|
let table_name = self.selected_table_name.clone();
|
||||||
|
let editor_config = EditorConfig::default(); // You might want to preserve the actual config
|
||||||
|
|
||||||
|
*self = Self::new(&editor_config);
|
||||||
|
self.profile_name = profile;
|
||||||
|
self.selected_table_id = table_id;
|
||||||
|
self.selected_table_name = table_name;
|
||||||
|
|
||||||
|
Some("Form cleared".to_string())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for AddLogicState {
|
impl Default for AddLogicState {
|
||||||
@@ -233,59 +273,18 @@ impl Default for AddLogicState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Implement external library's CanvasState for AddLogicState
|
||||||
impl CanvasState for AddLogicState {
|
impl CanvasState for AddLogicState {
|
||||||
fn current_field(&self) -> usize {
|
fn current_field(&self) -> usize {
|
||||||
match self.current_focus {
|
match self.current_focus {
|
||||||
AddLogicFocus::InputLogicName => 0,
|
AddLogicFocus::InputLogicName => 0,
|
||||||
AddLogicFocus::InputTargetColumn => 1,
|
AddLogicFocus::InputTargetColumn => 1,
|
||||||
AddLogicFocus::InputDescription => 2,
|
AddLogicFocus::InputDescription => 2,
|
||||||
|
// If focus is elsewhere, return the last canvas field used
|
||||||
_ => self.last_canvas_field,
|
_ => self.last_canvas_field,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn current_cursor_pos(&self) -> usize {
|
|
||||||
match self.current_focus {
|
|
||||||
AddLogicFocus::InputLogicName => self.logic_name_cursor_pos,
|
|
||||||
AddLogicFocus::InputTargetColumn => self.target_column_cursor_pos,
|
|
||||||
AddLogicFocus::InputDescription => self.description_cursor_pos,
|
|
||||||
_ => 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn has_unsaved_changes(&self) -> bool {
|
|
||||||
self.has_unsaved_changes
|
|
||||||
}
|
|
||||||
|
|
||||||
fn inputs(&self) -> Vec<&String> {
|
|
||||||
vec![
|
|
||||||
&self.logic_name_input,
|
|
||||||
&self.target_column_input,
|
|
||||||
&self.description_input,
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_current_input(&self) -> &str {
|
|
||||||
match self.current_focus {
|
|
||||||
AddLogicFocus::InputLogicName => &self.logic_name_input,
|
|
||||||
AddLogicFocus::InputTargetColumn => &self.target_column_input,
|
|
||||||
AddLogicFocus::InputDescription => &self.description_input,
|
|
||||||
_ => "",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_current_input_mut(&mut self) -> &mut String {
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fields(&self) -> Vec<&str> {
|
|
||||||
vec!["Logic Name", "Target Column", "Description"]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_current_field(&mut self, index: usize) {
|
fn set_current_field(&mut self, index: usize) {
|
||||||
let new_focus = match index {
|
let new_focus = match index {
|
||||||
0 => AddLogicFocus::InputLogicName,
|
0 => AddLogicFocus::InputLogicName,
|
||||||
@@ -303,6 +302,15 @@ impl CanvasState for AddLogicState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn current_cursor_pos(&self) -> usize {
|
||||||
|
match self.current_focus {
|
||||||
|
AddLogicFocus::InputLogicName => self.logic_name_cursor_pos,
|
||||||
|
AddLogicFocus::InputTargetColumn => self.target_column_cursor_pos,
|
||||||
|
AddLogicFocus::InputDescription => self.description_cursor_pos,
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn set_current_cursor_pos(&mut self, pos: usize) {
|
fn set_current_cursor_pos(&mut self, pos: usize) {
|
||||||
match self.current_focus {
|
match self.current_focus {
|
||||||
AddLogicFocus::InputLogicName => {
|
AddLogicFocus::InputLogicName => {
|
||||||
@@ -318,29 +326,117 @@ impl CanvasState for AddLogicState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_current_input(&self) -> &str {
|
||||||
|
match self.current_focus {
|
||||||
|
AddLogicFocus::InputLogicName => &self.logic_name_input,
|
||||||
|
AddLogicFocus::InputTargetColumn => &self.target_column_input,
|
||||||
|
AddLogicFocus::InputDescription => &self.description_input,
|
||||||
|
_ => "", // Should not happen if called correctly
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_current_input_mut(&mut self) -> &mut String {
|
||||||
|
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, // Fallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inputs(&self) -> Vec<&String> {
|
||||||
|
vec![
|
||||||
|
&self.logic_name_input,
|
||||||
|
&self.target_column_input,
|
||||||
|
&self.description_input,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fields(&self) -> Vec<&str> {
|
||||||
|
vec!["Logic Name", "Target Column", "Description"]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_unsaved_changes(&self) -> bool {
|
||||||
|
self.has_unsaved_changes
|
||||||
|
}
|
||||||
|
|
||||||
fn set_has_unsaved_changes(&mut self, changed: bool) {
|
fn set_has_unsaved_changes(&mut self, changed: bool) {
|
||||||
self.has_unsaved_changes = changed;
|
self.has_unsaved_changes = changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_suggestions(&self) -> Option<&[String]> {
|
fn handle_feature_action(&mut self, action: &CanvasAction, _context: &ActionContext) -> Option<String> {
|
||||||
if self.current_field() == 1
|
match action {
|
||||||
&& self.in_target_column_suggestion_mode
|
// Handle saving logic script
|
||||||
&& self.show_target_column_suggestions
|
CanvasAction::Custom(action_str) if action_str == "save_logic" => {
|
||||||
{
|
self.save_logic()
|
||||||
Some(&self.target_column_suggestions)
|
}
|
||||||
|
|
||||||
|
// Handle clearing the form
|
||||||
|
CanvasAction::Custom(action_str) if action_str == "clear_form" => {
|
||||||
|
self.clear_form()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle target column autocomplete activation
|
||||||
|
CanvasAction::Custom(action_str) if action_str == "activate_autocomplete" => {
|
||||||
|
if self.current_field() == 1 { // Target Column field
|
||||||
|
self.in_target_column_suggestion_mode = true;
|
||||||
|
self.update_target_column_suggestions();
|
||||||
|
Some("Autocomplete activated".to_string())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_selected_suggestion_index(&self) -> Option<usize> {
|
// Handle target column suggestion selection
|
||||||
if self.current_field() == 1
|
CanvasAction::Custom(action_str) if action_str == "select_suggestion" => {
|
||||||
&& self.in_target_column_suggestion_mode
|
if self.current_field() == 1 && self.in_target_column_suggestion_mode {
|
||||||
&& self.show_target_column_suggestions
|
if let Some(selected_idx) = self.selected_target_column_suggestion_index {
|
||||||
{
|
if let Some(suggestion) = self.target_column_suggestions.get(selected_idx) {
|
||||||
self.selected_target_column_suggestion_index
|
self.target_column_input = suggestion.clone();
|
||||||
} else {
|
self.target_column_cursor_pos = suggestion.len();
|
||||||
|
self.in_target_column_suggestion_mode = false;
|
||||||
|
self.show_target_column_suggestions = false;
|
||||||
|
self.has_unsaved_changes = true;
|
||||||
|
return Some(format!("Selected: {}", suggestion));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Custom validation when moving between fields
|
||||||
|
CanvasAction::NextField => {
|
||||||
|
match self.current_field() {
|
||||||
|
0 => { // Logic Name field
|
||||||
|
if self.logic_name_input.trim().is_empty() {
|
||||||
|
Some("Logic name cannot be empty".to_string())
|
||||||
|
} else {
|
||||||
|
None // Let canvas library handle the normal field movement
|
||||||
|
}
|
||||||
|
}
|
||||||
|
1 => { // Target Column field
|
||||||
|
// Update suggestions when entering target column field
|
||||||
|
self.update_target_column_suggestions();
|
||||||
|
None
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle character insertion with validation
|
||||||
|
CanvasAction::InsertChar(c) => {
|
||||||
|
if self.current_field() == 1 { // Target Column field
|
||||||
|
// Update suggestions after character insertion
|
||||||
|
// Note: Canvas library will handle the actual insertion
|
||||||
|
// This is just for triggering suggestion updates
|
||||||
|
None // Let canvas handle insertion, then we'll update suggestions
|
||||||
|
} else {
|
||||||
|
None // Let canvas handle normally
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let canvas library handle everything else
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user