From 15144c24bd66e1686c38398c4e0bd9f4742a6020 Mon Sep 17 00:00:00 2001 From: Juan Linietsky Date: Fri, 22 Dec 2023 17:50:21 +0100 Subject: [PATCH] Implement a transient_to_focused mode This intends to be the correct way to handle non-child windows becoming covered by the current window when becoming focused. Enabling this property on select windows, they will become transient to the currently focused one when becoming visible. This deprecates the "unparent_when_invisible" function introduced by #76025. --- doc/classes/Window.xml | 3 + editor/editor_node.cpp | 1 + platform/linuxbsd/x11/display_server_x11.cpp | 5 ++ platform/linuxbsd/x11/display_server_x11.h | 2 + platform/macos/display_server_macos.h | 2 + platform/macos/display_server_macos.mm | 4 ++ platform/windows/display_server_windows.cpp | 4 ++ platform/windows/display_server_windows.h | 2 + scene/main/window.cpp | 66 +++++++++++++++----- scene/main/window.h | 4 ++ servers/display_server.cpp | 4 ++ servers/display_server.h | 2 + 12 files changed, 85 insertions(+), 14 deletions(-) diff --git a/doc/classes/Window.xml b/doc/classes/Window.xml index 5f6b1960b78..ceefe063276 100644 --- a/doc/classes/Window.xml +++ b/doc/classes/Window.xml @@ -668,6 +668,9 @@ If [code]true[/code], the [Window] is transient, i.e. it's considered a child of another [Window]. The transient window will be destroyed with its transient parent and will return focus to their parent when closed. The transient window is displayed on top of a non-exclusive full-screen parent window. Transient windows can't enter full-screen mode. Note that behavior might be different depending on the platform. + + If [code]true[/code], and the [Window] is [member transient], this window will (at the time of becoming visible) become transient to the currently focused window instead of the immediate parent window in the hierarchy. Note that the transient parent is assigned at the time this window becomes visible, so changing it afterwards has no effect until re-shown. + If [code]true[/code], the [Window]'s background can be transparent. This is best used with embedded windows. [b]Note:[/b] Transparency support is implemented on Linux, macOS and Windows, but availability might vary depending on GPU driver, display manager, and compositor capabilities. diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index bd09019671f..5e93bd30ddf 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -7803,6 +7803,7 @@ EditorNode::EditorNode() { file = memnew(EditorFileDialog); gui_base->add_child(file); file->set_current_dir("res://"); + file->set_transient_to_focused(true); file_export_lib = memnew(EditorFileDialog); file_export_lib->set_title(TTR("Export Library")); diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index 1434342bbc7..c72a203c4a1 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -2891,10 +2891,15 @@ void DisplayServerX11::window_move_to_foreground(WindowID p_window) { XFlush(x11_display); } +DisplayServerX11::WindowID DisplayServerX11::get_focused_window() const { + return last_focused_window; +} + bool DisplayServerX11::window_is_focused(WindowID p_window) const { _THREAD_SAFE_METHOD_ ERR_FAIL_COND_V(!windows.has(p_window), false); + const WindowData &wd = windows[p_window]; return wd.focused; diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h index ac2c7843f61..378d8bb407e 100644 --- a/platform/linuxbsd/x11/display_server_x11.h +++ b/platform/linuxbsd/x11/display_server_x11.h @@ -491,6 +491,8 @@ public: virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID) override; virtual bool window_is_focused(WindowID p_window = MAIN_WINDOW_ID) const override; + virtual WindowID get_focused_window() const override; + virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const override; virtual bool can_any_window_draw() const override; diff --git a/platform/macos/display_server_macos.h b/platform/macos/display_server_macos.h index f8fd0f93efb..cc343a6d64d 100644 --- a/platform/macos/display_server_macos.h +++ b/platform/macos/display_server_macos.h @@ -428,6 +428,8 @@ public: virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID) override; virtual bool window_is_focused(WindowID p_window = MAIN_WINDOW_ID) const override; + virtual WindowID get_focused_window() const override; + virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const override; virtual bool can_any_window_draw() const override; diff --git a/platform/macos/display_server_macos.mm b/platform/macos/display_server_macos.mm index bd92b7d4721..378688f78af 100644 --- a/platform/macos/display_server_macos.mm +++ b/platform/macos/display_server_macos.mm @@ -3723,6 +3723,10 @@ bool DisplayServerMacOS::window_is_focused(WindowID p_window) const { return wd.focused; } +DisplayServerMacOS::WindowID DisplayServerMacOS::get_focused_window() const { + return last_focused_window; +} + bool DisplayServerMacOS::window_can_draw(WindowID p_window) const { return windows[p_window].is_visible; } diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index 2bec804c7c7..30935e6ff68 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -1916,6 +1916,10 @@ bool DisplayServerWindows::window_is_focused(WindowID p_window) const { return wd.window_focused; } +DisplayServerWindows::WindowID DisplayServerWindows::get_focused_window() const { + return last_focused_window; +} + bool DisplayServerWindows::window_can_draw(WindowID p_window) const { _THREAD_SAFE_METHOD_ diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h index 29c2460c108..2668e14540e 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -611,6 +611,8 @@ public: virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID) override; virtual bool window_is_focused(WindowID p_window = MAIN_WINDOW_ID) const override; + virtual WindowID get_focused_window() const override; + virtual bool window_can_draw(WindowID p_window = MAIN_WINDOW_ID) const override; virtual bool can_any_window_draw() const override; diff --git a/scene/main/window.cpp b/scene/main/window.cpp index eb431445ed4..87c7550dd3a 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -573,6 +573,10 @@ bool Window::is_in_edited_scene_root() const { void Window::_make_window() { ERR_FAIL_COND(window_id != DisplayServer::INVALID_WINDOW_ID); + if (transient && transient_to_focused) { + _make_transient(); + } + uint32_t f = 0; for (int i = 0; i < FLAG_MAX; i++) { if (flags[i]) { @@ -665,6 +669,10 @@ void Window::_clear_window() { _update_viewport_size(); RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::VIEWPORT_UPDATE_DISABLED); + + if (transient && transient_to_focused) { + _clear_transient(); + } } void Window::_rect_changed_callback(const Rect2i &p_callback) { @@ -864,18 +872,29 @@ void Window::_make_transient() { return; } //find transient parent - Viewport *vp = get_parent()->get_viewport(); - Window *window = nullptr; - while (vp) { - window = Object::cast_to(vp); - if (window) { - break; - } - if (!vp->get_parent()) { - break; - } - vp = vp->get_parent()->get_viewport(); + Window *window = nullptr; + + if (!is_embedded() && transient_to_focused) { + DisplayServer::WindowID focused_window_id = DisplayServer::get_singleton()->get_focused_window(); + if (focused_window_id != DisplayServer::INVALID_WINDOW_ID) { + window = Object::cast_to(ObjectDB::get_instance(DisplayServer::get_singleton()->window_get_attached_instance_id(focused_window_id))); + } + } + + if (!window) { + Viewport *vp = get_parent()->get_viewport(); + while (vp) { + window = Object::cast_to(vp); + if (window) { + break; + } + if (!vp->get_parent()) { + break; + } + + vp = vp->get_parent()->get_viewport(); + } } if (window) { @@ -919,17 +938,32 @@ void Window::set_transient(bool p_transient) { } if (transient) { - _make_transient(); + if (!transient_to_focused) { + _make_transient(); + } } else { _clear_transient(); } } bool Window::is_transient() const { - ERR_READ_THREAD_GUARD_V(false); return transient; } +void Window::set_transient_to_focused(bool p_transient_to_focused) { + ERR_MAIN_THREAD_GUARD; + if (transient_to_focused == p_transient_to_focused) { + return; + } + + transient_to_focused = p_transient_to_focused; +} + +bool Window::is_transient_to_focused() const { + ERR_READ_THREAD_GUARD_V(false); + return transient_to_focused; +} + void Window::set_exclusive(bool p_exclusive) { ERR_MAIN_THREAD_GUARD; if (exclusive == p_exclusive) { @@ -1259,7 +1293,7 @@ void Window::_notification(int p_what) { } } - if (transient) { + if (transient && !transient_to_focused) { _make_transient(); } if (visible) { @@ -2753,6 +2787,9 @@ void Window::_bind_methods() { ClassDB::bind_method(D_METHOD("set_transient", "transient"), &Window::set_transient); ClassDB::bind_method(D_METHOD("is_transient"), &Window::is_transient); + ClassDB::bind_method(D_METHOD("set_transient_to_focused", "enable"), &Window::set_transient_to_focused); + ClassDB::bind_method(D_METHOD("is_transient_to_focused"), &Window::is_transient_to_focused); + ClassDB::bind_method(D_METHOD("set_exclusive", "exclusive"), &Window::set_exclusive); ClassDB::bind_method(D_METHOD("is_exclusive"), &Window::is_exclusive); @@ -2884,6 +2921,7 @@ void Window::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "wrap_controls"), "set_wrap_controls", "is_wrapping_controls"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "transient"), "set_transient", "is_transient"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "transient_to_focused"), "set_transient_to_focused", "is_transient_to_focused"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "exclusive"), "set_exclusive", "is_exclusive"); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "unresizable"), "set_flag", "get_flag", FLAG_RESIZE_DISABLED); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "borderless"), "set_flag", "get_flag", FLAG_BORDERLESS); diff --git a/scene/main/window.h b/scene/main/window.h index 0682abc3c79..f63da3402c3 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -126,6 +126,7 @@ private: bool use_font_oversampling = false; bool transient = false; + bool transient_to_focused = false; bool exclusive = false; bool wrap_controls = false; bool updating_child_controls = false; @@ -313,6 +314,9 @@ public: void set_transient(bool p_transient); bool is_transient() const; + void set_transient_to_focused(bool p_transient_to_focused); + bool is_transient_to_focused() const; + void set_exclusive(bool p_exclusive); bool is_exclusive() const; diff --git a/servers/display_server.cpp b/servers/display_server.cpp index bb28bc0eb8b..a587f72d029 100644 --- a/servers/display_server.cpp +++ b/servers/display_server.cpp @@ -596,6 +596,10 @@ DisplayServer::VSyncMode DisplayServer::window_get_vsync_mode(WindowID p_window) return VSyncMode::VSYNC_ENABLED; } +DisplayServer::WindowID DisplayServer::get_focused_window() const { + return MAIN_WINDOW_ID; // Proper value for single windows. +} + void DisplayServer::set_context(Context p_context) { } diff --git a/servers/display_server.h b/servers/display_server.h index 4450677f718..92a4a4a6996 100644 --- a/servers/display_server.h +++ b/servers/display_server.h @@ -436,6 +436,8 @@ public: virtual void window_move_to_foreground(WindowID p_window = MAIN_WINDOW_ID) = 0; virtual bool window_is_focused(WindowID p_window = MAIN_WINDOW_ID) const = 0; + virtual WindowID get_focused_window() const; + virtual void window_set_window_buttons_offset(const Vector2i &p_offset, WindowID p_window = MAIN_WINDOW_ID) {} virtual Vector3i window_get_safe_title_margins(WindowID p_window = MAIN_WINDOW_ID) const { return Vector3i(); }