diff --git a/doc/classes/Window.xml b/doc/classes/Window.xml index 9c0e8011dc28..99906bf5f5ca 100644 --- a/doc/classes/Window.xml +++ b/doc/classes/Window.xml @@ -646,6 +646,10 @@ [b]Note:[/b] On Windows, the portion of a window that lies outside the region is not drawn, while on Linux (X11) and macOS it is. [b]Note:[/b] This property is implemented on Linux (X11), macOS and Windows. + + If [code]false[/code], only a single focused [Control] is allowed in [Viewport] nodes within this window. If [code]true[/code], every [Viewport] in this window keeps an independent state of their own focused [Control] node. + This setting affects only the window itself and child [SubViewport] nodes. Child [Window] nodes and their child nodes aren't affected by this setting. This is useful for splitscreen input handling. + If [code]true[/code], the [Window] will be considered a popup. Popups are sub-windows that don't show as separate windows in system's window manager's window list and will send close request when anything is clicked outside of them (unless [member exclusive] is enabled). diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index c8d2d71c2afa..d5e4dd9c4c78 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -2509,11 +2509,34 @@ bool Viewport::_gui_control_has_focus(const Control *p_control) { } void Viewport::_gui_control_grab_focus(Control *p_control) { - if (gui.key_focus && gui.key_focus == p_control) { - // No need for change. - return; + Window *base_window = get_base_window(); + if (base_window->is_multi_viewport_focus_enabled()) { + // Release previous focus. + if (gui.key_focus && gui.key_focus != p_control) { + gui_release_focus(); + } + + // Ensure chain of SubViewportContainer focus in parent Viewport. Needs to happen, even if control has focus in this Viewport. + Viewport *vp = this; + if (Object::cast_to(vp)) { + SubViewportContainer *svc = Object::cast_to(get_parent()); + if (svc) { + svc->grab_focus(); + } + } + if (gui.key_focus == p_control) { + // No need for change. + return; + } + } else { + if (gui.key_focus && gui.key_focus == p_control) { + // No need for change. + return; + } + get_tree()->call_group("_viewports", "_gui_remove_focus_for_window", (Node *)base_window); } - get_tree()->call_group("_viewports", "_gui_remove_focus_for_window", (Node *)get_base_window()); + + // Perform necessary adjustments for focuschange. if (p_control->is_inside_tree() && p_control->get_viewport() == this) { gui.key_focus = p_control; emit_signal(SNAME("gui_focus_changed"), p_control); diff --git a/scene/main/window.cpp b/scene/main/window.cpp index 6632dd903394..eec55de13d6b 100644 --- a/scene/main/window.cpp +++ b/scene/main/window.cpp @@ -2749,6 +2749,16 @@ void Window::_mouse_leave_viewport() { } } +void Window::set_multi_viewport_focus(bool p_enable) { + ERR_MAIN_THREAD_GUARD; + multi_viewport_focus = p_enable; +} + +bool Window::is_multi_viewport_focus_enabled() const { + ERR_READ_THREAD_GUARD_V(false); + return multi_viewport_focus; +} + void Window::_bind_methods() { ClassDB::bind_method(D_METHOD("set_title", "title"), &Window::set_title); ClassDB::bind_method(D_METHOD("get_title"), &Window::get_title); @@ -2903,6 +2913,9 @@ void Window::_bind_methods() { ClassDB::bind_method(D_METHOD("get_layout_direction"), &Window::get_layout_direction); ClassDB::bind_method(D_METHOD("is_layout_rtl"), &Window::is_layout_rtl); + ClassDB::bind_method(D_METHOD("set_multi_viewport_focus", "enable"), &Window::set_multi_viewport_focus); + ClassDB::bind_method(D_METHOD("is_multi_viewport_focus_enabled"), &Window::is_multi_viewport_focus_enabled); + #ifndef DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("set_auto_translate", "enable"), &Window::set_auto_translate); ClassDB::bind_method(D_METHOD("is_auto_translating"), &Window::is_auto_translating); @@ -2961,6 +2974,8 @@ void Window::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "content_scale_stretch", PROPERTY_HINT_ENUM, "Fractional,Integer"), "set_content_scale_stretch", "get_content_scale_stretch"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "content_scale_factor", PROPERTY_HINT_RANGE, "0.5,8.0,0.01"), "set_content_scale_factor", "get_content_scale_factor"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "multi_viewport_focus"), "set_multi_viewport_focus", "is_multi_viewport_focus_enabled"); + #ifndef DISABLE_DEPRECATED ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_translate", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), "set_auto_translate", "is_auto_translating"); #endif diff --git a/scene/main/window.h b/scene/main/window.h index ffcf50ccdd5f..e226f892656d 100644 --- a/scene/main/window.h +++ b/scene/main/window.h @@ -135,6 +135,8 @@ class Window : public Viewport { bool unparent_when_invisible = false; bool keep_title_visible = false; + bool multi_viewport_focus = false; + LayoutDirection layout_dir = LAYOUT_DIRECTION_INHERITED; void _update_child_controls(); @@ -474,6 +476,9 @@ class Window : public Viewport { virtual bool is_directly_attached_to_screen() const override; virtual bool is_attached_in_viewport() const override; + void set_multi_viewport_focus(bool p_enable); + bool is_multi_viewport_focus_enabled() const; + Rect2i get_parent_rect() const; virtual DisplayServer::WindowID get_window_id() const override;