more improvements

This commit is contained in:
Priec
2025-08-20 23:52:14 +02:00
parent aea2c39215
commit 1320884409
5 changed files with 67 additions and 58 deletions

View File

@@ -8,7 +8,7 @@ license.workspace = true
anyhow = { workspace = true } anyhow = { workspace = true }
async-trait = "0.1.88" async-trait = "0.1.88"
common = { path = "../common" } common = { path = "../common" }
canvas = { path = "../canvas", features = ["gui", "suggestions"] } canvas = { path = "../canvas", features = ["gui", "suggestions", "cursor-style"] }
ratatui = { workspace = true } ratatui = { workspace = true }
crossterm = { workspace = true } crossterm = { workspace = true }

View File

@@ -1,5 +1,6 @@
// src/components/form/form.rs // src/components/form/form.rs
use crate::config::colors::themes::Theme; use crate::config::colors::themes::Theme;
use crate::state::app::state::AppState;
use crate::state::pages::form::FormState; use crate::state::pages::form::FormState;
use ratatui::{ use ratatui::{
layout::{Alignment, Constraint, Direction, Layout, Margin, Rect}, layout::{Alignment, Constraint, Direction, Layout, Margin, Rect},
@@ -8,19 +9,17 @@ use ratatui::{
Frame, Frame,
}; };
use canvas::canvas::HighlightState; use canvas::canvas::HighlightState;
use canvas::{FormEditor, render_canvas_default, render_canvas, render_suggestions_dropdown, DefaultCanvasTheme}; use canvas::{
render_canvas, render_suggestions_dropdown, DefaultCanvasTheme, FormEditor,
};
pub fn render_form( pub fn render_form(
f: &mut Frame, f: &mut Frame,
area: Rect, area: Rect,
form_state: &FormState, app_state: &AppState,
fields: &[&str], // no longer needed, FormEditor handles this form_state: &FormState, // not needed directly anymore, editor holds it
current_field_idx: &usize, // no longer needed
inputs: &[&String], // no longer needed
table_name: &str, table_name: &str,
theme: &Theme, theme: &Theme,
is_edit_mode: bool, // FormEditor tracks mode internally
highlight_state: &HighlightState,
total_count: u64, total_count: u64,
current_position: u64, current_position: u64,
) { ) {
@@ -62,24 +61,19 @@ pub fn render_form(
.alignment(Alignment::Left); .alignment(Alignment::Left);
f.render_widget(count_para, main_layout[0]); f.render_widget(count_para, main_layout[0]);
// --- FORM RENDERING (Using new canvas API) --- // --- FORM RENDERING (Using persistent FormEditor) ---
let editor = FormEditor::new(form_state.clone()); if let Some(editor) = &app_state.form_editor {
let active_field_rect = render_canvas(f, main_layout[1], editor, theme);
let active_field_rect = render_canvas( // --- SUGGESTIONS DROPDOWN ---
f, if let Some(active_rect) = active_field_rect {
main_layout[1], render_suggestions_dropdown(
&editor, f,
theme, main_layout[1],
); active_rect,
&DefaultCanvasTheme,
// --- SUGGESTIONS DROPDOWN --- editor,
if let Some(active_rect) = active_field_rect { );
render_suggestions_dropdown( }
f,
main_layout[1],
active_rect,
&DefaultCanvasTheme,
&editor,
);
} }
} }

View File

@@ -16,7 +16,7 @@ use crate::modes::{
}; };
use crate::services::auth::AuthClient; use crate::services::auth::AuthClient;
use crate::services::grpc_client::GrpcClient; use crate::services::grpc_client::GrpcClient;
use canvas::FormEditor; use canvas::{FormEditor, AppMode as CanvasMode};
use crate::state::{ use crate::state::{
app::{ app::{
buffer::{AppView, BufferState}, buffer::{AppView, BufferState},
@@ -42,7 +42,6 @@ use crate::ui::handlers::context::UiContext;
use crate::ui::handlers::rat_state::UiStateHandler; use crate::ui::handlers::rat_state::UiStateHandler;
use anyhow::Result; use anyhow::Result;
use common::proto::komp_ac::search::search_response::Hit; use common::proto::komp_ac::search::search_response::Hit;
use crossterm::cursor::SetCursorStyle;
use crossterm::event::KeyModifiers; use crossterm::event::KeyModifiers;
use crossterm::event::{Event, KeyCode, KeyEvent}; use crossterm::event::{Event, KeyCode, KeyEvent};
use tokio::sync::mpsc; use tokio::sync::mpsc;
@@ -695,10 +694,12 @@ impl EventHandler {
else if config.get_read_only_action_for_key(key_code, modifiers).as_deref() == Some("enter_edit_mode_before") else if config.get_read_only_action_for_key(key_code, modifiers).as_deref() == Some("enter_edit_mode_before")
&& ModeManager::can_enter_edit_mode(current_mode) && ModeManager::can_enter_edit_mode(current_mode)
{ {
if let Some(editor) = &mut app_state.form_editor {
editor.enter_edit_mode();
}
self.is_edit_mode = true; self.is_edit_mode = true;
self.edit_mode_cooldown = true; self.edit_mode_cooldown = true;
self.command_message = "Edit mode".to_string(); self.command_message = "Edit mode".to_string();
terminal.set_cursor_style(SetCursorStyle::BlinkingBar)?;
return Ok(EventOutcome::Ok(self.command_message.clone())); return Ok(EventOutcome::Ok(self.command_message.clone()));
} }
else if config.get_read_only_action_for_key(key_code, modifiers).as_deref() == Some("enter_edit_mode_after") else if config.get_read_only_action_for_key(key_code, modifiers).as_deref() == Some("enter_edit_mode_after")
@@ -734,16 +735,21 @@ impl EventHandler {
); );
} }
if let Some(editor) = &mut app_state.form_editor {
editor.enter_edit_mode();
}
self.is_edit_mode = true; self.is_edit_mode = true;
self.edit_mode_cooldown = true; self.edit_mode_cooldown = true;
app_state.ui.focus_outside_canvas = false; app_state.ui.focus_outside_canvas = false;
self.command_message = "Edit mode (after cursor)".to_string(); self.command_message = "Edit mode (after cursor)".to_string();
terminal.set_cursor_style(SetCursorStyle::BlinkingBar)?;
return Ok(EventOutcome::Ok(self.command_message.clone())); return Ok(EventOutcome::Ok(self.command_message.clone()));
} }
else if config.get_read_only_action_for_key(key_code, modifiers) == Some("enter_command_mode") else if config.get_read_only_action_for_key(key_code, modifiers) == Some("enter_command_mode")
&& ModeManager::can_enter_command_mode(current_mode) && ModeManager::can_enter_command_mode(current_mode)
{ {
if let Some(editor) = &mut app_state.form_editor {
editor.set_mode(CanvasMode::Command);
}
self.command_mode = true; self.command_mode = true;
self.command_input.clear(); self.command_input.clear();
self.command_message.clear(); self.command_message.clear();
@@ -772,14 +778,15 @@ impl EventHandler {
// Try canvas action for form first // Try canvas action for form first
if app_state.ui.show_form { if app_state.ui.show_form {
let mut editor = FormEditor::new(form_state.clone()); if let Some(editor) = &mut app_state.form_editor {
if let Ok(Some(canvas_message)) = self.handle_form_canvas_action( if let Ok(Some(canvas_message)) = self.handle_form_canvas_action(
key_event, key_event,
&mut editor, editor,
config, config,
false, false,
).await { ).await {
return Ok(EventOutcome::Ok(canvas_message)); return Ok(EventOutcome::Ok(canvas_message));
}
} }
} }
@@ -788,9 +795,11 @@ impl EventHandler {
AppMode::Highlight => { AppMode::Highlight => {
if config.get_highlight_action_for_key(key_code, modifiers) == Some("exit_highlight_mode") { if config.get_highlight_action_for_key(key_code, modifiers) == Some("exit_highlight_mode") {
if let Some(editor) = &mut app_state.form_editor {
editor.exit_highlight_mode();
}
self.highlight_state = HighlightState::Off; self.highlight_state = HighlightState::Off;
self.command_message = "Exited highlight mode".to_string(); self.command_message = "Exited highlight mode".to_string();
terminal.set_cursor_style(SetCursorStyle::SteadyBlock)?;
return Ok(EventOutcome::Ok(self.command_message.clone())); return Ok(EventOutcome::Ok(self.command_message.clone()));
} else if config.get_highlight_action_for_key(key_code, modifiers) == Some("enter_highlight_mode_linewise") { } else if config.get_highlight_action_for_key(key_code, modifiers) == Some("enter_highlight_mode_linewise") {
if let HighlightState::Characterwise { anchor } = self.highlight_state { if let HighlightState::Characterwise { anchor } = self.highlight_state {
@@ -827,17 +836,18 @@ impl EventHandler {
// Try canvas action for form first // Try canvas action for form first
if app_state.ui.show_form { if app_state.ui.show_form {
let mut editor = FormEditor::new(form_state.clone()); if let Some(editor) = &mut app_state.form_editor {
if let Ok(Some(canvas_message)) = self.handle_form_canvas_action( if let Ok(Some(canvas_message)) = self.handle_form_canvas_action(
key_event, key_event,
&mut editor, editor,
config, config,
true, true,
).await { ).await {
if !canvas_message.is_empty() { if !canvas_message.is_empty() {
self.command_message = canvas_message.clone(); self.command_message = canvas_message.clone();
}
return Ok(EventOutcome::Ok(canvas_message));
} }
return Ok(EventOutcome::Ok(canvas_message));
} }
} }
@@ -850,6 +860,9 @@ impl EventHandler {
self.command_message.clear(); self.command_message.clear();
self.command_mode = false; self.command_mode = false;
self.key_sequence_tracker.reset(); self.key_sequence_tracker.reset();
if let Some(editor) = &mut app_state.form_editor {
editor.set_mode(CanvasMode::ReadOnly);
}
return Ok(EventOutcome::Ok( return Ok(EventOutcome::Ok(
"Exited command mode".to_string(), "Exited command mode".to_string(),
)); ));
@@ -1079,6 +1092,9 @@ impl EventHandler {
} }
} }
"force_quit" => { "force_quit" => {
if let Some(editor) = &mut app_state.form_editor {
editor.cleanup_cursor()?;
}
terminal.cleanup()?; terminal.cleanup()?;
Ok(EventOutcome::Exit( Ok(EventOutcome::Exit(
"Force exiting without saving.".to_string(), "Force exiting without saving.".to_string(),
@@ -1106,6 +1122,9 @@ impl EventHandler {
SaveOutcome::CreatedNew(_) => "New entry created.".to_string(), SaveOutcome::CreatedNew(_) => "New entry created.".to_string(),
} }
}; };
if let Some(editor) = &mut app_state.form_editor {
editor.cleanup_cursor()?;
}
terminal.cleanup()?; terminal.cleanup()?;
Ok(EventOutcome::Exit(format!( Ok(EventOutcome::Exit(format!(
"{}. Exiting application.", "{}. Exiting application.",

View File

@@ -7,6 +7,8 @@ use common::proto::komp_ac::table_structure::TableStructureResponse;
use crate::modes::handlers::mode_manager::AppMode; use crate::modes::handlers::mode_manager::AppMode;
use crate::state::app::search::SearchState; use crate::state::app::search::SearchState;
use crate::ui::handlers::context::DialogPurpose; use crate::ui::handlers::context::DialogPurpose;
use canvas::FormEditor;
use crate::state::pages::form::FormState;
use std::collections::HashMap; use std::collections::HashMap;
use std::env; use std::env;
use std::sync::Arc; use std::sync::Arc;
@@ -67,6 +69,8 @@ pub struct AppState {
// UI preferences // UI preferences
pub ui: UiState, pub ui: UiState,
pub form_editor: Option<FormEditor<FormState>>,
#[cfg(feature = "ui-debug")] #[cfg(feature = "ui-debug")]
pub debug_state: Option<DebugState>, pub debug_state: Option<DebugState>,
} }
@@ -86,6 +90,7 @@ impl AppState {
pending_table_structure_fetch: None, pending_table_structure_fetch: None,
search_state: None, search_state: None,
ui: UiState::default(), ui: UiState::default(),
form_editor: None,
#[cfg(feature = "ui-debug")] #[cfg(feature = "ui-debug")]
debug_state: None, debug_state: None,

View File

@@ -228,15 +228,6 @@ impl FormState {
self.autocomplete_loading = false; self.autocomplete_loading = false;
} }
// NEW: Add these methods to change modes
pub fn set_edit_mode(&mut self) {
self.app_mode = AppMode::Edit;
}
pub fn set_readonly_mode(&mut self) {
self.app_mode = AppMode::ReadOnly;
}
// Legacy method compatibility // Legacy method compatibility
pub fn fields(&self) -> Vec<&str> { pub fn fields(&self) -> Vec<&str> {
self.fields self.fields