|
@@ -67,20 +67,10 @@ void EmbeddedProcess::_notification(int p_what) {
|
|
} break;
|
|
} break;
|
|
case NOTIFICATION_APPLICATION_FOCUS_IN: {
|
|
case NOTIFICATION_APPLICATION_FOCUS_IN: {
|
|
application_has_focus = true;
|
|
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;
|
|
} break;
|
|
case NOTIFICATION_APPLICATION_FOCUS_OUT: {
|
|
case NOTIFICATION_APPLICATION_FOCUS_OUT: {
|
|
application_has_focus = false;
|
|
application_has_focus = false;
|
|
- embedded_process_was_focused = embedding_completed && current_process_id == focused_process_id;
|
|
|
|
} break;
|
|
} 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.
|
|
// 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,
|
|
// 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.
|
|
// 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;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // 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;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
- bool focused = has_focus();
|
|
|
|
|
|
+ // Input::is_mouse_button_pressed is not sufficient to detect the mouse button state
|
|
|
|
+ // while the floating game window is being resized.
|
|
|
|
+ BitField<MouseButtonMask> mouse_button_mask = DisplayServer::get_singleton()->mouse_get_button_state();
|
|
|
|
+ if (!mouse_button_mask.is_empty()) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
|
|
// Not stealing focus from a textfield.
|
|
// 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;
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -309,14 +312,17 @@ void EmbeddedProcess::_check_mouse_over() {
|
|
return;
|
|
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() {
|
|
void EmbeddedProcess::_check_focused_process_id() {
|
|
@@ -325,17 +331,48 @@ void EmbeddedProcess::_check_focused_process_id() {
|
|
focused_process_id = process_id;
|
|
focused_process_id = process_id;
|
|
if (focused_process_id == current_process_id) {
|
|
if (focused_process_id == current_process_id) {
|
|
// The embedded process got the focus.
|
|
// 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()) {
|
|
} else if (has_focus()) {
|
|
release_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<DisplayServer::WindowID> 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() {
|
|
void EmbeddedProcess::_bind_methods() {
|