// src/pages/login/state.rs use canvas::{AppMode, DataProvider}; use canvas::FormEditor; use std::fmt; #[derive(Debug, Clone)] pub struct LoginState { pub username: String, pub password: String, pub error_message: Option, pub current_field: usize, pub current_cursor_pos: usize, pub has_unsaved_changes: bool, pub login_request_pending: bool, pub app_mode: AppMode, } impl Default for LoginState { fn default() -> Self { Self { username: String::new(), password: String::new(), error_message: None, current_field: 0, current_cursor_pos: 0, has_unsaved_changes: false, login_request_pending: false, app_mode: canvas::AppMode::Edit, } } } impl LoginState { pub fn new() -> Self { Self { app_mode: canvas::AppMode::Edit, ..Default::default() } } pub fn current_field(&self) -> usize { self.current_field } pub fn current_cursor_pos(&self) -> usize { self.current_cursor_pos } pub fn set_current_field(&mut self, index: usize) { if index < 2 { self.current_field = index; } } pub fn set_current_cursor_pos(&mut self, pos: usize) { self.current_cursor_pos = pos; } pub fn get_current_input(&self) -> &str { match self.current_field { 0 => &self.username, 1 => &self.password, _ => "", } } pub fn get_current_input_mut(&mut self) -> &mut String { match self.current_field { 0 => &mut self.username, 1 => &mut self.password, _ => panic!("Invalid current_field index in LoginState"), } } pub fn current_mode(&self) -> AppMode { self.app_mode } pub fn has_unsaved_changes(&self) -> bool { self.has_unsaved_changes } pub fn set_has_unsaved_changes(&mut self, changed: bool) { self.has_unsaved_changes = changed; } } // Implement DataProvider for LoginState impl DataProvider for LoginState { fn field_count(&self) -> usize { 2 } fn field_name(&self, index: usize) -> &str { match index { 0 => "Username/Email", 1 => "Password", _ => "", } } fn field_value(&self, index: usize) -> &str { match index { 0 => &self.username, 1 => &self.password, _ => "", } } fn set_field_value(&mut self, index: usize, value: String) { match index { 0 => self.username = value, 1 => self.password = value, _ => {} } self.has_unsaved_changes = true; } fn supports_suggestions(&self, _field_index: usize) -> bool { false // Login form doesn't support suggestions } } /// Wrapper that owns both the raw login state and its editor pub struct LoginFormState { pub state: LoginState, pub editor: FormEditor, pub focus_outside_canvas: bool, pub focused_button_index: usize, } // manual debug because FormEditor doesnt implement debug impl fmt::Debug for LoginFormState { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("LoginFormState") .field("state", &self.state) // ✅ only print the data .finish() } } impl LoginFormState { /// Sync the editor's data provider back into our state pub fn sync_from_editor(&mut self) { // FormEditor holds the authoritative data let dp = self.editor.data_provider(); self.state = dp.clone(); // LoginState implements Clone } /// Create a new LoginFormState with default LoginState and FormEditor pub fn new() -> Self { let state = LoginState::default(); let editor = FormEditor::new(state.clone()); Self { state, editor, focus_outside_canvas: false, focused_button_index: 0, } } // === Delegates to LoginState fields === pub fn username(&self) -> &str { &self.state.username } pub fn username_mut(&mut self) -> &mut String { &mut self.state.username } pub fn password(&self) -> &str { &self.state.password } pub fn password_mut(&mut self) -> &mut String { &mut self.state.password } pub fn error_message(&self) -> Option<&String> { self.state.error_message.as_ref() } pub fn set_error_message(&mut self, msg: Option) { self.state.error_message = msg; } pub fn has_unsaved_changes(&self) -> bool { self.state.has_unsaved_changes } pub fn set_has_unsaved_changes(&mut self, changed: bool) { self.state.has_unsaved_changes = changed; } pub fn clear(&mut self) { self.state.username.clear(); self.state.password.clear(); self.state.error_message = None; self.state.has_unsaved_changes = false; self.state.login_request_pending = false; self.state.current_cursor_pos = 0; } // === Delegates to LoginState cursor/input === pub fn current_field(&self) -> usize { self.state.current_field() } pub fn set_current_field(&mut self, index: usize) { self.state.set_current_field(index); } pub fn current_cursor_pos(&self) -> usize { self.state.current_cursor_pos() } pub fn set_current_cursor_pos(&mut self, pos: usize) { self.state.set_current_cursor_pos(pos); } pub fn get_current_input(&self) -> &str { self.state.get_current_input() } pub fn get_current_input_mut(&mut self) -> &mut String { self.state.get_current_input_mut() } // === Delegates to FormEditor === pub fn mode(&self) -> AppMode { self.editor.mode() } pub fn cursor_position(&self) -> usize { self.editor.cursor_position() } pub fn handle_key_event( &mut self, key_event: crossterm::event::KeyEvent, ) -> canvas::keymap::KeyEventOutcome { self.editor.handle_key_event(key_event) } }