game_view_plugin.cpp 52 KB

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