379 lines
12 KiB
Rust
379 lines
12 KiB
Rust
// examples/basic_usage.rs
|
|
//! Basic usage patterns and quick start guide
|
|
//!
|
|
//! This example demonstrates the core patterns for using the canvas crate:
|
|
//! 1. Implementing CanvasState
|
|
//! 2. Using the ActionDispatcher
|
|
//! 3. Handling different types of actions
|
|
//! 4. Working with suggestions
|
|
//!
|
|
//! Run with: cargo run --example basic_usage
|
|
|
|
use canvas::prelude::*;
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
println!("🎨 Canvas Crate - Basic Usage Patterns");
|
|
println!("=====================================\n");
|
|
|
|
// Example 1: Minimal form implementation
|
|
example_1_minimal_form();
|
|
|
|
// Example 2: Form with suggestions
|
|
example_2_with_suggestions();
|
|
|
|
// Example 3: Custom actions
|
|
example_3_custom_actions().await;
|
|
|
|
// Example 4: Batch operations
|
|
example_4_batch_operations().await;
|
|
}
|
|
|
|
// Example 1: Minimal form - just the required methods
|
|
fn example_1_minimal_form() {
|
|
println!("📝 Example 1: Minimal Form Implementation");
|
|
|
|
#[derive(Debug)]
|
|
struct SimpleForm {
|
|
current_field: usize,
|
|
cursor_pos: usize,
|
|
name: String,
|
|
email: String,
|
|
has_changes: bool,
|
|
}
|
|
|
|
impl SimpleForm {
|
|
fn new() -> Self {
|
|
Self {
|
|
current_field: 0,
|
|
cursor_pos: 0,
|
|
name: String::new(),
|
|
email: String::new(),
|
|
has_changes: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl CanvasState for SimpleForm {
|
|
fn current_field(&self) -> usize { self.current_field }
|
|
fn current_cursor_pos(&self) -> usize { self.cursor_pos }
|
|
fn set_current_field(&mut self, index: usize) { self.current_field = index.min(1); }
|
|
fn set_current_cursor_pos(&mut self, pos: usize) { self.cursor_pos = pos; }
|
|
|
|
fn get_current_input(&self) -> &str {
|
|
match self.current_field {
|
|
0 => &self.name,
|
|
1 => &self.email,
|
|
_ => "",
|
|
}
|
|
}
|
|
|
|
fn get_current_input_mut(&mut self) -> &mut String {
|
|
match self.current_field {
|
|
0 => &mut self.name,
|
|
1 => &mut self.email,
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
fn inputs(&self) -> Vec<&String> { vec![&self.name, &self.email] }
|
|
fn fields(&self) -> Vec<&str> { vec!["Name", "Email"] }
|
|
fn has_unsaved_changes(&self) -> bool { self.has_changes }
|
|
fn set_has_unsaved_changes(&mut self, changed: bool) { self.has_changes = changed; }
|
|
}
|
|
|
|
let form = SimpleForm::new();
|
|
println!(" Created form with {} fields", form.fields().len());
|
|
println!(" Current field: {}", form.fields()[form.current_field()]);
|
|
println!(" ✅ Minimal implementation works!\n");
|
|
}
|
|
|
|
// Example 2: Form with suggestion support
|
|
fn example_2_with_suggestions() {
|
|
println!("💡 Example 2: Form with Suggestions");
|
|
|
|
#[derive(Debug)]
|
|
struct FormWithSuggestions {
|
|
current_field: usize,
|
|
cursor_pos: usize,
|
|
country: String,
|
|
has_changes: bool,
|
|
suggestions: SuggestionState,
|
|
}
|
|
|
|
impl FormWithSuggestions {
|
|
fn new() -> Self {
|
|
Self {
|
|
current_field: 0,
|
|
cursor_pos: 0,
|
|
country: String::new(),
|
|
has_changes: false,
|
|
suggestions: SuggestionState::default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl CanvasState for FormWithSuggestions {
|
|
fn current_field(&self) -> usize { self.current_field }
|
|
fn current_cursor_pos(&self) -> usize { self.cursor_pos }
|
|
fn set_current_field(&mut self, index: usize) { self.current_field = index; }
|
|
fn set_current_cursor_pos(&mut self, pos: usize) { self.cursor_pos = pos; }
|
|
|
|
fn get_current_input(&self) -> &str { &self.country }
|
|
fn get_current_input_mut(&mut self) -> &mut String { &mut self.country }
|
|
fn inputs(&self) -> Vec<&String> { vec![&self.country] }
|
|
fn fields(&self) -> Vec<&str> { vec!["Country"] }
|
|
fn has_unsaved_changes(&self) -> bool { self.has_changes }
|
|
fn set_has_unsaved_changes(&mut self, changed: bool) { self.has_changes = changed; }
|
|
|
|
// Suggestion support
|
|
fn get_suggestions(&self) -> Option<&[String]> {
|
|
if self.suggestions.is_active {
|
|
Some(&self.suggestions.suggestions)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
fn get_selected_suggestion_index(&self) -> Option<usize> {
|
|
self.suggestions.selected_index
|
|
}
|
|
|
|
fn set_selected_suggestion_index(&mut self, index: Option<usize>) {
|
|
self.suggestions.selected_index = index;
|
|
}
|
|
|
|
fn activate_suggestions(&mut self, suggestions: Vec<String>) {
|
|
self.suggestions.activate_with_suggestions(suggestions);
|
|
}
|
|
|
|
fn deactivate_suggestions(&mut self) {
|
|
self.suggestions.deactivate();
|
|
}
|
|
|
|
fn handle_feature_action(&mut self, action: &CanvasAction, _context: &ActionContext) -> Option<String> {
|
|
match action {
|
|
CanvasAction::SelectSuggestion => {
|
|
// Fix: Clone the suggestion first to avoid borrow checker issues
|
|
if let Some(suggestion) = self.suggestions.get_selected().cloned() {
|
|
self.country = suggestion.clone();
|
|
self.cursor_pos = suggestion.len();
|
|
self.deactivate_suggestions();
|
|
self.has_changes = true;
|
|
return Some(format!("Selected: {}", suggestion));
|
|
}
|
|
None
|
|
}
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
let mut form = FormWithSuggestions::new();
|
|
|
|
// Simulate user typing and triggering suggestions
|
|
form.activate_suggestions(vec![
|
|
"United States".to_string(),
|
|
"United Kingdom".to_string(),
|
|
"Ukraine".to_string(),
|
|
]);
|
|
|
|
println!(" Activated suggestions: {:?}", form.get_suggestions().unwrap());
|
|
println!(" Current selection: {:?}", form.get_selected_suggestion_index());
|
|
|
|
// Navigate suggestions
|
|
form.set_selected_suggestion_index(Some(1));
|
|
println!(" Navigated to: {}", form.suggestions.get_selected().unwrap());
|
|
println!(" ✅ Suggestions work!\n");
|
|
}
|
|
|
|
// Example 3: Custom actions
|
|
async fn example_3_custom_actions() {
|
|
println!("⚡ Example 3: Custom Actions");
|
|
|
|
#[derive(Debug)]
|
|
struct FormWithCustomActions {
|
|
current_field: usize,
|
|
cursor_pos: usize,
|
|
text: String,
|
|
has_changes: bool,
|
|
}
|
|
|
|
impl FormWithCustomActions {
|
|
fn new() -> Self {
|
|
Self {
|
|
current_field: 0,
|
|
cursor_pos: 0,
|
|
text: "hello world".to_string(),
|
|
has_changes: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl CanvasState for FormWithCustomActions {
|
|
fn current_field(&self) -> usize { self.current_field }
|
|
fn current_cursor_pos(&self) -> usize { self.cursor_pos }
|
|
fn set_current_field(&mut self, index: usize) { self.current_field = index; }
|
|
fn set_current_cursor_pos(&mut self, pos: usize) { self.cursor_pos = pos; }
|
|
|
|
fn get_current_input(&self) -> &str { &self.text }
|
|
fn get_current_input_mut(&mut self) -> &mut String { &mut self.text }
|
|
fn inputs(&self) -> Vec<&String> { vec![&self.text] }
|
|
fn fields(&self) -> Vec<&str> { vec!["Text"] }
|
|
fn has_unsaved_changes(&self) -> bool { self.has_changes }
|
|
fn set_has_unsaved_changes(&mut self, changed: bool) { self.has_changes = changed; }
|
|
|
|
fn handle_feature_action(&mut self, action: &CanvasAction, _context: &ActionContext) -> Option<String> {
|
|
match action {
|
|
CanvasAction::Custom(cmd) => match cmd.as_str() {
|
|
"uppercase" => {
|
|
self.text = self.text.to_uppercase();
|
|
self.has_changes = true;
|
|
Some("Converted to uppercase".to_string())
|
|
}
|
|
"reverse" => {
|
|
self.text = self.text.chars().rev().collect();
|
|
self.has_changes = true;
|
|
Some("Reversed text".to_string())
|
|
}
|
|
"word_count" => {
|
|
let count = self.text.split_whitespace().count();
|
|
Some(format!("Word count: {}", count))
|
|
}
|
|
_ => None,
|
|
},
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
let mut form = FormWithCustomActions::new();
|
|
let mut ideal_cursor = 0;
|
|
|
|
println!(" Initial text: '{}'", form.text);
|
|
|
|
// Execute custom actions
|
|
let result = ActionDispatcher::dispatch(
|
|
CanvasAction::Custom("uppercase".to_string()),
|
|
&mut form,
|
|
&mut ideal_cursor,
|
|
).await.unwrap();
|
|
|
|
println!(" After uppercase: '{}' - {}", form.text, result.message().unwrap());
|
|
|
|
let result = ActionDispatcher::dispatch(
|
|
CanvasAction::Custom("reverse".to_string()),
|
|
&mut form,
|
|
&mut ideal_cursor,
|
|
).await.unwrap();
|
|
|
|
println!(" After reverse: '{}' - {}", form.text, result.message().unwrap());
|
|
|
|
let result = ActionDispatcher::dispatch(
|
|
CanvasAction::Custom("word_count".to_string()),
|
|
&mut form,
|
|
&mut ideal_cursor,
|
|
).await.unwrap();
|
|
|
|
println!(" {}", result.message().unwrap());
|
|
println!(" ✅ Custom actions work!\n");
|
|
}
|
|
|
|
// Example 4: Batch operations
|
|
async fn example_4_batch_operations() {
|
|
println!("📦 Example 4: Batch Operations");
|
|
|
|
// Reuse the simple form from example 1
|
|
#[derive(Debug)]
|
|
struct SimpleForm {
|
|
current_field: usize,
|
|
cursor_pos: usize,
|
|
name: String,
|
|
email: String,
|
|
has_changes: bool,
|
|
}
|
|
|
|
impl SimpleForm {
|
|
fn new() -> Self {
|
|
Self {
|
|
current_field: 0,
|
|
cursor_pos: 0,
|
|
name: String::new(),
|
|
email: String::new(),
|
|
has_changes: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl CanvasState for SimpleForm {
|
|
fn current_field(&self) -> usize { self.current_field }
|
|
fn current_cursor_pos(&self) -> usize { self.cursor_pos }
|
|
fn set_current_field(&mut self, index: usize) { self.current_field = index.min(1); }
|
|
fn set_current_cursor_pos(&mut self, pos: usize) { self.cursor_pos = pos; }
|
|
|
|
fn get_current_input(&self) -> &str {
|
|
match self.current_field {
|
|
0 => &self.name,
|
|
1 => &self.email,
|
|
_ => "",
|
|
}
|
|
}
|
|
|
|
fn get_current_input_mut(&mut self) -> &mut String {
|
|
match self.current_field {
|
|
0 => &mut self.name,
|
|
1 => &mut self.email,
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
fn inputs(&self) -> Vec<&String> { vec![&self.name, &self.email] }
|
|
fn fields(&self) -> Vec<&str> { vec!["Name", "Email"] }
|
|
fn has_unsaved_changes(&self) -> bool { self.has_changes }
|
|
fn set_has_unsaved_changes(&mut self, changed: bool) { self.has_changes = changed; }
|
|
}
|
|
|
|
let mut form = SimpleForm::new();
|
|
let mut ideal_cursor = 0;
|
|
|
|
// Execute a sequence of actions to type "John" in the name field
|
|
let actions = vec![
|
|
CanvasAction::InsertChar('J'),
|
|
CanvasAction::InsertChar('o'),
|
|
CanvasAction::InsertChar('h'),
|
|
CanvasAction::InsertChar('n'),
|
|
CanvasAction::NextField, // Move to email field
|
|
CanvasAction::InsertChar('j'),
|
|
CanvasAction::InsertChar('o'),
|
|
CanvasAction::InsertChar('h'),
|
|
CanvasAction::InsertChar('n'),
|
|
CanvasAction::InsertChar('@'),
|
|
CanvasAction::InsertChar('e'),
|
|
CanvasAction::InsertChar('x'),
|
|
CanvasAction::InsertChar('a'),
|
|
CanvasAction::InsertChar('m'),
|
|
CanvasAction::InsertChar('p'),
|
|
CanvasAction::InsertChar('l'),
|
|
CanvasAction::InsertChar('e'),
|
|
CanvasAction::InsertChar('.'),
|
|
CanvasAction::InsertChar('c'),
|
|
CanvasAction::InsertChar('o'),
|
|
CanvasAction::InsertChar('m'),
|
|
];
|
|
|
|
println!(" Executing {} actions in batch...", actions.len());
|
|
|
|
let results = ActionDispatcher::dispatch_batch(
|
|
actions,
|
|
&mut form,
|
|
&mut ideal_cursor,
|
|
).await.unwrap();
|
|
|
|
println!(" Completed {} actions", results.len());
|
|
println!(" Final state:");
|
|
println!(" Name: '{}'", form.name);
|
|
println!(" Email: '{}'", form.email);
|
|
println!(" Current field: {}", form.fields()[form.current_field()]);
|
|
println!(" Has changes: {}", form.has_changes);
|
|
}
|