From ce10ca69794900896a4162efc823386ce5bde3dd Mon Sep 17 00:00:00 2001
From: Markus Sauermann <6299227+Sauermann@users.noreply.github.com>
Date: Thu, 29 Sep 2022 23:45:51 +0200
Subject: [PATCH] Create a virtual mouse move event after moving child nodes
This updates mouse cursor and mouse-over-states without the need
for additional mouse movements.
---
doc/classes/Viewport.xml | 6 ++++++
scene/gui/control.cpp | 3 ++-
scene/main/node.cpp | 8 ++++++++
scene/main/scene_tree.cpp | 3 ++-
scene/main/viewport.cpp | 12 +++++++++++-
scene/main/viewport.h | 1 +
scene/main/window.cpp | 9 ++++++---
scene/main/window.h | 2 +-
8 files changed, 37 insertions(+), 7 deletions(-)
diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml
index caab6ee9244..20a1424f418 100644
--- a/doc/classes/Viewport.xml
+++ b/doc/classes/Viewport.xml
@@ -212,6 +212,12 @@
Sets the number of subdivisions to use in the specified quadrant. A higher number of subdivisions allows you to have more shadows in the scene at once, but reduces the quality of the shadows. A good practice is to have quadrants with a varying number of subdivisions and to have as few subdivisions as possible.
+
+
+
+ Force instantly updating the display based on the current mouse cursor position. This includes updating the mouse cursor shape and sending necessary [signal Control.mouse_entered], [signal CollisionObject2D.mouse_entered], [signal CollisionObject3D.mouse_entered] and [signal Window.mouse_entered] signals and their respective [code]mouse_exited[/code] counterparts.
+
+
diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp
index 39bfa556e51..3beaf83f6f3 100644
--- a/scene/gui/control.cpp
+++ b/scene/gui/control.cpp
@@ -2400,7 +2400,8 @@ void Control::set_default_cursor_shape(CursorShape p_shape) {
return;
}
- get_viewport()->get_base_window()->update_mouse_cursor_shape();
+ // Display the new cursor shape instantly.
+ get_viewport()->update_mouse_cursor_state();
}
Control::CursorShape Control::get_default_cursor_shape() const {
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index ad7e445b5cb..264a152392e 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -216,6 +216,14 @@ void Node::_notification(int p_notification) {
memdelete(child);
}
} break;
+
+ case NOTIFICATION_CHILD_ORDER_CHANGED: {
+ // The order, in which canvas items are drawn gets rearranged.
+ // This makes it necessary to update mouse cursor and send according mouse_enter/mouse_exit signals for Control nodes.
+ if (get_viewport()) {
+ get_viewport()->update_mouse_cursor_state();
+ }
+ } break;
}
}
diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp
index 892cbb313b4..b18369dd11f 100644
--- a/scene/main/scene_tree.cpp
+++ b/scene/main/scene_tree.cpp
@@ -1395,7 +1395,8 @@ void SceneTree::_change_scene(Node *p_to) {
if (p_to) {
current_scene = p_to;
root->add_child(p_to);
- root->update_mouse_cursor_shape();
+ // Update display for cursor instantly.
+ root->update_mouse_cursor_state();
}
}
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index 532b431b06b..db93c98184f 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -2294,7 +2294,8 @@ void Viewport::_perform_drop(Control *p_control, Point2 p_pos) {
gui.dragging = false;
gui.drag_mouse_over = nullptr;
_propagate_viewport_notification(this, NOTIFICATION_DRAG_END);
- get_base_window()->update_mouse_cursor_shape();
+ // Display the new cursor shape instantly.
+ update_mouse_cursor_state();
}
void Viewport::_gui_cleanup_internal_state(Ref p_event) {
@@ -3535,6 +3536,14 @@ Transform2D Viewport::get_screen_transform_internal(bool p_absolute_position) co
return get_final_transform();
}
+void Viewport::update_mouse_cursor_state() {
+ // Updates need to happen in Window, because SubViewportContainers might be hidden behind other Controls.
+ Window *base_window = get_base_window();
+ if (base_window) {
+ base_window->update_mouse_cursor_state();
+ }
+}
+
void Viewport::set_canvas_cull_mask(uint32_t p_canvas_cull_mask) {
ERR_MAIN_THREAD_GUARD;
canvas_cull_mask = p_canvas_cull_mask;
@@ -4137,6 +4146,7 @@ void Viewport::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_mouse_position"), &Viewport::get_mouse_position);
ClassDB::bind_method(D_METHOD("warp_mouse", "position"), &Viewport::warp_mouse);
+ ClassDB::bind_method(D_METHOD("update_mouse_cursor_state"), &Viewport::update_mouse_cursor_state);
ClassDB::bind_method(D_METHOD("gui_get_drag_data"), &Viewport::gui_get_drag_data);
ClassDB::bind_method(D_METHOD("gui_is_dragging"), &Viewport::gui_is_dragging);
diff --git a/scene/main/viewport.h b/scene/main/viewport.h
index 7f93b21eedc..8870ed6ecae 100644
--- a/scene/main/viewport.h
+++ b/scene/main/viewport.h
@@ -582,6 +582,7 @@ public:
Vector2 get_mouse_position() const;
void warp_mouse(const Vector2 &p_position);
+ virtual void update_mouse_cursor_state();
void set_physics_object_picking(bool p_enable);
bool get_physics_object_picking();
diff --git a/scene/main/window.cpp b/scene/main/window.cpp
index 9f4ad88e649..26aa06e1a4d 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -719,10 +719,13 @@ void Window::_event_callback(DisplayServer::WindowEvent p_event) {
}
}
-void Window::update_mouse_cursor_shape() {
+void Window::update_mouse_cursor_state() {
ERR_MAIN_THREAD_GUARD;
- // The default shape is set in Viewport::_gui_input_event. To instantly
- // see the shape in the viewport we need to trigger a mouse motion event.
+ // Update states based on mouse cursor position.
+ // This includes updated mouse_enter or mouse_exit signals or the current mouse cursor shape.
+ // These details are set in Viewport::_gui_input_event. To instantly
+ // see the changes in the viewport, we need to trigger a mouse motion event.
+ // This function should be called whenever scene tree changes affect the mouse cursor.
Ref mm;
Vector2 pos = get_mouse_position();
Transform2D xform = get_global_canvas_transform().affine_inverse();
diff --git a/scene/main/window.h b/scene/main/window.h
index 5fce251e33a..bf5d6a13ee4 100644
--- a/scene/main/window.h
+++ b/scene/main/window.h
@@ -261,7 +261,7 @@ public:
void set_visible(bool p_visible);
bool is_visible() const;
- void update_mouse_cursor_shape();
+ void update_mouse_cursor_state() override;
void show();
void hide();