1use crate::{
2 makepad_code_editor::code_editor::*,
3 makepad_platform::*,
4 makepad_draw::*,
5 makepad_widgets::*,
6 makepad_widgets::file_tree::*,
7 file_system::file_system::*,
8 build_manager::{
9 run_view::*,
10 log_list::{
11 LogListAction
12 },
13 run_list::{
14 RunListAction
15 },
16 build_manager::{
17 BuildManager,
18 BuildManagerAction
19 },
20 }
21};
22
23live_design!{
24 import makepad_draw::shader::std::*;
25 import makepad_widgets::base::*;
26 import makepad_widgets::theme_desktop_dark::*;
27 import makepad_code_editor::code_editor::CodeEditor;
28
29 import makepad_studio::build_manager::run_view::RunView;
30 import makepad_studio::build_manager::log_list::LogList;
31 import makepad_studio::build_manager::run_list::RunList;
32
33 Logo = <Button> {
34 draw_icon: {
35 svg_file: dep("crate://self/resources/logo_makepad.svg"),
36 fn get_color(self) -> vec4 {
37 return #xffffff
38 }
39 }
40 icon_walk: {width: 300.0, height: Fit}
41 draw_bg: {
42 fn pixel(self) -> vec4 {
43 let sdf = Sdf2d::viewport(self.pos * self.rect_size);
44 return sdf.result
45 }
46 }
47 margin: {top: 20.0, right: 0.0, bottom: 30.0, left: 0.0}
48 padding: 0.0
49 text: ""
50 }
51
52 App = {{App}} {
53 ui: <Window> {
54 caption_bar = {visible: true, caption_label = {label = {text: "Makepad Studio"}}},
55 window: {inner_size: vec2(1600, 900)},
56 window_menu = {
57 main = Main {items: [app, file, edit, selection, view, run, window, help]}
58
59 app = Sub {name: "Makepad Studio", items: [about, line, settings, line, quit]}
60 about = Item {name: "About Makepad Studio", enabled: false}
61 settings = Item {name: "Settings", enabled: false}
62 quit = Item {name: "Quit Makepad Studio", key: KeyQ}
63
64 file = Sub {name: "File", items: [new_file, new_window, line, save_as, line, rename, line, close_editor, close_window]}
65 new_file = Item {name: "New File", enabled: false, shift: true, key: KeyN}
66 new_window = Item {name: "New Window", enabled: false, shift: true, key: KeyN}
67 save_as = Item {name: "Save As", enabled: false}
68 rename = Item {name: "Rename", enabled: false}
69 close_editor = Item {name: "Close Editor", enabled: false}
70 close_window = Item {name: "Close Window", enabled: false}
71
72 edit = Sub {name: "Edit", items: [undo, redo, line, cut, copy, paste, line, find, replace, line, find_in_files, replace_in_files]}
73 undo = Item {name: "Undo", enabled: false}
74 redo = Item {name: "Redo", enabled: false}
75 cut = Item {name: "Cut", enabled: false}
76 copy = Item {name: "Copy", enabled: false}
77 paste = Item {name: "Paste", enabled: false}
78 find = Item {name: "Find", enabled: false}
79 replace = Item {name: "Replace", enabled: false}
80 find_in_files = Item {name: "Find in Files", enabled: false}
81 replace_in_files = Item {name: "Replace in Files", enabled: false}
82
83 selection = Sub {name: "Selection", items: [select_all]}
84 select_all = Item {name: "Select All", enabled: false}
85
86 view = Sub {name: "View", items: [select_all]}
87 zoom_in = Item {name: "Zoom In", enabled: false}
88 zoom_out = Item {name: "Zoom Out", enabled: false}
89 select_all = Item {name: "Enter Full Screen", enabled: false}
90
91 run = Sub {name: "Run", items: [run_program]}
92 run_program = Item {name: "Run Program", enabled: false}
93
94 window = Sub {name: "Window", items: [minimize, zoom, line, all_to_front]}
95 minimize = Item {name: "Minimize", enabled: false}
96 zoom = Item {name: "Zoom", enabled: false}
97 all_to_front = Item {name: "Bring All to Front", enabled: false}
98
99 help = Sub {name: "Help", items: [about]}
100
101 line = Line,
102 }
103 body = {dock = <Dock> {
104 height: Fill,
105 width: Fill
106
107 root = Splitter {
108 axis: Horizontal,
109 align: FromA(230.0),
110 a: file_tree_tabs,
111 b: split1
112 }
113
114 split1 = Splitter {
115 axis: Vertical,
116 align: FromB(200.0),
117 a: split2,
118 b: log_tabs
119 }
120
121 split2 = Splitter {
122 axis: Horizontal,
123 align: Weighted(0.5),
124 a: edit_tabs,
125 b: run_tabs
126 }
127
128
129
130 file_tree_tabs = Tabs {
131 tabs: [file_tree, search, run_list],
132 selected: 2
133 }
134
135 edit_tabs = Tabs {
136 tabs: [edit_first],
137 selected: 0
138 }
139
140 log_tabs = Tabs {
141 tabs: [log_list],
142 selected: 0
143 }
144
145 run_tabs = Tabs {
146 tabs: [run_first],
147 selected: 0
148 }
149
150 file_tree = Tab {
151 name: "Explore",
152 closable: false,
153 kind: FileTree
154 }
155
156 search = Tab {
157 name: "Search"
158 closable: false,
159 kind: Search
160 }
161
162 run_first = Tab {
163 name: "View"
164 closable: false,
165 kind: RunFirst
166 }
167 edit_first = Tab {
168 name: "Edit"
169 closable: false,
170 kind: EditFirst
171 }
172
173 run_list = Tab {
174 name: "Run"
175 closable: false,
176 kind: RunList
177 }
178
179 file1 = Tab {
180 name: "app.rs",
181 closable: true,
182 kind: CodeEditor
183 }
184
185 log_list = Tab {
186 name: "Log",
187 closable: false,
188 kind: LogList
189 }
190
191 CodeEditor = <CodeEditor> {}
192 EditFirst = <RectView> {
193 draw_bg: {color: #052329}
194 <View> {
195 width: Fill,
196 height: Fill
197 align: {
198 x: 0.5,
199 y: 0.5
200 }
201 flow: Down
202
203 <Logo> {}
204
205 <Label> {
206 text: "Welcome to\nMakepad \n\n欢迎来到\nMakepad"
207 width: Fit,
208 margin: {left: 200}
209 draw_text: {
210 text_style: {
211 font_size: 20.0,
212 height_factor: 1.0,
213 font: {path: dep("crate://makepad-widgets/resources/GoNotoKurrent-Regular.ttf")}
214 },
215 }
216 }
217 }
218
219 }
220 RunFirst = <RectView> {
221 draw_bg: {color: #4}
222 <View> {
223 width: Fill,
224 height: Fill
225 align: {
226 x: 0.5,
227 y: 0.5
228 }
229 flow: Down
230 <Logo> {
231 draw_icon: {
232 fn get_color(self) -> vec4 {
233 return #7
234 }
235 }
236 }
237 }
238
239 }
240 RunList = <RunList> {
241 }
242 Search = <RectView> {
243 draw_bg: {color: #2}
244 }
245 RunView = <RunView> {}
246 FileTree = <FileTree> {}
247 LogList = <LogList> {}
248 }}
249 }
250 }
251}
252
253#[derive(Live)]
254pub struct App {
255 #[live] ui: WidgetRef,
256 #[live] build_manager: BuildManager,
257 #[rust] file_system: FileSystem,
258}
259
260impl LiveHook for App {
261 fn before_live_design(cx: &mut Cx) {
262 crate::makepad_widgets::live_design(cx);
263 crate::makepad_code_editor::live_design(cx);
264 crate::build_manager::build_manager::live_design(cx);
265 crate::build_manager::run_list::live_design(cx);
266 crate::build_manager::log_list::live_design(cx);
267 crate::build_manager::run_view::live_design(cx);
268 cx.start_stdin_service();
270 }
271
272 fn after_new_from_doc(&mut self, cx: &mut Cx) {
273 self.file_system.init(cx);
274 self.build_manager.init(cx);
275
276 }
278}
279
280app_main!(App);
281
282impl App {
283 fn open_code_file_by_path(&mut self, cx: &mut Cx, path: &str) {
284 let tab_id = LiveId::unique();
285 if let Some(file_id) = self.file_system.path_to_file_node_id(&path) {
286 self.file_system.request_open_file(tab_id, file_id);
287 let dock = self.ui.dock(id!(dock));
288 dock.create_and_select_tab(cx, live_id!(edit_tabs), tab_id, live_id!(CodeEditor), "".to_string(), TabClosable::Yes);
289 self.file_system.ensure_unique_tab_names(cx, &dock)
290 }
291 }
292}
293
294impl AppMain for App {
295
296
297 fn handle_event(&mut self, cx: &mut Cx, event: &Event) {
298 let dock = self.ui.dock(id!(dock));
299 let file_tree = self.ui.file_tree(id!(file_tree));
300 let log_list = self.ui.portal_list(id!(log_list));
301 let run_list = self.ui.flat_list(id!(run_list));
302 if let Event::Draw(event) = event {
303 let cx = &mut Cx2d::new(cx, event);
305 while let Some(next) = self.ui.draw_widget(cx).hook_widget() {
306
307 if let Some(mut file_tree) = file_tree.has_widget(&next).borrow_mut() {
308 file_tree.set_folder_is_open(cx, live_id!(root).into(), true, Animate::No);
309 self.file_system.draw_file_node(
310 cx,
311 live_id!(root).into(),
312 &mut *file_tree
313 );
314 }
315 else if let Some(mut run_view) = next.as_run_view().borrow_mut() {
316 let current_id = dock.drawing_item_id().unwrap();
317 run_view.draw(cx, current_id, &mut self.build_manager);
318 }
319 else if let Some(mut log_list) = log_list.has_widget(&next).borrow_mut() {
320 self.build_manager.draw_log(cx, &mut *log_list);
321 }
322 else if let Some(mut run_list) = run_list.has_widget(&next).borrow_mut() {
323 self.build_manager.draw_run_list(cx, &mut *run_list);
324 }
325 else if let Some(mut code_editor) = next.as_code_editor().borrow_mut() {
326 let current_id = dock.drawing_item_id().unwrap();
328 if let Some(session) = self.file_system.get_session_mut(current_id) {
329 code_editor.draw(cx, session);
330 }
331 }
332 }
333 return
335 }
336
337 if let Event::Destruct = event {
338 self.build_manager.clear_active_builds();
339 }
340
341 if let Event::KeyDown(KeyEvent {
342 key_code,
343 modifiers: KeyModifiers {logo, control, ..},
344 ..
345 }) = event {
346 if *control || *logo {
347 if let KeyCode::Backtick = key_code {
348 self.build_manager.start_recompile(cx);
349 }
350 else if let KeyCode::KeyK = key_code {
351 self.build_manager.clear_log(cx, &dock, &mut self.file_system);
352 log_list.redraw(cx);
353 }
354 }
355 }
356
357 for action in self.file_system.handle_event(cx, event, &self.ui) {
358 match action {
359 FileSystemAction::TreeLoaded => {
360 self.open_code_file_by_path(cx, "examples/news_feed/src/app.rs");
361 }
362 FileSystemAction::RecompileNeeded => {
363 self.build_manager.start_recompile_timer(cx, &self.ui);
364 }
365 FileSystemAction::LiveReloadNeeded(live_file_change) => {
366 self.build_manager.live_reload_needed(live_file_change);
367 self.build_manager.clear_log(cx, &dock, &mut self.file_system);
368 log_list.redraw(cx);
369 }
370 }
371 }
372
373 for (item_id, item) in dock.borrow_mut().unwrap().visible_items() {
375 if let Some(mut run_view) = item.as_run_view().borrow_mut() {
376 run_view.handle_event(cx, event, item_id, &mut self.build_manager);
377 }
378 else if let Some(mut code_editor) = item.as_code_editor().borrow_mut() {
379 if let Some(session) = self.file_system.get_session_mut(item_id) {
380 for action in code_editor.handle_event(cx, event, session) {
381 match action {
382 CodeEditorAction::TextDidChange => {
383 self.file_system.request_save_file(item_id)
385 }
386 }
387 }
388 }
389 }
390 }
391
392 for action in self.build_manager.handle_event(cx, event, &mut self.file_system, &dock) {
393 match action {
394 BuildManagerAction::RedrawLog => {
395 log_list.redraw(cx);
397 }
398 BuildManagerAction::StdinToHost {run_view_id, msg} => {
399 if let Some(mut run_view) = dock.item(run_view_id).as_run_view().borrow_mut() {
400 run_view.handle_stdin_to_host(cx, &msg, run_view_id, &mut self.build_manager);
401 }
402 }
403 _ => ()
404 }
405 }
406
407 let actions = self.ui.handle_widget_event(cx, event);
408
409 for (item_id, item) in run_list.items_with_actions(&actions) {
410 for action in self.build_manager.handle_run_list(cx, &run_list, item_id, item, &actions) {
411 match action {
412 RunListAction::Create(run_view_id, name) => {
413 let tab_bar_id = dock.find_tab_bar_of_tab(live_id!(run_first)).unwrap();
414 dock.create_and_select_tab(cx, tab_bar_id, run_view_id, live_id!(RunView), name, TabClosable::Yes);
415 dock.redraw(cx);
416 }
417 RunListAction::Destroy(run_view_id) => {
418 dock.close_tab(cx, run_view_id);
419 dock.redraw(cx);
420 }
421 _ => ()
422 }
423 log_list.redraw(cx);
424 }
425 }
426
427 for (item_id, item) in log_list.items_with_actions(&actions) {
428 for action in self.build_manager.handle_log_list(cx, &log_list, item_id, item, &actions) {
429 match action {
430 LogListAction::JumpToError{file_name, start, length} => {
431 if let Some(file_id) = self.file_system.path_to_file_node_id(&file_name) {
433 if let Some(tab_id) = self.file_system.file_node_id_to_tab_id(file_id){
434 dock.select_tab(cx, tab_id);
435 if let Some(mut editor) = dock.item(tab_id).as_code_editor().borrow_mut() {
437 if let Some(session) = self.file_system.get_session_mut(tab_id) {
438 editor.set_cursor_and_scroll(cx, start, length, session);
439 editor.set_key_focus(cx);
440 }
441 }
442 }
443 else{
444 let tab_id = LiveId::unique();
446 self.file_system.request_open_file(tab_id, file_id);
447 dock.create_and_select_tab(cx, live_id!(edit_tabs), tab_id, live_id!(CodeEditor), "".to_string(), TabClosable::Yes);
449 self.file_system.ensure_unique_tab_names(cx, &dock)
451 }
452 }
453 }
454 _ => ()
455 }
456 log_list.redraw(cx);
457 }
458 }
459
460 if let Some(tab_id) = dock.clicked_tab_close(&actions) {
461 dock.close_tab(cx, tab_id);
462 if self.build_manager.handle_tab_close(tab_id) {
463 log_list.redraw(cx);
464 run_list.redraw(cx);
465 }
466 self.file_system.remove_tab(tab_id);
467 self.file_system.ensure_unique_tab_names(cx, &dock);
468 }
469
470 if let Some(tab_id) = dock.should_tab_start_drag(&actions) {
471
472 dock.tab_start_drag(cx, tab_id, DragItem::FilePath {
473 path: "".to_string(), internal_id: Some(tab_id)
475 });
476 }
477
478 if let Some(drag) = dock.should_accept_drag(&actions) {
479 if drag.items.len() == 1 {
480 if drag.modifiers.logo {
481 dock.accept_drag(cx, drag, DragResponse::Copy);
482 }
483 else {
484 dock.accept_drag(cx, drag, DragResponse::Move);
485 }
486 }
487 }
488
489 if let Some(drop) = dock.has_drop(&actions) {
490
491 if let DragItem::FilePath {path, internal_id} = &drop.items[0] {
492 if let Some(internal_id) = internal_id { if drop.modifiers.logo {
494 dock.drop_clone(cx, drop.abs, *internal_id, LiveId::unique());
495 }
496 else {
497 dock.drop_move(cx, drop.abs, *internal_id);
498 }
499 self.file_system.ensure_unique_tab_names(cx, &dock);
500 }
501 else { let tab_id = LiveId::unique();
503 if let Some(file_id) = self.file_system.path_to_file_node_id(&path) {
504 self.file_system.request_open_file(tab_id, file_id);
505 dock.drop_create(cx, drop.abs, tab_id, live_id!(CodeEditor), "".to_string(), TabClosable::Yes);
506 self.file_system.ensure_unique_tab_names(cx, &dock)
507 }
508 }
509 }
510 }
511
512 if let Some(file_id) = file_tree.should_file_start_drag(&actions) {
513
514 let path = self.file_system.file_node_path(file_id);
515 file_tree.file_start_drag(cx, file_id, DragItem::FilePath {
516 path,
517 internal_id: None
518 });
519 }
520
521 if let Some(file_id) = file_tree.file_clicked(&actions) {
522 let tab_id = LiveId::unique();
524 self.file_system.request_open_file(tab_id, file_id);
525 dock.create_and_select_tab(cx, live_id!(edit_tabs), tab_id, live_id!(CodeEditor), "".to_string(), TabClosable::Yes);
527 self.file_system.ensure_unique_tab_names(cx, &dock)
529 }
530 }
531}