1#![allow(dead_code)]
3extern crate alloc;
4use alloc::string::String;
5use alloc::vec::Vec;
6
7use crate::kernel::draw::{Color, Screen};
8
9#[derive(Debug, Clone, PartialEq)]
10pub enum DialogMode {
11 Open,
12 Save,
13}
14
15pub struct FileDialog {
16 pub mode: DialogMode,
17 pub title: String,
18 pub files: Vec<String>,
19 pub selected_idx: Option<usize>,
20 pub filename_input: String,
21 pub is_open: bool,
22 pub result: Option<String>,
23 pub offset_y: usize,
24}
25
26impl Default for FileDialog {
27 fn default() -> Self {
28 Self::new()
29 }
30}
31
32impl FileDialog {
33 pub fn new() -> Self {
34 Self {
35 mode: DialogMode::Open,
36 title: String::new(),
37 files: Vec::new(),
38 selected_idx: None,
39 filename_input: String::new(),
40 is_open: false,
41 result: None,
42 offset_y: 0,
43 }
44 }
45
46 pub fn open(&mut self, mode: DialogMode, default_filename: &str) {
47 self.mode = mode.clone();
48 self.title = if mode == DialogMode::Open {
49 String::from("Open File")
50 } else {
51 String::from("Save File")
52 };
53 self.filename_input = String::from(default_filename);
54 self.is_open = true;
55 self.result = None;
56 self.selected_idx = None;
57 self.offset_y = 0;
58 self.refresh_files();
59 }
60
61 pub fn refresh_files(&mut self) {
62 self.files.clear();
63 let fs = crate::kernel::fs::get_fs();
64 for meta in fs.list_files() {
65 self.files.push(String::from(meta.get_filename()));
66 }
67 }
68
69 pub fn close(&mut self) {
70 self.is_open = false;
71 }
72
73 pub fn handle_mouse(
74 &mut self,
75 mx: i32,
76 my: i32,
77 left_click: bool,
78 screen_width: u32,
79 screen_height: u32,
80 ) -> bool {
81 if !self.is_open {
82 return false;
83 }
84
85 let cx = screen_width as i32 / 2;
86 let cy = screen_height as i32 / 2;
87 let w = 600;
88 let h = 400;
89 let x = cx - w / 2;
90 let y = cy - h / 2;
91
92 if left_click {
93 if mx < x || mx > x + w || my < y || my > y + h {
95 } else {
97 if mx >= x + w - 30 && mx <= x + w - 10 && my >= y + 10 && my <= y + 30 {
100 self.close();
101 return true;
102 }
103
104 let list_x = x + 20;
106 let list_y = y + 50;
107 let list_w = w - 40;
108 let list_h = 240;
109
110 if mx >= list_x && mx <= list_x + list_w && my >= list_y && my <= list_y + list_h {
111 let rel_y = my - list_y;
112 let item_idx = (rel_y / 30) as usize + self.offset_y;
113 if item_idx < self.files.len() {
114 self.selected_idx = Some(item_idx);
115 self.filename_input = self.files[item_idx].clone();
116 }
117 }
118
119 let btn_y = y + h - 40;
121 let cancel_x = x + w - 200;
122 let action_x = x + w - 90;
123
124 if mx >= cancel_x && mx <= cancel_x + 80 && my >= btn_y && my <= btn_y + 30 {
125 self.close();
126 } else if mx >= action_x
127 && mx <= action_x + 80
128 && my >= btn_y
129 && my <= btn_y + 30
130 && !self.filename_input.is_empty()
131 {
132 self.result = Some(self.filename_input.clone());
133 self.close();
134 }
135 }
136 } else {
137 }
139
140 true }
142
143 pub fn handle_key(&mut self, keycode: u8, c: Option<char>) -> bool {
144 if !self.is_open {
145 return false;
146 }
147
148 if keycode == 0x01 {
149 self.close();
151 return true;
152 }
153
154 if keycode == 0x1C {
155 if !self.filename_input.is_empty() {
157 self.result = Some(self.filename_input.clone());
158 self.close();
159 }
160 return true;
161 }
162
163 if keycode == 0x0E {
164 if !self.filename_input.is_empty() {
166 self.filename_input.pop();
167 }
168 return true;
169 }
170
171 if let Some(ch) = c {
172 if ch.is_ascii_alphanumeric() || ch == '.' || ch == '_' || ch == '-' {
173 self.filename_input.push(ch);
174 }
175 }
176
177 true }
179
180 pub fn draw(&self, screen: &mut Screen) {
181 if !self.is_open {
182 return;
183 }
184
185 let t = &crate::kernel::config::get_config().theme;
186
187 let cx = screen.width / 2;
188 let cy = screen.height / 2;
189 let w = 600;
190 let h = 400;
191 let x = cx - w / 2;
192 let y = cy - h / 2;
193
194 screen.boxfill(x + 5, y + 5, x + w + 5, y + h + 5, Color(0x80000000));
196
197 screen.boxfill(x, y, x + w, y + h, Color(t.login_dialog_bg));
199
200 screen.boxfill(x, y, x + w, y + 40, Color(t.window_title_bar));
202 screen.draw_string_vector(x + 20, y + 10, &self.title, Color(t.window_title_fg), 20);
203
204 screen.draw_string_vector(x + w - 30, y + 10, "X", Color(t.win_btn_close), 20);
206
207 let list_x = x + 20;
209 let list_y = y + 50;
210 let list_w = w - 40;
211 screen.boxfill(x + 20, y + 50, x + w - 20, y + 50 + 240, Color(t.terminal_bg));
212
213 for (i, file) in self.files.iter().skip(self.offset_y).take(8).enumerate() {
215 let item_y = list_y + (i as u32) * 30;
216 if Some(i + self.offset_y) == self.selected_idx {
217 screen.boxfill(
218 list_x,
219 item_y,
220 list_x + list_w,
221 item_y + 30,
222 Color(t.focus_highlight),
223 );
224 }
225 screen.draw_string_vector(list_x + 10, item_y + 6, file, Color(t.terminal_fg), 16);
226 }
227
228 let input_y = y + 310;
230 screen.draw_string_vector(x + 20, input_y + 5, "Filename:", Color(t.window_title_fg), 16);
231 screen.boxfill(
232 x + 110,
233 input_y,
234 x + w - 20,
235 input_y + 30,
236 Color(t.button_bg),
237 );
238 screen.draw_string_vector(
239 x + 115,
240 input_y + 6,
241 &self.filename_input,
242 Color(t.app_fg),
243 16,
244 );
245
246 let btn_y = y + h - 40;
248 let cancel_x = x + w - 200;
249 let action_x = x + w - 90;
250
251 screen.boxfill(
252 cancel_x,
253 btn_y,
254 cancel_x + 80,
255 btn_y + 30,
256 Color(t.button_bg),
257 );
258 screen.draw_string_vector(cancel_x + 10, btn_y + 6, "Cancel", Color(t.button_fg), 16);
259
260 screen.boxfill(action_x, btn_y, action_x + 80, btn_y + 30, Color(t.button_active_bg));
261 screen.draw_string_vector(
262 action_x + 20,
263 btn_y + 6,
264 if self.mode == DialogMode::Open {
265 "Open"
266 } else {
267 "Save"
268 },
269 Color(t.button_active_fg),
270 16,
271 );
272 }
273}
274
275pub static mut GLOBAL_FILE_DIALOG: Option<FileDialog> = None;