Repository: godotengine/godot (language: cpp)

Issue:
The project's window title isn't visible when using a floating game window
- *Related to https://github.com/godotengine/godot/issues/101415.*

### Tested versions

- Reproducible in: 4.4.dev7, 4.4.dev 24d74510e
- Not reproducible in: 4.3.stable

### System information

Godot v4.4.dev (24d74510e) - Fedora Linux 41 (KDE Plasma) on X11 - X11 display driver, Multi-window, 1 monitor - Vulkan (Forward+) - dedicated NVIDIA GeForce RTX 4090 (nvidia; 565.77) - 13th Gen Intel(R) Core(TM) i9-13900K (32 threads)

### Issue description

The window title doesn't follow the user-defined title on the root Window node anymore when it's a floating game window:

![Image](https://github.com/user-attachments/assets/3cc78ff4-e8e7-485f-86fd-a0e7da7d4b2d)

If you don't change the window title yourself, it's set to the project name by default (with a ` (DEBUG)` suffix since this is an editor build) and this isn't seen either when it's a floating game window.

In contrast, this is how it looks like without the floating window system (and with embedding disabled):

![Image](https://github.com/user-attachments/assets/e64ad8dc-08af-43f8-a850-32f4ed1d1115)

### Steps to reproduce

- Enable game window embedding with a floating window.
- Call `get_window().title = "Something"` in a script.
- Notice how the window title doesn't change and remains set to *Game Workspace*.

### Minimal reproduction project (MRP)

[test_window_title.zip](https://github.com/user-attachments/files/18411323/test_window_title.zip)


Discussion / hints:
I don't think it's really the expected behavior in my opinion because the floating window is not the game window. I could be wrong. Just to clarify, why do you need the floating window to have the same title as the game window?

After reviewing some of the issues you created, I believe one way to address all these problems could be to implement a mode where the floating window acts as a floating toolbar, positioned either on top of or beside the game window, and contains only the "Game View" toolbar. This approach would resolve issues such as using window manager shortcuts in the game view, the game view title, the viewport size from the project settings, and the problem where the game window's size or position occasionally falls out of sync. Implementing this mode should be simpler and less prone to issues, as it would only require ensuring that the "Game View Toolbar window" follows the game window when it moves or resizes. It could be hidden in fullscreen mode or always stay on top of the screen. Additionally, it would work on Mac!

Conversely, fixing the submitted issues directly would be quite complex and time-consuming, and I am almost certain that some are simply impossible to fully resolve.

Here’s an illustration of the concept:  

![Image](https://github.com/user-attachments/assets/5e78c1d8-8540-44dd-a188-2971ca3acef5)

I’m referring to these issues:  
https://github.com/godotengine/godot/issues/100883  
https://github.com/godotengine/godot/issues/101415  


What do you think of this suggestion?
> Just to clarify, why do you need the floating window to have the same title as the game window?

I'd like to be able to test logic related to setting the window title, e.g. if I'm developing a non-game application and want the currently open file to be visible in the window title:

![Image](https://github.com/user-attachments/assets/3ef33bc5-f426-4045-aa01-7ecc79fccb5f)

Additionally, making it part of the window title means it'll appear when alt-tabbing around, even when using a floating game window.

If we really don't want to lose the "game window" indication, we could make the existing game window text a prefix to the actual window title set by the project (and keep the `(DEBUG)` suffix so users are aware that running the project from the editor won't exhibit full performance).

Fix patch:
diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp
index 5b89cc8e13e4..1363fca3c2c9 100644
--- a/editor/debugger/script_editor_debugger.cpp
+++ b/editor/debugger/script_editor_debugger.cpp
@@ -826,6 +826,9 @@ void ScriptEditorDebugger::_parse_message(const String &p_msg, uint64_t p_thread
 		}
 	} else if (p_msg == "evaluation_return") {
 		expression_evaluator->add_value(p_data);
+	} else if (p_msg == "window:title") {
+		ERR_FAIL_COND(p_data.size() != 1);
+		emit_signal(SNAME("remote_window_title_changed"), p_data[0]);
 	} else {
 		int colon_index = p_msg.find_char(':');
 		ERR_FAIL_COND_MSG(colon_index < 1, "Invalid message received");
@@ -1784,6 +1787,7 @@ void ScriptEditorDebugger::_bind_methods() {
 	ADD_SIGNAL(MethodInfo("remote_object_property_updated", PropertyInfo(Variant::INT, "id"), PropertyInfo(Variant::STRING, "property")));
 	ADD_SIGNAL(MethodInfo("remote_tree_updated"));
 	ADD_SIGNAL(MethodInfo("remote_tree_select_requested", PropertyInfo(Variant::NODE_PATH, "path")));
+	ADD_SIGNAL(MethodInfo("remote_window_title_changed", PropertyInfo(Variant::STRING, "title")));
 	ADD_SIGNAL(MethodInfo("output", PropertyInfo(Variant::STRING, "msg"), PropertyInfo(Variant::INT, "level")));
 	ADD_SIGNAL(MethodInfo("stack_dump", PropertyInfo(Variant::ARRAY, "stack_dump")));
 	ADD_SIGNAL(MethodInfo("stack_frame_vars", PropertyInfo(Variant::INT, "num_vars")));
diff --git a/editor/plugins/embedded_process.cpp b/editor/plugins/embedded_process.cpp
index 75e532513464..06c207ca7942 100644
--- a/editor/plugins/embedded_process.cpp
+++ b/editor/plugins/embedded_process.cpp
@@ -145,6 +145,10 @@ bool EmbeddedProcess::is_embedding_completed() {
 	return embedding_completed;
 }
 
+int EmbeddedProcess::get_embedded_pid() const {
+	return current_process_id;
+}
+
 void EmbeddedProcess::embed_process(OS::ProcessID p_pid) {
 	if (!window) {
 		return;
diff --git a/editor/plugins/embedded_process.h b/editor/plugins/embedded_process.h
index d4265dc899c3..444cac1786b9 100644
--- a/editor/plugins/embedded_process.h
+++ b/editor/plugins/embedded_process.h
@@ -82,6 +82,7 @@ class EmbeddedProcess : public Control {
 	Rect2i get_screen_embedded_window_rect();
 	bool is_embedding_in_progress();
 	bool is_embedding_completed();
+	int get_embedded_pid() const;
 
 	EmbeddedProcess();
 	~EmbeddedProcess();
diff --git a/editor/plugins/game_view_plugin.cpp b/editor/plugins/game_view_plugin.cpp
index 212957dd2d9f..2078a3062fad 100644
--- a/editor/plugins/game_view_plugin.cpp
+++ b/editor/plugins/game_view_plugin.cpp
@@ -32,7 +32,9 @@
 
 #include "core/config/project_settings.h"
 #include "core/debugger/debugger_marshalls.h"
+#include "core/string/translation_server.h"
 #include "editor/debugger/editor_debugger_node.h"
+#include "editor/debugger/script_editor_debugger.h"
 #include "editor/editor_command_palette.h"
 #include "editor/editor_feature_profile.h"
 #include "editor/editor_interface.h"
@@ -203,6 +205,12 @@ void GameView::_sessions_changed() {
 	}
 
 	_update_debugger_buttons();
+
+	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();
+		}
+	}
 }
 
 void GameView::_instance_starting_static(int p_idx, List<String> &r_arguments) {
@@ -215,6 +223,11 @@ void GameView::_instance_starting(int p_idx, List<String> &r_arguments) {
 		return;
 	}
 	if (p_idx == 0 && embed_on_play && make_floating_on_play && !window_wrapper->get_window_enabled() && EditorNode::get_singleton()->is_multi_window_enabled()) {
+		// 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);
+
 		window_wrapper->restore_window_from_saved_position(floating_window_rect, floating_window_screen, floating_window_screen_rect);
 	}
 
@@ -255,6 +268,8 @@ void GameView::_stop_pressed() {
 		return;
 	}
 
+	_detach_script_debugger();
+
 	EditorNode::get_singleton()->set_unfocused_low_processor_usage_mode_enabled(true);
 	embedded_process->reset();
 	_update_ui();
@@ -272,6 +287,7 @@ void GameView::_stop_pressed() {
 }
 
 void GameView::_embedding_completed() {
+	_attach_script_debugger();
 	_update_ui();
 }
 
@@ -563,6 +579,36 @@ void GameView::_update_floating_window_settings() {
 	}
 }
 
+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);
+}
+
 void GameView::_update_arguments_for_instance(int p_idx, List<String> &r_arguments) {
 	if (p_idx != 0 || !embed_on_play || !DisplayServer::get_singleton()->has_feature(DisplayServer::FEATURE_WINDOW_EMBEDDING)) {
 		return;
diff --git a/editor/plugins/game_view_plugin.h b/editor/plugins/game_view_plugin.h
index 50d4170e92d3..959da73dcde8 100644
--- a/editor/plugins/game_view_plugin.h
+++ b/editor/plugins/game_view_plugin.h
@@ -40,6 +40,7 @@
 class EmbeddedProcess;
 class VSeparator;
 class WindowWrapper;
+class ScriptEditorDebugger;
 
 class GameViewDebugger : public EditorDebuggerPlugin {
 	GDCLASS(GameViewDebugger, EditorDebuggerPlugin);
@@ -101,6 +102,7 @@ class GameView : public VBoxContainer {
 	bool is_feature_enabled = true;
 	int active_sessions = 0;
 	int screen_index_before_start = -1;
+	ScriptEditorDebugger *embedded_script_debugger = nullptr;
 
 	bool embed_on_play = true;
 	bool make_floating_on_play = true;
@@ -162,6 +164,9 @@ class GameView : public VBoxContainer {
 
 	void _window_before_closing();
 	void _update_floating_window_settings();
+	void _attach_script_debugger();
+	void _detach_script_debugger();
+	void _remote_window_title_changed(String title);
 
 protected:
 	void _notification(int p_what);
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index 3476cb4cdc54..1bb01313e721 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -31,6 +31,7 @@
 #include "window.h"
 
 #include "core/config/project_settings.h"
+#include "core/debugger/engine_debugger.h"
 #include "core/input/shortcut.h"
 #include "core/string/translation_server.h"
 #include "scene/gui/control.h"
@@ -306,6 +307,14 @@ void Window::set_title(const String &p_title) {
 		}
 	}
 	emit_signal("title_changed");
+
+#ifdef DEBUG_ENABLED
+	if (EngineDebugger::get_singleton() && window_id == DisplayServer::MAIN_WINDOW_ID && !Engine::get_singleton()->is_project_manager_hint()) {
+		Array arr;
+		arr.push_back(tr_title);
+		EngineDebugger::get_singleton()->send_message("window:title", arr);
+	}
+#endif
 }
 
 String Window::get_title() const {


Test patch: