diff --git a/editor/plugins/embedded_process.cpp b/editor/plugins/embedded_process.cpp index 1638c437125..a348607a6e9 100644 --- a/editor/plugins/embedded_process.cpp +++ b/editor/plugins/embedded_process.cpp @@ -67,20 +67,10 @@ void EmbeddedProcess::_notification(int p_what) { } break; case NOTIFICATION_APPLICATION_FOCUS_IN: { application_has_focus = true; - if (embedded_process_was_focused) { - embedded_process_was_focused = false; - // Refocus the embedded process if it was focused when the application lost focus, - // but do not refocus if the embedded process is currently focused (indicating it just lost focus) - // or if the current window is a different popup or secondary window. - if (embedding_completed && current_process_id != focused_process_id && window && window->has_focus()) { - grab_focus(); - queue_update_embedded_process(); - } - } + last_application_focus_time = OS::get_singleton()->get_ticks_msec(); } break; case NOTIFICATION_APPLICATION_FOCUS_OUT: { application_has_focus = false; - embedded_process_was_focused = embedding_completed && current_process_id == focused_process_id; } break; } } @@ -286,14 +276,27 @@ void EmbeddedProcess::_check_mouse_over() { // This method checks if the mouse is over the embedded process while the current application is focused. // The goal is to give focus to the embedded process as soon as the mouse hovers over it, // allowing the user to interact with it immediately without needing to click first. - 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)) { + 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)) { return; } - bool focused = has_focus(); + // Before checking whether the mouse is truly inside the embedded process, ensure + // the editor has enough time to re-render. When a breakpoint is hit in the script editor, + // `_check_mouse_over` may be triggered before the editor hides the game workspace. + // This prevents the embedded process from regaining focus immediately after the editor has taken it. + if (OS::get_singleton()->get_ticks_msec() - last_application_focus_time < 500) { + return; + } + + // Input::is_mouse_button_pressed is not sufficient to detect the mouse button state + // while the floating game window is being resized. + BitField mouse_button_mask = DisplayServer::get_singleton()->mouse_get_button_state(); + if (!mouse_button_mask.is_empty()) { + return; + } // Not stealing focus from a textfield. - if (!focused && get_viewport()->gui_get_focus_owner() && get_viewport()->gui_get_focus_owner()->is_text_field()) { + if (get_viewport()->gui_get_focus_owner() && get_viewport()->gui_get_focus_owner()->is_text_field()) { return; } @@ -309,14 +312,17 @@ void EmbeddedProcess::_check_mouse_over() { return; } - // When we already have the focus and the user moves the mouse over the embedded process, - // we just need to refocus the process. - if (focused) { - queue_update_embedded_process(); - } else { - grab_focus(); - queue_redraw(); + // When there's a modal window, we don't want to grab the focus to prevent + // the game window to go in front of the modal window. + if (_get_current_modal_window()) { + return; } + + // Force "regrabbing" the game window focus. + last_updated_embedded_process_focused = false; + + grab_focus(); + queue_redraw(); } void EmbeddedProcess::_check_focused_process_id() { @@ -325,17 +331,48 @@ void EmbeddedProcess::_check_focused_process_id() { focused_process_id = process_id; if (focused_process_id == current_process_id) { // The embedded process got the focus. - emit_signal(SNAME("embedded_process_focused")); - if (has_focus()) { - // Redraw to updated the focus style. - queue_redraw(); - } else { - grab_focus(); + + // Refocus the current model when focusing the embedded process. + Window *modal_window = _get_current_modal_window(); + if (!modal_window) { + emit_signal(SNAME("embedded_process_focused")); + if (has_focus()) { + // Redraw to updated the focus style. + queue_redraw(); + } else { + grab_focus(); + } } } else if (has_focus()) { release_focus(); } } + + // Ensure that the opened modal dialog is refocused when the focused process is the embedded process. + if (!application_has_focus && focused_process_id == current_process_id) { + Window *modal_window = _get_current_modal_window(); + if (modal_window) { + if (modal_window->get_mode() == Window::MODE_MINIMIZED) { + modal_window->set_mode(Window::MODE_WINDOWED); + } + callable_mp(modal_window, &Window::grab_focus).call_deferred(); + } + } +} + +Window *EmbeddedProcess::_get_current_modal_window() { + Vector wl = DisplayServer::get_singleton()->get_window_list(); + for (const DisplayServer::WindowID &window_id : wl) { + Window *w = Window::get_from_id(window_id); + if (!w) { + continue; + } + + if (w->is_exclusive()) { + return w; + } + } + return nullptr; } void EmbeddedProcess::_bind_methods() { diff --git a/editor/plugins/embedded_process.h b/editor/plugins/embedded_process.h index 3e800923a75..6f56f255e61 100644 --- a/editor/plugins/embedded_process.h +++ b/editor/plugins/embedded_process.h @@ -37,7 +37,7 @@ class EmbeddedProcess : public Control { GDCLASS(EmbeddedProcess, Control); bool application_has_focus = true; - bool embedded_process_was_focused = false; + uint64_t last_application_focus_time = 0; OS::ProcessID focused_process_id = 0; OS::ProcessID current_process_id = 0; bool embedding_grab_focus = false; @@ -68,6 +68,7 @@ class EmbeddedProcess : public Control { void _check_focused_process_id(); bool _is_embedded_process_updatable(); Rect2i _get_global_embedded_window_rect(); + Window *_get_current_modal_window(); protected: static void _bind_methods(); diff --git a/platform/windows/display_server_windows.cpp b/platform/windows/display_server_windows.cpp index d0ddac4b7f9..1a407bdd4b2 100644 --- a/platform/windows/display_server_windows.cpp +++ b/platform/windows/display_server_windows.cpp @@ -2986,7 +2986,8 @@ Error DisplayServerWindows::embed_process(WindowID p_window, OS::ProcessID p_pid // (e.g., a screen to the left of the main screen). const Rect2i adjusted_rect = Rect2i(p_rect.position + _get_screens_origin(), p_rect.size); - 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); + // Use HWND_BOTTOM to prevent reordering of the embedded window over another popup. + 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); if (ep->is_visible != p_visible) { if (p_visible) {