1 /*************************************************************************/
2 /* script_editor_plugin.cpp */
3 /*************************************************************************/
4 /* This file is part of: */
5 /* GODOT ENGINE */
6 /* https://godotengine.org */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
9 /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
10 /* */
11 /* Permission is hereby granted, free of charge, to any person obtaining */
12 /* a copy of this software and associated documentation files (the */
13 /* "Software"), to deal in the Software without restriction, including */
14 /* without limitation the rights to use, copy, modify, merge, publish, */
15 /* distribute, sublicense, and/or sell copies of the Software, and to */
16 /* permit persons to whom the Software is furnished to do so, subject to */
17 /* the following conditions: */
18 /* */
19 /* The above copyright notice and this permission notice shall be */
20 /* included in all copies or substantial portions of the Software. */
21 /* */
22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29 /*************************************************************************/
30
31 #include "script_editor_plugin.h"
32
33 #include "core/io/resource_loader.h"
34 #include "core/os/file_access.h"
35 #include "core/os/input.h"
36 #include "core/os/keyboard.h"
37 #include "core/os/os.h"
38 #include "core/project_settings.h"
39 #include "editor/editor_node.h"
40 #include "editor/editor_run_script.h"
41 #include "editor/editor_scale.h"
42 #include "editor/editor_settings.h"
43 #include "editor/filesystem_dock.h"
44 #include "editor/find_in_files.h"
45 #include "editor/node_dock.h"
46 #include "editor/plugins/shader_editor_plugin.h"
47 #include "editor/script_editor_debugger.h"
48 #include "scene/main/viewport.h"
49 #include "scene/scene_string_names.h"
50 #include "script_text_editor.h"
51 #include "text_editor.h"
52
53 /*** SCRIPT EDITOR ****/
54
_bind_methods()55 void ScriptEditorBase::_bind_methods() {
56
57 ADD_SIGNAL(MethodInfo("name_changed"));
58 ADD_SIGNAL(MethodInfo("edited_script_changed"));
59 ADD_SIGNAL(MethodInfo("request_help", PropertyInfo(Variant::STRING, "topic")));
60 ADD_SIGNAL(MethodInfo("request_open_script_at_line", PropertyInfo(Variant::OBJECT, "script"), PropertyInfo(Variant::INT, "line")));
61 ADD_SIGNAL(MethodInfo("request_save_history"));
62 ADD_SIGNAL(MethodInfo("go_to_help", PropertyInfo(Variant::STRING, "what")));
63 // TODO: This signal is no use for VisualScript.
64 ADD_SIGNAL(MethodInfo("search_in_files_requested", PropertyInfo(Variant::STRING, "text")));
65 }
66
_is_built_in_script(Script * p_script)67 static bool _is_built_in_script(Script *p_script) {
68 String path = p_script->get_path();
69
70 return path.find("::") != -1;
71 }
72
73 class EditorScriptCodeCompletionCache : public ScriptCodeCompletionCache {
74
75 struct Cache {
76 uint64_t time_loaded;
77 RES cache;
78 };
79
80 Map<String, Cache> cached;
81
82 public:
83 uint64_t max_time_cache;
84 int max_cache_size;
85
cleanup()86 void cleanup() {
87
88 List<Map<String, Cache>::Element *> to_clean;
89
90 Map<String, Cache>::Element *I = cached.front();
91 while (I) {
92 if ((OS::get_singleton()->get_ticks_msec() - I->get().time_loaded) > max_time_cache) {
93 to_clean.push_back(I);
94 }
95 I = I->next();
96 }
97
98 while (to_clean.front()) {
99 cached.erase(to_clean.front()->get());
100 to_clean.pop_front();
101 }
102 }
103
get_cached_resource(const String & p_path)104 virtual RES get_cached_resource(const String &p_path) {
105
106 Map<String, Cache>::Element *E = cached.find(p_path);
107 if (!E) {
108
109 Cache c;
110 c.cache = ResourceLoader::load(p_path);
111 E = cached.insert(p_path, c);
112 }
113
114 E->get().time_loaded = OS::get_singleton()->get_ticks_msec();
115
116 if (cached.size() > max_cache_size) {
117 uint64_t older;
118 Map<String, Cache>::Element *O = cached.front();
119 older = O->get().time_loaded;
120 Map<String, Cache>::Element *I = O;
121 while (I) {
122 if (I->get().time_loaded < older) {
123 older = I->get().time_loaded;
124 O = I;
125 }
126 I = I->next();
127 }
128
129 if (O != E) { //should never happen..
130 cached.erase(O);
131 }
132 }
133
134 return E->get().cache;
135 }
136
EditorScriptCodeCompletionCache()137 EditorScriptCodeCompletionCache() {
138
139 max_cache_size = 128;
140 max_time_cache = 5 * 60 * 1000; //minutes, five
141 }
142
~EditorScriptCodeCompletionCache()143 virtual ~EditorScriptCodeCompletionCache() {}
144 };
145
popup_dialog(const Vector<String> & p_functions,bool p_dontclear)146 void ScriptEditorQuickOpen::popup_dialog(const Vector<String> &p_functions, bool p_dontclear) {
147
148 popup_centered_ratio(0.6);
149 if (p_dontclear)
150 search_box->select_all();
151 else
152 search_box->clear();
153 search_box->grab_focus();
154 functions = p_functions;
155 _update_search();
156 }
157
_text_changed(const String & p_newtext)158 void ScriptEditorQuickOpen::_text_changed(const String &p_newtext) {
159
160 _update_search();
161 }
162
_sbox_input(const Ref<InputEvent> & p_ie)163 void ScriptEditorQuickOpen::_sbox_input(const Ref<InputEvent> &p_ie) {
164
165 Ref<InputEventKey> k = p_ie;
166
167 if (k.is_valid() && (k->get_scancode() == KEY_UP ||
168 k->get_scancode() == KEY_DOWN ||
169 k->get_scancode() == KEY_PAGEUP ||
170 k->get_scancode() == KEY_PAGEDOWN)) {
171
172 search_options->call("_gui_input", k);
173 search_box->accept_event();
174 }
175 }
176
_update_search()177 void ScriptEditorQuickOpen::_update_search() {
178
179 search_options->clear();
180 TreeItem *root = search_options->create_item();
181
182 for (int i = 0; i < functions.size(); i++) {
183
184 String file = functions[i];
185 if ((search_box->get_text() == "" || file.findn(search_box->get_text()) != -1)) {
186
187 TreeItem *ti = search_options->create_item(root);
188 ti->set_text(0, file);
189 if (root->get_children() == ti)
190 ti->select(0);
191 }
192 }
193
194 get_ok()->set_disabled(root->get_children() == NULL);
195 }
196
_confirmed()197 void ScriptEditorQuickOpen::_confirmed() {
198
199 TreeItem *ti = search_options->get_selected();
200 if (!ti)
201 return;
202 int line = ti->get_text(0).get_slice(":", 1).to_int();
203
204 emit_signal("goto_line", line - 1);
205 hide();
206 }
207
_notification(int p_what)208 void ScriptEditorQuickOpen::_notification(int p_what) {
209
210 switch (p_what) {
211 case NOTIFICATION_ENTER_TREE: {
212 connect("confirmed", this, "_confirmed");
213
214 search_box->set_clear_button_enabled(true);
215 FALLTHROUGH;
216 }
217 case NOTIFICATION_THEME_CHANGED: {
218 search_box->set_right_icon(get_icon("Search", "EditorIcons"));
219 } break;
220 case NOTIFICATION_EXIT_TREE: {
221 disconnect("confirmed", this, "_confirmed");
222 } break;
223 }
224 }
225
_bind_methods()226 void ScriptEditorQuickOpen::_bind_methods() {
227
228 ClassDB::bind_method(D_METHOD("_text_changed"), &ScriptEditorQuickOpen::_text_changed);
229 ClassDB::bind_method(D_METHOD("_confirmed"), &ScriptEditorQuickOpen::_confirmed);
230 ClassDB::bind_method(D_METHOD("_sbox_input"), &ScriptEditorQuickOpen::_sbox_input);
231
232 ADD_SIGNAL(MethodInfo("goto_line", PropertyInfo(Variant::INT, "line")));
233 }
234
ScriptEditorQuickOpen()235 ScriptEditorQuickOpen::ScriptEditorQuickOpen() {
236
237 VBoxContainer *vbc = memnew(VBoxContainer);
238 add_child(vbc);
239 search_box = memnew(LineEdit);
240 vbc->add_margin_child(TTR("Search:"), search_box);
241 search_box->connect("text_changed", this, "_text_changed");
242 search_box->connect("gui_input", this, "_sbox_input");
243 search_options = memnew(Tree);
244 vbc->add_margin_child(TTR("Matches:"), search_options, true);
245 get_ok()->set_text(TTR("Open"));
246 get_ok()->set_disabled(true);
247 register_text_enter(search_box);
248 set_hide_on_ok(false);
249 search_options->connect("item_activated", this, "_confirmed");
250 search_options->set_hide_root(true);
251 search_options->set_hide_folding(true);
252 search_options->add_constant_override("draw_guides", 1);
253 }
254
255 /////////////////////////////////
256
257 ScriptEditor *ScriptEditor::script_editor = NULL;
258
259 /*** SCRIPT EDITOR ******/
260
_get_debug_tooltip(const String & p_text,Node * _se)261 String ScriptEditor::_get_debug_tooltip(const String &p_text, Node *_se) {
262
263 String val = debugger->get_var_value(p_text);
264 if (val != String()) {
265 return p_text + ": " + val;
266 } else {
267
268 return String();
269 }
270 }
271
_breaked(bool p_breaked,bool p_can_debug)272 void ScriptEditor::_breaked(bool p_breaked, bool p_can_debug) {
273
274 if (bool(EditorSettings::get_singleton()->get("text_editor/external/use_external_editor"))) {
275 return;
276 }
277
278 debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_NEXT), !(p_breaked && p_can_debug));
279 debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_STEP), !(p_breaked && p_can_debug));
280 debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_BREAK), p_breaked);
281 debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), !p_breaked);
282
283 for (int i = 0; i < tab_container->get_child_count(); i++) {
284
285 ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
286 if (!se) {
287 continue;
288 }
289
290 se->set_debugger_active(p_breaked);
291 }
292 }
293
_show_debugger(bool p_show)294 void ScriptEditor::_show_debugger(bool p_show) {
295
296 //debug_menu->get_popup()->set_item_checked( debug_menu->get_popup()->get_item_index(DEBUG_SHOW), p_show);
297 }
298
_script_created(Ref<Script> p_script)299 void ScriptEditor::_script_created(Ref<Script> p_script) {
300 editor->push_item(p_script.operator->());
301 }
302
_goto_script_line2(int p_line)303 void ScriptEditor::_goto_script_line2(int p_line) {
304
305 ScriptEditorBase *current = _get_current_editor();
306 if (current)
307 current->goto_line(p_line);
308 }
309
_goto_script_line(REF p_script,int p_line)310 void ScriptEditor::_goto_script_line(REF p_script, int p_line) {
311
312 Ref<Script> script = Object::cast_to<Script>(*p_script);
313 if (script.is_valid() && (script->has_source_code() || script->get_path().is_resource_file())) {
314 if (edit(p_script, p_line, 0)) {
315 editor->push_item(p_script.ptr());
316
317 ScriptEditorBase *current = _get_current_editor();
318 if (ScriptTextEditor *script_text_editor = Object::cast_to<ScriptTextEditor>(current)) {
319 script_text_editor->goto_line_centered(p_line);
320 } else if (current) {
321 current->goto_line(p_line, true);
322 }
323 }
324 }
325 }
326
_set_execution(REF p_script,int p_line)327 void ScriptEditor::_set_execution(REF p_script, int p_line) {
328 Ref<Script> script = Object::cast_to<Script>(*p_script);
329 if (script.is_valid() && (script->has_source_code() || script->get_path().is_resource_file())) {
330 for (int i = 0; i < tab_container->get_child_count(); i++) {
331
332 ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
333 if (!se)
334 continue;
335
336 if ((script != NULL && se->get_edited_resource() == p_script) || se->get_edited_resource()->get_path() == script->get_path()) {
337 se->set_executing_line(p_line);
338 }
339 }
340 }
341 }
342
_clear_execution(REF p_script)343 void ScriptEditor::_clear_execution(REF p_script) {
344 Ref<Script> script = Object::cast_to<Script>(*p_script);
345 if (script.is_valid() && (script->has_source_code() || script->get_path().is_resource_file())) {
346 for (int i = 0; i < tab_container->get_child_count(); i++) {
347
348 ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
349 if (!se)
350 continue;
351
352 if ((script != NULL && se->get_edited_resource() == p_script) || se->get_edited_resource()->get_path() == script->get_path()) {
353 se->clear_executing_line();
354 }
355 }
356 }
357 }
358
_get_current_editor() const359 ScriptEditorBase *ScriptEditor::_get_current_editor() const {
360
361 int selected = tab_container->get_current_tab();
362 if (selected < 0 || selected >= tab_container->get_child_count())
363 return NULL;
364
365 return Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected));
366 }
367
_update_history_arrows()368 void ScriptEditor::_update_history_arrows() {
369
370 script_back->set_disabled(history_pos <= 0);
371 script_forward->set_disabled(history_pos >= history.size() - 1);
372 }
373
_save_history()374 void ScriptEditor::_save_history() {
375
376 if (history_pos >= 0 && history_pos < history.size() && history[history_pos].control == tab_container->get_current_tab_control()) {
377
378 Node *n = tab_container->get_current_tab_control();
379
380 if (Object::cast_to<ScriptEditorBase>(n)) {
381
382 history.write[history_pos].state = Object::cast_to<ScriptEditorBase>(n)->get_edit_state();
383 }
384 if (Object::cast_to<EditorHelp>(n)) {
385
386 history.write[history_pos].state = Object::cast_to<EditorHelp>(n)->get_scroll();
387 }
388 }
389
390 history.resize(history_pos + 1);
391 ScriptHistory sh;
392 sh.control = tab_container->get_current_tab_control();
393 sh.state = Variant();
394
395 history.push_back(sh);
396 history_pos++;
397
398 _update_history_arrows();
399 }
400
_go_to_tab(int p_idx)401 void ScriptEditor::_go_to_tab(int p_idx) {
402
403 ScriptEditorBase *current = _get_current_editor();
404 if (current) {
405 if (current->is_unsaved()) {
406
407 current->apply_code();
408 }
409 }
410
411 Control *c = Object::cast_to<Control>(tab_container->get_child(p_idx));
412 if (!c)
413 return;
414
415 if (history_pos >= 0 && history_pos < history.size() && history[history_pos].control == tab_container->get_current_tab_control()) {
416
417 Node *n = tab_container->get_current_tab_control();
418
419 if (Object::cast_to<ScriptEditorBase>(n)) {
420
421 history.write[history_pos].state = Object::cast_to<ScriptEditorBase>(n)->get_edit_state();
422 }
423 if (Object::cast_to<EditorHelp>(n)) {
424
425 history.write[history_pos].state = Object::cast_to<EditorHelp>(n)->get_scroll();
426 }
427 }
428
429 history.resize(history_pos + 1);
430 ScriptHistory sh;
431 sh.control = c;
432 sh.state = Variant();
433
434 history.push_back(sh);
435 history_pos++;
436
437 tab_container->set_current_tab(p_idx);
438
439 c = tab_container->get_current_tab_control();
440
441 if (Object::cast_to<ScriptEditorBase>(c)) {
442
443 script_name_label->set_text(Object::cast_to<ScriptEditorBase>(c)->get_name());
444 script_icon->set_texture(Object::cast_to<ScriptEditorBase>(c)->get_icon());
445 if (is_visible_in_tree())
446 Object::cast_to<ScriptEditorBase>(c)->ensure_focus();
447
448 Ref<Script> script = Object::cast_to<ScriptEditorBase>(c)->get_edited_resource();
449 if (script != NULL) {
450 notify_script_changed(script);
451 }
452
453 Object::cast_to<ScriptEditorBase>(c)->validate();
454 }
455 if (Object::cast_to<EditorHelp>(c)) {
456
457 script_name_label->set_text(Object::cast_to<EditorHelp>(c)->get_class());
458 script_icon->set_texture(get_icon("Help", "EditorIcons"));
459 if (is_visible_in_tree())
460 Object::cast_to<EditorHelp>(c)->set_focused();
461 }
462
463 c->set_meta("__editor_pass", ++edit_pass);
464 _update_history_arrows();
465 _update_script_colors();
466 _update_members_overview();
467 _update_help_overview();
468 _update_selected_editor_menu();
469 _update_members_overview_visibility();
470 _update_help_overview_visibility();
471 }
472
_add_recent_script(String p_path)473 void ScriptEditor::_add_recent_script(String p_path) {
474
475 if (p_path.empty()) {
476 return;
477 }
478
479 Array rc = EditorSettings::get_singleton()->get_project_metadata("recent_files", "scripts", Array());
480 if (rc.find(p_path) != -1) {
481 rc.erase(p_path);
482 }
483 rc.push_front(p_path);
484 if (rc.size() > 10) {
485 rc.resize(10);
486 }
487
488 EditorSettings::get_singleton()->set_project_metadata("recent_files", "scripts", rc);
489 _update_recent_scripts();
490 }
491
_update_recent_scripts()492 void ScriptEditor::_update_recent_scripts() {
493
494 Array rc = EditorSettings::get_singleton()->get_project_metadata("recent_files", "scripts", Array());
495 recent_scripts->clear();
496
497 String path;
498 for (int i = 0; i < rc.size(); i++) {
499
500 path = rc[i];
501 recent_scripts->add_item(path.replace("res://", ""));
502 }
503
504 recent_scripts->add_separator();
505 recent_scripts->add_shortcut(ED_SHORTCUT("script_editor/clear_recent", TTR("Clear Recent Files")));
506
507 recent_scripts->set_as_minsize();
508 }
509
_open_recent_script(int p_idx)510 void ScriptEditor::_open_recent_script(int p_idx) {
511
512 // clear button
513 if (p_idx == recent_scripts->get_item_count() - 1) {
514 EditorSettings::get_singleton()->set_project_metadata("recent_files", "scripts", Array());
515 call_deferred("_update_recent_scripts");
516 return;
517 }
518
519 Array rc = EditorSettings::get_singleton()->get_project_metadata("recent_files", "scripts", Array());
520 ERR_FAIL_INDEX(p_idx, rc.size());
521
522 String path = rc[p_idx];
523 // if its not on disk its a help file or deleted
524 if (FileAccess::exists(path)) {
525 List<String> extensions;
526 ResourceLoader::get_recognized_extensions_for_type("Script", &extensions);
527
528 if (extensions.find(path.get_extension())) {
529 Ref<Script> script = ResourceLoader::load(path);
530 if (script.is_valid()) {
531 edit(script, true);
532 return;
533 }
534 }
535
536 Error err;
537 Ref<TextFile> text_file = _load_text_file(path, &err);
538 if (text_file.is_valid()) {
539 edit(text_file, true);
540 return;
541 }
542 // if it's a path then it's most likely a deleted file not help
543 } else if (path.find("::") != -1) {
544 // built-in script
545 String res_path = path.get_slice("::", 0);
546 if (ResourceLoader::get_resource_type(res_path) == "PackedScene") {
547 if (!EditorNode::get_singleton()->is_scene_open(res_path)) {
548 EditorNode::get_singleton()->load_scene(res_path);
549 }
550 } else {
551 EditorNode::get_singleton()->load_resource(res_path);
552 }
553 Ref<Script> script = ResourceLoader::load(path);
554 if (script.is_valid()) {
555 edit(script, true);
556 return;
557 }
558 } else if (!path.is_resource_file()) {
559 _help_class_open(path);
560 return;
561 }
562
563 rc.remove(p_idx);
564 EditorSettings::get_singleton()->set_project_metadata("recent_files", "scripts", rc);
565 _update_recent_scripts();
566 _show_error_dialog(path);
567 }
568
_show_error_dialog(String p_path)569 void ScriptEditor::_show_error_dialog(String p_path) {
570
571 error_dialog->set_text(vformat(TTR("Can't open '%s'. The file could have been moved or deleted."), p_path));
572 error_dialog->popup_centered_minsize();
573 }
574
_close_tab(int p_idx,bool p_save,bool p_history_back)575 void ScriptEditor::_close_tab(int p_idx, bool p_save, bool p_history_back) {
576
577 int selected = p_idx;
578 if (selected < 0 || selected >= tab_container->get_child_count())
579 return;
580
581 Node *tselected = tab_container->get_child(selected);
582
583 ScriptEditorBase *current = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected));
584 if (current) {
585 Ref<Script> script = current->get_edited_resource();
586 if (p_save) {
587 // Do not try to save internal scripts
588 if (!script.is_valid() || !(script->get_path() == "" || script->get_path().find("local://") != -1 || script->get_path().find("::") != -1)) {
589 _menu_option(FILE_SAVE);
590 }
591 }
592
593 if (script != NULL) {
594 previous_scripts.push_back(script->get_path());
595 notify_script_close(script);
596 }
597 }
598
599 // roll back to previous tab
600 if (p_history_back) {
601 _history_back();
602 }
603
604 //remove from history
605 history.resize(history_pos + 1);
606
607 for (int i = 0; i < history.size(); i++) {
608 if (history[i].control == tselected) {
609 history.remove(i);
610 i--;
611 history_pos--;
612 }
613 }
614
615 if (history_pos >= history.size()) {
616 history_pos = history.size() - 1;
617 }
618
619 int idx = tab_container->get_current_tab();
620 if (current) {
621 current->clear_edit_menu();
622 }
623 memdelete(tselected);
624 if (idx >= tab_container->get_child_count())
625 idx = tab_container->get_child_count() - 1;
626 if (idx >= 0) {
627
628 if (history_pos >= 0) {
629 idx = history[history_pos].control->get_index();
630 }
631 tab_container->set_current_tab(idx);
632 } else {
633 _update_selected_editor_menu();
634 }
635
636 _update_history_arrows();
637
638 _update_script_names();
639 _update_members_overview_visibility();
640 _update_help_overview_visibility();
641 _save_layout();
642 }
643
_close_current_tab()644 void ScriptEditor::_close_current_tab() {
645
646 _close_tab(tab_container->get_current_tab());
647 }
648
_close_discard_current_tab(const String & p_str)649 void ScriptEditor::_close_discard_current_tab(const String &p_str) {
650 _close_tab(tab_container->get_current_tab(), false);
651 erase_tab_confirm->hide();
652 }
653
_close_docs_tab()654 void ScriptEditor::_close_docs_tab() {
655
656 int child_count = tab_container->get_child_count();
657 for (int i = child_count - 1; i >= 0; i--) {
658
659 EditorHelp *se = Object::cast_to<EditorHelp>(tab_container->get_child(i));
660
661 if (se) {
662 _close_tab(i, true, false);
663 }
664 }
665 }
666
_copy_script_path()667 void ScriptEditor::_copy_script_path() {
668 ScriptEditorBase *se = _get_current_editor();
669 RES script = se->get_edited_resource();
670 OS::get_singleton()->set_clipboard(script->get_path());
671 }
672
_close_other_tabs()673 void ScriptEditor::_close_other_tabs() {
674
675 int child_count = tab_container->get_child_count();
676 int current_idx = tab_container->get_current_tab();
677 for (int i = child_count - 1; i >= 0; i--) {
678
679 if (i == current_idx) {
680 continue;
681 }
682
683 tab_container->set_current_tab(i);
684 ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
685
686 if (se) {
687
688 // Maybe there are unsaved changes
689 if (se->is_unsaved()) {
690 _ask_close_current_unsaved_tab(se);
691 continue;
692 }
693 }
694
695 _close_current_tab();
696 }
697 }
698
_close_all_tabs()699 void ScriptEditor::_close_all_tabs() {
700
701 int child_count = tab_container->get_child_count();
702 for (int i = child_count - 1; i >= 0; i--) {
703
704 tab_container->set_current_tab(i);
705 ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
706
707 if (se) {
708
709 // Maybe there are unsaved changes
710 if (se->is_unsaved()) {
711 _ask_close_current_unsaved_tab(se);
712 continue;
713 }
714 }
715
716 _close_current_tab();
717 }
718 }
719
_ask_close_current_unsaved_tab(ScriptEditorBase * current)720 void ScriptEditor::_ask_close_current_unsaved_tab(ScriptEditorBase *current) {
721 erase_tab_confirm->set_text(TTR("Close and save changes?") + "\n\"" + current->get_name() + "\"");
722 erase_tab_confirm->popup_centered_minsize();
723 }
724
_resave_scripts(const String & p_str)725 void ScriptEditor::_resave_scripts(const String &p_str) {
726
727 apply_scripts();
728
729 for (int i = 0; i < tab_container->get_child_count(); i++) {
730
731 ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
732 if (!se)
733 continue;
734
735 RES script = se->get_edited_resource();
736
737 if (script->get_path() == "" || script->get_path().find("local://") != -1 || script->get_path().find("::") != -1)
738 continue; //internal script, who cares
739
740 if (trim_trailing_whitespace_on_save) {
741 se->trim_trailing_whitespace();
742 }
743
744 se->insert_final_newline();
745
746 if (convert_indent_on_save) {
747 if (use_space_indentation) {
748 se->convert_indent_to_spaces();
749 } else {
750 se->convert_indent_to_tabs();
751 }
752 }
753
754 Ref<TextFile> text_file = script;
755 if (text_file != NULL) {
756 se->apply_code();
757 _save_text_file(text_file, text_file->get_path());
758 break;
759 } else {
760 editor->save_resource(script);
761 }
762 se->tag_saved_version();
763 }
764
765 disk_changed->hide();
766 }
767
_reload_scripts()768 void ScriptEditor::_reload_scripts() {
769
770 for (int i = 0; i < tab_container->get_child_count(); i++) {
771
772 ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
773 if (!se) {
774
775 continue;
776 }
777
778 RES edited_res = se->get_edited_resource();
779
780 if (edited_res->get_path() == "" || edited_res->get_path().find("local://") != -1 || edited_res->get_path().find("::") != -1) {
781
782 continue; //internal script, who cares
783 }
784
785 uint64_t last_date = edited_res->get_last_modified_time();
786 uint64_t date = FileAccess::get_modified_time(edited_res->get_path());
787
788 if (last_date == date) {
789 continue;
790 }
791
792 Ref<Script> script = edited_res;
793 if (script != NULL) {
794 Ref<Script> rel_script = ResourceLoader::load(script->get_path(), script->get_class(), true);
795 ERR_CONTINUE(!rel_script.is_valid());
796 script->set_source_code(rel_script->get_source_code());
797 script->set_last_modified_time(rel_script->get_last_modified_time());
798 script->reload();
799 }
800
801 Ref<TextFile> text_file = edited_res;
802 if (text_file != NULL) {
803 Error err;
804 Ref<TextFile> rel_text_file = _load_text_file(text_file->get_path(), &err);
805 ERR_CONTINUE(!rel_text_file.is_valid());
806 text_file->set_text(rel_text_file->get_text());
807 text_file->set_last_modified_time(rel_text_file->get_last_modified_time());
808 }
809 se->reload_text();
810 }
811
812 disk_changed->hide();
813 _update_script_names();
814 }
815
_res_saved_callback(const Ref<Resource> & p_res)816 void ScriptEditor::_res_saved_callback(const Ref<Resource> &p_res) {
817
818 for (int i = 0; i < tab_container->get_child_count(); i++) {
819
820 ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
821 if (!se) {
822
823 continue;
824 }
825
826 RES script = se->get_edited_resource();
827
828 if (script->get_path() == "" || script->get_path().find("local://") != -1 || script->get_path().find("::") != -1) {
829 continue; //internal script, who cares
830 }
831
832 if (script == p_res) {
833
834 se->tag_saved_version();
835 }
836 }
837
838 _update_script_names();
839
840 if (!pending_auto_reload && auto_reload_running_scripts) {
841 call_deferred("_live_auto_reload_running_scripts");
842 pending_auto_reload = true;
843 }
844 }
845
_live_auto_reload_running_scripts()846 void ScriptEditor::_live_auto_reload_running_scripts() {
847 pending_auto_reload = false;
848 debugger->reload_scripts();
849 }
850
_test_script_times_on_disk(RES p_for_script)851 bool ScriptEditor::_test_script_times_on_disk(RES p_for_script) {
852
853 disk_changed_list->clear();
854 TreeItem *r = disk_changed_list->create_item();
855 disk_changed_list->set_hide_root(true);
856
857 bool need_ask = false;
858 bool need_reload = false;
859 bool use_autoreload = bool(EDITOR_DEF("text_editor/files/auto_reload_scripts_on_external_change", false));
860
861 for (int i = 0; i < tab_container->get_child_count(); i++) {
862
863 ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
864 if (se) {
865
866 RES edited_res = se->get_edited_resource();
867 if (p_for_script.is_valid() && edited_res.is_valid() && p_for_script != edited_res)
868 continue;
869
870 if (edited_res->get_path() == "" || edited_res->get_path().find("local://") != -1 || edited_res->get_path().find("::") != -1)
871 continue; //internal script, who cares
872
873 uint64_t last_date = edited_res->get_last_modified_time();
874 uint64_t date = FileAccess::get_modified_time(edited_res->get_path());
875
876 if (last_date != date) {
877
878 TreeItem *ti = disk_changed_list->create_item(r);
879 ti->set_text(0, edited_res->get_path().get_file());
880
881 if (!use_autoreload || se->is_unsaved()) {
882 need_ask = true;
883 }
884 need_reload = true;
885 }
886 }
887 }
888
889 if (need_reload) {
890 if (!need_ask) {
891 script_editor->_reload_scripts();
892 need_reload = false;
893 } else {
894 disk_changed->call_deferred("popup_centered_ratio", 0.5);
895 }
896 }
897
898 return need_reload;
899 }
900
_file_dialog_action(String p_file)901 void ScriptEditor::_file_dialog_action(String p_file) {
902
903 switch (file_dialog_option) {
904 case FILE_NEW_TEXTFILE: {
905 Error err;
906 FileAccess *file = FileAccess::open(p_file, FileAccess::WRITE, &err);
907 if (err) {
908 editor->show_warning(TTR("Error writing TextFile:") + "\n" + p_file, TTR("Error!"));
909 break;
910 }
911 file->close();
912 memdelete(file);
913 FALLTHROUGH;
914 }
915 case FILE_OPEN: {
916
917 List<String> extensions;
918 ResourceLoader::get_recognized_extensions_for_type("Script", &extensions);
919 if (extensions.find(p_file.get_extension())) {
920 Ref<Script> scr = ResourceLoader::load(p_file);
921 if (!scr.is_valid()) {
922 editor->show_warning(TTR("Could not load file at:") + "\n\n" + p_file, TTR("Error!"));
923 file_dialog_option = -1;
924 return;
925 }
926
927 edit(scr);
928 file_dialog_option = -1;
929 return;
930 }
931
932 Error error;
933 Ref<TextFile> text_file = _load_text_file(p_file, &error);
934 if (error != OK) {
935 editor->show_warning(TTR("Could not load file at:") + "\n\n" + p_file, TTR("Error!"));
936 }
937
938 if (text_file.is_valid()) {
939 edit(text_file);
940 file_dialog_option = -1;
941 return;
942 }
943 } break;
944 case FILE_SAVE_AS: {
945 ScriptEditorBase *current = _get_current_editor();
946
947 String path = ProjectSettings::get_singleton()->localize_path(p_file);
948 Error err = _save_text_file(current->get_edited_resource(), path);
949
950 if (err != OK) {
951 editor->show_accept(TTR("Error saving file!"), TTR("OK"));
952 return;
953 }
954
955 ((Resource *)current->get_edited_resource().ptr())->set_path(path);
956 _update_script_names();
957 } break;
958 case THEME_SAVE_AS: {
959 if (!EditorSettings::get_singleton()->save_text_editor_theme_as(p_file)) {
960 editor->show_warning(TTR("Error while saving theme."), TTR("Error Saving"));
961 }
962 } break;
963 case THEME_IMPORT: {
964 if (!EditorSettings::get_singleton()->import_text_editor_theme(p_file)) {
965 editor->show_warning(TTR("Error importing theme."), TTR("Error Importing"));
966 }
967 } break;
968 }
969 file_dialog_option = -1;
970 }
971
_get_current_script()972 Ref<Script> ScriptEditor::_get_current_script() {
973
974 ScriptEditorBase *current = _get_current_editor();
975
976 if (current) {
977 Ref<Script> script = current->get_edited_resource();
978 return script != NULL ? script : NULL;
979 } else {
980 return NULL;
981 }
982 }
983
_get_open_scripts() const984 Array ScriptEditor::_get_open_scripts() const {
985
986 Array ret;
987 Vector<Ref<Script> > scripts = get_open_scripts();
988 int scrits_amount = scripts.size();
989 for (int idx_script = 0; idx_script < scrits_amount; idx_script++) {
990 ret.push_back(scripts[idx_script]);
991 }
992 return ret;
993 }
994
toggle_scripts_panel()995 bool ScriptEditor::toggle_scripts_panel() {
996 list_split->set_visible(!list_split->is_visible());
997 return list_split->is_visible();
998 }
999
is_scripts_panel_toggled()1000 bool ScriptEditor::is_scripts_panel_toggled() {
1001 return list_split->is_visible();
1002 }
1003
_menu_option(int p_option)1004 void ScriptEditor::_menu_option(int p_option) {
1005
1006 ScriptEditorBase *current = _get_current_editor();
1007 switch (p_option) {
1008 case FILE_NEW: {
1009 script_create_dialog->config("Node", "new_script", false, false);
1010 script_create_dialog->popup_centered();
1011 } break;
1012 case FILE_NEW_TEXTFILE: {
1013 file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE);
1014 file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
1015 file_dialog_option = FILE_NEW_TEXTFILE;
1016
1017 file_dialog->clear_filters();
1018 file_dialog->popup_centered_ratio();
1019 file_dialog->set_title(TTR("New Text File..."));
1020 } break;
1021 case FILE_OPEN: {
1022 file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE);
1023 file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
1024 file_dialog_option = FILE_OPEN;
1025
1026 List<String> extensions;
1027 ResourceLoader::get_recognized_extensions_for_type("Script", &extensions);
1028 file_dialog->clear_filters();
1029 for (int i = 0; i < extensions.size(); i++) {
1030 file_dialog->add_filter("*." + extensions[i] + " ; " + extensions[i].to_upper());
1031 }
1032
1033 file_dialog->popup_centered_ratio();
1034 file_dialog->set_title(TTR("Open File"));
1035 return;
1036 } break;
1037 case FILE_REOPEN_CLOSED: {
1038
1039 if (previous_scripts.empty())
1040 return;
1041
1042 String path = previous_scripts.back()->get();
1043 previous_scripts.pop_back();
1044
1045 List<String> extensions;
1046 ResourceLoader::get_recognized_extensions_for_type("Script", &extensions);
1047 bool built_in = !path.is_resource_file();
1048
1049 if (extensions.find(path.get_extension()) || built_in) {
1050 if (built_in) {
1051 String res_path = path.get_slice("::", 0);
1052 if (ResourceLoader::get_resource_type(res_path) == "PackedScene") {
1053 if (!EditorNode::get_singleton()->is_scene_open(res_path)) {
1054 EditorNode::get_singleton()->load_scene(res_path);
1055 script_editor->call_deferred("_menu_option", p_option);
1056 previous_scripts.push_back(path); //repeat the operation
1057 return;
1058 }
1059 } else {
1060 EditorNode::get_singleton()->load_resource(res_path);
1061 }
1062 }
1063
1064 Ref<Script> scr = ResourceLoader::load(path);
1065 if (!scr.is_valid()) {
1066 editor->show_warning(TTR("Could not load file at:") + "\n\n" + path, TTR("Error!"));
1067 file_dialog_option = -1;
1068 return;
1069 }
1070
1071 edit(scr);
1072 file_dialog_option = -1;
1073 return;
1074 } else {
1075 Error error;
1076 Ref<TextFile> text_file = _load_text_file(path, &error);
1077 if (error != OK)
1078 editor->show_warning(TTR("Could not load file at:") + "\n\n" + path, TTR("Error!"));
1079
1080 if (text_file.is_valid()) {
1081 edit(text_file);
1082 file_dialog_option = -1;
1083 return;
1084 }
1085 }
1086 } break;
1087 case FILE_SAVE_ALL: {
1088
1089 if (_test_script_times_on_disk())
1090 return;
1091
1092 save_all_scripts();
1093 } break;
1094 case SEARCH_IN_FILES: {
1095
1096 _on_find_in_files_requested("");
1097 } break;
1098 case SEARCH_HELP: {
1099
1100 help_search_dialog->popup_dialog();
1101 } break;
1102 case SEARCH_WEBSITE: {
1103
1104 OS::get_singleton()->shell_open("https://docs.godotengine.org/");
1105 } break;
1106 case WINDOW_NEXT: {
1107
1108 _history_forward();
1109 } break;
1110 case WINDOW_PREV: {
1111
1112 _history_back();
1113 } break;
1114 case WINDOW_SORT: {
1115 _sort_list_on_update = true;
1116 _update_script_names();
1117 } break;
1118 case DEBUG_SHOW: {
1119 if (debugger) {
1120 bool visible = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(DEBUG_SHOW));
1121 debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(DEBUG_SHOW), !visible);
1122 if (visible)
1123 debugger->hide();
1124 else
1125 debugger->show();
1126 }
1127 } break;
1128 case DEBUG_SHOW_KEEP_OPEN: {
1129 bool visible = debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(DEBUG_SHOW_KEEP_OPEN));
1130 if (debugger)
1131 debugger->set_hide_on_stop(visible);
1132 debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(DEBUG_SHOW_KEEP_OPEN), !visible);
1133 } break;
1134 case DEBUG_WITH_EXTERNAL_EDITOR: {
1135 bool debug_with_external_editor = !debug_menu->get_popup()->is_item_checked(debug_menu->get_popup()->get_item_index(DEBUG_WITH_EXTERNAL_EDITOR));
1136 debugger->set_debug_with_external_editor(debug_with_external_editor);
1137 debug_menu->get_popup()->set_item_checked(debug_menu->get_popup()->get_item_index(DEBUG_WITH_EXTERNAL_EDITOR), debug_with_external_editor);
1138 } break;
1139 case TOGGLE_SCRIPTS_PANEL: {
1140 if (current) {
1141 ScriptTextEditor *editor = Object::cast_to<ScriptTextEditor>(current);
1142 toggle_scripts_panel();
1143 if (editor) {
1144 editor->update_toggle_scripts_button();
1145 }
1146 } else {
1147 toggle_scripts_panel();
1148 }
1149 }
1150 }
1151
1152 if (current) {
1153
1154 switch (p_option) {
1155 case FILE_SAVE: {
1156
1157 if (_test_script_times_on_disk())
1158 return;
1159
1160 if (trim_trailing_whitespace_on_save)
1161 current->trim_trailing_whitespace();
1162
1163 current->insert_final_newline();
1164
1165 if (convert_indent_on_save) {
1166 if (use_space_indentation) {
1167 current->convert_indent_to_spaces();
1168 } else {
1169 current->convert_indent_to_tabs();
1170 }
1171 }
1172
1173 Ref<TextFile> text_file = current->get_edited_resource();
1174 if (text_file != NULL) {
1175 current->apply_code();
1176 _save_text_file(text_file, text_file->get_path());
1177 break;
1178 }
1179 editor->save_resource(current->get_edited_resource());
1180
1181 } break;
1182 case FILE_SAVE_AS: {
1183
1184 if (trim_trailing_whitespace_on_save)
1185 current->trim_trailing_whitespace();
1186
1187 current->insert_final_newline();
1188
1189 if (convert_indent_on_save) {
1190 if (use_space_indentation) {
1191 current->convert_indent_to_spaces();
1192 } else {
1193 current->convert_indent_to_tabs();
1194 }
1195 }
1196
1197 Ref<TextFile> text_file = current->get_edited_resource();
1198 if (text_file != NULL) {
1199 file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE);
1200 file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
1201 file_dialog_option = FILE_SAVE_AS;
1202
1203 List<String> extensions;
1204 ResourceLoader::get_recognized_extensions_for_type("Script", &extensions);
1205 file_dialog->clear_filters();
1206 file_dialog->set_current_dir(text_file->get_path().get_base_dir());
1207 file_dialog->set_current_file(text_file->get_path().get_file());
1208 file_dialog->popup_centered_ratio();
1209 file_dialog->set_title(TTR("Save File As..."));
1210 break;
1211 }
1212
1213 editor->push_item(Object::cast_to<Object>(current->get_edited_resource().ptr()));
1214 editor->save_resource_as(current->get_edited_resource());
1215
1216 } break;
1217
1218 case FILE_TOOL_RELOAD:
1219 case FILE_TOOL_RELOAD_SOFT: {
1220
1221 current->reload(p_option == FILE_TOOL_RELOAD_SOFT);
1222
1223 } break;
1224 case FILE_RUN: {
1225
1226 Ref<Script> scr = current->get_edited_resource();
1227 if (scr == NULL || scr.is_null()) {
1228 EditorNode::get_singleton()->show_warning(TTR("Can't obtain the script for running."));
1229 break;
1230 }
1231
1232 current->apply_code();
1233 Error err = scr->reload(false); //hard reload script before running always
1234
1235 if (err != OK) {
1236 EditorNode::get_singleton()->show_warning(TTR("Script failed reloading, check console for errors."));
1237 return;
1238 }
1239 if (!scr->is_tool()) {
1240
1241 EditorNode::get_singleton()->show_warning(TTR("Script is not in tool mode, will not be able to run."));
1242 return;
1243 }
1244
1245 if (!ClassDB::is_parent_class(scr->get_instance_base_type(), "EditorScript")) {
1246
1247 EditorNode::get_singleton()->show_warning(TTR("To run this script, it must inherit EditorScript and be set to tool mode."));
1248 return;
1249 }
1250
1251 Ref<EditorScript> es = memnew(EditorScript);
1252 es->set_script(scr.get_ref_ptr());
1253 es->set_editor(EditorNode::get_singleton());
1254
1255 es->_run();
1256
1257 EditorNode::get_undo_redo()->clear_history();
1258 } break;
1259 case FILE_CLOSE: {
1260 if (current->is_unsaved()) {
1261 _ask_close_current_unsaved_tab(current);
1262 } else {
1263 _close_current_tab();
1264 }
1265 } break;
1266 case FILE_COPY_PATH: {
1267 _copy_script_path();
1268 } break;
1269 case SHOW_IN_FILE_SYSTEM: {
1270 const RES script = current->get_edited_resource();
1271 const String path = script->get_path();
1272 if (!path.empty()) {
1273 FileSystemDock *file_system_dock = EditorNode::get_singleton()->get_filesystem_dock();
1274 file_system_dock->navigate_to_path(path);
1275 // Ensure that the FileSystem dock is visible.
1276 TabContainer *tab_container = (TabContainer *)file_system_dock->get_parent_control();
1277 tab_container->set_current_tab(file_system_dock->get_position_in_parent());
1278 }
1279 } break;
1280 case CLOSE_DOCS: {
1281 _close_docs_tab();
1282 } break;
1283 case CLOSE_OTHER_TABS: {
1284 _close_other_tabs();
1285 } break;
1286 case CLOSE_ALL: {
1287 _close_all_tabs();
1288 } break;
1289 case DEBUG_NEXT: {
1290
1291 if (debugger)
1292 debugger->debug_next();
1293 } break;
1294 case DEBUG_STEP: {
1295
1296 if (debugger)
1297 debugger->debug_step();
1298
1299 } break;
1300 case DEBUG_BREAK: {
1301
1302 if (debugger)
1303 debugger->debug_break();
1304
1305 } break;
1306 case DEBUG_CONTINUE: {
1307
1308 if (debugger)
1309 debugger->debug_continue();
1310
1311 } break;
1312 case WINDOW_MOVE_UP: {
1313
1314 if (tab_container->get_current_tab() > 0) {
1315 tab_container->move_child(current, tab_container->get_current_tab() - 1);
1316 tab_container->set_current_tab(tab_container->get_current_tab() - 1);
1317 _update_script_names();
1318 }
1319 } break;
1320 case WINDOW_MOVE_DOWN: {
1321
1322 if (tab_container->get_current_tab() < tab_container->get_child_count() - 1) {
1323 tab_container->move_child(current, tab_container->get_current_tab() + 1);
1324 tab_container->set_current_tab(tab_container->get_current_tab() + 1);
1325 _update_script_names();
1326 }
1327 } break;
1328 default: {
1329
1330 if (p_option >= WINDOW_SELECT_BASE) {
1331
1332 tab_container->set_current_tab(p_option - WINDOW_SELECT_BASE);
1333 _update_script_names();
1334 }
1335 }
1336 }
1337 } else {
1338
1339 EditorHelp *help = Object::cast_to<EditorHelp>(tab_container->get_current_tab_control());
1340 if (help) {
1341
1342 switch (p_option) {
1343 case HELP_SEARCH_FIND: {
1344 help->popup_search();
1345 } break;
1346 case HELP_SEARCH_FIND_NEXT: {
1347 help->search_again();
1348 } break;
1349 case HELP_SEARCH_FIND_PREVIOUS: {
1350 help->search_again(true);
1351 } break;
1352 case FILE_CLOSE: {
1353 _close_current_tab();
1354 } break;
1355 case CLOSE_DOCS: {
1356 _close_docs_tab();
1357 } break;
1358 case CLOSE_OTHER_TABS: {
1359 _close_other_tabs();
1360 } break;
1361 case CLOSE_ALL: {
1362 _close_all_tabs();
1363 } break;
1364 case WINDOW_MOVE_UP: {
1365
1366 if (tab_container->get_current_tab() > 0) {
1367 tab_container->move_child(help, tab_container->get_current_tab() - 1);
1368 tab_container->set_current_tab(tab_container->get_current_tab() - 1);
1369 _update_script_names();
1370 }
1371 } break;
1372 case WINDOW_MOVE_DOWN: {
1373
1374 if (tab_container->get_current_tab() < tab_container->get_child_count() - 1) {
1375 tab_container->move_child(help, tab_container->get_current_tab() + 1);
1376 tab_container->set_current_tab(tab_container->get_current_tab() + 1);
1377 _update_script_names();
1378 }
1379 } break;
1380 }
1381 }
1382 }
1383 }
1384
_theme_option(int p_option)1385 void ScriptEditor::_theme_option(int p_option) {
1386 switch (p_option) {
1387 case THEME_IMPORT: {
1388 file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE);
1389 file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
1390 file_dialog_option = THEME_IMPORT;
1391 file_dialog->clear_filters();
1392 file_dialog->add_filter("*.tet");
1393 file_dialog->popup_centered_ratio();
1394 file_dialog->set_title(TTR("Import Theme"));
1395 } break;
1396 case THEME_RELOAD: {
1397 EditorSettings::get_singleton()->load_text_editor_theme();
1398 } break;
1399 case THEME_SAVE: {
1400 if (EditorSettings::get_singleton()->is_default_text_editor_theme()) {
1401 ScriptEditor::_show_save_theme_as_dialog();
1402 } else if (!EditorSettings::get_singleton()->save_text_editor_theme()) {
1403 editor->show_warning(TTR("Error while saving theme"), TTR("Error saving"));
1404 }
1405 } break;
1406 case THEME_SAVE_AS: {
1407 ScriptEditor::_show_save_theme_as_dialog();
1408 } break;
1409 }
1410 }
1411
_show_save_theme_as_dialog()1412 void ScriptEditor::_show_save_theme_as_dialog() {
1413 file_dialog->set_mode(EditorFileDialog::MODE_SAVE_FILE);
1414 file_dialog->set_access(EditorFileDialog::ACCESS_FILESYSTEM);
1415 file_dialog_option = THEME_SAVE_AS;
1416 file_dialog->clear_filters();
1417 file_dialog->add_filter("*.tet");
1418 file_dialog->set_current_path(EditorSettings::get_singleton()->get_text_editor_themes_dir().plus_file(EditorSettings::get_singleton()->get("text_editor/theme/color_theme")));
1419 file_dialog->popup_centered_ratio();
1420 file_dialog->set_title(TTR("Save Theme As..."));
1421 }
1422
_tab_changed(int p_which)1423 void ScriptEditor::_tab_changed(int p_which) {
1424
1425 ensure_select_current();
1426 }
1427
_notification(int p_what)1428 void ScriptEditor::_notification(int p_what) {
1429
1430 switch (p_what) {
1431
1432 case NOTIFICATION_ENTER_TREE: {
1433
1434 editor->connect("play_pressed", this, "_editor_play");
1435 editor->connect("pause_pressed", this, "_editor_pause");
1436 editor->connect("stop_pressed", this, "_editor_stop");
1437 editor->connect("script_add_function_request", this, "_add_callback");
1438 editor->connect("resource_saved", this, "_res_saved_callback");
1439 script_list->connect("item_selected", this, "_script_selected");
1440
1441 members_overview->connect("item_selected", this, "_members_overview_selected");
1442 help_overview->connect("item_selected", this, "_help_overview_selected");
1443 script_split->connect("dragged", this, "_script_split_dragged");
1444
1445 EditorSettings::get_singleton()->connect("settings_changed", this, "_editor_settings_changed");
1446 FALLTHROUGH;
1447 }
1448 case NOTIFICATION_THEME_CHANGED: {
1449
1450 help_search->set_icon(get_icon("HelpSearch", "EditorIcons"));
1451 site_search->set_icon(get_icon("Instance", "EditorIcons"));
1452
1453 script_forward->set_icon(get_icon("Forward", "EditorIcons"));
1454 script_back->set_icon(get_icon("Back", "EditorIcons"));
1455
1456 members_overview_alphabeta_sort_button->set_icon(get_icon("Sort", "EditorIcons"));
1457
1458 filter_scripts->set_right_icon(get_icon("Search", "EditorIcons"));
1459 filter_methods->set_right_icon(get_icon("Search", "EditorIcons"));
1460
1461 filename->add_style_override("normal", editor->get_gui_base()->get_stylebox("normal", "LineEdit"));
1462
1463 recent_scripts->set_as_minsize();
1464 } break;
1465
1466 case NOTIFICATION_READY: {
1467
1468 get_tree()->connect("tree_changed", this, "_tree_changed");
1469 editor->get_inspector_dock()->connect("request_help", this, "_request_help");
1470 editor->connect("request_help_search", this, "_help_search");
1471 } break;
1472
1473 case NOTIFICATION_EXIT_TREE: {
1474
1475 editor->disconnect("play_pressed", this, "_editor_play");
1476 editor->disconnect("pause_pressed", this, "_editor_pause");
1477 editor->disconnect("stop_pressed", this, "_editor_stop");
1478 } break;
1479
1480 case MainLoop::NOTIFICATION_WM_FOCUS_IN: {
1481
1482 _test_script_times_on_disk();
1483 _update_modified_scripts_for_external_editor();
1484 } break;
1485
1486 case CanvasItem::NOTIFICATION_VISIBILITY_CHANGED: {
1487
1488 if (is_visible()) {
1489 find_in_files_button->show();
1490 } else {
1491 if (find_in_files->is_visible_in_tree()) {
1492 editor->hide_bottom_panel();
1493 }
1494 find_in_files_button->hide();
1495 }
1496
1497 } break;
1498
1499 default:
1500 break;
1501 }
1502 }
1503
can_take_away_focus() const1504 bool ScriptEditor::can_take_away_focus() const {
1505
1506 ScriptEditorBase *current = _get_current_editor();
1507 if (current)
1508 return current->can_lose_focus_on_node_selection();
1509 else
1510 return true;
1511 }
1512
close_builtin_scripts_from_scene(const String & p_scene)1513 void ScriptEditor::close_builtin_scripts_from_scene(const String &p_scene) {
1514
1515 for (int i = 0; i < tab_container->get_child_count(); i++) {
1516
1517 ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
1518
1519 if (se) {
1520
1521 Ref<Script> script = se->get_edited_resource();
1522 if (script == NULL || !script.is_valid())
1523 continue;
1524
1525 if (script->get_path().find("::") != -1 && script->get_path().begins_with(p_scene)) { //is an internal script and belongs to scene being closed
1526 _close_tab(i);
1527 i--;
1528 }
1529 }
1530 }
1531 }
1532
edited_scene_changed()1533 void ScriptEditor::edited_scene_changed() {
1534
1535 _update_modified_scripts_for_external_editor();
1536 }
1537
notify_script_close(const Ref<Script> & p_script)1538 void ScriptEditor::notify_script_close(const Ref<Script> &p_script) {
1539 emit_signal("script_close", p_script);
1540 }
1541
notify_script_changed(const Ref<Script> & p_script)1542 void ScriptEditor::notify_script_changed(const Ref<Script> &p_script) {
1543 emit_signal("editor_script_changed", p_script);
1544 }
1545
get_breakpoints(List<String> * p_breakpoints)1546 void ScriptEditor::get_breakpoints(List<String> *p_breakpoints) {
1547
1548 for (int i = 0; i < tab_container->get_child_count(); i++) {
1549
1550 ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
1551 if (!se)
1552 continue;
1553
1554 Ref<Script> script = se->get_edited_resource();
1555 if (script == NULL) {
1556 continue;
1557 }
1558
1559 List<int> bpoints;
1560 se->get_breakpoints(&bpoints);
1561 String base = script->get_path();
1562 if (base.begins_with("local://") || base == "") {
1563 continue;
1564 }
1565
1566 for (List<int>::Element *E = bpoints.front(); E; E = E->next()) {
1567
1568 p_breakpoints->push_back(base + ":" + itos(E->get() + 1));
1569 }
1570 }
1571 }
1572
ensure_focus_current()1573 void ScriptEditor::ensure_focus_current() {
1574
1575 if (!is_inside_tree())
1576 return;
1577
1578 ScriptEditorBase *current = _get_current_editor();
1579 if (current)
1580 current->ensure_focus();
1581 }
1582
_members_overview_selected(int p_idx)1583 void ScriptEditor::_members_overview_selected(int p_idx) {
1584 ScriptEditorBase *se = _get_current_editor();
1585 if (!se) {
1586 return;
1587 }
1588 // Go to the member's line and reset the cursor column. We can't change scroll_position
1589 // directly until we have gone to the line first, since code might be folded.
1590 se->goto_line(members_overview->get_item_metadata(p_idx));
1591 Dictionary state = se->get_edit_state();
1592 state["column"] = 0;
1593 state["scroll_position"] = members_overview->get_item_metadata(p_idx);
1594 se->set_edit_state(state);
1595 }
1596
_help_overview_selected(int p_idx)1597 void ScriptEditor::_help_overview_selected(int p_idx) {
1598 Node *current = tab_container->get_child(tab_container->get_current_tab());
1599 EditorHelp *se = Object::cast_to<EditorHelp>(current);
1600 if (!se) {
1601 return;
1602 }
1603 se->scroll_to_section(help_overview->get_item_metadata(p_idx));
1604 }
1605
_script_selected(int p_idx)1606 void ScriptEditor::_script_selected(int p_idx) {
1607
1608 grab_focus_block = !Input::get_singleton()->is_mouse_button_pressed(1); //amazing hack, simply amazing
1609
1610 _go_to_tab(script_list->get_item_metadata(p_idx));
1611 grab_focus_block = false;
1612 }
1613
ensure_select_current()1614 void ScriptEditor::ensure_select_current() {
1615
1616 if (tab_container->get_child_count() && tab_container->get_current_tab() >= 0) {
1617
1618 ScriptEditorBase *se = _get_current_editor();
1619 if (se) {
1620
1621 if (!grab_focus_block && is_visible_in_tree())
1622 se->ensure_focus();
1623 }
1624 }
1625
1626 _update_selected_editor_menu();
1627 }
1628
_find_scripts(Node * p_base,Node * p_current,Set<Ref<Script>> & used)1629 void ScriptEditor::_find_scripts(Node *p_base, Node *p_current, Set<Ref<Script> > &used) {
1630 if (p_current != p_base && p_current->get_owner() != p_base)
1631 return;
1632
1633 if (p_current->get_script_instance()) {
1634 Ref<Script> scr = p_current->get_script();
1635 if (scr.is_valid())
1636 used.insert(scr);
1637 }
1638
1639 for (int i = 0; i < p_current->get_child_count(); i++) {
1640 _find_scripts(p_base, p_current->get_child(i), used);
1641 }
1642 }
1643
1644 struct _ScriptEditorItemData {
1645
1646 String name;
1647 String sort_key;
1648 Ref<Texture> icon;
1649 int index;
1650 String tooltip;
1651 bool used;
1652 int category;
1653 Node *ref;
1654
operator <_ScriptEditorItemData1655 bool operator<(const _ScriptEditorItemData &id) const {
1656
1657 if (category == id.category) {
1658 if (sort_key == id.sort_key) {
1659 return index < id.index;
1660 } else {
1661 return sort_key < id.sort_key;
1662 }
1663 } else {
1664 return category < id.category;
1665 }
1666 }
1667 };
1668
_update_members_overview_visibility()1669 void ScriptEditor::_update_members_overview_visibility() {
1670
1671 ScriptEditorBase *se = _get_current_editor();
1672 if (!se) {
1673 members_overview_alphabeta_sort_button->set_visible(false);
1674 members_overview->set_visible(false);
1675 overview_vbox->set_visible(false);
1676 return;
1677 }
1678
1679 if (members_overview_enabled && se->show_members_overview()) {
1680 members_overview_alphabeta_sort_button->set_visible(true);
1681 members_overview->set_visible(true);
1682 overview_vbox->set_visible(true);
1683 } else {
1684 members_overview_alphabeta_sort_button->set_visible(false);
1685 members_overview->set_visible(false);
1686 overview_vbox->set_visible(false);
1687 }
1688 }
1689
_toggle_members_overview_alpha_sort(bool p_alphabetic_sort)1690 void ScriptEditor::_toggle_members_overview_alpha_sort(bool p_alphabetic_sort) {
1691 EditorSettings::get_singleton()->set("text_editor/tools/sort_members_outline_alphabetically", p_alphabetic_sort);
1692 _update_members_overview();
1693 }
1694
_update_members_overview()1695 void ScriptEditor::_update_members_overview() {
1696 members_overview->clear();
1697
1698 ScriptEditorBase *se = _get_current_editor();
1699 if (!se) {
1700 return;
1701 }
1702
1703 Vector<String> functions = se->get_functions();
1704 if (EditorSettings::get_singleton()->get("text_editor/tools/sort_members_outline_alphabetically")) {
1705 functions.sort();
1706 }
1707
1708 for (int i = 0; i < functions.size(); i++) {
1709 String filter = filter_methods->get_text();
1710 String name = functions[i].get_slice(":", 0);
1711 if (filter == "" || filter.is_subsequence_ofi(name)) {
1712 members_overview->add_item(name);
1713 members_overview->set_item_metadata(members_overview->get_item_count() - 1, functions[i].get_slice(":", 1).to_int() - 1);
1714 }
1715 }
1716
1717 String path = se->get_edited_resource()->get_path();
1718 bool built_in = !path.is_resource_file();
1719 String name = built_in ? path.get_file() : se->get_name();
1720 filename->set_text(name);
1721 }
1722
_update_help_overview_visibility()1723 void ScriptEditor::_update_help_overview_visibility() {
1724
1725 int selected = tab_container->get_current_tab();
1726 if (selected < 0 || selected >= tab_container->get_child_count()) {
1727 help_overview->set_visible(false);
1728 return;
1729 }
1730
1731 Node *current = tab_container->get_child(tab_container->get_current_tab());
1732 EditorHelp *se = Object::cast_to<EditorHelp>(current);
1733 if (!se) {
1734 help_overview->set_visible(false);
1735 return;
1736 }
1737
1738 if (help_overview_enabled) {
1739 members_overview_alphabeta_sort_button->set_visible(false);
1740 help_overview->set_visible(true);
1741 overview_vbox->set_visible(true);
1742 filename->set_text(se->get_name());
1743 } else {
1744 help_overview->set_visible(false);
1745 overview_vbox->set_visible(false);
1746 }
1747 }
1748
_update_help_overview()1749 void ScriptEditor::_update_help_overview() {
1750 help_overview->clear();
1751
1752 int selected = tab_container->get_current_tab();
1753 if (selected < 0 || selected >= tab_container->get_child_count())
1754 return;
1755
1756 Node *current = tab_container->get_child(tab_container->get_current_tab());
1757 EditorHelp *se = Object::cast_to<EditorHelp>(current);
1758 if (!se) {
1759 return;
1760 }
1761
1762 Vector<Pair<String, int> > sections = se->get_sections();
1763 for (int i = 0; i < sections.size(); i++) {
1764 help_overview->add_item(sections[i].first);
1765 help_overview->set_item_metadata(i, sections[i].second);
1766 }
1767 }
1768
_update_script_colors()1769 void ScriptEditor::_update_script_colors() {
1770
1771 bool script_temperature_enabled = EditorSettings::get_singleton()->get("text_editor/script_list/script_temperature_enabled");
1772 bool highlight_current = EditorSettings::get_singleton()->get("text_editor/script_list/highlight_current_script");
1773
1774 int hist_size = EditorSettings::get_singleton()->get("text_editor/script_list/script_temperature_history_size");
1775 Color hot_color = get_color("accent_color", "Editor");
1776 Color cold_color = get_color("font_color", "Editor");
1777
1778 for (int i = 0; i < script_list->get_item_count(); i++) {
1779
1780 int c = script_list->get_item_metadata(i);
1781 Node *n = tab_container->get_child(c);
1782 if (!n)
1783 continue;
1784
1785 script_list->set_item_custom_bg_color(i, Color(0, 0, 0, 0));
1786
1787 bool current = tab_container->get_current_tab() == c;
1788 if (current && highlight_current) {
1789 script_list->set_item_custom_bg_color(i, EditorSettings::get_singleton()->get("text_editor/script_list/current_script_background_color"));
1790
1791 } else if (script_temperature_enabled) {
1792
1793 if (!n->has_meta("__editor_pass")) {
1794 continue;
1795 }
1796
1797 int pass = n->get_meta("__editor_pass");
1798 int h = edit_pass - pass;
1799 if (h > hist_size) {
1800 continue;
1801 }
1802 int non_zero_hist_size = (hist_size == 0) ? 1 : hist_size;
1803 float v = Math::ease((edit_pass - pass) / float(non_zero_hist_size), 0.4);
1804
1805 script_list->set_item_custom_fg_color(i, hot_color.linear_interpolate(cold_color, v));
1806 }
1807 }
1808 }
1809
_update_script_names()1810 void ScriptEditor::_update_script_names() {
1811
1812 if (restoring_layout)
1813 return;
1814
1815 Set<Ref<Script> > used;
1816 Node *edited = EditorNode::get_singleton()->get_edited_scene();
1817 if (edited) {
1818 _find_scripts(edited, edited, used);
1819 }
1820
1821 script_list->clear();
1822 bool split_script_help = EditorSettings::get_singleton()->get("text_editor/script_list/group_help_pages");
1823 ScriptSortBy sort_by = (ScriptSortBy)(int)EditorSettings::get_singleton()->get("text_editor/script_list/sort_scripts_by");
1824 ScriptListName display_as = (ScriptListName)(int)EditorSettings::get_singleton()->get("text_editor/script_list/list_script_names_as");
1825
1826 Vector<_ScriptEditorItemData> sedata;
1827
1828 for (int i = 0; i < tab_container->get_child_count(); i++) {
1829
1830 ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
1831 if (se) {
1832
1833 Ref<Texture> icon = se->get_icon();
1834 String path = se->get_edited_resource()->get_path();
1835 bool built_in = !path.is_resource_file();
1836 String name;
1837
1838 if (built_in) {
1839
1840 name = path.get_file();
1841 const String &resource_name = se->get_edited_resource()->get_name();
1842 if (resource_name != "") {
1843 // If the built-in script has a custom resource name defined,
1844 // display the built-in script name as follows: `ResourceName (scene_file.tscn)`
1845 name = vformat("%s (%s)", resource_name, name.substr(0, name.find("::", 0)));
1846 }
1847 } else {
1848
1849 name = se->get_name();
1850 }
1851
1852 _ScriptEditorItemData sd;
1853 sd.icon = icon;
1854 sd.name = name;
1855 sd.tooltip = path;
1856 sd.index = i;
1857 sd.used = used.has(se->get_edited_resource());
1858 sd.category = 0;
1859 sd.ref = se;
1860
1861 switch (sort_by) {
1862 case SORT_BY_NAME: {
1863 sd.sort_key = name.to_lower();
1864 } break;
1865 case SORT_BY_PATH: {
1866 sd.sort_key = path;
1867 } break;
1868 case SORT_BY_NONE: {
1869 sd.sort_key = "";
1870 } break;
1871 }
1872
1873 switch (display_as) {
1874 case DISPLAY_NAME: {
1875 sd.name = name;
1876 } break;
1877 case DISPLAY_DIR_AND_NAME: {
1878 if (!path.get_base_dir().get_file().empty()) {
1879 sd.name = path.get_base_dir().get_file().plus_file(name);
1880 } else {
1881 sd.name = name;
1882 }
1883 } break;
1884 case DISPLAY_FULL_PATH: {
1885 sd.name = path;
1886 } break;
1887 }
1888
1889 sedata.push_back(sd);
1890 }
1891
1892 Vector<String> disambiguated_script_names;
1893 Vector<String> full_script_paths;
1894 for (int j = 0; j < sedata.size(); j++) {
1895 disambiguated_script_names.push_back(sedata[j].name.replace("(*)", ""));
1896 full_script_paths.push_back(sedata[j].tooltip);
1897 }
1898
1899 EditorNode::disambiguate_filenames(full_script_paths, disambiguated_script_names);
1900
1901 for (int j = 0; j < sedata.size(); j++) {
1902 if (sedata[j].name.ends_with("(*)")) {
1903 sedata.write[j].name = disambiguated_script_names[j] + "(*)";
1904 } else {
1905 sedata.write[j].name = disambiguated_script_names[j];
1906 }
1907 }
1908
1909 EditorHelp *eh = Object::cast_to<EditorHelp>(tab_container->get_child(i));
1910 if (eh) {
1911
1912 String name = eh->get_class();
1913 Ref<Texture> icon = get_icon("Help", "EditorIcons");
1914 String tooltip = vformat(TTR("%s Class Reference"), name);
1915
1916 _ScriptEditorItemData sd;
1917 sd.icon = icon;
1918 sd.name = name;
1919 sd.sort_key = name.to_lower();
1920 sd.tooltip = tooltip;
1921 sd.index = i;
1922 sd.used = false;
1923 sd.category = split_script_help ? 1 : 0;
1924 sd.ref = eh;
1925
1926 sedata.push_back(sd);
1927 }
1928 }
1929
1930 if (_sort_list_on_update && !sedata.empty()) {
1931 sedata.sort();
1932
1933 // change actual order of tab_container so that the order can be rearranged by user
1934 int cur_tab = tab_container->get_current_tab();
1935 int prev_tab = tab_container->get_previous_tab();
1936 int new_cur_tab = -1;
1937 int new_prev_tab = -1;
1938 for (int i = 0; i < sedata.size(); i++) {
1939 tab_container->move_child(sedata[i].ref, i);
1940 if (new_prev_tab == -1 && sedata[i].index == prev_tab) {
1941 new_prev_tab = i;
1942 }
1943 if (new_cur_tab == -1 && sedata[i].index == cur_tab) {
1944 new_cur_tab = i;
1945 }
1946 // Update index of sd entries for sorted order
1947 _ScriptEditorItemData sd = sedata[i];
1948 sd.index = i;
1949 sedata.set(i, sd);
1950 }
1951 tab_container->set_current_tab(new_prev_tab);
1952 tab_container->set_current_tab(new_cur_tab);
1953 _sort_list_on_update = false;
1954 }
1955
1956 Vector<_ScriptEditorItemData> sedata_filtered;
1957 for (int i = 0; i < sedata.size(); i++) {
1958 String filter = filter_scripts->get_text();
1959 if (filter == "" || filter.is_subsequence_ofi(sedata[i].name)) {
1960 sedata_filtered.push_back(sedata[i]);
1961 }
1962 }
1963
1964 for (int i = 0; i < sedata_filtered.size(); i++) {
1965 script_list->add_item(sedata_filtered[i].name, sedata_filtered[i].icon);
1966 int index = script_list->get_item_count() - 1;
1967 script_list->set_item_tooltip(index, sedata_filtered[i].tooltip);
1968 script_list->set_item_metadata(index, sedata_filtered[i].index); /* Saving as metadata the script's index in the tab container and not the filtered one */
1969 if (sedata_filtered[i].used) {
1970 script_list->set_item_custom_bg_color(index, Color(88 / 255.0, 88 / 255.0, 60 / 255.0));
1971 }
1972 if (tab_container->get_current_tab() == sedata_filtered[i].index) {
1973 script_list->select(index);
1974 script_name_label->set_text(sedata_filtered[i].name);
1975 script_icon->set_texture(sedata_filtered[i].icon);
1976 }
1977 }
1978
1979 if (!waiting_update_names) {
1980 _update_members_overview();
1981 _update_help_overview();
1982 } else {
1983 waiting_update_names = false;
1984 }
1985 _update_members_overview_visibility();
1986 _update_help_overview_visibility();
1987 _update_script_colors();
1988
1989 file_menu->get_popup()->set_item_disabled(file_menu->get_popup()->get_item_index(FILE_REOPEN_CLOSED), previous_scripts.empty());
1990 }
1991
_update_script_connections()1992 void ScriptEditor::_update_script_connections() {
1993 for (int i = 0; i < tab_container->get_child_count(); i++) {
1994 ScriptTextEditor *ste = Object::cast_to<ScriptTextEditor>(tab_container->get_child(i));
1995 if (!ste) {
1996 continue;
1997 }
1998 ste->_update_connected_methods();
1999 }
2000 }
2001
_load_text_file(const String & p_path,Error * r_error)2002 Ref<TextFile> ScriptEditor::_load_text_file(const String &p_path, Error *r_error) {
2003 if (r_error) {
2004 *r_error = ERR_FILE_CANT_OPEN;
2005 }
2006
2007 String local_path = ProjectSettings::get_singleton()->localize_path(p_path);
2008 String path = ResourceLoader::path_remap(local_path);
2009
2010 TextFile *text_file = memnew(TextFile);
2011 Ref<TextFile> text_res(text_file);
2012 Error err = text_file->load_text(path);
2013
2014 ERR_FAIL_COND_V_MSG(err != OK, RES(), "Cannot load text file '" + path + "'.");
2015
2016 text_file->set_file_path(local_path);
2017 text_file->set_path(local_path, true);
2018
2019 if (ResourceLoader::get_timestamp_on_load()) {
2020 text_file->set_last_modified_time(FileAccess::get_modified_time(path));
2021 }
2022
2023 if (r_error) {
2024 *r_error = OK;
2025 }
2026
2027 return text_res;
2028 }
2029
_save_text_file(Ref<TextFile> p_text_file,const String & p_path)2030 Error ScriptEditor::_save_text_file(Ref<TextFile> p_text_file, const String &p_path) {
2031 Ref<TextFile> sqscr = p_text_file;
2032 ERR_FAIL_COND_V(sqscr.is_null(), ERR_INVALID_PARAMETER);
2033
2034 String source = sqscr->get_text();
2035
2036 Error err;
2037 FileAccess *file = FileAccess::open(p_path, FileAccess::WRITE, &err);
2038
2039 ERR_FAIL_COND_V_MSG(err, err, "Cannot save text file '" + p_path + "'.");
2040
2041 file->store_string(source);
2042 if (file->get_error() != OK && file->get_error() != ERR_FILE_EOF) {
2043 memdelete(file);
2044 return ERR_CANT_CREATE;
2045 }
2046 file->close();
2047 memdelete(file);
2048
2049 if (ResourceSaver::get_timestamp_on_save()) {
2050 p_text_file->set_last_modified_time(FileAccess::get_modified_time(p_path));
2051 }
2052
2053 _res_saved_callback(sqscr);
2054 return OK;
2055 }
2056
edit(const RES & p_resource,int p_line,int p_col,bool p_grab_focus)2057 bool ScriptEditor::edit(const RES &p_resource, int p_line, int p_col, bool p_grab_focus) {
2058
2059 if (p_resource.is_null())
2060 return false;
2061
2062 Ref<Script> script = p_resource;
2063
2064 // Don't open dominant script if using an external editor.
2065 const bool use_external_editor =
2066 EditorSettings::get_singleton()->get("text_editor/external/use_external_editor") ||
2067 (script.is_valid() && script->get_language()->overrides_external_editor());
2068 const bool open_dominant = EditorSettings::get_singleton()->get("text_editor/files/open_dominant_script_on_scene_change");
2069
2070 const bool should_open = (open_dominant && !use_external_editor) || !EditorNode::get_singleton()->is_changing_scene();
2071
2072 if (script.is_valid() && script->get_language()->overrides_external_editor()) {
2073 if (should_open) {
2074 Error err = script->get_language()->open_in_external_editor(script, p_line >= 0 ? p_line : 0, p_col);
2075 if (err != OK)
2076 ERR_PRINT("Couldn't open script in the overridden external text editor");
2077 }
2078 return false;
2079 }
2080
2081 if (use_external_editor &&
2082 (debugger->get_dump_stack_script() != p_resource || debugger->get_debug_with_external_editor()) &&
2083 p_resource->get_path().is_resource_file() &&
2084 p_resource->get_class_name() != StringName("VisualScript")) {
2085 String path = EditorSettings::get_singleton()->get("text_editor/external/exec_path");
2086 String flags = EditorSettings::get_singleton()->get("text_editor/external/exec_flags");
2087
2088 List<String> args;
2089 bool has_file_flag = false;
2090 String script_path = ProjectSettings::get_singleton()->globalize_path(p_resource->get_path());
2091
2092 if (flags.size()) {
2093 String project_path = ProjectSettings::get_singleton()->get_resource_path();
2094
2095 flags = flags.replacen("{line}", itos(p_line > 0 ? p_line : 0));
2096 flags = flags.replacen("{col}", itos(p_col));
2097 flags = flags.strip_edges().replace("\\\\", "\\");
2098
2099 int from = 0;
2100 int num_chars = 0;
2101 bool inside_quotes = false;
2102
2103 for (int i = 0; i < flags.size(); i++) {
2104
2105 if (flags[i] == '"' && (!i || flags[i - 1] != '\\')) {
2106
2107 if (!inside_quotes) {
2108 from++;
2109 }
2110 inside_quotes = !inside_quotes;
2111
2112 } else if (flags[i] == '\0' || (!inside_quotes && flags[i] == ' ')) {
2113
2114 String arg = flags.substr(from, num_chars);
2115 if (arg.find("{file}") != -1) {
2116 has_file_flag = true;
2117 }
2118
2119 // do path replacement here, else there will be issues with spaces and quotes
2120 arg = arg.replacen("{project}", project_path);
2121 arg = arg.replacen("{file}", script_path);
2122 args.push_back(arg);
2123
2124 from = i + 1;
2125 num_chars = 0;
2126 } else {
2127 num_chars++;
2128 }
2129 }
2130 }
2131
2132 // Default to passing script path if no {file} flag is specified.
2133 if (!has_file_flag) {
2134 args.push_back(script_path);
2135 }
2136
2137 Error err = OS::get_singleton()->execute(path, args, false);
2138 if (err == OK)
2139 return false;
2140 WARN_PRINT("Couldn't open external text editor, using internal");
2141 }
2142
2143 for (int i = 0; i < tab_container->get_child_count(); i++) {
2144
2145 ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
2146 if (!se)
2147 continue;
2148
2149 if ((script != NULL && se->get_edited_resource() == p_resource) || se->get_edited_resource()->get_path() == p_resource->get_path()) {
2150
2151 if (should_open) {
2152 if (tab_container->get_current_tab() != i) {
2153 _go_to_tab(i);
2154 _update_script_names();
2155 }
2156 if (is_visible_in_tree())
2157 se->ensure_focus();
2158
2159 if (p_line > 0) {
2160 se->goto_line(p_line - 1);
2161 }
2162 }
2163 _update_script_names();
2164 script_list->ensure_current_is_visible();
2165 return true;
2166 }
2167 }
2168
2169 // doesn't have it, make a new one
2170
2171 ScriptEditorBase *se = NULL;
2172
2173 for (int i = script_editor_func_count - 1; i >= 0; i--) {
2174 se = script_editor_funcs[i](p_resource);
2175 if (se)
2176 break;
2177 }
2178 ERR_FAIL_COND_V(!se, false);
2179
2180 if (p_resource->get_class_name() != StringName("VisualScript")) {
2181 bool highlighter_set = false;
2182 for (int i = 0; i < syntax_highlighters_func_count; i++) {
2183 SyntaxHighlighter *highlighter = syntax_highlighters_funcs[i]();
2184 se->add_syntax_highlighter(highlighter);
2185
2186 if (script != NULL && !highlighter_set) {
2187 List<String> languages = highlighter->get_supported_languages();
2188 if (languages.find(script->get_language()->get_name())) {
2189 se->set_syntax_highlighter(highlighter);
2190 highlighter_set = true;
2191 }
2192 }
2193 }
2194 }
2195
2196 tab_container->add_child(se);
2197 se->set_edited_resource(p_resource);
2198 se->set_tooltip_request_func("_get_debug_tooltip", this);
2199 if (se->get_edit_menu()) {
2200 se->get_edit_menu()->hide();
2201 menu_hb->add_child(se->get_edit_menu());
2202 menu_hb->move_child(se->get_edit_menu(), 1);
2203 }
2204
2205 if (p_grab_focus) {
2206 _go_to_tab(tab_container->get_tab_count() - 1);
2207 }
2208
2209 _sort_list_on_update = true;
2210 _update_script_names();
2211 _save_layout();
2212 se->connect("name_changed", this, "_update_script_names");
2213 se->connect("edited_script_changed", this, "_script_changed");
2214 se->connect("request_help", this, "_help_search");
2215 se->connect("request_open_script_at_line", this, "_goto_script_line");
2216 se->connect("go_to_help", this, "_help_class_goto");
2217 se->connect("request_save_history", this, "_save_history");
2218 se->connect("search_in_files_requested", this, "_on_find_in_files_requested");
2219
2220 //test for modification, maybe the script was not edited but was loaded
2221
2222 _test_script_times_on_disk(p_resource);
2223 _update_modified_scripts_for_external_editor(p_resource);
2224
2225 if (p_line > 0) {
2226 se->goto_line(p_line - 1);
2227 }
2228
2229 notify_script_changed(p_resource);
2230 _add_recent_script(p_resource->get_path());
2231 return true;
2232 }
2233
save_all_scripts()2234 void ScriptEditor::save_all_scripts() {
2235
2236 for (int i = 0; i < tab_container->get_child_count(); i++) {
2237
2238 ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
2239 if (!se)
2240 continue;
2241
2242 if (convert_indent_on_save) {
2243 if (use_space_indentation) {
2244 se->convert_indent_to_spaces();
2245 } else {
2246 se->convert_indent_to_tabs();
2247 }
2248 }
2249
2250 if (trim_trailing_whitespace_on_save) {
2251 se->trim_trailing_whitespace();
2252 }
2253
2254 se->insert_final_newline();
2255
2256 if (!se->is_unsaved())
2257 continue;
2258
2259 RES edited_res = se->get_edited_resource();
2260 if (edited_res.is_valid()) {
2261 se->apply_code();
2262 }
2263
2264 if (edited_res->get_path() != "" && edited_res->get_path().find("local://") == -1 && edited_res->get_path().find("::") == -1) {
2265 Ref<TextFile> text_file = edited_res;
2266 if (text_file != NULL) {
2267 _save_text_file(text_file, text_file->get_path());
2268 continue;
2269 }
2270 editor->save_resource(edited_res); //external script, save it
2271 }
2272 }
2273
2274 _update_script_names();
2275 EditorFileSystem::get_singleton()->update_script_classes();
2276 }
2277
apply_scripts() const2278 void ScriptEditor::apply_scripts() const {
2279
2280 for (int i = 0; i < tab_container->get_child_count(); i++) {
2281
2282 ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
2283 if (!se)
2284 continue;
2285 se->apply_code();
2286 }
2287 }
2288
open_script_create_dialog(const String & p_base_name,const String & p_base_path)2289 void ScriptEditor::open_script_create_dialog(const String &p_base_name, const String &p_base_path) {
2290 _menu_option(FILE_NEW);
2291 script_create_dialog->config(p_base_name, p_base_path);
2292 }
2293
_editor_play()2294 void ScriptEditor::_editor_play() {
2295
2296 debugger->start();
2297 debug_menu->get_popup()->grab_focus();
2298 debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_NEXT), true);
2299 debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_STEP), true);
2300 debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_BREAK), false);
2301 debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), true);
2302 }
2303
_editor_pause()2304 void ScriptEditor::_editor_pause() {
2305 }
_editor_stop()2306 void ScriptEditor::_editor_stop() {
2307
2308 debugger->stop();
2309 debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_NEXT), true);
2310 debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_STEP), true);
2311 debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_BREAK), true);
2312 debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), true);
2313
2314 for (int i = 0; i < tab_container->get_child_count(); i++) {
2315
2316 ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
2317 if (!se) {
2318
2319 continue;
2320 }
2321
2322 se->set_debugger_active(false);
2323 }
2324 }
2325
_add_callback(Object * p_obj,const String & p_function,const PoolStringArray & p_args)2326 void ScriptEditor::_add_callback(Object *p_obj, const String &p_function, const PoolStringArray &p_args) {
2327
2328 ERR_FAIL_COND(!p_obj);
2329 Ref<Script> script = p_obj->get_script();
2330 ERR_FAIL_COND(!script.is_valid());
2331
2332 editor->push_item(script.ptr());
2333
2334 for (int i = 0; i < tab_container->get_child_count(); i++) {
2335
2336 ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
2337 if (!se)
2338 continue;
2339 if (se->get_edited_resource() != script)
2340 continue;
2341
2342 se->add_callback(p_function, p_args);
2343
2344 _go_to_tab(i);
2345
2346 script_list->select(script_list->find_metadata(i));
2347
2348 break;
2349 }
2350 }
2351
_save_layout()2352 void ScriptEditor::_save_layout() {
2353
2354 if (restoring_layout) {
2355 return;
2356 }
2357
2358 editor->save_layout();
2359 }
2360
_editor_settings_changed()2361 void ScriptEditor::_editor_settings_changed() {
2362
2363 trim_trailing_whitespace_on_save = EditorSettings::get_singleton()->get("text_editor/files/trim_trailing_whitespace_on_save");
2364 convert_indent_on_save = EditorSettings::get_singleton()->get("text_editor/indent/convert_indent_on_save");
2365 use_space_indentation = EditorSettings::get_singleton()->get("text_editor/indent/type");
2366
2367 members_overview_enabled = EditorSettings::get_singleton()->get("text_editor/script_list/show_members_overview");
2368 help_overview_enabled = EditorSettings::get_singleton()->get("text_editor/help/show_help_index");
2369 _update_members_overview_visibility();
2370 _update_help_overview_visibility();
2371
2372 _update_autosave_timer();
2373
2374 if (current_theme == "") {
2375 current_theme = EditorSettings::get_singleton()->get("text_editor/theme/color_theme");
2376 } else if (current_theme != EditorSettings::get_singleton()->get("text_editor/theme/color_theme")) {
2377 current_theme = EditorSettings::get_singleton()->get("text_editor/theme/color_theme");
2378 EditorSettings::get_singleton()->load_text_editor_theme();
2379 }
2380
2381 for (int i = 0; i < tab_container->get_child_count(); i++) {
2382
2383 ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
2384 if (!se)
2385 continue;
2386
2387 se->update_settings();
2388 }
2389 _update_script_colors();
2390 _update_script_names();
2391
2392 ScriptServer::set_reload_scripts_on_save(EDITOR_DEF("text_editor/files/auto_reload_and_parse_scripts_on_save", true));
2393 }
2394
_autosave_scripts()2395 void ScriptEditor::_autosave_scripts() {
2396
2397 save_all_scripts();
2398 }
2399
_update_autosave_timer()2400 void ScriptEditor::_update_autosave_timer() {
2401
2402 if (!autosave_timer->is_inside_tree()) {
2403 return;
2404 }
2405
2406 float autosave_time = EditorSettings::get_singleton()->get("text_editor/files/autosave_interval_secs");
2407 if (autosave_time > 0) {
2408 autosave_timer->set_wait_time(autosave_time);
2409 autosave_timer->start();
2410 } else {
2411 autosave_timer->stop();
2412 }
2413 }
2414
_tree_changed()2415 void ScriptEditor::_tree_changed() {
2416
2417 if (waiting_update_names)
2418 return;
2419
2420 waiting_update_names = true;
2421 call_deferred("_update_script_names");
2422 call_deferred("_update_script_connections");
2423 }
2424
_script_split_dragged(float)2425 void ScriptEditor::_script_split_dragged(float) {
2426
2427 _save_layout();
2428 }
2429
get_drag_data_fw(const Point2 & p_point,Control * p_from)2430 Variant ScriptEditor::get_drag_data_fw(const Point2 &p_point, Control *p_from) {
2431
2432 if (tab_container->get_child_count() == 0)
2433 return Variant();
2434
2435 Node *cur_node = tab_container->get_child(tab_container->get_current_tab());
2436
2437 HBoxContainer *drag_preview = memnew(HBoxContainer);
2438 String preview_name = "";
2439 Ref<Texture> preview_icon;
2440
2441 ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(cur_node);
2442 if (se) {
2443 preview_name = se->get_name();
2444 preview_icon = se->get_icon();
2445 }
2446 EditorHelp *eh = Object::cast_to<EditorHelp>(cur_node);
2447 if (eh) {
2448 preview_name = eh->get_class();
2449 preview_icon = get_icon("Help", "EditorIcons");
2450 }
2451
2452 if (!preview_icon.is_null()) {
2453 TextureRect *tf = memnew(TextureRect);
2454 tf->set_texture(preview_icon);
2455 drag_preview->add_child(tf);
2456 }
2457 Label *label = memnew(Label(preview_name));
2458 drag_preview->add_child(label);
2459 set_drag_preview(drag_preview);
2460
2461 Dictionary drag_data;
2462 drag_data["type"] = "script_list_element"; // using a custom type because node caused problems when dragging to scene tree
2463 drag_data["script_list_element"] = cur_node;
2464
2465 return drag_data;
2466 }
2467
can_drop_data_fw(const Point2 & p_point,const Variant & p_data,Control * p_from) const2468 bool ScriptEditor::can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const {
2469
2470 Dictionary d = p_data;
2471 if (!d.has("type"))
2472 return false;
2473
2474 if (String(d["type"]) == "script_list_element") {
2475
2476 Node *node = d["script_list_element"];
2477
2478 ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(node);
2479 if (se) {
2480 return true;
2481 }
2482 EditorHelp *eh = Object::cast_to<EditorHelp>(node);
2483 if (eh) {
2484 return true;
2485 }
2486 }
2487
2488 if (String(d["type"]) == "nodes") {
2489
2490 Array nodes = d["nodes"];
2491 if (nodes.size() == 0)
2492 return false;
2493 Node *node = get_node((nodes[0]));
2494
2495 ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(node);
2496 if (se) {
2497 return true;
2498 }
2499 EditorHelp *eh = Object::cast_to<EditorHelp>(node);
2500 if (eh) {
2501 return true;
2502 }
2503 }
2504
2505 if (String(d["type"]) == "files") {
2506
2507 Vector<String> files = d["files"];
2508
2509 if (files.size() == 0)
2510 return false; //weird
2511
2512 for (int i = 0; i < files.size(); i++) {
2513 String file = files[i];
2514 if (file == "" || !FileAccess::exists(file))
2515 continue;
2516 Ref<Script> scr = ResourceLoader::load(file);
2517 if (scr.is_valid()) {
2518 return true;
2519 }
2520 }
2521 return true;
2522 }
2523
2524 return false;
2525 }
2526
drop_data_fw(const Point2 & p_point,const Variant & p_data,Control * p_from)2527 void ScriptEditor::drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) {
2528
2529 if (!can_drop_data_fw(p_point, p_data, p_from))
2530 return;
2531
2532 Dictionary d = p_data;
2533 if (!d.has("type"))
2534 return;
2535
2536 if (String(d["type"]) == "script_list_element") {
2537
2538 Node *node = d["script_list_element"];
2539
2540 ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(node);
2541 EditorHelp *eh = Object::cast_to<EditorHelp>(node);
2542 if (se || eh) {
2543 int new_index = 0;
2544 if (script_list->get_item_count() > 0) {
2545 new_index = script_list->get_item_metadata(script_list->get_item_at_position(p_point));
2546 }
2547 tab_container->move_child(node, new_index);
2548 tab_container->set_current_tab(new_index);
2549 _update_script_names();
2550 }
2551 }
2552
2553 if (String(d["type"]) == "nodes") {
2554
2555 Array nodes = d["nodes"];
2556 if (nodes.size() == 0)
2557 return;
2558 Node *node = get_node(nodes[0]);
2559
2560 ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(node);
2561 EditorHelp *eh = Object::cast_to<EditorHelp>(node);
2562 if (se || eh) {
2563 int new_index = 0;
2564 if (script_list->get_item_count() > 0) {
2565 new_index = script_list->get_item_metadata(script_list->get_item_at_position(p_point));
2566 }
2567 tab_container->move_child(node, new_index);
2568 tab_container->set_current_tab(new_index);
2569 _update_script_names();
2570 }
2571 }
2572
2573 if (String(d["type"]) == "files") {
2574
2575 Vector<String> files = d["files"];
2576
2577 int new_index = 0;
2578 if (script_list->get_item_count() > 0) {
2579 new_index = script_list->get_item_metadata(script_list->get_item_at_position(p_point));
2580 }
2581 int num_tabs_before = tab_container->get_child_count();
2582 for (int i = 0; i < files.size(); i++) {
2583 String file = files[i];
2584 if (file == "" || !FileAccess::exists(file))
2585 continue;
2586 Ref<Script> scr = ResourceLoader::load(file);
2587 if (scr.is_valid()) {
2588 edit(scr);
2589 if (tab_container->get_child_count() > num_tabs_before) {
2590 tab_container->move_child(tab_container->get_child(tab_container->get_child_count() - 1), new_index);
2591 num_tabs_before = tab_container->get_child_count();
2592 } else { /* Maybe script was already open */
2593 tab_container->move_child(tab_container->get_child(tab_container->get_current_tab()), new_index);
2594 }
2595 }
2596 }
2597 tab_container->set_current_tab(new_index);
2598 _update_script_names();
2599 }
2600 }
2601
_unhandled_input(const Ref<InputEvent> & p_event)2602 void ScriptEditor::_unhandled_input(const Ref<InputEvent> &p_event) {
2603 if (!is_visible_in_tree() || !p_event->is_pressed() || p_event->is_echo())
2604 return;
2605 if (ED_IS_SHORTCUT("script_editor/next_script", p_event)) {
2606 if (script_list->get_item_count() > 1) {
2607 int next_tab = script_list->get_current() + 1;
2608 next_tab %= script_list->get_item_count();
2609 _go_to_tab(script_list->get_item_metadata(next_tab));
2610 _update_script_names();
2611 }
2612 }
2613 if (ED_IS_SHORTCUT("script_editor/prev_script", p_event)) {
2614 if (script_list->get_item_count() > 1) {
2615 int next_tab = script_list->get_current() - 1;
2616 next_tab = next_tab >= 0 ? next_tab : script_list->get_item_count() - 1;
2617 _go_to_tab(script_list->get_item_metadata(next_tab));
2618 _update_script_names();
2619 }
2620 }
2621 if (ED_IS_SHORTCUT("script_editor/window_move_up", p_event)) {
2622 _menu_option(WINDOW_MOVE_UP);
2623 }
2624 if (ED_IS_SHORTCUT("script_editor/window_move_down", p_event)) {
2625 _menu_option(WINDOW_MOVE_DOWN);
2626 }
2627 }
2628
_script_list_gui_input(const Ref<InputEvent> & ev)2629 void ScriptEditor::_script_list_gui_input(const Ref<InputEvent> &ev) {
2630
2631 Ref<InputEventMouseButton> mb = ev;
2632 if (mb.is_valid() && mb->is_pressed()) {
2633 switch (mb->get_button_index()) {
2634
2635 case BUTTON_MIDDLE: {
2636 // Right-click selects automatically; middle-click does not.
2637 int idx = script_list->get_item_at_position(mb->get_position(), true);
2638 if (idx >= 0) {
2639 script_list->select(idx);
2640 _script_selected(idx);
2641 _menu_option(FILE_CLOSE);
2642 }
2643 } break;
2644
2645 case BUTTON_RIGHT: {
2646 _make_script_list_context_menu();
2647 } break;
2648 }
2649 }
2650 }
2651
_make_script_list_context_menu()2652 void ScriptEditor::_make_script_list_context_menu() {
2653
2654 context_menu->clear();
2655
2656 int selected = tab_container->get_current_tab();
2657 if (selected < 0 || selected >= tab_container->get_child_count())
2658 return;
2659
2660 ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(selected));
2661 if (se) {
2662 context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/save"), FILE_SAVE);
2663 context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/save_as"), FILE_SAVE_AS);
2664 }
2665 context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/close_file"), FILE_CLOSE);
2666 context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/close_all"), CLOSE_ALL);
2667 context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/close_other_tabs"), CLOSE_OTHER_TABS);
2668 context_menu->add_separator();
2669 if (se) {
2670 Ref<Script> scr = se->get_edited_resource();
2671 if (scr != NULL) {
2672 context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/reload_script_soft"), FILE_TOOL_RELOAD_SOFT);
2673 if (!scr.is_null() && scr->is_tool()) {
2674 context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/run_file"), FILE_RUN);
2675 context_menu->add_separator();
2676 }
2677 }
2678 context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/copy_path"), FILE_COPY_PATH);
2679 context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/show_in_file_system"), SHOW_IN_FILE_SYSTEM);
2680 context_menu->add_separator();
2681 }
2682
2683 context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/window_move_up"), WINDOW_MOVE_UP);
2684 context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/window_move_down"), WINDOW_MOVE_DOWN);
2685 context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/window_sort"), WINDOW_SORT);
2686 context_menu->add_shortcut(ED_GET_SHORTCUT("script_editor/toggle_scripts_panel"), TOGGLE_SCRIPTS_PANEL);
2687
2688 context_menu->set_position(get_global_transform().xform(get_local_mouse_position()));
2689 context_menu->set_size(Vector2(1, 1));
2690 context_menu->popup();
2691 }
2692
set_window_layout(Ref<ConfigFile> p_layout)2693 void ScriptEditor::set_window_layout(Ref<ConfigFile> p_layout) {
2694
2695 if (!bool(EDITOR_DEF("text_editor/files/restore_scripts_on_load", true))) {
2696 return;
2697 }
2698
2699 if (!p_layout->has_section_key("ScriptEditor", "open_scripts") && !p_layout->has_section_key("ScriptEditor", "open_help"))
2700 return;
2701
2702 Array scripts = p_layout->get_value("ScriptEditor", "open_scripts");
2703 Array helps;
2704 if (p_layout->has_section_key("ScriptEditor", "open_help"))
2705 helps = p_layout->get_value("ScriptEditor", "open_help");
2706
2707 restoring_layout = true;
2708
2709 List<String> extensions;
2710 ResourceLoader::get_recognized_extensions_for_type("Script", &extensions);
2711
2712 for (int i = 0; i < scripts.size(); i++) {
2713
2714 String path = scripts[i];
2715
2716 Dictionary script_info = scripts[i];
2717 if (!script_info.empty()) {
2718 path = script_info["path"];
2719 }
2720
2721 if (!FileAccess::exists(path))
2722 continue;
2723
2724 if (extensions.find(path.get_extension())) {
2725 Ref<Script> scr = ResourceLoader::load(path);
2726 if (!scr.is_valid()) {
2727 continue;
2728 }
2729 if (!edit(scr)) {
2730 continue;
2731 }
2732 } else {
2733 Error error;
2734 Ref<TextFile> text_file = _load_text_file(path, &error);
2735 if (error != OK || !text_file.is_valid()) {
2736 continue;
2737 }
2738 if (!edit(text_file)) {
2739 continue;
2740 }
2741 }
2742
2743 if (!script_info.empty()) {
2744 ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(tab_container->get_tab_count() - 1));
2745 if (se) {
2746 se->set_edit_state(script_info["state"]);
2747 }
2748 }
2749 }
2750
2751 for (int i = 0; i < helps.size(); i++) {
2752
2753 String path = helps[i];
2754 if (path == "") { // invalid, skip
2755 continue;
2756 }
2757 _help_class_open(path);
2758 }
2759
2760 for (int i = 0; i < tab_container->get_child_count(); i++) {
2761 tab_container->get_child(i)->set_meta("__editor_pass", Variant());
2762 }
2763
2764 if (p_layout->has_section_key("ScriptEditor", "split_offset")) {
2765 script_split->set_split_offset(p_layout->get_value("ScriptEditor", "split_offset"));
2766 }
2767
2768 restoring_layout = false;
2769
2770 _update_script_names();
2771 }
2772
get_window_layout(Ref<ConfigFile> p_layout)2773 void ScriptEditor::get_window_layout(Ref<ConfigFile> p_layout) {
2774
2775 Array scripts;
2776 Array helps;
2777
2778 for (int i = 0; i < tab_container->get_child_count(); i++) {
2779
2780 ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
2781 if (se) {
2782
2783 String path = se->get_edited_resource()->get_path();
2784 if (!path.is_resource_file())
2785 continue;
2786
2787 Dictionary script_info;
2788 script_info["path"] = path;
2789 script_info["state"] = se->get_edit_state();
2790
2791 scripts.push_back(script_info);
2792 }
2793
2794 EditorHelp *eh = Object::cast_to<EditorHelp>(tab_container->get_child(i));
2795
2796 if (eh) {
2797
2798 helps.push_back(eh->get_class());
2799 }
2800 }
2801
2802 p_layout->set_value("ScriptEditor", "open_scripts", scripts);
2803 p_layout->set_value("ScriptEditor", "open_help", helps);
2804 p_layout->set_value("ScriptEditor", "split_offset", script_split->get_split_offset());
2805 }
2806
_help_class_open(const String & p_class)2807 void ScriptEditor::_help_class_open(const String &p_class) {
2808
2809 if (p_class == "")
2810 return;
2811
2812 for (int i = 0; i < tab_container->get_child_count(); i++) {
2813
2814 EditorHelp *eh = Object::cast_to<EditorHelp>(tab_container->get_child(i));
2815
2816 if (eh && eh->get_class() == p_class) {
2817
2818 _go_to_tab(i);
2819 _update_script_names();
2820 return;
2821 }
2822 }
2823
2824 EditorHelp *eh = memnew(EditorHelp);
2825
2826 eh->set_name(p_class);
2827 tab_container->add_child(eh);
2828 _go_to_tab(tab_container->get_tab_count() - 1);
2829 eh->go_to_class(p_class, 0);
2830 eh->connect("go_to_help", this, "_help_class_goto");
2831 _add_recent_script(p_class);
2832 _sort_list_on_update = true;
2833 _update_script_names();
2834 _save_layout();
2835 }
2836
_help_class_goto(const String & p_desc)2837 void ScriptEditor::_help_class_goto(const String &p_desc) {
2838
2839 String cname = p_desc.get_slice(":", 1);
2840
2841 for (int i = 0; i < tab_container->get_child_count(); i++) {
2842
2843 EditorHelp *eh = Object::cast_to<EditorHelp>(tab_container->get_child(i));
2844
2845 if (eh && eh->get_class() == cname) {
2846
2847 _go_to_tab(i);
2848 eh->go_to_help(p_desc);
2849 _update_script_names();
2850 return;
2851 }
2852 }
2853
2854 EditorHelp *eh = memnew(EditorHelp);
2855
2856 eh->set_name(cname);
2857 tab_container->add_child(eh);
2858 _go_to_tab(tab_container->get_tab_count() - 1);
2859 eh->go_to_help(p_desc);
2860 eh->connect("go_to_help", this, "_help_class_goto");
2861 _add_recent_script(eh->get_class());
2862 _sort_list_on_update = true;
2863 _update_script_names();
2864 _save_layout();
2865 }
2866
_update_selected_editor_menu()2867 void ScriptEditor::_update_selected_editor_menu() {
2868
2869 for (int i = 0; i < tab_container->get_child_count(); i++) {
2870
2871 bool current = tab_container->get_current_tab() == i;
2872
2873 ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
2874 if (se && se->get_edit_menu()) {
2875
2876 if (current)
2877 se->get_edit_menu()->show();
2878 else
2879 se->get_edit_menu()->hide();
2880 }
2881 }
2882
2883 EditorHelp *eh = Object::cast_to<EditorHelp>(tab_container->get_current_tab_control());
2884 script_search_menu->get_popup()->clear();
2885 if (eh) {
2886
2887 script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find", TTR("Find..."), KEY_MASK_CMD | KEY_F), HELP_SEARCH_FIND);
2888 script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_next", TTR("Find Next"), KEY_F3), HELP_SEARCH_FIND_NEXT);
2889 script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_previous", TTR("Find Previous"), KEY_MASK_SHIFT | KEY_F3), HELP_SEARCH_FIND_PREVIOUS);
2890 script_search_menu->get_popup()->add_separator();
2891 script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_in_files", TTR("Find in Files"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F), SEARCH_IN_FILES);
2892 script_search_menu->show();
2893 } else {
2894
2895 if (tab_container->get_child_count() == 0) {
2896 script_search_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/find_in_files", TTR("Find in Files"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_F), SEARCH_IN_FILES);
2897 script_search_menu->show();
2898 } else {
2899 script_search_menu->hide();
2900 }
2901 }
2902 }
2903
_update_history_pos(int p_new_pos)2904 void ScriptEditor::_update_history_pos(int p_new_pos) {
2905
2906 Node *n = tab_container->get_current_tab_control();
2907
2908 if (Object::cast_to<ScriptEditorBase>(n)) {
2909
2910 history.write[history_pos].state = Object::cast_to<ScriptEditorBase>(n)->get_edit_state();
2911 }
2912 if (Object::cast_to<EditorHelp>(n)) {
2913
2914 history.write[history_pos].state = Object::cast_to<EditorHelp>(n)->get_scroll();
2915 }
2916
2917 history_pos = p_new_pos;
2918 tab_container->set_current_tab(history[history_pos].control->get_index());
2919
2920 n = history[history_pos].control;
2921
2922 if (Object::cast_to<ScriptEditorBase>(n)) {
2923
2924 Object::cast_to<ScriptEditorBase>(n)->set_edit_state(history[history_pos].state);
2925 Object::cast_to<ScriptEditorBase>(n)->ensure_focus();
2926
2927 Ref<Script> script = Object::cast_to<ScriptEditorBase>(n)->get_edited_resource();
2928 if (script != NULL) {
2929 notify_script_changed(script);
2930 }
2931 }
2932
2933 if (Object::cast_to<EditorHelp>(n)) {
2934
2935 Object::cast_to<EditorHelp>(n)->set_scroll(history[history_pos].state);
2936 Object::cast_to<EditorHelp>(n)->set_focused();
2937 }
2938
2939 n->set_meta("__editor_pass", ++edit_pass);
2940 _update_script_names();
2941 _update_history_arrows();
2942 _update_selected_editor_menu();
2943 }
2944
_history_forward()2945 void ScriptEditor::_history_forward() {
2946
2947 if (history_pos < history.size() - 1) {
2948 _update_history_pos(history_pos + 1);
2949 }
2950 }
2951
_history_back()2952 void ScriptEditor::_history_back() {
2953
2954 if (history_pos > 0) {
2955 _update_history_pos(history_pos - 1);
2956 }
2957 }
2958
get_open_scripts() const2959 Vector<Ref<Script> > ScriptEditor::get_open_scripts() const {
2960
2961 Vector<Ref<Script> > out_scripts = Vector<Ref<Script> >();
2962
2963 for (int i = 0; i < tab_container->get_child_count(); i++) {
2964 ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i));
2965 if (!se)
2966 continue;
2967
2968 Ref<Script> script = se->get_edited_resource();
2969 if (script != NULL) {
2970 out_scripts.push_back(script);
2971 }
2972 }
2973
2974 return out_scripts;
2975 }
2976
set_scene_root_script(Ref<Script> p_script)2977 void ScriptEditor::set_scene_root_script(Ref<Script> p_script) {
2978 // Don't open dominant script if using an external editor.
2979 const bool use_external_editor =
2980 EditorSettings::get_singleton()->get("text_editor/external/use_external_editor") ||
2981 (p_script.is_valid() && p_script->get_language()->overrides_external_editor());
2982 const bool open_dominant = EditorSettings::get_singleton()->get("text_editor/files/open_dominant_script_on_scene_change");
2983
2984 if (open_dominant && !use_external_editor && p_script.is_valid()) {
2985 edit(p_script);
2986 }
2987 }
2988
script_goto_method(Ref<Script> p_script,const String & p_method)2989 bool ScriptEditor::script_goto_method(Ref<Script> p_script, const String &p_method) {
2990
2991 int line = p_script->get_member_line(p_method);
2992
2993 if (line == -1)
2994 return false;
2995
2996 return edit(p_script, line, 0);
2997 }
2998
set_live_auto_reload_running_scripts(bool p_enabled)2999 void ScriptEditor::set_live_auto_reload_running_scripts(bool p_enabled) {
3000
3001 auto_reload_running_scripts = p_enabled;
3002 }
3003
_help_search(String p_text)3004 void ScriptEditor::_help_search(String p_text) {
3005 help_search_dialog->popup_dialog(p_text);
3006 }
3007
_open_script_request(const String & p_path)3008 void ScriptEditor::_open_script_request(const String &p_path) {
3009
3010 Ref<Script> script = ResourceLoader::load(p_path);
3011 if (script.is_valid()) {
3012 script_editor->edit(script, false);
3013 return;
3014 }
3015
3016 Error err;
3017 Ref<TextFile> text_file = script_editor->_load_text_file(p_path, &err);
3018 if (text_file.is_valid()) {
3019 script_editor->edit(text_file, false);
3020 return;
3021 }
3022 }
3023
3024 int ScriptEditor::syntax_highlighters_func_count = 0;
3025 CreateSyntaxHighlighterFunc ScriptEditor::syntax_highlighters_funcs[ScriptEditor::SYNTAX_HIGHLIGHTER_FUNC_MAX];
3026
register_create_syntax_highlighter_function(CreateSyntaxHighlighterFunc p_func)3027 void ScriptEditor::register_create_syntax_highlighter_function(CreateSyntaxHighlighterFunc p_func) {
3028 ERR_FAIL_COND(syntax_highlighters_func_count == SYNTAX_HIGHLIGHTER_FUNC_MAX);
3029 syntax_highlighters_funcs[syntax_highlighters_func_count++] = p_func;
3030 }
3031
3032 int ScriptEditor::script_editor_func_count = 0;
3033 CreateScriptEditorFunc ScriptEditor::script_editor_funcs[ScriptEditor::SCRIPT_EDITOR_FUNC_MAX];
3034
register_create_script_editor_function(CreateScriptEditorFunc p_func)3035 void ScriptEditor::register_create_script_editor_function(CreateScriptEditorFunc p_func) {
3036
3037 ERR_FAIL_COND(script_editor_func_count == SCRIPT_EDITOR_FUNC_MAX);
3038 script_editor_funcs[script_editor_func_count++] = p_func;
3039 }
3040
_script_changed()3041 void ScriptEditor::_script_changed() {
3042
3043 NodeDock::singleton->update_lists();
3044 }
3045
_on_find_in_files_requested(String text)3046 void ScriptEditor::_on_find_in_files_requested(String text) {
3047
3048 find_in_files_dialog->set_search_text(text);
3049 find_in_files_dialog->popup_centered_minsize();
3050 }
3051
_on_find_in_files_result_selected(String fpath,int line_number,int begin,int end)3052 void ScriptEditor::_on_find_in_files_result_selected(String fpath, int line_number, int begin, int end) {
3053
3054 if (ResourceLoader::exists(fpath)) {
3055 RES res = ResourceLoader::load(fpath);
3056
3057 if (fpath.get_extension() == "shader") {
3058 ShaderEditorPlugin *shader_editor = Object::cast_to<ShaderEditorPlugin>(EditorNode::get_singleton()->get_editor_data().get_editor("Shader"));
3059 shader_editor->edit(res.ptr());
3060 shader_editor->make_visible(true);
3061 shader_editor->get_shader_editor()->goto_line_selection(line_number - 1, begin, end);
3062 return;
3063 } else {
3064 Ref<Script> script = res;
3065 if (script.is_valid()) {
3066 edit(script);
3067
3068 ScriptTextEditor *ste = Object::cast_to<ScriptTextEditor>(_get_current_editor());
3069 if (ste) {
3070 ste->goto_line_selection(line_number - 1, begin, end);
3071 }
3072 return;
3073 }
3074 }
3075 }
3076
3077 // If the file is not a valid resource/script, load it as a text file.
3078 Error err;
3079 Ref<TextFile> text_file = _load_text_file(fpath, &err);
3080 if (text_file.is_valid()) {
3081 edit(text_file);
3082
3083 TextEditor *te = Object::cast_to<TextEditor>(_get_current_editor());
3084 if (te) {
3085 te->goto_line_selection(line_number - 1, begin, end);
3086 }
3087 }
3088 }
3089
_start_find_in_files(bool with_replace)3090 void ScriptEditor::_start_find_in_files(bool with_replace) {
3091
3092 FindInFiles *f = find_in_files->get_finder();
3093
3094 f->set_search_text(find_in_files_dialog->get_search_text());
3095 f->set_match_case(find_in_files_dialog->is_match_case());
3096 f->set_whole_words(find_in_files_dialog->is_whole_words());
3097 f->set_folder(find_in_files_dialog->get_folder());
3098 f->set_filter(find_in_files_dialog->get_filter());
3099
3100 find_in_files->set_with_replace(with_replace);
3101 find_in_files->start_search();
3102
3103 editor->make_bottom_panel_item_visible(find_in_files);
3104 }
3105
_on_find_in_files_modified_files(PoolStringArray paths)3106 void ScriptEditor::_on_find_in_files_modified_files(PoolStringArray paths) {
3107
3108 _test_script_times_on_disk();
3109 _update_modified_scripts_for_external_editor();
3110 }
3111
_filter_scripts_text_changed(const String & p_newtext)3112 void ScriptEditor::_filter_scripts_text_changed(const String &p_newtext) {
3113 _update_script_names();
3114 }
3115
_filter_methods_text_changed(const String & p_newtext)3116 void ScriptEditor::_filter_methods_text_changed(const String &p_newtext) {
3117 _update_members_overview();
3118 }
3119
_bind_methods()3120 void ScriptEditor::_bind_methods() {
3121
3122 ClassDB::bind_method("_file_dialog_action", &ScriptEditor::_file_dialog_action);
3123 ClassDB::bind_method("_tab_changed", &ScriptEditor::_tab_changed);
3124 ClassDB::bind_method("_menu_option", &ScriptEditor::_menu_option);
3125 ClassDB::bind_method("_close_current_tab", &ScriptEditor::_close_current_tab);
3126 ClassDB::bind_method("_close_discard_current_tab", &ScriptEditor::_close_discard_current_tab);
3127 ClassDB::bind_method("_close_docs_tab", &ScriptEditor::_close_docs_tab);
3128 ClassDB::bind_method("_close_all_tabs", &ScriptEditor::_close_all_tabs);
3129 ClassDB::bind_method("_close_other_tabs", &ScriptEditor::_close_other_tabs);
3130 ClassDB::bind_method("_open_recent_script", &ScriptEditor::_open_recent_script);
3131 ClassDB::bind_method("_theme_option", &ScriptEditor::_theme_option);
3132 ClassDB::bind_method("_editor_play", &ScriptEditor::_editor_play);
3133 ClassDB::bind_method("_editor_pause", &ScriptEditor::_editor_pause);
3134 ClassDB::bind_method("_editor_stop", &ScriptEditor::_editor_stop);
3135 ClassDB::bind_method("_add_callback", &ScriptEditor::_add_callback);
3136 ClassDB::bind_method("_reload_scripts", &ScriptEditor::_reload_scripts);
3137 ClassDB::bind_method("_resave_scripts", &ScriptEditor::_resave_scripts);
3138 ClassDB::bind_method("_res_saved_callback", &ScriptEditor::_res_saved_callback);
3139 ClassDB::bind_method("_goto_script_line", &ScriptEditor::_goto_script_line);
3140 ClassDB::bind_method("_goto_script_line2", &ScriptEditor::_goto_script_line2);
3141 ClassDB::bind_method("_set_execution", &ScriptEditor::_set_execution);
3142 ClassDB::bind_method("_clear_execution", &ScriptEditor::_clear_execution);
3143 ClassDB::bind_method("_help_search", &ScriptEditor::_help_search);
3144 ClassDB::bind_method("_save_history", &ScriptEditor::_save_history);
3145 ClassDB::bind_method("_copy_script_path", &ScriptEditor::_copy_script_path);
3146
3147 ClassDB::bind_method("_breaked", &ScriptEditor::_breaked);
3148 ClassDB::bind_method("_show_debugger", &ScriptEditor::_show_debugger);
3149 ClassDB::bind_method("_get_debug_tooltip", &ScriptEditor::_get_debug_tooltip);
3150 ClassDB::bind_method("_autosave_scripts", &ScriptEditor::_autosave_scripts);
3151 ClassDB::bind_method("_update_autosave_timer", &ScriptEditor::_update_autosave_timer);
3152 ClassDB::bind_method("_editor_settings_changed", &ScriptEditor::_editor_settings_changed);
3153 ClassDB::bind_method("_update_script_names", &ScriptEditor::_update_script_names);
3154 ClassDB::bind_method("_update_script_connections", &ScriptEditor::_update_script_connections);
3155 ClassDB::bind_method("_tree_changed", &ScriptEditor::_tree_changed);
3156 ClassDB::bind_method("_members_overview_selected", &ScriptEditor::_members_overview_selected);
3157 ClassDB::bind_method("_help_overview_selected", &ScriptEditor::_help_overview_selected);
3158 ClassDB::bind_method("_script_selected", &ScriptEditor::_script_selected);
3159 ClassDB::bind_method("_script_created", &ScriptEditor::_script_created);
3160 ClassDB::bind_method("_script_split_dragged", &ScriptEditor::_script_split_dragged);
3161 ClassDB::bind_method("_help_class_open", &ScriptEditor::_help_class_open);
3162 ClassDB::bind_method("_help_class_goto", &ScriptEditor::_help_class_goto);
3163 ClassDB::bind_method("_request_help", &ScriptEditor::_help_class_open);
3164 ClassDB::bind_method("_history_forward", &ScriptEditor::_history_forward);
3165 ClassDB::bind_method("_history_back", &ScriptEditor::_history_back);
3166 ClassDB::bind_method("_live_auto_reload_running_scripts", &ScriptEditor::_live_auto_reload_running_scripts);
3167 ClassDB::bind_method("_unhandled_input", &ScriptEditor::_unhandled_input);
3168 ClassDB::bind_method("_script_list_gui_input", &ScriptEditor::_script_list_gui_input);
3169 ClassDB::bind_method("_toggle_members_overview_alpha_sort", &ScriptEditor::_toggle_members_overview_alpha_sort);
3170 ClassDB::bind_method("_update_members_overview", &ScriptEditor::_update_members_overview);
3171 ClassDB::bind_method("_script_changed", &ScriptEditor::_script_changed);
3172 ClassDB::bind_method("_filter_scripts_text_changed", &ScriptEditor::_filter_scripts_text_changed);
3173 ClassDB::bind_method("_filter_methods_text_changed", &ScriptEditor::_filter_methods_text_changed);
3174 ClassDB::bind_method("_update_recent_scripts", &ScriptEditor::_update_recent_scripts);
3175 ClassDB::bind_method("_on_find_in_files_requested", &ScriptEditor::_on_find_in_files_requested);
3176 ClassDB::bind_method("_start_find_in_files", &ScriptEditor::_start_find_in_files);
3177 ClassDB::bind_method("_on_find_in_files_result_selected", &ScriptEditor::_on_find_in_files_result_selected);
3178 ClassDB::bind_method("_on_find_in_files_modified_files", &ScriptEditor::_on_find_in_files_modified_files);
3179
3180 ClassDB::bind_method(D_METHOD("get_drag_data_fw", "point", "from"), &ScriptEditor::get_drag_data_fw);
3181 ClassDB::bind_method(D_METHOD("can_drop_data_fw", "point", "data", "from"), &ScriptEditor::can_drop_data_fw);
3182 ClassDB::bind_method(D_METHOD("drop_data_fw", "point", "data", "from"), &ScriptEditor::drop_data_fw);
3183
3184 ClassDB::bind_method(D_METHOD("goto_line", "line_number"), &ScriptEditor::_goto_script_line2);
3185 ClassDB::bind_method(D_METHOD("get_current_script"), &ScriptEditor::_get_current_script);
3186 ClassDB::bind_method(D_METHOD("get_open_scripts"), &ScriptEditor::_get_open_scripts);
3187 ClassDB::bind_method(D_METHOD("open_script_create_dialog", "base_name", "base_path"), &ScriptEditor::open_script_create_dialog);
3188
3189 ADD_SIGNAL(MethodInfo("editor_script_changed", PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script")));
3190 ADD_SIGNAL(MethodInfo("script_close", PropertyInfo(Variant::OBJECT, "script", PROPERTY_HINT_RESOURCE_TYPE, "Script")));
3191 }
3192
ScriptEditor(EditorNode * p_editor)3193 ScriptEditor::ScriptEditor(EditorNode *p_editor) {
3194
3195 current_theme = "";
3196
3197 completion_cache = memnew(EditorScriptCodeCompletionCache);
3198 restoring_layout = false;
3199 waiting_update_names = false;
3200 pending_auto_reload = false;
3201 auto_reload_running_scripts = true;
3202 members_overview_enabled = EditorSettings::get_singleton()->get("text_editor/script_list/show_members_overview");
3203 help_overview_enabled = EditorSettings::get_singleton()->get("text_editor/help/show_help_index");
3204 editor = p_editor;
3205
3206 VBoxContainer *main_container = memnew(VBoxContainer);
3207 add_child(main_container);
3208
3209 menu_hb = memnew(HBoxContainer);
3210 main_container->add_child(menu_hb);
3211
3212 script_split = memnew(HSplitContainer);
3213 main_container->add_child(script_split);
3214 script_split->set_v_size_flags(SIZE_EXPAND_FILL);
3215
3216 list_split = memnew(VSplitContainer);
3217 script_split->add_child(list_split);
3218 list_split->set_v_size_flags(SIZE_EXPAND_FILL);
3219
3220 scripts_vbox = memnew(VBoxContainer);
3221 scripts_vbox->set_v_size_flags(SIZE_EXPAND_FILL);
3222 list_split->add_child(scripts_vbox);
3223
3224 filter_scripts = memnew(LineEdit);
3225 filter_scripts->set_placeholder(TTR("Filter scripts"));
3226 filter_scripts->set_clear_button_enabled(true);
3227 filter_scripts->connect("text_changed", this, "_filter_scripts_text_changed");
3228 scripts_vbox->add_child(filter_scripts);
3229
3230 script_list = memnew(ItemList);
3231 scripts_vbox->add_child(script_list);
3232 script_list->set_custom_minimum_size(Size2(150, 60) * EDSCALE); //need to give a bit of limit to avoid it from disappearing
3233 script_list->set_v_size_flags(SIZE_EXPAND_FILL);
3234 script_split->set_split_offset(70 * EDSCALE);
3235 _sort_list_on_update = true;
3236 script_list->connect("gui_input", this, "_script_list_gui_input", varray(), CONNECT_DEFERRED);
3237 script_list->set_allow_rmb_select(true);
3238 script_list->set_drag_forwarding(this);
3239
3240 context_menu = memnew(PopupMenu);
3241 add_child(context_menu);
3242 context_menu->connect("id_pressed", this, "_menu_option");
3243 context_menu->set_hide_on_window_lose_focus(true);
3244
3245 overview_vbox = memnew(VBoxContainer);
3246 overview_vbox->set_custom_minimum_size(Size2(0, 90));
3247 overview_vbox->set_v_size_flags(SIZE_EXPAND_FILL);
3248
3249 list_split->add_child(overview_vbox);
3250 buttons_hbox = memnew(HBoxContainer);
3251 overview_vbox->add_child(buttons_hbox);
3252
3253 filename = memnew(Label);
3254 filename->set_clip_text(true);
3255 filename->set_h_size_flags(SIZE_EXPAND_FILL);
3256 filename->add_style_override("normal", EditorNode::get_singleton()->get_gui_base()->get_stylebox("normal", "LineEdit"));
3257 buttons_hbox->add_child(filename);
3258
3259 members_overview_alphabeta_sort_button = memnew(ToolButton);
3260 members_overview_alphabeta_sort_button->set_tooltip(TTR("Toggle alphabetical sorting of the method list."));
3261 members_overview_alphabeta_sort_button->set_toggle_mode(true);
3262 members_overview_alphabeta_sort_button->set_pressed(EditorSettings::get_singleton()->get("text_editor/tools/sort_members_outline_alphabetically"));
3263 members_overview_alphabeta_sort_button->connect("toggled", this, "_toggle_members_overview_alpha_sort");
3264
3265 buttons_hbox->add_child(members_overview_alphabeta_sort_button);
3266
3267 filter_methods = memnew(LineEdit);
3268 filter_methods->set_placeholder(TTR("Filter methods"));
3269 filter_methods->set_clear_button_enabled(true);
3270 filter_methods->connect("text_changed", this, "_filter_methods_text_changed");
3271 overview_vbox->add_child(filter_methods);
3272
3273 members_overview = memnew(ItemList);
3274 overview_vbox->add_child(members_overview);
3275
3276 members_overview->set_allow_reselect(true);
3277 members_overview->set_custom_minimum_size(Size2(0, 60) * EDSCALE); //need to give a bit of limit to avoid it from disappearing
3278 members_overview->set_v_size_flags(SIZE_EXPAND_FILL);
3279 members_overview->set_allow_rmb_select(true);
3280
3281 help_overview = memnew(ItemList);
3282 overview_vbox->add_child(help_overview);
3283 help_overview->set_allow_reselect(true);
3284 help_overview->set_custom_minimum_size(Size2(0, 60) * EDSCALE); //need to give a bit of limit to avoid it from disappearing
3285 help_overview->set_v_size_flags(SIZE_EXPAND_FILL);
3286
3287 tab_container = memnew(TabContainer);
3288 tab_container->set_tabs_visible(false);
3289 tab_container->set_custom_minimum_size(Size2(200, 0) * EDSCALE);
3290 script_split->add_child(tab_container);
3291 tab_container->set_h_size_flags(SIZE_EXPAND_FILL);
3292
3293 ED_SHORTCUT("script_editor/window_sort", TTR("Sort"));
3294 ED_SHORTCUT("script_editor/window_move_up", TTR("Move Up"), KEY_MASK_SHIFT | KEY_MASK_ALT | KEY_UP);
3295 ED_SHORTCUT("script_editor/window_move_down", TTR("Move Down"), KEY_MASK_SHIFT | KEY_MASK_ALT | KEY_DOWN);
3296 ED_SHORTCUT("script_editor/next_script", TTR("Next script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_PERIOD); // these should be KEY_GREATER and KEY_LESS but those don't work
3297 ED_SHORTCUT("script_editor/prev_script", TTR("Previous script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_COMMA);
3298 set_process_unhandled_input(true);
3299
3300 file_menu = memnew(MenuButton);
3301 menu_hb->add_child(file_menu);
3302 file_menu->set_text(TTR("File"));
3303 file_menu->set_switch_on_hover(true);
3304 file_menu->get_popup()->set_hide_on_window_lose_focus(true);
3305 file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/new", TTR("New Script...")), FILE_NEW);
3306 file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/new_textfile", TTR("New Text File...")), FILE_NEW_TEXTFILE);
3307 file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/open", TTR("Open...")), FILE_OPEN);
3308 file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/reopen_closed_script", TTR("Reopen Closed Script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_T), FILE_REOPEN_CLOSED);
3309 file_menu->get_popup()->add_submenu_item(TTR("Open Recent"), "RecentScripts", FILE_OPEN_RECENT);
3310
3311 recent_scripts = memnew(PopupMenu);
3312 recent_scripts->set_name("RecentScripts");
3313 file_menu->get_popup()->add_child(recent_scripts);
3314 recent_scripts->connect("id_pressed", this, "_open_recent_script");
3315 _update_recent_scripts();
3316
3317 file_menu->get_popup()->add_separator();
3318 file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save", TTR("Save"), KEY_MASK_ALT | KEY_MASK_CMD | KEY_S), FILE_SAVE);
3319 file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_as", TTR("Save As...")), FILE_SAVE_AS);
3320 file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/save_all", TTR("Save All"), KEY_MASK_SHIFT | KEY_MASK_ALT | KEY_S), FILE_SAVE_ALL);
3321 file_menu->get_popup()->add_separator();
3322 file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/reload_script_soft", TTR("Soft Reload Script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_R), FILE_TOOL_RELOAD_SOFT);
3323 file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/copy_path", TTR("Copy Script Path")), FILE_COPY_PATH);
3324 file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/show_in_file_system", TTR("Show in FileSystem")), SHOW_IN_FILE_SYSTEM);
3325 file_menu->get_popup()->add_separator();
3326
3327 file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/history_previous", TTR("History Previous"), KEY_MASK_ALT | KEY_LEFT), WINDOW_PREV);
3328 file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/history_next", TTR("History Next"), KEY_MASK_ALT | KEY_RIGHT), WINDOW_NEXT);
3329 file_menu->get_popup()->add_separator();
3330
3331 file_menu->get_popup()->add_submenu_item(TTR("Theme"), "Theme", FILE_THEME);
3332
3333 theme_submenu = memnew(PopupMenu);
3334 theme_submenu->set_name("Theme");
3335 file_menu->get_popup()->add_child(theme_submenu);
3336 theme_submenu->connect("id_pressed", this, "_theme_option");
3337 theme_submenu->add_shortcut(ED_SHORTCUT("script_editor/import_theme", TTR("Import Theme...")), THEME_IMPORT);
3338 theme_submenu->add_shortcut(ED_SHORTCUT("script_editor/reload_theme", TTR("Reload Theme")), THEME_RELOAD);
3339
3340 theme_submenu->add_separator();
3341 theme_submenu->add_shortcut(ED_SHORTCUT("script_editor/save_theme", TTR("Save Theme")), THEME_SAVE);
3342 theme_submenu->add_shortcut(ED_SHORTCUT("script_editor/save_theme_as", TTR("Save Theme As...")), THEME_SAVE_AS);
3343
3344 file_menu->get_popup()->add_separator();
3345 file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/close_file", TTR("Close"), KEY_MASK_CMD | KEY_W), FILE_CLOSE);
3346 file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/close_all", TTR("Close All")), CLOSE_ALL);
3347 file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/close_other_tabs", TTR("Close Other Tabs")), CLOSE_OTHER_TABS);
3348 file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/close_docs", TTR("Close Docs")), CLOSE_DOCS);
3349
3350 file_menu->get_popup()->add_separator();
3351 file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/run_file", TTR("Run"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_X), FILE_RUN);
3352
3353 file_menu->get_popup()->add_separator();
3354 file_menu->get_popup()->add_shortcut(ED_SHORTCUT("script_editor/toggle_scripts_panel", TTR("Toggle Scripts Panel"), KEY_MASK_CMD | KEY_BACKSLASH), TOGGLE_SCRIPTS_PANEL);
3355 file_menu->get_popup()->connect("id_pressed", this, "_menu_option");
3356
3357 script_search_menu = memnew(MenuButton);
3358 menu_hb->add_child(script_search_menu);
3359 script_search_menu->set_text(TTR("Search"));
3360 script_search_menu->set_switch_on_hover(true);
3361 script_search_menu->get_popup()->set_hide_on_window_lose_focus(true);
3362 script_search_menu->get_popup()->connect("id_pressed", this, "_menu_option");
3363
3364 debug_menu = memnew(MenuButton);
3365 menu_hb->add_child(debug_menu);
3366 debug_menu->set_text(TTR("Debug"));
3367 debug_menu->set_switch_on_hover(true);
3368 debug_menu->get_popup()->set_hide_on_window_lose_focus(true);
3369 debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/step_into", TTR("Step Into"), KEY_F11), DEBUG_STEP);
3370 debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/step_over", TTR("Step Over"), KEY_F10), DEBUG_NEXT);
3371 debug_menu->get_popup()->add_separator();
3372 debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/break", TTR("Break")), DEBUG_BREAK);
3373 debug_menu->get_popup()->add_shortcut(ED_SHORTCUT("debugger/continue", TTR("Continue"), KEY_F12), DEBUG_CONTINUE);
3374 debug_menu->get_popup()->add_separator();
3375 //debug_menu->get_popup()->add_check_item("Show Debugger",DEBUG_SHOW);
3376 debug_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("debugger/keep_debugger_open", TTR("Keep Debugger Open")), DEBUG_SHOW_KEEP_OPEN);
3377 debug_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("debugger/debug_with_external_editor", TTR("Debug with External Editor")), DEBUG_WITH_EXTERNAL_EDITOR);
3378 debug_menu->get_popup()->connect("id_pressed", this, "_menu_option");
3379
3380 debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_NEXT), true);
3381 debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_STEP), true);
3382 debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_BREAK), true);
3383 debug_menu->get_popup()->set_item_disabled(debug_menu->get_popup()->get_item_index(DEBUG_CONTINUE), true);
3384
3385 menu_hb->add_spacer();
3386
3387 script_icon = memnew(TextureRect);
3388 menu_hb->add_child(script_icon);
3389 script_name_label = memnew(Label);
3390 menu_hb->add_child(script_name_label);
3391
3392 script_icon->hide();
3393 script_name_label->hide();
3394
3395 menu_hb->add_spacer();
3396
3397 site_search = memnew(ToolButton);
3398 site_search->set_text(TTR("Online Docs"));
3399 site_search->connect("pressed", this, "_menu_option", varray(SEARCH_WEBSITE));
3400 menu_hb->add_child(site_search);
3401 site_search->set_tooltip(TTR("Open Godot online documentation."));
3402
3403 help_search = memnew(ToolButton);
3404 help_search->set_text(TTR("Search Help"));
3405 help_search->connect("pressed", this, "_menu_option", varray(SEARCH_HELP));
3406 menu_hb->add_child(help_search);
3407 help_search->set_tooltip(TTR("Search the reference documentation."));
3408
3409 menu_hb->add_child(memnew(VSeparator));
3410
3411 script_back = memnew(ToolButton);
3412 script_back->connect("pressed", this, "_history_back");
3413 menu_hb->add_child(script_back);
3414 script_back->set_disabled(true);
3415 script_back->set_tooltip(TTR("Go to previous edited document."));
3416
3417 script_forward = memnew(ToolButton);
3418 script_forward->connect("pressed", this, "_history_forward");
3419 menu_hb->add_child(script_forward);
3420 script_forward->set_disabled(true);
3421 script_forward->set_tooltip(TTR("Go to next edited document."));
3422
3423 tab_container->connect("tab_changed", this, "_tab_changed");
3424
3425 erase_tab_confirm = memnew(ConfirmationDialog);
3426 erase_tab_confirm->get_ok()->set_text(TTR("Save"));
3427 erase_tab_confirm->add_button(TTR("Discard"), OS::get_singleton()->get_swap_ok_cancel(), "discard");
3428 erase_tab_confirm->connect("confirmed", this, "_close_current_tab");
3429 erase_tab_confirm->connect("custom_action", this, "_close_discard_current_tab");
3430 add_child(erase_tab_confirm);
3431
3432 script_create_dialog = memnew(ScriptCreateDialog);
3433 script_create_dialog->set_title(TTR("Create Script"));
3434 add_child(script_create_dialog);
3435 script_create_dialog->connect("script_created", this, "_script_created");
3436
3437 file_dialog_option = -1;
3438 file_dialog = memnew(EditorFileDialog);
3439 add_child(file_dialog);
3440 file_dialog->connect("file_selected", this, "_file_dialog_action");
3441
3442 error_dialog = memnew(AcceptDialog);
3443 add_child(error_dialog);
3444
3445 debugger = memnew(ScriptEditorDebugger(editor));
3446 debugger->connect("goto_script_line", this, "_goto_script_line");
3447 debugger->connect("set_execution", this, "_set_execution");
3448 debugger->connect("clear_execution", this, "_clear_execution");
3449 debugger->connect("show_debugger", this, "_show_debugger");
3450
3451 disk_changed = memnew(ConfirmationDialog);
3452 {
3453 VBoxContainer *vbc = memnew(VBoxContainer);
3454 disk_changed->add_child(vbc);
3455
3456 Label *dl = memnew(Label);
3457 dl->set_text(TTR("The following files are newer on disk.\nWhat action should be taken?:"));
3458 vbc->add_child(dl);
3459
3460 disk_changed_list = memnew(Tree);
3461 vbc->add_child(disk_changed_list);
3462 disk_changed_list->set_v_size_flags(SIZE_EXPAND_FILL);
3463
3464 disk_changed->connect("confirmed", this, "_reload_scripts");
3465 disk_changed->get_ok()->set_text(TTR("Reload"));
3466
3467 disk_changed->add_button(TTR("Resave"), !OS::get_singleton()->get_swap_ok_cancel(), "resave");
3468 disk_changed->connect("custom_action", this, "_resave_scripts");
3469 }
3470
3471 add_child(disk_changed);
3472
3473 script_editor = this;
3474
3475 Button *db = EditorNode::get_singleton()->add_bottom_panel_item(TTR("Debugger"), debugger);
3476 debugger->set_tool_button(db);
3477
3478 debugger->connect("breaked", this, "_breaked");
3479
3480 autosave_timer = memnew(Timer);
3481 autosave_timer->set_one_shot(false);
3482 autosave_timer->connect(SceneStringNames::get_singleton()->tree_entered, this, "_update_autosave_timer");
3483 autosave_timer->connect("timeout", this, "_autosave_scripts");
3484 add_child(autosave_timer);
3485
3486 grab_focus_block = false;
3487
3488 help_search_dialog = memnew(EditorHelpSearch);
3489 add_child(help_search_dialog);
3490 help_search_dialog->connect("go_to_help", this, "_help_class_goto");
3491
3492 find_in_files_dialog = memnew(FindInFilesDialog);
3493 find_in_files_dialog->connect(FindInFilesDialog::SIGNAL_FIND_REQUESTED, this, "_start_find_in_files", varray(false));
3494 find_in_files_dialog->connect(FindInFilesDialog::SIGNAL_REPLACE_REQUESTED, this, "_start_find_in_files", varray(true));
3495 add_child(find_in_files_dialog);
3496 find_in_files = memnew(FindInFilesPanel);
3497 find_in_files_button = editor->add_bottom_panel_item(TTR("Search Results"), find_in_files);
3498 find_in_files->set_custom_minimum_size(Size2(0, 200) * EDSCALE);
3499 find_in_files->connect(FindInFilesPanel::SIGNAL_RESULT_SELECTED, this, "_on_find_in_files_result_selected");
3500 find_in_files->connect(FindInFilesPanel::SIGNAL_FILES_MODIFIED, this, "_on_find_in_files_modified_files");
3501 find_in_files->hide();
3502 find_in_files_button->hide();
3503
3504 history_pos = -1;
3505 //debugger_gui->hide();
3506
3507 edit_pass = 0;
3508 trim_trailing_whitespace_on_save = EditorSettings::get_singleton()->get("text_editor/files/trim_trailing_whitespace_on_save");
3509 convert_indent_on_save = EditorSettings::get_singleton()->get("text_editor/indent/convert_indent_on_save");
3510 use_space_indentation = EditorSettings::get_singleton()->get("text_editor/indent/type");
3511
3512 ScriptServer::edit_request_func = _open_script_request;
3513
3514 add_style_override("panel", editor->get_gui_base()->get_stylebox("ScriptEditorPanel", "EditorStyles"));
3515 tab_container->add_style_override("panel", editor->get_gui_base()->get_stylebox("ScriptEditor", "EditorStyles"));
3516 }
3517
~ScriptEditor()3518 ScriptEditor::~ScriptEditor() {
3519
3520 memdelete(completion_cache);
3521 }
3522
edit(Object * p_object)3523 void ScriptEditorPlugin::edit(Object *p_object) {
3524
3525 if (Object::cast_to<Script>(p_object)) {
3526
3527 Script *p_script = Object::cast_to<Script>(p_object);
3528 String res_path = p_script->get_path().get_slice("::", 0);
3529
3530 if (_is_built_in_script(p_script)) {
3531 if (ResourceLoader::get_resource_type(res_path) == "PackedScene") {
3532 if (!EditorNode::get_singleton()->is_scene_open(res_path)) {
3533 EditorNode::get_singleton()->load_scene(res_path);
3534 }
3535 } else {
3536 EditorNode::get_singleton()->load_resource(res_path);
3537 }
3538 }
3539 script_editor->edit(p_script);
3540 } else if (Object::cast_to<TextFile>(p_object)) {
3541 script_editor->edit(Object::cast_to<TextFile>(p_object));
3542 }
3543 }
3544
handles(Object * p_object) const3545 bool ScriptEditorPlugin::handles(Object *p_object) const {
3546
3547 if (Object::cast_to<TextFile>(p_object)) {
3548 return true;
3549 }
3550
3551 if (Object::cast_to<Script>(p_object)) {
3552 return true;
3553 }
3554
3555 return p_object->is_class("Script");
3556 }
3557
make_visible(bool p_visible)3558 void ScriptEditorPlugin::make_visible(bool p_visible) {
3559
3560 if (p_visible) {
3561 script_editor->show();
3562 script_editor->set_process(true);
3563 script_editor->ensure_select_current();
3564 } else {
3565
3566 script_editor->hide();
3567 script_editor->set_process(false);
3568 }
3569 }
3570
selected_notify()3571 void ScriptEditorPlugin::selected_notify() {
3572
3573 script_editor->ensure_select_current();
3574 }
3575
save_external_data()3576 void ScriptEditorPlugin::save_external_data() {
3577
3578 script_editor->save_all_scripts();
3579 }
3580
apply_changes()3581 void ScriptEditorPlugin::apply_changes() {
3582
3583 script_editor->apply_scripts();
3584 }
3585
restore_global_state()3586 void ScriptEditorPlugin::restore_global_state() {
3587 }
3588
save_global_state()3589 void ScriptEditorPlugin::save_global_state() {
3590 }
3591
set_window_layout(Ref<ConfigFile> p_layout)3592 void ScriptEditorPlugin::set_window_layout(Ref<ConfigFile> p_layout) {
3593
3594 script_editor->set_window_layout(p_layout);
3595 }
3596
get_window_layout(Ref<ConfigFile> p_layout)3597 void ScriptEditorPlugin::get_window_layout(Ref<ConfigFile> p_layout) {
3598
3599 script_editor->get_window_layout(p_layout);
3600 }
3601
get_breakpoints(List<String> * p_breakpoints)3602 void ScriptEditorPlugin::get_breakpoints(List<String> *p_breakpoints) {
3603
3604 script_editor->get_breakpoints(p_breakpoints);
3605 }
3606
edited_scene_changed()3607 void ScriptEditorPlugin::edited_scene_changed() {
3608
3609 script_editor->edited_scene_changed();
3610 }
3611
ScriptEditorPlugin(EditorNode * p_node)3612 ScriptEditorPlugin::ScriptEditorPlugin(EditorNode *p_node) {
3613
3614 editor = p_node;
3615 script_editor = memnew(ScriptEditor(p_node));
3616 editor->get_viewport()->add_child(script_editor);
3617 script_editor->set_v_size_flags(Control::SIZE_EXPAND_FILL);
3618
3619 script_editor->hide();
3620
3621 EDITOR_DEF("text_editor/files/auto_reload_scripts_on_external_change", true);
3622 ScriptServer::set_reload_scripts_on_save(EDITOR_DEF("text_editor/files/auto_reload_and_parse_scripts_on_save", true));
3623 EDITOR_DEF("text_editor/files/open_dominant_script_on_scene_change", true);
3624 EDITOR_DEF("text_editor/external/use_external_editor", false);
3625 EDITOR_DEF("text_editor/external/exec_path", "");
3626 EDITOR_DEF("text_editor/script_list/script_temperature_enabled", true);
3627 EDITOR_DEF("text_editor/script_list/highlight_current_script", true);
3628 EDITOR_DEF("text_editor/script_list/script_temperature_history_size", 15);
3629 EDITOR_DEF("text_editor/script_list/current_script_background_color", Color(1, 1, 1, 0.3));
3630 EDITOR_DEF("text_editor/script_list/group_help_pages", true);
3631 EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "text_editor/script_list/sort_scripts_by", PROPERTY_HINT_ENUM, "Name,Path,None"));
3632 EDITOR_DEF("text_editor/script_list/sort_scripts_by", 0);
3633 EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::INT, "text_editor/script_list/list_script_names_as", PROPERTY_HINT_ENUM, "Name,Parent Directory And Name,Full Path"));
3634 EDITOR_DEF("text_editor/script_list/list_script_names_as", 0);
3635 EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "text_editor/external/exec_path", PROPERTY_HINT_GLOBAL_FILE));
3636 EDITOR_DEF("text_editor/external/exec_flags", "{file}");
3637 EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "text_editor/external/exec_flags", PROPERTY_HINT_PLACEHOLDER_TEXT, "Call flags with placeholders: {project}, {file}, {col}, {line}."));
3638
3639 ED_SHORTCUT("script_editor/reopen_closed_script", TTR("Reopen Closed Script"), KEY_MASK_CMD | KEY_MASK_SHIFT | KEY_T);
3640 ED_SHORTCUT("script_editor/clear_recent", TTR("Clear Recent Scripts"));
3641 }
3642
~ScriptEditorPlugin()3643 ScriptEditorPlugin::~ScriptEditorPlugin() {
3644 }
3645