editor_run_bar.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  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. #include "editor_run_bar.h"
  31. #include "core/config/project_settings.h"
  32. #include "editor/debugger/editor_debugger_node.h"
  33. #include "editor/editor_command_palette.h"
  34. #include "editor/editor_node.h"
  35. #include "editor/editor_quick_open.h"
  36. #include "editor/editor_run_native.h"
  37. #include "editor/editor_settings.h"
  38. #include "scene/gui/box_container.h"
  39. #include "scene/gui/button.h"
  40. #include "scene/gui/panel_container.h"
  41. EditorRunBar *EditorRunBar::singleton = nullptr;
  42. void EditorRunBar::_notification(int p_what) {
  43. switch (p_what) {
  44. case NOTIFICATION_POSTINITIALIZE: {
  45. _reset_play_buttons();
  46. } break;
  47. case NOTIFICATION_THEME_CHANGED: {
  48. _update_play_buttons();
  49. pause_button->set_icon(get_theme_icon(SNAME("Pause"), SNAME("EditorIcons")));
  50. stop_button->set_icon(get_theme_icon(SNAME("Stop"), SNAME("EditorIcons")));
  51. if (is_movie_maker_enabled()) {
  52. main_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("LaunchPadMovieMode"), SNAME("EditorStyles")));
  53. write_movie_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("MovieWriterButtonPressed"), SNAME("EditorStyles")));
  54. } else {
  55. main_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("LaunchPadNormal"), SNAME("EditorStyles")));
  56. write_movie_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("MovieWriterButtonNormal"), SNAME("EditorStyles")));
  57. }
  58. write_movie_button->set_icon(get_theme_icon(SNAME("MainMovieWrite"), SNAME("EditorIcons")));
  59. // This button behaves differently, so color it as such.
  60. write_movie_button->add_theme_color_override("icon_normal_color", Color(1, 1, 1, 0.7));
  61. write_movie_button->add_theme_color_override("icon_pressed_color", Color(0, 0, 0, 0.84));
  62. write_movie_button->add_theme_color_override("icon_hover_color", Color(1, 1, 1, 0.9));
  63. } break;
  64. }
  65. }
  66. void EditorRunBar::_reset_play_buttons() {
  67. play_button->set_pressed(false);
  68. play_button->set_icon(get_theme_icon(SNAME("MainPlay"), SNAME("EditorIcons")));
  69. play_button->set_tooltip_text(TTR("Play the project."));
  70. play_scene_button->set_pressed(false);
  71. play_scene_button->set_icon(get_theme_icon(SNAME("PlayScene"), SNAME("EditorIcons")));
  72. play_scene_button->set_tooltip_text(TTR("Play the edited scene."));
  73. play_custom_scene_button->set_pressed(false);
  74. play_custom_scene_button->set_icon(get_theme_icon(SNAME("PlayCustom"), SNAME("EditorIcons")));
  75. play_custom_scene_button->set_tooltip_text(TTR("Play a custom scene."));
  76. }
  77. void EditorRunBar::_update_play_buttons() {
  78. _reset_play_buttons();
  79. if (!is_playing()) {
  80. return;
  81. }
  82. Button *active_button = nullptr;
  83. if (current_mode == RUN_CURRENT) {
  84. active_button = play_scene_button;
  85. } else if (current_mode == RUN_CUSTOM) {
  86. active_button = play_custom_scene_button;
  87. } else {
  88. active_button = play_button;
  89. }
  90. if (active_button) {
  91. active_button->set_pressed(true);
  92. active_button->set_icon(get_theme_icon(SNAME("Reload"), SNAME("EditorIcons")));
  93. active_button->set_tooltip_text(TTR("Reload the played scene."));
  94. }
  95. }
  96. void EditorRunBar::_write_movie_toggled(bool p_enabled) {
  97. if (p_enabled) {
  98. add_theme_style_override("panel", get_theme_stylebox(SNAME("LaunchPadMovieMode"), SNAME("EditorStyles")));
  99. write_movie_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("MovieWriterButtonPressed"), SNAME("EditorStyles")));
  100. } else {
  101. add_theme_style_override("panel", get_theme_stylebox(SNAME("LaunchPadNormal"), SNAME("EditorStyles")));
  102. write_movie_panel->add_theme_style_override("panel", get_theme_stylebox(SNAME("MovieWriterButtonNormal"), SNAME("EditorStyles")));
  103. }
  104. }
  105. void EditorRunBar::_quick_run_selected() {
  106. play_custom_scene(quick_run->get_selected());
  107. }
  108. void EditorRunBar::_play_custom_pressed() {
  109. if (editor_run.get_status() == EditorRun::STATUS_STOP || current_mode != RunMode::RUN_CUSTOM) {
  110. stop_playing();
  111. quick_run->popup_dialog("PackedScene", true);
  112. quick_run->set_title(TTR("Quick Run Scene..."));
  113. play_custom_scene_button->set_pressed(false);
  114. } else {
  115. // Reload if already running a custom scene.
  116. String last_custom_scene = run_custom_filename; // This is necessary to have a copy of the string.
  117. play_custom_scene(last_custom_scene);
  118. }
  119. }
  120. void EditorRunBar::_play_current_pressed() {
  121. if (editor_run.get_status() == EditorRun::STATUS_STOP || current_mode != RunMode::RUN_CURRENT) {
  122. play_current_scene();
  123. } else {
  124. // Reload if already running the current scene.
  125. play_current_scene(true);
  126. }
  127. }
  128. void EditorRunBar::_run_scene(const String &p_scene_path) {
  129. ERR_FAIL_COND_MSG(current_mode == RUN_CUSTOM && p_scene_path.is_empty(), "Attempting to run a custom scene with an empty path.");
  130. if (editor_run.get_status() == EditorRun::STATUS_PLAY) {
  131. return;
  132. }
  133. _reset_play_buttons();
  134. String write_movie_file;
  135. if (is_movie_maker_enabled()) {
  136. if (current_mode == RUN_CURRENT) {
  137. Node *scene_root = nullptr;
  138. if (p_scene_path.is_empty()) {
  139. scene_root = get_tree()->get_edited_scene_root();
  140. } else {
  141. int scene_index = EditorNode::get_editor_data().get_edited_scene_from_path(p_scene_path);
  142. if (scene_index >= 0) {
  143. scene_root = EditorNode::get_editor_data().get_edited_scene_root(scene_index);
  144. }
  145. }
  146. if (scene_root && scene_root->has_meta("movie_file")) {
  147. // If the scene file has a movie_file metadata set, use this as file.
  148. // Quick workaround if you want to have multiple scenes that write to
  149. // multiple movies.
  150. write_movie_file = scene_root->get_meta("movie_file");
  151. }
  152. }
  153. if (write_movie_file.is_empty()) {
  154. write_movie_file = GLOBAL_GET("editor/movie_writer/movie_file");
  155. }
  156. if (write_movie_file.is_empty()) {
  157. // TODO: Provide options to directly resolve the issue with a custom dialog.
  158. 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"));
  159. return;
  160. }
  161. }
  162. String run_filename;
  163. switch (current_mode) {
  164. case RUN_CUSTOM: {
  165. run_filename = p_scene_path;
  166. run_custom_filename = run_filename;
  167. } break;
  168. case RUN_CURRENT: {
  169. if (!p_scene_path.is_empty()) {
  170. run_filename = p_scene_path;
  171. run_current_filename = run_filename;
  172. break;
  173. }
  174. Node *scene_root = get_tree()->get_edited_scene_root();
  175. if (!scene_root) {
  176. EditorNode::get_singleton()->show_accept(TTR("There is no defined scene to run."), TTR("OK"));
  177. return;
  178. }
  179. if (scene_root->get_scene_file_path().is_empty()) {
  180. EditorNode::get_singleton()->save_before_run();
  181. return;
  182. }
  183. run_filename = scene_root->get_scene_file_path();
  184. run_current_filename = run_filename;
  185. } break;
  186. default: {
  187. if (!EditorNode::get_singleton()->ensure_main_scene(false)) {
  188. return;
  189. }
  190. run_filename = GLOBAL_DEF_BASIC("application/run/main_scene", "");
  191. } break;
  192. }
  193. EditorNode::get_singleton()->try_autosave();
  194. if (!EditorNode::get_singleton()->call_build()) {
  195. return;
  196. }
  197. EditorDebuggerNode::get_singleton()->start();
  198. Error error = editor_run.run(run_filename, write_movie_file);
  199. if (error != OK) {
  200. EditorDebuggerNode::get_singleton()->stop();
  201. EditorNode::get_singleton()->show_accept(TTR("Could not start subprocess(es)!"), TTR("OK"));
  202. return;
  203. }
  204. _update_play_buttons();
  205. stop_button->set_disabled(false);
  206. emit_signal(SNAME("play_pressed"));
  207. }
  208. void EditorRunBar::_run_native(const Ref<EditorExportPreset> &p_preset) {
  209. EditorNode::get_singleton()->try_autosave();
  210. if (run_native->is_deploy_debug_remote_enabled()) {
  211. stop_playing();
  212. if (!EditorNode::get_singleton()->call_build()) {
  213. return; // Build failed.
  214. }
  215. EditorDebuggerNode::get_singleton()->start(p_preset->get_platform()->get_debug_protocol());
  216. emit_signal(SNAME("play_pressed"));
  217. editor_run.run_native_notify();
  218. }
  219. }
  220. void EditorRunBar::play_main_scene(bool p_from_native) {
  221. if (p_from_native) {
  222. run_native->resume_run_native();
  223. } else {
  224. stop_playing();
  225. current_mode = RunMode::RUN_MAIN;
  226. _run_scene();
  227. }
  228. }
  229. void EditorRunBar::play_current_scene(bool p_reload) {
  230. EditorNode::get_singleton()->save_default_environment();
  231. stop_playing();
  232. current_mode = RunMode::RUN_CURRENT;
  233. if (p_reload) {
  234. String last_current_scene = run_current_filename; // This is necessary to have a copy of the string.
  235. _run_scene(last_current_scene);
  236. } else {
  237. _run_scene();
  238. }
  239. }
  240. void EditorRunBar::play_custom_scene(const String &p_custom) {
  241. stop_playing();
  242. current_mode = RunMode::RUN_CUSTOM;
  243. _run_scene(p_custom);
  244. }
  245. void EditorRunBar::stop_playing() {
  246. if (editor_run.get_status() == EditorRun::STATUS_STOP) {
  247. return;
  248. }
  249. current_mode = RunMode::STOPPED;
  250. editor_run.stop();
  251. EditorDebuggerNode::get_singleton()->stop();
  252. run_custom_filename.clear();
  253. run_current_filename.clear();
  254. stop_button->set_pressed(false);
  255. stop_button->set_disabled(true);
  256. _reset_play_buttons();
  257. emit_signal(SNAME("stop_pressed"));
  258. }
  259. bool EditorRunBar::is_playing() const {
  260. EditorRun::Status status = editor_run.get_status();
  261. return (status == EditorRun::STATUS_PLAY || status == EditorRun::STATUS_PAUSED);
  262. }
  263. String EditorRunBar::get_playing_scene() const {
  264. String run_filename = editor_run.get_running_scene();
  265. if (run_filename.is_empty() && is_playing()) {
  266. run_filename = GLOBAL_GET("application/run/main_scene"); // Must be the main scene then.
  267. }
  268. return run_filename;
  269. }
  270. Error EditorRunBar::start_native_device(int p_device_id) {
  271. return run_native->start_run_native(p_device_id);
  272. }
  273. OS::ProcessID EditorRunBar::has_child_process(OS::ProcessID p_pid) const {
  274. return editor_run.has_child_process(p_pid);
  275. }
  276. void EditorRunBar::stop_child_process(OS::ProcessID p_pid) {
  277. if (!has_child_process(p_pid)) {
  278. return;
  279. }
  280. editor_run.stop_child_process(p_pid);
  281. if (!editor_run.get_child_process_count()) { // All children stopped. Closing.
  282. stop_playing();
  283. }
  284. }
  285. void EditorRunBar::set_movie_maker_enabled(bool p_enabled) {
  286. write_movie_button->set_pressed(p_enabled);
  287. }
  288. bool EditorRunBar::is_movie_maker_enabled() const {
  289. return write_movie_button->is_pressed();
  290. }
  291. void EditorRunBar::_bind_methods() {
  292. ADD_SIGNAL(MethodInfo("play_pressed"));
  293. ADD_SIGNAL(MethodInfo("stop_pressed"));
  294. }
  295. EditorRunBar::EditorRunBar() {
  296. singleton = this;
  297. main_panel = memnew(PanelContainer);
  298. add_child(main_panel);
  299. HBoxContainer *main_hbox = memnew(HBoxContainer);
  300. main_panel->add_child(main_hbox);
  301. play_button = memnew(Button);
  302. main_hbox->add_child(play_button);
  303. play_button->set_flat(true);
  304. play_button->set_toggle_mode(true);
  305. play_button->set_focus_mode(Control::FOCUS_NONE);
  306. play_button->set_tooltip_text(TTR("Run the project's default scene."));
  307. play_button->connect("pressed", callable_mp(this, &EditorRunBar::play_main_scene).bind(false));
  308. ED_SHORTCUT_AND_COMMAND("editor/run_project", TTR("Run Project"), Key::F5);
  309. ED_SHORTCUT_OVERRIDE("editor/run_project", "macos", KeyModifierMask::META | Key::B);
  310. play_button->set_shortcut(ED_GET_SHORTCUT("editor/run_project"));
  311. pause_button = memnew(Button);
  312. main_hbox->add_child(pause_button);
  313. pause_button->set_flat(true);
  314. pause_button->set_toggle_mode(true);
  315. pause_button->set_focus_mode(Control::FOCUS_NONE);
  316. pause_button->set_tooltip_text(TTR("Pause the running project's execution for debugging."));
  317. pause_button->set_disabled(true);
  318. ED_SHORTCUT("editor/pause_running_project", TTR("Pause Running Project"), Key::F7);
  319. ED_SHORTCUT_OVERRIDE("editor/pause_running_project", "macos", KeyModifierMask::META | KeyModifierMask::CTRL | Key::Y);
  320. pause_button->set_shortcut(ED_GET_SHORTCUT("editor/pause_running_project"));
  321. stop_button = memnew(Button);
  322. main_hbox->add_child(stop_button);
  323. stop_button->set_flat(true);
  324. stop_button->set_focus_mode(Control::FOCUS_NONE);
  325. stop_button->set_tooltip_text(TTR("Stop the currently running project."));
  326. stop_button->set_disabled(true);
  327. stop_button->connect("pressed", callable_mp(this, &EditorRunBar::stop_playing));
  328. ED_SHORTCUT("editor/stop_running_project", TTR("Stop Running Project"), Key::F8);
  329. ED_SHORTCUT_OVERRIDE("editor/stop_running_project", "macos", KeyModifierMask::META | Key::PERIOD);
  330. stop_button->set_shortcut(ED_GET_SHORTCUT("editor/stop_running_project"));
  331. run_native = memnew(EditorRunNative);
  332. main_hbox->add_child(run_native);
  333. run_native->connect("native_run", callable_mp(this, &EditorRunBar::_run_native));
  334. play_scene_button = memnew(Button);
  335. main_hbox->add_child(play_scene_button);
  336. play_scene_button->set_flat(true);
  337. play_scene_button->set_toggle_mode(true);
  338. play_scene_button->set_focus_mode(Control::FOCUS_NONE);
  339. play_scene_button->set_tooltip_text(TTR("Run the currently edited scene."));
  340. play_scene_button->connect("pressed", callable_mp(this, &EditorRunBar::_play_current_pressed));
  341. ED_SHORTCUT_AND_COMMAND("editor/run_current_scene", TTR("Run Current Scene"), Key::F6);
  342. ED_SHORTCUT_OVERRIDE("editor/run_current_scene", "macos", KeyModifierMask::META | Key::R);
  343. play_scene_button->set_shortcut(ED_GET_SHORTCUT("editor/run_current_scene"));
  344. play_custom_scene_button = memnew(Button);
  345. main_hbox->add_child(play_custom_scene_button);
  346. play_custom_scene_button->set_flat(true);
  347. play_custom_scene_button->set_toggle_mode(true);
  348. play_custom_scene_button->set_focus_mode(Control::FOCUS_NONE);
  349. play_custom_scene_button->set_tooltip_text(TTR("Run a specific scene."));
  350. play_custom_scene_button->connect("pressed", callable_mp(this, &EditorRunBar::_play_custom_pressed));
  351. ED_SHORTCUT_AND_COMMAND("editor/run_specific_scene", TTR("Run Specific Scene"), KeyModifierMask::CTRL | KeyModifierMask::SHIFT | Key::F5);
  352. ED_SHORTCUT_OVERRIDE("editor/run_specific_scene", "macos", KeyModifierMask::META | KeyModifierMask::SHIFT | Key::R);
  353. play_custom_scene_button->set_shortcut(ED_GET_SHORTCUT("editor/run_specific_scene"));
  354. write_movie_panel = memnew(PanelContainer);
  355. main_hbox->add_child(write_movie_panel);
  356. write_movie_button = memnew(Button);
  357. write_movie_panel->add_child(write_movie_button);
  358. write_movie_button->set_flat(true);
  359. write_movie_button->set_toggle_mode(true);
  360. write_movie_button->set_pressed(false);
  361. write_movie_button->set_focus_mode(Control::FOCUS_NONE);
  362. write_movie_button->set_tooltip_text(TTR("Enable Movie Maker mode.\nThe project will run at stable FPS and the visual and audio output will be recorded to a video file."));
  363. write_movie_button->connect("toggled", callable_mp(this, &EditorRunBar::_write_movie_toggled));
  364. quick_run = memnew(EditorQuickOpen);
  365. add_child(quick_run);
  366. quick_run->connect("quick_open", callable_mp(this, &EditorRunBar::_quick_run_selected));
  367. }