167 lines
4.8 KiB
Rust
167 lines
4.8 KiB
Rust
// src/editor/core.rs
|
|
|
|
#[cfg(feature = "cursor-style")]
|
|
use crate::canvas::CursorManager;
|
|
|
|
use crate::canvas::modes::AppMode;
|
|
use crate::canvas::state::EditorState;
|
|
use crate::DataProvider;
|
|
#[cfg(feature = "suggestions")]
|
|
use crate::SuggestionItem;
|
|
|
|
// NEW: Import keymap types when keymap feature is enabled
|
|
#[cfg(feature = "keymap")]
|
|
use crate::keymap::{CanvasKeyMap, KeySequenceTracker};
|
|
|
|
pub struct FormEditor<D: DataProvider> {
|
|
pub(crate) ui_state: EditorState,
|
|
pub(crate) data_provider: D,
|
|
#[cfg(feature = "suggestions")]
|
|
pub(crate) suggestions: Vec<SuggestionItem>,
|
|
|
|
#[cfg(feature = "validation")]
|
|
pub(crate) external_validation_callback: Option<
|
|
Box<
|
|
dyn FnMut(usize, &str) -> crate::validation::ExternalValidationState
|
|
+ Send
|
|
+ Sync,
|
|
>,
|
|
>,
|
|
|
|
// NEW: Injected keymap and sequence tracker (keymap feature only)
|
|
#[cfg(feature = "keymap")]
|
|
pub(crate) keymap: Option<CanvasKeyMap>,
|
|
#[cfg(feature = "keymap")]
|
|
pub(crate) seq_tracker: KeySequenceTracker,
|
|
}
|
|
|
|
impl<D: DataProvider> FormEditor<D> {
|
|
// Make helpers visible to sibling modules in this crate
|
|
pub(crate) fn char_to_byte_index(s: &str, char_idx: usize) -> usize {
|
|
s.char_indices()
|
|
.nth(char_idx)
|
|
.map(|(byte_idx, _)| byte_idx)
|
|
.unwrap_or_else(|| s.len())
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub(crate) fn byte_to_char_index(s: &str, byte_idx: usize) -> usize {
|
|
s[..byte_idx].chars().count()
|
|
}
|
|
|
|
pub fn new(data_provider: D) -> Self {
|
|
let editor = Self {
|
|
ui_state: EditorState::new(),
|
|
data_provider,
|
|
#[cfg(feature = "suggestions")]
|
|
suggestions: Vec::new(),
|
|
#[cfg(feature = "validation")]
|
|
external_validation_callback: None,
|
|
// NEW: Initialize keymap fields
|
|
#[cfg(feature = "keymap")]
|
|
keymap: None,
|
|
#[cfg(feature = "keymap")]
|
|
seq_tracker: KeySequenceTracker::new(400), // 400ms default timeout
|
|
};
|
|
|
|
#[cfg(feature = "validation")]
|
|
{
|
|
let mut editor = editor;
|
|
editor.initialize_validation();
|
|
|
|
#[cfg(feature = "cursor-style")]
|
|
{
|
|
let _ = CursorManager::update_for_mode(editor.ui_state.current_mode);
|
|
}
|
|
editor
|
|
}
|
|
#[cfg(not(feature = "validation"))]
|
|
{
|
|
#[cfg(feature = "cursor-style")]
|
|
{
|
|
let _ = CursorManager::update_for_mode(editor.ui_state.current_mode);
|
|
}
|
|
editor
|
|
}
|
|
}
|
|
|
|
// NEW: Keymap management methods (keymap feature only)
|
|
|
|
/// Set the keymap for this editor instance
|
|
#[cfg(feature = "keymap")]
|
|
pub fn set_keymap(&mut self, keymap: CanvasKeyMap) {
|
|
self.keymap = Some(keymap);
|
|
}
|
|
|
|
/// Check if this editor has a keymap configured
|
|
#[cfg(feature = "keymap")]
|
|
pub fn has_keymap(&self) -> bool {
|
|
self.keymap.is_some()
|
|
}
|
|
|
|
/// Set the timeout for multi-key sequences (in milliseconds)
|
|
#[cfg(feature = "keymap")]
|
|
pub fn set_key_sequence_timeout_ms(&mut self, timeout_ms: u64) {
|
|
self.seq_tracker = KeySequenceTracker::new(timeout_ms);
|
|
}
|
|
|
|
// Library-internal, used by multiple modules
|
|
pub(crate) fn current_text(&self) -> &str {
|
|
let field_index = self.ui_state.current_field;
|
|
if field_index < self.data_provider.field_count() {
|
|
self.data_provider.field_value(field_index)
|
|
} else {
|
|
""
|
|
}
|
|
}
|
|
|
|
// Read-only getters
|
|
pub fn current_field(&self) -> usize {
|
|
self.ui_state.current_field()
|
|
}
|
|
pub fn cursor_position(&self) -> usize {
|
|
self.ui_state.cursor_position()
|
|
}
|
|
pub fn mode(&self) -> AppMode {
|
|
self.ui_state.mode()
|
|
}
|
|
#[cfg(feature = "suggestions")]
|
|
pub fn is_suggestions_active(&self) -> bool {
|
|
self.ui_state.is_suggestions_active()
|
|
}
|
|
pub fn ui_state(&self) -> &EditorState {
|
|
&self.ui_state
|
|
}
|
|
pub fn data_provider(&self) -> &D {
|
|
&self.data_provider
|
|
}
|
|
pub fn data_provider_mut(&mut self) -> &mut D {
|
|
&mut self.data_provider
|
|
}
|
|
#[cfg(feature = "suggestions")]
|
|
pub fn suggestions(&self) -> &[SuggestionItem] {
|
|
&self.suggestions
|
|
}
|
|
|
|
#[cfg(feature = "validation")]
|
|
pub fn validation_state(&self) -> &crate::validation::ValidationState {
|
|
self.ui_state.validation_state()
|
|
}
|
|
|
|
// Cursor cleanup
|
|
#[cfg(feature = "cursor-style")]
|
|
pub fn cleanup_cursor(&self) -> std::io::Result<()> {
|
|
CursorManager::reset()
|
|
}
|
|
#[cfg(not(feature = "cursor-style"))]
|
|
pub fn cleanup_cursor(&self) -> std::io::Result<()> {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl<D: DataProvider> Drop for FormEditor<D> {
|
|
fn drop(&mut self) {
|
|
let _ = self.cleanup_cursor();
|
|
}
|
|
}
|