From bb83381cadfedc33558c7c38e1303aaca7a597e7 Mon Sep 17 00:00:00 2001 From: Jayden Sipe Date: Thu, 16 Apr 2026 23:58:42 -0400 Subject: [PATCH] Clean and simplify game embed toolbar --- editor/docks/scene_tree_dock.cpp | 2 +- editor/icons/EmbedDisabled.svg | 1 + editor/icons/EmbedFloating.svg | 1 + editor/icons/EmbedFused.svg | 1 + editor/icons/Suspend.svg | 2 +- editor/run/game_view_plugin.cpp | 259 +++++++++++++++++++------------ editor/run/game_view_plugin.h | 27 ++-- editor/themes/theme_classic.cpp | 24 ++- editor/themes/theme_modern.cpp | 24 ++- 9 files changed, 223 insertions(+), 118 deletions(-) create mode 100644 editor/icons/EmbedDisabled.svg create mode 100644 editor/icons/EmbedFloating.svg create mode 100644 editor/icons/EmbedFused.svg diff --git a/editor/docks/scene_tree_dock.cpp b/editor/docks/scene_tree_dock.cpp index 438924f14318..a786e46851ac 100644 --- a/editor/docks/scene_tree_dock.cpp +++ b/editor/docks/scene_tree_dock.cpp @@ -5072,7 +5072,7 @@ SceneTreeDock::SceneTreeDock(Node *p_scene_root, EditorSelection *p_editor_selec tree_menu->connect(SceneStringName(id_pressed), callable_mp(this, &SceneTreeDock::_tool_selected).bind(false)); button_panel = memnew(PanelContainer); - button_panel->set_theme_type_variation("PanelContainerTabbarInner"); + button_panel->set_theme_type_variation("PanelContainerButtonGroup"); main_vbox->add_child(button_panel); HBoxContainer *button_hb = memnew(HBoxContainer); diff --git a/editor/icons/EmbedDisabled.svg b/editor/icons/EmbedDisabled.svg new file mode 100644 index 000000000000..c94b44e2a395 --- /dev/null +++ b/editor/icons/EmbedDisabled.svg @@ -0,0 +1 @@ + diff --git a/editor/icons/EmbedFloating.svg b/editor/icons/EmbedFloating.svg new file mode 100644 index 000000000000..9e2d8b75bf24 --- /dev/null +++ b/editor/icons/EmbedFloating.svg @@ -0,0 +1 @@ + diff --git a/editor/icons/EmbedFused.svg b/editor/icons/EmbedFused.svg new file mode 100644 index 000000000000..ade21a8f9641 --- /dev/null +++ b/editor/icons/EmbedFused.svg @@ -0,0 +1 @@ + diff --git a/editor/icons/Suspend.svg b/editor/icons/Suspend.svg index daba9c27d22a..6dedba642771 100644 --- a/editor/icons/Suspend.svg +++ b/editor/icons/Suspend.svg @@ -1 +1 @@ - + diff --git a/editor/run/game_view_plugin.cpp b/editor/run/game_view_plugin.cpp index 9c137bdfd271..e6ce82e57733 100644 --- a/editor/run/game_view_plugin.cpp +++ b/editor/run/game_view_plugin.cpp @@ -49,6 +49,7 @@ #include "editor/settings/editor_feature_profile.h" #include "editor/settings/editor_settings.h" #include "editor/themes/editor_scale.h" +#include "scene/gui/box_container.h" #include "scene/gui/button.h" #include "scene/gui/label.h" #include "scene/gui/menu_button.h" @@ -429,7 +430,7 @@ bool GameViewDebugger::has_capture(const String &p_capture) const { GameViewDebugger::GameViewDebugger() { EditorFeatureProfileManager::get_singleton()->connect("current_feature_profile_changed", callable_mp(this, &GameViewDebugger::_feature_profile_changed)); - ED_SHORTCUT("editor/suspend_resume_embedded_project", TTRC("Suspend/Resume Embedded Project"), Key::F9); + ED_SHORTCUT("editor/suspend_resume_embedded_project", TTRC("Suspend/Resume Embedded Game"), Key::F9); ED_SHORTCUT_OVERRIDE("editor/suspend_resume_embedded_project", "macos", KeyModifierMask::META | KeyModifierMask::SHIFT | Key::B); ED_SHORTCUT("editor/next_frame_embedded_project", TTRC("Next Frame"), Key::F10); @@ -648,10 +649,6 @@ void GameView::_update_debugger_buttons() { camera_override_button->set_disabled(empty); speed_state_button->set_disabled(empty); game_size_label->set_visible(!empty); - game_size_placeholder->set_visible(empty); - bool disabled = time_scale_index == DEFAULT_TIME_SCALE_INDEX; - - reset_speed_button->set_disabled(empty || disabled); PopupMenu *menu = camera_override_menu->get_popup(); @@ -752,6 +749,11 @@ void GameView::_select_mode_pressed(int p_option) { void GameView::_selection_options_menu_id_pressed(int p_id) { switch (p_id) { + case SELECTION_HIDE: { + selection_hide = !selection_hide; + debugger->set_selection_visible(selection_hide); + EditorSettings::get_singleton()->set_project_metadata("game_view", "hide_selection", selection_hide); + } break; case SELECTION_AVOID_LOCKED: { selection_avoid_locked = !selection_avoid_locked; debugger->set_selection_avoid_locked(selection_avoid_locked); @@ -765,26 +767,40 @@ void GameView::_selection_options_menu_id_pressed(int p_id) { } PopupMenu *menu = selection_options_menu->get_popup(); + menu->set_item_checked(menu->get_item_index(SELECTION_HIDE), selection_hide); menu->set_item_checked(menu->get_item_index(SELECTION_AVOID_LOCKED), selection_avoid_locked); menu->set_item_checked(menu->get_item_index(SELECTION_PREFER_GROUP), selection_prefer_group); } -void GameView::_game_window_options_menu_menu_id_pressed(int p_id) { - switch (p_id) { - case WINDOW_RUN_GAME_EMBEDDED: { - embed_on_play = !embed_on_play; - 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); - } +void GameView::_game_embed_mode_pressed(int p_option) { + switch ((EmbedMode)p_option) { + case EMBED_TYPE_DISABLED: { + embed_on_play = false; + make_floating_on_play = false; + game_embed_mode_menu->set_button_icon(get_editor_theme_icon(SNAME("EmbedDisabled"))); } break; - case WINDOW_MAKE_FLOATING_ON_PLAY: { - make_floating_on_play = !make_floating_on_play; - 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); - } + case EMBED_TYPE_FLOATING: { + embed_on_play = true; + make_floating_on_play = true; + game_embed_mode_menu->set_button_icon(get_editor_theme_icon(SNAME("EmbedFloating"))); } break; + case EMBED_TYPE_EDITOR: { + embed_on_play = true; + make_floating_on_play = false; + game_embed_mode_menu->set_button_icon(get_editor_theme_icon(SNAME("EmbedFused"))); + } break; + case EMBED_TYPE_MAX: + break; + } + EditorSettings::get_singleton()->set_project_metadata("game_view", "embed_on_play", embed_on_play); + EditorSettings::get_singleton()->set_project_metadata("game_view", "make_floating_on_play", make_floating_on_play); + + _update_embed_buttons(); + _update_ui(); +} + +void GameView::_game_window_options_menu_menu_id_pressed(int p_id) { + switch (p_id) { case WINDOW_SIZE_MODE_FIXED: case WINDOW_SIZE_MODE_KEEP_ASPECT: case WINDOW_SIZE_MODE_STRETCH: { @@ -815,6 +831,7 @@ void GameView::_reset_time_scales() { time_scale_index = DEFAULT_TIME_SCALE_INDEX; debugger->reset_time_scale(); if (is_inside_tree()) { + _update_speed_state_icon(DEFAULT_TIME_SCALE_INDEX); _update_speed_buttons(); } } @@ -822,12 +839,35 @@ void GameView::_reset_time_scales() { void GameView::_speed_state_menu_pressed(int p_id) { time_scale_index = p_id; debugger->set_time_scale(time_scale_range[time_scale_index]); + _update_speed_state_icon(p_id); _update_speed_buttons(); } +void GameView::_update_speed_state_icon(int p_id) { + PopupMenu *menu = speed_state_button->get_popup(); + for (int i = 0; i < speed_state_button->get_item_count(); i++) { + if (i == DEFAULT_TIME_SCALE_INDEX) { + continue; + } + + menu->set_item_icon(i, nullptr); + } + + menu->set_item_icon(p_id, get_editor_theme_icon(SNAME("KeyValue"))); + if (p_id == DEFAULT_TIME_SCALE_INDEX) { + menu->set_item_icon_modulate(p_id, get_theme_color(SNAME("mono_color"), EditorStringName(Editor))); + } else { + menu->set_item_icon(DEFAULT_TIME_SCALE_INDEX, get_editor_theme_icon(SNAME("KeyBezierHandle"))); + + if (p_id > DEFAULT_TIME_SCALE_INDEX) { + menu->set_item_icon_modulate(p_id, get_theme_color(SNAME("success_color"), EditorStringName(Editor))); + } else { + menu->set_item_icon_modulate(p_id, get_theme_color(SNAME("warning_color"), EditorStringName(Editor))); + } + } +} + void GameView::_update_speed_buttons() { - bool disabled = time_scale_index == DEFAULT_TIME_SCALE_INDEX; - reset_speed_button->set_disabled(disabled); speed_state_button->set_text(vformat(U"%s×", time_scale_label[time_scale_index])); _update_speed_state_color(); } @@ -842,6 +882,9 @@ void GameView::_update_speed_state_color() { text_color = get_theme_color(SNAME("warning_color"), EditorStringName(Editor)); } speed_state_button->add_theme_color_override(SceneStringName(font_color), text_color); + speed_state_button->add_theme_color_override(SNAME("font_hover_color"), text_color); + speed_state_button->add_theme_color_override(SNAME("font_hover_pressed_color"), text_color); + speed_state_button->add_theme_color_override(SNAME("font_pressed_color"), text_color); } void GameView::_update_speed_state_size() { @@ -909,9 +952,13 @@ void GameView::_update_ui() { } else if (EditorRunBar::get_singleton()->is_playing()) { state_label->set_text(TTRC("Game running not embedded.")); } else if (embed_on_play) { - state_label->set_text(TTRC("Press play to start the game.")); + if (make_floating_on_play) { + state_label->set_text(TTRC("The game will run in a floating window with an attached toolbar.")); + } else { + state_label->set_text(TTRC("The game will run here within the Game workspace.")); + } } else { - state_label->set_text(TTRC("Embedding is disabled.")); + state_label->set_text(TTRC("The game will run in a floating window without the toolbar.")); } break; case EMBED_NOT_AVAILABLE_FEATURE_NOT_SUPPORTED: @@ -930,7 +977,7 @@ void GameView::_update_ui() { 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.")); break; case EMBED_NOT_AVAILABLE_SINGLE_WINDOW_MODE: - state_label->set_text(TTRC("Game embedding not available in single window mode.")); + state_label->set_text(TTRC("Game embedding not available in single-window mode.")); break; case EMBED_NOT_AVAILABLE_HEADLESS: state_label->set_text(TTRC("Game embedding not available when the game starts in headless mode.")); @@ -947,16 +994,17 @@ void GameView::_update_ui() { } void GameView::_update_embed_menu_options() { - bool is_multi_window = window_wrapper->is_window_available(); PopupMenu *menu = game_window_options_menu->get_popup(); - menu->set_item_checked(menu->get_item_index(WINDOW_RUN_GAME_EMBEDDED), embed_on_play); - menu->set_item_checked(menu->get_item_index(WINDOW_MAKE_FLOATING_ON_PLAY), make_floating_on_play && is_multi_window); - menu->set_item_checked(menu->get_item_index(WINDOW_SIZE_MODE_FIXED), embed_size_mode == SIZE_MODE_FIXED); menu->set_item_checked(menu->get_item_index(WINDOW_SIZE_MODE_KEEP_ASPECT), embed_size_mode == SIZE_MODE_KEEP_ASPECT); menu->set_item_checked(menu->get_item_index(WINDOW_SIZE_MODE_STRETCH), embed_size_mode == SIZE_MODE_STRETCH); +} - menu->set_item_disabled(menu->get_item_index(WINDOW_MAKE_FLOATING_ON_PLAY), !embed_on_play || !is_multi_window); +void GameView::_update_embed_buttons() { + PopupMenu *menu = game_embed_mode_menu->get_popup(); + menu->set_item_checked(menu->get_item_index(EMBED_TYPE_EDITOR), embed_on_play && !make_floating_on_play); + menu->set_item_checked(menu->get_item_index(EMBED_TYPE_FLOATING), make_floating_on_play && window_wrapper->is_window_available()); + menu->set_item_checked(menu->get_item_index(EMBED_TYPE_DISABLED), !embed_on_play && !make_floating_on_play); } void GameView::_update_game_window_size_label() { @@ -997,14 +1045,6 @@ void GameView::_update_embed_window_size() { } } -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); - - EditorSettings::get_singleton()->set_project_metadata("game_view", "hide_selection", p_pressed); -} - 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"))); @@ -1132,7 +1172,6 @@ void GameView::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: { suspend_button->set_button_icon(get_editor_theme_icon(SNAME("Suspend"))); next_frame_button->set_button_icon(get_editor_theme_icon(SNAME("NextFrame"))); - reset_speed_button->set_button_icon(get_editor_theme_icon(SNAME("Reload"))); 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"))); @@ -1141,16 +1180,22 @@ void GameView::_notification(int p_what) { 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"))); - selection_options_menu->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); + selection_options_menu->set_button_icon(get_editor_theme_icon(SNAME("GuiDropdown"))); debug_mute_audio_button->set_button_icon(get_editor_theme_icon(debug_mute_audio ? SNAME("AudioMute") : SNAME("AudioStreamPlayer"))); camera_override_button->set_button_icon(get_editor_theme_icon(SNAME("Camera"))); - camera_override_menu->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); + camera_override_menu->set_button_icon(get_editor_theme_icon(SNAME("GuiDropdown"))); - game_window_options_menu->set_button_icon(get_editor_theme_icon(SNAME("GuiTabMenuHl"))); + game_embed_mode_menu->set_button_icon(get_editor_theme_icon(SNAME("EmbedFused"))); + PopupMenu *game_embed_menu = game_embed_mode_menu->get_popup(); + game_embed_menu->set_item_icon(game_embed_menu->get_item_index(EMBED_TYPE_EDITOR), get_editor_theme_icon(SNAME("EmbedFused"))); + game_embed_menu->set_item_icon(game_embed_menu->get_item_index(EMBED_TYPE_FLOATING), get_editor_theme_icon(SNAME("EmbedFloating"))); + game_embed_menu->set_item_icon(game_embed_menu->get_item_index(EMBED_TYPE_DISABLED), get_editor_theme_icon(SNAME("EmbedDisabled"))); + game_window_options_menu->set_button_icon(get_editor_theme_icon(SNAME("GuiDropdown"))); + + _update_speed_state_icon(time_scale_index); _update_speed_state_size(); _update_speed_state_color(); } break; @@ -1163,14 +1208,17 @@ void GameView::_notification(int p_what) { case -1: { // Disabled. embed_on_play = false; make_floating_on_play = false; + game_embed_mode_menu->set_button_icon(get_editor_theme_icon(SNAME("EmbedDisabled"))); } break; case 1: { // Embed. embed_on_play = true; make_floating_on_play = false; + game_embed_mode_menu->set_button_icon(get_editor_theme_icon(SNAME("EmbedFused"))); } break; case 2: { // Floating. embed_on_play = true; make_floating_on_play = true; + game_embed_mode_menu->set_button_icon(get_editor_theme_icon(SNAME("EmbedFloating"))); } break; default: { embed_on_play = EditorSettings::get_singleton()->get_project_metadata("game_view", "embed_on_play", true); @@ -1178,6 +1226,7 @@ void GameView::_notification(int p_what) { } break; } embed_size_mode = (EmbedSizeMode)(int)EditorSettings::get_singleton()->get_project_metadata("game_view", "embed_size_mode", SIZE_MODE_FIXED); + _update_embed_buttons(); _update_embed_menu_options(); EditorRunBar::get_singleton()->connect("play_pressed", callable_mp(this, &GameView::_play_pressed)); @@ -1440,6 +1489,9 @@ GameView::GameView(Ref p_debugger, EmbeddedProcessBase *p_embe MarginContainer *toolbar_margin = memnew(MarginContainer); toolbar_margin->set_theme_type_variation("MainToolBarMargin"); + toolbar_margin->add_theme_constant_override("margin_top", 1 * EDSCALE); + toolbar_margin->add_theme_constant_override("margin_bottom", 1 * EDSCALE); + toolbar_margin->set_custom_maximum_size(Size2(-1, 36 * EDSCALE)); add_child(toolbar_margin); // FIXME: Turn this back into a FlowContainer once GH-115523 is fixed. @@ -1447,6 +1499,7 @@ GameView::GameView(Ref p_debugger, EmbeddedProcessBase *p_embe toolbar_margin->add_child(main_menu_fc); HBoxContainer *process_hb = memnew(HBoxContainer); + main_menu_fc->add_child(process_hb); suspend_button = memnew(Button); process_hb->add_child(suspend_button); @@ -1455,7 +1508,7 @@ GameView::GameView(Ref p_debugger, EmbeddedProcessBase *p_embe suspend_button->connect(SceneStringName(toggled), callable_mp(this, &GameView::_suspend_button_toggled)); suspend_button->set_accessibility_name(TTRC("Suspend")); suspend_button->set_shortcut(ED_GET_SHORTCUT("editor/suspend_resume_embedded_project")); - suspend_button->set_tooltip_text(TTRC("Force pause at SceneTree level. Stops all processing, but you can still interact with the project.")); + suspend_button->set_tooltip_text(TTRC("Force pause at SceneTree level. Stops all processing, but you can still interact with the game.")); next_frame_button = memnew(Button); process_hb->add_child(next_frame_button); @@ -1464,6 +1517,8 @@ GameView::GameView(Ref p_debugger, EmbeddedProcessBase *p_embe next_frame_button->set_accessibility_name(TTRC("Next Frame")); next_frame_button->set_shortcut(ED_GET_SHORTCUT("editor/next_frame_embedded_project")); + process_hb->add_child(memnew(VSeparator)); + speed_state_button = memnew(MenuButton); process_hb->add_child(speed_state_button); speed_state_button->set_text(U"1.0×"); @@ -1478,17 +1533,14 @@ GameView::GameView(Ref p_debugger, EmbeddedProcessBase *p_embe menu->add_item(vformat(U"%s×", lbl)); } - reset_speed_button = memnew(Button); - process_hb->add_child(reset_speed_button); - reset_speed_button->set_theme_type_variation(SceneStringName(FlatButton)); - reset_speed_button->set_tooltip_text(TTRC("Reset the game speed.")); - reset_speed_button->set_accessibility_name(TTRC("Reset Speed")); - reset_speed_button->connect(SceneStringName(pressed), callable_mp(this, &GameView::_reset_time_scales)); - process_hb->add_child(memnew(VSeparator)); + PanelContainer *input_panel = memnew(PanelContainer); + input_panel->set_theme_type_variation("PanelContainerButtonGroup"); + main_menu_fc->add_child(input_panel); + HBoxContainer *input_hb = memnew(HBoxContainer); - main_menu_fc->add_child(input_hb); + input_panel->add_child(input_hb); node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE] = memnew(Button); input_hb->add_child(node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE]); @@ -1497,7 +1549,7 @@ GameView::GameView(Ref p_debugger, EmbeddedProcessBase *p_embe node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE]->set_pressed(true); node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE]->set_theme_type_variation("FlatButtonNoIconTint"); 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(TTRC("Allow game input.")); + node_type_button[RuntimeNodeSelect::NODE_TYPE_NONE]->set_tooltip_text(TTRC("Allow game input as usual.")); node_type_button[RuntimeNodeSelect::NODE_TYPE_2D] = memnew(Button); input_hb->add_child(node_type_button[RuntimeNodeSelect::NODE_TYPE_2D]); @@ -1505,7 +1557,7 @@ GameView::GameView(Ref p_debugger, EmbeddedProcessBase *p_embe node_type_button[RuntimeNodeSelect::NODE_TYPE_2D]->set_toggle_mode(true); node_type_button[RuntimeNodeSelect::NODE_TYPE_2D]->set_theme_type_variation("FlatButtonNoIconTint"); 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(TTRC("Disable game input and allow to select Node2Ds, Controls, and manipulate the 2D camera.")); + node_type_button[RuntimeNodeSelect::NODE_TYPE_2D]->set_tooltip_text(TTRC("Disable game input to allow selecting Node2Ds and Controls, as well as manipulate the 2D camera.")); node_type_button[RuntimeNodeSelect::NODE_TYPE_3D] = memnew(Button); input_hb->add_child(node_type_button[RuntimeNodeSelect::NODE_TYPE_3D]); @@ -1513,12 +1565,16 @@ GameView::GameView(Ref p_debugger, EmbeddedProcessBase *p_embe node_type_button[RuntimeNodeSelect::NODE_TYPE_3D]->set_toggle_mode(true); node_type_button[RuntimeNodeSelect::NODE_TYPE_3D]->set_theme_type_variation("FlatButtonNoIconTint"); 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(TTRC("Disable game input and allow to select Node3Ds and manipulate the 3D camera.")); + node_type_button[RuntimeNodeSelect::NODE_TYPE_3D]->set_tooltip_text(TTRC("Disable game input to allow selecting Node3Ds and manipulating the 3D camera.")); - input_hb->add_child(memnew(VSeparator)); + main_menu_fc->add_child(memnew(VSeparator)); + + PanelContainer *selection_panel = memnew(PanelContainer); + selection_panel->set_theme_type_variation("PanelContainerButtonGroup"); + main_menu_fc->add_child(selection_panel); HBoxContainer *selection_hb = memnew(HBoxContainer); - main_menu_fc->add_child(selection_hb); + selection_panel->add_child(selection_hb); select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE] = memnew(Button); selection_hb->add_child(select_mode_button[RuntimeNodeSelect::SELECT_MODE_SINGLE]); @@ -1538,52 +1594,38 @@ GameView::GameView(Ref p_debugger, EmbeddedProcessBase *p_embe _select_mode_pressed(EditorSettings::get_singleton()->get_project_metadata("game_view", "select_mode", 0)); - hide_selection = memnew(Button); - selection_hb->add_child(hide_selection); - hide_selection->set_toggle_mode(true); - hide_selection->set_theme_type_variation(SceneStringName(FlatButton)); - hide_selection->set_tooltip_text(TTRC("Toggle Selection Visibility")); - hide_selection->set_pressed(EditorSettings::get_singleton()->get_project_metadata("game_view", "hide_selection", false)); - if (hide_selection->is_pressed()) { - debugger->set_selection_visible(false); - } - hide_selection->connect(SceneStringName(toggled), callable_mp(this, &GameView::_hide_selection_toggled)); - selection_options_menu = memnew(MenuButton); selection_hb->add_child(selection_options_menu); selection_options_menu->set_flat(false); - selection_options_menu->set_theme_type_variation("FlatMenuButton"); + selection_options_menu->set_theme_type_variation("FlatMenuButtonNoIconTint"); selection_options_menu->set_h_size_flags(SIZE_SHRINK_END); - selection_options_menu->set_tooltip_text(TTRC("Selection Options")); + selection_options_menu->set_tooltip_text(TTRC("Selection options.")); PopupMenu *selection_menu = selection_options_menu->get_popup(); selection_menu->connect(SceneStringName(id_pressed), callable_mp(this, &GameView::_selection_options_menu_id_pressed)); + selection_menu->add_check_item(TTRC("Show Selection Visibility"), SELECTION_HIDE); selection_menu->add_check_item(TTRC("Don't Select Locked Nodes"), SELECTION_AVOID_LOCKED); selection_menu->add_check_item(TTRC("Select Group Over Children"), SELECTION_PREFER_GROUP); + selection_hide = EditorSettings::get_singleton()->get_project_metadata("game_view", "hide_selection", true); selection_avoid_locked = EditorSettings::get_singleton()->get_project_metadata("game_view", "selection_avoid_locked", false); selection_prefer_group = EditorSettings::get_singleton()->get_project_metadata("game_view", "selection_prefer_group", false); + selection_menu->set_item_checked(selection_menu->get_item_index(SELECTION_HIDE), selection_hide); selection_menu->set_item_checked(selection_menu->get_item_index(SELECTION_AVOID_LOCKED), selection_avoid_locked); selection_menu->set_item_checked(selection_menu->get_item_index(SELECTION_PREFER_GROUP), selection_prefer_group); + debugger->set_selection_visible(selection_hide); debugger->set_selection_avoid_locked(selection_avoid_locked); debugger->set_selection_prefer_group(selection_prefer_group); - selection_hb->add_child(memnew(VSeparator)); + main_menu_fc->add_child(memnew(VSeparator)); - HBoxContainer *audio_hb = memnew(HBoxContainer); - main_menu_fc->add_child(audio_hb); - - debug_mute_audio_button = memnew(Button); - audio_hb->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.")); - - audio_hb->add_child(memnew(VSeparator)); + PanelContainer *camera_panel = memnew(PanelContainer); + camera_panel->set_theme_type_variation("PanelContainerButtonGroup"); + main_menu_fc->add_child(camera_panel); HBoxContainer *camera_hb = memnew(HBoxContainer); - main_menu_fc->add_child(camera_hb); + camera_panel->add_child(camera_hb); camera_override_button = memnew(Button); camera_hb->add_child(camera_override_button); @@ -1595,9 +1637,9 @@ GameView::GameView(Ref p_debugger, EmbeddedProcessBase *p_embe camera_override_menu = memnew(MenuButton); camera_hb->add_child(camera_override_menu); camera_override_menu->set_flat(false); - camera_override_menu->set_theme_type_variation("FlatMenuButton"); + camera_override_menu->set_theme_type_variation("FlatMenuButtonNoIconTint"); camera_override_menu->set_h_size_flags(SIZE_SHRINK_END); - camera_override_menu->set_tooltip_text(TTRC("Camera Override Options")); + camera_override_menu->set_tooltip_text(TTRC("Camera override options.")); menu = camera_override_menu->get_popup(); menu->connect(SceneStringName(id_pressed), callable_mp(this, &GameView::_camera_override_menu_id_pressed)); @@ -1607,14 +1649,32 @@ GameView::GameView(Ref p_debugger, EmbeddedProcessBase *p_embe menu->add_radio_check_item(TTRC("Manipulate In-Game"), CAMERA_MODE_INGAME); menu->set_item_checked(menu->get_item_index(CAMERA_MODE_INGAME), true); menu->add_radio_check_item(TTRC("Manipulate From Editors"), CAMERA_MODE_EDITORS); - camera_hb->add_child(memnew(VSeparator)); + + main_menu_fc->add_child(memnew(VSeparator)); + + HBoxContainer *audio_hb = memnew(HBoxContainer); + main_menu_fc->add_child(audio_hb); + + debug_mute_audio_button = memnew(Button); + audio_hb->add_child(debug_mute_audio_button); + debug_mute_audio_button->set_theme_type_variation(SceneStringName(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.")); embedding_hb = memnew(HBoxContainer); embedding_hb->set_h_size_flags(Control::SIZE_EXPAND_FILL); + embedding_hb->set_alignment(ALIGNMENT_END); main_menu_fc->add_child(embedding_hb); + PanelContainer *embed_panel = memnew(PanelContainer); + embed_panel->set_theme_type_variation("PanelContainerButtonGroup"); + game_size_label = memnew(Label()); + game_hb = memnew(HBoxContainer); + embedding_hb->add_child(game_size_label); + embedding_hb->add_child(embed_panel); + embed_panel->add_child(game_hb); game_size_label->hide(); // 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 @@ -1624,29 +1684,38 @@ GameView::GameView(Ref p_debugger, EmbeddedProcessBase *p_embe game_size_label->set_horizontal_alignment(HorizontalAlignment::HORIZONTAL_ALIGNMENT_RIGHT); game_size_label->set_mouse_filter(MouseFilter::MOUSE_FILTER_PASS); - game_size_placeholder = memnew(Control()); - embedding_hb->add_child(game_size_placeholder); - game_size_placeholder->set_h_size_flags(game_size_label->get_h_size_flags()); + game_embed_mode_menu = memnew(MenuButton); + game_hb->add_child(game_embed_mode_menu); + game_embed_mode_menu->set_flat(false); + game_embed_mode_menu->set_theme_type_variation("FlatMenuButtonNoIconTint"); + game_embed_mode_menu->set_h_size_flags(SIZE_SHRINK_END); + game_embed_mode_menu->set_tooltip_text(TTRC("Game embed modes.")); + + PopupMenu *game_embed_menu = game_embed_mode_menu->get_popup(); + game_embed_menu->connect(SceneStringName(id_pressed), callable_mp(this, &GameView::_game_embed_mode_pressed)); + game_embed_menu->add_radio_check_item(TTRC("Embed in editor"), EMBED_TYPE_EDITOR); + game_embed_menu->add_radio_check_item(TTRC("Float window with toolbar"), EMBED_TYPE_FLOATING); + game_embed_menu->add_radio_check_item(TTRC("Float window without toolbar"), EMBED_TYPE_DISABLED); + game_embed_menu->set_item_checked(game_embed_menu->get_item_index(EMBED_TYPE_EDITOR), embed_on_play && !make_floating_on_play); + game_embed_menu->set_item_checked(game_embed_menu->get_item_index(EMBED_TYPE_FLOATING), make_floating_on_play && window_wrapper->is_window_available()); + game_embed_menu->set_item_checked(game_embed_menu->get_item_index(EMBED_TYPE_DISABLED), !embed_on_play && !make_floating_on_play); game_window_options_menu = memnew(MenuButton); - embedding_hb->add_child(game_window_options_menu); + game_hb->add_child(game_window_options_menu); game_window_options_menu->set_flat(false); - game_window_options_menu->set_theme_type_variation("FlatMenuButton"); + game_window_options_menu->set_theme_type_variation("FlatMenuButtonNoIconTint"); game_window_options_menu->set_h_size_flags(SIZE_SHRINK_END); - game_window_options_menu->set_tooltip_text(TTRC("Game Window Options")); + game_window_options_menu->set_tooltip_text(TTRC("Game window options.")); menu = game_window_options_menu->get_popup(); menu->connect(SceneStringName(id_pressed), callable_mp(this, &GameView::_game_window_options_menu_menu_id_pressed)); - menu->add_check_item(TTRC("Embed Game on Next Play"), WINDOW_RUN_GAME_EMBEDDED); - menu->add_check_item(TTRC("Make Game Workspace Floating on Next Play"), WINDOW_MAKE_FLOATING_ON_PLAY); - menu->add_separator(TTRC("Embedded Window Sizing")); menu->add_radio_check_item(TTRC("Fixed Size"), WINDOW_SIZE_MODE_FIXED); - menu->set_item_tooltip(menu->get_item_index(WINDOW_SIZE_MODE_FIXED), TTRC("Embedded game size is based on project settings.\nThe 'Keep Aspect' mode is used when the Game Workspace is smaller than the desired size.")); + menu->set_item_tooltip(menu->get_item_index(WINDOW_SIZE_MODE_FIXED), TTRC("Embedded game size is based on project settings.\nThe 'Keep Aspect' mode is used when the Game workspace is smaller than the desired size.")); menu->add_radio_check_item(TTRC("Keep Aspect Ratio"), WINDOW_SIZE_MODE_KEEP_ASPECT); menu->set_item_tooltip(menu->get_item_index(WINDOW_SIZE_MODE_KEEP_ASPECT), TTRC("Keep the aspect ratio of the embedded game.")); menu->add_radio_check_item(TTRC("Stretch to Fit"), WINDOW_SIZE_MODE_STRETCH); - menu->set_item_tooltip(menu->get_item_index(WINDOW_SIZE_MODE_STRETCH), TTRC("Embedded game size stretches to fit the Game Workspace.")); + menu->set_item_tooltip(menu->get_item_index(WINDOW_SIZE_MODE_STRETCH), TTRC("Embedded game size stretches to fit the Game workspace.")); panel = memnew(Panel); add_child(panel); diff --git a/editor/run/game_view_plugin.h b/editor/run/game_view_plugin.h index ddcc21dd9dab..2121495d22d2 100644 --- a/editor/run/game_view_plugin.h +++ b/editor/run/game_view_plugin.h @@ -36,6 +36,7 @@ #include "editor/plugins/editor_plugin.h" #include "scene/debugger/runtime_node_select.h" #include "scene/gui/box_container.h" +#include "scene/gui/menu_button.h" class EmbeddedProcessBase; class VSeparator; @@ -124,10 +125,9 @@ class GameView : public VBoxContainer { CAMERA_RESET_3D, CAMERA_MODE_INGAME, CAMERA_MODE_EDITORS, + SELECTION_HIDE, SELECTION_AVOID_LOCKED, SELECTION_PREFER_GROUP, - WINDOW_RUN_GAME_EMBEDDED, - WINDOW_MAKE_FLOATING_ON_PLAY, WINDOW_SIZE_MODE_FIXED, WINDOW_SIZE_MODE_KEEP_ASPECT, WINDOW_SIZE_MODE_STRETCH, @@ -153,6 +153,13 @@ class GameView : public VBoxContainer { EMBED_NOT_AVAILABLE_HEADLESS, }; + enum EmbedMode { + EMBED_TYPE_DISABLED, + EMBED_TYPE_FLOATING, + EMBED_TYPE_EDITOR, + EMBED_TYPE_MAX, + }; + inline static GameView *singleton = nullptr; Ref debugger; @@ -174,6 +181,7 @@ class GameView : public VBoxContainer { bool debug_mute_audio = false; + bool selection_hide = true; bool selection_avoid_locked = false; bool selection_prefer_group = false; @@ -183,18 +191,19 @@ class GameView : public VBoxContainer { Button *node_type_button[RuntimeNodeSelect::NODE_TYPE_MAX]; Button *select_mode_button[RuntimeNodeSelect::SELECT_MODE_MAX]; - Button *hide_selection = nullptr; MenuButton *selection_options_menu = nullptr; - Button *debug_mute_audio_button = nullptr; - Button *camera_override_button = nullptr; MenuButton *camera_override_menu = nullptr; + Button *debug_mute_audio_button = nullptr; + HBoxContainer *embedding_hb = nullptr; MenuButton *game_window_options_menu = nullptr; Label *game_size_label = nullptr; - Control *game_size_placeholder = nullptr; + HBoxContainer *game_hb = nullptr; + MenuButton *game_embed_mode_menu = nullptr; + Button *game_embed_mode_button[EmbedMode::EMBED_TYPE_MAX]; Panel *panel = nullptr; EmbeddedProcessBase *embedded_process = nullptr; Label *state_label = nullptr; @@ -213,7 +222,6 @@ class GameView : public VBoxContainer { bool renderer_supports_hdr_output = false; MenuButton *speed_state_button = nullptr; - Button *reset_speed_button = nullptr; void _sessions_changed(); @@ -225,12 +233,14 @@ class GameView : public VBoxContainer { void _node_type_pressed(int p_option); void _select_mode_pressed(int p_option); + void _game_embed_mode_pressed(int p_option); void _selection_options_menu_id_pressed(int p_id); void _game_window_options_menu_menu_id_pressed(int p_id); void _reset_time_scales(); void _speed_state_menu_pressed(int p_id); void _update_speed_buttons(); + void _update_speed_state_icon(int p_id); void _update_speed_state_color(); void _update_speed_state_size(); @@ -248,13 +258,12 @@ class GameView : public VBoxContainer { EmbedAvailability _get_embed_available(); void _update_ui(); void _update_embed_menu_options(); + void _update_embed_buttons(); void _update_game_window_size_label(); void _update_embed_window_size(); void _update_arguments_for_instance(int p_idx, List &r_arguments); void _show_update_window_wrapper(); - void _hide_selection_toggled(bool p_pressed); - void _debug_mute_audio_button_pressed(); void _setup_complete(); void _game_window_size_received(const Array &p_state); diff --git a/editor/themes/theme_classic.cpp b/editor/themes/theme_classic.cpp index f8b7632b7bbf..c9b2a87ee77b 100644 --- a/editor/themes/theme_classic.cpp +++ b/editor/themes/theme_classic.cpp @@ -1887,8 +1887,8 @@ void ThemeClassic::populate_editor_styles(const Ref &p_theme, Edito p_theme->set_type_variation("FlatButtonNoIconTint", "FlatButton"); p_theme->set_color("icon_pressed_color", "FlatButtonNoIconTint", p_config.icon_normal_color); - p_theme->set_color("icon_hover_color", "FlatButtonNoIconTint", p_config.mono_color); - p_theme->set_color("icon_hover_pressed_color", "FlatButtonNoIconTint", p_config.mono_color); + p_theme->set_color("icon_hover_color", "FlatButtonNoIconTint", p_config.dark_icon_and_font ? p_config.mono_color : p_config.mono_color.inverted()); + p_theme->set_color("icon_hover_pressed_color", "FlatButtonNoIconTint", p_config.dark_icon_and_font ? p_config.mono_color : p_config.mono_color.inverted()); // Variation for the AssetLib thumbnails. @@ -1901,8 +1901,8 @@ void ThemeClassic::populate_editor_styles(const Ref &p_theme, Edito p_theme->set_type_variation("FlatMenuButtonNoIconTint", "FlatMenuButton"); p_theme->set_color("icon_pressed_color", "FlatMenuButtonNoIconTint", p_config.icon_normal_color); - p_theme->set_color("icon_hover_color", "FlatMenuButtonNoIconTint", p_config.mono_color); - p_theme->set_color("icon_hover_pressed_color", "FlatMenuButtonNoIconTint", p_config.mono_color); + p_theme->set_color("icon_hover_color", "FlatMenuButtonNoIconTint", p_config.dark_icon_and_font ? p_config.mono_color : p_config.mono_color.inverted()); + p_theme->set_color("icon_hover_pressed_color", "FlatMenuButtonNoIconTint", p_config.dark_icon_and_font ? p_config.mono_color : p_config.mono_color.inverted()); // Variation for Editor Log filter buttons. p_theme->set_type_variation("EditorLogFilterButton", "Button"); @@ -1933,8 +1933,8 @@ void ThemeClassic::populate_editor_styles(const Ref &p_theme, Edito { p_theme->set_type_variation("CheckBoxNoIconTint", "CheckBox"); p_theme->set_color("icon_pressed_color", "CheckBoxNoIconTint", p_config.icon_normal_color); - p_theme->set_color("icon_hover_color", "CheckBoxNoIconTint", p_config.mono_color); - p_theme->set_color("icon_hover_pressed_color", "CheckBoxNoIconTint", p_config.mono_color); + p_theme->set_color("icon_hover_color", "CheckBoxNoIconTint", p_config.dark_icon_and_font ? p_config.mono_color : p_config.mono_color.inverted()); + p_theme->set_color("icon_hover_pressed_color", "CheckBoxNoIconTint", p_config.dark_icon_and_font ? p_config.mono_color : p_config.mono_color.inverted()); } // Buttons styles that stand out against the panel background (e.g. AssetLib). @@ -2062,6 +2062,18 @@ void ThemeClassic::populate_editor_styles(const Ref &p_theme, Edito p_theme->set_stylebox(SceneStringName(panel), "PanelContainerTabbarInner", EditorThemeManager::make_empty_stylebox(0, 0, 0, 0)); } + // PanelContainerButtonGroup. + { + p_theme->set_type_variation("PanelContainerButtonGroup", "PanelContainer"); + + Ref style_button_group = p_theme->get_stylebox(SNAME("tabbar_background"), SNAME("TabContainer"))->duplicate(); + style_button_group->set_content_margin_all(p_config.base_margin * EDSCALE); + style_button_group->set_corner_radius_all(p_config.corner_radius); + style_button_group->set_bg_color(p_config.dark_color_1.lerp(p_config.mono_color, 0.15)); + + p_theme->set_stylebox(SceneStringName(panel), "PanelContainerButtonGroup", style_button_group); + } + // TreeLineEdit. { Ref tree_line_edit_style = p_theme->get_stylebox(CoreStringName(normal), SNAME("LineEdit"))->duplicate(); diff --git a/editor/themes/theme_modern.cpp b/editor/themes/theme_modern.cpp index cc030f772498..b0c7763d6dc6 100644 --- a/editor/themes/theme_modern.cpp +++ b/editor/themes/theme_modern.cpp @@ -1947,13 +1947,13 @@ void ThemeModern::populate_editor_styles(const Ref &p_theme, Editor p_theme->set_type_variation("FlatButtonNoIconTint", "FlatButton"); p_theme->set_color("icon_pressed_color", "FlatButtonNoIconTint", p_config.icon_normal_color); - p_theme->set_color("icon_hover_color", "FlatButtonNoIconTint", p_config.mono_color); - p_theme->set_color("icon_hover_pressed_color", "FlatButtonNoIconTint", p_config.mono_color); + p_theme->set_color("icon_hover_color", "FlatButtonNoIconTint", p_config.dark_icon_and_font ? p_config.mono_color : p_config.mono_color_inv); + p_theme->set_color("icon_hover_pressed_color", "FlatButtonNoIconTint", p_config.dark_icon_and_font ? p_config.mono_color : p_config.mono_color_inv); p_theme->set_type_variation("FlatMenuButtonNoIconTint", "FlatMenuButton"); p_theme->set_color("icon_pressed_color", "FlatMenuButtonNoIconTint", p_config.icon_normal_color); - p_theme->set_color("icon_hover_color", "FlatMenuButtonNoIconTint", p_config.mono_color); - p_theme->set_color("icon_hover_pressed_color", "FlatMenuButtonNoIconTint", p_config.mono_color); + p_theme->set_color("icon_hover_color", "FlatMenuButtonNoIconTint", p_config.dark_icon_and_font ? p_config.mono_color : p_config.mono_color_inv); + p_theme->set_color("icon_hover_pressed_color", "FlatMenuButtonNoIconTint", p_config.dark_icon_and_font ? p_config.mono_color : p_config.mono_color_inv); // Variation for the AssetLib thumbnails. @@ -1985,8 +1985,8 @@ void ThemeModern::populate_editor_styles(const Ref &p_theme, Editor { p_theme->set_type_variation("CheckBoxNoIconTint", "CheckBox"); p_theme->set_color("icon_pressed_color", "CheckBoxNoIconTint", p_config.icon_normal_color); - p_theme->set_color("icon_hover_color", "CheckBoxNoIconTint", p_config.mono_color); - p_theme->set_color("icon_hover_pressed_color", "CheckBoxNoIconTint", p_config.mono_color); + p_theme->set_color("icon_hover_color", "CheckBoxNoIconTint", p_config.dark_icon_and_font ? p_config.mono_color : p_config.mono_color_inv); + p_theme->set_color("icon_hover_pressed_color", "CheckBoxNoIconTint", p_config.dark_icon_and_font ? p_config.mono_color : p_config.mono_color_inv); } // Buttons styles that stand out against the panel background (e.g. AssetLib). @@ -2175,6 +2175,18 @@ void ThemeModern::populate_editor_styles(const Ref &p_theme, Editor p_theme->set_stylebox(SceneStringName(panel), "PanelContainerTabbarInner", style_tabbar_background_inner); } + // PanelContainerButtonGroup. + { + p_theme->set_type_variation("PanelContainerButtonGroup", "PanelContainer"); + + Ref style_button_group = p_theme->get_stylebox(SNAME("tabbar_background"), SNAME("TabContainer"))->duplicate(); + style_button_group->set_content_margin_all(p_config.base_margin * EDSCALE); + style_button_group->set_corner_radius_all(p_config.corner_radius > 0 ? (p_config.corner_radius + p_config.base_margin) * EDSCALE : 0); + style_button_group->set_bg_color(p_config.surface_lower_color.lerp(p_config.mono_color_inv, 0.15).lightened(0.02)); + + p_theme->set_stylebox(SceneStringName(panel), "PanelContainerButtonGroup", style_button_group); + } + // TreeLineEdit. { Ref tree_line_edit_style = p_theme->get_stylebox(CoreStringName(normal), SNAME("LineEdit"))->duplicate();