2024-09-18 18:07:18 -03:00
/**************************************************************************/
/* game_view_plugin.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
# include "game_view_plugin.h"
2024-09-25 16:57:23 -04:00
# include "core/config/project_settings.h"
2024-11-06 04:01:58 +01:00
# include "core/debugger/debugger_marshalls.h"
2025-01-19 15:45:59 -05:00
# include "core/string/translation_server.h"
2024-09-25 16:57:23 -04:00
# include "editor/debugger/editor_debugger_node.h"
2025-01-19 15:45:59 -05:00
# include "editor/debugger/script_editor_debugger.h"
2025-01-10 14:53:41 -05:00
# include "editor/editor_feature_profile.h"
2024-09-25 16:57:23 -04:00
# include "editor/editor_interface.h"
2024-09-18 18:07:18 -03:00
# include "editor/editor_main_screen.h"
# include "editor/editor_node.h"
# include "editor/editor_settings.h"
2025-01-20 18:33:12 -05:00
# include "editor/editor_string_names.h"
2025-02-17 18:14:41 -05:00
# include "editor/gui/editor_bottom_panel.h"
2024-09-25 16:57:23 -04:00
# include "editor/gui/editor_run_bar.h"
# include "editor/plugins/embedded_process.h"
2024-09-18 18:07:18 -03:00
# include "editor/themes/editor_scale.h"
2024-09-25 16:57:23 -04:00
# include "editor/window_wrapper.h"
2024-09-18 18:07:18 -03:00
# include "scene/gui/button.h"
2024-09-25 16:57:23 -04:00
# include "scene/gui/label.h"
2024-09-18 18:07:18 -03:00
# include "scene/gui/menu_button.h"
# include "scene/gui/panel.h"
# include "scene/gui/separator.h"
void GameViewDebugger : : _session_started ( Ref < EditorDebuggerSession > p_session ) {
2025-01-10 14:53:41 -05:00
if ( ! is_feature_enabled ) {
return ;
}
2024-11-06 04:01:58 +01:00
Dictionary settings ;
2024-11-18 15:44:38 -03:00
settings [ " debugger/max_node_selection " ] = EDITOR_GET ( " debugger/max_node_selection " ) ;
2024-11-06 04:01:58 +01:00
settings [ " editors/panning/2d_editor_panning_scheme " ] = EDITOR_GET ( " editors/panning/2d_editor_panning_scheme " ) ;
settings [ " editors/panning/simple_panning " ] = EDITOR_GET ( " editors/panning/simple_panning " ) ;
settings [ " editors/panning/warped_mouse_panning " ] = EDITOR_GET ( " editors/panning/warped_mouse_panning " ) ;
settings [ " editors/panning/2d_editor_pan_speed " ] = EDITOR_GET ( " editors/panning/2d_editor_pan_speed " ) ;
2024-11-18 15:44:38 -03:00
settings [ " editors/polygon_editor/point_grab_radius " ] = EDITOR_GET ( " editors/polygon_editor/point_grab_radius " ) ;
2024-11-06 04:01:58 +01:00
settings [ " canvas_item_editor/pan_view " ] = DebuggerMarshalls : : serialize_key_shortcut ( ED_GET_SHORTCUT ( " canvas_item_editor/pan_view " ) ) ;
2024-11-18 15:44:38 -03:00
settings [ " box_selection_fill_color " ] = EditorNode : : get_singleton ( ) - > get_editor_theme ( ) - > get_color ( SNAME ( " box_selection_fill_color " ) , EditorStringName ( Editor ) ) ;
settings [ " box_selection_stroke_color " ] = EditorNode : : get_singleton ( ) - > get_editor_theme ( ) - > get_color ( SNAME ( " box_selection_stroke_color " ) , EditorStringName ( Editor ) ) ;
2025-03-06 23:07:24 -03:00
# ifndef _3D_DISABLED
settings [ " editors/3d/default_fov " ] = EDITOR_GET ( " editors/3d/default_fov " ) ;
settings [ " editors/3d/default_z_near " ] = EDITOR_GET ( " editors/3d/default_z_near " ) ;
settings [ " editors/3d/default_z_far " ] = EDITOR_GET ( " editors/3d/default_z_far " ) ;
settings [ " editors/3d/navigation/invert_x_axis " ] = EDITOR_GET ( " editors/3d/navigation/invert_x_axis " ) ;
settings [ " editors/3d/navigation/invert_y_axis " ] = EDITOR_GET ( " editors/3d/navigation/invert_y_axis " ) ;
settings [ " editors/3d/navigation/warped_mouse_panning " ] = EDITOR_GET ( " editors/3d/navigation/warped_mouse_panning " ) ;
2025-02-09 14:22:24 +01:00
settings [ " editors/3d/freelook/freelook_base_speed " ] = EDITOR_GET ( " editors/3d/freelook/freelook_base_speed " ) ;
2025-03-06 23:07:24 -03:00
settings [ " editors/3d/freelook/freelook_sensitivity " ] = EDITOR_GET ( " editors/3d/freelook/freelook_sensitivity " ) ;
settings [ " editors/3d/navigation_feel/orbit_sensitivity " ] = EDITOR_GET ( " editors/3d/navigation_feel/orbit_sensitivity " ) ;
settings [ " editors/3d/navigation_feel/translation_sensitivity " ] = EDITOR_GET ( " editors/3d/navigation_feel/translation_sensitivity " ) ;
2024-11-18 15:44:38 -03:00
settings [ " editors/3d/selection_box_color " ] = EDITOR_GET ( " editors/3d/selection_box_color " ) ;
settings [ " editors/3d/freelook/freelook_base_speed " ] = EDITOR_GET ( " editors/3d/freelook/freelook_base_speed " ) ;
2025-03-06 23:07:24 -03:00
# endif // _3D_DISABLED
2024-11-18 15:44:38 -03:00
Array setup_data ;
2024-11-06 04:01:58 +01:00
setup_data . append ( settings ) ;
p_session - > send_message ( " scene:runtime_node_select_setup " , setup_data ) ;
2024-09-18 18:07:18 -03:00
Array type ;
type . append ( node_type ) ;
p_session - > send_message ( " scene:runtime_node_select_set_type " , type ) ;
Array visible ;
visible . append ( selection_visible ) ;
p_session - > send_message ( " scene:runtime_node_select_set_visible " , visible ) ;
Array mode ;
mode . append ( select_mode ) ;
p_session - > send_message ( " scene:runtime_node_select_set_mode " , mode ) ;
2024-11-21 21:31:00 -08:00
Array mute_audio_data ;
mute_audio_data . append ( mute_audio ) ;
p_session - > send_message ( " scene:debug_mute_audio " , mute_audio_data ) ;
2024-09-18 18:07:18 -03:00
emit_signal ( SNAME ( " session_started " ) ) ;
}
void GameViewDebugger : : _session_stopped ( ) {
2025-01-10 14:53:41 -05:00
if ( ! is_feature_enabled ) {
return ;
}
2024-09-18 18:07:18 -03:00
emit_signal ( SNAME ( " session_stopped " ) ) ;
}
2025-01-10 14:53:41 -05:00
void GameViewDebugger : : set_is_feature_enabled ( bool p_enabled ) {
is_feature_enabled = p_enabled ;
}
2024-09-18 18:07:18 -03:00
void GameViewDebugger : : set_suspend ( bool p_enabled ) {
Array message ;
message . append ( p_enabled ) ;
for ( Ref < EditorDebuggerSession > & I : sessions ) {
if ( I - > is_active ( ) ) {
I - > send_message ( " scene:suspend_changed " , message ) ;
}
}
}
void GameViewDebugger : : next_frame ( ) {
for ( Ref < EditorDebuggerSession > & I : sessions ) {
if ( I - > is_active ( ) ) {
I - > send_message ( " scene:next_frame " , Array ( ) ) ;
}
}
}
void GameViewDebugger : : set_node_type ( int p_type ) {
node_type = p_type ;
Array message ;
message . append ( p_type ) ;
for ( Ref < EditorDebuggerSession > & I : sessions ) {
if ( I - > is_active ( ) ) {
I - > send_message ( " scene:runtime_node_select_set_type " , message ) ;
}
}
}
void GameViewDebugger : : set_selection_visible ( bool p_visible ) {
selection_visible = p_visible ;
Array message ;
message . append ( p_visible ) ;
for ( Ref < EditorDebuggerSession > & I : sessions ) {
if ( I - > is_active ( ) ) {
I - > send_message ( " scene:runtime_node_select_set_visible " , message ) ;
}
}
}
void GameViewDebugger : : set_select_mode ( int p_mode ) {
select_mode = p_mode ;
Array message ;
message . append ( p_mode ) ;
for ( Ref < EditorDebuggerSession > & I : sessions ) {
if ( I - > is_active ( ) ) {
I - > send_message ( " scene:runtime_node_select_set_mode " , message ) ;
}
}
}
2024-11-21 21:31:00 -08:00
void GameViewDebugger : : set_debug_mute_audio ( bool p_enabled ) {
mute_audio = p_enabled ;
EditorDebuggerNode : : get_singleton ( ) - > set_debug_mute_audio ( p_enabled ) ;
}
2024-09-18 18:07:18 -03:00
void GameViewDebugger : : set_camera_override ( bool p_enabled ) {
EditorDebuggerNode : : get_singleton ( ) - > set_camera_override ( p_enabled ? camera_override_mode : EditorDebuggerNode : : OVERRIDE_NONE ) ;
}
void GameViewDebugger : : set_camera_manipulate_mode ( EditorDebuggerNode : : CameraOverride p_mode ) {
camera_override_mode = p_mode ;
if ( EditorDebuggerNode : : get_singleton ( ) - > get_camera_override ( ) ! = EditorDebuggerNode : : OVERRIDE_NONE ) {
set_camera_override ( true ) ;
}
}
void GameViewDebugger : : reset_camera_2d_position ( ) {
for ( Ref < EditorDebuggerSession > & I : sessions ) {
if ( I - > is_active ( ) ) {
I - > send_message ( " scene:runtime_node_select_reset_camera_2d " , Array ( ) ) ;
}
}
}
void GameViewDebugger : : reset_camera_3d_position ( ) {
for ( Ref < EditorDebuggerSession > & I : sessions ) {
if ( I - > is_active ( ) ) {
I - > send_message ( " scene:runtime_node_select_reset_camera_3d " , Array ( ) ) ;
}
}
}
void GameViewDebugger : : setup_session ( int p_session_id ) {
Ref < EditorDebuggerSession > session = get_session ( p_session_id ) ;
ERR_FAIL_COND ( session . is_null ( ) ) ;
sessions . append ( session ) ;
session - > connect ( " started " , callable_mp ( this , & GameViewDebugger : : _session_started ) . bind ( session ) ) ;
session - > connect ( " stopped " , callable_mp ( this , & GameViewDebugger : : _session_stopped ) ) ;
}
void GameViewDebugger : : _bind_methods ( ) {
ADD_SIGNAL ( MethodInfo ( " session_started " ) ) ;
ADD_SIGNAL ( MethodInfo ( " session_stopped " ) ) ;
}
///////
void GameView : : _sessions_changed ( ) {
// The debugger session's `session_started/stopped` signal can be unreliable, so count it manually.
active_sessions = 0 ;
Array sessions = debugger - > get_sessions ( ) ;
for ( int i = 0 ; i < sessions . size ( ) ; i + + ) {
if ( Object : : cast_to < EditorDebuggerSession > ( sessions [ i ] ) - > is_active ( ) ) {
active_sessions + + ;
}
}
_update_debugger_buttons ( ) ;
2025-01-19 15:45:59 -05:00
if ( embedded_process - > is_embedding_completed ( ) ) {
if ( ! embedded_script_debugger | | ! embedded_script_debugger - > is_session_active ( ) | | embedded_script_debugger - > get_remote_pid ( ) ! = embedded_process - > get_embedded_pid ( ) ) {
_attach_script_debugger ( ) ;
}
}
2024-09-18 18:07:18 -03:00
}
2024-09-25 16:57:23 -04:00
void GameView : : _instance_starting_static ( int p_idx , List < String > & r_arguments ) {
ERR_FAIL_NULL ( singleton ) ;
singleton - > _instance_starting ( p_idx , r_arguments ) ;
}
void GameView : : _instance_starting ( int p_idx , List < String > & r_arguments ) {
2025-01-10 14:53:41 -05:00
if ( ! is_feature_enabled ) {
return ;
}
2025-01-31 07:22:24 -05:00
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 ) {
2025-01-19 15:45:59 -05:00
// Set the Floating Window default title. Always considered in DEBUG mode, same as in Window::set_title.
String appname = GLOBAL_GET ( " application/config/name " ) ;
appname = vformat ( " %s (DEBUG) " , TranslationServer : : get_singleton ( ) - > translate ( appname ) ) ;
window_wrapper - > set_window_title ( appname ) ;
2025-01-20 18:33:12 -05:00
_show_update_window_wrapper ( ) ;
2025-02-01 21:15:54 -05:00
embedded_process - > grab_focus ( ) ;
2024-09-25 16:57:23 -04:00
}
_update_arguments_for_instance ( p_idx , r_arguments ) ;
}
2025-01-20 18:33:12 -05:00
void GameView : : _show_update_window_wrapper ( ) {
EditorRun : : WindowPlacement placement = EditorRun : : get_window_placement ( ) ;
Point2 position = floating_window_rect . position ;
Size2i size = floating_window_rect . size ;
int screen = floating_window_screen ;
// Obtain the size around the embedded process control. Usually, the difference between the game view's get_size
// and the embedded control should work. However, when the control is hidden and has never been displayed,
// the size of the embedded control is not calculated.
Size2 old_min_size = embedded_process - > get_custom_minimum_size ( ) ;
embedded_process - > set_custom_minimum_size ( Size2i ( ) ) ;
2025-02-11 21:41:03 -05:00
Size2 embedded_process_min_size = get_minimum_size ( ) ;
Size2 wrapped_margins_size = window_wrapper - > get_margins_size ( ) ;
Size2 wrapped_min_size = window_wrapper - > get_minimum_size ( ) ;
Point2 offset_embedded_process = embedded_process - > get_global_position ( ) - get_global_position ( ) ;
// On the first startup, the global position of the embedded process control is invalid because it was
2025-03-01 10:31:31 -05:00
// never displayed. We will calculate it manually using the minimum size of the window.
2025-02-11 21:41:03 -05:00
if ( offset_embedded_process = = Point2 ( ) ) {
offset_embedded_process . y = wrapped_min_size . y ;
}
offset_embedded_process . x + = embedded_process - > get_margin_size ( SIDE_LEFT ) ;
offset_embedded_process . y + = embedded_process - > get_margin_size ( SIDE_TOP ) ;
offset_embedded_process + = window_wrapper - > get_margins_top_left ( ) ;
2025-01-20 18:33:12 -05:00
embedded_process - > set_custom_minimum_size ( old_min_size ) ;
2025-02-11 21:41:03 -05:00
Point2 size_diff_embedded_process = Point2 ( 0 , embedded_process_min_size . y ) + embedded_process - > get_margins_size ( ) ;
2025-01-20 18:33:12 -05:00
if ( placement . position ! = Point2i ( INT_MAX , INT_MAX ) ) {
position = placement . position - offset_embedded_process ;
screen = placement . screen ;
}
if ( placement . size ! = Size2i ( ) ) {
size = placement . size + size_diff_embedded_process + wrapped_margins_size ;
}
window_wrapper - > restore_window_from_saved_position ( Rect2 ( position , size ) , screen , Rect2i ( ) ) ;
}
2024-09-25 16:57:23 -04:00
void GameView : : _play_pressed ( ) {
2025-01-10 14:53:41 -05:00
if ( ! is_feature_enabled ) {
return ;
}
2024-09-25 16:57:23 -04:00
OS : : ProcessID current_process_id = EditorRunBar : : get_singleton ( ) - > get_current_process ( ) ;
if ( current_process_id = = 0 ) {
return ;
}
if ( ! window_wrapper - > get_window_enabled ( ) ) {
screen_index_before_start = EditorNode : : get_singleton ( ) - > get_editor_main_screen ( ) - > get_selected_index ( ) ;
}
2025-01-20 18:33:12 -05:00
if ( embed_on_play & & _get_embed_available ( ) = = EMBED_AVAILABLE ) {
2024-09-25 16:57:23 -04:00
// It's important to disable the low power mode when unfocused because otherwise
// the button in the editor are not responsive and if the user moves the mouse quickly,
// the mouse clicks are not registered.
EditorNode : : get_singleton ( ) - > set_unfocused_low_processor_usage_mode_enabled ( false ) ;
_update_embed_window_size ( ) ;
if ( ! window_wrapper - > get_window_enabled ( ) ) {
EditorNode : : get_singleton ( ) - > get_editor_main_screen ( ) - > select ( EditorMainScreen : : EDITOR_GAME ) ;
2025-02-17 18:14:41 -05:00
// Reset the normal size of the bottom panel when fully expanded.
EditorNode : : get_singleton ( ) - > get_bottom_panel ( ) - > set_expanded ( false ) ;
2024-09-25 16:57:23 -04:00
embedded_process - > grab_focus ( ) ;
}
embedded_process - > embed_process ( current_process_id ) ;
_update_ui ( ) ;
}
}
void GameView : : _stop_pressed ( ) {
2025-01-10 14:53:41 -05:00
if ( ! is_feature_enabled ) {
return ;
}
2025-01-19 15:45:59 -05:00
_detach_script_debugger ( ) ;
2025-01-24 20:44:47 -05:00
paused = false ;
2025-01-19 15:45:59 -05:00
2024-09-25 16:57:23 -04:00
EditorNode : : get_singleton ( ) - > set_unfocused_low_processor_usage_mode_enabled ( true ) ;
embedded_process - > reset ( ) ;
_update_ui ( ) ;
if ( window_wrapper - > get_window_enabled ( ) ) {
window_wrapper - > set_window_enabled ( false ) ;
}
if ( screen_index_before_start > = 0 & & EditorNode : : get_singleton ( ) - > get_editor_main_screen ( ) - > get_selected_index ( ) = = EditorMainScreen : : EDITOR_GAME ) {
// We go back to the screen where the user was before starting the game.
EditorNode : : get_singleton ( ) - > get_editor_main_screen ( ) - > select ( screen_index_before_start ) ;
}
screen_index_before_start = - 1 ;
}
void GameView : : _embedding_completed ( ) {
2025-01-19 15:45:59 -05:00
_attach_script_debugger ( ) ;
2024-09-25 16:57:23 -04:00
_update_ui ( ) ;
}
void GameView : : _embedding_failed ( ) {
state_label - > set_text ( TTR ( " Connection impossible to the game process. " ) ) ;
}
void GameView : : _embedded_process_updated ( ) {
const Rect2i game_rect = embedded_process - > get_screen_embedded_window_rect ( ) ;
game_size_label - > set_text ( vformat ( " %dx%d " , game_rect . size . x , game_rect . size . y ) ) ;
}
2024-12-29 18:16:19 -05:00
void GameView : : _embedded_process_focused ( ) {
if ( embed_on_play & & ! window_wrapper - > get_window_enabled ( ) ) {
EditorNode : : get_singleton ( ) - > get_editor_main_screen ( ) - > select ( EditorMainScreen : : EDITOR_GAME ) ;
}
}
2025-01-20 18:33:12 -05:00
void GameView : : _editor_or_project_settings_changed ( ) {
2024-09-25 16:57:23 -04:00
// Update the window size and aspect ratio.
_update_embed_window_size ( ) ;
2025-01-20 18:33:12 -05:00
if ( window_wrapper - > get_window_enabled ( ) ) {
_show_update_window_wrapper ( ) ;
if ( embedded_process - > is_embedding_completed ( ) ) {
embedded_process - > queue_update_embedded_process ( ) ;
}
}
_update_ui ( ) ;
2024-09-25 16:57:23 -04:00
}
2024-09-18 18:07:18 -03:00
void GameView : : _update_debugger_buttons ( ) {
bool empty = active_sessions = = 0 ;
suspend_button - > set_disabled ( empty ) ;
camera_override_button - > set_disabled ( empty ) ;
PopupMenu * menu = camera_override_menu - > get_popup ( ) ;
bool disable_camera_reset = empty | | ! camera_override_button - > is_pressed ( ) | | ! menu - > is_item_checked ( menu - > get_item_index ( CAMERA_MODE_INGAME ) ) ;
menu - > set_item_disabled ( CAMERA_RESET_2D , disable_camera_reset ) ;
menu - > set_item_disabled ( CAMERA_RESET_3D , disable_camera_reset ) ;
if ( empty ) {
suspend_button - > set_pressed ( false ) ;
camera_override_button - > set_pressed ( false ) ;
}
next_frame_button - > set_disabled ( ! suspend_button - > is_pressed ( ) ) ;
}
void GameView : : _suspend_button_toggled ( bool p_pressed ) {
_update_debugger_buttons ( ) ;
debugger - > set_suspend ( p_pressed ) ;
}
void GameView : : _node_type_pressed ( int p_option ) {
RuntimeNodeSelect : : NodeType type = ( RuntimeNodeSelect : : NodeType ) p_option ;
for ( int i = 0 ; i < RuntimeNodeSelect : : NODE_TYPE_MAX ; i + + ) {
node_type_button [ i ] - > set_pressed_no_signal ( i = = type ) ;
}
_update_debugger_buttons ( ) ;
debugger - > set_node_type ( type ) ;
}
void GameView : : _select_mode_pressed ( int p_option ) {
RuntimeNodeSelect : : SelectMode mode = ( RuntimeNodeSelect : : SelectMode ) p_option ;
for ( int i = 0 ; i < RuntimeNodeSelect : : SELECT_MODE_MAX ; i + + ) {
select_mode_button [ i ] - > set_pressed_no_signal ( i = = mode ) ;
}
debugger - > set_select_mode ( mode ) ;
2024-11-18 15:44:38 -03:00
EditorSettings : : get_singleton ( ) - > set_project_metadata ( " game_view " , " select_mode " , mode ) ;
2024-09-18 18:07:18 -03:00
}
2024-09-25 16:57:23 -04:00
void GameView : : _embed_options_menu_menu_id_pressed ( int p_id ) {
switch ( p_id ) {
case EMBED_RUN_GAME_EMBEDDED : {
embed_on_play = ! embed_on_play ;
2025-01-08 12:17:35 +02:00
int game_mode = EDITOR_GET ( " run/window_placement/game_embed_mode " ) ;
if ( game_mode = = 0 ) { // Save only if not overridden by editor.
EditorSettings : : get_singleton ( ) - > set_project_metadata ( " game_view " , " embed_on_play " , embed_on_play ) ;
}
2024-09-25 16:57:23 -04:00
} break ;
case EMBED_MAKE_FLOATING_ON_PLAY : {
make_floating_on_play = ! make_floating_on_play ;
2025-01-08 12:17:35 +02:00
int game_mode = EDITOR_GET ( " run/window_placement/game_embed_mode " ) ;
if ( game_mode = = 0 ) { // Save only if not overridden by editor.
EditorSettings : : get_singleton ( ) - > set_project_metadata ( " game_view " , " make_floating_on_play " , make_floating_on_play ) ;
}
2024-09-25 16:57:23 -04:00
} break ;
}
_update_embed_menu_options ( ) ;
2025-01-20 18:33:12 -05:00
_update_ui ( ) ;
2024-09-25 16:57:23 -04:00
}
2025-01-20 18:33:12 -05:00
void GameView : : _size_mode_button_pressed ( int size_mode ) {
embed_size_mode = ( EmbedSizeMode ) size_mode ;
2025-01-25 13:25:56 -03:00
EditorSettings : : get_singleton ( ) - > set_project_metadata ( " game_view " , " embed_size_mode " , size_mode ) ;
2025-01-20 18:33:12 -05:00
_update_embed_menu_options ( ) ;
_update_embed_window_size ( ) ;
}
GameView : : EmbedAvailability GameView : : _get_embed_available ( ) {
if ( ! DisplayServer : : get_singleton ( ) - > has_feature ( DisplayServer : : FEATURE_WINDOW_EMBEDDING ) ) {
return EMBED_NOT_AVAILABLE_FEATURE_NOT_SUPPORTED ;
}
2025-01-31 07:22:24 -05:00
if ( get_tree ( ) - > get_root ( ) - > is_embedding_subwindows ( ) ) {
2025-01-22 16:34:01 -05:00
return EMBED_NOT_AVAILABLE_SINGLE_WINDOW_MODE ;
}
2025-02-13 18:42:42 -05:00
String display_driver = GLOBAL_GET ( " display/display_server/driver " ) ;
if ( display_driver = = " headless " | | display_driver = = " wayland " ) {
return EMBED_NOT_AVAILABLE_PROJECT_DISPLAY_DRIVER ;
}
2025-01-20 18:33:12 -05:00
EditorRun : : WindowPlacement placement = EditorRun : : get_window_placement ( ) ;
if ( placement . force_fullscreen ) {
return EMBED_NOT_AVAILABLE_FULLSCREEN ;
}
if ( placement . force_maximized ) {
return EMBED_NOT_AVAILABLE_MAXIMIZED ;
}
DisplayServer : : WindowMode window_mode = ( DisplayServer : : WindowMode ) ( GLOBAL_GET ( " display/window/size/mode " ) . operator int ( ) ) ;
if ( window_mode = = DisplayServer : : WindowMode : : WINDOW_MODE_MINIMIZED ) {
return EMBED_NOT_AVAILABLE_MINIMIZED ;
}
if ( window_mode = = DisplayServer : : WindowMode : : WINDOW_MODE_MAXIMIZED ) {
return EMBED_NOT_AVAILABLE_MAXIMIZED ;
}
if ( window_mode = = DisplayServer : : WindowMode : : WINDOW_MODE_FULLSCREEN | | window_mode = = DisplayServer : : WindowMode : : WINDOW_MODE_EXCLUSIVE_FULLSCREEN ) {
return EMBED_NOT_AVAILABLE_FULLSCREEN ;
}
return EMBED_AVAILABLE ;
2024-09-25 16:57:23 -04:00
}
void GameView : : _update_ui ( ) {
bool show_game_size = false ;
2025-01-20 18:33:12 -05:00
EmbedAvailability available = _get_embed_available ( ) ;
switch ( available ) {
case EMBED_AVAILABLE :
if ( embedded_process - > is_embedding_completed ( ) ) {
state_label - > set_text ( " " ) ;
show_game_size = true ;
} else if ( embedded_process - > is_embedding_in_progress ( ) ) {
state_label - > set_text ( TTR ( " Game starting... " ) ) ;
} else if ( EditorRunBar : : get_singleton ( ) - > is_playing ( ) ) {
state_label - > set_text ( TTR ( " Game running not embedded. " ) ) ;
} else if ( embed_on_play ) {
state_label - > set_text ( TTR ( " Press play to start the game. " ) ) ;
} else {
state_label - > set_text ( TTR ( " Embedding is disabled. " ) ) ;
}
break ;
case EMBED_NOT_AVAILABLE_FEATURE_NOT_SUPPORTED :
2025-02-13 18:42:42 -05:00
if ( DisplayServer : : get_singleton ( ) - > get_name ( ) = = " Wayland " ) {
state_label - > set_text ( TTR ( " Game embedding not available on Wayland. \n Wayland can be disabled in the Editor Settings (Run > Platforms > Linux/*BSD > Prefer Wayland). " ) ) ;
} else {
state_label - > set_text ( TTR ( " Game embedding not available on your OS. " ) ) ;
}
break ;
case EMBED_NOT_AVAILABLE_PROJECT_DISPLAY_DRIVER :
state_label - > set_text ( vformat ( TTR ( " Game embedding not available for the Display Server: '%s'. \n Display Server can be modified in the Project Settings (Display > Display Server > Driver). " ) , GLOBAL_GET ( " display/display_server/driver " ) ) ) ;
2025-01-20 18:33:12 -05:00
break ;
case EMBED_NOT_AVAILABLE_MINIMIZED :
2025-02-10 18:45:30 +01:00
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. " ) ) ;
2025-01-20 18:33:12 -05:00
break ;
case EMBED_NOT_AVAILABLE_MAXIMIZED :
2025-02-10 18:45:30 +01:00
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. " ) ) ;
2025-01-20 18:33:12 -05:00
break ;
case EMBED_NOT_AVAILABLE_FULLSCREEN :
2025-02-10 18:45:30 +01:00
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. " ) ) ;
2025-01-20 18:33:12 -05:00
break ;
2025-01-22 16:34:01 -05:00
case EMBED_NOT_AVAILABLE_SINGLE_WINDOW_MODE :
state_label - > set_text ( TTR ( " Game embedding not available in single window mode. " ) ) ;
break ;
2025-01-20 18:33:12 -05:00
}
if ( available = = EMBED_AVAILABLE ) {
if ( state_label - > has_theme_color_override ( SceneStringName ( font_color ) ) ) {
state_label - > remove_theme_color_override ( SceneStringName ( font_color ) ) ;
}
2024-09-25 16:57:23 -04:00
} else {
2025-01-20 18:33:12 -05:00
state_label - > add_theme_color_override ( SceneStringName ( font_color ) , state_label - > get_theme_color ( SNAME ( " warning_color " ) , EditorStringName ( Editor ) ) ) ;
2024-09-25 16:57:23 -04:00
}
game_size_label - > set_visible ( show_game_size ) ;
}
void GameView : : _update_embed_menu_options ( ) {
2025-01-31 07:22:24 -05:00
bool is_multi_window = window_wrapper - > is_window_available ( ) ;
2024-09-25 16:57:23 -04:00
PopupMenu * menu = embed_options_menu - > get_popup ( ) ;
menu - > set_item_checked ( menu - > get_item_index ( EMBED_RUN_GAME_EMBEDDED ) , embed_on_play ) ;
2025-01-31 07:22:24 -05:00
menu - > set_item_checked ( menu - > get_item_index ( EMBED_MAKE_FLOATING_ON_PLAY ) , make_floating_on_play & & is_multi_window ) ;
2024-09-25 16:57:23 -04:00
2025-01-31 07:22:24 -05:00
menu - > set_item_disabled ( menu - > get_item_index ( EMBED_MAKE_FLOATING_ON_PLAY ) , ! embed_on_play | | ! is_multi_window ) ;
2025-01-20 18:33:12 -05:00
fixed_size_button - > set_pressed ( embed_size_mode = = SIZE_MODE_FIXED ) ;
keep_aspect_button - > set_pressed ( embed_size_mode = = SIZE_MODE_KEEP_ASPECT ) ;
stretch_button - > set_pressed ( embed_size_mode = = SIZE_MODE_STRETCH ) ;
2024-09-25 16:57:23 -04:00
}
void GameView : : _update_embed_window_size ( ) {
2025-01-24 20:44:47 -05:00
if ( paused ) {
// When paused, Godot does not re-render. As a result, resizing the game window to a larger size
// causes artifacts and flickering. However, resizing to a smaller size seems fine.
// To prevent artifacts and flickering, we will force the game window to maintain its size.
// Using the same technique as SIZE_MODE_FIXED, the embedded process control will
// prevent resizing the game to a larger size while maintaining the aspect ratio.
embedded_process - > set_window_size ( size_paused ) ;
embedded_process - > set_keep_aspect ( false ) ;
2025-01-20 18:33:12 -05:00
} else {
2025-01-24 20:44:47 -05:00
if ( embed_size_mode = = SIZE_MODE_FIXED | | embed_size_mode = = SIZE_MODE_KEEP_ASPECT ) {
// The embedded process control will need the desired window size.
EditorRun : : WindowPlacement placement = EditorRun : : get_window_placement ( ) ;
embedded_process - > set_window_size ( placement . size ) ;
} else {
// Stretch... No need for the window size.
embedded_process - > set_window_size ( Size2i ( ) ) ;
}
embedded_process - > set_keep_aspect ( embed_size_mode = = SIZE_MODE_KEEP_ASPECT ) ;
2024-09-25 16:57:23 -04:00
}
}
2024-09-18 18:07:18 -03:00
void GameView : : _hide_selection_toggled ( bool p_pressed ) {
hide_selection - > set_button_icon ( get_editor_theme_icon ( p_pressed ? SNAME ( " GuiVisibilityHidden " ) : SNAME ( " GuiVisibilityVisible " ) ) ) ;
debugger - > set_selection_visible ( ! p_pressed ) ;
2024-11-18 15:44:38 -03:00
EditorSettings : : get_singleton ( ) - > set_project_metadata ( " game_view " , " hide_selection " , p_pressed ) ;
2024-09-18 18:07:18 -03:00
}
2024-11-21 21:31:00 -08:00
void GameView : : _debug_mute_audio_button_pressed ( ) {
debug_mute_audio = ! debug_mute_audio ;
debug_mute_audio_button - > set_button_icon ( get_editor_theme_icon ( debug_mute_audio ? SNAME ( " AudioMute " ) : SNAME ( " AudioStreamPlayer " ) ) ) ;
debug_mute_audio_button - > set_tooltip_text ( debug_mute_audio ? TTRC ( " Unmute game audio. " ) : TTRC ( " Mute game audio. " ) ) ;
debugger - > set_debug_mute_audio ( debug_mute_audio ) ;
}
2024-09-18 18:07:18 -03:00
void GameView : : _camera_override_button_toggled ( bool p_pressed ) {
_update_debugger_buttons ( ) ;
debugger - > set_camera_override ( p_pressed ) ;
}
void GameView : : _camera_override_menu_id_pressed ( int p_id ) {
PopupMenu * menu = camera_override_menu - > get_popup ( ) ;
if ( p_id ! = CAMERA_RESET_2D & & p_id ! = CAMERA_RESET_3D ) {
for ( int i = 0 ; i < menu - > get_item_count ( ) ; i + + ) {
menu - > set_item_checked ( i , false ) ;
}
}
switch ( p_id ) {
case CAMERA_RESET_2D : {
debugger - > reset_camera_2d_position ( ) ;
} break ;
case CAMERA_RESET_3D : {
debugger - > reset_camera_3d_position ( ) ;
} break ;
case CAMERA_MODE_INGAME : {
debugger - > set_camera_manipulate_mode ( EditorDebuggerNode : : OVERRIDE_INGAME ) ;
menu - > set_item_checked ( menu - > get_item_index ( p_id ) , true ) ;
_update_debugger_buttons ( ) ;
2024-11-18 15:44:38 -03:00
EditorSettings : : get_singleton ( ) - > set_project_metadata ( " game_view " , " camera_override_mode " , p_id ) ;
2024-09-18 18:07:18 -03:00
} break ;
case CAMERA_MODE_EDITORS : {
debugger - > set_camera_manipulate_mode ( EditorDebuggerNode : : OVERRIDE_EDITORS ) ;
menu - > set_item_checked ( menu - > get_item_index ( p_id ) , true ) ;
_update_debugger_buttons ( ) ;
2024-11-18 15:44:38 -03:00
EditorSettings : : get_singleton ( ) - > set_project_metadata ( " game_view " , " camera_override_mode " , p_id ) ;
2024-09-18 18:07:18 -03:00
} break ;
}
}
void GameView : : _notification ( int p_what ) {
switch ( p_what ) {
case NOTIFICATION_THEME_CHANGED : {
suspend_button - > set_button_icon ( get_editor_theme_icon ( SNAME ( " Pause " ) ) ) ;
next_frame_button - > set_button_icon ( get_editor_theme_icon ( SNAME ( " NextFrame " ) ) ) ;
node_type_button [ RuntimeNodeSelect : : NODE_TYPE_NONE ] - > set_button_icon ( get_editor_theme_icon ( SNAME ( " InputEventJoypadMotion " ) ) ) ;
node_type_button [ RuntimeNodeSelect : : NODE_TYPE_2D ] - > set_button_icon ( get_editor_theme_icon ( SNAME ( " 2DNodes " ) ) ) ;
# ifndef _3D_DISABLED
node_type_button [ RuntimeNodeSelect : : NODE_TYPE_3D ] - > set_button_icon ( get_editor_theme_icon ( SNAME ( " Node3D " ) ) ) ;
# endif // _3D_DISABLED
select_mode_button [ RuntimeNodeSelect : : SELECT_MODE_SINGLE ] - > set_button_icon ( get_editor_theme_icon ( SNAME ( " ToolSelect " ) ) ) ;
select_mode_button [ RuntimeNodeSelect : : SELECT_MODE_LIST ] - > set_button_icon ( get_editor_theme_icon ( SNAME ( " ListSelect " ) ) ) ;
hide_selection - > set_button_icon ( get_editor_theme_icon ( hide_selection - > is_pressed ( ) ? SNAME ( " GuiVisibilityHidden " ) : SNAME ( " GuiVisibilityVisible " ) ) ) ;
2025-01-20 18:33:12 -05:00
fixed_size_button - > set_button_icon ( get_editor_theme_icon ( SNAME ( " FixedSize " ) ) ) ;
2024-09-25 16:57:23 -04:00
keep_aspect_button - > set_button_icon ( get_editor_theme_icon ( SNAME ( " KeepAspect " ) ) ) ;
2025-01-20 18:33:12 -05:00
stretch_button - > set_button_icon ( get_editor_theme_icon ( SNAME ( " Stretch " ) ) ) ;
2024-09-25 16:57:23 -04:00
embed_options_menu - > set_button_icon ( get_editor_theme_icon ( SNAME ( " GuiTabMenuHl " ) ) ) ;
2024-09-18 18:07:18 -03:00
2024-11-21 21:31:00 -08:00
debug_mute_audio_button - > set_button_icon ( get_editor_theme_icon ( debug_mute_audio ? SNAME ( " AudioMute " ) : SNAME ( " AudioStreamPlayer " ) ) ) ;
2024-09-18 18:07:18 -03:00
camera_override_button - > set_button_icon ( get_editor_theme_icon ( SNAME ( " Camera " ) ) ) ;
camera_override_menu - > set_button_icon ( get_editor_theme_icon ( SNAME ( " GuiTabMenuHl " ) ) ) ;
} break ;
2024-09-25 16:57:23 -04:00
case NOTIFICATION_READY : {
if ( DisplayServer : : get_singleton ( ) - > has_feature ( DisplayServer : : FEATURE_WINDOW_EMBEDDING ) ) {
// Embedding available.
2025-01-08 12:17:35 +02:00
int game_mode = EDITOR_GET ( " run/window_placement/game_embed_mode " ) ;
switch ( game_mode ) {
2025-01-07 21:31:53 -08:00
case - 1 : { // Disabled.
embed_on_play = false ;
make_floating_on_play = false ;
} break ;
2025-01-08 12:17:35 +02:00
case 1 : { // Embed.
embed_on_play = true ;
make_floating_on_play = false ;
} break ;
case 2 : { // Floating.
embed_on_play = true ;
make_floating_on_play = true ;
} break ;
default : {
embed_on_play = EditorSettings : : get_singleton ( ) - > get_project_metadata ( " game_view " , " embed_on_play " , true ) ;
make_floating_on_play = EditorSettings : : get_singleton ( ) - > get_project_metadata ( " game_view " , " make_floating_on_play " , true ) ;
} break ;
}
2025-01-20 18:33:12 -05:00
embed_size_mode = ( EmbedSizeMode ) ( int ) EditorSettings : : get_singleton ( ) - > get_project_metadata ( " game_view " , " embed_size_mode " , SIZE_MODE_FIXED ) ;
2025-01-08 12:17:35 +02:00
keep_aspect_button - > set_pressed ( EditorSettings : : get_singleton ( ) - > get_project_metadata ( " game_view " , " keep_aspect " , true ) ) ;
2024-09-25 16:57:23 -04:00
_update_embed_menu_options ( ) ;
EditorRunBar : : get_singleton ( ) - > connect ( " play_pressed " , callable_mp ( this , & GameView : : _play_pressed ) ) ;
EditorRunBar : : get_singleton ( ) - > connect ( " stop_pressed " , callable_mp ( this , & GameView : : _stop_pressed ) ) ;
EditorRun : : instance_starting_callback = _instance_starting_static ;
// Listen for project settings changes to update the window size and aspect ratio.
2025-01-20 18:33:12 -05:00
ProjectSettings : : get_singleton ( ) - > connect ( " settings_changed " , callable_mp ( this , & GameView : : _editor_or_project_settings_changed ) ) ;
EditorSettings : : get_singleton ( ) - > connect ( " settings_changed " , callable_mp ( this , & GameView : : _editor_or_project_settings_changed ) ) ;
2024-09-25 16:57:23 -04:00
} else {
// Embedding not available.
embedding_separator - > hide ( ) ;
embed_options_menu - > hide ( ) ;
2025-01-20 18:33:12 -05:00
fixed_size_button - > hide ( ) ;
2024-09-25 16:57:23 -04:00
keep_aspect_button - > hide ( ) ;
2025-01-20 18:33:12 -05:00
stretch_button - > hide ( ) ;
2024-09-25 16:57:23 -04:00
}
_update_ui ( ) ;
} break ;
case NOTIFICATION_WM_POSITION_CHANGED : {
if ( window_wrapper - > get_window_enabled ( ) ) {
_update_floating_window_settings ( ) ;
}
} break ;
2024-09-18 18:07:18 -03:00
}
}
2025-01-10 14:53:41 -05:00
void GameView : : set_is_feature_enabled ( bool p_enabled ) {
is_feature_enabled = p_enabled ;
}
2024-09-25 16:57:23 -04:00
void GameView : : set_window_layout ( Ref < ConfigFile > p_layout ) {
floating_window_rect = p_layout - > get_value ( " GameView " , " floating_window_rect " , Rect2i ( ) ) ;
floating_window_screen = p_layout - > get_value ( " GameView " , " floating_window_screen " , - 1 ) ;
}
void GameView : : get_window_layout ( Ref < ConfigFile > p_layout ) {
if ( window_wrapper - > get_window_enabled ( ) ) {
_update_floating_window_settings ( ) ;
}
p_layout - > set_value ( " GameView " , " floating_window_rect " , floating_window_rect ) ;
p_layout - > set_value ( " GameView " , " floating_window_screen " , floating_window_screen ) ;
}
void GameView : : _update_floating_window_settings ( ) {
if ( window_wrapper - > get_window_enabled ( ) ) {
floating_window_rect = window_wrapper - > get_window_rect ( ) ;
floating_window_screen = window_wrapper - > get_window_screen ( ) ;
}
}
2025-01-19 15:45:59 -05:00
void GameView : : _attach_script_debugger ( ) {
if ( embedded_script_debugger ) {
_detach_script_debugger ( ) ;
}
embedded_script_debugger = nullptr ;
for ( int i = 0 ; EditorDebuggerNode : : get_singleton ( ) - > get_debugger ( i ) ; i + + ) {
ScriptEditorDebugger * script_debugger = EditorDebuggerNode : : get_singleton ( ) - > get_debugger ( i ) ;
if ( script_debugger - > is_session_active ( ) & & script_debugger - > get_remote_pid ( ) = = embedded_process - > get_embedded_pid ( ) ) {
embedded_script_debugger = script_debugger ;
break ;
}
}
if ( embedded_script_debugger ) {
embedded_script_debugger - > connect ( " remote_window_title_changed " , callable_mp ( this , & GameView : : _remote_window_title_changed ) ) ;
}
}
void GameView : : _detach_script_debugger ( ) {
if ( embedded_script_debugger ) {
embedded_script_debugger - > disconnect ( " remote_window_title_changed " , callable_mp ( this , & GameView : : _remote_window_title_changed ) ) ;
embedded_script_debugger = nullptr ;
}
}
void GameView : : _remote_window_title_changed ( String title ) {
window_wrapper - > set_window_title ( title ) ;
}
2024-09-25 16:57:23 -04:00
void GameView : : _update_arguments_for_instance ( int p_idx , List < String > & r_arguments ) {
2025-01-20 18:33:12 -05:00
if ( p_idx ! = 0 | | ! embed_on_play | | _get_embed_available ( ) ! = EMBED_AVAILABLE ) {
2024-09-25 16:57:23 -04:00
return ;
}
// Remove duplicates/unwanted parameters.
List < String > : : Element * E = r_arguments . front ( ) ;
2025-01-18 00:36:07 -05:00
List < String > : : Element * user_args_element = nullptr ;
2024-09-25 16:57:23 -04:00
while ( E ) {
List < String > : : Element * N = E - > next ( ) ;
//For these parameters, we need to also renove the value.
if ( E - > get ( ) = = " --position " | | E - > get ( ) = = " --resolution " | | E - > get ( ) = = " --screen " ) {
r_arguments . erase ( E ) ;
if ( N ) {
List < String > : : Element * V = N - > next ( ) ;
r_arguments . erase ( N ) ;
N = V ;
}
} else if ( E - > get ( ) = = " -f " | | E - > get ( ) = = " --fullscreen " | | E - > get ( ) = = " -m " | | E - > get ( ) = = " --maximized " | | E - > get ( ) = = " -t " | | E - > get ( ) = = " -always-on-top " ) {
r_arguments . erase ( E ) ;
2025-01-18 00:36:07 -05:00
} else if ( E - > get ( ) = = " -- " | | E - > get ( ) = = " ++ " ) {
user_args_element = E ;
break ;
2024-09-25 16:57:23 -04:00
}
E = N ;
}
// Add the editor window's native ID so the started game can directly set it as its parent.
2025-01-18 00:36:07 -05:00
List < String > : : Element * N = r_arguments . insert_before ( user_args_element , " --wid " ) ;
N = r_arguments . insert_after ( N , itos ( DisplayServer : : get_singleton ( ) - > window_get_native_handle ( DisplayServer : : WINDOW_HANDLE , get_window ( ) - > get_window_id ( ) ) ) ) ;
2024-09-25 16:57:23 -04:00
// Be sure to have the correct window size in the embedded_process control.
_update_embed_window_size ( ) ;
Rect2i rect = embedded_process - > get_screen_embedded_window_rect ( ) ;
2025-02-11 21:41:03 -05:00
2025-03-01 10:31:31 -05:00
// Usually, the global rect of the embedded process control is invalid because it was hidden. We will calculate it manually.
if ( ! window_wrapper - > get_window_enabled ( ) ) {
2025-02-20 20:24:12 -05:00
Size2 old_min_size = embedded_process - > get_custom_minimum_size ( ) ;
embedded_process - > set_custom_minimum_size ( Size2i ( ) ) ;
Control * container = EditorNode : : get_singleton ( ) - > get_editor_main_screen ( ) - > get_control ( ) ;
rect = container - > get_global_rect ( ) ;
Size2 wrapped_min_size = window_wrapper - > get_minimum_size ( ) ;
rect . position . y + = wrapped_min_size . y ;
rect . size . y - = wrapped_min_size . y ;
rect = embedded_process - > get_adjusted_embedded_window_rect ( rect ) ;
embedded_process - > set_custom_minimum_size ( old_min_size ) ;
}
2025-02-11 21:41:03 -05:00
// When using the floating window, we need to force the position and size from the
// editor/project settings, because the get_screen_embedded_window_rect of the
// embedded_process will be updated only on the next frame.
2025-02-20 20:24:12 -05:00
if ( window_wrapper - > get_window_enabled ( ) ) {
2025-02-11 21:41:03 -05:00
EditorRun : : WindowPlacement placement = EditorRun : : get_window_placement ( ) ;
if ( placement . position ! = Point2i ( INT_MAX , INT_MAX ) ) {
rect . position = placement . position ;
}
if ( placement . size ! = Size2i ( ) ) {
rect . size = placement . size ;
}
}
2025-01-18 00:36:07 -05:00
N = r_arguments . insert_after ( N , " --position " ) ;
N = r_arguments . insert_after ( N , itos ( rect . position . x ) + " , " + itos ( rect . position . y ) ) ;
N = r_arguments . insert_after ( N , " --resolution " ) ;
r_arguments . insert_after ( N , itos ( rect . size . x ) + " x " + itos ( rect . size . y ) ) ;
2024-09-25 16:57:23 -04:00
}
2025-01-21 19:12:27 -05:00
void GameView : : _window_close_request ( ) {
2024-09-25 16:57:23 -04:00
// Before the parent window closed, we close the embedded game. That prevents
// the embedded game to be seen without a parent window for a fraction of second.
if ( EditorRunBar : : get_singleton ( ) - > is_playing ( ) & & ( embedded_process - > is_embedding_completed ( ) | | embedded_process - > is_embedding_in_progress ( ) ) ) {
2025-01-21 19:12:27 -05:00
// When the embedding is not complete, we need to kill the process.
2025-01-24 20:44:47 -05:00
// If the game is paused, the close request will not be processed by the game, so it's better to kill the process.
if ( paused | | embedded_process - > is_embedding_in_progress ( ) ) {
2025-02-05 19:51:30 -05:00
embedded_process - > reset ( ) ;
2025-01-21 19:12:27 -05:00
// Call deferred to prevent the _stop_pressed callback to be executed before the wrapper window
// actually closes.
callable_mp ( EditorRunBar : : get_singleton ( ) , & EditorRunBar : : stop_playing ) . call_deferred ( ) ;
2025-02-05 19:51:30 -05:00
} else {
// Try to gracefully close the window. That way, the NOTIFICATION_WM_CLOSE_REQUEST
// notification should be propagated in the game process.
embedded_process - > request_close ( ) ;
2025-01-21 19:12:27 -05:00
}
2024-09-25 16:57:23 -04:00
}
}
2025-01-24 20:44:47 -05:00
void GameView : : _debugger_breaked ( bool p_breaked , bool p_can_debug ) {
if ( p_breaked = = paused ) {
return ;
}
paused = p_breaked ;
if ( paused ) {
size_paused = embedded_process - > get_screen_embedded_window_rect ( ) . size ;
}
_update_embed_window_size ( ) ;
}
2024-09-25 16:57:23 -04:00
GameView : : GameView ( Ref < GameViewDebugger > p_debugger , WindowWrapper * p_wrapper ) {
singleton = this ;
2024-09-18 18:07:18 -03:00
debugger = p_debugger ;
2024-09-25 16:57:23 -04:00
window_wrapper = p_wrapper ;
2024-09-18 18:07:18 -03:00
// Add some margin to the sides for better aesthetics.
// This prevents the first button's hover/pressed effect from "touching" the panel's border,
// which looks ugly.
MarginContainer * toolbar_margin = memnew ( MarginContainer ) ;
toolbar_margin - > add_theme_constant_override ( " margin_left " , 4 * EDSCALE ) ;
toolbar_margin - > add_theme_constant_override ( " margin_right " , 4 * EDSCALE ) ;
add_child ( toolbar_margin ) ;
HBoxContainer * main_menu_hbox = memnew ( HBoxContainer ) ;
toolbar_margin - > add_child ( main_menu_hbox ) ;
suspend_button = memnew ( Button ) ;
main_menu_hbox - > add_child ( suspend_button ) ;
suspend_button - > set_toggle_mode ( true ) ;
2024-12-05 17:13:20 +03:00
suspend_button - > set_theme_type_variation ( SceneStringName ( FlatButton ) ) ;
2024-09-18 18:07:18 -03:00
suspend_button - > connect ( SceneStringName ( toggled ) , callable_mp ( this , & GameView : : _suspend_button_toggled ) ) ;
suspend_button - > set_tooltip_text ( TTR ( " Suspend " ) ) ;
next_frame_button = memnew ( Button ) ;
main_menu_hbox - > add_child ( next_frame_button ) ;
2024-12-05 17:13:20 +03:00
next_frame_button - > set_theme_type_variation ( SceneStringName ( FlatButton ) ) ;
2024-09-18 18:07:18 -03:00
next_frame_button - > connect ( SceneStringName ( pressed ) , callable_mp ( * debugger , & GameViewDebugger : : next_frame ) ) ;
next_frame_button - > set_tooltip_text ( TTR ( " Next Frame " ) ) ;
main_menu_hbox - > add_child ( memnew ( VSeparator ) ) ;
node_type_button [ RuntimeNodeSelect : : NODE_TYPE_NONE ] = memnew ( Button ) ;
main_menu_hbox - > add_child ( node_type_button [ RuntimeNodeSelect : : NODE_TYPE_NONE ] ) ;
node_type_button [ RuntimeNodeSelect : : NODE_TYPE_NONE ] - > set_text ( TTR ( " Input " ) ) ;
node_type_button [ RuntimeNodeSelect : : NODE_TYPE_NONE ] - > set_toggle_mode ( true ) ;
node_type_button [ RuntimeNodeSelect : : NODE_TYPE_NONE ] - > set_pressed ( true ) ;
2024-12-05 17:13:20 +03:00
node_type_button [ RuntimeNodeSelect : : NODE_TYPE_NONE ] - > set_theme_type_variation ( SceneStringName ( FlatButton ) ) ;
2024-09-18 18:07:18 -03:00
node_type_button [ RuntimeNodeSelect : : NODE_TYPE_NONE ] - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & GameView : : _node_type_pressed ) . bind ( RuntimeNodeSelect : : NODE_TYPE_NONE ) ) ;
node_type_button [ RuntimeNodeSelect : : NODE_TYPE_NONE ] - > set_tooltip_text ( TTR ( " Allow game input. " ) ) ;
node_type_button [ RuntimeNodeSelect : : NODE_TYPE_2D ] = memnew ( Button ) ;
main_menu_hbox - > add_child ( node_type_button [ RuntimeNodeSelect : : NODE_TYPE_2D ] ) ;
node_type_button [ RuntimeNodeSelect : : NODE_TYPE_2D ] - > set_text ( TTR ( " 2D " ) ) ;
node_type_button [ RuntimeNodeSelect : : NODE_TYPE_2D ] - > set_toggle_mode ( true ) ;
2024-12-05 17:13:20 +03:00
node_type_button [ RuntimeNodeSelect : : NODE_TYPE_2D ] - > set_theme_type_variation ( SceneStringName ( FlatButton ) ) ;
2024-09-18 18:07:18 -03:00
node_type_button [ RuntimeNodeSelect : : NODE_TYPE_2D ] - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & GameView : : _node_type_pressed ) . bind ( RuntimeNodeSelect : : NODE_TYPE_2D ) ) ;
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. " ) ) ;
# ifndef _3D_DISABLED
node_type_button [ RuntimeNodeSelect : : NODE_TYPE_3D ] = memnew ( Button ) ;
main_menu_hbox - > add_child ( node_type_button [ RuntimeNodeSelect : : NODE_TYPE_3D ] ) ;
node_type_button [ RuntimeNodeSelect : : NODE_TYPE_3D ] - > set_text ( TTR ( " 3D " ) ) ;
node_type_button [ RuntimeNodeSelect : : NODE_TYPE_3D ] - > set_toggle_mode ( true ) ;
2024-12-05 17:13:20 +03:00
node_type_button [ RuntimeNodeSelect : : NODE_TYPE_3D ] - > set_theme_type_variation ( SceneStringName ( FlatButton ) ) ;
2024-09-18 18:07:18 -03:00
node_type_button [ RuntimeNodeSelect : : NODE_TYPE_3D ] - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & GameView : : _node_type_pressed ) . bind ( RuntimeNodeSelect : : NODE_TYPE_3D ) ) ;
node_type_button [ RuntimeNodeSelect : : NODE_TYPE_3D ] - > set_tooltip_text ( TTR ( " Disable game input and allow to select Node3Ds and manipulate the 3D camera. " ) ) ;
# endif // _3D_DISABLED
main_menu_hbox - > add_child ( memnew ( VSeparator ) ) ;
hide_selection = memnew ( Button ) ;
main_menu_hbox - > add_child ( hide_selection ) ;
hide_selection - > set_toggle_mode ( true ) ;
2024-12-05 17:13:20 +03:00
hide_selection - > set_theme_type_variation ( SceneStringName ( FlatButton ) ) ;
2024-09-18 18:07:18 -03:00
hide_selection - > connect ( SceneStringName ( toggled ) , callable_mp ( this , & GameView : : _hide_selection_toggled ) ) ;
hide_selection - > set_tooltip_text ( TTR ( " Toggle Selection Visibility " ) ) ;
2024-11-18 15:44:38 -03:00
hide_selection - > set_pressed ( EditorSettings : : get_singleton ( ) - > get_project_metadata ( " game_view " , " hide_selection " , false ) ) ;
2024-09-18 18:07:18 -03:00
main_menu_hbox - > add_child ( memnew ( VSeparator ) ) ;
select_mode_button [ RuntimeNodeSelect : : SELECT_MODE_SINGLE ] = memnew ( Button ) ;
main_menu_hbox - > add_child ( select_mode_button [ RuntimeNodeSelect : : SELECT_MODE_SINGLE ] ) ;
select_mode_button [ RuntimeNodeSelect : : SELECT_MODE_SINGLE ] - > set_toggle_mode ( true ) ;
select_mode_button [ RuntimeNodeSelect : : SELECT_MODE_SINGLE ] - > set_pressed ( true ) ;
2024-12-05 17:13:20 +03:00
select_mode_button [ RuntimeNodeSelect : : SELECT_MODE_SINGLE ] - > set_theme_type_variation ( SceneStringName ( FlatButton ) ) ;
2024-09-18 18:07:18 -03:00
select_mode_button [ RuntimeNodeSelect : : SELECT_MODE_SINGLE ] - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & GameView : : _select_mode_pressed ) . bind ( RuntimeNodeSelect : : SELECT_MODE_SINGLE ) ) ;
2024-10-18 19:07:44 +08:00
select_mode_button [ RuntimeNodeSelect : : SELECT_MODE_SINGLE ] - > set_shortcut ( ED_SHORTCUT ( " spatial_editor/tool_select " , TTRC ( " Select Mode " ) , Key : : Q ) ) ;
2024-09-18 18:07:18 -03:00
select_mode_button [ RuntimeNodeSelect : : SELECT_MODE_SINGLE ] - > set_shortcut_context ( this ) ;
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. " ) ) ;
select_mode_button [ RuntimeNodeSelect : : SELECT_MODE_LIST ] = memnew ( Button ) ;
main_menu_hbox - > add_child ( select_mode_button [ RuntimeNodeSelect : : SELECT_MODE_LIST ] ) ;
select_mode_button [ RuntimeNodeSelect : : SELECT_MODE_LIST ] - > set_toggle_mode ( true ) ;
2024-12-05 17:13:20 +03:00
select_mode_button [ RuntimeNodeSelect : : SELECT_MODE_LIST ] - > set_theme_type_variation ( SceneStringName ( FlatButton ) ) ;
2024-09-18 18:07:18 -03:00
select_mode_button [ RuntimeNodeSelect : : SELECT_MODE_LIST ] - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & GameView : : _select_mode_pressed ) . bind ( RuntimeNodeSelect : : SELECT_MODE_LIST ) ) ;
select_mode_button [ RuntimeNodeSelect : : SELECT_MODE_LIST ] - > set_tooltip_text ( TTR ( " Show list of selectable nodes at position clicked. " ) ) ;
2024-11-18 15:44:38 -03:00
_select_mode_pressed ( EditorSettings : : get_singleton ( ) - > get_project_metadata ( " game_view " , " select_mode " , 0 ) ) ;
2024-09-18 18:07:18 -03:00
main_menu_hbox - > add_child ( memnew ( VSeparator ) ) ;
2024-11-21 21:31:00 -08:00
debug_mute_audio_button = memnew ( Button ) ;
main_menu_hbox - > add_child ( debug_mute_audio_button ) ;
debug_mute_audio_button - > set_theme_type_variation ( " FlatButton " ) ;
debug_mute_audio_button - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & GameView : : _debug_mute_audio_button_pressed ) ) ;
debug_mute_audio_button - > set_tooltip_text ( debug_mute_audio ? TTRC ( " Unmute game audio. " ) : TTRC ( " Mute game audio. " ) ) ;
main_menu_hbox - > add_child ( memnew ( VSeparator ) ) ;
2024-09-18 18:07:18 -03:00
camera_override_button = memnew ( Button ) ;
main_menu_hbox - > add_child ( camera_override_button ) ;
camera_override_button - > set_toggle_mode ( true ) ;
2024-12-05 17:13:20 +03:00
camera_override_button - > set_theme_type_variation ( SceneStringName ( FlatButton ) ) ;
2024-09-18 18:07:18 -03:00
camera_override_button - > set_tooltip_text ( TTR ( " Override the in-game camera. " ) ) ;
2024-11-18 15:44:38 -03:00
camera_override_button - > connect ( SceneStringName ( toggled ) , callable_mp ( this , & GameView : : _camera_override_button_toggled ) ) ;
2024-09-18 18:07:18 -03:00
camera_override_menu = memnew ( MenuButton ) ;
main_menu_hbox - > add_child ( camera_override_menu ) ;
camera_override_menu - > set_flat ( false ) ;
camera_override_menu - > set_theme_type_variation ( " FlatMenuButton " ) ;
camera_override_menu - > set_h_size_flags ( SIZE_SHRINK_END ) ;
camera_override_menu - > set_tooltip_text ( TTR ( " Camera Override Options " ) ) ;
2024-11-18 15:44:38 -03:00
_camera_override_menu_id_pressed ( EditorSettings : : get_singleton ( ) - > get_project_metadata ( " game_view " , " camera_override_mode " , 0 ) ) ;
2024-09-18 18:07:18 -03:00
PopupMenu * menu = camera_override_menu - > get_popup ( ) ;
menu - > connect ( SceneStringName ( id_pressed ) , callable_mp ( this , & GameView : : _camera_override_menu_id_pressed ) ) ;
menu - > add_item ( TTR ( " Reset 2D Camera " ) , CAMERA_RESET_2D ) ;
menu - > add_item ( TTR ( " Reset 3D Camera " ) , CAMERA_RESET_3D ) ;
menu - > add_separator ( ) ;
menu - > add_radio_check_item ( TTR ( " Manipulate In-Game " ) , CAMERA_MODE_INGAME ) ;
menu - > set_item_checked ( menu - > get_item_index ( CAMERA_MODE_INGAME ) , true ) ;
menu - > add_radio_check_item ( TTR ( " Manipulate From Editors " ) , CAMERA_MODE_EDITORS ) ;
2024-09-25 16:57:23 -04:00
embedding_separator = memnew ( VSeparator ) ;
main_menu_hbox - > add_child ( embedding_separator ) ;
2025-01-20 18:33:12 -05:00
fixed_size_button = memnew ( Button ) ;
main_menu_hbox - > add_child ( fixed_size_button ) ;
fixed_size_button - > set_toggle_mode ( true ) ;
fixed_size_button - > set_theme_type_variation ( " FlatButton " ) ;
fixed_size_button - > set_tooltip_text ( TTR ( " Embedded game size is based on project settings. \n The 'Keep Aspect' mode is used when the Game Workspace is smaller than the desired size. " ) ) ;
fixed_size_button - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & GameView : : _size_mode_button_pressed ) . bind ( SIZE_MODE_FIXED ) ) ;
2024-09-25 16:57:23 -04:00
keep_aspect_button = memnew ( Button ) ;
main_menu_hbox - > add_child ( keep_aspect_button ) ;
keep_aspect_button - > set_toggle_mode ( true ) ;
keep_aspect_button - > set_theme_type_variation ( " FlatButton " ) ;
keep_aspect_button - > set_tooltip_text ( TTR ( " Keep the aspect ratio of the embedded game. " ) ) ;
2025-01-20 18:33:12 -05:00
keep_aspect_button - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & GameView : : _size_mode_button_pressed ) . bind ( SIZE_MODE_KEEP_ASPECT ) ) ;
stretch_button = memnew ( Button ) ;
main_menu_hbox - > add_child ( stretch_button ) ;
stretch_button - > set_toggle_mode ( true ) ;
stretch_button - > set_theme_type_variation ( " FlatButton " ) ;
stretch_button - > set_tooltip_text ( TTR ( " Embedded game size stretches to fit the Game Workspace. " ) ) ;
stretch_button - > connect ( SceneStringName ( pressed ) , callable_mp ( this , & GameView : : _size_mode_button_pressed ) . bind ( SIZE_MODE_STRETCH ) ) ;
2024-09-25 16:57:23 -04:00
embed_options_menu = memnew ( MenuButton ) ;
main_menu_hbox - > add_child ( embed_options_menu ) ;
embed_options_menu - > set_flat ( false ) ;
embed_options_menu - > set_theme_type_variation ( " FlatMenuButton " ) ;
embed_options_menu - > set_h_size_flags ( SIZE_SHRINK_END ) ;
embed_options_menu - > set_tooltip_text ( TTR ( " Embedding Options " ) ) ;
menu = embed_options_menu - > get_popup ( ) ;
menu - > connect ( SceneStringName ( id_pressed ) , callable_mp ( this , & GameView : : _embed_options_menu_menu_id_pressed ) ) ;
menu - > add_check_item ( TTR ( " Embed Game on Next Play " ) , EMBED_RUN_GAME_EMBEDDED ) ;
menu - > add_check_item ( TTR ( " Make Game Workspace Floating on Next Play " ) , EMBED_MAKE_FLOATING_ON_PLAY ) ;
main_menu_hbox - > add_spacer ( ) ;
game_size_label = memnew ( Label ( ) ) ;
main_menu_hbox - > add_child ( game_size_label ) ;
game_size_label - > hide ( ) ;
2025-01-31 12:02:06 -05:00
// Setting the minimum size prevents the game workspace from resizing indefinitely
// due to the label size oscillating by a few pixels when the game is in stretch mode
// and the game workspace is at its minimum size.
game_size_label - > set_custom_minimum_size ( Size2 ( 80 * EDSCALE , 0 ) ) ;
game_size_label - > set_horizontal_alignment ( HorizontalAlignment : : HORIZONTAL_ALIGNMENT_RIGHT ) ;
2024-09-18 18:07:18 -03:00
panel = memnew ( Panel ) ;
add_child ( panel ) ;
panel - > set_theme_type_variation ( " GamePanel " ) ;
panel - > set_v_size_flags ( SIZE_EXPAND_FILL ) ;
2024-09-25 16:57:23 -04:00
embedded_process = memnew ( EmbeddedProcess ) ;
panel - > add_child ( embedded_process ) ;
embedded_process - > set_anchors_and_offsets_preset ( PRESET_FULL_RECT ) ;
embedded_process - > connect ( " embedding_failed " , callable_mp ( this , & GameView : : _embedding_failed ) ) ;
embedded_process - > connect ( " embedding_completed " , callable_mp ( this , & GameView : : _embedding_completed ) ) ;
embedded_process - > connect ( " embedded_process_updated " , callable_mp ( this , & GameView : : _embedded_process_updated ) ) ;
2024-12-29 18:16:19 -05:00
embedded_process - > connect ( " embedded_process_focused " , callable_mp ( this , & GameView : : _embedded_process_focused ) ) ;
2024-09-25 16:57:23 -04:00
embedded_process - > set_custom_minimum_size ( Size2i ( 100 , 100 ) ) ;
2025-01-20 18:33:12 -05:00
MarginContainer * state_container = memnew ( MarginContainer ) ;
state_container - > add_theme_constant_override ( " margin_left " , 8 * EDSCALE ) ;
state_container - > add_theme_constant_override ( " margin_right " , 8 * EDSCALE ) ;
state_container - > set_anchors_and_offsets_preset ( PRESET_FULL_RECT ) ;
panel - > add_child ( state_container ) ;
2024-09-25 16:57:23 -04:00
state_label = memnew ( Label ( ) ) ;
2025-01-20 18:33:12 -05:00
state_container - > add_child ( state_label ) ;
2024-09-25 16:57:23 -04:00
state_label - > set_horizontal_alignment ( HORIZONTAL_ALIGNMENT_CENTER ) ;
state_label - > set_vertical_alignment ( VERTICAL_ALIGNMENT_CENTER ) ;
state_label - > set_autowrap_mode ( TextServer : : AUTOWRAP_WORD ) ;
state_label - > set_anchors_and_offsets_preset ( PRESET_FULL_RECT ) ;
_update_debugger_buttons ( ) ;
2024-09-18 18:07:18 -03:00
p_debugger - > connect ( " session_started " , callable_mp ( this , & GameView : : _sessions_changed ) ) ;
p_debugger - > connect ( " session_stopped " , callable_mp ( this , & GameView : : _sessions_changed ) ) ;
2024-09-25 16:57:23 -04:00
2025-01-21 19:12:27 -05:00
p_wrapper - > set_override_close_request ( true ) ;
p_wrapper - > connect ( " window_close_requested " , callable_mp ( this , & GameView : : _window_close_request ) ) ;
2024-09-25 16:57:23 -04:00
p_wrapper - > connect ( " window_size_changed " , callable_mp ( this , & GameView : : _update_floating_window_settings ) ) ;
2025-01-24 20:44:47 -05:00
EditorDebuggerNode : : get_singleton ( ) - > connect ( " breaked " , callable_mp ( this , & GameView : : _debugger_breaked ) ) ;
2024-09-18 18:07:18 -03:00
}
///////
2025-01-07 21:31:53 -08:00
void GameViewPlugin : : selected_notify ( ) {
if ( _is_window_wrapper_enabled ( ) ) {
# ifdef ANDROID_ENABLED
notify_main_screen_changed ( get_plugin_name ( ) ) ;
# else
window_wrapper - > grab_window_focus ( ) ;
2024-11-18 15:44:38 -03:00
# endif // ANDROID_ENABLED
2025-01-07 21:31:53 -08:00
_focus_another_editor ( ) ;
}
}
# ifndef ANDROID_ENABLED
2024-09-18 18:07:18 -03:00
void GameViewPlugin : : make_visible ( bool p_visible ) {
2024-09-25 16:57:23 -04:00
if ( p_visible ) {
window_wrapper - > show ( ) ;
} else {
window_wrapper - > hide ( ) ;
}
}
void GameViewPlugin : : set_window_layout ( Ref < ConfigFile > p_layout ) {
game_view - > set_window_layout ( p_layout ) ;
}
void GameViewPlugin : : get_window_layout ( Ref < ConfigFile > p_layout ) {
game_view - > get_window_layout ( p_layout ) ;
2024-09-18 18:07:18 -03:00
}
2024-11-18 15:44:38 -03:00
# endif // ANDROID_ENABLED
2025-01-07 21:31:53 -08:00
2024-09-18 18:07:18 -03:00
void GameViewPlugin : : _notification ( int p_what ) {
switch ( p_what ) {
case NOTIFICATION_ENTER_TREE : {
add_debugger_plugin ( debugger ) ;
2024-09-25 16:57:23 -04:00
connect ( " main_screen_changed " , callable_mp ( this , & GameViewPlugin : : _save_last_editor ) ) ;
2024-09-18 18:07:18 -03:00
} break ;
case NOTIFICATION_EXIT_TREE : {
remove_debugger_plugin ( debugger ) ;
2024-09-25 16:57:23 -04:00
disconnect ( " main_screen_changed " , callable_mp ( this , & GameViewPlugin : : _save_last_editor ) ) ;
2024-09-18 18:07:18 -03:00
} break ;
}
}
2025-01-10 14:53:41 -05:00
void GameViewPlugin : : _feature_profile_changed ( ) {
bool is_feature_enabled = true ;
Ref < EditorFeatureProfile > profile = EditorFeatureProfileManager : : get_singleton ( ) - > get_current_profile ( ) ;
if ( profile . is_valid ( ) ) {
is_feature_enabled = ! profile - > is_feature_disabled ( EditorFeatureProfile : : FEATURE_GAME ) ;
}
if ( debugger . is_valid ( ) ) {
debugger - > set_is_feature_enabled ( is_feature_enabled ) ;
}
2025-01-07 21:31:53 -08:00
# ifndef ANDROID_ENABLED
2025-01-10 14:53:41 -05:00
if ( game_view ) {
game_view - > set_is_feature_enabled ( is_feature_enabled ) ;
}
2024-11-18 15:44:38 -03:00
# endif // ANDROID_ENABLED
2024-09-25 16:57:23 -04:00
}
void GameViewPlugin : : _save_last_editor ( const String & p_editor ) {
2025-01-24 20:44:47 -05:00
if ( p_editor ! = get_plugin_name ( ) ) {
2024-09-25 16:57:23 -04:00
last_editor = p_editor ;
}
}
void GameViewPlugin : : _focus_another_editor ( ) {
2025-01-07 21:31:53 -08:00
if ( _is_window_wrapper_enabled ( ) ) {
2025-01-24 20:44:47 -05:00
if ( last_editor . is_empty ( ) ) {
EditorNode : : get_singleton ( ) - > get_editor_main_screen ( ) - > select ( EditorMainScreen : : EDITOR_2D ) ;
} else {
EditorInterface : : get_singleton ( ) - > set_main_screen_editor ( last_editor ) ;
}
2024-09-25 16:57:23 -04:00
}
}
2025-01-07 21:31:53 -08:00
bool GameViewPlugin : : _is_window_wrapper_enabled ( ) const {
# ifdef ANDROID_ENABLED
return true ;
# else
return window_wrapper - > get_window_enabled ( ) ;
2024-11-18 15:44:38 -03:00
# endif // ANDROID_ENABLED
2025-01-07 21:31:53 -08:00
}
2024-09-18 18:07:18 -03:00
GameViewPlugin : : GameViewPlugin ( ) {
2025-01-07 21:31:53 -08:00
debugger . instantiate ( ) ;
# ifndef ANDROID_ENABLED
2024-09-25 16:57:23 -04:00
window_wrapper = memnew ( WindowWrapper ) ;
window_wrapper - > set_window_title ( vformat ( TTR ( " %s - Godot Engine " ) , TTR ( " Game Workspace " ) ) ) ;
window_wrapper - > set_margins_enabled ( true ) ;
game_view = memnew ( GameView ( debugger , window_wrapper ) ) ;
2024-09-18 18:07:18 -03:00
game_view - > set_v_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
2024-09-25 16:57:23 -04:00
window_wrapper - > set_wrapped_control ( game_view , nullptr ) ;
EditorNode : : get_singleton ( ) - > get_editor_main_screen ( ) - > get_control ( ) - > add_child ( window_wrapper ) ;
window_wrapper - > set_v_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
window_wrapper - > hide ( ) ;
2024-11-18 15:44:38 -03:00
window_wrapper - > connect ( " window_visibility_changed " , callable_mp ( this , & GameViewPlugin : : _focus_another_editor ) . unbind ( 1 ) ) ;
# endif // ANDROID_ENABLED
2025-01-10 14:53:41 -05:00
EditorFeatureProfileManager : : get_singleton ( ) - > connect ( " current_feature_profile_changed " , callable_mp ( this , & GameViewPlugin : : _feature_profile_changed ) ) ;
2024-09-18 18:07:18 -03:00
}