toggle on and off mode

This commit is contained in:
filipriec
2025-02-17 23:26:53 +01:00
parent 4cea540cae
commit 14d0b5d3dc
5 changed files with 194 additions and 131 deletions

View File

@@ -4,3 +4,4 @@ save = [":w", "ctrl+s"]
quit = [":q", "ctrl+q"] quit = [":q", "ctrl+q"]
force_quit = [":q!", "ctrl+shift+q"] force_quit = [":q!", "ctrl+shift+q"]
save_and_quit = [":wq", "ctrl+shift+s"] save_and_quit = [":wq", "ctrl+shift+s"]
toggle_edit_mode = ["i", "ctrl+e"]

View File

@@ -15,6 +15,7 @@ pub fn render_form(
current_field: &usize, current_field: &usize,
inputs: &[&String], inputs: &[&String],
theme: &Theme, theme: &Theme,
is_edit_mode: bool,
) { ) {
// Create Adresar card // Create Adresar card
let adresar_card = Block::default() let adresar_card = Block::default()
@@ -40,7 +41,11 @@ pub fn render_form(
// Create compact input container // Create compact input container
let input_container = Block::default() let input_container = Block::default()
.borders(Borders::ALL) .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)); .style(Style::default().bg(theme.bg));
// Place the input container at the top // Place the input container at the top
@@ -89,7 +94,7 @@ pub fn render_form(
f.render_widget(input_display, input_rows[i]); f.render_widget(input_display, input_rows[i]);
// Position cursor at the correct position in the active field // 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_x = input_rows[i].x + input.len() as u16;
let cursor_y = input_rows[i].y; let cursor_y = input_rows[i].y;
f.set_cursor(cursor_x, cursor_y); f.set_cursor(cursor_x, cursor_y);

View File

@@ -8,23 +8,27 @@ use ratatui::{
}; };
use crate::client::colors::Theme; 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 // Program name and version
let program_info = format!("multieko2 v{}", env!("CARGO_PKG_VERSION")); let program_info = format!("multieko2 v{}", env!("CARGO_PKG_VERSION"));
// Create the status line text let mode_text = if is_edit_mode {
let status_line = Line::from(vec![ "[EDIT]"
Span::styled(current_dir, Style::default().fg(theme.fg)), } else {
Span::styled( "[READ-ONLY]"
program_info, };
Style::default().fg(theme.secondary).add_modifier(ratatui::style::Modifier::BOLD), // Use `secondary` color
),
]);
// Render the status line // Render the status line
let paragraph = Paragraph::new(status_line) let text = format!(" {} | {}", mode_text, current_dir);
.block(Block::default().style(Style::default().bg(theme.bg))) let paragraph = Paragraph::new(text)
.alignment(Alignment::Left); .style(Style::default().fg(theme.fg).bg(theme.bg));
f.render_widget(paragraph, area); f.render_widget(paragraph, area);
} }

View File

@@ -63,4 +63,12 @@ impl Config {
} }
None 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
}
}
} }

View File

@@ -48,6 +48,8 @@ pub async fn run_ui() -> Result<(), Box<dyn std::error::Error>> {
// Track the current message to display in the command line // Track the current message to display in the command line
let mut command_message = String::new(); let mut command_message = String::new();
let mut is_edit_mode = false;
let mut edit_mode_cooldown = false;
loop { loop {
app_terminal.draw(|f| { app_terminal.draw(|f| {
@@ -77,6 +79,7 @@ pub async fn run_ui() -> Result<(), Box<dyn std::error::Error>> {
&ucet, &skladm, &ico, &kontakt, &telefon, &skladu, &fax, &ucet, &skladm, &ico, &kontakt, &telefon, &skladu, &fax,
], ],
&theme, &theme,
is_edit_mode,
); );
// Right panel - Preview Card // Right panel - Preview Card
@@ -90,7 +93,7 @@ pub async fn run_ui() -> Result<(), Box<dyn std::error::Error>> {
); );
// Status line // Status line
render_status_line(f, root[1], &current_dir, &theme); render_status_line(f, root[1], &current_dir, &theme, is_edit_mode);
// Command line // Command line
render_command_line(f, root[2], &command_input, command_mode, &theme, &command_message); render_command_line(f, root[2], &command_input, command_mode, &theme, &command_message);
@@ -98,10 +101,105 @@ pub async fn run_ui() -> Result<(), Box<dyn std::error::Error>> {
// Event handling // Event handling
if let Event::Key(key) = app_terminal.read_event()? { 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 { match key.code {
KeyCode::Enter => { KeyCode::Char(':') => {
// Create PostAdresarRequest from form data 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 { let form_data = PostAdresarRequest {
firma: firma.clone(), firma: firma.clone(),
kz: kz.clone(), kz: kz.clone(),
@@ -120,132 +218,79 @@ pub async fn run_ui() -> Result<(), Box<dyn std::error::Error>> {
fax: fax.clone(), fax: fax.clone(),
}; };
// Validate command format // Pass form data to handle_command (remove &config)
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 let (should_exit, message) = app_terminal
.handle_command(action, &mut is_saved, &form_data) .handle_command(action, &mut is_saved, &form_data)
.await?; .await?;
command_message = message; command_message = message;
command_mode = false;
command_input.clear();
if should_exit { if should_exit {
return Ok(()); return Ok(());
} }
} } else {
KeyCode::Char(c) => command_input.push(c), match key.code {
KeyCode::Backspace => { KeyCode::Char(':') => {
command_input.pop(); // Ignore the return value command_mode = true;
} command_input.clear();
KeyCode::Esc => { command_message.clear();
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();
} }
} KeyCode::Tab => {
KeyCode::BackTab => current_field = current_field.saturating_sub(1), if key.modifiers.contains(KeyModifiers::SHIFT) {
KeyCode::Down => current_field = (current_field + 1) % fields.len(), current_field = current_field.saturating_sub(1);
KeyCode::Up => current_field = current_field.saturating_sub(1), } else {
KeyCode::Enter => current_field = (current_field + 1) % fields.len(), 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::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;
} }
} }