@@ -73,20 +73,10 @@ void EmbeddedProcess::_notification(int p_what) {
73
73
} break ;
74
74
case NOTIFICATION_APPLICATION_FOCUS_IN: {
75
75
application_has_focus = true ;
76
- if (embedded_process_was_focused) {
77
- embedded_process_was_focused = false ;
78
- // Refocus the embedded process if it was focused when the application lost focus,
79
- // but do not refocus if the embedded process is currently focused (indicating it just lost focus)
80
- // or if the current window is a different popup or secondary window.
81
- if (embedding_completed && current_process_id != focused_process_id && window && window->has_focus ()) {
82
- grab_focus ();
83
- queue_update_embedded_process ();
84
- }
85
- }
76
+ last_application_focus_time = OS::get_singleton ()->get_ticks_msec ();
86
77
} break ;
87
78
case NOTIFICATION_APPLICATION_FOCUS_OUT: {
88
79
application_has_focus = false ;
89
- embedded_process_was_focused = embedding_completed && current_process_id == focused_process_id;
90
80
} break ;
91
81
}
92
82
}
@@ -295,14 +285,27 @@ void EmbeddedProcess::_check_mouse_over() {
295
285
// This method checks if the mouse is over the embedded process while the current application is focused.
296
286
// The goal is to give focus to the embedded process as soon as the mouse hovers over it,
297
287
// allowing the user to interact with it immediately without needing to click first.
298
- 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)) {
288
+ 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)) {
289
+ return ;
290
+ }
291
+
292
+ // Before checking whether the mouse is truly inside the embedded process, ensure
293
+ // the editor has enough time to re-render. When a breakpoint is hit in the script editor,
294
+ // `_check_mouse_over` may be triggered before the editor hides the game workspace.
295
+ // This prevents the embedded process from regaining focus immediately after the editor has taken it.
296
+ if (OS::get_singleton ()->get_ticks_msec () - last_application_focus_time < 500 ) {
299
297
return ;
300
298
}
301
299
302
- bool focused = has_focus ();
300
+ // Input::is_mouse_button_pressed is not sufficient to detect the mouse button state
301
+ // while the floating game window is being resized.
302
+ BitField<MouseButtonMask> mouse_button_mask = DisplayServer::get_singleton ()->mouse_get_button_state ();
303
+ if (!mouse_button_mask.is_empty ()) {
304
+ return ;
305
+ }
303
306
304
307
// Not stealing focus from a textfield.
305
- if (!focused && get_viewport ()->gui_get_focus_owner () && get_viewport ()->gui_get_focus_owner ()->is_text_field ()) {
308
+ if (get_viewport ()->gui_get_focus_owner () && get_viewport ()->gui_get_focus_owner ()->is_text_field ()) {
306
309
return ;
307
310
}
308
311
@@ -318,14 +321,17 @@ void EmbeddedProcess::_check_mouse_over() {
318
321
return ;
319
322
}
320
323
321
- // When we already have the focus and the user moves the mouse over the embedded process,
322
- // we just need to refocus the process.
323
- if (focused) {
324
- queue_update_embedded_process ();
325
- } else {
326
- grab_focus ();
327
- queue_redraw ();
324
+ // When there's a modal window, we don't want to grab the focus to prevent
325
+ // the game window to go in front of the modal window.
326
+ if (_get_current_modal_window ()) {
327
+ return ;
328
328
}
329
+
330
+ // Force "regrabbing" the game window focus.
331
+ last_updated_embedded_process_focused = false ;
332
+
333
+ grab_focus ();
334
+ queue_redraw ();
329
335
}
330
336
331
337
void EmbeddedProcess::_check_focused_process_id () {
@@ -334,17 +340,48 @@ void EmbeddedProcess::_check_focused_process_id() {
334
340
focused_process_id = process_id;
335
341
if (focused_process_id == current_process_id) {
336
342
// The embedded process got the focus.
337
- emit_signal (SNAME (" embedded_process_focused" ));
338
- if (has_focus ()) {
339
- // Redraw to updated the focus style.
340
- queue_redraw ();
341
- } else {
342
- grab_focus ();
343
+
344
+ // Refocus the current model when focusing the embedded process.
345
+ Window *modal_window = _get_current_modal_window ();
346
+ if (!modal_window) {
347
+ emit_signal (SNAME (" embedded_process_focused" ));
348
+ if (has_focus ()) {
349
+ // Redraw to updated the focus style.
350
+ queue_redraw ();
351
+ } else {
352
+ grab_focus ();
353
+ }
343
354
}
344
355
} else if (has_focus ()) {
345
356
release_focus ();
346
357
}
347
358
}
359
+
360
+ // Ensure that the opened modal dialog is refocused when the focused process is the embedded process.
361
+ if (!application_has_focus && focused_process_id == current_process_id) {
362
+ Window *modal_window = _get_current_modal_window ();
363
+ if (modal_window) {
364
+ if (modal_window->get_mode () == Window::MODE_MINIMIZED) {
365
+ modal_window->set_mode (Window::MODE_WINDOWED);
366
+ }
367
+ callable_mp (modal_window, &Window::grab_focus).call_deferred ();
368
+ }
369
+ }
370
+ }
371
+
372
+ Window *EmbeddedProcess::_get_current_modal_window () {
373
+ Vector<DisplayServer::WindowID> wl = DisplayServer::get_singleton ()->get_window_list ();
374
+ for (const DisplayServer::WindowID &window_id : wl) {
375
+ Window *w = Window::get_from_id (window_id);
376
+ if (!w) {
377
+ continue ;
378
+ }
379
+
380
+ if (w->is_exclusive ()) {
381
+ return w;
382
+ }
383
+ }
384
+ return nullptr ;
348
385
}
349
386
350
387
void EmbeddedProcess::_bind_methods () {
0 commit comments