249 lines
6.1 KiB
Rust
249 lines
6.1 KiB
Rust
// 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<String>,
|
|
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<LoginState>,
|
|
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<String>) {
|
|
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)
|
|
}
|
|
}
|