diff --git a/canvas/examples/validation_1.rs b/canvas/examples/validation_1.rs index f89510b..07d0d5c 100644 --- a/canvas/examples/validation_1.rs +++ b/canvas/examples/validation_1.rs @@ -34,7 +34,6 @@ use ratatui::{ widgets::{Block, Borders, Paragraph, Wrap}, Frame, Terminal, }; - use canvas::{ canvas::{ gui::render_canvas_default, @@ -62,10 +61,8 @@ struct ValidationFormEditor { impl ValidationFormEditor { fn new(data_provider: D) -> Self { let mut editor = FormEditor::new(data_provider); - // Enable validation by default editor.set_validation_enabled(true); - Self { editor, has_unsaved_changes: false, @@ -98,7 +95,6 @@ impl ValidationFormEditor { fn toggle_validation(&mut self) { self.validation_enabled = !self.validation_enabled; self.editor.set_validation_enabled(self.validation_enabled); - if self.validation_enabled { self.debug_message = "βœ… Validation ENABLED - Try exceeding limits!".to_string(); } else { @@ -110,14 +106,12 @@ impl ValidationFormEditor { if !self.validation_enabled { return (true, None); } - let can_switch = self.editor.can_switch_fields(); let reason = if !can_switch { self.editor.field_switch_block_reason() } else { None }; - (can_switch, reason) } @@ -125,11 +119,9 @@ impl ValidationFormEditor { if !self.validation_enabled { return "❌ DISABLED".to_string(); } - if self.field_switch_blocked { return "🚫 SWITCH BLOCKED".to_string(); } - let summary = self.editor.validation_summary(); if summary.has_errors() { format!("❌ {} ERRORS", summary.error_fields) @@ -162,7 +154,6 @@ impl ValidationFormEditor { for i in 0..field_count { self.editor.validate_field(i); } - let summary = self.editor.validation_summary(); self.debug_message = format!( "πŸ” Validated all fields: {} valid, {} warnings, {} errors", @@ -250,7 +241,6 @@ impl ValidationFormEditor { if !self.validation_enabled { return; } - if let Some(result) = self.editor.current_field_validation() { match result { ValidationResult::Valid => { @@ -298,7 +288,10 @@ impl ValidationFormEditor { ValidationResult::Valid => { // Don't spam with valid messages, just show character count if applicable if let Some(limits) = self.get_current_field_limits() { - if let Some(status) = limits.status_text(self.editor.current_text()) { + let field_index = self.editor.current_field(); + if let Some(status) = limits.status_text( + self.editor.data_provider().field_value(field_index) + ) { self.debug_message = format!("✏️ {}", status); } } @@ -537,7 +530,6 @@ fn handle_key_press( editor.enter_edit_mode(); editor.clear_command_buffer(); } - // Escape: Exit edit mode (_, KeyCode::Esc, _) => { if mode == AppMode::Edit { @@ -630,7 +622,6 @@ fn handle_key_press( summary.validated_fields )); } - _ => { if editor.has_pending_command() { editor.clear_command_buffer(); @@ -662,7 +653,6 @@ fn run_app( } } } - Ok(()) } @@ -706,27 +696,27 @@ fn render_validation_status( }; let validation_status = editor.get_validation_status(); + let status_text = if editor.has_pending_command() { - format!("-- {} -- {} [{}] | Validation: {}", + format!("-- {} -- {} [{}] | Validation: {}", mode_text, editor.debug_message(), editor.get_command_buffer(), validation_status) } else if editor.has_unsaved_changes() { - format!("-- {} -- [Modified] {} | Validation: {}", + format!("-- {} -- [Modified] {} | Validation: {}", mode_text, editor.debug_message(), validation_status) } else { - format!("-- {} -- {} | Validation: {}", + format!("-- {} -- {} | Validation: {}", mode_text, editor.debug_message(), validation_status) }; let status = Paragraph::new(Line::from(Span::raw(status_text))) .block(Block::default().borders(Borders::ALL).title("πŸ” Validation Status")); - f.render_widget(status, chunks[0]); // Validation summary with field switching info let summary = editor.editor.validation_summary(); let summary_text = if editor.validation_enabled { let switch_info = if editor.field_switch_blocked { - format!("\n🚫 Field switching blocked: {}", + format!("\n🚫 Field switching blocked: {}", editor.block_reason.as_deref().unwrap_or("Unknown reason")) } else { let (can_switch, reason) = editor.check_field_switch_allowed(); @@ -765,7 +755,6 @@ fn render_validation_status( .block(Block::default().borders(Borders::ALL).title("πŸ“ˆ Validation Overview")) .style(summary_style) .wrap(Wrap { trim: true }); - f.render_widget(validation_summary, chunks[1]); // Enhanced help text @@ -792,7 +781,6 @@ fn render_validation_status( .block(Block::default().borders(Borders::ALL).title("πŸš€ Validation Commands")) .style(Style::default().fg(Color::Gray)) .wrap(Wrap { trim: true }); - f.render_widget(help, chunks[2]); } @@ -817,10 +805,10 @@ fn main() -> Result<(), Box> { let data = ValidationDemoData::new(); let mut editor = ValidationFormEditor::new(data); - + // Initialize with normal mode - library automatically sets block cursor editor.set_mode(AppMode::ReadOnly); - + // Demonstrate that CursorManager is available and working CursorManager::update_for_mode(AppMode::ReadOnly)?; diff --git a/canvas/examples/validation_2.rs b/canvas/examples/validation_2.rs index 24e8fa6..a20078f 100644 --- a/canvas/examples/validation_2.rs +++ b/canvas/examples/validation_2.rs @@ -71,12 +71,12 @@ impl AdvancedPatternFormEditor { // ... (keeping all the same methods as before for brevity) // [All the previous methods: clear_command_buffer, add_to_command_buffer, etc.] - + fn clear_command_buffer(&mut self) { self.command_buffer.clear(); } fn add_to_command_buffer(&mut self, ch: char) { self.command_buffer.push(ch); } fn get_command_buffer(&self) -> &str { &self.command_buffer } fn has_pending_command(&self) -> bool { !self.command_buffer.is_empty() } - + fn toggle_validation(&mut self) { self.validation_enabled = !self.validation_enabled; self.editor.set_validation_enabled(self.validation_enabled); @@ -89,14 +89,14 @@ impl AdvancedPatternFormEditor { fn move_left(&mut self) { self.editor.move_left(); self.field_switch_blocked = false; self.block_reason = None; } fn move_right(&mut self) { self.editor.move_right(); self.field_switch_blocked = false; self.block_reason = None; } - + fn move_up(&mut self) { match self.editor.move_up() { Ok(()) => { self.update_field_validation_status(); self.field_switch_blocked = false; self.block_reason = None; } Err(e) => { self.field_switch_blocked = true; self.block_reason = Some(e.to_string()); self.debug_message = format!("🚫 Field switch blocked: {}", e); } } } - + fn move_down(&mut self) { match self.editor.move_down() { Ok(()) => { self.update_field_validation_status(); self.field_switch_blocked = false; self.block_reason = None; } @@ -106,19 +106,19 @@ impl AdvancedPatternFormEditor { fn move_line_start(&mut self) { self.editor.move_line_start(); } fn move_line_end(&mut self) { self.editor.move_line_end(); } - + fn enter_edit_mode(&mut self) { // Library will automatically update cursor to bar | in insert mode self.editor.enter_edit_mode(); self.debug_message = "✏️ INSERT MODE - Cursor: Steady Bar | - Testing advanced pattern validation".to_string(); } - + fn enter_append_mode(&mut self) { // Library will automatically update cursor to bar | in insert mode self.editor.enter_append_mode(); self.debug_message = "✏️ INSERT (append) - Cursor: Steady Bar | - Advanced patterns active".to_string(); } - + fn exit_edit_mode(&mut self) { // Library will automatically update cursor to block β–ˆ in normal mode self.editor.exit_edit_mode(); @@ -156,7 +156,10 @@ impl AdvancedPatternFormEditor { fn current_field(&self) -> usize { self.editor.current_field() } fn cursor_position(&self) -> usize { self.editor.cursor_position() } fn mode(&self) -> AppMode { self.editor.mode() } - fn current_text(&self) -> &str { self.editor.current_text() } + fn current_text(&self) -> &str { + let field_index = self.editor.current_field(); + self.editor.data_provider().field_value(field_index) + } fn data_provider(&self) -> &D { self.editor.data_provider() } fn ui_state(&self) -> &canvas::EditorState { self.editor.ui_state() } fn set_mode(&mut self, mode: AppMode) { self.editor.set_mode(mode); } @@ -384,7 +387,7 @@ impl DataProvider for AdvancedPatternData { // Even positions (0,2,4...): vowels (a,e,i,o,u) // Odd positions (1,3,5...): consonants let vowels = ['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U']; - + // For demo purposes, we'll just accept alphabetic characters // In real usage, you'd implement the alternating logic based on position c.is_alphabetic() diff --git a/canvas/examples/validation_3.rs b/canvas/examples/validation_3.rs index 25d7474..f0a5106 100644 --- a/canvas/examples/validation_3.rs +++ b/canvas/examples/validation_3.rs @@ -108,7 +108,7 @@ impl MaskDemoFormEditor { fn get_current_field_info(&self) -> (String, String, String) { let field_index = self.editor.current_field(); - let raw_data = self.editor.current_text(); + let raw_data = self.editor.data_provider().field_value(field_index); let display_data = if self.validation_enabled { self.editor.current_display_text() } else { @@ -117,8 +117,8 @@ impl MaskDemoFormEditor { let mask_info = if let Some(config) = self.editor.validation_state().get_field_config(field_index) { if let Some(mask) = &config.display_mask { - format!("Pattern: '{}', Mode: {:?}", - mask.pattern(), + format!("Pattern: '{}', Mode: {:?}", + mask.pattern(), mask.display_mode()) } else { "No mask configured".to_string() @@ -131,13 +131,13 @@ impl MaskDemoFormEditor { } // === ENHANCED MOVEMENT WITH MASK AWARENESS === - fn move_left(&mut self) { - self.editor.move_left(); + fn move_left(&mut self) { + self.editor.move_left(); self.update_cursor_info(); } - fn move_right(&mut self) { - self.editor.move_right(); + fn move_right(&mut self) { + self.editor.move_right(); self.update_cursor_info(); } @@ -155,13 +155,13 @@ impl MaskDemoFormEditor { } } - fn move_line_start(&mut self) { - self.editor.move_line_start(); + fn move_line_start(&mut self) { + self.editor.move_line_start(); self.update_cursor_info(); } - fn move_line_end(&mut self) { - self.editor.move_line_end(); + fn move_line_end(&mut self) { + self.editor.move_line_end(); self.update_cursor_info(); } @@ -237,12 +237,15 @@ impl MaskDemoFormEditor { fn current_field(&self) -> usize { self.editor.current_field() } fn cursor_position(&self) -> usize { self.editor.cursor_position() } fn mode(&self) -> AppMode { self.editor.mode() } - fn current_text(&self) -> &str { self.editor.current_text() } + fn current_text(&self) -> &str { + let field_index = self.editor.current_field(); + self.editor.data_provider().field_value(field_index) + } fn data_provider(&self) -> &D { self.editor.data_provider() } fn ui_state(&self) -> &canvas::EditorState { self.editor.ui_state() } - fn set_mode(&mut self, mode: AppMode) { + fn set_mode(&mut self, mode: AppMode) { // Library automatically updates cursor for the mode - self.editor.set_mode(mode); + self.editor.set_mode(mode); } fn next_field(&mut self) { @@ -265,7 +268,7 @@ impl MaskDemoFormEditor { fn show_mask_details(&mut self) { let (raw, display, mask_info) = self.get_current_field_info(); - self.debug_message = format!("πŸ” Field {}: {} | Raw: '{}' Display: '{}'", + self.debug_message = format!("πŸ” Field {}: {} | Raw: '{}' Display: '{}'", self.current_field() + 1, mask_info, raw, display); } @@ -331,11 +334,11 @@ impl DataProvider for MaskDemoData { .build()) } 1 => { - // πŸ“ž Phone (Template) - FIXED: Perfect mask/limit coordination + // πŸ“ž Phone (Template) - FIXED: Perfect mask/limit coordination let phone_template = DisplayMask::new("(###) ###-####", '#') .with_template('_'); Some(ValidationConfigBuilder::new() - .with_display_mask(phone_template) + .with_display_mask(phone_template) .with_max_length(10) // βœ… CRITICAL: Exactly matches 10 input positions .build()) } @@ -361,7 +364,7 @@ impl DataProvider for MaskDemoData { let ssn_mask = DisplayMask::new("XXX-XX-XXXX", 'X'); Some(ValidationConfigBuilder::new() .with_display_mask(ssn_mask) - .with_max_length(9) // βœ… CRITICAL: Exactly matches 9 input positions + .with_max_length(9) // βœ… CRITICAL: Exactly matches 9 input positions .build()) } 6 => { @@ -595,9 +598,9 @@ fn render_mask_status( }; let mask_status = editor.get_mask_status(); - let status_text = format!("-- {} -- {} | Masks: {} | View: {}", - mode_text, - editor.debug_message(), + let status_text = format!("-- {} -- {} | Masks: {} | View: {}", + mode_text, + editor.debug_message(), mask_status, if editor.show_raw_data { "RAW" } else { "FORMATTED" }); @@ -609,7 +612,7 @@ fn render_mask_status( // Data comparison showing raw vs display let (raw_data, display_data, mask_info) = editor.get_current_field_info(); let field_name = editor.data_provider().field_name(editor.current_field()); - + let comparison_text = format!( "πŸ“ Current Field: {}\n\ πŸ”§ Mask Config: {}\n\ @@ -648,7 +651,7 @@ fn render_mask_status( β€’ Dynamic vs Template modes β€’ Custom separators β€’ Different input chars\n\ \n\ Commands: i/a=insert, m=mask details, r=toggle raw/display view\n\ - Movement: hjkl/arrows=move, 0=$=line start/end, Tab=next field, F1=toggle masks\n\ + Movement: hjkl/arrows=move, 0/$=line start/end, Tab=next field, F1=toggle masks\n\ ?=detailed info, Ctrl+C=quit" } AppMode::Edit => { diff --git a/canvas/examples/validation_4.rs b/canvas/examples/validation_4.rs index d807fed..66d11ec 100644 --- a/canvas/examples/validation_4.rs +++ b/canvas/examples/validation_4.rs @@ -1,6 +1,6 @@ /* examples/validation_4.rs Enhanced Feature 4 Demo: Multiple custom formatters with comprehensive edge cases - + Demonstrates: - Multiple formatter types: PSC, Phone, Credit Card, Date - Edge case handling: incomplete input, invalid chars, overflow @@ -53,18 +53,18 @@ impl CustomFormatter for PSCFormatter { if raw.is_empty() { return FormattingResult::success(""); } - + // Validate: only digits allowed if !raw.chars().all(|c| c.is_ascii_digit()) { return FormattingResult::error("PSC must contain only digits"); } - + let len = raw.chars().count(); match len { 0 => FormattingResult::success(""), 1..=3 => FormattingResult::success(raw), 4 => FormattingResult::warning( - format!("{} ", &raw[..3]), + format!("{} ", &raw[..3]), "PSC incomplete (4/5 digits)" ), 5 => { @@ -88,12 +88,12 @@ impl CustomFormatter for PhoneFormatter { if raw.is_empty() { return FormattingResult::success(""); } - + // Only digits allowed if !raw.chars().all(|c| c.is_ascii_digit()) { return FormattingResult::error("Phone must contain only digits"); } - + let len = raw.chars().count(); match len { 0 => FormattingResult::success(""), @@ -120,11 +120,11 @@ impl CustomFormatter for CreditCardFormatter { if raw.is_empty() { return FormattingResult::success(""); } - + if !raw.chars().all(|c| c.is_ascii_digit()) { return FormattingResult::error("Card number must contain only digits"); } - + let mut formatted = String::new(); for (i, ch) in raw.chars().enumerate() { if i > 0 && i % 4 == 0 { @@ -132,7 +132,7 @@ impl CustomFormatter for CreditCardFormatter { } formatted.push(ch); } - + let len = raw.chars().count(); match len { 0..=15 => FormattingResult::warning(formatted, format!("Card incomplete ({}/16 digits)", len)), @@ -155,11 +155,11 @@ impl CustomFormatter for DateFormatter { if raw.is_empty() { return FormattingResult::success(""); } - + if !raw.chars().all(|c| c.is_ascii_digit()) { return FormattingResult::error("Date must contain only digits"); } - + let len = raw.len(); match len { 0 => FormattingResult::success(""), @@ -170,11 +170,11 @@ impl CustomFormatter for DateFormatter { let month = &raw[..2]; let day = &raw[2..4]; let year = &raw[4..]; - + // Basic validation let m: u32 = month.parse().unwrap_or(0); let d: u32 = day.parse().unwrap_or(0); - + if m == 0 || m > 12 { FormattingResult::warning( format!("{}/{}/{}", month, day, year), @@ -217,15 +217,15 @@ impl DataProvider for MultiFormatterDemoData { fn field_count(&self) -> usize { self.fields.len() } - + fn field_name(&self, index: usize) -> &str { &self.fields[index].0 } - + fn field_value(&self, index: usize) -> &str { &self.fields[index].1 } - + fn set_field_value(&mut self, index: usize, value: String) { self.fields[index].1 = value; } @@ -288,7 +288,7 @@ impl EnhancedDemoEditor { match self.editor.current_field() { 0 => "PSC", 1 => "Phone", - 2 => "Credit Card", + 2 => "Credit Card", 3 => "Date", _ => "Plain Text", } @@ -320,16 +320,16 @@ impl EnhancedDemoEditor { // Edge cases vec!["00000", "0000000000", "0000000000000000", "99", "13012024", ""], ]; - + self.example_mode = (self.example_mode + 1) % examples.len(); let current_examples = &examples[self.example_mode]; - + for (i, example) in current_examples.iter().enumerate() { if i < self.editor.data_provider().field_count() { self.editor.data_provider_mut().set_field_value(i, example.to_string()); } } - + let mode_names = ["Valid Examples", "Incomplete Input", "Invalid Characters", "Edge Cases"]; self.debug_message = format!("πŸ“‹ Loaded: {}", mode_names[self.example_mode]); } @@ -364,9 +364,10 @@ impl EnhancedDemoEditor { } fn get_current_field_analysis(&self) -> (String, String, String, Option) { - let raw = self.editor.current_text(); + let field_index = self.editor.current_field(); + let raw = self.editor.data_provider().field_value(field_index); let display = self.editor.current_display_text(); - + let status = if raw == display { if self.has_formatter() { if self.mode() == AppMode::Edit { @@ -445,9 +446,10 @@ impl EnhancedDemoEditor { let raw_pos = self.editor.cursor_position(); let display_pos = self.editor.display_cursor_position(); - let raw = self.editor.current_text(); + let field_index = self.editor.current_field(); + let raw = self.editor.data_provider().field_value(field_index); let display = self.editor.current_display_text(); - + if raw_pos != display_pos { self.debug_message = format!( "πŸ—ΊοΈ Position mapping: Raw[{}]='{}' ↔ Display[{}]='{}'", @@ -468,7 +470,7 @@ impl EnhancedDemoEditor { fn data_provider(&self) -> &D { self.editor.data_provider() } fn data_provider_mut(&mut self) -> &mut D { self.editor.data_provider_mut() } fn ui_state(&self) -> &canvas::EditorState { self.editor.ui_state() } - + fn move_up(&mut self) { let _ = self.editor.move_up(); } fn move_down(&mut self) { let _ = self.editor.move_down(); } fn move_left(&mut self) { let _ = self.editor.move_left(); } @@ -488,7 +490,7 @@ fn handle_key_press( let mode = editor.mode(); // Quit - if matches!(key, KeyCode::F(10)) || + if matches!(key, KeyCode::F(10)) || (key == KeyCode::Char('q') && modifiers.contains(KeyModifiers::CONTROL)) || (key == KeyCode::Char('c') && modifiers.contains(KeyModifiers::CONTROL)) { return Ok(false); @@ -530,7 +532,7 @@ fn handle_key_press( let (raw, display, status, warning) = editor.get_current_field_analysis(); let warning_text = warning.map(|w| format!(" ⚠️ {}", w)).unwrap_or_default(); editor.debug_message = format!( - "πŸ” Field {}: {} | Raw: '{}' | Display: '{}'{}", + "πŸ” Field {}: {} | Raw: '{}' | Display: '{}'{}", editor.current_field() + 1, status, raw, display, warning_text ); }, @@ -618,7 +620,7 @@ fn render_enhanced_status( let (raw, display, status, warning) = editor.get_current_field_analysis(); let field_name = editor.data_provider().field_name(editor.current_field()); let field_type = editor.current_field_type(); - + let mut analysis_lines = vec![ format!("πŸ“ Current: {} ({})", field_name, field_type), format!("πŸ”§ Status: {}", status),