From 14d0b5d3dc26ae2a2e4fecd2885b0caf12c3caa1 Mon Sep 17 00:00:00 2001 From: filipriec Date: Mon, 17 Feb 2025 23:26:53 +0100 Subject: [PATCH] toggle on and off mode --- config.toml | 1 + src/client/components/form.rs | 9 +- src/client/components/status_line.rs | 28 +-- src/client/config.rs | 8 + src/client/ui.rs | 279 ++++++++++++++++----------- 5 files changed, 194 insertions(+), 131 deletions(-) diff --git a/config.toml b/config.toml index 79e5157..c668bb2 100644 --- a/config.toml +++ b/config.toml @@ -4,3 +4,4 @@ save = [":w", "ctrl+s"] quit = [":q", "ctrl+q"] force_quit = [":q!", "ctrl+shift+q"] save_and_quit = [":wq", "ctrl+shift+s"] +toggle_edit_mode = ["i", "ctrl+e"] diff --git a/src/client/components/form.rs b/src/client/components/form.rs index 1c06fd8..2c8a790 100644 --- a/src/client/components/form.rs +++ b/src/client/components/form.rs @@ -15,6 +15,7 @@ pub fn render_form( current_field: &usize, inputs: &[&String], theme: &Theme, + is_edit_mode: bool, ) { // Create Adresar card let adresar_card = Block::default() @@ -40,7 +41,11 @@ pub fn render_form( // Create compact input container let input_container = Block::default() .borders(Borders::ALL) - .border_style(Style::default().fg(theme.accent)) + .border_style(if is_edit_mode { + Style::default().fg(theme.accent) + } else { + Style::default().fg(theme.secondary) + }) .style(Style::default().bg(theme.bg)); // Place the input container at the top @@ -89,7 +94,7 @@ pub fn render_form( f.render_widget(input_display, input_rows[i]); // Position cursor at the correct position in the active field - if is_active { + if is_active && is_edit_mode { // Move cursor logic inside the loop let cursor_x = input_rows[i].x + input.len() as u16; let cursor_y = input_rows[i].y; f.set_cursor(cursor_x, cursor_y); diff --git a/src/client/components/status_line.rs b/src/client/components/status_line.rs index 884ff87..77aa5fb 100644 --- a/src/client/components/status_line.rs +++ b/src/client/components/status_line.rs @@ -8,23 +8,27 @@ use ratatui::{ }; use crate::client::colors::Theme; -pub fn render_status_line(f: &mut Frame, area: Rect, current_dir: &str, theme: &Theme) { +pub fn render_status_line( + f: &mut Frame, + area: Rect, + current_dir: &str, + theme: &Theme, + is_edit_mode: bool +) { + // Program name and version let program_info = format!("multieko2 v{}", env!("CARGO_PKG_VERSION")); - // Create the status line text - let status_line = Line::from(vec![ - Span::styled(current_dir, Style::default().fg(theme.fg)), - Span::styled( - program_info, - Style::default().fg(theme.secondary).add_modifier(ratatui::style::Modifier::BOLD), // Use `secondary` color - ), - ]); + let mode_text = if is_edit_mode { + "[EDIT]" + } else { + "[READ-ONLY]" + }; // Render the status line - let paragraph = Paragraph::new(status_line) - .block(Block::default().style(Style::default().bg(theme.bg))) - .alignment(Alignment::Left); + let text = format!(" {} | {}", mode_text, current_dir); + let paragraph = Paragraph::new(text) + .style(Style::default().fg(theme.fg).bg(theme.bg)); f.render_widget(paragraph, area); } diff --git a/src/client/config.rs b/src/client/config.rs index 0856825..9584c89 100644 --- a/src/client/config.rs +++ b/src/client/config.rs @@ -63,4 +63,12 @@ impl Config { } None } + + pub fn is_toggle_edit_mode(&self, key: KeyCode, modifiers: KeyModifiers) -> bool { + if let Some(bindings) = self.keybindings.get("toggle_edit_mode") { + bindings.iter().any(|b| Self::matches_keybinding(b, key, modifiers)) + } else { + false + } + } } diff --git a/src/client/ui.rs b/src/client/ui.rs index 54a3404..58eb376 100644 --- a/src/client/ui.rs +++ b/src/client/ui.rs @@ -48,6 +48,8 @@ pub async fn run_ui() -> Result<(), Box> { // Track the current message to display in the command line let mut command_message = String::new(); + let mut is_edit_mode = false; + let mut edit_mode_cooldown = false; loop { app_terminal.draw(|f| { @@ -77,6 +79,7 @@ pub async fn run_ui() -> Result<(), Box> { &ucet, &skladm, &ico, &kontakt, &telefon, &skladu, &fax, ], &theme, + is_edit_mode, ); // Right panel - Preview Card @@ -90,7 +93,7 @@ pub async fn run_ui() -> Result<(), Box> { ); // Status line - render_status_line(f, root[1], ¤t_dir, &theme); + render_status_line(f, root[1], ¤t_dir, &theme, is_edit_mode); // Command line render_command_line(f, root[2], &command_input, command_mode, &theme, &command_message); @@ -98,10 +101,105 @@ pub async fn run_ui() -> Result<(), Box> { // Event handling if let Event::Key(key) = app_terminal.read_event()? { - if command_mode { + // First check for edit mode toggle (works in any mode) + if config.is_toggle_edit_mode(key.code, key.modifiers) { + is_edit_mode = !is_edit_mode; + edit_mode_cooldown = true; // Prevent accidental double-toggles + command_message = format!("{} mode", if is_edit_mode { "Edit" } else { "Read-only" }); + continue; + } + + if !is_edit_mode { + // Read-only mode handling match key.code { - KeyCode::Enter => { - // Create PostAdresarRequest from form data + KeyCode::Char(':') => { + command_mode = true; + command_input.clear(); + command_message.clear(); + } + KeyCode::Esc => { + command_mode = false; + command_input.clear(); + command_message.clear(); + } + // Allow navigation but prevent editing + KeyCode::Tab | KeyCode::BackTab | KeyCode::Down | KeyCode::Up => { + if key.modifiers.contains(KeyModifiers::SHIFT) { + current_field = current_field.saturating_sub(1); + } else { + current_field = (current_field + 1) % fields.len(); + } + } + _ => { + // Block all other inputs + if !edit_mode_cooldown { + command_message = format!("Read-only mode - press {} to edit", + config.keybindings.get("toggle_edit_mode").unwrap()[0]); + } + } + } + } else { + // Existing edit mode handling + if command_mode { + match key.code { + KeyCode::Enter => { + // Create PostAdresarRequest from form data + let form_data = PostAdresarRequest { + firma: firma.clone(), + kz: kz.clone(), + drc: drc.clone(), + ulica: ulica.clone(), + psc: psc.clone(), + mesto: mesto.clone(), + stat: stat.clone(), + banka: banka.clone(), + ucet: ucet.clone(), + skladm: skladm.clone(), + ico: ico.clone(), + kontakt: kontakt.clone(), + telefon: telefon.clone(), + skladu: skladu.clone(), + fax: fax.clone(), + }; + + // Validate command format + let command = command_input.trim(); + if command.is_empty() { + command_message = "Empty command".to_string(); + continue; + } + + // Look up the action for the command string (e.g., "w") + let action = config.get_action_for_command(command) + .unwrap_or("unknown"); + + // Pass the resolved action to handle_command + let (should_exit, message) = app_terminal + .handle_command(action, &mut is_saved, &form_data) + .await?; + + command_message = message; + command_mode = false; + command_input.clear(); + + if should_exit { + return Ok(()); + } + } + KeyCode::Char(c) => command_input.push(c), + KeyCode::Backspace => { + command_input.pop(); // Ignore the return value + } + KeyCode::Esc => { + command_mode = false; + command_input.clear(); + command_message.clear(); + } + _ => {} + } + } else { + // Check for keybindings + if let Some(action) = config.get_action_for_key(key.code, key.modifiers) { let form_data = PostAdresarRequest { firma: firma.clone(), kz: kz.clone(), @@ -120,132 +218,79 @@ pub async fn run_ui() -> Result<(), Box> { fax: fax.clone(), }; - // Validate command format - let command = command_input.trim(); - if command.is_empty() { - command_message = "Empty command".to_string(); - continue; - } - - // Look up the action for the command string (e.g., "w") - let action = config.get_action_for_command(command) - .unwrap_or("unknown"); - - // Pass the resolved action to handle_command + // Pass form data to handle_command (remove &config) let (should_exit, message) = app_terminal .handle_command(action, &mut is_saved, &form_data) .await?; - command_message = message; - command_mode = false; - command_input.clear(); - if should_exit { return Ok(()); } - } - KeyCode::Char(c) => command_input.push(c), - KeyCode::Backspace => { - command_input.pop(); // Ignore the return value - } - KeyCode::Esc => { - command_mode = false; - command_input.clear(); - command_message.clear(); - } - _ => {} - } - } else { - // Check for keybindings - if let Some(action) = config.get_action_for_key(key.code, key.modifiers) { - let form_data = PostAdresarRequest { - firma: firma.clone(), - kz: kz.clone(), - drc: drc.clone(), - ulica: ulica.clone(), - psc: psc.clone(), - mesto: mesto.clone(), - stat: stat.clone(), - banka: banka.clone(), - ucet: ucet.clone(), - skladm: skladm.clone(), - ico: ico.clone(), - kontakt: kontakt.clone(), - telefon: telefon.clone(), - skladu: skladu.clone(), - fax: fax.clone(), - }; - - // Pass form data to handle_command (remove &config) - let (should_exit, message) = app_terminal - .handle_command(action, &mut is_saved, &form_data) - .await?; - command_message = message; - if should_exit { - return Ok(()); - } - } else { - match key.code { - KeyCode::Char(':') => { - command_mode = true; - command_input.clear(); - command_message.clear(); - } - KeyCode::Tab => { - if key.modifiers.contains(KeyModifiers::SHIFT) { - current_field = current_field.saturating_sub(1); - } else { - current_field = (current_field + 1) % fields.len(); + } else { + match key.code { + KeyCode::Char(':') => { + command_mode = true; + command_input.clear(); + command_message.clear(); } - } - KeyCode::BackTab => current_field = current_field.saturating_sub(1), - KeyCode::Down => current_field = (current_field + 1) % fields.len(), - KeyCode::Up => current_field = current_field.saturating_sub(1), - KeyCode::Enter => current_field = (current_field + 1) % fields.len(), - KeyCode::Char(c) => { - match current_field { - 0 => firma.push(c), - 1 => kz.push(c), - 2 => drc.push(c), - 3 => ulica.push(c), - 4 => psc.push(c), - 5 => mesto.push(c), - 6 => stat.push(c), - 7 => banka.push(c), - 8 => ucet.push(c), - 9 => skladm.push(c), - 10 => ico.push(c), - 11 => kontakt.push(c), - 12 => telefon.push(c), - 13 => skladu.push(c), - 14 => fax.push(c), - _ => (), + KeyCode::Tab => { + if key.modifiers.contains(KeyModifiers::SHIFT) { + current_field = current_field.saturating_sub(1); + } else { + current_field = (current_field + 1) % fields.len(); + } } + KeyCode::BackTab => current_field = current_field.saturating_sub(1), + KeyCode::Down => current_field = (current_field + 1) % fields.len(), + KeyCode::Up => current_field = current_field.saturating_sub(1), + KeyCode::Enter => current_field = (current_field + 1) % fields.len(), + KeyCode::Char(c) => { + match current_field { + 0 => firma.push(c), + 1 => kz.push(c), + 2 => drc.push(c), + 3 => ulica.push(c), + 4 => psc.push(c), + 5 => mesto.push(c), + 6 => stat.push(c), + 7 => banka.push(c), + 8 => ucet.push(c), + 9 => skladm.push(c), + 10 => ico.push(c), + 11 => kontakt.push(c), + 12 => telefon.push(c), + 13 => skladu.push(c), + 14 => fax.push(c), + _ => (), + } + } + KeyCode::Backspace => { + match current_field { + 0 => firma.pop(), + 1 => kz.pop(), + 2 => drc.pop(), + 3 => ulica.pop(), + 4 => psc.pop(), + 5 => mesto.pop(), + 6 => stat.pop(), + 7 => banka.pop(), + 8 => ucet.pop(), + 9 => skladm.pop(), + 10 => ico.pop(), + 11 => kontakt.pop(), + 12 => telefon.pop(), + 13 => skladu.pop(), + 14 => fax.pop(), + _ => None, + }; + } + _ => {} } - KeyCode::Backspace => { - match current_field { - 0 => firma.pop(), - 1 => kz.pop(), - 2 => drc.pop(), - 3 => ulica.pop(), - 4 => psc.pop(), - 5 => mesto.pop(), - 6 => stat.pop(), - 7 => banka.pop(), - 8 => ucet.pop(), - 9 => skladm.pop(), - 10 => ico.pop(), - 11 => kontakt.pop(), - 12 => telefon.pop(), - 13 => skladu.pop(), - 14 => fax.pop(), - _ => None, - }; - } - _ => {} } } } } + + edit_mode_cooldown = false; } }