game_view_plugin.cpp 64 KB

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