Browse Source

Scrolling with middle mouse button (#423)

Igor Segalla 2 years ago
parent
commit
d129fb8066

+ 7 - 1
Backends/RmlUi_Platform_GLFW.cpp

@@ -73,6 +73,12 @@ void SystemInterface_GLFW::SetMouseCursor(const Rml::String& cursor_name)
 		cursor = cursor_cross;
 	else if (cursor_name == "text")
 		cursor = cursor_text;
+	else if (cursor_name == "rmlui-scroll-idle")
+		cursor = cursor_pointer;
+	else if (cursor_name == "rmlui-scroll-up")
+		cursor = cursor_pointer;
+	else if (cursor_name == "rmlui-scroll-down")
+		cursor = cursor_pointer;
 	else if (cursor_name == "unavailable")
 		cursor = nullptr;
 
@@ -331,4 +337,4 @@ Rml::Input::KeyIdentifier RmlGLFW::ConvertKey(int glfw_key)
 	// clang-format on
 
 	return Rml::Input::KI_UNKNOWN;
-}
+}

+ 6 - 0
Backends/RmlUi_Platform_SDL.cpp

@@ -80,6 +80,12 @@ void SystemInterface_SDL::SetMouseCursor(const Rml::String& cursor_name)
 		cursor = cursor_cross;
 	else if (cursor_name == "text")
 		cursor = cursor_text;
+	else if (cursor_name == "rmlui-scroll-idle")
+		cursor = cursor_move;
+	else if (cursor_name == "rmlui-scroll-up")
+		cursor = cursor_move;
+	else if (cursor_name == "rmlui-scroll-down")
+		cursor = cursor_move;
 	else if (cursor_name == "unavailable")
 		cursor = cursor_unavailable;
 

+ 6 - 0
Backends/RmlUi_Platform_SFML.cpp

@@ -73,6 +73,12 @@ void SystemInterface_SFML::SetMouseCursor(const Rml::String& cursor_name)
 			cursor = &cursor_cross;
 		else if (cursor_name == "text")
 			cursor = &cursor_text;
+		else if (cursor_name == "rmlui-scroll-idle")
+			cursor = &cursor_move;
+		else if (cursor_name == "rmlui-scroll-up")
+			cursor = &cursor_move;
+		else if (cursor_name == "rmlui-scroll-down")
+			cursor = &cursor_move;
 		else if (cursor_name == "unavailable")
 			cursor = &cursor_unavailable;
 

+ 6 - 0
Backends/RmlUi_Platform_Win32.cpp

@@ -87,6 +87,12 @@ void SystemInterface_Win32::SetMouseCursor(const Rml::String& cursor_name)
 			cursor_handle = cursor_cross;
 		else if (cursor_name == "text")
 			cursor_handle = cursor_text;
+		else if (cursor_name == "rmlui-scroll-idle")
+			cursor_handle = cursor_move;
+		else if (cursor_name == "rmlui-scroll-up")
+			cursor_handle = cursor_move;
+		else if (cursor_name == "rmlui-scroll-down")
+			cursor_handle = cursor_move;
 		else if (cursor_name == "unavailable")
 			cursor_handle = cursor_unavailable;
 

+ 6 - 0
Backends/RmlUi_Platform_X11.cpp

@@ -116,6 +116,12 @@ void SystemInterface_X11::SetMouseCursor(const Rml::String& cursor_name)
 			cursor_handle = cursor_cross;
 		else if (cursor_name == "text")
 			cursor_handle = cursor_text;
+		else if (cursor_name == "rmlui-scroll-idle")
+			cursor_handle = cursor_move;
+		else if (cursor_name == "rmlui-scroll-up")
+			cursor_handle = cursor_move;
+		else if (cursor_name == "rmlui-scroll-down")
+			cursor_handle = cursor_move;
 		else if (cursor_name == "unavailable")
 			cursor_handle = cursor_unavailable;
 

+ 17 - 3
Include/RmlUi/Core/Context.h

@@ -205,12 +205,12 @@ public:
 	/// @return True if the mouse is not interacting with any elements in the context (see 'IsMouseInteracting'), otherwise false.
 	bool ProcessMouseMove(int x, int y, int key_modifier_state);
 	/// Sends a mouse-button down event into this context.
-	/// @param[in] button_index The index of the button that was pressed; 0 for the left button, 1 for right, and any others from 2 onwards.
+	/// @param[in] button_index The index of the button that was pressed; 0 for the left button, 1 for right, and 2 for middle button.
 	/// @param[in] key_modifier_state The state of key modifiers (shift, control, caps-lock, etc) keys; this should be generated by ORing together members of the Input::KeyModifier enumeration.
 	/// @return True if the mouse is not interacting with any elements in the context (see 'IsMouseInteracting'), otherwise false.
 	bool ProcessMouseButtonDown(int button_index, int key_modifier_state);
 	/// Sends a mouse-button up event into this context.
-	/// @param[in] button_index The index of the button that was release; 0 for the left button, 1 for right, and any others from 2 onwards.
+	/// @param[in] button_index The index of the button that was release; 0 for the left button, 1 for right, and 2 for middle button.
 	/// @param[in] key_modifier_state The state of key modifiers (shift, control, caps-lock, etc) keys; this should be generated by ORing together members of the Input::KeyModifier enumeration.
 	/// @return True if the mouse is not interacting with any elements in the context (see 'IsMouseInteracting'), otherwise false.
 	bool ProcessMouseButtonUp(int button_index, int key_modifier_state);
@@ -308,7 +308,7 @@ private:
 
 	// The element that was clicked on last.
 	Element* last_click_element;
-	// The time the last click occured.
+	// The time the last click occurred.
 	double last_click_time;
 	// Mouse position during the last mouse_down event.
 	Vector2i last_click_mouse_position;
@@ -317,6 +317,14 @@ private:
 	Vector2i mouse_position;
 	bool mouse_active;
 
+	// Scrolling element with scroll button controller.
+	Vector2i started_scroll_position;
+	Element* scroll_hover;
+	bool holding_scroll;
+
+	// The time the last update occurred.
+	double last_update_time;
+
 	// Enables cursor handling.
 	bool enable_cursor;
 	String cursor_name;
@@ -369,6 +377,12 @@ private:
 	// Returns the data model with the provided name, or nullptr if it does not exist.
 	DataModel* GetDataModelPtr(const String& name) const;
 
+	// Returns the scrolling cursor based on scroll direction.
+	String GetScrollCursor() const;
+
+	// Reset mouse scrolling parameters.
+	void ResetScrollParameters();
+
 	// Builds the parameters for a generic key event.
 	void GenerateKeyEventParameters(Dictionary& parameters, Input::KeyIdentifier key_identifier);
 	// Builds the parameters for a generic mouse event.

+ 65 - 3
Source/Core/Context.cpp

@@ -51,8 +51,9 @@ namespace Rml {
 
 static constexpr float DOUBLE_CLICK_TIME = 0.5f;     // [s]
 static constexpr float DOUBLE_CLICK_MAX_DIST = 3.f;  // [dp]
+static constexpr float SCROLL_MIDDLE_MOUSE_SPEED_FACTOR = 0.5f;
 
-Context::Context(const String& name) : name(name), dimensions(0, 0), density_independent_pixel_ratio(1.0f), mouse_position(0, 0), clip_origin(-1, -1), clip_dimensions(-1, -1)
+Context::Context(const String& name) : name(name), dimensions(0, 0), density_independent_pixel_ratio(1.0f), mouse_position(0, 0), started_scroll_position(0, 0), clip_origin(-1, -1), clip_dimensions(-1, -1)
 {
 	instancer = nullptr;
 
@@ -86,6 +87,7 @@ Context::Context(const String& name) : name(name), dimensions(0, 0), density_ind
 	hover = nullptr;
 	active = nullptr;
 	drag = nullptr;
+	scroll_hover = nullptr;
 
 	drag_started = false;
 	drag_verbose = false;
@@ -94,7 +96,9 @@ Context::Context(const String& name) : name(name), dimensions(0, 0), density_ind
 
 	last_click_element = nullptr;
 	last_click_time = 0;
+	last_update_time = 0;
 
+	holding_scroll = false;
 	mouse_active = false;
 
 	enable_cursor = true;
@@ -182,7 +186,24 @@ float Context::GetDensityIndependentPixelRatio() const
 bool Context::Update()
 {
 	RMLUI_ZoneScoped;
-	
+
+	double current_time = GetSystemInterface()->GetElapsedTime();
+
+	if (scroll_hover)
+	{
+		const Vector2i scroll_delta = mouse_position - started_scroll_position;
+		const float delta_time = float(current_time - last_update_time);
+		const float scroll_speed = scroll_delta.y * SCROLL_MIDDLE_MOUSE_SPEED_FACTOR * delta_time;
+
+		Dictionary scroll_parameters;
+		GenerateMouseEventParameters(scroll_parameters);
+		scroll_parameters["wheel_delta"] = scroll_speed;
+
+		// Scroll event was not handled by any element, it means that we don't have anything to scroll.
+		if (scroll_hover->DispatchEvent(EventId::Mousescroll, scroll_parameters))
+			ResetScrollParameters();
+	}
+
 	// Update the hover chain to detect any new or moved elements under the mouse.
 	if (mouse_active)
 		UpdateHoverChain(mouse_position);
@@ -209,6 +230,9 @@ bool Context::Update()
 	// Release any documents that were unloaded during the update.
 	ReleaseUnloadedDocuments();
 
+	// Last update time used to calculate delta time posteriorly.
+	last_update_time = current_time;
+
 	return true;
 }
 
@@ -620,6 +644,9 @@ bool Context::ProcessMouseMove(int x, int y, int key_modifier_state)
 			if (drag_hover && drag_verbose)
 				drag_hover->DispatchEvent(EventId::Dragmove, drag_parameters);
 		}
+
+		if (scroll_hover)
+			holding_scroll = true;
 	}
 
 	return !IsMouseInteracting();
@@ -726,6 +753,17 @@ bool Context::ProcessMouseButtonDown(int button_index, int key_modifier_state)
 			hover->DispatchEvent(EventId::Mousedown, parameters);
 	}
 
+	if (scroll_hover)
+	{
+		ResetScrollParameters();
+	}
+	else if (button_index == 2)
+	{
+		scroll_hover = hover;
+		holding_scroll = false;
+		started_scroll_position = mouse_position;
+	}
+
 	return !IsMouseInteracting();
 }
 
@@ -801,6 +839,9 @@ bool Context::ProcessMouseButtonUp(int button_index, int key_modifier_state)
 			hover->DispatchEvent(EventId::Mouseup, parameters);
 	}
 
+	if (scroll_hover && holding_scroll)
+		ResetScrollParameters();
+
 	return result;
 }
 
@@ -1092,7 +1133,9 @@ void Context::UpdateHoverChain(Vector2i old_mouse_position, int key_modifier_sta
 	{
 		String new_cursor_name;
 
-		if(drag)
+		if (scroll_hover)
+			new_cursor_name = GetScrollCursor();
+		else if(drag)
 			new_cursor_name = drag->GetComputedValues().cursor();
 		else if (hover)
 			new_cursor_name = hover->GetComputedValues().cursor();
@@ -1290,6 +1333,25 @@ DataModel* Context::GetDataModelPtr(const String& name) const
 	return nullptr;
 }
 
+// Returns the scrolling cursor based on scroll direction.
+String Context::GetScrollCursor() const
+{
+	const int scroll_direction = started_scroll_position.y - mouse_position.y;
+
+	if (scroll_direction == 0)
+		return "rmlui-scroll-idle";
+
+	return scroll_direction > 0 ? "rmlui-scroll-up" : "rmlui-scroll-down";
+}
+
+// Reset mouse scrolling parameters.
+void Context::ResetScrollParameters()
+{
+	scroll_hover = nullptr;
+	holding_scroll = false;
+	started_scroll_position = Vector2i(0, 0);
+}
+
 // Builds the parameters for a generic key event.
 void Context::GenerateKeyEventParameters(Dictionary& parameters, Input::KeyIdentifier key_identifier)
 {