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