canvas library config removed compeltely
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
// examples/canvas_gui_demo.rs
|
||||
|
||||
use std::io;
|
||||
use crossterm::{
|
||||
event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyModifiers},
|
||||
@@ -20,9 +22,7 @@ use canvas::{
|
||||
state::{ActionContext, CanvasState},
|
||||
theme::CanvasTheme,
|
||||
},
|
||||
config::CanvasConfig,
|
||||
dispatcher::ActionDispatcher,
|
||||
CanvasAction,
|
||||
CanvasAction, execute,
|
||||
};
|
||||
|
||||
// Simple theme implementation
|
||||
@@ -49,8 +49,6 @@ struct DemoFormState {
|
||||
mode: AppMode,
|
||||
highlight_state: HighlightState,
|
||||
has_changes: bool,
|
||||
ideal_cursor_column: usize,
|
||||
last_action: Option<String>,
|
||||
debug_message: String,
|
||||
}
|
||||
|
||||
@@ -78,9 +76,7 @@ impl DemoFormState {
|
||||
mode: AppMode::ReadOnly,
|
||||
highlight_state: HighlightState::Off,
|
||||
has_changes: false,
|
||||
ideal_cursor_column: 0,
|
||||
last_action: None,
|
||||
debug_message: "Ready".to_string(),
|
||||
debug_message: "Ready - Use hjkl to move, w for next word, i to edit".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,98 +177,125 @@ impl CanvasState for DemoFormState {
|
||||
}
|
||||
}
|
||||
|
||||
async fn run_app<B: Backend>(terminal: &mut Terminal<B>, mut state: DemoFormState, config: CanvasConfig) -> io::Result<()> {
|
||||
/// Simple key mapping - users have full control!
|
||||
async fn handle_key_press(key: KeyCode, modifiers: KeyModifiers, state: &mut DemoFormState) -> bool {
|
||||
let is_edit_mode = state.mode == AppMode::Edit;
|
||||
|
||||
// Handle quit first
|
||||
if (key == KeyCode::Char('q') && modifiers.contains(KeyModifiers::CONTROL)) ||
|
||||
(key == KeyCode::Char('c') && modifiers.contains(KeyModifiers::CONTROL)) ||
|
||||
key == KeyCode::F(10) {
|
||||
return false; // Signal to quit
|
||||
}
|
||||
|
||||
// Users directly map keys to actions - no configuration needed!
|
||||
let action = match (state.mode, key, modifiers) {
|
||||
// === READ-ONLY MODE KEYS ===
|
||||
(AppMode::ReadOnly, KeyCode::Char('h'), _) => Some(CanvasAction::MoveLeft),
|
||||
(AppMode::ReadOnly, KeyCode::Char('j'), _) => Some(CanvasAction::MoveDown),
|
||||
(AppMode::ReadOnly, KeyCode::Char('k'), _) => Some(CanvasAction::MoveUp),
|
||||
(AppMode::ReadOnly, KeyCode::Char('l'), _) => Some(CanvasAction::MoveRight),
|
||||
(AppMode::ReadOnly, KeyCode::Char('w'), _) => Some(CanvasAction::MoveWordNext),
|
||||
(AppMode::ReadOnly, KeyCode::Char('b'), _) => Some(CanvasAction::MoveWordPrev),
|
||||
(AppMode::ReadOnly, KeyCode::Char('e'), _) => Some(CanvasAction::MoveWordEnd),
|
||||
(AppMode::ReadOnly, KeyCode::Char('0'), _) => Some(CanvasAction::MoveLineStart),
|
||||
(AppMode::ReadOnly, KeyCode::Char('$'), _) => Some(CanvasAction::MoveLineEnd),
|
||||
(AppMode::ReadOnly, KeyCode::Tab, _) => Some(CanvasAction::NextField),
|
||||
(AppMode::ReadOnly, KeyCode::BackTab, _) => Some(CanvasAction::PrevField),
|
||||
|
||||
// === EDIT MODE KEYS ===
|
||||
(AppMode::Edit, KeyCode::Left, _) => Some(CanvasAction::MoveLeft),
|
||||
(AppMode::Edit, KeyCode::Right, _) => Some(CanvasAction::MoveRight),
|
||||
(AppMode::Edit, KeyCode::Up, _) => Some(CanvasAction::MoveUp),
|
||||
(AppMode::Edit, KeyCode::Down, _) => Some(CanvasAction::MoveDown),
|
||||
(AppMode::Edit, KeyCode::Home, _) => Some(CanvasAction::MoveLineStart),
|
||||
(AppMode::Edit, KeyCode::End, _) => Some(CanvasAction::MoveLineEnd),
|
||||
(AppMode::Edit, KeyCode::Backspace, _) => Some(CanvasAction::DeleteBackward),
|
||||
(AppMode::Edit, KeyCode::Delete, _) => Some(CanvasAction::DeleteForward),
|
||||
(AppMode::Edit, KeyCode::Tab, _) => Some(CanvasAction::NextField),
|
||||
(AppMode::Edit, KeyCode::BackTab, _) => Some(CanvasAction::PrevField),
|
||||
|
||||
// Vim-style movement in edit mode (optional)
|
||||
(AppMode::Edit, KeyCode::Char('h'), m) if m.contains(KeyModifiers::CONTROL) => Some(CanvasAction::MoveLeft),
|
||||
(AppMode::Edit, KeyCode::Char('l'), m) if m.contains(KeyModifiers::CONTROL) => Some(CanvasAction::MoveRight),
|
||||
|
||||
// Word movement with Ctrl in edit mode
|
||||
(AppMode::Edit, KeyCode::Left, m) if m.contains(KeyModifiers::CONTROL) => Some(CanvasAction::MoveWordPrev),
|
||||
(AppMode::Edit, KeyCode::Right, m) if m.contains(KeyModifiers::CONTROL) => Some(CanvasAction::MoveWordNext),
|
||||
|
||||
// === MODE TRANSITIONS ===
|
||||
(AppMode::ReadOnly, KeyCode::Char('i'), _) => Some(CanvasAction::Custom("enter_edit_mode".to_string())),
|
||||
(AppMode::ReadOnly, KeyCode::Char('a'), _) => {
|
||||
// 'a' moves to end of line then enters edit mode
|
||||
if let Ok(_) = execute(CanvasAction::MoveLineEnd, state).await {
|
||||
Some(CanvasAction::Custom("enter_edit_mode".to_string()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
(AppMode::ReadOnly, KeyCode::Char('v'), _) => Some(CanvasAction::Custom("enter_highlight_mode".to_string())),
|
||||
(_, KeyCode::Esc, _) => Some(CanvasAction::Custom("enter_readonly_mode".to_string())),
|
||||
|
||||
// === CHARACTER INPUT IN EDIT MODE ===
|
||||
(AppMode::Edit, KeyCode::Char(c), m) if !m.contains(KeyModifiers::CONTROL) && !m.contains(KeyModifiers::ALT) => {
|
||||
Some(CanvasAction::InsertChar(c))
|
||||
},
|
||||
|
||||
// === ARROW KEYS IN READ-ONLY MODE ===
|
||||
(AppMode::ReadOnly, KeyCode::Left, _) => Some(CanvasAction::MoveLeft),
|
||||
(AppMode::ReadOnly, KeyCode::Right, _) => Some(CanvasAction::MoveRight),
|
||||
(AppMode::ReadOnly, KeyCode::Up, _) => Some(CanvasAction::MoveUp),
|
||||
(AppMode::ReadOnly, KeyCode::Down, _) => Some(CanvasAction::MoveDown),
|
||||
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// Execute the action if we found one
|
||||
if let Some(action) = action {
|
||||
match execute(action.clone(), state).await {
|
||||
Ok(result) => {
|
||||
if result.is_success() {
|
||||
// Mark as changed for editing actions
|
||||
if is_edit_mode {
|
||||
match action {
|
||||
CanvasAction::InsertChar(_) | CanvasAction::DeleteBackward | CanvasAction::DeleteForward => {
|
||||
state.set_has_unsaved_changes(true);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(msg) = result.message() {
|
||||
state.debug_message = msg.to_string();
|
||||
} else {
|
||||
state.debug_message = format!("Executed: {}", action.description());
|
||||
}
|
||||
} else if let Some(msg) = result.message() {
|
||||
state.debug_message = format!("Error: {}", msg);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
state.debug_message = format!("Error executing action: {}", e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
state.debug_message = format!("Unhandled key: {:?} (mode: {:?})", key, state.mode);
|
||||
}
|
||||
|
||||
true // Continue running
|
||||
}
|
||||
|
||||
async fn run_app<B: Backend>(terminal: &mut Terminal<B>, mut state: DemoFormState) -> io::Result<()> {
|
||||
let theme = DemoTheme;
|
||||
|
||||
loop {
|
||||
terminal.draw(|f| ui(f, &state, &theme))?;
|
||||
|
||||
if let Event::Key(key) = event::read()? {
|
||||
// Handle quit
|
||||
if (key.code == KeyCode::Char('q') && key.modifiers.contains(KeyModifiers::CONTROL)) ||
|
||||
(key.code == KeyCode::Char('c') && key.modifiers.contains(KeyModifiers::CONTROL)) ||
|
||||
key.code == KeyCode::F(10) {
|
||||
let should_continue = handle_key_press(key.code, key.modifiers, &mut state).await;
|
||||
if !should_continue {
|
||||
break;
|
||||
}
|
||||
|
||||
let is_edit_mode = state.mode == AppMode::Edit;
|
||||
let mut handled = false;
|
||||
|
||||
// First priority: Try to dispatch through config system
|
||||
let mut ideal_cursor = state.ideal_cursor_column;
|
||||
|
||||
if let Ok(Some(result)) = ActionDispatcher::dispatch_key(
|
||||
key.code,
|
||||
key.modifiers,
|
||||
&mut state,
|
||||
&mut ideal_cursor,
|
||||
is_edit_mode,
|
||||
false,
|
||||
).await {
|
||||
state.ideal_cursor_column = ideal_cursor;
|
||||
state.debug_message = format!("Config handled: {:?}", key.code);
|
||||
|
||||
// Mark as changed for text modification keys in edit mode
|
||||
if is_edit_mode {
|
||||
match key.code {
|
||||
KeyCode::Char(_) | KeyCode::Backspace | KeyCode::Delete => {
|
||||
state.set_has_unsaved_changes(true);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
|
||||
// Second priority: Handle character input in edit mode
|
||||
if !handled && is_edit_mode {
|
||||
if let KeyCode::Char(c) = key.code {
|
||||
if !key.modifiers.contains(KeyModifiers::CONTROL) && !key.modifiers.contains(KeyModifiers::ALT) {
|
||||
let action = CanvasAction::InsertChar(c);
|
||||
let mut ideal_cursor = state.ideal_cursor_column;
|
||||
if let Ok(_) = ActionDispatcher::dispatch_with_config(
|
||||
action,
|
||||
&mut state,
|
||||
&mut ideal_cursor,
|
||||
Some(&config),
|
||||
).await {
|
||||
state.ideal_cursor_column = ideal_cursor;
|
||||
state.set_has_unsaved_changes(true);
|
||||
state.debug_message = format!("Inserted char: '{}'", c);
|
||||
handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Third priority: Fallback mode transitions
|
||||
if !handled {
|
||||
match (state.mode, key.code) {
|
||||
(AppMode::ReadOnly, KeyCode::Char('i') | KeyCode::Char('a') | KeyCode::Insert) => {
|
||||
state.enter_edit_mode();
|
||||
if key.code == KeyCode::Char('a') {
|
||||
state.cursor_pos = state.fields[state.current_field].len();
|
||||
}
|
||||
state.debug_message = format!("Entered edit mode via {:?}", key.code);
|
||||
handled = true;
|
||||
}
|
||||
(AppMode::ReadOnly, KeyCode::Char('v')) => {
|
||||
state.enter_highlight_mode();
|
||||
state.debug_message = "Entered visual mode".to_string();
|
||||
handled = true;
|
||||
}
|
||||
(_, KeyCode::Esc) => {
|
||||
state.enter_readonly_mode();
|
||||
state.debug_message = "Entered read-only mode".to_string();
|
||||
handled = true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if !handled {
|
||||
state.debug_message = format!("Unhandled key: {:?}", key.code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,15 +336,15 @@ fn ui(f: &mut Frame, state: &DemoFormState, theme: &DemoTheme) {
|
||||
format!("-- {} --", mode_text)
|
||||
};
|
||||
|
||||
let position_text = format!("Field: {}/{} | Cursor: {} | Column: {}",
|
||||
let position_text = format!("Field: {}/{} | Cursor: {} | Actions: {}",
|
||||
state.current_field + 1,
|
||||
state.fields.len(),
|
||||
state.cursor_pos,
|
||||
state.ideal_cursor_column);
|
||||
CanvasAction::movement_actions().len() + CanvasAction::editing_actions().len());
|
||||
|
||||
let help_text = match state.mode {
|
||||
AppMode::ReadOnly => "hjkl/arrows: Move | Tab/Shift+Tab: Fields | w/b/e: Words | 0/$: Line | gg/G: File | i/a: Edit | v: Visual | F10: Quit",
|
||||
AppMode::Edit => "Type to edit | hjkl/arrows: Move | Tab/Enter: Next field | Backspace/Delete: Delete | Home/End: Line | Esc: Normal | F10: Quit",
|
||||
AppMode::ReadOnly => "hjkl/arrows: Move | Tab/Shift+Tab: Fields | w/b/e: Words | 0/$: Line | i/a: Edit | v: Visual | F10: Quit",
|
||||
AppMode::Edit => "Type to edit | Arrows/Ctrl+arrows: Move | Tab: Next field | Backspace/Delete: Delete | Home/End: Line | Esc: Normal | F10: Quit",
|
||||
AppMode::Highlight => "hjkl/arrows: Select | w/b/e: Words | 0/$: Line | Esc: Normal | F10: Quit",
|
||||
_ => "Esc: Normal | F10: Quit",
|
||||
};
|
||||
@@ -339,8 +362,6 @@ fn ui(f: &mut Frame, state: &DemoFormState, theme: &DemoTheme) {
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let config = CanvasConfig::load();
|
||||
|
||||
enable_raw_mode()?;
|
||||
let mut stdout = io::stdout();
|
||||
execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
|
||||
@@ -349,7 +370,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
let state = DemoFormState::new();
|
||||
|
||||
let res = run_app(&mut terminal, state, config).await;
|
||||
let res = run_app(&mut terminal, state).await;
|
||||
|
||||
disable_raw_mode()?;
|
||||
execute!(
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
// examples/generate_template.rs
|
||||
use canvas::config::CanvasConfig;
|
||||
use std::env;
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
||||
if args.len() > 1 && args[1] == "clean" {
|
||||
// Generate clean template with 80% active code
|
||||
let template = CanvasConfig::generate_clean_template();
|
||||
println!("{}", template);
|
||||
} else {
|
||||
// Generate verbose template with descriptions (default)
|
||||
let template = CanvasConfig::generate_template();
|
||||
println!("{}", template);
|
||||
}
|
||||
}
|
||||
|
||||
// Usage:
|
||||
// cargo run --example generate_template > canvas_config.toml
|
||||
// cargo run --example generate_template clean > canvas_config_clean.toml
|
||||
Reference in New Issue
Block a user