1#![allow(dead_code)]
7
8use crate::kernel::draw::{Color, Screen};
9use crate::os_lib::web_engine::WebEngine;
10use alloc::string::String;
11use alloc::vec::Vec;
12
13const MENU_HEIGHT: u32 = 24;
14
15struct NoteTab {
17 filename: String,
18 content: String,
19 dirty: bool,
20}
21
22impl NoteTab {
23 fn new_untitled() -> Self {
24 Self {
25 filename: String::from("Untitled"),
26 content: String::new(),
27 dirty: false,
28 }
29 }
30
31 fn title(&self) -> String {
32 if self.dirty {
33 alloc::format!("* {}", self.filename)
34 } else {
35 self.filename.clone()
36 }
37 }
38}
39
40pub struct NoteApp {
41 tabs_data: Vec<NoteTab>,
42 active_idx: usize,
43 pub engine: WebEngine,
44 pub last_win_w: u32,
45 pub last_win_h: u32,
46 pub show_menu: bool,
47 pub open_menu: Option<u8>, pub last_btn: bool,
49 ctrl_held: bool,
50 alt_held: bool,
51
52 pub panel_slide: i32,
54 pub panel_mode: u8, pub panel_files: Vec<String>,
56 pub panel_input: String,
57 pub panel_selected: Option<usize>,
58
59 pub output_panel_open: bool,
61 pub output_text: String,
62 pub output_panel_height: u32,
63}
64
65impl Default for NoteApp {
66 fn default() -> Self {
67 Self::new()
68 }
69}
70
71impl NoteApp {
72 pub fn open_file(filename: &str, content: &str) -> Self {
74 let mut app = Self::new();
75 if let Some(tab) = app.tabs_data.get_mut(0) {
76 tab.filename = String::from(filename);
77 tab.content = String::from(content);
78 tab.dirty = false;
79 }
80 app.engine
81 .form_values
82 .insert(String::from("editor"), String::from(content));
83 app.engine
84 .form_values
85 .insert(String::from("filename"), String::from(filename));
86 app.engine.dirty = true;
87 app
88 }
89
90 pub fn new() -> Self {
91 let mut engine = WebEngine::new();
92 engine
93 .form_values
94 .insert(String::from("filename"), String::from("Untitled"));
95 engine.parse_and_layout(
98 "<style>html { margin: 0; }</style><textarea id=\"editor\" autofocus style=\"width: 100%; height: 100%; margin: 0; border: none;\"></textarea>",
99 None,
100 0, 600, 450,
101 );
102 Self {
103 tabs_data: alloc::vec![NoteTab::new_untitled()],
104 active_idx: 0,
105 engine,
106 last_win_w: 600,
107 last_win_h: 450,
108 show_menu: false,
109 open_menu: None,
110 last_btn: false,
111 ctrl_held: false,
112 alt_held: false,
113 panel_slide: 0,
114 panel_mode: 0,
115 panel_files: Vec::new(),
116 panel_input: String::new(),
117 panel_selected: None,
118 output_panel_open: false,
119 output_text: String::new(),
120 output_panel_height: 120,
121 }
122 }
123
124 fn flush_current_tab(&mut self) {
126 if let Some(content) = self.engine.form_values.get("editor") {
127 let content = content.clone();
128 if let Some(tab) = self.tabs_data.get_mut(self.active_idx) {
129 if tab.content != content {
130 tab.content = content;
131 tab.dirty = true;
132 }
133 }
134 }
135 }
136
137 fn load_tab_into_engine(&mut self, idx: usize) {
139 if idx >= self.tabs_data.len() {
140 return;
141 }
142 let content = self.tabs_data[idx].content.clone();
143 let filename = self.tabs_data[idx].filename.clone();
144 self.engine
145 .form_values
146 .insert(String::from("editor"), content);
147 self.engine
148 .form_values
149 .insert(String::from("filename"), filename);
150 self.engine.dirty = true;
151 }
152
153 fn open_panel(&mut self, mode: u8) {
154 self.flush_current_tab();
155 self.panel_mode = mode;
156 if let Some(tab) = self.tabs_data.get(self.active_idx) {
157 self.panel_input = tab.filename.clone();
158 }
159 self.panel_selected = None;
160 self.panel_files.clear();
161 let fs = crate::kernel::fs::get_fs();
162 for meta in fs.list_files() {
163 self.panel_files.push(String::from(meta.get_filename()));
164 }
165 }
166
167 fn save_current(&mut self) {
168 self.flush_current_tab();
169 let (filename, content) = if let Some(tab) = self.tabs_data.get_mut(self.active_idx) {
170 (tab.filename.clone(), tab.content.clone())
171 } else {
172 return;
173 };
174
175 let fs = crate::kernel::fs::get_fs();
176 if fs.save_file(&filename, content.as_bytes(), "note").is_ok() {
177 crate::info!("[FS] Note: Saved to {}", filename);
178 if let Some(tab) = self.tabs_data.get_mut(self.active_idx) {
179 tab.dirty = false;
180 }
181 self.engine
182 .form_values
183 .insert(String::from("filename"), filename.clone());
184 crate::kernel::window_mgr::get_instance().dirty = true;
185 } else {
186 crate::error!("[FS] Note: Failed to save {}", filename);
187 }
188 }
189
190 fn load_file_into_current(&mut self) {
191 let filename = if let Some(tab) = self.tabs_data.get(self.active_idx) {
192 tab.filename.clone()
193 } else {
194 return;
195 };
196
197 let fs = crate::kernel::fs::get_fs();
198 if let Some((_, content_bytes)) = fs.read_file(&filename) {
199 if let Ok(content_str) = core::str::from_utf8(&content_bytes) {
200 if let Some(tab) = self.tabs_data.get_mut(self.active_idx) {
201 tab.content = String::from(content_str);
202 tab.dirty = false;
203 }
204 self.load_tab_into_engine(self.active_idx);
205 crate::info!("[FS] Note: Loaded from {}", filename);
206 crate::kernel::window_mgr::get_instance().dirty = true;
207 }
208 }
209 }
210
211 fn new_tab_action(&mut self) {
212 self.flush_current_tab();
213 self.tabs_data.push(NoteTab::new_untitled());
214 let new_idx = self.tabs_data.len() - 1;
215 self.active_idx = new_idx;
216 self.load_tab_into_engine(new_idx);
217 crate::kernel::window_mgr::get_instance().dirty = true;
219 }
220
221 fn run_script(&mut self) {
223 self.flush_current_tab();
224 let content = self
225 .engine
226 .form_values
227 .get("editor")
228 .cloned()
229 .unwrap_or_default();
230
231 self.output_text = String::from("Running script...\n");
232 self.output_panel_open = true;
233 crate::kernel::window_mgr::get_instance().dirty = true;
234
235 let mut run_env = crate::os_lib::aura::eval::setup_global_env(core::ptr::null_mut());
237
238 let manifest = crate::kernel::app_sandbox::AppManifest {
240 name: String::from("note_sandbox"),
241 permissions: alloc::vec![], ..Default::default()
243 };
244
245 let mut sandbox = crate::kernel::app_sandbox::AppSandbox::new(0, manifest);
246 sandbox.data_dir = String::from("/apps/note/sandbox/");
247 run_env.sandbox = Some(sandbox);
248
249 run_env.set(
251 alloc::string::String::from("cwd"),
252 crate::os_lib::aura::eval::Value::Str(String::from("/apps/note/sandbox/")),
253 );
254
255 let result = crate::os_lib::aura::eval_string(&content, &mut run_env);
257
258 if result.is_empty() {
259 self.output_text = String::from("Execution finished (no output).");
260 } else {
261 self.output_text = result;
262 }
263 }
264}
265
266impl crate::kernel::window_mgr::App for NoteApp {
267 fn name(&self) -> &str {
268 "note"
269 }
270
271 fn instance_info(&self) -> String {
272 if let Some(tab) = self.tabs_data.get(self.active_idx) {
273 tab.filename.clone()
274 } else {
275 String::from("Note")
276 }
277 }
278
279 fn draw(&mut self, screen: &Screen, win_x: u32, win_y: u32, win_w: u32, win_h: u32) {
280 let config = crate::kernel::config::get_config();
281 let bg_color = Color(config.theme.active_window_border);
282 let text_color = Color(config.theme.window_title_fg);
283 let highlight = Color(config.theme.focus_highlight);
284
285 let target_editor_h = if self.output_panel_open {
287 win_h
288 .saturating_sub(MENU_HEIGHT)
289 .saturating_sub(self.output_panel_height)
290 } else {
291 win_h.saturating_sub(MENU_HEIGHT)
292 };
293
294 if self.last_win_w != win_w
295 || self.last_win_h != win_h
296 || self.engine.win_h != target_editor_h
297 {
298 self.last_win_w = win_w;
299 self.last_win_h = win_h;
300 self.engine.win_w = win_w;
301 self.engine.win_h = target_editor_h;
302 let html = self.engine.last_html.clone();
303 self.engine
304 .parse_and_layout(&html, None, 0, self.engine.win_w, self.engine.win_h);
305 }
306
307 if let Some(content) = self.engine.form_values.get("editor") {
309 let content = content.clone();
310 if let Some(tab) = self.tabs_data.get_mut(self.active_idx) {
311 if !tab.dirty && tab.content != content {
312 tab.dirty = true;
313 crate::kernel::window_mgr::get_instance().dirty = true;
314 }
315 tab.content = content;
316 }
317 }
318
319 screen.boxfill(
321 win_x,
322 win_y,
323 win_x + win_w - 1,
324 win_y + MENU_HEIGHT - 1,
325 bg_color,
326 );
327 let menus = ["File", "Edit", "Run", "Format", "View", "Help"];
328 for (i, title) in menus.iter().enumerate() {
329 let mx = win_x + 10 + (i as u32 * 55);
330 if Some(i as u8) == self.open_menu {
331 screen.boxfill(mx - 5, win_y, mx + 45, win_y + MENU_HEIGHT - 1, highlight);
332 }
333 screen.draw_string_vector(mx, win_y + 4, title, text_color, 14);
334 }
335
336 if win_h > MENU_HEIGHT {
338 let editor_h = if self.output_panel_open {
339 win_h
340 .saturating_sub(MENU_HEIGHT)
341 .saturating_sub(self.output_panel_height)
342 } else {
343 win_h.saturating_sub(MENU_HEIGHT)
344 };
345 if editor_h > 0 {
346 self.engine
347 .draw(screen, win_x, win_y + MENU_HEIGHT, win_w, editor_h, 0);
348 }
349 }
350
351 if let Some(menu_idx) = self.open_menu {
353 let mx = win_x + 10 + (menu_idx as u32 * 55) - 5;
354 let my = win_y + MENU_HEIGHT;
355 let mw = 200u32;
356 let items: &[(&str, &str)] = match menu_idx {
357 0 => &[
358 ("New Tab", "Alt+T"),
359 ("Open...", "Alt+O"),
360 ("Save", "Alt+S"),
361 ("Save As...", ""),
362 ],
363 1 => &[("Undo", ""), ("Cut", ""), ("Copy", ""), ("Paste", "")],
364 2 => &[], 3 => &[(
366 if self.engine.editor_wrap {
367 "Word Wrap: ON"
368 } else {
369 "Word Wrap: OFF"
370 },
371 "",
372 )],
373 4 => &[
374 ("Zoom In", ""),
375 ("Zoom Out", ""),
376 ("Toggle Output", "Alt+P"),
377 ],
378 5 => &[("About AtmNote", "")],
379 _ => &[],
380 };
381
382 let mh = (items.len() as u32) * 24 + 4;
383 if mh > 4 {
384 screen.boxfill(mx + 2, my + 2, mx + mw + 2, my + mh + 2, Color(0x80000000));
385 screen.boxfill(mx, my, mx + mw, my + mh, bg_color);
386
387 for (j, (label, shortcut)) in items.iter().enumerate() {
388 let iy = my + 4 + (j as u32 * 24);
389 screen.draw_string_vector(mx + 10, iy, label, text_color, 14);
390 if !shortcut.is_empty() {
391 screen.draw_string_vector(
392 mx + mw - 60,
393 iy,
394 shortcut,
395 Color(config.theme.app_fg),
396 12,
397 );
398 }
399 }
400 }
401 }
402
403 if self.panel_mode > 0 {
405 if self.panel_slide < 100 {
406 self.panel_slide += 10;
407 if self.panel_slide > 100 {
408 self.panel_slide = 100;
409 }
410 crate::kernel::window_mgr::get_instance().dirty = true;
411 }
412 } else if self.panel_slide > 0 {
413 self.panel_slide -= 10;
414 if self.panel_slide < 0 {
415 self.panel_slide = 0;
416 }
417 crate::kernel::window_mgr::get_instance().dirty = true;
418 }
419
420 if self.panel_slide > 0 {
421 let panel_w = 300u32;
422 let current_w = (panel_w * self.panel_slide as u32) / 100;
423 let px = win_x + win_w - current_w;
424 let py = win_y + MENU_HEIGHT;
425 let ph = win_h.saturating_sub(MENU_HEIGHT);
426
427 screen.boxfill(px, py, win_x + win_w, py + ph, Color(config.theme.app_bg));
428 screen.boxfill(px.saturating_sub(2), py, px, py + ph, Color(0x80000000));
429
430 if self.panel_slide >= 100 {
431 let title = if self.panel_mode == 1 {
432 "Open File"
433 } else {
434 "Save As"
435 };
436 screen.draw_string_vector(px + 10, py + 10, title, Color(config.theme.app_fg), 18);
437
438 let list_y = py + 40;
439 let list_h = ph.saturating_sub(120);
440 screen.boxfill(
441 px + 10,
442 list_y,
443 px + panel_w - 10,
444 list_y + list_h,
445 Color(config.theme.terminal_bg),
446 );
447 for (i, file) in self.panel_files.iter().take(12).enumerate() {
448 let item_y = list_y + (i as u32) * 22;
449 if Some(i) == self.panel_selected {
450 screen.boxfill(
451 px + 10,
452 item_y,
453 px + panel_w - 10,
454 item_y + 22,
455 Color(config.theme.focus_highlight),
456 );
457 }
458 screen.draw_string_vector(
459 px + 15,
460 item_y + 3,
461 file,
462 Color(config.theme.app_fg),
463 13,
464 );
465 }
466
467 let input_y = py + ph.saturating_sub(70);
468 screen.boxfill(
469 px + 10,
470 input_y,
471 px + panel_w - 10,
472 input_y + 24,
473 Color(config.theme.terminal_bg),
474 );
475 screen.draw_string_vector(
476 px + 15,
477 input_y + 4,
478 &self.panel_input,
479 Color(config.theme.app_fg),
480 14,
481 );
482 let cx = px + 15 + (self.panel_input.len() as u32 * 8);
484 screen.boxfill(
485 cx,
486 input_y + 4,
487 cx + 1,
488 input_y + 18,
489 Color(config.theme.app_fg),
490 );
491
492 let btn_y = py + ph.saturating_sub(35);
493 screen.boxfill(
494 px + 10,
495 btn_y,
496 px + 80,
497 btn_y + 24,
498 Color(config.theme.focus_highlight),
499 );
500 screen.draw_string_vector(px + 20, btn_y + 4, "Cancel", text_color, 14);
501 screen.boxfill(
502 px + 100,
503 btn_y,
504 px + 180,
505 btn_y + 24,
506 Color(config.theme.button_active_bg),
507 );
508 let action_label = if self.panel_mode == 1 { "Open" } else { "Save" };
509 screen.draw_string_vector(px + 120, btn_y + 4, action_label, text_color, 14);
510 }
511 }
512
513 if self.output_panel_open {
515 let py = win_y + win_h - self.output_panel_height;
516 let ph = self.output_panel_height;
517 let px = win_x;
518 let pw = win_w;
519
520 screen.boxfill(px, py, px + pw - 1, py + ph - 1, Color(config.theme.ui_overlay_bg));
522 screen.boxfill(
524 px,
525 py,
526 px + pw - 1,
527 py + 1,
528 Color(config.theme.active_window_border),
529 );
530
531 let title_bg = Color(config.theme.active_window_border);
533 let title_h = 20u32;
534 screen.boxfill(px, py + 2, px + pw - 1, py + title_h - 1, title_bg);
535 screen.draw_string_vector(
536 px + 10,
537 py + 4,
538 "Execution Output",
539 Color(config.theme.window_title_fg),
540 12,
541 );
542
543 let close_btn_w = 20u32;
545 let bx1 = px + pw - close_btn_w - 5;
546 let bx2 = px + pw - 5;
547 screen.boxfill(bx1, py + 3, bx2, py + title_h - 3, Color(config.theme.win_btn_close));
548 screen.draw_string_vector(bx1 + 6, py + 4, "X", Color(config.theme.browser_fg), 11);
549
550 let text_y_start = py + title_h + 5;
552 let text_h_limit = ph.saturating_sub(title_h).saturating_sub(10);
553 let line_height = 16u32;
554 let max_display_lines = (text_h_limit / line_height) as usize;
555
556 let lines: Vec<&str> = self.output_text.split('\n').collect();
557 let start_line = if lines.len() > max_display_lines {
558 lines.len() - max_display_lines
559 } else {
560 0
561 };
562
563 for (i, line) in lines[start_line..].iter().enumerate() {
564 let ly = text_y_start + (i as u32 * line_height);
565 let text_color = if line.starts_with("Eval Error:")
566 || line.starts_with("Syntax Error:")
567 || line.starts_with("Error:")
568 {
569 Color(config.theme.error_fg)
570 } else {
571 Color(config.theme.browser_fg)
572 };
573 screen.draw_string_vector(px + 15, ly, line, text_color, 12);
574 }
575 }
576 }
577
578 fn on_mouse(
579 &mut self,
580 local_x: i32,
581 local_y: i32,
582 btn_left: bool,
583 btn_right: bool,
584 wheel: i32,
585 ) {
586 let just_pressed = btn_left && !self.last_btn;
587 self.last_btn = btn_left;
588
589 if self.panel_mode > 0 && self.panel_slide >= 100 {
591 if just_pressed {
592 let panel_w = 300i32;
593 let px = self.last_win_w as i32 - panel_w;
594 if local_x >= px {
595 let py = MENU_HEIGHT as i32;
596 let ph = self.last_win_h as i32 - MENU_HEIGHT as i32;
597 let list_y = py + 40;
598 let list_h = ph - 120;
599
600 if local_y >= list_y && local_y <= list_y + list_h {
601 let idx = (local_y - list_y) / 22;
602 if idx >= 0 && (idx as usize) < self.panel_files.len() {
603 self.panel_selected = Some(idx as usize);
604 self.panel_input = self.panel_files[idx as usize].clone();
605 crate::kernel::window_mgr::get_instance().dirty = true;
606 }
607 }
608
609 let btn_y = py + ph - 35;
610 if local_y >= btn_y && local_y <= btn_y + 24 {
611 if local_x >= px + 10 && local_x <= px + 80 {
612 self.panel_mode = 0;
613 } else if local_x >= px + 100
614 && local_x <= px + 180
615 && !self.panel_input.is_empty()
616 {
617 if let Some(tab) = self.tabs_data.get_mut(self.active_idx) {
618 tab.filename = self.panel_input.clone();
619 }
620 if self.panel_mode == 1 {
621 self.load_file_into_current();
622 } else {
623 self.save_current();
624 }
625 self.panel_mode = 0;
626 }
627 }
628 } else {
629 self.panel_mode = 0;
630 }
631 }
632 return;
633 }
634
635 if self.output_panel_open {
637 let py = self.last_win_h as i32 - self.output_panel_height as i32;
638 let close_btn_w = 20i32;
639 let bx1 = self.last_win_w as i32 - close_btn_w - 5;
640 let bx2 = self.last_win_w as i32 - 5;
641 if just_pressed
642 && local_y >= py + 3
643 && local_y <= py + 17
644 && local_x >= bx1
645 && local_x <= bx2
646 {
647 self.output_panel_open = false;
648 crate::kernel::window_mgr::get_instance().dirty = true;
649 return;
650 }
651 }
652
653 if just_pressed {
654 if let Some(menu_idx) = self.open_menu {
656 let mx = 10 + (menu_idx as i32 * 55) - 5;
657 let my = MENU_HEIGHT as i32;
658 let mw = 200;
659 let items_len: i32 = match menu_idx {
660 0 => 4,
661 1 => 4,
662 2 => 0,
663 3 => 1,
664 4 => 3,
665 5 => 1,
666 _ => 0,
667 };
668 let mh = items_len * 24 + 4;
669
670 if items_len > 0
671 && local_x >= mx
672 && local_x <= mx + mw
673 && local_y >= my
674 && local_y <= my + mh
675 {
676 let item_idx = (local_y - my - 2) / 24;
677 if menu_idx == 0 {
678 match item_idx {
680 0 => self.new_tab_action(), 1 => self.open_panel(1), 2 => self.save_current(), 3 => self.open_panel(2), _ => {}
685 }
686 } else if menu_idx == 3 {
687 if item_idx == 0 {
689 self.engine.editor_wrap = !self.engine.editor_wrap;
690 self.engine.dirty = true;
691 let html = self.engine.last_html.clone();
692 self.engine.parse_and_layout(
693 &html,
694 None,
695 0,
696 self.engine.win_w,
697 self.engine.win_h,
698 );
699 }
700 } else if menu_idx == 4 {
701 if item_idx == 2 {
703 self.output_panel_open = !self.output_panel_open;
704 crate::kernel::window_mgr::get_instance().dirty = true;
705 }
706 }
707 }
708 self.open_menu = None;
709 return;
710 }
711
712 if local_y < MENU_HEIGHT as i32 {
714 if (5..60).contains(&local_x) {
715 self.open_menu = Some(0);
716 } else if (60..115).contains(&local_x) {
717 self.open_menu = Some(1);
718 } else if (115..170).contains(&local_x) {
719 self.open_menu = None;
720 self.run_script();
721 } else if (170..225).contains(&local_x) {
722 self.open_menu = Some(3);
723 } else if (225..280).contains(&local_x) {
724 self.open_menu = Some(4);
725 } else if (280..335).contains(&local_x) {
726 self.open_menu = Some(5);
727 }
728 return;
729 }
730 }
731
732 let editor_h = if self.output_panel_open {
734 self.last_win_h
735 .saturating_sub(MENU_HEIGHT)
736 .saturating_sub(self.output_panel_height)
737 } else {
738 self.last_win_h.saturating_sub(MENU_HEIGHT)
739 };
740 if self.open_menu.is_none()
741 && local_y >= MENU_HEIGHT as i32
742 && local_y < (MENU_HEIGHT + editor_h) as i32
743 {
744 self.engine.on_mouse(
745 local_x,
746 local_y - MENU_HEIGHT as i32,
747 btn_left,
748 btn_right,
749 wheel,
750 0,
751 );
752 }
753 }
754
755 fn on_key(&mut self, keycode: u8, pressed: bool, ascii: Option<char>) {
756 if keycode == 0x1D {
758 self.ctrl_held = pressed;
759 return;
760 }
761
762 if keycode == 0x38 {
764 self.alt_held = pressed;
765 return;
766 }
767
768 if pressed && self.alt_held && keycode == 0x19 {
770 self.output_panel_open = !self.output_panel_open;
771 crate::kernel::window_mgr::get_instance().dirty = true;
772 return;
773 }
774
775 if self.panel_mode > 0 {
776 if !pressed {
777 return;
778 }
779 match keycode {
780 0x01 => {
781 self.panel_mode = 0;
782 } 0x1C => {
784 if !self.panel_input.is_empty() {
786 if let Some(tab) = self.tabs_data.get_mut(self.active_idx) {
787 tab.filename = self.panel_input.clone();
788 }
789 if self.panel_mode == 1 {
790 self.load_file_into_current();
791 } else {
792 self.save_current();
793 }
794 self.panel_mode = 0;
795 }
796 }
797 0x0E => {
798 self.panel_input.pop();
799 } _ => {
801 if let Some(ch) = ascii {
802 if ch.is_ascii_alphanumeric()
803 || ch == '.'
804 || ch == '_'
805 || ch == '-'
806 || ch == '/'
807 {
808 self.panel_input.push(ch);
809 }
810 }
811 }
812 }
813 crate::kernel::window_mgr::get_instance().dirty = true;
814 return;
815 }
816
817 if self.open_menu.is_some() {
818 if pressed && keycode == 0x01 {
819 self.open_menu = None;
820 }
821 return;
822 }
823
824 self.engine.on_key(keycode, pressed, ascii);
828 }
829
830 fn open_file_dialog(&mut self) {
831 self.open_panel(1);
832 }
833
834 fn save_file(&mut self) {
835 if self
836 .tabs_data
837 .get(self.active_idx)
838 .map(|t| t.filename == "Untitled")
839 .unwrap_or(true)
840 {
841 self.open_panel(2); } else {
843 self.save_current();
844 }
845 }
846
847 fn tabs(&self) -> Vec<String> {
848 self.tabs_data.iter().map(|t| t.title()).collect()
849 }
850
851 fn active_tab(&self) -> usize {
852 self.active_idx
853 }
854
855 fn switch_tab(&mut self, idx: usize) {
856 if idx >= self.tabs_data.len() || idx == self.active_idx {
857 return;
858 }
859 self.flush_current_tab();
860 self.active_idx = idx;
861 self.load_tab_into_engine(idx);
862 }
863
864 fn add_tab(&mut self) {
865 self.new_tab_action();
866 }
867
868 fn close_tab(&mut self, idx: usize) -> bool {
869 if self.tabs_data.is_empty() {
870 return true;
871 }
872 self.flush_current_tab();
873 self.tabs_data.remove(idx);
874 if self.tabs_data.is_empty() {
875 self.engine.active = false;
876 return true;
877 }
878 self.active_idx = self.active_idx.min(self.tabs_data.len() - 1);
879 let new_idx = self.active_idx;
880 self.load_tab_into_engine(new_idx);
881 crate::kernel::window_mgr::get_instance().dirty = true;
882 false
883 }
884
885 fn get_dom_value(&self, id: &str) -> Option<String> {
886 if id == "filename" {
887 self.tabs_data
888 .get(self.active_idx)
889 .map(|t| t.filename.clone())
890 } else {
891 self.engine.form_values.get(id).cloned()
892 }
893 }
894
895 fn set_dom_value(&mut self, id: &str, val: &str) {
896 if id == "filename" {
897 if let Some(tab) = self.tabs_data.get_mut(self.active_idx) {
898 tab.filename = String::from(val);
899 }
900 self.engine
901 .form_values
902 .insert(String::from("filename"), String::from(val));
903 } else {
904 self.engine
905 .form_values
906 .insert(String::from(id), String::from(val));
907 }
908 self.engine.dirty = true;
909 }
910}