Browse Source

Make smooth scrolling behavior configurable for the context

Michael Ragazzon 2 years ago
parent
commit
3ea69d86bd

+ 1 - 0
CMake/FileList.cmake

@@ -192,6 +192,7 @@ set(Core_PUB_HDR_FILES
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/PropertySpecification.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/PropertySpecification.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/RenderInterface.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/RenderInterface.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ScriptInterface.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ScriptInterface.h
+    ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/ScrollTypes.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Spritesheet.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Spritesheet.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Stream.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/Stream.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/StreamMemory.h
     ${PROJECT_SOURCE_DIR}/Include/RmlUi/Core/StreamMemory.h

+ 7 - 1
Include/RmlUi/Core/Context.h

@@ -34,6 +34,7 @@
 #include "Traits.h"
 #include "Traits.h"
 #include "Input.h"
 #include "Input.h"
 #include "ScriptInterface.h"
 #include "ScriptInterface.h"
+#include "ScrollTypes.h"
 
 
 namespace Rml {
 namespace Rml {
 
 
@@ -230,6 +231,11 @@ public:
 	/// @return True if the mouse hovers over or has activated an element in this context, otherwise false.
 	/// @return True if the mouse hovers over or has activated an element in this context, otherwise false.
 	bool IsMouseInteracting() const;
 	bool IsMouseInteracting() const;
 
 
+	/// Sets the default scroll behavior, such as for mouse wheel processing and scrollbar interaction.
+	/// @param[in] scroll_behavior The default smooth scroll behavior, set to instant to disable smooth scrolling.
+	/// @param[in] speed_factor A factor for adjusting the final smooth scrolling speed, must be strictly positive, defaults to 1.0.
+	void SetDefaultScrollBehavior(ScrollBehavior scroll_behavior, float speed_factor);
+
 	/// Gets the context's render interface.
 	/// Gets the context's render interface.
 	/// @return The render interface the context renders through.
 	/// @return The render interface the context renders through.
 	RenderInterface* GetRenderInterface() const;
 	RenderInterface* GetRenderInterface() const;
@@ -372,7 +378,7 @@ private:
 	void ReleaseDragClone();
 	void ReleaseDragClone();
 
 
 	// Scroll the target by the given amount, using smooth scrolling.
 	// Scroll the target by the given amount, using smooth scrolling.
-	void PerformSmoothscrollOnTarget(Element* target, Vector2f delta_offset);
+	void PerformSmoothscrollOnTarget(Element* target, Vector2f delta_offset, ScrollBehavior scroll_behavior);
 
 
 	// Returns the data model with the provided name, or nullptr if it does not exist.
 	// Returns the data model with the provided name, or nullptr if it does not exist.
 	DataModel* GetDataModelPtr(const String& name) const;
 	DataModel* GetDataModelPtr(const String& name) const;

+ 2 - 24
Include/RmlUi/Core/Element.h

@@ -36,6 +36,7 @@
 #include "ObserverPtr.h"
 #include "ObserverPtr.h"
 #include "Property.h"
 #include "Property.h"
 #include "ScriptInterface.h"
 #include "ScriptInterface.h"
+#include "ScrollTypes.h"
 #include "StyleTypes.h"
 #include "StyleTypes.h"
 #include "Transform.h"
 #include "Transform.h"
 #include "Tween.h"
 #include "Tween.h"
@@ -66,29 +67,6 @@ class TransformState;
 struct ElementMeta;
 struct ElementMeta;
 struct StackingOrderedChild;
 struct StackingOrderedChild;
 
 
-enum class ScrollBehavior {
-	Auto,    // Same as Instant.
-	Smooth,  // Scroll to the destination using a smooth animation.
-	Instant, // Scroll to the destination instantly.
-};
-enum class ScrollAlignment {
-	Start,   // Align to the top or left edge of the parent element.
-	Center,  // Align to the center of the parent element.
-	End,     // Align to the bottom or right edge of the parent element.
-	Nearest, // Align with minimal scroll change.
-};
-/**
-	Defines behavior of Element::ScrollIntoView.
- */
-struct ScrollIntoViewOptions {
-	ScrollIntoViewOptions(ScrollAlignment vertical = ScrollAlignment::Start, ScrollAlignment horizontal = ScrollAlignment::Nearest,
-		ScrollBehavior behavior = ScrollBehavior::Auto) : vertical(vertical), horizontal(horizontal), behavior(behavior)
-	{}
-	ScrollAlignment vertical;
-	ScrollAlignment horizontal;
-	ScrollBehavior behavior;
-};
-
 /**
 /**
 	A generic element in the DOM tree.
 	A generic element in the DOM tree.
 
 
@@ -532,7 +510,7 @@ public:
 	/// @param[in] position The scroll destination coordinates.
 	/// @param[in] position The scroll destination coordinates.
 	/// @param[in] behavior Smooth scrolling behavior.
 	/// @param[in] behavior Smooth scrolling behavior.
 	/// @note Smooth scrolling can only be applied to a single element at a time, any active smooth scrolls will be cancelled.
 	/// @note Smooth scrolling can only be applied to a single element at a time, any active smooth scrolls will be cancelled.
-	void ScrollTo(Vector2f offset, ScrollBehavior behavior = ScrollBehavior::Auto);
+	void ScrollTo(Vector2f offset, ScrollBehavior behavior = ScrollBehavior::Instant);
 
 
 	/// Append a child to this element.
 	/// Append a child to this element.
 	/// @param[in] element The element to append as a child.
 	/// @param[in] element The element to append as a child.

+ 63 - 0
Include/RmlUi/Core/ScrollTypes.h

@@ -0,0 +1,63 @@
+/*
+ * This source file is part of RmlUi, the HTML/CSS Interface Middleware
+ *
+ * For the latest information, see http://github.com/mikke89/RmlUi
+ *
+ * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
+ * Copyright (c) 2019 The RmlUi Team, and contributors
+ *
+ * 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 RMLUI_CORE_SCROLLTYPES_H
+#define RMLUI_CORE_SCROLLTYPES_H
+
+namespace Rml {
+
+enum class ScrollBehavior {
+	Auto,    // Scroll using the context's configured setting.
+	Smooth,  // Scroll using a smooth animation.
+	Instant, // Scroll instantly.
+};
+
+enum class ScrollAlignment {
+	Start,   // Align to the top or left edge of the parent element.
+	Center,  // Align to the center of the parent element.
+	End,     // Align to the bottom or right edge of the parent element.
+	Nearest, // Align with minimal scroll change.
+};
+
+/**
+    Defines behavior of Element::ScrollIntoView.
+ */
+struct ScrollIntoViewOptions {
+	ScrollIntoViewOptions(ScrollAlignment vertical = ScrollAlignment::Start, ScrollAlignment horizontal = ScrollAlignment::Nearest,
+		ScrollBehavior behavior = ScrollBehavior::Instant) :
+		horizontal(horizontal),
+		vertical(vertical), behavior(behavior)
+	{}
+	ScrollAlignment vertical;
+	ScrollAlignment horizontal;
+	ScrollBehavior behavior;
+};
+
+} // namespace Rml
+
+#endif

+ 13 - 7
Source/Core/Context.cpp

@@ -848,13 +848,15 @@ bool Context::ProcessMouseWheel(float wheel_delta, int key_modifier_state)
 	if (!hover->DispatchEvent(EventId::Mousescroll, scroll_parameters))
 	if (!hover->DispatchEvent(EventId::Mousescroll, scroll_parameters))
 		return false;
 		return false;
 
 
-	if (scroll_controller->GetMode() != ScrollController::Mode::Smoothscroll)
-		scroll_controller->ActivateSmoothscroll(hover->GetClosestScrollableContainer());
-
 	const float unit_scroll_length = UNIT_SCROLL_LENGTH * density_independent_pixel_ratio;
 	const float unit_scroll_length = UNIT_SCROLL_LENGTH * density_independent_pixel_ratio;
 	const Vector2f scroll_length = {0.f, wheel_delta * unit_scroll_length};
 	const Vector2f scroll_length = {0.f, wheel_delta * unit_scroll_length};
+	Element* target = hover->GetClosestScrollableContainer();
+
+	if (scroll_controller->GetMode() == ScrollController::Mode::Smoothscroll && scroll_controller->GetTarget() == target)
+		scroll_controller->IncrementSmoothscrollTarget(scroll_length);
+	else
+		scroll_controller->ActivateSmoothscroll(target, scroll_length, ScrollBehavior::Auto);
 
 
-	scroll_controller->IncrementSmoothscrollTarget(scroll_length);
 	return false;
 	return false;
 }
 }
 
 
@@ -873,6 +875,11 @@ bool Context::IsMouseInteracting() const
 	return (hover && hover != root.get()) || (active && active != root.get()) || scroll_controller->GetMode() == ScrollController::Mode::Autoscroll;
 	return (hover && hover != root.get()) || (active && active != root.get()) || scroll_controller->GetMode() == ScrollController::Mode::Autoscroll;
 }
 }
 
 
+void Context::SetDefaultScrollBehavior(ScrollBehavior scroll_behavior, float speed_factor)
+{
+	scroll_controller->SetDefaultScrollBehavior(scroll_behavior, speed_factor);
+}
+
 // Gets the context's render interface.
 // Gets the context's render interface.
 RenderInterface* Context::GetRenderInterface() const
 RenderInterface* Context::GetRenderInterface() const
 {
 {
@@ -1327,10 +1334,9 @@ void Context::ReleaseDragClone()
 	}
 	}
 }
 }
 
 
-void Context::PerformSmoothscrollOnTarget(Element* target, Vector2f delta_offset)
+void Context::PerformSmoothscrollOnTarget(Element* target, Vector2f delta_offset, ScrollBehavior scroll_behavior)
 {
 {
-	scroll_controller->ActivateSmoothscroll(target);
-	scroll_controller->IncrementSmoothscrollTarget(delta_offset);
+	scroll_controller->ActivateSmoothscroll(target, delta_offset, scroll_behavior);
 }
 }
 
 
 DataModel* Context::GetDataModelPtr(const String& name) const
 DataModel* Context::GetDataModelPtr(const String& name) const

+ 2 - 2
Source/Core/Element.cpp

@@ -1325,13 +1325,13 @@ void Element::ScrollIntoView(bool align_with_top)
 
 
 void Element::ScrollTo(Vector2f offset, ScrollBehavior behavior)
 void Element::ScrollTo(Vector2f offset, ScrollBehavior behavior)
 {
 {
-	if (behavior == ScrollBehavior::Smooth)
+	if (behavior != ScrollBehavior::Instant)
 	{
 	{
 		if (Context* context = GetContext())
 		if (Context* context = GetContext())
 		{
 		{
 			const Vector2f max_offset = {GetScrollWidth() - GetClientWidth(), GetScrollHeight() - GetClientHeight()};
 			const Vector2f max_offset = {GetScrollWidth() - GetClientWidth(), GetScrollHeight() - GetClientHeight()};
 			offset = Math::Min(Math::Max(offset, Vector2f(0.0f)), max_offset);
 			offset = Math::Min(Math::Max(offset, Vector2f(0.0f)), max_offset);
-			context->PerformSmoothscrollOnTarget(this, offset - scroll_offset);
+			context->PerformSmoothscrollOnTarget(this, offset - scroll_offset, behavior);
 			return;
 			return;
 		}
 		}
 	}
 	}

+ 29 - 3
Source/Core/ScrollController.cpp

@@ -97,14 +97,25 @@ void ScrollController::ActivateAutoscroll(Element* in_target, Vector2i start_pos
 	UpdateTime();
 	UpdateTime();
 }
 }
 
 
-void ScrollController::ActivateSmoothscroll(Element* in_target)
+void ScrollController::ActivateSmoothscroll(Element* in_target, Vector2f delta_distance, ScrollBehavior scroll_behavior)
 {
 {
 	Reset();
 	Reset();
 	if (!in_target)
 	if (!in_target)
 		return;
 		return;
+
 	target = in_target;
 	target = in_target;
+
+	// Do instant scroll if preferred.
+	if (smoothscroll_prefer_instant && scroll_behavior != ScrollBehavior::Smooth)
+	{
+		PerformScrollOnTarget(delta_distance);
+		target = nullptr;
+		return;
+	}
+
 	mode = Mode::Smoothscroll;
 	mode = Mode::Smoothscroll;
 	UpdateTime();
 	UpdateTime();
+	IncrementSmoothscrollTarget(delta_distance);
 }
 }
 
 
 void ScrollController::Update(Vector2i mouse_position, float dp_ratio)
 void ScrollController::Update(Vector2i mouse_position, float dp_ratio)
@@ -145,7 +156,7 @@ void ScrollController::UpdateSmoothscroll(float dp_ratio)
 	const Vector2f velocity = CalculateSmoothscrollVelocity(target_delta, smoothscroll_scrolled_distance, dp_ratio);
 	const Vector2f velocity = CalculateSmoothscrollVelocity(target_delta, smoothscroll_scrolled_distance, dp_ratio);
 
 
 	const float dt = UpdateTime();
 	const float dt = UpdateTime();
-	Vector2f scroll_distance = (velocity * dt).Round();
+	Vector2f scroll_distance = (smoothscroll_speed_factor * velocity * dt).Round();
 
 
 	for (int i = 0; i < 2; i++)
 	for (int i = 0; i < 2; i++)
 	{
 	{
@@ -201,7 +212,22 @@ void ScrollController::IncrementSmoothscrollTarget(Vector2f delta_distance)
 
 
 void ScrollController::Reset()
 void ScrollController::Reset()
 {
 {
-	*this = ScrollController{};
+	mode = Mode::None;
+	target = nullptr;
+
+	autoscroll_start_position = Vector2i{};
+	autoscroll_accumulated_length = Vector2f{};
+	autoscroll_moved = false;
+
+	smoothscroll_target_distance = Vector2f{};
+	smoothscroll_scrolled_distance = Vector2f{};
+	// Keep smoothscroll configuration parameters.
+}
+
+void ScrollController::SetDefaultScrollBehavior(ScrollBehavior scroll_behavior, float speed_factor)
+{
+	smoothscroll_prefer_instant = (scroll_behavior == ScrollBehavior::Instant);
+	smoothscroll_speed_factor = speed_factor;
 }
 }
 
 
 String ScrollController::GetAutoscrollCursor(Vector2i mouse_position, float dp_ratio) const
 String ScrollController::GetAutoscrollCursor(Vector2i mouse_position, float dp_ratio) const

+ 9 - 1
Source/Core/ScrollController.h

@@ -30,6 +30,7 @@
 #define RMLUI_CORE_SCROLLCONTROLLER_H
 #define RMLUI_CORE_SCROLLCONTROLLER_H
 
 
 #include "../../Include/RmlUi/Core/Header.h"
 #include "../../Include/RmlUi/Core/Header.h"
+#include "../../Include/RmlUi/Core/ScrollTypes.h"
 #include "../../Include/RmlUi/Core/Types.h"
 #include "../../Include/RmlUi/Core/Types.h"
 
 
 namespace Rml {
 namespace Rml {
@@ -50,14 +51,18 @@ public:
 
 
 	void ActivateAutoscroll(Element* target, Vector2i start_position);
 	void ActivateAutoscroll(Element* target, Vector2i start_position);
 
 
-	void ActivateSmoothscroll(Element* target);
+	void ActivateSmoothscroll(Element* target, Vector2f delta_distance, ScrollBehavior scroll_behavior);
 
 
 	void Update(Vector2i mouse_position, float dp_ratio);
 	void Update(Vector2i mouse_position, float dp_ratio);
 
 
 	void IncrementSmoothscrollTarget(Vector2f delta_distance);
 	void IncrementSmoothscrollTarget(Vector2f delta_distance);
 
 
+	// Resets any active mode and its state.
 	void Reset();
 	void Reset();
 
 
+	// Sets the scroll behavior for mouse wheel processing and scrollbar interaction.
+	void SetDefaultScrollBehavior(ScrollBehavior scroll_behavior, float speed_factor);
+
 	// Returns the autoscroll cursor based on the active scroll velocity.
 	// Returns the autoscroll cursor based on the active scroll velocity.
 	String GetAutoscrollCursor(Vector2i mouse_position, float dp_ratio) const;
 	String GetAutoscrollCursor(Vector2i mouse_position, float dp_ratio) const;
 	// Returns true if autoscroll mode is active and the cursor has been moved outside the idle scroll area.
 	// Returns true if autoscroll mode is active and the cursor has been moved outside the idle scroll area.
@@ -85,6 +90,9 @@ private:
 	Vector2f autoscroll_accumulated_length;
 	Vector2f autoscroll_accumulated_length;
 	bool autoscroll_moved = false;
 	bool autoscroll_moved = false;
 
 
+	bool smoothscroll_prefer_instant = false;
+	float smoothscroll_speed_factor = 1.f;
+
 	Vector2f smoothscroll_target_distance;
 	Vector2f smoothscroll_target_distance;
 	Vector2f smoothscroll_scrolled_distance;
 	Vector2f smoothscroll_scrolled_distance;
 };
 };

+ 4 - 4
Source/Core/WidgetScroll.cpp

@@ -490,22 +490,22 @@ void WidgetScroll::FormatElements(const Vector2f containing_block, float slider_
 
 
 void WidgetScroll::ScrollLineDown()
 void WidgetScroll::ScrollLineDown()
 {
 {
-	Scroll(SCROLL_LINE_LENGTH * ElementUtilities::GetDensityIndependentPixelRatio(parent), ScrollBehavior::Smooth);
+	Scroll(SCROLL_LINE_LENGTH * ElementUtilities::GetDensityIndependentPixelRatio(parent), ScrollBehavior::Auto);
 }
 }
 
 
 void WidgetScroll::ScrollLineUp()
 void WidgetScroll::ScrollLineUp()
 {
 {
-	Scroll(-SCROLL_LINE_LENGTH * ElementUtilities::GetDensityIndependentPixelRatio(parent), ScrollBehavior::Smooth);
+	Scroll(-SCROLL_LINE_LENGTH * ElementUtilities::GetDensityIndependentPixelRatio(parent), ScrollBehavior::Auto);
 }
 }
 
 
 void WidgetScroll::ScrollPageDown()
 void WidgetScroll::ScrollPageDown()
 {
 {
-	Scroll(SCROLL_PAGE_FACTOR * bar_length, ScrollBehavior::Smooth);
+	Scroll(SCROLL_PAGE_FACTOR * bar_length, ScrollBehavior::Auto);
 }
 }
 
 
 void WidgetScroll::ScrollPageUp()
 void WidgetScroll::ScrollPageUp()
 {
 {
-	Scroll(-SCROLL_PAGE_FACTOR * bar_length, ScrollBehavior::Smooth);
+	Scroll(-SCROLL_PAGE_FACTOR * bar_length, ScrollBehavior::Auto);
 }
 }
 
 
 void WidgetScroll::Scroll(float distance, ScrollBehavior behavior)
 void WidgetScroll::Scroll(float distance, ScrollBehavior behavior)