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();