1use anyhow::Result;
2use ratatui_textarea::TextArea;
3use std::collections::HashMap;
4use std::path::PathBuf;
5
6use crate::actions::UndoHistory;
7use crate::excel::Workbook;
8
9pub enum InputMode {
10 Normal,
11 Editing,
12 Command,
13 SearchForward,
14 SearchBackward,
15 Help,
16}
17
18pub struct AppState<'a> {
19 pub workbook: Workbook,
20 pub file_path: PathBuf,
21 pub selected_cell: (usize, usize), pub start_row: usize,
23 pub start_col: usize,
24 pub visible_rows: usize,
25 pub visible_cols: usize,
26 pub input_mode: InputMode,
27 pub input_buffer: String,
28 pub text_area: TextArea<'a>,
29 pub should_quit: bool,
30 pub column_widths: Vec<usize>, pub sheet_column_widths: HashMap<String, Vec<usize>>, pub clipboard: Option<String>, pub g_pressed: bool, pub search_query: String, pub search_results: Vec<(usize, usize)>, pub current_search_idx: Option<usize>, pub search_direction: bool, pub highlight_enabled: bool, pub info_panel_height: usize,
40 pub notification_messages: Vec<String>,
41 pub max_notifications: usize,
42 pub help_text: String,
43 pub help_scroll: usize,
44 pub help_visible_lines: usize,
45 pub undo_history: UndoHistory,
46}
47
48impl AppState<'_> {
49 pub fn new(workbook: Workbook, file_path: PathBuf) -> Result<Self> {
50 let max_cols = workbook.get_current_sheet().max_cols;
52 let default_width = 15;
53 let column_widths = vec![default_width; max_cols + 1];
54
55 let mut sheet_column_widths = HashMap::with_capacity(workbook.get_sheet_names().len());
57 let sheet_names = workbook.get_sheet_names();
58
59 for (i, name) in sheet_names.iter().enumerate() {
60 if i == workbook.get_current_sheet_index() {
61 sheet_column_widths.insert(name.clone(), column_widths.clone());
62 } else {
63 let sheet_max_cols = if let Some(sheet) = workbook.get_sheet_by_index(i) {
64 sheet.max_cols
65 } else {
66 max_cols };
68 sheet_column_widths.insert(name.clone(), vec![default_width; sheet_max_cols + 1]);
69 }
70 }
71
72 let text_area = TextArea::default();
74
75 Ok(Self {
76 workbook,
77 file_path,
78 selected_cell: (1, 1), start_row: 1,
80 start_col: 1,
81 visible_rows: 30, visible_cols: 15, input_mode: InputMode::Normal,
84 input_buffer: String::new(),
85 text_area,
86 should_quit: false,
87 column_widths,
88 sheet_column_widths,
89 clipboard: None,
90 g_pressed: false,
91 search_query: String::new(),
92 search_results: Vec::new(),
93 current_search_idx: None,
94 search_direction: true, highlight_enabled: true, info_panel_height: 10,
97 notification_messages: Vec::new(),
98 max_notifications: 5,
99 help_text: String::new(),
100 help_scroll: 0,
101 help_visible_lines: 20,
102 undo_history: UndoHistory::new(),
103 })
104 }
105
106 pub fn add_notification(&mut self, message: String) {
107 self.notification_messages.push(message);
108
109 if self.notification_messages.len() > self.max_notifications {
110 self.notification_messages.remove(0);
111 }
112 }
113
114 pub fn adjust_info_panel_height(&mut self, delta: isize) {
115 let new_height = (self.info_panel_height as isize + delta).clamp(6, 16) as usize;
116 if new_height != self.info_panel_height {
117 self.info_panel_height = new_height;
118 self.add_notification(format!("Info panel height: {}", self.info_panel_height));
119 }
120 }
121
122 pub fn get_cell_content(&self, row: usize, col: usize) -> String {
123 let sheet = self.workbook.get_current_sheet();
124
125 if row < sheet.data.len() && col < sheet.data[0].len() {
126 let cell = &sheet.data[row][col];
127 if cell.is_formula {
128 let mut result = String::with_capacity(9 + cell.value.len());
129 result.push_str("Formula: ");
130 result.push_str(&cell.value);
131 result
132 } else {
133 cell.value.clone()
134 }
135 } else {
136 String::new()
137 }
138 }
139
140 pub fn get_cell_content_mut(&mut self, row: usize, col: usize) -> String {
141 self.workbook.ensure_cell_exists(row, col);
142
143 self.ensure_column_widths();
144
145 let sheet = self.workbook.get_current_sheet();
146 let cell = &sheet.data[row][col];
147
148 if cell.is_formula {
149 let mut result = String::with_capacity(9 + cell.value.len());
150 result.push_str("Formula: ");
151 result.push_str(&cell.value);
152 result
153 } else {
154 cell.value.clone()
155 }
156 }
157
158 pub fn cancel_input(&mut self) {
159 if let InputMode::Help = self.input_mode {
161 self.input_mode = InputMode::Normal;
162 return;
163 }
164
165 self.input_mode = InputMode::Normal;
167 self.input_buffer = String::new();
168 self.text_area = TextArea::default();
169 }
170
171 pub fn add_char_to_input(&mut self, c: char) {
172 self.input_buffer.push(c);
173 }
174
175 pub fn delete_char_from_input(&mut self) {
176 self.input_buffer.pop();
177 }
178
179 pub fn start_command_mode(&mut self) {
180 self.input_mode = InputMode::Command;
181 self.input_buffer = String::new();
182 }
183}