different structure of the library
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
// canvas/src/gui/autocomplete.rs
|
// canvas/src/autocomplete/gui.rs
|
||||||
|
|
||||||
#[cfg(feature = "gui")]
|
#[cfg(feature = "gui")]
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
@@ -9,7 +9,9 @@ use ratatui::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::autocomplete::AutocompleteState;
|
use crate::autocomplete::AutocompleteState;
|
||||||
use super::theme::CanvasTheme;
|
|
||||||
|
#[cfg(feature = "gui")]
|
||||||
|
use crate::canvas::theme::CanvasTheme;
|
||||||
|
|
||||||
#[cfg(feature = "gui")]
|
#[cfg(feature = "gui")]
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::UnicodeWidthStr;
|
||||||
8
canvas/src/autocomplete/mod.rs
Normal file
8
canvas/src/autocomplete/mod.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// src/autocomplete/mod.rs
|
||||||
|
pub mod types;
|
||||||
|
pub mod gui;
|
||||||
|
pub mod state; // Add this line
|
||||||
|
|
||||||
|
// Re-export autocomplete types
|
||||||
|
pub use types::{SuggestionItem, AutocompleteState};
|
||||||
|
pub use state::AutocompleteCanvasState; // Add this line
|
||||||
@@ -1,100 +1,6 @@
|
|||||||
// canvas/src/state.rs
|
// canvas/src/state.rs
|
||||||
|
|
||||||
use crate::actions::CanvasAction;
|
use crate::canvas::state::CanvasState;
|
||||||
|
|
||||||
/// Context passed to feature-specific action handlers
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ActionContext {
|
|
||||||
pub key_code: Option<crossterm::event::KeyCode>, // Kept for backwards compatibility
|
|
||||||
pub ideal_cursor_column: usize,
|
|
||||||
pub current_input: String,
|
|
||||||
pub current_field: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Core trait that any form-like state must implement to work with the canvas system.
|
|
||||||
/// This enables the same mode behaviors (edit, read-only, highlight) to work across
|
|
||||||
/// any implementation - login forms, data entry forms, configuration screens, etc.
|
|
||||||
pub trait CanvasState {
|
|
||||||
// --- Core Navigation ---
|
|
||||||
fn current_field(&self) -> usize;
|
|
||||||
fn current_cursor_pos(&self) -> usize;
|
|
||||||
fn set_current_field(&mut self, index: usize);
|
|
||||||
fn set_current_cursor_pos(&mut self, pos: usize);
|
|
||||||
|
|
||||||
// --- Data Access ---
|
|
||||||
fn get_current_input(&self) -> &str;
|
|
||||||
fn get_current_input_mut(&mut self) -> &mut String;
|
|
||||||
fn inputs(&self) -> Vec<&String>;
|
|
||||||
fn fields(&self) -> Vec<&str>;
|
|
||||||
|
|
||||||
// --- State Management ---
|
|
||||||
fn has_unsaved_changes(&self) -> bool;
|
|
||||||
fn set_has_unsaved_changes(&mut self, changed: bool);
|
|
||||||
|
|
||||||
// --- LEGACY AUTOCOMPLETE SUPPORT (for backwards compatibility) ---
|
|
||||||
|
|
||||||
/// Legacy suggestion support (deprecated - use AutocompleteCanvasState for rich features)
|
|
||||||
fn get_suggestions(&self) -> Option<&[String]> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Legacy selected suggestion index (deprecated)
|
|
||||||
fn get_selected_suggestion_index(&self) -> Option<usize> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Legacy suggestion index setter (deprecated)
|
|
||||||
fn set_selected_suggestion_index(&mut self, _index: Option<usize>) {
|
|
||||||
// Default: no-op
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Legacy activate suggestions (deprecated)
|
|
||||||
fn activate_suggestions(&mut self, _suggestions: Vec<String>) {
|
|
||||||
// Default: no-op
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Legacy deactivate suggestions (deprecated)
|
|
||||||
fn deactivate_suggestions(&mut self) {
|
|
||||||
// Default: no-op
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Feature-specific action handling ---
|
|
||||||
|
|
||||||
/// Feature-specific action handling (NEW: Type-safe)
|
|
||||||
fn handle_feature_action(&mut self, _action: &CanvasAction, _context: &ActionContext) -> Option<String> {
|
|
||||||
None // Default: no feature-specific handling
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Legacy string-based action handling (for backwards compatibility)
|
|
||||||
fn handle_feature_action_legacy(&mut self, action: &str, context: &ActionContext) -> Option<String> {
|
|
||||||
// Convert string to typed action and delegate
|
|
||||||
let typed_action = match action {
|
|
||||||
"insert_char" => {
|
|
||||||
// This is tricky - we need the char from the KeyCode in context
|
|
||||||
if let Some(crossterm::event::KeyCode::Char(c)) = context.key_code {
|
|
||||||
CanvasAction::InsertChar(c)
|
|
||||||
} else {
|
|
||||||
CanvasAction::Custom(action.to_string())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => CanvasAction::from_string(action),
|
|
||||||
};
|
|
||||||
self.handle_feature_action(&typed_action, context)
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Display Overrides (for links, computed values, etc.) ---
|
|
||||||
|
|
||||||
fn get_display_value_for_field(&self, index: usize) -> &str {
|
|
||||||
self.inputs()
|
|
||||||
.get(index)
|
|
||||||
.map(|s| s.as_str())
|
|
||||||
.unwrap_or("")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn has_display_override(&self, _index: usize) -> bool {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// OPTIONAL extension trait for states that want rich autocomplete functionality.
|
/// OPTIONAL extension trait for states that want rich autocomplete functionality.
|
||||||
/// Only implement this if you need the new autocomplete features.
|
/// Only implement this if you need the new autocomplete features.
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
// canvas/src/actions/edit.rs
|
// canvas/src/actions/edit.rs
|
||||||
|
|
||||||
use crate::state::{CanvasState, AutocompleteCanvasState, ActionContext};
|
use crate::canvas::state::{CanvasState, ActionContext};
|
||||||
use crate::actions::types::{CanvasAction, ActionResult};
|
use crate::autocomplete::state::AutocompleteCanvasState;
|
||||||
|
use crate::canvas::actions::types::{CanvasAction, ActionResult};
|
||||||
use crossterm::event::{KeyCode, KeyEvent};
|
use crossterm::event::{KeyCode, KeyEvent};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// canvas/src/gui/canvas.rs
|
// canvas/src/canvas/gui.rs
|
||||||
|
|
||||||
#[cfg(feature = "gui")]
|
#[cfg(feature = "gui")]
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
@@ -9,9 +9,11 @@ use ratatui::{
|
|||||||
Frame,
|
Frame,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::state::CanvasState;
|
use crate::canvas::state::CanvasState;
|
||||||
use crate::modes::HighlightState;
|
use crate::canvas::modes::HighlightState;
|
||||||
use super::theme::CanvasTheme;
|
|
||||||
|
#[cfg(feature = "gui")]
|
||||||
|
use crate::canvas::theme::CanvasTheme;
|
||||||
|
|
||||||
#[cfg(feature = "gui")]
|
#[cfg(feature = "gui")]
|
||||||
use std::cmp::{max, min};
|
use std::cmp::{max, min};
|
||||||
14
canvas/src/canvas/mod.rs
Normal file
14
canvas/src/canvas/mod.rs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
// src/canvas/mod.rs
|
||||||
|
pub mod actions;
|
||||||
|
pub mod modes;
|
||||||
|
pub mod gui;
|
||||||
|
pub mod theme;
|
||||||
|
pub mod state; // Add this line
|
||||||
|
|
||||||
|
// Re-export commonly used canvas types
|
||||||
|
pub use actions::{CanvasAction, ActionResult};
|
||||||
|
pub use modes::{AppMode, ModeManager, HighlightState};
|
||||||
|
pub use state::{CanvasState, ActionContext}; // Add this line
|
||||||
|
|
||||||
|
#[cfg(feature = "gui")]
|
||||||
|
pub use theme::CanvasTheme;
|
||||||
97
canvas/src/canvas/state.rs
Normal file
97
canvas/src/canvas/state.rs
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
// canvas/src/state.rs
|
||||||
|
|
||||||
|
use crate::canvas::actions::CanvasAction;
|
||||||
|
|
||||||
|
/// Context passed to feature-specific action handlers
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ActionContext {
|
||||||
|
pub key_code: Option<crossterm::event::KeyCode>, // Kept for backwards compatibility
|
||||||
|
pub ideal_cursor_column: usize,
|
||||||
|
pub current_input: String,
|
||||||
|
pub current_field: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Core trait that any form-like state must implement to work with the canvas system.
|
||||||
|
/// This enables the same mode behaviors (edit, read-only, highlight) to work across
|
||||||
|
/// any implementation - login forms, data entry forms, configuration screens, etc.
|
||||||
|
pub trait CanvasState {
|
||||||
|
// --- Core Navigation ---
|
||||||
|
fn current_field(&self) -> usize;
|
||||||
|
fn current_cursor_pos(&self) -> usize;
|
||||||
|
fn set_current_field(&mut self, index: usize);
|
||||||
|
fn set_current_cursor_pos(&mut self, pos: usize);
|
||||||
|
|
||||||
|
// --- Data Access ---
|
||||||
|
fn get_current_input(&self) -> &str;
|
||||||
|
fn get_current_input_mut(&mut self) -> &mut String;
|
||||||
|
fn inputs(&self) -> Vec<&String>;
|
||||||
|
fn fields(&self) -> Vec<&str>;
|
||||||
|
|
||||||
|
// --- State Management ---
|
||||||
|
fn has_unsaved_changes(&self) -> bool;
|
||||||
|
fn set_has_unsaved_changes(&mut self, changed: bool);
|
||||||
|
|
||||||
|
// --- LEGACY AUTOCOMPLETE SUPPORT (for backwards compatibility) ---
|
||||||
|
|
||||||
|
/// Legacy suggestion support (deprecated - use AutocompleteCanvasState for rich features)
|
||||||
|
fn get_suggestions(&self) -> Option<&[String]> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Legacy selected suggestion index (deprecated)
|
||||||
|
fn get_selected_suggestion_index(&self) -> Option<usize> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Legacy suggestion index setter (deprecated)
|
||||||
|
fn set_selected_suggestion_index(&mut self, _index: Option<usize>) {
|
||||||
|
// Default: no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Legacy activate suggestions (deprecated)
|
||||||
|
fn activate_suggestions(&mut self, _suggestions: Vec<String>) {
|
||||||
|
// Default: no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Legacy deactivate suggestions (deprecated)
|
||||||
|
fn deactivate_suggestions(&mut self) {
|
||||||
|
// Default: no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Feature-specific action handling ---
|
||||||
|
|
||||||
|
/// Feature-specific action handling (NEW: Type-safe)
|
||||||
|
fn handle_feature_action(&mut self, _action: &CanvasAction, _context: &ActionContext) -> Option<String> {
|
||||||
|
None // Default: no feature-specific handling
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Legacy string-based action handling (for backwards compatibility)
|
||||||
|
fn handle_feature_action_legacy(&mut self, action: &str, context: &ActionContext) -> Option<String> {
|
||||||
|
// Convert string to typed action and delegate
|
||||||
|
let typed_action = match action {
|
||||||
|
"insert_char" => {
|
||||||
|
// This is tricky - we need the char from the KeyCode in context
|
||||||
|
if let Some(crossterm::event::KeyCode::Char(c)) = context.key_code {
|
||||||
|
CanvasAction::InsertChar(c)
|
||||||
|
} else {
|
||||||
|
CanvasAction::Custom(action.to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => CanvasAction::from_string(action),
|
||||||
|
};
|
||||||
|
self.handle_feature_action(&typed_action, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Display Overrides (for links, computed values, etc.) ---
|
||||||
|
|
||||||
|
fn get_display_value_for_field(&self, index: usize) -> &str {
|
||||||
|
self.inputs()
|
||||||
|
.get(index)
|
||||||
|
.map(|s| s.as_str())
|
||||||
|
.unwrap_or("")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_display_override(&self, _index: usize) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -476,5 +476,5 @@ impl CanvasConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Re-export for convenience
|
// Re-export for convenience
|
||||||
pub use crate::actions::CanvasAction;
|
pub use crate::canvas::actions::CanvasAction;
|
||||||
pub use crate::dispatcher::ActionDispatcher;
|
pub use crate::dispatcher::ActionDispatcher;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// canvas/src/dispatcher.rs
|
// canvas/src/dispatcher.rs
|
||||||
|
|
||||||
use crate::state::CanvasState;
|
use crate::canvas::state::CanvasState;
|
||||||
use crate::actions::{CanvasAction, ActionResult, execute_canvas_action};
|
use crate::canvas::actions::{CanvasAction, ActionResult, execute_canvas_action};
|
||||||
|
|
||||||
/// High-level action dispatcher that coordinates between different action types
|
/// High-level action dispatcher that coordinates between different action types
|
||||||
pub struct ActionDispatcher;
|
pub struct ActionDispatcher;
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
// canvas/src/gui/mod.rs
|
|
||||||
|
|
||||||
#[cfg(feature = "gui")]
|
|
||||||
pub mod canvas;
|
|
||||||
|
|
||||||
#[cfg(feature = "gui")]
|
|
||||||
pub mod autocomplete;
|
|
||||||
|
|
||||||
#[cfg(feature = "gui")]
|
|
||||||
pub mod theme;
|
|
||||||
|
|
||||||
// Export the separate rendering functions
|
|
||||||
#[cfg(feature = "gui")]
|
|
||||||
pub use canvas::render_canvas;
|
|
||||||
|
|
||||||
#[cfg(feature = "gui")]
|
|
||||||
pub use autocomplete::render_autocomplete_dropdown;
|
|
||||||
|
|
||||||
#[cfg(feature = "gui")]
|
|
||||||
pub use theme::CanvasTheme;
|
|
||||||
@@ -1,25 +1,19 @@
|
|||||||
// canvas/src/lib.rs
|
// src/lib.rs
|
||||||
|
pub mod canvas;
|
||||||
pub mod actions;
|
pub mod autocomplete;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod dispatcher;
|
pub mod dispatcher;
|
||||||
pub mod state;
|
|
||||||
pub mod suggestions; // Keep for backwards compatibility
|
pub mod suggestions; // Keep for backwards compatibility
|
||||||
pub mod autocomplete; // NEW: Core autocomplete functionality
|
|
||||||
pub mod modes;
|
// Re-export from modules
|
||||||
|
pub use canvas::{CanvasAction, ActionResult, AppMode, ModeManager, HighlightState};
|
||||||
|
|
||||||
#[cfg(feature = "gui")]
|
#[cfg(feature = "gui")]
|
||||||
pub mod gui;
|
pub use canvas::CanvasTheme;
|
||||||
|
|
||||||
// Re-export commonly used types
|
pub use autocomplete::{SuggestionItem, AutocompleteState};
|
||||||
pub use actions::{CanvasAction, ActionResult};
|
|
||||||
pub use dispatcher::ActionDispatcher;
|
pub use dispatcher::ActionDispatcher;
|
||||||
pub use state::{CanvasState, ActionContext};
|
pub use canvas::state::{CanvasState, ActionContext}; // Fixed path
|
||||||
pub use autocomplete::{SuggestionItem, AutocompleteState}; // NEW
|
|
||||||
pub use modes::{AppMode, ModeManager, HighlightState};
|
|
||||||
|
|
||||||
#[cfg(feature = "gui")]
|
// Backwards compatibility
|
||||||
pub use gui::{render_canvas, CanvasTheme};
|
|
||||||
|
|
||||||
// Keep backwards compatibility exports
|
|
||||||
pub use suggestions::SuggestionState;
|
pub use suggestions::SuggestionState;
|
||||||
|
|||||||
Reference in New Issue
Block a user