diff --git a/doc/classes/DisplayServer.xml b/doc/classes/DisplayServer.xml index d28fd5d8174..a6b854e36a4 100644 --- a/doc/classes/DisplayServer.xml +++ b/doc/classes/DisplayServer.xml @@ -644,6 +644,16 @@ + + + + + + If set to [code]true[/code], this window will always stay on top of its parent window, parent window will ignore input while this window is opened. + [b]Note:[/b] On macOS, exclusive windows are confined to the same space (virtual desktop or screen) as the parent window. + [b]Note:[/b] This method is implemented on macOS and Windows. + + diff --git a/platform/osx/display_server_osx.h b/platform/osx/display_server_osx.h index eaf03d298eb..2b57983ca7d 100644 --- a/platform/osx/display_server_osx.h +++ b/platform/osx/display_server_osx.h @@ -95,6 +95,7 @@ public: ObjectID instance_id; WindowID transient_parent = INVALID_WINDOW_ID; + bool exclusive = false; Set transient_children; bool layered_window = false; @@ -274,6 +275,7 @@ public: virtual void window_set_position(const Point2i &p_position, WindowID p_window = MAIN_WINDOW_ID) override; virtual void window_set_transient(WindowID p_window, WindowID p_parent) override; + virtual void window_set_exclusive(WindowID p_window, bool p_exclusive) override; virtual void window_set_max_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override; virtual Size2i window_get_max_size(WindowID p_window = MAIN_WINDOW_ID) const override; diff --git a/platform/osx/display_server_osx.mm b/platform/osx/display_server_osx.mm index 137b1b45b0b..b7258e6cf42 100644 --- a/platform/osx/display_server_osx.mm +++ b/platform/osx/display_server_osx.mm @@ -1471,6 +1471,24 @@ void DisplayServerOSX::window_set_current_screen(int p_screen, WindowID p_window } } +void DisplayServerOSX::window_set_exclusive(WindowID p_window, bool p_exclusive) { + _THREAD_SAFE_METHOD_ + ERR_FAIL_COND(!windows.has(p_window)); + WindowData &wd = windows[p_window]; + if (wd.exclusive != p_exclusive) { + wd.exclusive = p_exclusive; + if (wd.transient_parent != INVALID_WINDOW_ID) { + WindowData &wd_parent = windows[wd.transient_parent]; + if (wd.exclusive) { + ERR_FAIL_COND_MSG([[wd_parent.window_object childWindows] count] > 0, "Transient parent has another exclusive child."); + [wd_parent.window_object addChildWindow:wd.window_object ordered:NSWindowAbove]; + } else { + [wd_parent.window_object removeChildWindow:wd.window_object]; + } + } + } +} + Point2i DisplayServerOSX::window_get_position(WindowID p_window) const { _THREAD_SAFE_METHOD_ @@ -1541,8 +1559,11 @@ void DisplayServerOSX::window_set_transient(WindowID p_window, WindowID p_parent wd_window.transient_parent = INVALID_WINDOW_ID; wd_parent.transient_children.erase(p_window); - [wd_window.window_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; + + if (wd_window.exclusive) { + [wd_parent.window_object removeChildWindow:wd_window.window_object]; + } } else { ERR_FAIL_COND(!windows.has(p_parent)); ERR_FAIL_COND_MSG(wd_window.transient_parent != INVALID_WINDOW_ID, "Window already has a transient parent"); @@ -1550,8 +1571,11 @@ void DisplayServerOSX::window_set_transient(WindowID p_window, WindowID p_parent wd_window.transient_parent = p_parent; wd_parent.transient_children.insert(p_window); - [wd_window.window_object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary]; + + if (wd_window.exclusive) { + [wd_parent.window_object addChildWindow:wd_window.window_object ordered:NSWindowAbove]; + } } } diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index 36c87f26839..c7955ebf314 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -830,6 +830,23 @@ void DisplayServerWindows::window_set_position(const Point2i &p_position, Window _update_real_mouse_position(p_window); } +void DisplayServerWindows::window_set_exclusive(WindowID p_window, bool p_exclusive) { + _THREAD_SAFE_METHOD_ + ERR_FAIL_COND(!windows.has(p_window)); + WindowData &wd = windows[p_window]; + if (wd.exclusive != p_exclusive) { + wd.exclusive = p_exclusive; + if (wd.transient_parent != INVALID_WINDOW_ID) { + WindowData &wd_parent = windows[wd.transient_parent]; + if (wd.exclusive) { + SetWindowLongPtr(wd.hWnd, GWLP_HWNDPARENT, (LONG_PTR)wd_parent.hWnd); + } else { + SetWindowLongPtr(wd.hWnd, GWLP_HWNDPARENT, (LONG_PTR) nullptr); + } + } + } +} + void DisplayServerWindows::window_set_transient(WindowID p_window, WindowID p_parent) { _THREAD_SAFE_METHOD_ @@ -852,7 +869,9 @@ void DisplayServerWindows::window_set_transient(WindowID p_window, WindowID p_pa wd_window.transient_parent = INVALID_WINDOW_ID; wd_parent.transient_children.erase(p_window); - SetWindowLongPtr(wd_window.hWnd, GWLP_HWNDPARENT, (LONG_PTR) nullptr); + if (wd_window.exclusive) { + SetWindowLongPtr(wd_window.hWnd, GWLP_HWNDPARENT, (LONG_PTR) nullptr); + } } else { ERR_FAIL_COND(!windows.has(p_parent)); ERR_FAIL_COND_MSG(wd_window.transient_parent != INVALID_WINDOW_ID, "Window already has a transient parent"); @@ -861,7 +880,9 @@ void DisplayServerWindows::window_set_transient(WindowID p_window, WindowID p_pa wd_window.transient_parent = p_parent; wd_parent.transient_children.insert(p_window); - SetWindowLongPtr(wd_window.hWnd, GWLP_HWNDPARENT, (LONG_PTR)wd_parent.hWnd); + if (wd_window.exclusive) { + SetWindowLongPtr(wd_window.hWnd, GWLP_HWNDPARENT, (LONG_PTR)wd_parent.hWnd); + } } } diff --git a/platform/windows/display_server_windows.h b/platform/windows/display_server_windows.h index d36ca97ebee..7561f9bb770 100644 --- a/platform/windows/display_server_windows.h +++ b/platform/windows/display_server_windows.h @@ -339,6 +339,7 @@ class DisplayServerWindows : public DisplayServer { bool always_on_top = false; bool no_focus = false; bool window_has_focus = false; + bool exclusive = false; // Used to transfer data between events using timer. WPARAM saved_wparam; @@ -499,6 +500,7 @@ public: virtual void window_set_position(const Point2i &p_position, WindowID p_window = MAIN_WINDOW_ID) override; virtual void window_set_transient(WindowID p_window, WindowID p_parent) override; + virtual void window_set_exclusive(WindowID p_window, bool p_exclusive) override; virtual void window_set_max_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) override; virtual Size2i window_get_max_size(WindowID p_window = MAIN_WINDOW_ID) const override; diff --git a/scene/main/window.cpp b/scene/main/window.cpp index b5e0e7b110d..77c0fa2a48b 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -255,6 +255,7 @@ void Window::_make_window() { #endif DisplayServer::get_singleton()->window_set_title(tr_title, window_id); DisplayServer::get_singleton()->window_attach_instance_id(get_instance_id(), window_id); + DisplayServer::get_singleton()->window_set_exclusive(window_id, exclusive); _update_window_size(); @@ -523,6 +524,10 @@ void Window::set_exclusive(bool p_exclusive) { exclusive = p_exclusive; + if (!embedder && window_id != DisplayServer::INVALID_WINDOW_ID) { + DisplayServer::get_singleton()->window_set_exclusive(window_id, exclusive); + } + if (transient_parent) { if (p_exclusive && is_inside_tree() && is_visible()) { ERR_FAIL_COND_MSG(transient_parent->exclusive_child && transient_parent->exclusive_child != this, "Transient parent has another exclusive child."); diff --git a/servers/display_server.cpp b/servers/display_server.cpp index d880df2a9b2..4d7e2b4d9ff 100644 --- a/servers/display_server.cpp +++ b/servers/display_server.cpp @@ -204,6 +204,10 @@ void DisplayServer::delete_sub_window(WindowID p_id) { ERR_FAIL_MSG("Sub-windows not supported by this display server."); } +void DisplayServer::window_set_exclusive(WindowID p_window, bool p_exclusive) { + // Do nothing, if not supported. +} + void DisplayServer::window_set_mouse_passthrough(const Vector &p_region, WindowID p_window) { ERR_FAIL_MSG("Mouse passthrough not supported by this display server."); } @@ -436,6 +440,7 @@ void DisplayServer::_bind_methods() { ClassDB::bind_method(D_METHOD("window_can_draw", "window_id"), &DisplayServer::window_can_draw, DEFVAL(MAIN_WINDOW_ID)); ClassDB::bind_method(D_METHOD("window_set_transient", "window_id", "parent_window_id"), &DisplayServer::window_set_transient); + ClassDB::bind_method(D_METHOD("window_set_exclusive", "window_id", "exclusive"), &DisplayServer::window_set_exclusive); ClassDB::bind_method(D_METHOD("window_set_ime_active", "active", "window_id"), &DisplayServer::window_set_ime_active, DEFVAL(MAIN_WINDOW_ID)); ClassDB::bind_method(D_METHOD("window_set_ime_position", "position", "window_id"), &DisplayServer::window_set_ime_position, DEFVAL(MAIN_WINDOW_ID)); diff --git a/servers/display_server.h b/servers/display_server.h index bd38f28f175..81ac551f572 100644 --- a/servers/display_server.h +++ b/servers/display_server.h @@ -277,6 +277,7 @@ public: virtual void window_set_position(const Point2i &p_position, WindowID p_window = MAIN_WINDOW_ID) = 0; virtual void window_set_transient(WindowID p_window, WindowID p_parent) = 0; + virtual void window_set_exclusive(WindowID p_window, bool p_exclusive); virtual void window_set_max_size(const Size2i p_size, WindowID p_window = MAIN_WINDOW_ID) = 0; virtual Size2i window_get_max_size(WindowID p_window = MAIN_WINDOW_ID) const = 0;