From 19c62f7e1731eeff51485da27558b5810218dd8f Mon Sep 17 00:00:00 2001
From: Markus Sauermann <6299227+Sauermann@users.noreply.github.com>
Date: Fri, 2 Jun 2023 21:12:45 +0200
Subject: [PATCH] Perform update_mouse_cursor_state at most once per frame.
Set a flag to perform the update once after input event processing.
Add a color picker unit-test for the related issue.
---
doc/classes/Viewport.xml | 2 +-
scene/main/window.cpp | 16 ++++++++-
scene/main/window.h | 2 ++
tests/scene/test_color_picker.h | 61 +++++++++++++++++++++++++++++++++
tests/test_main.cpp | 1 +
5 files changed, 80 insertions(+), 2 deletions(-)
create mode 100644 tests/scene/test_color_picker.h
diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml
index d363a115505a..fdd4306d13ee 100644
--- a/doc/classes/Viewport.xml
+++ b/doc/classes/Viewport.xml
@@ -217,7 +217,7 @@
- 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.
+ Force updating the display based on the current mouse cursor position at the end of the current frame. 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/main/window.cpp b/scene/main/window.cpp
index 26aa06e1a4d9..562bb68d697f 100644
--- a/scene/main/window.cpp
+++ b/scene/main/window.cpp
@@ -721,11 +721,16 @@ void Window::_event_callback(DisplayServer::WindowEvent p_event) {
void Window::update_mouse_cursor_state() {
ERR_MAIN_THREAD_GUARD;
- // Update states based on mouse cursor position.
+ // Flag the mouse cursor state to be updated at the end of the frame 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.
+
+ pending_mouse_cursor_state_update = true;
+}
+
+void Window::_update_mouse_cursor_state() {
Ref mm;
Vector2 pos = get_mouse_position();
Transform2D xform = get_global_canvas_transform().affine_inverse();
@@ -1274,6 +1279,14 @@ void Window::_notification(int p_what) {
RS::get_singleton()->viewport_set_active(get_viewport_rid(), false);
} break;
+
+ case NOTIFICATION_PROCESS: {
+ // TODO: remove this section and disable processing when update_mouse_cursor_state no longer creates InputEvents.
+ if (pending_mouse_cursor_state_update) {
+ _update_mouse_cursor_state();
+ pending_mouse_cursor_state_update = false;
+ }
+ } break;
}
}
@@ -2737,6 +2750,7 @@ Window::Window() {
max_size_used = max_size; // Update max_size_used.
}
+ set_process(true);
theme_owner = memnew(ThemeOwner);
RS::get_singleton()->viewport_set_update_mode(get_viewport_rid(), RS::VIEWPORT_UPDATE_DISABLED);
}
diff --git a/scene/main/window.h b/scene/main/window.h
index bf5d6a13ee45..73efe45a14d4 100644
--- a/scene/main/window.h
+++ b/scene/main/window.h
@@ -196,6 +196,8 @@ class Window : public Viewport {
virtual bool _can_consume_input_events() const override;
Ref debugger_stop_shortcut;
+ bool pending_mouse_cursor_state_update = false;
+ void _update_mouse_cursor_state();
protected:
virtual Rect2i _popup_adjust_rect() const { return Rect2i(); }
diff --git a/tests/scene/test_color_picker.h b/tests/scene/test_color_picker.h
new file mode 100644
index 000000000000..0c657a0167ac
--- /dev/null
+++ b/tests/scene/test_color_picker.h
@@ -0,0 +1,61 @@
+/**************************************************************************/
+/* test_color_picker.h */
+/**************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/**************************************************************************/
+
+#ifndef TEST_COLOR_PICKER_H
+#define TEST_COLOR_PICKER_H
+
+#include "scene/gui/color_picker.h"
+
+#include "tests/test_macros.h"
+
+namespace TestColorPicker {
+
+TEST_CASE("[SceneTree][ColorPicker]") {
+ ColorPicker *cp = memnew(ColorPicker);
+ Window *root = SceneTree::get_singleton()->get_root();
+ root->add_child(cp);
+
+ SUBCASE("[COLOR_PICKER] Mouse movement after Slider release") {
+ Point2i pos_left = Point2i(50, 340); // On the left side of the red slider.
+ Point2i pos_right = Point2i(200, 340); // On the right side of the red slider.
+ SEND_GUI_MOUSE_MOTION_EVENT(pos_left, MouseButtonMask::NONE, Key::NONE);
+ SEND_GUI_MOUSE_BUTTON_EVENT(pos_left, MouseButton::LEFT, MouseButtonMask::LEFT, Key::NONE);
+ CHECK(cp->get_pick_color().r < 0.5);
+ SEND_GUI_MOUSE_MOTION_EVENT(pos_right, MouseButtonMask::LEFT, Key::NONE);
+ CHECK(cp->get_pick_color().r > 0.5);
+ SEND_GUI_MOUSE_BUTTON_RELEASED_EVENT(pos_right, MouseButton::LEFT, MouseButtonMask::NONE, Key::NONE);
+ SEND_GUI_MOUSE_MOTION_EVENT(pos_left, MouseButtonMask::NONE, Key::NONE);
+ CHECK(cp->get_pick_color().r > 0.5); // Issue GH-77773.
+ }
+}
+
+} // namespace TestColorPicker
+
+#endif // TEST_COLOR_PICKER_H
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index 05d7df038cdc..ef071f41159f 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -91,6 +91,7 @@
#include "tests/scene/test_audio_stream_wav.h"
#include "tests/scene/test_bit_map.h"
#include "tests/scene/test_code_edit.h"
+#include "tests/scene/test_color_picker.h"
#include "tests/scene/test_curve.h"
#include "tests/scene/test_curve_2d.h"
#include "tests/scene/test_curve_3d.h"