game_view_plugin.cpp 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279
  1. /**************************************************************************/
  2. /* game_view_plugin.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 "game_view_plugin.h"
  31. #include "core/config/project_settings.h"
  32. #include "core/debugger/debugger_marshalls.h"
  33. #include "core/string/translation_server.h"
  34. #include "editor/debugger/editor_debugger_node.h"
  35. #include "editor/debugger/script_editor_debugger.h"
  36. #include "editor/editor_feature_profile.h"
  37. #include "editor/editor_interface.h"
  38. #include "editor/editor_main_screen.h"
  39. #include "editor/editor_node.h"
  40. #include "editor/editor_settings.h"
  41. #include "editor/editor_string_names.h"
  42. #include "editor/gui/editor_bottom_panel.h"
  43. #include "editor/gui/editor_run_bar.h"
  44. #include "editor/plugins/embedded_process.h"
  45. #include "editor/themes/editor_scale.h"
  46. #include "editor/window_wrapper.h"
  47. #include "scene/gui/button.h"
  48. #include "scene/gui/label.h"
  49. #include "scene/gui/menu_button.h"
  50. #include "scene/gui/panel.h"
  51. #include "scene/gui/separator.h"
  52. void GameViewDebugger::_session_started(Ref<EditorDebuggerSession> p_session) {
  53. if (!is_feature_enabled) {
  54. return;
  55. }
  56. Dictionary settings;
  57. settings["debugger/max_node_selection"] = EDITOR_GET("debugger/max_node_selection");
  58. settings["editors/panning/2d_editor_panning_scheme"] = EDITOR_GET("editors/panning/2d_editor_panning_scheme");
  59. settings["editors/panning/simple_panning"] = EDITOR_GET("editors/panning/simple_panning");
  60. settings["editors/panning/warped_mouse_panning"] = EDITOR_GET("editors/panning/warped_mouse_panning");
  61. settings["editors/panning/2d_editor_pan_speed"] = EDITOR_GET("editors/panning/2d_editor_pan_speed");
  62. settings["editors/polygon_editor/point_grab_radius"] = EDITOR_GET("editors/polygon_editor/point_grab_radius");
  63. settings["canvas_item_editor/pan_view"] = DebuggerMarshalls::serialize_key_shortcut(ED_GET_SHORTCUT("canvas_item_editor/pan_view"));
  64. settings["box_selection_fill_color"] = EditorNode::get_singleton()->get_editor_theme()->get_color(SNAME("box_selection_fill_color"), EditorStringName(Editor));
  65. settings["box_selection_stroke_color"] = EditorNode::get_singleton()->get_editor_theme()->get_color(SNAME("box_selection_stroke_color"), EditorStringName(Editor));
  66. settings["editors/3d/default_fov"] = EDITOR_GET("editors/3d/default_fov");
  67. settings["editors/3d/default_z_near"] = EDITOR_GET("editors/3d/default_z_near");
  68. settings["editors/3d/default_z_far"] = EDITOR_GET("editors/3d/default_z_far");
  69. settings["editors/3d/navigation/invert_x_axis"] = EDITOR_GET("editors/3d/navigation/invert_x_axis");
  70. settings["editors/3d/navigation/invert_y_axis"] = EDITOR_GET("editors/3d/navigation/invert_y_axis");
  71. settings["editors/3d/navigation/warped_mouse_panning"] = EDITOR_GET("editors/3d/navigation/warped_mouse_panning");
  72. settings["editors/3d/freelook/freelook_base_speed"] = EDITOR_GET("editors/3d/freelook/freelook_base_speed");
  73. settings["editors/3d/freelook/freelook_sensitivity"] = EDITOR_GET("editors/3d/freelook/freelook_sensitivity");
  74. settings["editors/3d/navigation_feel/orbit_sensitivity"] = EDITOR_GET("editors/3d/navigation_feel/orbit_sensitivity");
  75. settings["editors/3d/navigation_feel/translation_sensitivity"] = EDITOR_GET("editors/3d/navigation_feel/translation_sensitivity");
  76. settings["editors/3d/selection_box_color"] = EDITOR_GET("editors/3d/selection_box_color");
  77. settings["editors/3d/freelook/freelook_base_speed"] = EDITOR_GET("editors/3d/freelook/freelook_base_speed");
  78. Array setup_data;
  79. setup_data.append(settings);
  80. p_session->send_message("scene:runtime_node_select_setup", setup_data);
  81. Array type;
  82. type.append(node_type);
  83. p_session->send_message("scene:runtime_node_select_set_type", type);
  84. Array visible;
  85. visible.append(selection_visible);
  86. p_session->send_message("scene:runtime_node_select_set_visible", visible);
  87. Array mode;
  88. mode.append(select_mode);
  89. p_session->send_message("scene:runtime_node_select_set_mode", mode);
  90. Array mute_audio_data;
  91. mute_audio_data.append(mute_audio);
  92. p_session->send_message("scene:debug_mute_audio", mute_audio_data);
  93. Dictionary shortcut_settings;
  94. shortcut_settings["editor/suspend_resume_embedded_project"] = DebuggerMarshalls::serialize_key_shortcut(ED_GET_SHORTCUT("editor/suspend_resume_embedded_project"));
  95. shortcut_settings["editor/next_frame_embedded_project"] = DebuggerMarshalls::serialize_key_shortcut(ED_GET_SHORTCUT("editor/next_frame_embedded_project"));
  96. p_session->send_message("scene:setup_embedded_shortcuts", { shortcut_settings });
  97. emit_signal(SNAME("session_started"));
  98. }
  99. void GameViewDebugger::_session_stopped() {
  100. if (!is_feature_enabled) {
  101. return;
  102. }
  103. emit_signal(SNAME("session_stopped"));
  104. }
  105. void GameViewDebugger::set_suspend(bool p_enabled) {
  106. Array message;
  107. message.append(p_enabled);
  108. for (Ref<EditorDebuggerSession> &I : sessions) {
  109. if (I->is_active()) {
  110. I->send_message("scene:suspend_changed", message);
  111. }
  112. }
  113. }
  114. void GameViewDebugger::next_frame() {
  115. for (Ref<EditorDebuggerSession> &I : sessions) {
  116. if (I->is_active()) {
  117. I->send_message("scene:next_frame", Array());
  118. }
  119. }
  120. }
  121. void GameViewDebugger::set_node_type(int p_type) {
  122. node_type = p_type;
  123. Array message;
  124. message.append(p_type);
  125. for (Ref<EditorDebuggerSession> &I : sessions) {
  126. if (I->is_active()) {
  127. I->send_message("scene:runtime_node_select_set_type", message);
  128. }
  129. }
  130. }
  131. void GameViewDebugger::set_selection_visible(bool p_visible) {
  132. selection_visible = p_visible;
  133. Array message;
  134. message.append(p_visible);
  135. for (Ref<EditorDebuggerSession> &I : sessions) {
  136. if (I->is_active()) {
  137. I->send_message("scene:runtime_node_select_set_visible", message);
  138. }
  139. }
  140. }
  141. void GameViewDebugger::set_select_mode(int p_mode) {
  142. select_mode = p_mode;
  143. Array message;
  144. message.append(p_mode);
  145. for (Ref<EditorDebuggerSession> &I : sessions) {
  146. if (I->is_active()) {
  147. I->send_message("scene:runtime_node_select_set_mode", message);
  148. }
  149. }
  150. }
  151. void GameViewDebugger::set_debug_mute_audio(bool p_enabled) {
  152. mute_audio = p_enabled;
  153. EditorDebuggerNode::get_singleton()->set_debug_mute_audio(p_enabled);
  154. }
  155. void GameViewDebugger::set_camera_override(bool p_enabled) {
  156. EditorDebuggerNode::get_singleton()->set_camera_override(p_enabled ? camera_override_mode : EditorDebuggerNode::OVERRIDE_NONE);
  157. }
  158. void GameViewDebugger::set_camera_manipulate_mode(EditorDebuggerNode::CameraOverride p_mode) {
  159. camera_override_mode = p_mode;
  160. if (EditorDebuggerNode::get_singleton()->get_camera_override() != EditorDebuggerNode::OVERRIDE_NONE) {
  161. set_camera_override(true);
  162. }
  163. }
  164. void GameViewDebugger::reset_camera_2d_position() {
  165. for (Ref<EditorDebuggerSession> &I : sessions) {
  166. if (I->is_active()) {
  167. I->send_message("scene:runtime_node_select_reset_camera_2d", Array());
  168. }
  169. }
  170. }
  171. void GameViewDebugger::reset_camera_3d_position() {
  172. for (Ref<EditorDebuggerSession> &I : sessions) {
  173. if (I->is_active()) {
  174. I->send_message("scene:runtime_node_select_reset_camera_3d", Array());
  175. }
  176. }
  177. }
  178. void GameViewDebugger::setup_session(int p_session_id) {
  179. Ref<EditorDebuggerSession> session = get_session(p_session_id);
  180. ERR_FAIL_COND(session.is_null());
  181. sessions.append(session);
  182. session->connect("started", callable_mp(this, &GameViewDebugger::_session_started).bind(session));
  183. session->connect("stopped", callable_mp(this, &GameViewDebugger::_session_stopped));
  184. }
  185. void GameViewDebugger::_feature_profile_changed() {
  186. Ref<EditorFeatureProfile> profile = EditorFeatureProfileManager::get_singleton()->get_current_profile();
  187. is_feature_enabled = profile.is_null() || !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_GAME);
  188. }
  189. void GameViewDebugger::_bind_methods() {
  190. ADD_SIGNAL(MethodInfo("session_started"));
  191. ADD_SIGNAL(MethodInfo("session_stopped"));
  192. }
  193. GameViewDebugger::GameViewDebugger() {
  194. EditorFeatureProfileManager::get_singleton()->connect("current_feature_profile_changed", callable_mp(this, &GameViewDebugger::_feature_profile_changed));
  195. }
  196. ///////
  197. void GameView::_sessions_changed() {
  198. // The debugger session's `session_started/stopped` signal can be unreliable, so count it manually.
  199. active_sessions = 0;
  200. Array sessions = debugger->get_sessions();
  201. for (int i = 0; i < sessions.size(); i++) {
  202. if (Object::cast_to<EditorDebuggerSession>(sessions[i])->is_active()) {
  203. active_sessions++;
  204. }
  205. }
  206. _update_debugger_buttons();
  207. #ifdef MACOS_ENABLED
  208. if (!embedded_script_debugger || !embedded_script_debugger->is_session_active() || embedded_script_debugger->get_remote_pid() != embedded_process->get_embedded_pid()) {
  209. _attach_script_debugger();
  210. }
  211. #else
  212. if (embedded_process->is_embedding_completed()) {
  213. if (!embedded_script_debugger || !embedded_script_debugger->is_session_active() || embedded_script_debugger->get_remote_pid() != embedded_process->get_embedded_pid()) {
  214. _attach_script_debugger();
  215. }
  216. }
  217. #endif
  218. }
  219. void GameView::_instance_starting_static(int p_idx, List<String> &r_arguments) {
  220. ERR_FAIL_NULL(singleton);
  221. singleton->_instance_starting(p_idx, r_arguments);
  222. }
  223. void GameView::_instance_starting(int p_idx, List<String> &r_arguments) {
  224. if (!is_feature_enabled) {
  225. return;
  226. }
  227. if (p_idx == 0 && embed_on_play && make_floating_on_play && window_wrapper->is_window_available() && !window_wrapper->get_window_enabled() && _get_embed_available() == EMBED_AVAILABLE) {
  228. // Set the Floating Window default title. Always considered in DEBUG mode, same as in Window::set_title.
  229. String appname = GLOBAL_GET("application/config/name");
  230. appname = vformat("%s (DEBUG)", TranslationServer::get_singleton()->translate(appname));
  231. window_wrapper->set_window_title(appname);
  232. _show_update_window_wrapper();
  233. embedded_process->grab_focus();
  234. }
  235. _update_arguments_for_instance(p_idx, r_arguments);
  236. }
  237. void GameView::_show_update_window_wrapper() {
  238. EditorRun::WindowPlacement placement = EditorRun::get_window_placement();
  239. Point2 position = floating_window_rect.position;
  240. Size2i size = floating_window_rect.size;
  241. int screen = floating_window_screen;
  242. // Obtain the size around the embedded process control. Usually, the difference between the game view's get_size
  243. // and the embedded control should work. However, when the control is hidden and has never been displayed,
  244. // the size of the embedded control is not calculated.
  245. Size2 old_min_size = embedded_process->get_custom_minimum_size();
  246. embedded_process->set_custom_minimum_size(Size2i());
  247. Size2 embedded_process_min_size = get_minimum_size();
  248. Size2 wrapped_margins_size = window_wrapper->get_margins_size();
  249. Size2 wrapped_min_size = window_wrapper->get_minimum_size();
  250. Point2 offset_embedded_process = embedded_process->get_global_position() - get_global_position();
  251. // On the first startup, the global position of the embedded process control is invalid because it was
  252. // never displayed. We will calculate it manually using the minimum size of the window.
  253. if (offset_embedded_process == Point2()) {
  254. offset_embedded_process.y = wrapped_min_size.y;
  255. }
  256. offset_embedded_process.x += embedded_process->get_margin_size(SIDE_LEFT);
  257. offset_embedded_process.y += embedded_process->get_margin_size(SIDE_TOP);
  258. offset_embedded_process += window_wrapper->get_margins_top_left();
  259. embedded_process->set_custom_minimum_size(old_min_size);
  260. Point2 size_diff_embedded_process = Point2(0, embedded_process_min_size.y) + embedded_process->get_margins_size();
  261. if (placement.position != Point2i(INT_MAX, INT_MAX)) {
  262. position = placement.position - offset_embedded_process;
  263. screen = placement.screen;
  264. }
  265. if (placement.size != Size2i()) {
  266. size = placement.size + size_diff_embedded_process + wrapped_margins_size;
  267. }
  268. window_wrapper->restore_window_from_saved_position(Rect2(position, size), screen, Rect2i());
  269. }
  270. void GameView::_play_pressed() {
  271. if (!is_feature_enabled) {
  272. return;
  273. }
  274. OS::ProcessID current_process_id = EditorRunBar::get_singleton()->get_current_process();
  275. if (current_process_id == 0) {
  276. return;
  277. }
  278. if (!window_wrapper->get_window_enabled()) {
  279. screen_index_before_start = EditorNode::get_singleton()->get_editor_main_screen()->get_selected_index();
  280. }
  281. if (embed_on_play && _get_embed_available() == EMBED_AVAILABLE) {
  282. // It's important to disable the low power mode when unfocused because otherwise
  283. // the button in the editor are not responsive and if the user moves the mouse quickly,
  284. // the mouse clicks are not registered.
  285. EditorNode::get_singleton()->set_unfocused_low_processor_usage_mode_enabled(false);
  286. _update_embed_window_size();
  287. if (!window_wrapper->get_window_enabled()) {
  288. EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_GAME);
  289. // Reset the normal size of the bottom panel when fully expanded.
  290. EditorNode::get_singleton()->get_bottom_panel()->set_expanded(false);
  291. embedded_process->grab_focus();
  292. }
  293. embedded_process->embed_process(current_process_id);
  294. _update_ui();
  295. }
  296. }
  297. void GameView::_stop_pressed() {
  298. if (!is_feature_enabled) {
  299. return;
  300. }
  301. _detach_script_debugger();
  302. paused = false;
  303. EditorNode::get_singleton()->set_unfocused_low_processor_usage_mode_enabled(true);
  304. embedded_process->reset();
  305. _update_ui();
  306. if (window_wrapper->get_window_enabled()) {
  307. window_wrapper->set_window_enabled(false);
  308. }
  309. if (screen_index_before_start >= 0 && EditorNode::get_singleton()->get_editor_main_screen()->get_selected_index() == EditorMainScreen::EDITOR_GAME) {
  310. // We go back to the screen where the user was before starting the game.
  311. EditorNode::get_singleton()->get_editor_main_screen()->select(screen_index_before_start);
  312. }
  313. screen_index_before_start = -1;
  314. }
  315. void GameView::_embedding_completed() {
  316. #ifndef MACOS_ENABLED
  317. _attach_script_debugger();
  318. #endif
  319. _update_ui();
  320. if (make_floating_on_play) {
  321. get_window()->set_flag(Window::FLAG_ALWAYS_ON_TOP, bool(GLOBAL_GET("display/window/size/always_on_top")));
  322. }
  323. }
  324. void GameView::_embedding_failed() {
  325. state_label->set_text(TTR("Connection impossible to the game process."));
  326. }
  327. void GameView::_embedded_process_updated() {
  328. const Rect2i game_rect = embedded_process->get_screen_embedded_window_rect();
  329. game_size_label->set_text(vformat("%dx%d", game_rect.size.x, game_rect.size.y));
  330. }
  331. void GameView::_embedded_process_focused() {
  332. if (embed_on_play && !window_wrapper->get_window_enabled()) {
  333. EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_GAME);
  334. }
  335. }
  336. void GameView::_editor_or_project_settings_changed() {
  337. // Update the window size and aspect ratio.
  338. _update_embed_window_size();
  339. if (window_wrapper->get_window_enabled()) {
  340. _show_update_window_wrapper();
  341. if (embedded_process->is_embedding_completed()) {
  342. embedded_process->queue_update_embedded_process();
  343. }
  344. }
  345. _update_ui();
  346. }
  347. void GameView::_update_debugger_buttons() {
  348. bool empty = active_sessions == 0;
  349. suspend_button->set_disabled(empty);
  350. camera_override_button->set_disabled(empty);
  351. PopupMenu *menu = camera_override_menu->get_popup();
  352. bool disable_camera_reset = empty || !camera_override_button->is_pressed() || !menu->is_item_checked(menu->get_item_index(CAMERA_MODE_INGAME));
  353. menu->set_item_disabled(CAMERA_RESET_2D, disable_camera_reset);
  354. menu->set_item_disabled(CAMERA_RESET_3D, disable_camera_reset);
  355. if (empty) {
  356. suspend_button->set_pressed(false);
  357. camera_override_button->set_pressed(false);
  358. }
  359. next_frame_button->set_disabled(!suspend_button->is_pressed());
  360. }
  361. void GameView::_handle_shortcut_requested(int p_embed_action) {
  362. switch (p_embed_action) {
  363. case ScriptEditorDebugger::EMBED_SUSPEND_TOGGLE: {
  364. _toggle_suspend_button();
  365. } break;
  366. case ScriptEditorDebugger::EMBED_NEXT_FRAME: {
  367. debugger->next_frame();
  368. } break;
  369. }
  370. }
  371. void GameView::_toggle_suspend_button() {
  372. const bool new_pressed = !suspend_button->is_pressed();
  373. suspend_button->set_pressed(new_pressed);
  374. _suspend_button_toggled(new_pressed);
  375. }
  376. void GameView::_suspend_button_toggled(bool p_pressed) {
  377. _update_debugger_buttons();
  378. debugger->set_suspend(p_pressed);
  379. }
  380. void GameView::_node_type_pressed(int p_option) {
  381. RuntimeNodeSelect::NodeType type = (RuntimeNodeSelect::NodeType)p_option;
  382. for (int i = 0; i < RuntimeNodeSelect::NODE_TYPE_MAX; i++) {
  383. node_type_button[i]->set_pressed_no_signal(i == type);
  384. }
  385. _update_debugger_buttons();
  386. debugger->set_node_type(type);
  387. }
  388. void GameView::_select_mode_pressed(int p_option) {
  389. RuntimeNodeSelect::SelectMode mode = (RuntimeNodeSelect::SelectMode)p_option;
  390. if (!select_mode_button[mode]->is_visible()) {
  391. return;
  392. }
  393. for (int i = 0; i < RuntimeNodeSelect::SELECT_MODE_MAX; i++) {
  394. select_mode_button[i]->set_pressed_no_signal(i == mode);
  395. }
  396. debugger->set_select_mode(mode);
  397. EditorSettings::get_singleton()->set_project_metadata("game_view", "select_mode", mode);
  398. }
  399. void GameView::_embed_options_menu_menu_id_pressed(int p_id) {
  400. switch (p_id) {
  401. case EMBED_RUN_GAME_EMBEDDED: {
  402. embed_on_play = !embed_on_play;
  403. int game_mode = EDITOR_GET("run/window_placement/game_embed_mode");
  404. if (game_mode == 0) { // Save only if not overridden by editor.
  405. EditorSettings::get_singleton()->set_project_metadata("game_view", "embed_on_play", embed_on_play);
  406. }
  407. } break;
  408. case EMBED_MAKE_FLOATING_ON_PLAY: {
  409. make_floating_on_play = !make_floating_on_play;
  410. int game_mode = EDITOR_GET("run/window_placement/game_embed_mode");
  411. if (game_mode == 0) { // Save only if not overridden by editor.
  412. EditorSettings::get_singleton()->set_project_metadata("game_view", "make_floating_on_play", make_floating_on_play);
  413. }
  414. } break;
  415. }
  416. _update_embed_menu_options();
  417. _update_ui();
  418. }
  419. void GameView::_size_mode_button_pressed(int size_mode) {
  420. embed_size_mode = (EmbedSizeMode)size_mode;
  421. EditorSettings::get_singleton()->set_project_metadata("game_view", "embed_size_mode", size_mode);
  422. _update_embed_menu_options();
  423. _update_embed_window_size();
  424. }
  425. GameView::EmbedAvailability GameView::_get_embed_available() {
  426. if (!DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_WINDOW_EMBEDDING)) {
  427. return EMBED_NOT_AVAILABLE_FEATURE_NOT_SUPPORTED;
  428. }
  429. #ifndef MACOS_ENABLED
  430. if (get_tree()->get_root()->is_embedding_subwindows()) {
  431. return EMBED_NOT_AVAILABLE_SINGLE_WINDOW_MODE;
  432. }
  433. #endif
  434. String display_driver = GLOBAL_GET("display/display_server/driver");
  435. if (display_driver == "headless" || display_driver == "wayland") {
  436. return EMBED_NOT_AVAILABLE_PROJECT_DISPLAY_DRIVER;
  437. }
  438. EditorRun::WindowPlacement placement = EditorRun::get_window_placement();
  439. if (placement.force_fullscreen) {
  440. return EMBED_NOT_AVAILABLE_FULLSCREEN;
  441. }
  442. if (placement.force_maximized) {
  443. return EMBED_NOT_AVAILABLE_MAXIMIZED;
  444. }
  445. DisplayServer::WindowMode window_mode = (DisplayServer::WindowMode)(GLOBAL_GET("display/window/size/mode").operator int());
  446. if (window_mode == DisplayServer::WindowMode::WINDOW_MODE_MINIMIZED) {
  447. return EMBED_NOT_AVAILABLE_MINIMIZED;
  448. }
  449. if (window_mode == DisplayServer::WindowMode::WINDOW_MODE_MAXIMIZED) {
  450. return EMBED_NOT_AVAILABLE_MAXIMIZED;
  451. }
  452. if (window_mode == DisplayServer::WindowMode::WINDOW_MODE_FULLSCREEN || window_mode == DisplayServer::WindowMode::WINDOW_MODE_EXCLUSIVE_FULLSCREEN) {
  453. return EMBED_NOT_AVAILABLE_FULLSCREEN;
  454. }
  455. return EMBED_AVAILABLE;
  456. }
  457. void GameView::_update_ui() {
  458. bool show_game_size = false;
  459. EmbedAvailability available = _get_embed_available();
  460. switch (available) {
  461. case EMBED_AVAILABLE:
  462. if (embedded_process->is_embedding_completed()) {
  463. state_label->set_text("");
  464. show_game_size = true;
  465. } else if (embedded_process->is_embedding_in_progress()) {
  466. state_label->set_text(TTR("Game starting..."));
  467. } else if (EditorRunBar::get_singleton()->is_playing()) {
  468. state_label->set_text(TTR("Game running not embedded."));
  469. } else if (embed_on_play) {
  470. state_label->set_text(TTR("Press play to start the game."));
  471. } else {
  472. state_label->set_text(TTR("Embedding is disabled."));
  473. }
  474. break;
  475. case EMBED_NOT_AVAILABLE_FEATURE_NOT_SUPPORTED:
  476. if (DisplayServer::get_singleton()->get_name() == "Wayland") {
  477. state_label->set_text(TTR("Game embedding not available on Wayland.\nWayland can be disabled in the Editor Settings (Run > Platforms > Linux/*BSD > Prefer Wayland)."));
  478. } else {
  479. state_label->set_text(TTR("Game embedding not available on your OS."));
  480. }
  481. break;
  482. case EMBED_NOT_AVAILABLE_PROJECT_DISPLAY_DRIVER:
  483. state_label->set_text(vformat(TTR("Game embedding not available for the Display Server: '%s'.\nDisplay Server can be modified in the Project Settings (Display > Display Server > Driver)."), GLOBAL_GET("display/display_server/driver")));
  484. break;
  485. case EMBED_NOT_AVAILABLE_MINIMIZED:
  486. state_label->set_text(TTR("Game embedding not available when the game starts minimized.") + "\n" + TTR("Consider overriding the window mode project setting with the editor feature tag to Windowed to use game embedding while leaving the exported project intact."));
  487. break;
  488. case EMBED_NOT_AVAILABLE_MAXIMIZED:
  489. state_label->set_text(TTR("Game embedding not available when the game starts maximized.") + "\n" + TTR("Consider overriding the window mode project setting with the editor feature tag to Windowed to use game embedding while leaving the exported project intact."));
  490. break;
  491. case EMBED_NOT_AVAILABLE_FULLSCREEN:
  492. state_label->set_text(TTR("Game embedding not available when the game starts in fullscreen.") + "\n" + TTR("Consider overriding the window mode project setting with the editor feature tag to Windowed to use game embedding while leaving the exported project intact."));
  493. break;
  494. case EMBED_NOT_AVAILABLE_SINGLE_WINDOW_MODE:
  495. state_label->set_text(TTR("Game embedding not available in single window mode."));
  496. break;
  497. }
  498. if (available == EMBED_AVAILABLE) {
  499. if (state_label->has_theme_color_override(SceneStringName(font_color))) {
  500. state_label->remove_theme_color_override(SceneStringName(font_color));
  501. }
  502. } else {
  503. state_label->add_theme_color_override(SceneStringName(font_color), state_label->get_theme_color(SNAME("warning_color"), EditorStringName(Editor)));
  504. }
  505. game_size_label->set_visible(show_game_size);
  506. }
  507. void GameView::_update_embed_menu_options() {
  508. bool is_multi_window = window_wrapper->is_window_available();
  509. PopupMenu *menu = embed_options_menu->get_popup();
  510. menu->set_item_checked(menu->get_item_index(EMBED_RUN_GAME_EMBEDDED), embed_on_play);
  511. menu->set_item_checked(menu->get_item_index(EMBED_MAKE_FLOATING_ON_PLAY), make_floating_on_play && is_multi_window);
  512. menu->set_item_disabled(menu->get_item_index(EMBED_MAKE_FLOATING_ON_PLAY), !embed_on_play || !is_multi_window);
  513. fixed_size_button->set_pressed(embed_size_mode == SIZE_MODE_FIXED);
  514. keep_aspect_button->set_pressed(embed_size_mode == SIZE_MODE_KEEP_ASPECT);
  515. stretch_button->set_pressed(embed_size_mode == SIZE_MODE_STRETCH);
  516. }
  517. void GameView::_update_embed_window_size() {
  518. if (paused) {
  519. // When paused, Godot does not re-render. As a result, resizing the game window to a larger size
  520. // causes artifacts and flickering. However, resizing to a smaller size seems fine.
  521. // To prevent artifacts and flickering, we will force the game window to maintain its size.
  522. // Using the same technique as SIZE_MODE_FIXED, the embedded process control will
  523. // prevent resizing the game to a larger size while maintaining the aspect ratio.
  524. embedded_process->set_window_size(size_paused);
  525. embedded_process->set_keep_aspect(false);
  526. } else {
  527. if (embed_size_mode == SIZE_MODE_FIXED || embed_size_mode == SIZE_MODE_KEEP_ASPECT) {
  528. // The embedded process control will need the desired window size.
  529. EditorRun::WindowPlacement placement = EditorRun::get_window_placement();
  530. embedded_process->set_window_size(placement.size);
  531. } else {
  532. // Stretch... No need for the window size.
  533. embedded_process->set_window_size(Size2i());
  534. }
  535. embedded_process->set_keep_aspect(embed_size_mode == SIZE_MODE_KEEP_ASPECT);
  536. }
  537. }
  538. void GameView::_hide_selection_toggled(bool p_pressed) {
  539. hide_selection->set_button_icon(get_editor_theme_icon(p_pressed ? SNAME("GuiVisibilityHidden") : SNAME("GuiVisibilityVisible")));
  540. debugger->set_selection_visible(!p_pressed);
  541. EditorSettings::get_singleton()->set_project_metadata("game_view", "hide_selection", p_pressed);
  542. }
  543. void GameView::_debug_mute_audio_button_pressed() {
  544. debug_mute_audio = !debug_mute_audio;
  545. debug_mute_audio_button->set_button_icon(get_editor_theme_icon(debug_mute_audio ? SNAME("AudioMute") : SNAME("AudioStreamPlayer")));
  546. debug_mute_audio_button->set_tooltip_text(debug_mute_audio ? TTRC("Unmute game audio.") : TTRC("Mute game audio."));
  547. debugger->set_debug_mute_audio(debug_mute_audio);
  548. }
  549. void GameView::_camera_override_button_toggled(bool p_pressed) {
  550. _update_debugger_buttons();
  551. debugger->set_camera_override(p_pressed);
  552. }
  553. void GameView::_camera_override_menu_id_pressed(int p_id) {
  554. PopupMenu *menu = camera_override_menu->get_popup();
  555. if (p_id != CAMERA_RESET_2D && p_id != CAMERA_RESET_3D) {
  556. for (int i = 0; i < menu->get_item_count(); i++) {
  557. menu->set_item_checked(i, false);
  558. }
  559. }
  560. switch (p_id) {
  561. case CAMERA_RESET_2D: {
  562. debugger->reset_camera_2d_position();
  563. } break;
  564. case CAMERA_RESET_3D: {
  565. debugger->reset_camera_3d_position();
  566. } break;
  567. case CAMERA_MODE_INGAME: {
  568. debugger->set_camera_manipulate_mode(EditorDebuggerNode::OVERRIDE_INGAME);
  569. menu->set_item_checked(menu->get_item_index(p_id), true);
  570. _update_debugger_buttons();
  571. EditorSettings::get_singleton()->set_project_metadata("game_view", "camera_override_mode", p_id);
  572. } break;
  573. case CAMERA_MODE_EDITORS: {
  574. debugger->set_camera_manipulate_mode(EditorDebuggerNode::OVERRIDE_EDITORS);
  575. menu->set_item_checked(menu->get_item_index(p_id), true);
  576. _update_debugger_buttons();
  577. EditorSettings::get_singleton()->set_project_metadata("game_view", "camera_override_mode", p_id);
  578. } break;
  579. }
  580. }
  581. void GameView::_notification(int p_what) {
  582. switch (p_what) {
  583. case NOTIFICATION_THEME_CHANGED: {
  584. suspend_button->set_button_icon(get_editor_theme_icon(SNAME("Pause")));
  585. next_frame_button->set_button_icon(get_editor_theme_icon(SNAME("NextFrame")));
  586. node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE]->set_button_icon(get_editor_theme_icon(SNAME("InputEventJoypadMotion")));
  587. node_type_button[RuntimeNodeSelect::NODE_TYPE_2D]->set_button_icon(get_editor_theme_icon(SNAME("2DNodes")));
  588. node_type_button[RuntimeNodeSelect::NODE_TYPE_3D]->set_button_icon(get_editor_theme_icon(SNAME("Node3D")));
  589. select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE]->set_button_icon(get_editor_theme_icon(SNAME("ToolSelect")));
  590. select_mode_button[RuntimeNodeSelect::SELECT_MODE_LIST]->set_button_icon(get_editor_theme_icon(SNAME("ListSelect")));
  591. hide_selection->set_button_icon(get_editor_theme_icon(hide_selection->is_pressed() ? SNAME("GuiVisibilityHidden") : SNAME("GuiVisibilityVisible")));
  592. fixed_size_button->set_button_icon(get_editor_theme_icon(SNAME("FixedSize")));
  593. keep_aspect_button->set_button_icon(get_editor_theme_icon(SNAME("KeepAspect")));
  594. stretch_button->set_button_icon(get_editor_theme_icon(SNAME("Stretch")));
  595. embed_options_menu->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl")));
  596. debug_mute_audio_button->set_button_icon(get_editor_theme_icon(debug_mute_audio ? SNAME("AudioMute") : SNAME("AudioStreamPlayer")));
  597. camera_override_button->set_button_icon(get_editor_theme_icon(SNAME("Camera")));
  598. camera_override_menu->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl")));
  599. } break;
  600. case NOTIFICATION_READY: {
  601. if (DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_WINDOW_EMBEDDING)) {
  602. // Embedding available.
  603. int game_mode = EDITOR_GET("run/window_placement/game_embed_mode");
  604. switch (game_mode) {
  605. case -1: { // Disabled.
  606. embed_on_play = false;
  607. make_floating_on_play = false;
  608. } break;
  609. case 1: { // Embed.
  610. embed_on_play = true;
  611. make_floating_on_play = false;
  612. } break;
  613. case 2: { // Floating.
  614. embed_on_play = true;
  615. make_floating_on_play = true;
  616. } break;
  617. default: {
  618. embed_on_play = EditorSettings::get_singleton()->get_project_metadata("game_view", "embed_on_play", true);
  619. make_floating_on_play = EditorSettings::get_singleton()->get_project_metadata("game_view", "make_floating_on_play", true);
  620. } break;
  621. }
  622. embed_size_mode = (EmbedSizeMode)(int)EditorSettings::get_singleton()->get_project_metadata("game_view", "embed_size_mode", SIZE_MODE_FIXED);
  623. keep_aspect_button->set_pressed(EditorSettings::get_singleton()->get_project_metadata("game_view", "keep_aspect", true));
  624. _update_embed_menu_options();
  625. EditorRunBar::get_singleton()->connect("play_pressed", callable_mp(this, &GameView::_play_pressed));
  626. EditorRunBar::get_singleton()->connect("stop_pressed", callable_mp(this, &GameView::_stop_pressed));
  627. EditorRun::instance_starting_callback = _instance_starting_static;
  628. // Listen for project settings changes to update the window size and aspect ratio.
  629. ProjectSettings::get_singleton()->connect("settings_changed", callable_mp(this, &GameView::_editor_or_project_settings_changed));
  630. EditorSettings::get_singleton()->connect("settings_changed", callable_mp(this, &GameView::_editor_or_project_settings_changed));
  631. } else {
  632. // Embedding not available.
  633. embedding_separator->hide();
  634. embed_options_menu->hide();
  635. fixed_size_button->hide();
  636. keep_aspect_button->hide();
  637. stretch_button->hide();
  638. }
  639. _update_ui();
  640. } break;
  641. case NOTIFICATION_WM_POSITION_CHANGED: {
  642. if (window_wrapper->get_window_enabled()) {
  643. _update_floating_window_settings();
  644. }
  645. } break;
  646. }
  647. }
  648. void GameView::set_window_layout(Ref<ConfigFile> p_layout) {
  649. floating_window_rect = p_layout->get_value("GameView", "floating_window_rect", Rect2i());
  650. floating_window_screen = p_layout->get_value("GameView", "floating_window_screen", -1);
  651. }
  652. void GameView::get_window_layout(Ref<ConfigFile> p_layout) {
  653. if (window_wrapper->get_window_enabled()) {
  654. _update_floating_window_settings();
  655. }
  656. p_layout->set_value("GameView", "floating_window_rect", floating_window_rect);
  657. p_layout->set_value("GameView", "floating_window_screen", floating_window_screen);
  658. }
  659. void GameView::_update_floating_window_settings() {
  660. if (window_wrapper->get_window_enabled()) {
  661. floating_window_rect = window_wrapper->get_window_rect();
  662. floating_window_screen = window_wrapper->get_window_screen();
  663. }
  664. }
  665. void GameView::_attach_script_debugger() {
  666. if (embedded_script_debugger) {
  667. _detach_script_debugger();
  668. }
  669. embedded_script_debugger = nullptr;
  670. int i = 0;
  671. while (ScriptEditorDebugger *script_debugger = EditorDebuggerNode::get_singleton()->get_debugger(i)) {
  672. if (script_debugger->is_session_active() && script_debugger->get_remote_pid() == embedded_process->get_embedded_pid()) {
  673. embedded_script_debugger = script_debugger;
  674. break;
  675. }
  676. i++;
  677. }
  678. #ifdef MACOS_ENABLED
  679. embedded_process->set_script_debugger(embedded_script_debugger);
  680. #endif
  681. if (embedded_script_debugger) {
  682. embedded_script_debugger->connect("remote_window_title_changed", callable_mp(this, &GameView::_remote_window_title_changed));
  683. embedded_script_debugger->connect("embed_shortcut_requested", callable_mp(this, &GameView::_handle_shortcut_requested));
  684. }
  685. }
  686. void GameView::_detach_script_debugger() {
  687. if (embedded_script_debugger) {
  688. embedded_script_debugger->disconnect("remote_window_title_changed", callable_mp(this, &GameView::_remote_window_title_changed));
  689. embedded_script_debugger->disconnect("embed_shortcut_requested", callable_mp(this, &GameView::_handle_shortcut_requested));
  690. embedded_script_debugger = nullptr;
  691. }
  692. }
  693. void GameView::_remote_window_title_changed(String title) {
  694. window_wrapper->set_window_title(title);
  695. }
  696. void GameView::_update_arguments_for_instance(int p_idx, List<String> &r_arguments) {
  697. if (p_idx != 0 || !embed_on_play || _get_embed_available() != EMBED_AVAILABLE) {
  698. return;
  699. }
  700. // Remove duplicates/unwanted parameters.
  701. List<String>::Element *E = r_arguments.front();
  702. List<String>::Element *user_args_element = nullptr;
  703. while (E) {
  704. List<String>::Element *N = E->next();
  705. //For these parameters, we need to also renove the value.
  706. if (E->get() == "--position" || E->get() == "--resolution" || E->get() == "--screen") {
  707. r_arguments.erase(E);
  708. if (N) {
  709. List<String>::Element *V = N->next();
  710. r_arguments.erase(N);
  711. N = V;
  712. }
  713. } else if (E->get() == "-f" || E->get() == "--fullscreen" || E->get() == "-m" || E->get() == "--maximized" || E->get() == "-t" || E->get() == "-always-on-top") {
  714. r_arguments.erase(E);
  715. } else if (E->get() == "--" || E->get() == "++") {
  716. user_args_element = E;
  717. break;
  718. }
  719. E = N;
  720. }
  721. // Add the editor window's native ID so the started game can directly set it as its parent.
  722. List<String>::Element *N = r_arguments.insert_before(user_args_element, "--wid");
  723. N = r_arguments.insert_after(N, itos(DisplayServer::get_singleton()->window_get_native_handle(DisplayServer::WINDOW_HANDLE, get_window()->get_window_id())));
  724. #if MACOS_ENABLED
  725. r_arguments.push_back("--display-driver");
  726. r_arguments.push_back("embedded");
  727. r_arguments.push_back("--embedded");
  728. #endif
  729. // Be sure to have the correct window size in the embedded_process control.
  730. _update_embed_window_size();
  731. Rect2i rect = embedded_process->get_screen_embedded_window_rect();
  732. // Usually, the global rect of the embedded process control is invalid because it was hidden. We will calculate it manually.
  733. if (!window_wrapper->get_window_enabled()) {
  734. Size2 old_min_size = embedded_process->get_custom_minimum_size();
  735. embedded_process->set_custom_minimum_size(Size2i());
  736. Control *container = EditorNode::get_singleton()->get_editor_main_screen()->get_control();
  737. rect = container->get_global_rect();
  738. Size2 wrapped_min_size = window_wrapper->get_minimum_size();
  739. rect.position.y += wrapped_min_size.y;
  740. rect.size.y -= wrapped_min_size.y;
  741. rect = embedded_process->get_adjusted_embedded_window_rect(rect);
  742. embedded_process->set_custom_minimum_size(old_min_size);
  743. }
  744. // When using the floating window, we need to force the position and size from the
  745. // editor/project settings, because the get_screen_embedded_window_rect of the
  746. // embedded_process will be updated only on the next frame.
  747. if (window_wrapper->get_window_enabled()) {
  748. EditorRun::WindowPlacement placement = EditorRun::get_window_placement();
  749. if (placement.position != Point2i(INT_MAX, INT_MAX)) {
  750. rect.position = placement.position;
  751. }
  752. if (placement.size != Size2i()) {
  753. rect.size = placement.size;
  754. }
  755. }
  756. N = r_arguments.insert_after(N, "--position");
  757. N = r_arguments.insert_after(N, itos(rect.position.x) + "," + itos(rect.position.y));
  758. N = r_arguments.insert_after(N, "--resolution");
  759. r_arguments.insert_after(N, itos(rect.size.x) + "x" + itos(rect.size.y));
  760. }
  761. void GameView::_window_close_request() {
  762. // Before the parent window closed, we close the embedded game. That prevents
  763. // the embedded game to be seen without a parent window for a fraction of second.
  764. if (EditorRunBar::get_singleton()->is_playing() && (embedded_process->is_embedding_completed() || embedded_process->is_embedding_in_progress())) {
  765. // When the embedding is not complete, we need to kill the process.
  766. // If the game is paused, the close request will not be processed by the game, so it's better to kill the process.
  767. if (paused || embedded_process->is_embedding_in_progress()) {
  768. embedded_process->reset();
  769. // Call deferred to prevent the _stop_pressed callback to be executed before the wrapper window
  770. // actually closes.
  771. callable_mp(EditorRunBar::get_singleton(), &EditorRunBar::stop_playing).call_deferred();
  772. } else {
  773. // Try to gracefully close the window. That way, the NOTIFICATION_WM_CLOSE_REQUEST
  774. // notification should be propagated in the game process.
  775. embedded_process->request_close();
  776. }
  777. }
  778. }
  779. void GameView::_debugger_breaked(bool p_breaked, bool p_can_debug) {
  780. if (p_breaked == paused) {
  781. return;
  782. }
  783. paused = p_breaked;
  784. if (paused) {
  785. size_paused = embedded_process->get_screen_embedded_window_rect().size;
  786. }
  787. _update_embed_window_size();
  788. }
  789. void GameView::_feature_profile_changed() {
  790. Ref<EditorFeatureProfile> profile = EditorFeatureProfileManager::get_singleton()->get_current_profile();
  791. bool is_profile_null = profile.is_null();
  792. is_feature_enabled = is_profile_null || !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_GAME);
  793. bool is_3d_enabled = is_profile_null || !profile->is_feature_disabled(EditorFeatureProfile::FEATURE_3D);
  794. if (!is_3d_enabled && node_type_button[RuntimeNodeSelect::NODE_TYPE_3D]->is_pressed()) {
  795. _node_type_pressed(RuntimeNodeSelect::NODE_TYPE_NONE);
  796. }
  797. node_type_button[RuntimeNodeSelect::NODE_TYPE_3D]->set_visible(is_3d_enabled);
  798. }
  799. GameView::GameView(Ref<GameViewDebugger> p_debugger, EmbeddedProcessBase *p_embedded_process, WindowWrapper *p_wrapper) {
  800. singleton = this;
  801. debugger = p_debugger;
  802. window_wrapper = p_wrapper;
  803. embedded_process = p_embedded_process;
  804. // Add some margin to the sides for better aesthetics.
  805. // This prevents the first button's hover/pressed effect from "touching" the panel's border,
  806. // which looks ugly.
  807. MarginContainer *toolbar_margin = memnew(MarginContainer);
  808. toolbar_margin->add_theme_constant_override("margin_left", 4 * EDSCALE);
  809. toolbar_margin->add_theme_constant_override("margin_right", 4 * EDSCALE);
  810. add_child(toolbar_margin);
  811. HBoxContainer *main_menu_hbox = memnew(HBoxContainer);
  812. toolbar_margin->add_child(main_menu_hbox);
  813. suspend_button = memnew(Button);
  814. main_menu_hbox->add_child(suspend_button);
  815. suspend_button->set_toggle_mode(true);
  816. suspend_button->set_theme_type_variation(SceneStringName(FlatButton));
  817. suspend_button->connect(SceneStringName(toggled), callable_mp(this, &GameView::_suspend_button_toggled));
  818. suspend_button->set_tooltip_text(TTR("Suspend"));
  819. suspend_button->set_accessibility_name(TTRC("Suspend"));
  820. ED_SHORTCUT("editor/suspend_resume_embedded_project", TTRC("Suspend/Resume Embedded Project"), Key::F9);
  821. ED_SHORTCUT_OVERRIDE("editor/suspend_resume_embedded_project", "macos", KeyModifierMask::META | KeyModifierMask::SHIFT | Key::B);
  822. suspend_button->set_shortcut(ED_GET_SHORTCUT("editor/suspend_resume_embedded_project"));
  823. next_frame_button = memnew(Button);
  824. main_menu_hbox->add_child(next_frame_button);
  825. next_frame_button->set_theme_type_variation(SceneStringName(FlatButton));
  826. next_frame_button->connect(SceneStringName(pressed), callable_mp(*debugger, &GameViewDebugger::next_frame));
  827. next_frame_button->set_tooltip_text(TTR("Next Frame"));
  828. next_frame_button->set_accessibility_name(TTRC("Next Frame"));
  829. next_frame_button->set_shortcut(ED_SHORTCUT("editor/next_frame_embedded_project", TTRC("Next Frame"), Key::F10));
  830. main_menu_hbox->add_child(memnew(VSeparator));
  831. node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE] = memnew(Button);
  832. main_menu_hbox->add_child(node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE]);
  833. node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE]->set_text(TTR("Input"));
  834. node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE]->set_toggle_mode(true);
  835. node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE]->set_pressed(true);
  836. node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE]->set_theme_type_variation(SceneStringName(FlatButton));
  837. node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE]->connect(SceneStringName(pressed), callable_mp(this, &GameView::_node_type_pressed).bind(RuntimeNodeSelect::NODE_TYPE_NONE));
  838. node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE]->set_tooltip_text(TTR("Allow game input."));
  839. node_type_button[RuntimeNodeSelect::NODE_TYPE_2D] = memnew(Button);
  840. main_menu_hbox->add_child(node_type_button[RuntimeNodeSelect::NODE_TYPE_2D]);
  841. node_type_button[RuntimeNodeSelect::NODE_TYPE_2D]->set_text(TTR("2D"));
  842. node_type_button[RuntimeNodeSelect::NODE_TYPE_2D]->set_toggle_mode(true);
  843. node_type_button[RuntimeNodeSelect::NODE_TYPE_2D]->set_theme_type_variation(SceneStringName(FlatButton));
  844. node_type_button[RuntimeNodeSelect::NODE_TYPE_2D]->connect(SceneStringName(pressed), callable_mp(this, &GameView::_node_type_pressed).bind(RuntimeNodeSelect::NODE_TYPE_2D));
  845. node_type_button[RuntimeNodeSelect::NODE_TYPE_2D]->set_tooltip_text(TTR("Disable game input and allow to select Node2Ds, Controls, and manipulate the 2D camera."));
  846. node_type_button[RuntimeNodeSelect::NODE_TYPE_3D] = memnew(Button);
  847. main_menu_hbox->add_child(node_type_button[RuntimeNodeSelect::NODE_TYPE_3D]);
  848. node_type_button[RuntimeNodeSelect::NODE_TYPE_3D]->set_text(TTR("3D"));
  849. node_type_button[RuntimeNodeSelect::NODE_TYPE_3D]->set_toggle_mode(true);
  850. node_type_button[RuntimeNodeSelect::NODE_TYPE_3D]->set_theme_type_variation(SceneStringName(FlatButton));
  851. node_type_button[RuntimeNodeSelect::NODE_TYPE_3D]->connect(SceneStringName(pressed), callable_mp(this, &GameView::_node_type_pressed).bind(RuntimeNodeSelect::NODE_TYPE_3D));
  852. node_type_button[RuntimeNodeSelect::NODE_TYPE_3D]->set_tooltip_text(TTR("Disable game input and allow to select Node3Ds and manipulate the 3D camera."));
  853. main_menu_hbox->add_child(memnew(VSeparator));
  854. hide_selection = memnew(Button);
  855. main_menu_hbox->add_child(hide_selection);
  856. hide_selection->set_toggle_mode(true);
  857. hide_selection->set_theme_type_variation(SceneStringName(FlatButton));
  858. hide_selection->connect(SceneStringName(toggled), callable_mp(this, &GameView::_hide_selection_toggled));
  859. hide_selection->set_tooltip_text(TTR("Toggle Selection Visibility"));
  860. hide_selection->set_accessibility_name(TTRC("Selection Visibility"));
  861. hide_selection->set_pressed(EditorSettings::get_singleton()->get_project_metadata("game_view", "hide_selection", false));
  862. main_menu_hbox->add_child(memnew(VSeparator));
  863. select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE] = memnew(Button);
  864. main_menu_hbox->add_child(select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE]);
  865. select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE]->set_toggle_mode(true);
  866. select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE]->set_pressed(true);
  867. select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE]->set_theme_type_variation(SceneStringName(FlatButton));
  868. select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE]->connect(SceneStringName(pressed), callable_mp(this, &GameView::_select_mode_pressed).bind(RuntimeNodeSelect::SELECT_MODE_SINGLE));
  869. select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE]->set_shortcut(ED_SHORTCUT("spatial_editor/tool_select", TTRC("Select Mode"), Key::Q));
  870. select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE]->set_shortcut_context(this);
  871. select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE]->set_tooltip_text(keycode_get_string((Key)KeyModifierMask::CMD_OR_CTRL) + TTR("Alt+RMB: Show list of all nodes at position clicked."));
  872. select_mode_button[RuntimeNodeSelect::SELECT_MODE_LIST] = memnew(Button);
  873. main_menu_hbox->add_child(select_mode_button[RuntimeNodeSelect::SELECT_MODE_LIST]);
  874. select_mode_button[RuntimeNodeSelect::SELECT_MODE_LIST]->set_toggle_mode(true);
  875. select_mode_button[RuntimeNodeSelect::SELECT_MODE_LIST]->set_theme_type_variation(SceneStringName(FlatButton));
  876. select_mode_button[RuntimeNodeSelect::SELECT_MODE_LIST]->connect(SceneStringName(pressed), callable_mp(this, &GameView::_select_mode_pressed).bind(RuntimeNodeSelect::SELECT_MODE_LIST));
  877. select_mode_button[RuntimeNodeSelect::SELECT_MODE_LIST]->set_tooltip_text(TTR("Show list of selectable nodes at position clicked."));
  878. _select_mode_pressed(EditorSettings::get_singleton()->get_project_metadata("game_view", "select_mode", 0));
  879. main_menu_hbox->add_child(memnew(VSeparator));
  880. debug_mute_audio_button = memnew(Button);
  881. main_menu_hbox->add_child(debug_mute_audio_button);
  882. debug_mute_audio_button->set_theme_type_variation("FlatButton");
  883. debug_mute_audio_button->connect(SceneStringName(pressed), callable_mp(this, &GameView::_debug_mute_audio_button_pressed));
  884. debug_mute_audio_button->set_tooltip_text(debug_mute_audio ? TTRC("Unmute game audio.") : TTRC("Mute game audio."));
  885. main_menu_hbox->add_child(memnew(VSeparator));
  886. camera_override_button = memnew(Button);
  887. main_menu_hbox->add_child(camera_override_button);
  888. camera_override_button->set_toggle_mode(true);
  889. camera_override_button->set_theme_type_variation(SceneStringName(FlatButton));
  890. camera_override_button->set_tooltip_text(TTR("Override the in-game camera."));
  891. camera_override_button->set_accessibility_name(TTRC("Override In-game Camera"));
  892. camera_override_button->connect(SceneStringName(toggled), callable_mp(this, &GameView::_camera_override_button_toggled));
  893. camera_override_menu = memnew(MenuButton);
  894. main_menu_hbox->add_child(camera_override_menu);
  895. camera_override_menu->set_flat(false);
  896. camera_override_menu->set_theme_type_variation("FlatMenuButton");
  897. camera_override_menu->set_h_size_flags(SIZE_SHRINK_END);
  898. camera_override_menu->set_tooltip_text(TTR("Camera Override Options"));
  899. camera_override_menu->set_accessibility_name(TTRC("Camera Override Options"));
  900. PopupMenu *menu = camera_override_menu->get_popup();
  901. menu->connect(SceneStringName(id_pressed), callable_mp(this, &GameView::_camera_override_menu_id_pressed));
  902. menu->add_item(TTR("Reset 2D Camera"), CAMERA_RESET_2D);
  903. menu->add_item(TTR("Reset 3D Camera"), CAMERA_RESET_3D);
  904. menu->add_separator();
  905. menu->add_radio_check_item(TTR("Manipulate In-Game"), CAMERA_MODE_INGAME);
  906. menu->set_item_checked(menu->get_item_index(CAMERA_MODE_INGAME), true);
  907. menu->add_radio_check_item(TTR("Manipulate From Editors"), CAMERA_MODE_EDITORS);
  908. _camera_override_menu_id_pressed(EditorSettings::get_singleton()->get_project_metadata("game_view", "camera_override_mode", 0));
  909. embedding_separator = memnew(VSeparator);
  910. main_menu_hbox->add_child(embedding_separator);
  911. fixed_size_button = memnew(Button);
  912. main_menu_hbox->add_child(fixed_size_button);
  913. fixed_size_button->set_toggle_mode(true);
  914. fixed_size_button->set_theme_type_variation("FlatButton");
  915. fixed_size_button->set_tooltip_text(TTR("Embedded game size is based on project settings.\nThe 'Keep Aspect' mode is used when the Game Workspace is smaller than the desired size."));
  916. fixed_size_button->set_accessibility_name(TTRC("Fixed Size"));
  917. fixed_size_button->connect(SceneStringName(pressed), callable_mp(this, &GameView::_size_mode_button_pressed).bind(SIZE_MODE_FIXED));
  918. keep_aspect_button = memnew(Button);
  919. main_menu_hbox->add_child(keep_aspect_button);
  920. keep_aspect_button->set_toggle_mode(true);
  921. keep_aspect_button->set_theme_type_variation("FlatButton");
  922. keep_aspect_button->set_tooltip_text(TTR("Keep the aspect ratio of the embedded game."));
  923. keep_aspect_button->set_accessibility_name(TTRC("Keep Aspect Ratio"));
  924. keep_aspect_button->connect(SceneStringName(pressed), callable_mp(this, &GameView::_size_mode_button_pressed).bind(SIZE_MODE_KEEP_ASPECT));
  925. stretch_button = memnew(Button);
  926. main_menu_hbox->add_child(stretch_button);
  927. stretch_button->set_toggle_mode(true);
  928. stretch_button->set_theme_type_variation("FlatButton");
  929. stretch_button->set_tooltip_text(TTR("Embedded game size stretches to fit the Game Workspace."));
  930. stretch_button->set_accessibility_name(TTRC("Stretch"));
  931. stretch_button->connect(SceneStringName(pressed), callable_mp(this, &GameView::_size_mode_button_pressed).bind(SIZE_MODE_STRETCH));
  932. embed_options_menu = memnew(MenuButton);
  933. main_menu_hbox->add_child(embed_options_menu);
  934. embed_options_menu->set_flat(false);
  935. embed_options_menu->set_theme_type_variation("FlatMenuButton");
  936. embed_options_menu->set_h_size_flags(SIZE_SHRINK_END);
  937. embed_options_menu->set_tooltip_text(TTR("Embedding Options"));
  938. embed_options_menu->set_accessibility_name(TTRC("Embedding Options"));
  939. menu = embed_options_menu->get_popup();
  940. menu->connect(SceneStringName(id_pressed), callable_mp(this, &GameView::_embed_options_menu_menu_id_pressed));
  941. menu->add_check_item(TTR("Embed Game on Next Play"), EMBED_RUN_GAME_EMBEDDED);
  942. menu->add_check_item(TTR("Make Game Workspace Floating on Next Play"), EMBED_MAKE_FLOATING_ON_PLAY);
  943. main_menu_hbox->add_spacer();
  944. game_size_label = memnew(Label());
  945. main_menu_hbox->add_child(game_size_label);
  946. game_size_label->hide();
  947. // Setting the minimum size prevents the game workspace from resizing indefinitely
  948. // due to the label size oscillating by a few pixels when the game is in stretch mode
  949. // and the game workspace is at its minimum size.
  950. game_size_label->set_custom_minimum_size(Size2(80 * EDSCALE, 0));
  951. game_size_label->set_horizontal_alignment(HorizontalAlignment::HORIZONTAL_ALIGNMENT_RIGHT);
  952. panel = memnew(Panel);
  953. add_child(panel);
  954. panel->set_theme_type_variation("GamePanel");
  955. panel->set_v_size_flags(SIZE_EXPAND_FILL);
  956. panel->add_child(embedded_process);
  957. embedded_process->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
  958. embedded_process->connect("embedding_failed", callable_mp(this, &GameView::_embedding_failed));
  959. embedded_process->connect("embedding_completed", callable_mp(this, &GameView::_embedding_completed));
  960. embedded_process->connect("embedded_process_updated", callable_mp(this, &GameView::_embedded_process_updated));
  961. embedded_process->connect("embedded_process_focused", callable_mp(this, &GameView::_embedded_process_focused));
  962. embedded_process->set_custom_minimum_size(Size2i(100, 100));
  963. MarginContainer *state_container = memnew(MarginContainer);
  964. state_container->add_theme_constant_override("margin_left", 8 * EDSCALE);
  965. state_container->add_theme_constant_override("margin_right", 8 * EDSCALE);
  966. state_container->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
  967. #ifdef MACOS_ENABLED
  968. state_container->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
  969. #endif
  970. panel->add_child(state_container);
  971. state_label = memnew(Label());
  972. state_container->add_child(state_label);
  973. state_label->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_CENTER);
  974. state_label->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);
  975. state_label->set_autowrap_mode(TextServer::AUTOWRAP_WORD);
  976. state_label->set_anchors_and_offsets_preset(PRESET_FULL_RECT);
  977. _update_debugger_buttons();
  978. p_debugger->connect("session_started", callable_mp(this, &GameView::_sessions_changed));
  979. p_debugger->connect("session_stopped", callable_mp(this, &GameView::_sessions_changed));
  980. p_wrapper->set_override_close_request(true);
  981. p_wrapper->connect("window_close_requested", callable_mp(this, &GameView::_window_close_request));
  982. p_wrapper->connect("window_size_changed", callable_mp(this, &GameView::_update_floating_window_settings));
  983. EditorDebuggerNode::get_singleton()->connect("breaked", callable_mp(this, &GameView::_debugger_breaked));
  984. EditorFeatureProfileManager::get_singleton()->connect("current_feature_profile_changed", callable_mp(this, &GameView::_feature_profile_changed));
  985. }
  986. ///////
  987. void GameViewPluginBase::selected_notify() {
  988. if (_is_window_wrapper_enabled()) {
  989. #ifdef ANDROID_ENABLED
  990. notify_main_screen_changed(get_plugin_name());
  991. #else
  992. window_wrapper->grab_window_focus();
  993. #endif // ANDROID_ENABLED
  994. _focus_another_editor();
  995. }
  996. }
  997. #ifndef ANDROID_ENABLED
  998. void GameViewPluginBase::make_visible(bool p_visible) {
  999. if (p_visible) {
  1000. window_wrapper->show();
  1001. } else {
  1002. window_wrapper->hide();
  1003. }
  1004. }
  1005. void GameViewPluginBase::set_window_layout(Ref<ConfigFile> p_layout) {
  1006. game_view->set_window_layout(p_layout);
  1007. }
  1008. void GameViewPluginBase::get_window_layout(Ref<ConfigFile> p_layout) {
  1009. game_view->get_window_layout(p_layout);
  1010. }
  1011. void GameViewPluginBase::setup(Ref<GameViewDebugger> p_debugger, EmbeddedProcessBase *p_embedded_process) {
  1012. debugger = p_debugger;
  1013. window_wrapper = memnew(WindowWrapper);
  1014. window_wrapper->set_window_title(vformat(TTR("%s - Godot Engine"), TTR("Game Workspace")));
  1015. window_wrapper->set_margins_enabled(true);
  1016. game_view = memnew(GameView(debugger, p_embedded_process, window_wrapper));
  1017. game_view->set_v_size_flags(Control::SIZE_EXPAND_FILL);
  1018. window_wrapper->set_wrapped_control(game_view, nullptr);
  1019. EditorNode::get_singleton()->get_editor_main_screen()->get_control()->add_child(window_wrapper);
  1020. window_wrapper->set_v_size_flags(Control::SIZE_EXPAND_FILL);
  1021. window_wrapper->hide();
  1022. window_wrapper->connect("window_visibility_changed", callable_mp(this, &GameViewPlugin::_focus_another_editor).unbind(1));
  1023. }
  1024. #endif // ANDROID_ENABLED
  1025. void GameViewPluginBase::_notification(int p_what) {
  1026. switch (p_what) {
  1027. case NOTIFICATION_ENTER_TREE: {
  1028. add_debugger_plugin(debugger);
  1029. connect("main_screen_changed", callable_mp(this, &GameViewPluginBase::_save_last_editor));
  1030. } break;
  1031. case NOTIFICATION_EXIT_TREE: {
  1032. remove_debugger_plugin(debugger);
  1033. disconnect("main_screen_changed", callable_mp(this, &GameViewPluginBase::_save_last_editor));
  1034. } break;
  1035. }
  1036. }
  1037. void GameViewPluginBase::_save_last_editor(const String &p_editor) {
  1038. if (p_editor != get_plugin_name()) {
  1039. last_editor = p_editor;
  1040. }
  1041. }
  1042. void GameViewPluginBase::_focus_another_editor() {
  1043. if (_is_window_wrapper_enabled()) {
  1044. if (last_editor.is_empty()) {
  1045. EditorNode::get_singleton()->get_editor_main_screen()->select(EditorMainScreen::EDITOR_2D);
  1046. } else {
  1047. EditorInterface::get_singleton()->set_main_screen_editor(last_editor);
  1048. }
  1049. }
  1050. }
  1051. bool GameViewPluginBase::_is_window_wrapper_enabled() const {
  1052. #ifdef ANDROID_ENABLED
  1053. return true;
  1054. #else
  1055. return window_wrapper->get_window_enabled();
  1056. #endif // ANDROID_ENABLED
  1057. }
  1058. GameViewPluginBase::GameViewPluginBase() {
  1059. }
  1060. GameViewPlugin::GameViewPlugin() :
  1061. GameViewPluginBase() {
  1062. #ifndef ANDROID_ENABLED
  1063. Ref<GameViewDebugger> game_view_debugger;
  1064. game_view_debugger.instantiate();
  1065. EmbeddedProcess *embedded_process = memnew(EmbeddedProcess);
  1066. setup(game_view_debugger, embedded_process);
  1067. #endif
  1068. }