Add exclusive window handling to DisplayServer (on macOS and Windows).

This commit is contained in:
bruvzg 2022-01-19 14:04:05 +02:00
parent cf13f8af64
commit 7d59b81d79
No known key found for this signature in database
GPG key ID: 7960FCF39844EC38
8 changed files with 74 additions and 4 deletions

View file

@ -638,6 +638,16 @@
<description>
</description>
</method>
<method name="window_set_exclusive">
<return type="void" />
<argument index="0" name="window_id" type="int" />
<argument index="1" name="exclusive" type="bool" />
<description>
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.
</description>
</method>
<method name="window_set_flag">
<return type="void" />
<argument index="0" name="flag" type="int" enum="DisplayServer.WindowFlags" />

View file

@ -95,6 +95,7 @@ public:
ObjectID instance_id;
WindowID transient_parent = INVALID_WINDOW_ID;
bool exclusive = false;
Set<WindowID> 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;

View file

@ -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];
}
}
}

View file

@ -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);
}
}
}

View file

@ -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;

View file

@ -254,6 +254,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();
@ -522,6 +523,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.");

View file

@ -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<Vector2> &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));

View file

@ -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;