Skip to content

Commit 149f218

Browse files
committed
Fix Modal Dialog with Embedded Game
1 parent 9ee1873 commit 149f218

File tree

3 files changed

+56
-29
lines changed

3 files changed

+56
-29
lines changed

editor/plugins/embedded_process.cpp

+53-27
Original file line numberDiff line numberDiff line change
@@ -81,20 +81,17 @@ void EmbeddedProcess::_notification(int p_what) {
8181
} break;
8282
case NOTIFICATION_APPLICATION_FOCUS_IN: {
8383
application_has_focus = true;
84-
if (embedded_process_was_focused) {
85-
embedded_process_was_focused = false;
86-
// Refocus the embedded process if it was focused when the application lost focus,
87-
// but do not refocus if the embedded process is currently focused (indicating it just lost focus)
88-
// or if the current window is a different popup or secondary window.
89-
if (embedding_completed && current_process_id != focused_process_id && window && window->has_focus()) {
90-
grab_focus();
91-
queue_update_embedded_process();
92-
}
93-
}
9484
} break;
9585
case NOTIFICATION_APPLICATION_FOCUS_OUT: {
9686
application_has_focus = false;
97-
embedded_process_was_focused = embedding_completed && current_process_id == focused_process_id;
87+
88+
// Refocus the current model when focusing the embedded process.
89+
if (embedding_completed && current_process_id == DisplayServer::get_singleton()->get_focused_process_id()) {
90+
Window *modal_window = _get_current_modal_window();
91+
if (modal_window) {
92+
callable_mp(modal_window, &Window::grab_focus).call_deferred();
93+
}
94+
}
9895
} break;
9996
}
10097
}
@@ -266,7 +263,6 @@ void EmbeddedProcess::_update_embedded_process() {
266263
}
267264
last_updated_embedded_process_focused = focus;
268265
}
269-
270266
DisplayServer::get_singleton()->embed_process(window->get_window_id(), current_process_id, get_screen_embedded_window_rect(), is_visible_in_tree(), must_grab_focus);
271267
emit_signal(SNAME("embedded_process_updated"));
272268
}
@@ -287,14 +283,19 @@ void EmbeddedProcess::_check_mouse_over() {
287283
// This method checks if the mouse is over the embedded process while the current application is focused.
288284
// The goal is to give focus to the embedded process as soon as the mouse hovers over it,
289285
// allowing the user to interact with it immediately without needing to click first.
290-
if (!is_visible_in_tree() || !embedding_completed || !application_has_focus || !window || !window->has_focus() || Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT) || Input::get_singleton()->is_mouse_button_pressed(MouseButton::RIGHT)) {
286+
if (!embedding_completed || !application_has_focus || !window || has_focus() || !is_visible_in_tree() || !window->has_focus() || Input::get_singleton()->is_mouse_button_pressed(MouseButton::LEFT) || Input::get_singleton()->is_mouse_button_pressed(MouseButton::RIGHT)) {
291287
return;
292288
}
293289

294-
bool focused = has_focus();
290+
// Input::is_mouse_button_pressed is not sufficient to detect the mouse button state
291+
// while the floating game window is being resized.
292+
BitField<MouseButtonMask> mouse_button_mask = DisplayServer::get_singleton()->mouse_get_button_state();
293+
if (!mouse_button_mask.is_empty()) {
294+
return;
295+
}
295296

296297
// Not stealing focus from a textfield.
297-
if (!focused && get_viewport()->gui_get_focus_owner() && get_viewport()->gui_get_focus_owner()->is_text_field()) {
298+
if (get_viewport()->gui_get_focus_owner() && get_viewport()->gui_get_focus_owner()->is_text_field()) {
298299
return;
299300
}
300301

@@ -310,14 +311,17 @@ void EmbeddedProcess::_check_mouse_over() {
310311
return;
311312
}
312313

313-
// When we already have the focus and the user moves the mouse over the embedded process,
314-
// we just need to refocus the process.
315-
if (focused) {
316-
queue_update_embedded_process();
317-
} else {
318-
grab_focus();
319-
queue_redraw();
314+
// When there's a modal window, we don't want to grab the focus to prevent
315+
// the game window to go in front of the modal window.
316+
if (_get_current_modal_window()) {
317+
return;
320318
}
319+
320+
// Force "regrabbing" the game window focus.
321+
last_updated_embedded_process_focused = false;
322+
323+
grab_focus();
324+
queue_redraw();
321325
}
322326

323327
void EmbeddedProcess::_check_focused_process_id() {
@@ -326,19 +330,41 @@ void EmbeddedProcess::_check_focused_process_id() {
326330
focused_process_id = process_id;
327331
if (focused_process_id == current_process_id) {
328332
// The embedded process got the focus.
329-
emit_signal(SNAME("embedded_process_focused"));
330-
if (has_focus()) {
331-
// Redraw to updated the focus style.
332-
queue_redraw();
333+
334+
// Refocus the current model when focusing the embedded process.
335+
Window *modal_window = _get_current_modal_window();
336+
if (modal_window) {
337+
callable_mp(modal_window, &Window::grab_focus).call_deferred();
333338
} else {
334-
grab_focus();
339+
emit_signal(SNAME("embedded_process_focused"));
340+
if (has_focus()) {
341+
// Redraw to updated the focus style.
342+
queue_redraw();
343+
} else {
344+
grab_focus();
345+
}
335346
}
336347
} else if (has_focus()) {
337348
release_focus();
338349
}
339350
}
340351
}
341352

353+
Window *EmbeddedProcess::_get_current_modal_window() {
354+
Vector<DisplayServer::WindowID> wl = DisplayServer::get_singleton()->get_window_list();
355+
for (const DisplayServer::WindowID &window_id : wl) {
356+
Window *w = Window::get_from_id(window_id);
357+
if (!w) {
358+
continue;
359+
}
360+
361+
if (w->is_exclusive()) {
362+
return w;
363+
}
364+
}
365+
return nullptr;
366+
}
367+
342368
void EmbeddedProcess::_bind_methods() {
343369
ADD_SIGNAL(MethodInfo("embedding_completed"));
344370
ADD_SIGNAL(MethodInfo("embedding_failed"));

editor/plugins/embedded_process.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ class EmbeddedProcess : public Control {
3737
GDCLASS(EmbeddedProcess, Control);
3838

3939
bool application_has_focus = true;
40-
bool embedded_process_was_focused = false;
4140
OS::ProcessID focused_process_id = 0;
4241
OS::ProcessID current_process_id = 0;
4342
bool embedding_grab_focus = false;
@@ -66,6 +65,7 @@ class EmbeddedProcess : public Control {
6665
void _check_focused_process_id();
6766
bool _is_embedded_process_updatable();
6867
Rect2i _get_global_embedded_window_rect();
68+
Window *_get_current_modal_window();
6969

7070
protected:
7171
static void _bind_methods();

platform/windows/display_server_windows.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -2986,7 +2986,8 @@ Error DisplayServerWindows::embed_process(WindowID p_window, OS::ProcessID p_pid
29862986
// (e.g., a screen to the left of the main screen).
29872987
const Rect2i adjusted_rect = Rect2i(p_rect.position + _get_screens_origin(), p_rect.size);
29882988

2989-
SetWindowPos(ep->window_handle, nullptr, adjusted_rect.position.x, adjusted_rect.position.y, adjusted_rect.size.x, adjusted_rect.size.y, SWP_NOZORDER | SWP_NOACTIVATE | SWP_ASYNCWINDOWPOS);
2989+
// Use HWND_BOTTOM to prevent reordering of the embedded window over another popup.
2990+
SetWindowPos(ep->window_handle, HWND_BOTTOM, adjusted_rect.position.x, adjusted_rect.position.y, adjusted_rect.size.x, adjusted_rect.size.y, SWP_NOZORDER | SWP_NOACTIVATE | SWP_ASYNCWINDOWPOS);
29902991

29912992
if (ep->is_visible != p_visible) {
29922993
if (p_visible) {

0 commit comments

Comments
 (0)