Jelajahi Sumber

Make `SubViewportContainer` event propagation aware of focused Control

Propagate `InputEvents` without position either
- during the Input-stage, when it has focus or
- during the Unhandled-Input-stage, otherwise

This makes sure that Gui-Input happens in the correct order.

Move "Unhandled key Input" handling before "Unhandled Input" handling,
so that Unicode input with Alt / Ctrl modifiers are sent to the nodes
in the correct order.

Change the default focus mode of `SubViewportContainer` from
`FOCUS_NONE` to `FOCUS_CLICK`, to automatically remove focus from
other `Control`-nodes, when clicking inside a `SubViewport`.
Markus Sauermann 2 tahun lalu
induk
melakukan
e6386bc843

+ 1 - 0
doc/classes/SubViewportContainer.xml

@@ -11,6 +11,7 @@
 	<tutorials>
 	</tutorials>
 	<members>
+		<member name="focus_mode" type="int" setter="set_focus_mode" getter="get_focus_mode" overrides="Control" enum="Control.FocusMode" default="1" />
 		<member name="stretch" type="bool" setter="set_stretch" getter="is_stretch_enabled" default="false">
 			If [code]true[/code], the sub-viewport will be automatically resized to the control's size.
 			[b]Note:[/b] If [code]true[/code], this will prohibit changing [member SubViewport.size] of its children manually.

+ 2 - 2
doc/classes/Viewport.xml

@@ -160,8 +160,8 @@
 				- [method Node._input]
 				- [method Control._gui_input] for [Control] nodes
 				- [method Node._shortcut_input]
-				- [method Node._unhandled_input]
 				- [method Node._unhandled_key_input]
+				- [method Node._unhandled_input]
 				If an earlier method marks the input as handled via [method set_input_as_handled], any later method in this list will not be called.
 				If none of the methods handle the event and [member physics_object_picking] is [code]true[/code], the event is used for physics object picking.
 			</description>
@@ -183,8 +183,8 @@
 				While this method serves a similar purpose as [method Input.parse_input_event], it does not remap the specified [param event] based on project settings like [member ProjectSettings.input_devices/pointing/emulate_touch_from_mouse].
 				Calling this method will propagate calls to child nodes for following methods in the given order:
 				- [method Node._shortcut_input]
-				- [method Node._unhandled_input]
 				- [method Node._unhandled_key_input]
+				- [method Node._unhandled_input]
 				If an earlier method marks the input as handled via [method set_input_as_handled], any later method in this list will not be called.
 				If none of the methods handle the event and [member physics_object_picking] is [code]true[/code], the event is used for physics object picking.
 				[b]Note:[/b] This method doesn't propagate input events to embedded [Window]s or [SubViewport]s.

+ 22 - 1
scene/gui/subviewport_container.cpp

@@ -154,6 +154,18 @@ void SubViewportContainer::_notification(int p_what) {
 		case NOTIFICATION_MOUSE_EXIT: {
 			_notify_viewports(NOTIFICATION_VP_MOUSE_EXIT);
 		} break;
+
+		case NOTIFICATION_FOCUS_ENTER: {
+			// If focused, send InputEvent to the SubViewport before the Gui-Input stage.
+			set_process_input(true);
+			set_process_unhandled_input(false);
+		} break;
+
+		case NOTIFICATION_FOCUS_EXIT: {
+			// A different Control has focus and should receive Gui-Input before the InputEvent is sent to the SubViewport.
+			set_process_input(false);
+			set_process_unhandled_input(true);
+		} break;
 	}
 }
 
@@ -168,6 +180,14 @@ void SubViewportContainer::_notify_viewports(int p_notification) {
 }
 
 void SubViewportContainer::input(const Ref<InputEvent> &p_event) {
+	_propagate_nonpositional_event(p_event);
+}
+
+void SubViewportContainer::unhandled_input(const Ref<InputEvent> &p_event) {
+	_propagate_nonpositional_event(p_event);
+}
+
+void SubViewportContainer::_propagate_nonpositional_event(const Ref<InputEvent> &p_event) {
 	ERR_FAIL_COND(p_event.is_null());
 
 	if (Engine::get_singleton()->is_editor_hint()) {
@@ -262,5 +282,6 @@ void SubViewportContainer::_bind_methods() {
 }
 
 SubViewportContainer::SubViewportContainer() {
-	set_process_input(true);
+	set_process_unhandled_input(true);
+	set_focus_mode(FOCUS_CLICK);
 }

+ 2 - 0
scene/gui/subviewport_container.h

@@ -41,6 +41,7 @@ class SubViewportContainer : public Container {
 	void _notify_viewports(int p_notification);
 	bool _is_propagated_in_gui_input(const Ref<InputEvent> &p_event);
 	void _send_event_to_viewports(const Ref<InputEvent> &p_event);
+	void _propagate_nonpositional_event(const Ref<InputEvent> &p_event);
 
 protected:
 	void _notification(int p_what);
@@ -54,6 +55,7 @@ public:
 	bool is_stretch_enabled() const;
 
 	virtual void input(const Ref<InputEvent> &p_event) override;
+	virtual void unhandled_input(const Ref<InputEvent> &p_event) override;
 	virtual void gui_input(const Ref<InputEvent> &p_event) override;
 	void set_stretch_shrink(int p_shrink);
 	int get_stretch_shrink() const;

+ 6 - 6
scene/main/viewport.cpp

@@ -3025,18 +3025,18 @@ void Viewport::_push_unhandled_input_internal(const Ref<InputEvent> &p_event) {
 		get_tree()->_call_input_pause(shortcut_input_group, SceneTree::CALL_INPUT_TYPE_SHORTCUT_INPUT, p_event, this);
 	}
 
-	// Unhandled Input.
-	if (!is_input_handled()) {
-		ERR_FAIL_COND(!is_inside_tree());
-		get_tree()->_call_input_pause(unhandled_input_group, SceneTree::CALL_INPUT_TYPE_UNHANDLED_INPUT, p_event, this);
-	}
-
 	// Unhandled key Input - Used for performance reasons - This is called a lot less than _unhandled_input since it ignores MouseMotion, and to handle Unicode input with Alt / Ctrl modifiers after handling shortcuts.
 	if (!is_input_handled() && (Object::cast_to<InputEventKey>(*p_event) != nullptr)) {
 		ERR_FAIL_COND(!is_inside_tree());
 		get_tree()->_call_input_pause(unhandled_key_input_group, SceneTree::CALL_INPUT_TYPE_UNHANDLED_KEY_INPUT, p_event, this);
 	}
 
+	// Unhandled Input.
+	if (!is_input_handled()) {
+		ERR_FAIL_COND(!is_inside_tree());
+		get_tree()->_call_input_pause(unhandled_input_group, SceneTree::CALL_INPUT_TYPE_UNHANDLED_INPUT, p_event, this);
+	}
+
 	if (physics_object_picking && !is_input_handled()) {
 		if (Input::get_singleton()->get_mouse_mode() != Input::MOUSE_MODE_CAPTURED &&
 				(Object::cast_to<InputEventMouse>(*p_event) ||