switch handled by the library from now on
This commit is contained in:
@@ -65,7 +65,7 @@ impl<D: DataProvider> FormEditor<D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get current field text (convenience method)
|
/// Get current field text (convenience method)
|
||||||
pub fn current_text(&self) -> &str {
|
fn current_text(&self) -> &str {
|
||||||
// Convenience wrapper, kept for compatibility with existing code
|
// Convenience wrapper, kept for compatibility with existing code
|
||||||
let field_index = self.ui_state.current_field;
|
let field_index = self.ui_state.current_field;
|
||||||
if field_index < self.data_provider.field_count() {
|
if field_index < self.data_provider.field_count() {
|
||||||
@@ -351,6 +351,10 @@ impl<D: DataProvider> FormEditor<D> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear any previous switch block status on successful transition start
|
||||||
|
#[cfg(feature = "validation")]
|
||||||
|
self.ui_state.validation.clear_last_switch_block();
|
||||||
|
|
||||||
// 3. Blocking validation before leaving current field
|
// 3. Blocking validation before leaving current field
|
||||||
#[cfg(feature = "validation")]
|
#[cfg(feature = "validation")]
|
||||||
{
|
{
|
||||||
@@ -361,6 +365,10 @@ impl<D: DataProvider> FormEditor<D> {
|
|||||||
.validation
|
.validation
|
||||||
.field_switch_block_reason(prev_field, current_text)
|
.field_switch_block_reason(prev_field, current_text)
|
||||||
{
|
{
|
||||||
|
// Record the block reason for UI
|
||||||
|
self.ui_state
|
||||||
|
.validation
|
||||||
|
.set_last_switch_block(reason.clone());
|
||||||
tracing::debug!("Field switch blocked: {}", reason);
|
tracing::debug!("Field switch blocked: {}", reason);
|
||||||
return Err(anyhow::anyhow!("Cannot switch fields: {}", reason));
|
return Err(anyhow::anyhow!("Cannot switch fields: {}", reason));
|
||||||
}
|
}
|
||||||
@@ -737,6 +745,53 @@ impl<D: DataProvider> FormEditor<D> {
|
|||||||
self.ui_state.validation.field_switch_block_reason(self.ui_state.current_field, current_text)
|
self.ui_state.validation.field_switch_block_reason(self.ui_state.current_field, current_text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the last field switch block reason (UI convenience)
|
||||||
|
#[cfg(feature = "validation")]
|
||||||
|
pub fn last_switch_block(&self) -> Option<&str> {
|
||||||
|
self.ui_state.validation.last_switch_block()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get character limits status text for current field (UI convenience)
|
||||||
|
#[cfg(feature = "validation")]
|
||||||
|
pub fn current_limits_status_text(&self) -> Option<String> {
|
||||||
|
let idx = self.ui_state.current_field;
|
||||||
|
if let Some(cfg) = self.ui_state.validation.get_field_config(idx) {
|
||||||
|
if let Some(limits) = &cfg.character_limits {
|
||||||
|
return limits.status_text(self.current_text());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get current custom formatter warning (UI convenience)
|
||||||
|
#[cfg(feature = "validation")]
|
||||||
|
pub fn current_formatter_warning(&self) -> Option<String> {
|
||||||
|
let idx = self.ui_state.current_field;
|
||||||
|
if let Some(cfg) = self.ui_state.validation.get_field_config(idx) {
|
||||||
|
if let Some((_fmt, _mapper, warn)) = cfg.run_custom_formatter(self.current_text()) {
|
||||||
|
return warn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get external validation state for specific field (UI convenience)
|
||||||
|
#[cfg(feature = "validation")]
|
||||||
|
pub fn external_validation_of(
|
||||||
|
&self,
|
||||||
|
field_index: usize,
|
||||||
|
) -> crate::validation::ExternalValidationState {
|
||||||
|
self.ui_state
|
||||||
|
.validation
|
||||||
|
.get_external_validation(field_index)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear all external validation states (UI convenience)
|
||||||
|
#[cfg(feature = "validation")]
|
||||||
|
pub fn clear_all_external_validation(&mut self) {
|
||||||
|
self.ui_state.validation.clear_all_external_validation();
|
||||||
|
}
|
||||||
|
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
// ASYNC OPERATIONS: Only suggestions need async
|
// ASYNC OPERATIONS: Only suggestions need async
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
@@ -798,9 +853,8 @@ impl<D: DataProvider> FormEditor<D> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Update cursor position
|
// Update cursor position
|
||||||
let char_len = suggestion.value_to_store.chars().count();
|
self.ui_state.cursor_pos = suggestion.value_to_store.len();
|
||||||
self.ui_state.cursor_pos = char_len;
|
self.ui_state.ideal_cursor_column = self.ui_state.cursor_pos;
|
||||||
self.ui_state.ideal_cursor_column = char_len;
|
|
||||||
|
|
||||||
// Close suggestions
|
// Close suggestions
|
||||||
self.ui_state.deactivate_suggestions();
|
self.ui_state.deactivate_suggestions();
|
||||||
@@ -1145,6 +1199,13 @@ impl<D: DataProvider> FormEditor<D> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Handle Escape key in ReadOnly mode (closes suggestions if active)
|
||||||
|
pub fn handle_escape_readonly(&mut self) {
|
||||||
|
if self.ui_state.suggestions.is_active {
|
||||||
|
self.close_suggestions();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Exit edit mode to read-only mode (vim Escape)
|
/// Exit edit mode to read-only mode (vim Escape)
|
||||||
pub fn exit_edit_mode(&mut self) -> Result<()> {
|
pub fn exit_edit_mode(&mut self) -> Result<()> {
|
||||||
// Validate current field content when exiting edit mode
|
// Validate current field content when exiting edit mode
|
||||||
@@ -1153,6 +1214,10 @@ impl<D: DataProvider> FormEditor<D> {
|
|||||||
let current_text = self.current_text();
|
let current_text = self.current_text();
|
||||||
if !self.ui_state.validation.allows_field_switch(self.ui_state.current_field, current_text) {
|
if !self.ui_state.validation.allows_field_switch(self.ui_state.current_field, current_text) {
|
||||||
if let Some(reason) = self.ui_state.validation.field_switch_block_reason(self.ui_state.current_field, current_text) {
|
if let Some(reason) = self.ui_state.validation.field_switch_block_reason(self.ui_state.current_field, current_text) {
|
||||||
|
// Record the block reason for UI
|
||||||
|
self.ui_state
|
||||||
|
.validation
|
||||||
|
.set_last_switch_block(reason.clone());
|
||||||
return Err(anyhow::anyhow!("Cannot exit edit mode: {}", reason));
|
return Err(anyhow::anyhow!("Cannot exit edit mode: {}", reason));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ pub struct ValidationState {
|
|||||||
|
|
||||||
/// External validation results per field (Feature 5)
|
/// External validation results per field (Feature 5)
|
||||||
external_results: HashMap<usize, ExternalValidationState>,
|
external_results: HashMap<usize, ExternalValidationState>,
|
||||||
|
|
||||||
|
last_switch_block: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ValidationState {
|
impl ValidationState {
|
||||||
@@ -32,6 +34,7 @@ impl ValidationState {
|
|||||||
validated_fields: std::collections::HashSet::new(),
|
validated_fields: std::collections::HashSet::new(),
|
||||||
enabled: true,
|
enabled: true,
|
||||||
external_results: HashMap::new(),
|
external_results: HashMap::new(),
|
||||||
|
last_switch_block: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,6 +259,22 @@ impl ValidationState {
|
|||||||
error_fields: errors,
|
error_fields: errors,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Set the last switch block reason (for UI convenience)
|
||||||
|
pub fn set_last_switch_block<S: Into<String>>(&mut self, reason: S) {
|
||||||
|
self.last_switch_block = Some(reason.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear the last switch block reason
|
||||||
|
pub fn clear_last_switch_block(&mut self) {
|
||||||
|
self.last_switch_block = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the last switch block reason (if any)
|
||||||
|
pub fn last_switch_block(&self) -> Option<&str> {
|
||||||
|
self.last_switch_block.as_deref()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Summary of validation state across all fields
|
/// Summary of validation state across all fields
|
||||||
|
|||||||
Reference in New Issue
Block a user