From 710b57d493bcb2ffaad50abe59c1da5b33888aab Mon Sep 17 00:00:00 2001 From: Ruslan Mustakov Date: Tue, 27 Feb 2018 22:37:20 +0700 Subject: [PATCH] Make BaseButton not emit press when container is scrolled This fixes the problem described in #13996 in a proper way. This also adds "deadzone" property to ScrollContainer. It can be used on mobile, where taps are not as precise as mouse clicks. Player could slightly move their finger when tapping, in which case we still want the button to be pressed rather than the container to be scrolled. (cherry picked from commit dcf5be92a3aede861909c08d06b114669bbe75e8) --- scene/gui/base_button.cpp | 2 +- scene/gui/control.cpp | 2 + scene/gui/control.h | 2 + scene/gui/scroll_container.cpp | 72 +++++++++++++++++++++++----------- scene/gui/scroll_container.h | 6 +++ 5 files changed, 60 insertions(+), 24 deletions(-) diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index d5dc5b3a530..8a0a40286d7 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -252,7 +252,7 @@ void BaseButton::_notification(int p_what) { status.hovering = false; update(); } - if (p_what == NOTIFICATION_DRAG_BEGIN) { + if (p_what == NOTIFICATION_DRAG_BEGIN || p_what == NOTIFICATION_SCROLL_BEGIN) { if (status.press_attempt) { status.press_attempt = false; diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index d8571a77672..7a77e63f92b 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -2884,6 +2884,8 @@ void Control::_bind_methods() { BIND_CONSTANT(NOTIFICATION_FOCUS_EXIT); BIND_CONSTANT(NOTIFICATION_THEME_CHANGED); BIND_CONSTANT(NOTIFICATION_MODAL_CLOSE); + BIND_CONSTANT(NOTIFICATION_SCROLL_BEGIN); + BIND_CONSTANT(NOTIFICATION_SCROLL_END); BIND_ENUM_CONSTANT(CURSOR_ARROW); BIND_ENUM_CONSTANT(CURSOR_IBEAM); diff --git a/scene/gui/control.h b/scene/gui/control.h index 51325f27b5a..75813ce8218 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -271,6 +271,8 @@ public: NOTIFICATION_FOCUS_EXIT = 44, NOTIFICATION_THEME_CHANGED = 45, NOTIFICATION_MODAL_CLOSE = 46, + NOTIFICATION_SCROLL_BEGIN = 47, + NOTIFICATION_SCROLL_END = 48, }; diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index 33b3d464864..217df275b93 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -75,6 +75,12 @@ void ScrollContainer::_cancel_drag() { drag_accum = Vector2(); last_drag_accum = Vector2(); drag_from = Vector2(); + + if (beyond_deadzone) { + emit_signal("scroll_ended"); + propagate_notification(NOTIFICATION_SCROLL_END); + beyond_deadzone = false; + } } void ScrollContainer::_gui_input(const Ref &p_gui_input) { @@ -122,13 +128,7 @@ void ScrollContainer::_gui_input(const Ref &p_gui_input) { if (mb->is_pressed()) { if (drag_touching) { - set_physics_process(false); - drag_touching_deaccel = false; - drag_touching = false; - drag_speed = Vector2(); - drag_accum = Vector2(); - last_drag_accum = Vector2(); - drag_from = Vector2(); + _cancel_drag(); } if (true) { @@ -138,6 +138,7 @@ void ScrollContainer::_gui_input(const Ref &p_gui_input) { drag_from = Vector2(h_scroll->get_value(), v_scroll->get_value()); drag_touching = OS::get_singleton()->has_touchscreen_ui_hint(); drag_touching_deaccel = false; + beyond_deadzone = false; time_since_motion = 0; if (drag_touching) { set_physics_process(true); @@ -149,9 +150,7 @@ void ScrollContainer::_gui_input(const Ref &p_gui_input) { if (drag_touching) { if (drag_speed == Vector2()) { - drag_touching_deaccel = false; - drag_touching = false; - set_physics_process(false); + _cancel_drag(); } else { drag_touching_deaccel = true; @@ -168,17 +167,27 @@ void ScrollContainer::_gui_input(const Ref &p_gui_input) { Vector2 motion = Vector2(mm->get_relative().x, mm->get_relative().y); drag_accum -= motion; - Vector2 diff = drag_from + drag_accum; - if (scroll_h) - h_scroll->set_value(diff.x); - else - drag_accum.x = 0; - if (scroll_v) - v_scroll->set_value(diff.y); - else - drag_accum.y = 0; - time_since_motion = 0; + if (beyond_deadzone || scroll_h && Math::abs(drag_accum.x) > deadzone || scroll_v && Math::abs(drag_accum.y) > deadzone) { + if (!beyond_deadzone) { + propagate_notification(NOTIFICATION_SCROLL_BEGIN); + emit_signal("scroll_started"); + + beyond_deadzone = true; + // resetting drag_accum here ensures smooth scrolling after reaching deadzone + drag_accum = -motion; + } + Vector2 diff = drag_from + drag_accum; + if (scroll_h) + h_scroll->set_value(diff.x); + else + drag_accum.x = 0; + if (scroll_v) + v_scroll->set_value(diff.y); + else + drag_accum.y = 0; + time_since_motion = 0; + } } } @@ -323,9 +332,7 @@ void ScrollContainer::_notification(int p_what) { drag_speed = Vector2(sgn_x * val_x, sgn_y * val_y); if (turnoff_h && turnoff_v) { - set_physics_process(false); - drag_touching = false; - drag_touching_deaccel = false; + _cancel_drag(); } } else { @@ -430,6 +437,14 @@ void ScrollContainer::set_h_scroll(int p_pos) { _cancel_drag(); } +int ScrollContainer::get_deadzone() const { + return deadzone; +} + +void ScrollContainer::set_deadzone(int p_deadzone) { + deadzone = p_deadzone; +} + String ScrollContainer::get_configuration_warning() const { int found = 0; @@ -466,12 +481,20 @@ void ScrollContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_h_scroll"), &ScrollContainer::get_h_scroll); ClassDB::bind_method(D_METHOD("set_v_scroll", "value"), &ScrollContainer::set_v_scroll); ClassDB::bind_method(D_METHOD("get_v_scroll"), &ScrollContainer::get_v_scroll); + ClassDB::bind_method(D_METHOD("set_deadzone", "deadzone"), &ScrollContainer::set_deadzone); + ClassDB::bind_method(D_METHOD("get_deadzone"), &ScrollContainer::get_deadzone); + + ADD_SIGNAL(MethodInfo("scroll_started")); + ADD_SIGNAL(MethodInfo("scroll_ended")); ADD_GROUP("Scroll", "scroll_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_horizontal_enabled"), "set_enable_h_scroll", "is_h_scroll_enabled"); ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_horizontal"), "set_h_scroll", "get_h_scroll"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "scroll_vertical_enabled"), "set_enable_v_scroll", "is_v_scroll_enabled"); ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_vertical"), "set_v_scroll", "get_v_scroll"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_deadzone"), "set_deadzone", "get_deadzone"); + + GLOBAL_DEF("gui/common/default_scroll_deadzone", 0); }; ScrollContainer::ScrollContainer() { @@ -490,8 +513,11 @@ ScrollContainer::ScrollContainer() { drag_speed = Vector2(); drag_touching = false; drag_touching_deaccel = false; + beyond_deadzone = false; scroll_h = true; scroll_v = true; + deadzone = GLOBAL_GET("gui/common/default_scroll_deadzone"); + set_clip_contents(true); }; diff --git a/scene/gui/scroll_container.h b/scene/gui/scroll_container.h index 6e3387918b9..3fe1ed447a7 100644 --- a/scene/gui/scroll_container.h +++ b/scene/gui/scroll_container.h @@ -56,10 +56,13 @@ class ScrollContainer : public Container { bool drag_touching; bool drag_touching_deaccel; bool click_handled; + bool beyond_deadzone; bool scroll_h; bool scroll_v; + int deadzone; + void _cancel_drag(); protected: @@ -86,6 +89,9 @@ public: void set_enable_v_scroll(bool p_enable); bool is_v_scroll_enabled() const; + int get_deadzone() const; + void set_deadzone(int p_deadzone); + virtual bool clips_input() const; virtual String get_configuration_warning() const;