1use crate::{
2 makepad_code_editor::code_editor::*,
3 makepad_code_editor::selection::Affinity,
4 makepad_code_editor::session::SelectionMode,
5 makepad_code_editor::history::NewGroup,
6 makepad_widgets::*,
7 makepad_micro_serde::*,
8 makepad_widgets::file_tree::*,
9 makepad_platform::os::cx_stdin::*,
10 makepad_file_protocol::SearchItem,
11 makepad_file_server::FileSystemRoots,
12 file_system::file_system::*,
13 studio_editor::*,
14 run_view::*,
15 snapshot::*,
16 studio_file_tree::*,
17 makepad_platform::studio::{JumpToFile,EditFile, SelectInFile, PatchFile, SwapSelection},
18 log_list::*,
19 makepad_code_editor::{CodeSession,text::{Position}},
20 ai_chat::ai_chat_manager::AiChatManager,
21 build_manager::{
22 build_protocol::BuildProcess,
23 build_manager::{
24 BuildManager,
25 BuildManagerAction
26 },
27 }
28};
29use std::fs::File;
30use std::io::Write;
31use std::env;
32
33live_design!{
34 use crate::app_ui::*;
35 use link::widgets::*;
36
37 App = {{App}} {
38 ui: <Root>{
39 <AppUI> {}
40 }
41 }
42}
43
44#[derive(Live, LiveHook)]
45pub struct App {
46 #[live] pub ui: WidgetRef,
47 #[rust] pub data: AppData,
48}
49
50impl LiveRegister for App{
51 fn live_register(cx: &mut Cx) {
52 crate::live_design(cx);
53 cx.start_stdin_service();
55 }
56}
57
58app_main!(App);
59
60impl App {
61
62 pub fn open_code_file_by_path(&mut self, cx: &mut Cx, path: &str) {
63 if let Some(file_id) = self.data.file_system.path_to_file_node_id(&path) {
64 let dock = self.ui.dock(id!(dock));
65 let tab_id = dock.unique_id(file_id.0);
66 self.data.file_system.request_open_file(tab_id, file_id);
67 let (tab_bar, pos) = dock.find_tab_bar_of_tab(live_id!(edit_first)).unwrap();
68 let template = FileSystem::get_editor_template_from_path(path);
70 dock.create_and_select_tab(cx, tab_bar, tab_id, template, "".to_string(), live_id!(CloseableTab), Some(pos));
71 self.data.file_system.ensure_unique_tab_names(cx, &dock)
72 }
73 }
74
75 pub fn load_state(&mut self, cx:&mut Cx, slot:usize){
76
77 if let Ok(contents) = std::fs::read_to_string(format!("makepad_state{}.ron", slot)) {
78 match AppStateRon::deserialize_ron(&contents) {
79 Ok(state)=>{
80 self.data.build_manager.stop_all_active_builds(cx);
82 let dock = self.ui.dock(id!(dock));
84 if let Some(mut dock) = dock.borrow_mut() {
85 dock.load_state(cx, state.dock_items);
86
87 self.data.file_system.tab_id_to_file_node_id = state.tab_id_to_file_node_id.clone();
88 for (tab_id, file_node_id) in state.tab_id_to_file_node_id.iter() {
89 self.data.file_system.request_open_file(*tab_id, *file_node_id);
90 }
91 for process in state.processes{
94 if let Some(binary_id) = self.data.build_manager.binary_name_to_id(&process.binary){
95 self.data.build_manager.start_active_build(cx, binary_id, process.target);
96 }
97 }
98 };
99 self.ui.clear_query_cache();
100 return;
101 }
106 Err(e)=>{
107 println!("ERR {:?}",e);
108 }
109 }
110 }
111 }
112
113 fn save_state(&self, slot:usize){
114 let dock = self.ui.dock(id!(dock));
115 let dock_items = dock.clone_state().unwrap();
116
117 let mut processes = Vec::new();
119 for build in self.data.build_manager.active.builds.values(){
120 processes.push(build.process.clone());
121 }
122
123 let state = AppStateRon{
126 dock_items,
127 processes,
128 tab_id_to_file_node_id: self.data.file_system.tab_id_to_file_node_id.clone()
129 };
130 let saved = state.serialize_ron();
131 let mut f = File::create(format!("makepad_state{}.ron", slot)).expect("Unable to create file");
132 f.write_all(saved.as_bytes()).expect("Unable to write data");
133 }
134}
135
136#[derive(Default)]
137pub struct AppData{
138 pub build_manager: BuildManager,
139 pub file_system: FileSystem,
140 pub ai_chat_manager: AiChatManager,
141}
142
143#[derive(DefaultNone, Debug, Clone)]
146pub enum AppAction{
147 JumpTo(JumpToFile),
148 SelectInFile(SelectInFile),
149 SwapSelection(SwapSelection),
150 RedrawLog,
151 RedrawProfiler,
152 RedrawFile(LiveId),
153 FocusDesign(LiveId),
154 EditFile(EditFile),
155 PatchFile(PatchFile),
156 StartRecompile,
157 ReloadFileTree,
158 RecompileStarted,
159 RedrawSnapshots,
160 ClearLog,
161 SetSnapshotMessage{message:String},
162 SendAiChatToBackend{chat_id:LiveId, history_slot:usize},
163 CancelAiGeneration{chat_id:LiveId},
164 SaveAiChat{chat_id:LiveId},
165 RedrawAiChat{chat_id:LiveId},
166 RunAiChat{chat_id:LiveId, history_slot:usize, item_id:usize},
167 DestroyRunViews{run_view_id:LiveId},
168 None
169}
170
171impl MatchEvent for App{
172 fn handle_startup(&mut self, cx:&mut Cx){
173 let mut roots = Vec::new();
174 let current_dir = env::current_dir().unwrap();
175
176 for arg in std::env::args(){
177 if let Some(prefix) = arg.strip_prefix("--root="){
178 for root in prefix.split(","){
179 let mut parts = root.splitn(2,":");
180 let base = parts.next().expect("name:path expected");
181 let path = parts.next().expect("name:path expected");
182 let dir = current_dir.clone();
183 roots.push((base.to_string(), dir.join(path).canonicalize().unwrap()));
184 }
185 }
186 else{
187 }
188 }
189 if roots.is_empty(){
190 let dir1 = current_dir.join("./").canonicalize().unwrap();
191 roots.push(("makepad".to_string(),dir1));
193 }
195 let roots = FileSystemRoots{roots};
196 self.data.file_system.init(cx, roots.clone());
197 self.data.build_manager.init(cx, roots);
198
199 self.data.build_manager.start_http_server();
201 }
203
204 fn handle_action(&mut self, cx:&mut Cx, action:&Action){
205 let dock = self.ui.dock(id!(dock));
206 let file_tree = self.ui.studio_file_tree(id!(file_tree));
207 let log_list = self.ui.log_list(id!(log_list));
208 let run_list = self.ui.view(id!(run_list_tab));
209 let profiler = self.ui.view(id!(profiler));
210 let search = self.ui.view(id!(search));
211 let snapshot = self.ui.snapshot(id!(snapshot_tab));
212
213 match action.cast(){
214 AppAction::SwapSelection(ss)=>{
215 let s1_start = Position{line_index: ss.s1_line_start as usize, byte_index:ss.s1_column_start as usize};
216 let s1_end = Position{line_index: ss.s1_line_end as usize, byte_index:ss.s1_column_end as usize};
217 let s2_start = Position{line_index: ss.s2_line_start as usize, byte_index:ss.s2_column_start as usize};
218 let s2_end = Position{line_index: ss.s2_line_end as usize, byte_index:ss.s2_column_end as usize};
219 if let Some(s1_file_id) = self.data.file_system.path_to_file_node_id(&ss.s1_file_name) {
220 if let Some(s2_file_id) = self.data.file_system.path_to_file_node_id(&ss.s1_file_name) {
221 if let Some(OpenDocument::Code(doc1)) = self.data.file_system.open_documents.get(&s1_file_id) {
222 if let Some(OpenDocument::Code(doc2)) = self.data.file_system.open_documents.get(&s2_file_id) {
223 let mut session1 = CodeSession::new(doc1.clone());
225 let mut session2 = CodeSession::new(doc2.clone());
226
227 session1.set_selection(session1.clamp_position(s1_start), Affinity::After, SelectionMode::Simple, NewGroup::Yes);
229 session1.move_to(session1.clamp_position(s1_end), Affinity::Before, NewGroup::Yes);
230
231 session2.set_selection(session2.clamp_position(s2_start), Affinity::After, SelectionMode::Simple, NewGroup::Yes);
232 session2.move_to(session2.clamp_position(s2_end), Affinity::Before, NewGroup::Yes);
233
234 let text1 = session1.copy();
236 let text2 = session2.copy();
237
238 session1.paste(text2.into());
240 session1.handle_changes();
241 session2.handle_changes();
242 self.data.file_system.handle_sessions();
243
244 session2.paste(text1.into());
245 session1.handle_changes();
246 session2.handle_changes();
247 self.data.file_system.handle_sessions();
248
249 cx.action(AppAction::RedrawFile(s1_file_id));
251 cx.action(AppAction::RedrawFile(s2_file_id));
252 self.data.file_system.request_save_file_for_file_node_id(s1_file_id, false);
254 }
256 }
257 }
258 }
259 }
260 AppAction::SelectInFile(sf)=>{
261 let start = Position{line_index: sf.line_start as usize, byte_index:sf.column_start as usize};
262 let end = Position{line_index: sf.line_end as usize, byte_index:sf.column_end as usize};
263 if let Some(file_id) = self.data.file_system.path_to_file_node_id(&sf.file_name) {
264 if let Some(tab_id) = self.data.file_system.file_node_id_to_tab_id(file_id){
265 dock.select_tab(cx, tab_id);
266 if let Some(mut editor) = dock.item(tab_id).studio_code_editor(id!(editor)).borrow_mut() {
268 if let Some(EditSession::Code(session)) = self.data.file_system.get_session_mut(tab_id) {
269 editor.editor.set_selection_and_scroll(cx, start, end, session);
270 editor.editor.set_key_focus(cx);
271 }
272 }
273 }
274 }
275 }
276 AppAction::JumpTo(jt)=>{
277 let pos = Position{line_index: jt.line as usize, byte_index:jt.column as usize};
278 if let Some(file_id) = self.data.file_system.path_to_file_node_id(&jt.file_name) {
279 if let Some(tab_id) = self.data.file_system.file_node_id_to_tab_id(file_id){
280 dock.select_tab(cx, tab_id);
281 if let Some(mut editor) = dock.item(tab_id).studio_code_editor(id!(editor)).borrow_mut() {
283 if let Some(EditSession::Code(session)) = self.data.file_system.get_session_mut(tab_id) {
284 editor.editor.set_cursor_and_scroll(cx, pos, session);
285 editor.editor.set_key_focus(cx);
286 }
287 }
288 }
289 else{
290 let tab_id = dock.unique_id(file_id.0);
292 self.data.file_system.request_open_file(tab_id, file_id);
293 let (tab_bar, pos) = dock.find_tab_bar_of_tab(live_id!(edit_first)).unwrap();
295 let template = FileSystem::get_editor_template_from_path(&jt.file_name);
296 dock.create_and_select_tab(cx, tab_bar, tab_id, template, "".to_string(), live_id!(CloseableTab), Some(pos));
297 self.data.file_system.ensure_unique_tab_names(cx, &dock)
299 }
300 }
301 }
302 AppAction::PatchFile(_ef)=>{
303 panic!()
304 }
333 AppAction::EditFile(ef)=>{
334 let start = Position{line_index: ef.line_start as usize, byte_index:ef.column_start as usize};
335 let end = Position{line_index: ef.line_end as usize, byte_index:ef.column_end as usize};
336 if let Some(file_id) = self.data.file_system.path_to_file_node_id(&ef.file_name) {
337 if let Some(tab_id) = self.data.file_system.file_node_id_to_tab_id(file_id){
338 dock.select_tab(cx, tab_id);
339 if let Some(mut editor) = dock.item(tab_id).studio_code_editor(id!(editor)).borrow_mut() {
341 if let Some(EditSession::Code(session)) = self.data.file_system.get_session_mut(tab_id) {
342 session.set_selection(
344 start,
345 Affinity::After,
346 SelectionMode::Simple,
347 NewGroup::Yes
348 );
349 session.move_to(
350 end,
351 Affinity::Before,
352 NewGroup::Yes
353 );
354 session.paste(ef.replace.into());
355 }
357 self.data.file_system.handle_sessions();
358 editor.redraw(cx);
359 self.data.file_system.request_save_file_for_file_node_id(file_id, false)
360 }
361 }
362 }
363 }
364 AppAction::RedrawFile(file_id)=>{
365 self.data.file_system.redraw_view_by_file_id(cx, file_id, &dock);
366 }
367 AppAction::ClearLog=>{
368 self.data.build_manager.clear_log(cx, &dock, &mut self.data.file_system);
369 log_list.reset_scroll(cx);
370 log_list.redraw(cx);
371 profiler.redraw(cx);
372 }
373 AppAction::ReloadFileTree=>{
374 self.data.file_system.file_client.load_file_tree();
375 }
376 AppAction::RedrawProfiler=>{
377 profiler.redraw(cx);
378 }
379 AppAction::RedrawLog=>{
380 log_list.redraw(cx);
381 }
382 AppAction::StartRecompile=>{
383 self.data.build_manager.start_recompile(cx);
384 }
385 AppAction::FocusDesign(build_id)=>{
386 let mut id = None;
387 if let Some(mut dock) = dock.borrow_mut() {
388 for (tab_id, (_, item)) in dock.items().iter() {
389 if let Some(run_view) = item.as_run_view().borrow_mut() {
390 if run_view.build_id == Some(build_id) {
391 if let WindowKindId::Design = run_view.kind_id{
392 id = Some(*tab_id);
394 break;
395 }
396 }
397 }
398 }
399 }
400 if let Some(id) = id{
401 dock.select_tab(cx, id);
402 }
403 }
404 AppAction::RecompileStarted=>{
405 if let Some(mut dock) = dock.borrow_mut() {
406 for (_id, (_, item)) in dock.items().iter() {
407 if let Some(mut run_view) = item.as_run_view().borrow_mut() {
408 run_view.recompile_started(cx);
409 run_view.resend_framebuffer(cx);
410 }
411 }
412 }
413 }
414 AppAction::None=>(),
415 AppAction::SendAiChatToBackend{chat_id, history_slot}=>{
416 self.data.ai_chat_manager.send_chat_to_backend(cx, chat_id, history_slot, &mut self.data.file_system);
417 }
418 AppAction::CancelAiGeneration{chat_id}=>{
419 self.data.ai_chat_manager.cancel_chat_generation(cx, &self.ui, chat_id, &mut self.data.file_system);
420 }
421 AppAction::SaveAiChat{chat_id}=>{
422 self.data.file_system.request_save_file_for_file_node_id(chat_id, false);
423 }
424 AppAction::RedrawAiChat{chat_id}=>{
425 self.data.ai_chat_manager.redraw_ai_chat_by_id(cx, chat_id,&self.ui, &mut self.data.file_system);
426 }
427 AppAction::RunAiChat{chat_id, history_slot, item_id}=>{
428 self.data.ai_chat_manager.run_ai_chat(cx, chat_id, history_slot, item_id, &mut self.data.file_system);
429 }
430 AppAction::DestroyRunViews{run_view_id} => {
431 dock.close_tab(cx, run_view_id);
432 dock.close_tab(cx, run_view_id.add(1));
433 dock.close_tab(cx, run_view_id.add(2));
434 dock.redraw(cx);
435 log_list.redraw(cx);
436 }
437 AppAction::SetSnapshotMessage{message}=>{
438 snapshot.set_message(cx, message);
439 }
440 AppAction::RedrawSnapshots=>{
441 snapshot.redraw(cx);
442 }
443 }
444
445 match action.cast(){
446 BuildManagerAction::StdinToHost {build_id, msg} => {
447 match msg{
448 StdinToHost::CreateWindow{window_id, kind_id}=>{
449 let panel_id = build_id.add(window_id as u64);
450 if let Some(name) = self.data.build_manager.process_name(build_id){
451
452 let (tab_bar_id, pos) = if kind_id == 0{
453 dock.find_tab_bar_of_tab(live_id!(run_first)).unwrap()
454 }
455 else if kind_id == 1{
456 dock.find_tab_bar_of_tab(live_id!(design_first)).unwrap()
457 }
458 else{
459 dock.find_tab_bar_of_tab(live_id!(outline_first)).unwrap()
460 };
461
462
463 let item = dock.create_and_select_tab(cx, tab_bar_id, panel_id, live_id!(RunView), name.clone(), live_id!(CloseableTab), Some(pos)).unwrap();
466
467 if let Some(mut item) = item.as_run_view().borrow_mut(){
468 item.window_id = window_id;
469 item.build_id = Some(build_id);
470 item.kind_id = WindowKindId::from_usize(kind_id);
471 }
472 else{
473 println!("WHIT");
474 }
475
476 dock.redraw(cx);
477 log_list.redraw(cx);
478 }
479 }
480 StdinToHost::SetCursor(cursor) => {
481 cx.set_cursor(cursor)
482 }
483 StdinToHost::ReadyToStart => {
484 if let Some(mut dock) = dock.borrow_mut() {
486 for (_, (_, item)) in dock.items().iter() {
487 if let Some(mut run_view) = item.as_run_view().borrow_mut() {
488 if run_view.build_id == Some(build_id){
489 run_view.ready_to_start(cx);
490 }
491 }
492 }
493 }
494 }
495 StdinToHost::DrawCompleteAndFlip(presentable_draw) => {
496 if let Some(mut dock) = dock.borrow_mut() {
497 for (_, (_, item)) in dock.items().iter() {
498 if let Some(mut run_view) = item.as_run_view().borrow_mut() {
499 if run_view.build_id == Some(build_id) && run_view.window_id == presentable_draw.window_id{
500 run_view.draw_complete_and_flip(cx, &presentable_draw, &mut self.data.build_manager);
501 }
502 }
503 }
504 }
505 }
506 }
507 }
508 BuildManagerAction::None=>()
509 }
510
511 match action.cast(){
512 FileSystemAction::TreeLoaded => {
513 file_tree.redraw(cx);
514 self.load_state(cx, 0);
515 self.data.ai_chat_manager.init(&mut self.data.file_system);
516 }
517 FileSystemAction::SnapshotImageLoaded => {
518 snapshot.redraw(cx);
519 }
520 FileSystemAction::RecompileNeeded => {
521 self.data.build_manager.start_recompile_timer(cx);
522 }
523 FileSystemAction::LiveReloadNeeded(live_file_change) => {
524 self.data.build_manager.live_reload_needed(live_file_change);
525 self.data.build_manager.clear_log(cx, &dock, &mut self.data.file_system);
526 log_list.redraw(cx);
527 }
528 FileSystemAction::FileChangedOnDisk(_res)=>{
529
530 }
531 FileSystemAction::SearchResults=>{
532 search.redraw(cx);
533 }
534 FileSystemAction::None=>()
535 }
536
537 if let Some(action) = action.as_widget_action(){
538 match action.cast(){
539 CodeEditorAction::UnhandledKeyDown(ke) if ke.key_code == KeyCode::F12 && !ke.modifiers.shift =>{
540 if let Some(word) = self.data.file_system.get_word_under_cursor_for_session(action.path.from_end(1)){
541 dock.select_tab(cx, live_id!(search));
542 let set = vec![SearchItem{
543 needle:word.clone(),
544 prefixes: Some(vec![
545 format!("struct "),
546 format!("enum "),
547 format!("fn "),
548 format!("type "),
549 format!("trait "),
550 format!("pub ")
551 ]),
552 pre_word_boundary:true,
553 post_word_boundary:true
554 }];
555 search.text_input(id!(search_input)).set_text(cx, &word);
556 self.data.file_system.search_string(cx, set);
557 }
558 },
559 CodeEditorAction::UnhandledKeyDown(ke) if ke.key_code == KeyCode::F12 && ke.modifiers.shift =>{
560 if let Some(word) = self.data.file_system.get_word_under_cursor_for_session(action.path.from_end(1)){
561 dock.select_tab(cx, live_id!(search));
562 let set = vec![SearchItem{
563 needle:word.clone(),
564 prefixes: None,
565 pre_word_boundary:ke.modifiers.control,
566 post_word_boundary:ke.modifiers.control
567 }];
568 search.text_input(id!(search_input)).set_text(cx, &word);
569 self.data.file_system.search_string(cx, set);
570 }
571 },
572 CodeEditorAction::TextDidChange => {
573 self.data.file_system.request_save_file_for_tab_id(action.path.from_end(1), false)
575 }
576 CodeEditorAction::UnhandledKeyDown(_)=>{}
577 CodeEditorAction::None=>{}
578 }
579
580 match action.cast(){
581 DockAction::TabCloseWasPressed(tab_id)=>{
582 dock.close_tab(cx, tab_id);
583 if self.data.build_manager.handle_tab_close(tab_id) {
584 log_list.redraw(cx);
585 run_list.redraw(cx);
586 }
587 self.data.file_system.remove_tab(tab_id);
588 self.data.file_system.ensure_unique_tab_names(cx, &dock);
589 }
590 DockAction::ShouldTabStartDrag(tab_id)=>{
591 dock.tab_start_drag(cx, tab_id, DragItem::FilePath {
592 path: "".to_string(), internal_id: Some(tab_id)
594 });
595 }
596 DockAction::Drag(drag_event)=>{
597 if drag_event.items.len() == 1 {
598 if drag_event.modifiers.logo {
599 dock.accept_drag(cx, drag_event, DragResponse::Copy);
600 }
601 else {
602 dock.accept_drag(cx, drag_event, DragResponse::Move);
603 }
604 }
605 }
606 DockAction::Drop(drop_event)=>{
607 if let DragItem::FilePath {path, internal_id} = &drop_event.items[0] {
608 if let Some(internal_id) = internal_id { if drop_event.modifiers.logo {
610 let tab_id = dock.unique_id(internal_id.0);
611 dock.drop_clone(cx, drop_event.abs, *internal_id, tab_id, live_id!(CloseableTab));
612 }
613 else {
614 dock.drop_move(cx, drop_event.abs, *internal_id);
615 }
616 self.data.file_system.ensure_unique_tab_names(cx, &dock);
617 }
618 else { if let Some(file_id) = self.data.file_system.path_to_file_node_id(&path) {
620 let tab_id = dock.unique_id(file_id.0);
621 self.data.file_system.request_open_file(tab_id, file_id);
622 let template = FileSystem::get_editor_template_from_path(&path);
623 dock.drop_create(cx, drop_event.abs, tab_id, template, "".to_string(), live_id!(CloseableTab));
624 self.data.file_system.ensure_unique_tab_names(cx, &dock)
625 }
626 }
627 }
628 },
629 _=>()
630 }
631 }
632 }
633
634 fn handle_key_down(&mut self, cx: &mut Cx, event: &KeyEvent){
635 let KeyEvent {
636 key_code,
637 modifiers: KeyModifiers {logo, control, ..},
638 ..
639 } = event;
640 if *control || *logo {
641 if let KeyCode::Backtick = key_code {
642 cx.action(AppAction::ClearLog);
643 cx.action(AppAction::RecompileStarted);
644 cx.action(AppAction::StartRecompile);
645 }
646 else if let KeyCode::KeyK = key_code {
647 cx.action(AppAction::ClearLog);
648 }
649 else if let KeyCode::KeyR = key_code{
650 cx.action(AppAction::ReloadFileTree);
651 }
652 }
653 }
654
655 fn handle_actions(&mut self, cx: &mut Cx, actions:&Actions){
656 let file_tree = self.ui.file_tree(id!(file_tree));
657 let dock = self.ui.dock(id!(dock));
658 for action in actions{
659 self.handle_action(cx, action);
660 }
661 if let Some(file_id) = file_tree.should_file_start_drag(&actions) {
662 let path = self.data.file_system.file_node_path(file_id);
663 file_tree.file_start_drag(cx, file_id, DragItem::FilePath {
664 path,
665 internal_id: None
666 });
667 }
668
669 for (i,id) in [*id!(preset_1),*id!(preset_2),*id!(preset_3),*id!(preset_4)].iter().enumerate(){
670 if let Some(km) = self.ui.button(id).pressed_modifiers(actions){
671 if km.control{
672 self.save_state(i+1)
673 }
674 else{
675 self.load_state(cx, i+1);
676 cx.redraw_all();
677 }
678 }
679 }
680
681 if let Some(file_id) = file_tree.file_clicked(&actions) {
682 if let Some(tab_id) = self.data.file_system.file_node_id_to_tab_id(file_id) {
684 dock.select_tab(cx, tab_id);
686 } else {
687 let tab_id = dock.unique_id(file_id.0);
688 self.data.file_system.request_open_file(tab_id, file_id);
689 self.data.file_system.request_open_file(tab_id, file_id);
690
691 let path = self.data.file_system.file_node_id_to_path(file_id).unwrap();
693 let tab_after = FileSystem::get_tab_after_from_path(path);
694 let (tab_bar, pos) = dock.find_tab_bar_of_tab(tab_after).unwrap();
695 let template = FileSystem::get_editor_template_from_path(path);
696 dock.create_and_select_tab(cx, tab_bar, tab_id, template, "".to_string(), live_id!(CloseableTab), Some(pos));
697
698 self.data.file_system.ensure_unique_tab_names(cx, &dock)
700 }
701 }
702 }
703
704 fn handle_shutdown(&mut self, _cx:&mut Cx){
705 self.data.build_manager.clear_active_builds();
706 }
707}
708
709impl AppMain for App {
710
711 fn handle_event(&mut self, cx: &mut Cx, event: &Event) {
712 self.match_event(cx, event);
713 self.ui.handle_event(cx, event, &mut Scope::with_data(&mut self.data));
714
715 self.data.file_system.handle_event(cx, event, &self.ui);
716 self.data.build_manager.handle_event(cx, event, &mut self.data.file_system);
717 self.data.ai_chat_manager.handle_event(cx, event, &mut self.data.file_system);
718 if self.ui.dock(id!(dock)).check_and_clear_need_save(){
719 self.save_state(0);
720 }
721 }
722}
723
724use std::collections::HashMap;
726#[derive(SerRon, DeRon)]
727pub struct AppStateRon{
728 dock_items: HashMap<LiveId, DockItem>,
729 processes: Vec<BuildProcess>,
730 tab_id_to_file_node_id: HashMap<LiveId, LiveId>,
731}