Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/editor/run/editor_run_bar.cpp
20900 views
1
/**************************************************************************/
2
/* editor_run_bar.cpp */
3
/**************************************************************************/
4
/* This file is part of: */
5
/* GODOT ENGINE */
6
/* https://godotengine.org */
7
/**************************************************************************/
8
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
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 "editor_run_bar.h"
32
33
#include "core/config/project_settings.h"
34
#include "editor/debugger/editor_debugger_node.h"
35
#include "editor/debugger/script_editor_debugger.h"
36
#include "editor/editor_node.h"
37
#include "editor/editor_string_names.h"
38
#include "editor/gui/editor_bottom_panel.h"
39
#include "editor/gui/editor_quick_open_dialog.h"
40
#include "editor/gui/editor_toaster.h"
41
#include "editor/run/editor_run_native.h"
42
#include "editor/settings/editor_command_palette.h"
43
#include "editor/settings/editor_settings.h"
44
#include "editor/settings/project_settings_editor.h"
45
#include "editor/themes/editor_scale.h"
46
#include "scene/gui/box_container.h"
47
#include "scene/gui/button.h"
48
#include "scene/gui/menu_button.h"
49
#include "scene/gui/panel_container.h"
50
51
#ifndef XR_DISABLED
52
#include "servers/xr/xr_server.h"
53
#endif // XR_DISABLED
54
55
EditorRunBar *EditorRunBar::singleton = nullptr;
56
57
void EditorRunBar::_notification(int p_what) {
58
switch (p_what) {
59
case NOTIFICATION_POSTINITIALIZE: {
60
_reset_play_buttons();
61
} break;
62
63
case NOTIFICATION_READY: {
64
if (Engine::get_singleton()->is_recovery_mode_hint()) {
65
recovery_mode_show_dialog();
66
}
67
} break;
68
69
case NOTIFICATION_THEME_CHANGED: {
70
if (Engine::get_singleton()->is_recovery_mode_hint()) {
71
main_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("LaunchPadRecoveryMode"), EditorStringName(EditorStyles)));
72
recovery_mode_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("RecoveryModeButton"), EditorStringName(EditorStyles)));
73
recovery_mode_button->add_theme_style_override("hover", get_theme_stylebox(SNAME("RecoveryModeButton"), EditorStringName(EditorStyles)));
74
75
recovery_mode_button->set_button_icon(get_editor_theme_icon(SNAME("NodeWarning")));
76
recovery_mode_reload_button->set_button_icon(get_editor_theme_icon(SNAME("Reload")));
77
78
recovery_mode_button->begin_bulk_theme_override();
79
recovery_mode_button->add_theme_color_override("icon_normal_color", Color(0.3, 0.3, 0.3, 1));
80
recovery_mode_button->add_theme_color_override("icon_pressed_color", Color(0.4, 0.4, 0.4, 1));
81
recovery_mode_button->add_theme_color_override("icon_hover_color", Color(0.6, 0.6, 0.6, 1));
82
Color dark_color = get_theme_color("recovery_mode_text_color", EditorStringName(Editor));
83
recovery_mode_button->add_theme_color_override(SceneStringName(font_color), dark_color);
84
recovery_mode_button->add_theme_color_override("font_pressed_color", dark_color.lightened(0.2));
85
recovery_mode_button->add_theme_color_override("font_hover_color", dark_color.lightened(0.4));
86
recovery_mode_button->add_theme_color_override("font_hover_pressed_color", dark_color.lightened(0.2));
87
recovery_mode_button->end_bulk_theme_override();
88
89
return;
90
}
91
92
_update_play_buttons();
93
profiler_autostart_indicator->set_button_icon(get_editor_theme_icon(SNAME("ProfilerAutostartWarning")));
94
pause_button->set_button_icon(get_editor_theme_icon(SNAME("Pause")));
95
stop_button->set_button_icon(get_editor_theme_icon(SNAME("Stop")));
96
97
if (is_movie_maker_enabled()) {
98
main_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("LaunchPadMovieMode"), EditorStringName(EditorStyles)));
99
write_movie_button->set_theme_type_variation("RunBarButtonMovieMakerEnabled");
100
101
write_movie_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("MovieWriterButtonPressed"), EditorStringName(EditorStyles)));
102
} else {
103
main_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("LaunchPadNormal"), EditorStringName(EditorStyles)));
104
write_movie_button->set_theme_type_variation("RunBarButtonMovieMakerDisabled");
105
106
write_movie_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("MovieWriterButtonNormal"), EditorStringName(EditorStyles)));
107
}
108
109
write_movie_button->set_button_icon(get_editor_theme_icon(SNAME("MainMovieWrite")));
110
111
} break;
112
}
113
}
114
115
void EditorRunBar::_reset_play_buttons() {
116
if (Engine::get_singleton()->is_recovery_mode_hint()) {
117
return;
118
}
119
120
play_button->set_pressed(false);
121
play_button->set_button_icon(get_editor_theme_icon(SNAME("MainPlay")));
122
play_button->set_tooltip_text(TTRC("Run the project's main scene."));
123
124
play_scene_button->set_pressed(false);
125
play_scene_button->set_button_icon(get_editor_theme_icon(SNAME("PlayScene")));
126
play_scene_button->set_tooltip_text(TTRC("Play the currently edited scene."));
127
128
play_custom_scene_button->set_pressed(false);
129
play_custom_scene_button->set_button_icon(get_editor_theme_icon(SNAME("PlayCustom")));
130
play_custom_scene_button->set_tooltip_text(TTRC("Play a custom scene."));
131
}
132
133
void EditorRunBar::_update_play_buttons() {
134
if (Engine::get_singleton()->is_recovery_mode_hint()) {
135
return;
136
}
137
138
_reset_play_buttons();
139
if (!is_playing()) {
140
return;
141
}
142
143
Button *active_button = nullptr;
144
if (current_mode == RUN_CURRENT) {
145
active_button = play_scene_button;
146
active_button->set_tooltip_text(TTRC("Reload the played scene that was being edited."));
147
} else if (current_mode == RUN_CUSTOM) {
148
active_button = play_custom_scene_button;
149
active_button->set_tooltip_text(TTRC("Reload the played custom scene."));
150
} else {
151
active_button = play_button;
152
active_button->set_tooltip_text(TTRC("Reload the played main scene."));
153
}
154
155
if (active_button) {
156
active_button->set_pressed(true);
157
active_button->set_button_icon(get_editor_theme_icon(SNAME("Reload")));
158
}
159
}
160
161
void EditorRunBar::_movie_maker_item_pressed(int p_id) {
162
switch (p_id) {
163
case MOVIE_MAKER_TOGGLE: {
164
bool new_enabled = !is_movie_maker_enabled();
165
set_movie_maker_enabled(new_enabled);
166
write_movie_button->get_popup()->set_item_checked(0, new_enabled);
167
write_movie_button->set_pressed(new_enabled);
168
_write_movie_toggled(new_enabled);
169
break;
170
}
171
case MOVIE_MAKER_OPEN_SETTINGS:
172
ProjectSettingsEditor::get_singleton()->popup_project_settings(true);
173
ProjectSettingsEditor::get_singleton()->set_general_page("editor/movie_writer");
174
break;
175
}
176
}
177
178
void EditorRunBar::_write_movie_toggled(bool p_enabled) {
179
if (p_enabled) {
180
add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("LaunchPadMovieMode"), EditorStringName(EditorStyles)));
181
write_movie_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("MovieWriterButtonPressed"), EditorStringName(EditorStyles)));
182
} else {
183
add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("LaunchPadNormal"), EditorStringName(EditorStyles)));
184
write_movie_panel->add_theme_style_override(SceneStringName(panel), get_theme_stylebox(SNAME("MovieWriterButtonNormal"), EditorStringName(EditorStyles)));
185
}
186
}
187
188
Vector<String> EditorRunBar::_get_xr_mode_play_args(RunXRModeMenuItem p_menu_item) {
189
Vector<String> play_args;
190
if (p_menu_item == RunXRModeMenuItem::OFF) {
191
// Play in regular mode, xr mode off.
192
play_args.push_back("--xr-mode");
193
play_args.push_back("off");
194
} else if (p_menu_item == RunXRModeMenuItem::ON) {
195
// Play in xr mode.
196
play_args.push_back("--xr-mode");
197
play_args.push_back("on");
198
}
199
return play_args;
200
}
201
202
void EditorRunBar::_quick_run_selected(const String &p_file_path, int p_menu_item) {
203
play_custom_scene(p_file_path, _get_xr_mode_play_args(static_cast<RunXRModeMenuItem>(p_menu_item)));
204
}
205
206
void EditorRunBar::_play_custom_pressed(int p_menu_item) {
207
if (p_menu_item != RunXRModeMenuItem::INVALID) {
208
MenuButton *menu_button = Object::cast_to<MenuButton>(play_custom_scene_button);
209
if (menu_button && menu_button->get_popup()) {
210
// Set the shortcut to the last selected option.
211
menu_button->get_popup()->set_item_shortcut(RunXRModeMenuItem::OFF, p_menu_item == RunXRModeMenuItem::OFF ? ED_GET_SHORTCUT("editor/run_specific_scene") : Ref<Shortcut>());
212
menu_button->get_popup()->set_item_shortcut(RunXRModeMenuItem::ON, p_menu_item == RunXRModeMenuItem::ON ? ED_GET_SHORTCUT("editor/run_specific_scene") : Ref<Shortcut>());
213
}
214
}
215
216
if (editor_run.get_status() == EditorRun::STATUS_STOP || current_mode != RunMode::RUN_CUSTOM) {
217
stop_playing();
218
219
EditorNode::get_singleton()->get_quick_open_dialog()->popup_dialog({ "PackedScene" }, callable_mp(this, &EditorRunBar::_quick_run_selected).bind(p_menu_item));
220
play_custom_scene_button->set_pressed(false);
221
} else {
222
Vector<String> play_args = _get_xr_mode_play_args(static_cast<RunXRModeMenuItem>(p_menu_item));
223
224
// Reload if already running a custom scene.
225
String last_custom_scene = run_custom_filename; // This is necessary to have a copy of the string.
226
play_custom_scene(last_custom_scene, play_args);
227
}
228
}
229
230
void EditorRunBar::_play_current_pressed(int p_menu_item) {
231
if (p_menu_item != RunXRModeMenuItem::INVALID) {
232
MenuButton *menu_button = Object::cast_to<MenuButton>(play_scene_button);
233
if (menu_button && menu_button->get_popup()) {
234
// Set the shortcut to the last selected option.
235
menu_button->get_popup()->set_item_shortcut(RunXRModeMenuItem::OFF, p_menu_item == RunXRModeMenuItem::OFF ? ED_GET_SHORTCUT("editor/run_current_scene") : Ref<Shortcut>());
236
menu_button->get_popup()->set_item_shortcut(RunXRModeMenuItem::ON, p_menu_item == RunXRModeMenuItem::ON ? ED_GET_SHORTCUT("editor/run_current_scene") : Ref<Shortcut>());
237
}
238
}
239
240
Vector<String> play_args = _get_xr_mode_play_args(static_cast<RunXRModeMenuItem>(p_menu_item));
241
242
if (editor_run.get_status() == EditorRun::STATUS_STOP || current_mode != RunMode::RUN_CURRENT) {
243
play_current_scene(false, play_args);
244
} else {
245
// Reload if already running the current scene.
246
play_current_scene(true, play_args);
247
}
248
}
249
250
void EditorRunBar::_run_scene(const String &p_scene_path, const Vector<String> &p_run_args) {
251
ERR_FAIL_COND_MSG(current_mode == RUN_CUSTOM && p_scene_path.is_empty(), "Attempting to run a custom scene with an empty path.");
252
253
if (editor_run.get_status() == EditorRun::STATUS_PLAY) {
254
return;
255
}
256
257
if (!EditorNode::get_singleton()->validate_custom_directory()) {
258
return;
259
}
260
261
_reset_play_buttons();
262
263
String write_movie_file;
264
if (is_movie_maker_enabled()) {
265
if (current_mode == RUN_CURRENT) {
266
Node *scene_root = nullptr;
267
if (p_scene_path.is_empty()) {
268
scene_root = get_tree()->get_edited_scene_root();
269
} else {
270
int scene_index = EditorNode::get_editor_data().get_edited_scene_from_path(p_scene_path);
271
if (scene_index >= 0) {
272
scene_root = EditorNode::get_editor_data().get_edited_scene_root(scene_index);
273
}
274
}
275
276
if (scene_root && scene_root->has_meta("movie_file")) {
277
// If the scene file has a movie_file metadata set, use this as file.
278
// Quick workaround if you want to have multiple scenes that write to
279
// multiple movies.
280
write_movie_file = scene_root->get_meta("movie_file");
281
}
282
}
283
284
if (write_movie_file.is_empty()) {
285
write_movie_file = GLOBAL_GET("editor/movie_writer/movie_file");
286
}
287
288
if (write_movie_file.is_empty()) {
289
// TODO: Provide options to directly resolve the issue with a custom dialog.
290
EditorNode::get_singleton()->show_accept(TTR("Movie Maker mode is enabled, but no movie file path has been specified.\nA default movie file path can be specified in the project settings under the Editor > Movie Writer category.\nAlternatively, for running single scenes, a `movie_file` string metadata can be added to the root node,\nspecifying the path to a movie file that will be used when recording that scene."), TTR("OK"));
291
return;
292
}
293
}
294
295
String run_filename;
296
switch (current_mode) {
297
case RUN_CUSTOM: {
298
run_filename = ResourceUID::ensure_path(p_scene_path);
299
run_custom_filename = run_filename;
300
} break;
301
302
case RUN_CURRENT: {
303
if (!p_scene_path.is_empty()) {
304
run_filename = p_scene_path;
305
run_current_filename = run_filename;
306
break;
307
}
308
309
Node *scene_root = get_tree()->get_edited_scene_root();
310
if (!scene_root) {
311
EditorNode::get_singleton()->show_accept(TTR("There is no defined scene to run."), TTR("OK"));
312
return;
313
}
314
315
if (scene_root->get_scene_file_path().is_empty()) {
316
EditorNode::get_singleton()->save_before_run();
317
return;
318
}
319
320
run_filename = scene_root->get_scene_file_path();
321
run_current_filename = run_filename;
322
} break;
323
324
default: {
325
if (!EditorNode::get_singleton()->ensure_main_scene(false)) {
326
return;
327
}
328
329
run_filename = GLOBAL_GET("application/run/main_scene");
330
} break;
331
}
332
333
EditorNode::get_singleton()->try_autosave();
334
if (!EditorNode::get_singleton()->call_build()) {
335
return;
336
}
337
338
Vector<String> args = p_run_args;
339
EditorNode::get_singleton()->call_run_scene(run_filename, args);
340
341
// Use the existing URI, in case it is overridden by the CLI.
342
String uri = EditorDebuggerNode::get_singleton()->get_server_uri();
343
if (uri.is_empty()) {
344
uri = "tcp://";
345
}
346
EditorDebuggerNode::get_singleton()->start(uri);
347
Error error = editor_run.run(run_filename, write_movie_file, args);
348
if (error != OK) {
349
EditorDebuggerNode::get_singleton()->stop();
350
EditorNode::get_singleton()->show_accept(TTR("Could not start subprocess(es)!"), TTR("OK"));
351
return;
352
}
353
354
_update_play_buttons();
355
stop_button->set_disabled(false);
356
357
emit_signal(SNAME("play_pressed"));
358
}
359
360
void EditorRunBar::_run_native(const Ref<EditorExportPreset> &p_preset) {
361
EditorNode::get_singleton()->try_autosave();
362
363
if (run_native->is_deploy_debug_remote_enabled()) {
364
stop_playing();
365
366
if (!EditorNode::get_singleton()->call_build()) {
367
return; // Build failed.
368
}
369
370
EditorDebuggerNode::get_singleton()->start(p_preset->get_platform()->get_debug_protocol());
371
emit_signal(SNAME("play_pressed"));
372
editor_run.run_native_notify();
373
}
374
}
375
376
void EditorRunBar::_profiler_autostart_indicator_pressed() {
377
// Switch to the first profiler tab in the bottom panel.
378
EditorDebuggerNode::get_singleton()->make_visible();
379
380
if (EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_profiler", false)) {
381
EditorDebuggerNode::get_singleton()->get_current_debugger()->switch_to_debugger(3);
382
} else if (EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_visual_profiler", false)) {
383
EditorDebuggerNode::get_singleton()->get_current_debugger()->switch_to_debugger(4);
384
} else {
385
// Switch to the network profiler tab.
386
EditorDebuggerNode::get_singleton()->get_current_debugger()->switch_to_debugger(8);
387
}
388
}
389
390
void EditorRunBar::recovery_mode_show_dialog() {
391
recovery_mode_popup->popup_centered();
392
}
393
394
void EditorRunBar::recovery_mode_reload_project() {
395
EditorNode::get_singleton()->trigger_menu_option(EditorNode::PROJECT_RELOAD_CURRENT_PROJECT, false);
396
}
397
398
void EditorRunBar::play_main_scene(bool p_from_native, const Vector<String> &p_play_args) {
399
if (Engine::get_singleton()->is_recovery_mode_hint()) {
400
EditorToaster::get_singleton()->popup_str(TTR("Recovery Mode is enabled. Disable it to run the project."), EditorToaster::SEVERITY_WARNING);
401
return;
402
}
403
404
if (p_from_native) {
405
run_native->resume_run_native();
406
} else {
407
stop_playing();
408
409
current_mode = RunMode::RUN_MAIN;
410
_run_scene("", p_play_args);
411
}
412
}
413
414
void EditorRunBar::play_current_scene(bool p_reload, const Vector<String> &p_play_args) {
415
if (Engine::get_singleton()->is_recovery_mode_hint()) {
416
EditorToaster::get_singleton()->popup_str(TTR("Recovery Mode is enabled. Disable it to run the project."), EditorToaster::SEVERITY_WARNING);
417
return;
418
}
419
420
String last_current_scene = run_current_filename; // This is necessary to have a copy of the string.
421
422
EditorNode::get_singleton()->save_default_environment();
423
stop_playing();
424
425
current_mode = RunMode::RUN_CURRENT;
426
if (p_reload) {
427
_run_scene(last_current_scene, p_play_args);
428
} else {
429
_run_scene("", p_play_args);
430
}
431
}
432
433
void EditorRunBar::play_custom_scene(const String &p_custom, const Vector<String> &p_play_args) {
434
if (Engine::get_singleton()->is_recovery_mode_hint()) {
435
EditorToaster::get_singleton()->popup_str(TTR("Recovery Mode is enabled. Disable it to run the project."), EditorToaster::SEVERITY_WARNING);
436
return;
437
}
438
439
stop_playing();
440
441
current_mode = RunMode::RUN_CUSTOM;
442
_run_scene(p_custom, p_play_args);
443
}
444
445
void EditorRunBar::stop_playing() {
446
if (editor_run.get_status() == EditorRun::STATUS_STOP) {
447
return;
448
}
449
450
current_mode = RunMode::STOPPED;
451
editor_run.stop();
452
EditorDebuggerNode::get_singleton()->stop();
453
454
run_custom_filename.clear();
455
run_current_filename.clear();
456
stop_button->set_pressed(false);
457
stop_button->set_disabled(true);
458
_reset_play_buttons();
459
460
emit_signal(SNAME("stop_pressed"));
461
}
462
463
bool EditorRunBar::is_playing() const {
464
EditorRun::Status status = editor_run.get_status();
465
return (status == EditorRun::STATUS_PLAY || status == EditorRun::STATUS_PAUSED);
466
}
467
468
String EditorRunBar::get_playing_scene() const {
469
String run_filename = editor_run.get_running_scene();
470
if (run_filename.is_empty() && is_playing()) {
471
run_filename = GLOBAL_GET("application/run/main_scene"); // Must be the main scene then.
472
}
473
474
return run_filename;
475
}
476
477
Error EditorRunBar::start_native_device(int p_device_id) const {
478
return run_native->start_run_native(p_device_id);
479
}
480
481
OS::ProcessID EditorRunBar::has_child_process(OS::ProcessID p_pid) const {
482
return editor_run.has_child_process(p_pid);
483
}
484
485
void EditorRunBar::stop_child_process(OS::ProcessID p_pid) {
486
if (!has_child_process(p_pid)) {
487
return;
488
}
489
490
editor_run.stop_child_process(p_pid);
491
if (!editor_run.get_child_process_count()) { // All children stopped. Closing.
492
stop_playing();
493
}
494
}
495
496
OS::ProcessID EditorRunBar::get_current_process() const {
497
return editor_run.get_current_process();
498
}
499
500
void EditorRunBar::set_movie_maker_enabled(bool p_enabled) {
501
movie_maker_enabled = p_enabled;
502
write_movie_button->get_popup()->set_item_checked(0, p_enabled);
503
}
504
505
bool EditorRunBar::is_movie_maker_enabled() const {
506
return movie_maker_enabled;
507
}
508
509
void EditorRunBar::update_profiler_autostart_indicator() {
510
bool profiler_active = EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_profiler", false);
511
bool visual_profiler_active = EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_visual_profiler", false);
512
bool network_profiler_active = EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_network_profiler", false);
513
bool any_profiler_active = profiler_active | visual_profiler_active | network_profiler_active;
514
any_profiler_active &= !Engine::get_singleton()->is_recovery_mode_hint();
515
profiler_autostart_indicator->set_visible(any_profiler_active);
516
if (any_profiler_active) {
517
String tooltip = TTR("Autostart is enabled for the following profilers, which can have a performance impact:");
518
if (profiler_active) {
519
tooltip += "\n- " + TTR("Profiler");
520
}
521
if (visual_profiler_active) {
522
tooltip += "\n- " + TTR("Visual Profiler");
523
}
524
if (network_profiler_active) {
525
tooltip += "\n- " + TTR("Network Profiler");
526
}
527
tooltip += "\n\n" + TTR("Click to open the first profiler for which autostart is enabled.");
528
profiler_autostart_indicator->set_tooltip_text(tooltip);
529
}
530
}
531
532
HBoxContainer *EditorRunBar::get_buttons_container() {
533
return main_hbox;
534
}
535
536
void EditorRunBar::_bind_methods() {
537
ADD_SIGNAL(MethodInfo("play_pressed"));
538
ADD_SIGNAL(MethodInfo("stop_pressed"));
539
}
540
541
EditorRunBar::EditorRunBar() {
542
singleton = this;
543
544
outer_hbox = memnew(HBoxContainer);
545
add_child(outer_hbox);
546
547
// Use a button for the indicator since it comes with a background panel and pixel perfect centering of an icon.
548
profiler_autostart_indicator = memnew(Button);
549
profiler_autostart_indicator->set_tooltip_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
550
profiler_autostart_indicator->set_icon_alignment(HORIZONTAL_ALIGNMENT_CENTER);
551
profiler_autostart_indicator->set_focus_mode(FOCUS_ACCESSIBILITY);
552
profiler_autostart_indicator->set_theme_type_variation("ProfilerAutostartIndicator");
553
profiler_autostart_indicator->connect(SceneStringName(pressed), callable_mp(this, &EditorRunBar::_profiler_autostart_indicator_pressed));
554
outer_hbox->add_child(profiler_autostart_indicator);
555
update_profiler_autostart_indicator();
556
557
main_panel = memnew(PanelContainer);
558
outer_hbox->add_child(main_panel);
559
560
main_hbox = memnew(HBoxContainer);
561
main_panel->add_child(main_hbox);
562
563
if (Engine::get_singleton()->is_recovery_mode_hint()) {
564
recovery_mode_popup = memnew(AcceptDialog);
565
recovery_mode_popup->set_min_size(Size2(550, 70) * EDSCALE);
566
recovery_mode_popup->set_title(TTR("Recovery Mode"));
567
recovery_mode_popup->set_text(
568
TTR("Godot opened the project in Recovery Mode, which is a special mode that can help recover projects that crash the engine upon initialization. The following features have been temporarily disabled:") +
569
String::utf8("\n\n• ") + TTR("Tool scripts") +
570
String::utf8("\n• ") + TTR("Editor plugins") +
571
String::utf8("\n• ") + TTR("GDExtension addons") +
572
String::utf8("\n• ") + TTR("Automatic scene restoring") +
573
String::utf8("\n\n") + TTR("If the project cannot be opened outside of this mode, then it's very likely any of these components is preventing this project from launching. This mode is intended only for basic editing to troubleshoot such issues, and therefore it is not possible to run a project in this mode.") +
574
String::utf8("\n\n") + TTR("To disable Recovery Mode, reload the project by pressing the Reload button next to the Recovery Mode banner, or by reopening the project normally."));
575
recovery_mode_popup->set_autowrap(true);
576
add_child(recovery_mode_popup);
577
578
recovery_mode_reload_button = memnew(Button);
579
main_hbox->add_child(recovery_mode_reload_button);
580
recovery_mode_reload_button->set_theme_type_variation("RunBarButton");
581
recovery_mode_reload_button->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
582
recovery_mode_reload_button->set_tooltip_text(TTR("Disable recovery mode and reload the project."));
583
recovery_mode_reload_button->connect(SceneStringName(pressed), callable_mp(this, &EditorRunBar::recovery_mode_reload_project));
584
585
recovery_mode_panel = memnew(PanelContainer);
586
main_hbox->add_child(recovery_mode_panel);
587
588
recovery_mode_button = memnew(Button);
589
recovery_mode_panel->add_child(recovery_mode_button);
590
recovery_mode_button->set_theme_type_variation("RunBarButton");
591
recovery_mode_button->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
592
recovery_mode_button->set_text(TTR("Recovery Mode"));
593
recovery_mode_button->set_tooltip_text(TTR("Recovery Mode is enabled. Click for more details."));
594
recovery_mode_button->connect(SceneStringName(pressed), callable_mp(this, &EditorRunBar::recovery_mode_show_dialog));
595
596
return;
597
}
598
599
play_button = memnew(Button);
600
main_hbox->add_child(play_button);
601
play_button->set_theme_type_variation("RunBarButton");
602
play_button->set_toggle_mode(true);
603
play_button->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
604
ED_SHORTCUT_AND_COMMAND("editor/run_project", TTRC("Run Project"), Key::F5);
605
ED_SHORTCUT_OVERRIDE("editor/run_project", "macos", KeyModifierMask::META | Key::B);
606
play_button->set_shortcut(ED_GET_SHORTCUT("editor/run_project"));
607
play_button->set_tooltip_text(TTRC("Run the project's main scene."));
608
play_button->connect(SceneStringName(pressed), callable_mp(this, &EditorRunBar::play_main_scene).bind(false, Vector<String>()));
609
610
pause_button = memnew(Button);
611
main_hbox->add_child(pause_button);
612
pause_button->set_theme_type_variation("RunBarButton");
613
pause_button->set_toggle_mode(true);
614
pause_button->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
615
pause_button->set_tooltip_text(TTRC("Pause the running project's execution for debugging."));
616
pause_button->set_disabled(true);
617
618
ED_SHORTCUT("editor/pause_running_project", TTRC("Pause Running Project"), Key::F7);
619
ED_SHORTCUT_OVERRIDE("editor/pause_running_project", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::Y);
620
pause_button->set_shortcut(ED_GET_SHORTCUT("editor/pause_running_project"));
621
622
stop_button = memnew(Button);
623
main_hbox->add_child(stop_button);
624
stop_button->set_theme_type_variation("RunBarButton");
625
stop_button->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
626
stop_button->set_tooltip_text(TTRC("Stop the currently running project."));
627
stop_button->set_disabled(true);
628
stop_button->connect(SceneStringName(pressed), callable_mp(this, &EditorRunBar::stop_playing));
629
630
ED_SHORTCUT("editor/stop_running_project", TTRC("Stop Running Project"), Key::F8);
631
ED_SHORTCUT_OVERRIDE("editor/stop_running_project", "macos", KeyModifierMask::META | Key::PERIOD);
632
stop_button->set_shortcut(ED_GET_SHORTCUT("editor/stop_running_project"));
633
634
run_native = memnew(EditorRunNative);
635
main_hbox->add_child(run_native);
636
run_native->connect("native_run", callable_mp(this, &EditorRunBar::_run_native));
637
638
bool add_play_xr_mode_options = false;
639
#ifndef XR_DISABLED
640
if (XRServer::get_xr_mode() == XRServer::XRMODE_ON ||
641
(XRServer::get_xr_mode() == XRServer::XRMODE_DEFAULT && GLOBAL_GET("xr/openxr/enabled"))) {
642
// If OpenXR is enabled, we turn the `play_scene_button` and
643
// `play_custom_scene_button` into MenuButtons to provide the option to start a scene in
644
// either regular mode or XR mode.
645
add_play_xr_mode_options = true;
646
}
647
#endif // XR_DISABLED
648
649
ED_SHORTCUT_AND_COMMAND("editor/run_current_scene", TTRC("Run Current Scene"), Key::F6);
650
ED_SHORTCUT_OVERRIDE("editor/run_current_scene", "macos", KeyModifierMask::META | Key::R);
651
652
if (add_play_xr_mode_options) {
653
MenuButton *menu_button = memnew(MenuButton);
654
PopupMenu *popup = menu_button->get_popup();
655
popup->add_item(TTRC("Run Scene in Regular Mode"), RunXRModeMenuItem::OFF);
656
popup->add_item(TTRC("Run Scene in XR Mode"), RunXRModeMenuItem::ON);
657
popup->connect(SceneStringName(id_pressed), callable_mp(this, &EditorRunBar::_play_current_pressed));
658
popup->set_item_shortcut(RunXRModeMenuItem::ON, ED_GET_SHORTCUT("editor/run_current_scene"));
659
play_scene_button = menu_button;
660
} else {
661
play_scene_button = memnew(Button);
662
play_scene_button->set_toggle_mode(true);
663
play_scene_button->connect(SceneStringName(pressed), callable_mp(this, &EditorRunBar::_play_current_pressed).bind(RunXRModeMenuItem::INVALID));
664
play_scene_button->set_shortcut(ED_GET_SHORTCUT("editor/run_current_scene"));
665
}
666
main_hbox->add_child(play_scene_button);
667
668
play_scene_button->set_tooltip_text(TTRC("Play the currently edited scene."));
669
play_scene_button->set_theme_type_variation("RunBarButton");
670
play_scene_button->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
671
672
ED_SHORTCUT_AND_COMMAND("editor/run_specific_scene", TTRC("Run Specific Scene"), KeyModifierMask::CTRL | KeyModifierMask::SHIFT | Key::F5);
673
ED_SHORTCUT_OVERRIDE("editor/run_specific_scene", "macos", KeyModifierMask::META | KeyModifierMask::SHIFT | Key::R);
674
675
if (add_play_xr_mode_options) {
676
MenuButton *menu_button = memnew(MenuButton);
677
PopupMenu *popup = menu_button->get_popup();
678
popup->add_item(TTRC("Run in Regular Mode"), RunXRModeMenuItem::OFF);
679
popup->add_item(TTRC("Run in XR Mode"), RunXRModeMenuItem::ON);
680
popup->connect(SceneStringName(id_pressed), callable_mp(this, &EditorRunBar::_play_custom_pressed));
681
popup->set_item_shortcut(RunXRModeMenuItem::ON, ED_GET_SHORTCUT("editor/run_specific_scene"));
682
play_custom_scene_button = menu_button;
683
} else {
684
play_custom_scene_button = memnew(Button);
685
play_custom_scene_button->set_toggle_mode(true);
686
play_custom_scene_button->connect(SceneStringName(pressed), callable_mp(this, &EditorRunBar::_play_custom_pressed).bind(RunXRModeMenuItem::INVALID));
687
play_custom_scene_button->set_shortcut(ED_GET_SHORTCUT("editor/run_specific_scene"));
688
}
689
main_hbox->add_child(play_custom_scene_button);
690
691
play_custom_scene_button->set_tooltip_text(TTRC("Play a custom scene."));
692
play_custom_scene_button->set_theme_type_variation("RunBarButton");
693
play_custom_scene_button->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
694
695
write_movie_panel = memnew(PanelContainer);
696
main_hbox->add_child(write_movie_panel);
697
698
write_movie_button = memnew(MenuButton);
699
PopupMenu *write_movie_popup = write_movie_button->get_popup();
700
write_movie_popup->add_check_item(TTRC("Enable Movie Maker Mode"), MOVIE_MAKER_TOGGLE);
701
write_movie_popup->add_item(TTRC("Open Movie Maker Settings..."), MOVIE_MAKER_OPEN_SETTINGS);
702
write_movie_popup->connect(SceneStringName(id_pressed), callable_mp(this, &EditorRunBar::_movie_maker_item_pressed));
703
704
write_movie_panel->add_child(write_movie_button);
705
write_movie_button->set_theme_type_variation("RunBarButtonMovieMakerDisabled");
706
write_movie_button->set_focus_mode(Control::FOCUS_ACCESSIBILITY);
707
write_movie_button->set_tooltip_text(TTRC("Enable Movie Maker mode.\nThe project will run at stable FPS and the visual and audio output will be recorded to a video file."));
708
write_movie_button->set_accessibility_name(TTRC("Enable Movie Maker Mode"));
709
write_movie_button->set_flat(false);
710
}
711
712