[Scummvm-git-logs] scummvm master -> c041092694285068d2a83f53fc7f6e460d4d1d8e

sev- noreply at scummvm.org
Mon Apr 20 21:33:32 UTC 2026


This automated email contains information about 11 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .

Summary:
15a0877a89 GUI: Add accelerated wheel scrolling to FluidScroller
67facd983f GUI: Implement fluid wheel scrolling in AboutDialog
c1f0043775 GUI: Implement drag to scroll in Grid
15c1eaefcf GUI: Implement Fluid scroll in Launcher Grid
af56d299da GUI: Implement drag to scroll in List
cb71d7b6cf GUI: Implement Fluid scroll in Launcher List
a2abc4c1e5 GUI: Centralize mouse wheel handling in FluidScroller
5ef242d112 GUI: Implenet Fluid Scroll in Richtext widget
441f97f4e4 GUI: Implement drag to scroll in ScrollContainer
84dab33d07 GUI: Implenet Fluid Scroll in ScrollContainer
c041092694 GUI: Centralize scroll step size logic in FluidScroller


Commit: 15a0877a896273e891eb534220bf27010b2a2fbb
    https://github.com/scummvm/scummvm/commit/15a0877a896273e891eb534220bf27010b2a2fbb
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-04-20T23:33:23+02:00

Commit Message:
GUI: Add accelerated wheel scrolling to FluidScroller

Changed paths:
    gui/animation/FluidScroll.cpp
    gui/animation/FluidScroll.h


diff --git a/gui/animation/FluidScroll.cpp b/gui/animation/FluidScroll.cpp
index 19220415dd9..d714f7d0d0b 100644
--- a/gui/animation/FluidScroll.cpp
+++ b/gui/animation/FluidScroll.cpp
@@ -97,6 +97,7 @@ FluidScroller::FluidScroller() :
 	_animationOffset(0.0f), 
 	_maxScroll(0.0f), 
 	_viewportHeight(0),
+	_lastWheelTime(0),
 	_initialVelocity(0.0f),
 	_lambda(0.0f),
 	_stretchDistance(0.0f),
@@ -111,6 +112,7 @@ void FluidScroller::setBounds(float maxScroll, int viewportHeight) {
 void FluidScroller::reset() {
 	_mode = kModeNone;
 	_startTime = 0;
+	_lastWheelTime = 0;
 	_initialVelocity = 0.0f;
 	_scrollPosRaw = 0.0f;
 	_velocityTracker.reset();
@@ -134,8 +136,10 @@ float FluidScroller::setPosition(float pos, bool checkBound) {
 }
 
 void FluidScroller::startFling() {
-	float velocity = _velocityTracker.calculateVelocity();
-	
+	startFling(_velocityTracker.calculateVelocity());
+}
+
+void FluidScroller::startFling(float velocity) {
 	if (fabsf(velocity) < 0.1f) {
 		checkBoundaries();
 		return;
@@ -147,6 +151,24 @@ void FluidScroller::startFling() {
 	_animationOffset = _scrollPosRaw;
 }
 
+void FluidScroller::feedWheel(uint32 time, float deltaY) {
+	uint32 dt = time - _lastWheelTime;
+	_lastWheelTime = time;
+
+	/*
+	 * Cap the duration to prevent extreme high/low velocity
+	 * Otherwise use the actual interval
+	 */ 
+	uint32 effectiveDt = dt;
+	if (dt > 200)
+		effectiveDt = 200;
+	else if (dt < 20)
+		effectiveDt = 20;
+
+	float velocity = deltaY / (float)effectiveDt;
+	startFling(velocity);
+}
+
 void FluidScroller::absorb(float velocity, float distance) {
 	_mode = kModeSpringBack;
 	_startTime = g_system->getMillis();
diff --git a/gui/animation/FluidScroll.h b/gui/animation/FluidScroll.h
index 80f1fbb0ec7..15e14f06ed1 100644
--- a/gui/animation/FluidScroll.h
+++ b/gui/animation/FluidScroll.h
@@ -58,6 +58,12 @@ public:
 	// Start a fling using the recorded velocity
 	void startFling();
 
+	// Start a fling with a specific initial velocity
+	void startFling(float velocity);
+
+	// Record a wheel tick and start/update a fling
+	void feedWheel(uint32 time, float deltaY);
+
 	// Check if there is an active animation (fling or spring-back)
 	bool isAnimating() const { return _mode != kModeNone; }
 
@@ -102,6 +108,7 @@ private:
 	};
 
 	VelocityTracker _velocityTracker;
+	uint32 _lastWheelTime;
 
 	Mode _mode;
 	uint32 _startTime;


Commit: 67facd983f0a25d3c1e4cae5b283c640e3cbf342
    https://github.com/scummvm/scummvm/commit/67facd983f0a25d3c1e4cae5b283c640e3cbf342
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-04-20T23:33:23+02:00

Commit Message:
GUI: Implement fluid wheel scrolling in AboutDialog

Changed paths:
    gui/about.cpp


diff --git a/gui/about.cpp b/gui/about.cpp
index 64634d7e58e..c7968731761 100644
--- a/gui/about.cpp
+++ b/gui/about.cpp
@@ -443,12 +443,10 @@ void AboutDialog::handleMouseWheel(int x, int y, int direction) {
 
 	_autoScroll = false;
 	_fluidScroller->stopAnimation();
-
-	_scrollPos += stepping;
-	_scrollPos = _fluidScroller->setPosition(_scrollPos, true);
+	_fluidScroller->feedWheel(g_system->getMillis(), (float)stepping);
 
 	if (_scrollbar) {
-		_scrollbar->_currentPos = (int)_scrollPos;
+		_scrollbar->_currentPos = (int)_fluidScroller->getVisualPosition();
 		_scrollbar->recalc();
 	}
 


Commit: c1f004377528ec18a844b5bc4bddcc83bcd2d5e3
    https://github.com/scummvm/scummvm/commit/c1f004377528ec18a844b5bc4bddcc83bcd2d5e3
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-04-20T23:33:23+02:00

Commit Message:
GUI: Implement drag to scroll in Grid

Changed paths:
    gui/widgets/grid.cpp
    gui/widgets/grid.h


diff --git a/gui/widgets/grid.cpp b/gui/widgets/grid.cpp
index 8b8ab8a27a6..fc731d7a5f4 100644
--- a/gui/widgets/grid.cpp
+++ b/gui/widgets/grid.cpp
@@ -91,8 +91,8 @@ void GridItemWidget::drawWidget() {
 	// Check if this entry is in the selected entries list
 	bool isSelected = _grid->_selectedItems[_activeEntry->entryID];
 
-	// Draw selection highlight if this entry is selected or hovered
-	if (isSelected || _isHighlighted) {
+	// Draw selection highlight if this entry is selected or hovered (not while dragging)
+	if (isSelected || (_isHighlighted && !_grid->_isMouseDown)) {
 		Common::Rect r(_x - kMarginX, _y - kMarginY,
 					   _x + _w + kMarginX, _y + _h + kMarginY);
 		// Draw a highlighted BG on hover
@@ -209,6 +209,50 @@ void GridItemWidget::handleMouseMoved(int x, int y, int button) {
 	if (!_isHighlighted) {
 		handleMouseEntered(button);
 	}
+	_grid->handleMouseMoved(x + _x, y + _y, button);
+}
+
+void GridItemWidget::doSelection() {
+	if (_activeEntry->isHeader) {
+		_grid->_selectedEntry = nullptr;
+		_grid->toggleGroup(_activeEntry->entryID);
+	} else if (_isHighlighted && isVisible()) {
+		_grid->_selectedEntry = _activeEntry;
+
+		if (!_grid->isMultiSelectEnabled()) {
+			_grid->clearSelection();
+			_grid->markSelectedItem(_activeEntry->entryID, true);
+			_grid->_lastSelectedEntryID = _activeEntry->entryID;
+			sendCommand(kItemClicked, _activeEntry->entryID);
+			return;
+		}
+
+		int32 keyState = g_system->getEventManager()->getModifierState();
+		bool ctrlPressed = (keyState & Common::KBD_CTRL) != 0;
+		bool shiftPressed = (keyState & Common::KBD_SHIFT) != 0;
+
+		if (ctrlPressed) {
+			if (_grid->isItemSelected(_activeEntry->entryID)) {
+				_grid->markSelectedItem(_activeEntry->entryID, false);
+			} else {
+				_grid->markSelectedItem(_activeEntry->entryID, true);
+				_grid->_lastSelectedEntryID = _activeEntry->entryID;
+			}
+		} else if (shiftPressed && _grid->_lastSelectedEntryID >= 0) {
+			int startID = _grid->getVisualPos(_grid->_lastSelectedEntryID);
+			int endID = _grid->getVisualPos(_activeEntry->entryID);
+			if (startID >= 0 && endID >= 0) {
+				_grid->selectVisualRange(startID, endID);
+			}
+			_grid->_lastSelectedEntryID = _activeEntry->entryID;
+		} else {
+			_grid->clearSelection();
+			_grid->markSelectedItem(_activeEntry->entryID, true);
+			_grid->_lastSelectedEntryID = _activeEntry->entryID;
+		}
+
+		sendCommand(kItemClicked, _activeEntry->entryID);
+	}
 }
 
 void GridWidget::toggleGroup(int groupID) {
@@ -257,54 +301,11 @@ void GridWidget::saveClosedGroups(const Common::U32String &groupName) {
 }
 
 void GridItemWidget::handleMouseDown(int x, int y, int button, int clickCount) {
-	if (_activeEntry->isHeader) {
-		_grid->_selectedEntry = nullptr;
-		_grid->toggleGroup(_activeEntry->entryID);
-	} else if (_isHighlighted && isVisible()) {
-		_grid->_selectedEntry = _activeEntry;
-
-		// If multi-select is not enabled, use simple single-selection
-		if (!_grid->isMultiSelectEnabled()) {
-			_grid->clearSelection();
-			_grid->markSelectedItem(_activeEntry->entryID, true);
-			_grid->_lastSelectedEntryID = _activeEntry->entryID;
-			sendCommand(kItemClicked, _activeEntry->entryID);
-			return;
-		}
-
-		// Get the current keyboard state
-		int32 keyState = g_system->getEventManager()->getModifierState();
-		bool ctrlPressed = (keyState & Common::KBD_CTRL) != 0;
-		bool shiftPressed = (keyState & Common::KBD_SHIFT) != 0;
-
-		if (ctrlPressed) {
-			// Ctrl+Click: Toggle selection of this item
-			if (_grid->isItemSelected(_activeEntry->entryID)) {
-				_grid->markSelectedItem(_activeEntry->entryID, false);
-			} else {
-				_grid->markSelectedItem(_activeEntry->entryID, true);
-				_grid->_lastSelectedEntryID = _activeEntry->entryID;
-			}
-		} else if (shiftPressed && _grid->_lastSelectedEntryID >= 0) {
-			// Shift+Click: Select range from last selected to current item
-			// Must select based on visual order, not by entryID range
-			int startID = _grid->getVisualPos(_grid->_lastSelectedEntryID);
-			int endID = _grid->getVisualPos(_activeEntry->entryID);
-
-			// If we found both positions, select all items between them (visually)
-			if (startID >= 0 && endID >= 0) {
-				_grid->selectVisualRange(startID, endID);
-			}
-			_grid->_lastSelectedEntryID = _activeEntry->entryID;
-		} else {
-			// Regular click: Select only this item
-			_grid->clearSelection();
-			_grid->markSelectedItem(_activeEntry->entryID, true);
-			_grid->_lastSelectedEntryID = _activeEntry->entryID;
-		}
+	_grid->handleMouseDown(x + _x, y + _y, button, clickCount);
+}
 
-		sendCommand(kItemClicked, _activeEntry->entryID);
-	}
+void GridItemWidget::handleMouseUp(int x, int y, int button, int clickCount) {
+	_grid->handleMouseUp(x + _x, y + _y, button, clickCount);
 }
 
 #pragma mark -
@@ -514,8 +515,16 @@ GridWidget::GridWidget(GuiObject *boss, const Common::String &name)
 	_selectedItems.clear();
 	_lastSelectedEntryID = -1;
 
+	_isMouseDown = false;
+	_isDragging = false;
+	_selectionPending = false;
+	_dragStartY = 0;
+	_dragLastY = 0;
+
 	_filterMatcher = GridWidgetDefaultMatcher;
 	_filterMatcherArg = nullptr;
+
+	setFlags(getFlags() | WIDGET_TRACK_MOUSE);
 }
 
 GridWidget::~GridWidget() {
@@ -1001,6 +1010,53 @@ void GridWidget::handleMouseWheel(int x, int y, int direction) {
 	_scrollPos = _scrollBar->_currentPos;
 }
 
+void GridWidget::handleMouseDown(int x, int y, int button, int clickCount) {
+	_isMouseDown = true;
+	_mouseDownTime = g_system->getMillis();
+	_isDragging = false;
+	_selectionPending = true;
+	_dragStartY = y;
+	_dragLastY = y;
+}
+
+void GridWidget::handleMouseUp(int x, int y, int button, int clickCount) {
+	bool wasPending = _selectionPending;
+	bool wasDragging = _isDragging;
+
+	_isMouseDown = false;
+	_isDragging = false;
+	_selectionPending = false;
+
+	if (wasPending && !wasDragging) {
+		// Find which item was clicked and select it
+		Widget *w = findWidget(x, y);
+		if (w && w != this && w->getType() == kContainerWidget)
+			((GridItemWidget *)w)->doSelection();
+	}
+}
+
+void GridWidget::handleMouseMoved(int x, int y, int button) {
+	if (!_isMouseDown)
+		return;
+
+	if (!_isDragging && ABS(y - _dragStartY) > kDragThreshold) {
+		_isDragging = true;
+		_selectionPending = false;
+	}
+
+	if (_isDragging) {
+		int deltaY = _dragLastY - y;
+		_dragLastY = y;
+
+		int newPos = _scrollPos + deltaY;
+		int maxScroll = _scrollBar->_numEntries - _scrollBar->_entriesPerPage;
+		newPos = MAX(0, MIN(newPos, maxScroll));
+
+		if (_scrollPos != newPos)
+			handleCommand(this, kSetPositionCmd, newPos);
+	}
+}
+
 bool GridWidget::handleKeyDown(Common::KeyState state) {
 	return false;
 }
diff --git a/gui/widgets/grid.h b/gui/widgets/grid.h
index 7dd709ebdcb..a33b84e2ecb 100644
--- a/gui/widgets/grid.h
+++ b/gui/widgets/grid.h
@@ -100,6 +100,7 @@ public:
 
 /* GridWidget */
 class GridWidget : public ContainerWidget, public CommandSender {
+	friend class GridItemWidget;
 public:
 	typedef bool (*FilterMatcher)(void *arg, int idx, const Common::U32String &item, const Common::U32String &token);
 
@@ -166,6 +167,14 @@ protected:
 	FilterMatcher _filterMatcher;
 	void *_filterMatcherArg;
 
+	// Drag to scroll
+	bool _isMouseDown;
+	bool _isDragging;
+	bool _selectionPending;
+	int _dragStartY, _dragLastY;
+	uint32 _mouseDownTime;
+	static const int kDragThreshold = 5;
+
 public:
 	int				_gridItemHeight;
 	int				_gridItemWidth;
@@ -227,6 +236,9 @@ public:
 	int getThumbnailWidth() const { return _thumbnailWidth; }
 
 	void handleMouseWheel(int x, int y, int direction) override;
+	void handleMouseDown(int x, int y, int button, int clickCount) override;
+	void handleMouseUp(int x, int y, int button, int clickCount) override;
+	void handleMouseMoved(int x, int y, int button) override;
 	void handleCommand(CommandSender *sender, uint32 cmd, uint32 data) override;
 	void reflowLayout() override;
 
@@ -276,7 +288,9 @@ public:
 	void handleMouseEntered(int button) override;
 	void handleMouseLeft(int button) override;
 	void handleMouseDown(int x, int y, int button, int clickCount) override;
+	void handleMouseUp(int x, int y, int button, int clickCount) override;
 	void handleMouseMoved(int x, int y, int button) override;
+	void doSelection();
 };
 
 } // End of namespace GUI


Commit: 15c1eaefcf750d1af9a321ac18accd0ef5a832a4
    https://github.com/scummvm/scummvm/commit/15c1eaefcf750d1af9a321ac18accd0ef5a832a4
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-04-20T23:33:23+02:00

Commit Message:
GUI: Implement Fluid scroll in Launcher Grid

Changed paths:
    gui/widgets/grid.cpp
    gui/widgets/grid.h


diff --git a/gui/widgets/grid.cpp b/gui/widgets/grid.cpp
index fc731d7a5f4..93bdafd1bad 100644
--- a/gui/widgets/grid.cpp
+++ b/gui/widgets/grid.cpp
@@ -28,6 +28,7 @@
 
 #include "gui/gui-manager.h"
 #include "gui/widgets/grid.h"
+#include "gui/animation/FluidScroll.h"
 
 #include "gui/ThemeEval.h"
 
@@ -490,7 +491,7 @@ GridWidget::GridWidget(GuiObject *boss, const Common::String &name)
 
 	_scrollBar = new ScrollBarWidget(this, _w - _scrollBarWidth, _y, _scrollBarWidth, _y + _h);
 	_scrollBar->setTarget(this);
-	_scrollPos = 0;
+	_scrollPos = 0.0f;
 	_scrollSpeed = 1;
 	_firstVisibleItem = 0;
 	_lastVisibleItem = 0;
@@ -521,10 +522,12 @@ GridWidget::GridWidget(GuiObject *boss, const Common::String &name)
 	_dragStartY = 0;
 	_dragLastY = 0;
 
+	_fluidScroller = new FluidScroller();
+
 	_filterMatcher = GridWidgetDefaultMatcher;
 	_filterMatcherArg = nullptr;
 
-	setFlags(getFlags() | WIDGET_TRACK_MOUSE);
+	setFlags(getFlags() | WIDGET_TRACK_MOUSE | WIDGET_WANT_TICKLE | WIDGET_RETAIN_FOCUS);
 }
 
 GridWidget::~GridWidget() {
@@ -541,6 +544,7 @@ GridWidget::~GridWidget() {
 	_platformIconsAlpha.clear();
 	_languageIconsAlpha.clear();
 	_extraIconsAlpha.clear();
+	delete _fluidScroller;
 }
 
 Common::SharedPtr<Graphics::ManagedSurface> GridWidget::filenameToSurface(const Common::String &name) {
@@ -876,7 +880,7 @@ void GridWidget::move(int x, int y) {
 // Scroll to entry id. Optional parameter to decide if the entry should be forced to be on the top, or merely
 // scrolled into view.
 void GridWidget::scrollToEntry(int id, bool forceToTop) {
-	int newScrollPos = _scrollPos;
+	float newScrollPos = _scrollPos;
 	for (uint i = 0; i < _sortedEntryList.size(); ++i) {
 		if ((!_sortedEntryList[i]->isHeader) && (_sortedEntryList[i]->entryID == id)) {
 			if (forceToTop) {
@@ -1006,17 +1010,24 @@ void GridWidget::selectVisualRange(int startPos, int endPos) {
 }
 
 void GridWidget::handleMouseWheel(int x, int y, int direction) {
-	_scrollBar->handleMouseWheel(x, y, direction);
-	_scrollPos = _scrollBar->_currentPos;
+	const float stepping = (float)_scrollBar->_singleStep * direction;
+	if (stepping == 0.0f)
+		return;
+
+	_fluidScroller->stopAnimation();
+	_fluidScroller->feedWheel(g_system->getMillis(), stepping);
 }
 
 void GridWidget::handleMouseDown(int x, int y, int button, int clickCount) {
-	_isMouseDown = true;
-	_mouseDownTime = g_system->getMillis();
-	_isDragging = false;
+	if (button == 1) {
+		_isMouseDown = true;
+		_mouseDownTime = g_system->getMillis();
+		_isDragging = false;
+		_dragStartY = y;
+		_dragLastY = y;
+	}
 	_selectionPending = true;
-	_dragStartY = y;
-	_dragLastY = y;
+	_fluidScroller->stopAnimation();
 }
 
 void GridWidget::handleMouseUp(int x, int y, int button, int clickCount) {
@@ -1033,6 +1044,9 @@ void GridWidget::handleMouseUp(int x, int y, int button, int clickCount) {
 		if (w && w != this && w->getType() == kContainerWidget)
 			((GridItemWidget *)w)->doSelection();
 	}
+
+	if (wasDragging)
+		_fluidScroller->startFling();
 }
 
 void GridWidget::handleMouseMoved(int x, int y, int button) {
@@ -1048,15 +1062,28 @@ void GridWidget::handleMouseMoved(int x, int y, int button) {
 		int deltaY = _dragLastY - y;
 		_dragLastY = y;
 
-		int newPos = _scrollPos + deltaY;
-		int maxScroll = _scrollBar->_numEntries - _scrollBar->_entriesPerPage;
-		newPos = MAX(0, MIN(newPos, maxScroll));
-
-		if (_scrollPos != newPos)
-			handleCommand(this, kSetPositionCmd, newPos);
+		if (deltaY != 0) {
+			_fluidScroller->feedDrag(g_system->getMillis(), deltaY);
+			_scrollPos = _fluidScroller->getVisualPosition();
+			applyScrollPos();
+		}
 	}
 }
 
+void GridWidget::applyScrollPos() {
+	if (calcVisibleEntries())
+		reloadThumbnails();
+
+	assignEntriesToItems();
+	scrollBarRecalc();	
+	g_gui.scheduleTopDialogRedraw();
+}
+
+void GridWidget::handleTickle() {
+	if (_fluidScroller->update(g_system->getMillis(), _scrollPos))
+		applyScrollPos();
+}
+
 bool GridWidget::handleKeyDown(Common::KeyState state) {
 	return false;
 }
@@ -1069,16 +1096,12 @@ void GridWidget::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
 	// Work in progress
 	switch (cmd) {
 	case kSetPositionCmd:
-		if (_scrollPos != (int)data) {
-			_scrollPos = data;
+		if (_scrollPos != (float)data) {
+			_scrollPos = (float)data;
+			_fluidScroller->stopAnimation();
+			_scrollPos = _fluidScroller->setPosition(_scrollPos, false);
 
-			if (calcVisibleEntries()) {
-				reloadThumbnails();
-			}
-
-			assignEntriesToItems();
-			scrollBarRecalc();
-			markAsDirty();
+			applyScrollPos();
 
 			((GUI::Dialog *)_boss)->setFocusWidget(this);
 		}
@@ -1245,6 +1268,8 @@ void GridWidget::reflowLayout() {
 		scrollToEntry(_selectedEntry->entryID, false);
 	}
 	scrollBarRecalc();
+	int maxScroll = MAX(0, _scrollBar->_numEntries - _scrollBar->_entriesPerPage);
+	_fluidScroller->setBounds((float)maxScroll, _scrollBar->_entriesPerPage);
 	markAsDirty();
 }
 
@@ -1262,12 +1287,13 @@ void GridWidget::openTrayAtSelected() {
 void GridWidget::scrollBarRecalc() {
 	_scrollBar->_numEntries = _innerHeight;
 	_scrollBar->_entriesPerPage = _scrollWindowHeight - 2 * _scrollWindowPaddingY;
-	_scrollBar->_currentPos = _scrollPos;
+	_scrollBar->_currentPos = (int)_scrollPos;
 	_scrollBar->_singleStep = kLineHeight;
 
-	_scrollBar->checkBounds(_scrollBar->_currentPos);
-	_scrollPos = _scrollBar->_currentPos;
 	_scrollBar->recalc();
+
+	int maxScroll = MAX(0, _scrollBar->_numEntries - _scrollBar->_entriesPerPage);
+	_fluidScroller->setBounds((float)maxScroll, _scrollBar->_entriesPerPage);
 }
 
 void GridWidget::setFilter(const Common::U32String &filter) {
diff --git a/gui/widgets/grid.h b/gui/widgets/grid.h
index a33b84e2ecb..20975936664 100644
--- a/gui/widgets/grid.h
+++ b/gui/widgets/grid.h
@@ -35,6 +35,7 @@ namespace GUI {
 class ScrollBarWidget;
 class GridItemWidget;
 class GridWidget;
+class FluidScroller;
 
 enum {
 	kPlayButtonCmd = 'PLAY',
@@ -137,7 +138,7 @@ protected:
 	int				_scrollWindowHeight;
 	int				_scrollWindowWidth;
 	int				_scrollSpeed;
-	int				_scrollPos;
+	float			_scrollPos;
 	int				_innerHeight;
 	int				_innerWidth;
 	int				_thumbnailHeight;
@@ -175,6 +176,8 @@ protected:
 	uint32 _mouseDownTime;
 	static const int kDragThreshold = 5;
 
+	FluidScroller *_fluidScroller;
+
 public:
 	int				_gridItemHeight;
 	int				_gridItemWidth;
@@ -230,7 +233,7 @@ public:
 	int getNewSel(int index);
 	int getVisualPos(int entryID) const;
 	void selectVisualRange(int startPos, int endPos);
-	int getScrollPos() const { return _scrollPos; }
+	float getScrollPos() const { return _scrollPos; }
 	int getSelected() const { return ((_selectedEntry == nullptr) ? -1 : _selectedEntry->entryID); }
 	int getThumbnailHeight() const { return _thumbnailHeight; }
 	int getThumbnailWidth() const { return _thumbnailWidth; }
@@ -239,6 +242,8 @@ public:
 	void handleMouseDown(int x, int y, int button, int clickCount) override;
 	void handleMouseUp(int x, int y, int button, int clickCount) override;
 	void handleMouseMoved(int x, int y, int button) override;
+	void handleTickle() override;
+	void applyScrollPos(); // Updates the grid's visual elements to match current scroll position
 	void handleCommand(CommandSender *sender, uint32 cmd, uint32 data) override;
 	void reflowLayout() override;
 


Commit: af56d299da48ded9e93a64b744fa1750e6fed735
    https://github.com/scummvm/scummvm/commit/af56d299da48ded9e93a64b744fa1750e6fed735
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-04-20T23:33:23+02:00

Commit Message:
GUI: Implement drag to scroll in List

Changed paths:
    gui/widgets/groupedlist.cpp
    gui/widgets/list.cpp
    gui/widgets/list.h


diff --git a/gui/widgets/groupedlist.cpp b/gui/widgets/groupedlist.cpp
index 00535bb2492..cca93701185 100644
--- a/gui/widgets/groupedlist.cpp
+++ b/gui/widgets/groupedlist.cpp
@@ -258,22 +258,13 @@ void GroupedListWidget::handleMouseDown(int x, int y, int button, int clickCount
 	if (!isEnabled())
 		return;
 
-	// First check whether the selection changed
-	int newSelectedItem = findItem(x, y);
-	if (newSelectedItem == -1)
-		return;
+	_isMouseDown = true;
+	_isDragging = false;
+	_dragLastY = 0;
 
-	if (isGroupHeader(_listIndex[newSelectedItem])) {
-		int groupID = indexToGroupID(_listIndex[newSelectedItem]);
-		int oldSelection = getSelected();
-		_selectedItem = -1;
-		toggleGroup(groupID);
-		if (oldSelection != -1) {
-			_selectedItem = findDataIndex(oldSelection);
-			sendCommand(kListSelectionChangedCmd, _selectedItem);
-		}
-		markAsDirty();
-		return;
+	if (button == 1) {
+		_dragStartY = y;
+		_dragLastY = y;
 	}
 
 	// TODO: Determine where inside the string the user clicked and place the
@@ -281,53 +272,74 @@ void GroupedListWidget::handleMouseDown(int x, int y, int button, int clickCount
 	// See _editScrollOffset and EditTextWidget::handleMouseDown.
 	if (_editMode)
 		abortEditMode();
+}
 
-	int dataIndex = _listIndex[newSelectedItem];
-	if (dataIndex < 0)
-		return;
-
-	// Get modifier keys
-	int modifiers = g_system->getEventManager()->getModifierState();
-	bool ctrlClick = (modifiers & Common::KBD_CTRL) != 0;
-	bool shiftClick = (modifiers & Common::KBD_SHIFT) != 0;
-
-	// Only handle multi-select if it's enabled
-	if (_multiSelectEnabled && (shiftClick || ctrlClick)) {
-		if (shiftClick && _lastSelectionStartItem != -1) {
-			// Shift+Click: Select range in terms of underlying data indices
-			int startListIndex = _lastSelectionStartItem;
-			int endListIndex = newSelectedItem;              
-			selectItemRange(startListIndex, endListIndex);
-			_selectedItem = newSelectedItem;
-			_lastSelectionStartItem = newSelectedItem;
-			sendCommand(kListSelectionChangedCmd, _selectedItem);
-		} else if (ctrlClick) {
-			// Ctrl+Click: toggle selection for the underlying data index
-			if (isItemSelected(newSelectedItem)) {
-				markSelectedItem(newSelectedItem, false);
-			} else {
-				markSelectedItem(newSelectedItem, true);
-				_selectedItem = newSelectedItem;
-				_lastSelectionStartItem = newSelectedItem;
+void GroupedListWidget::handleMouseUp(int x, int y, int button, int clickCount) {
+	if (button == 1 || button == 2) {
+		if (_isMouseDown && !_isDragging) {
+			int newSelectedItem = findItem(x, y);
+			if (newSelectedItem != -1) {
+				if (isGroupHeader(_listIndex[newSelectedItem])) {
+					int groupID = indexToGroupID(_listIndex[newSelectedItem]);
+					int oldSelection = getSelected();
+					_selectedItem = -1;
+					toggleGroup(groupID);
+					if (oldSelection != -1) {
+						_selectedItem = findDataIndex(oldSelection);
+						sendCommand(kListSelectionChangedCmd, _selectedItem);
+					}
+					applyScrollPos();
+				} else {
+					int dataIndex = _listIndex[newSelectedItem];
+					if (dataIndex >= 0) {
+						// Get modifier keys
+						int modifiers = g_system->getEventManager()->getModifierState();
+						bool ctrlClick = (modifiers & Common::KBD_CTRL) != 0;
+						bool shiftClick = (modifiers & Common::KBD_SHIFT) != 0;
+
+						// Only handle multi-select if it's enabled
+						if (_multiSelectEnabled && (shiftClick || ctrlClick)) {
+							if (shiftClick && _lastSelectionStartItem != -1) {
+								// Shift+Click: Select range in terms of underlying data indices
+								int startListIndex = _lastSelectionStartItem;
+								int endListIndex = newSelectedItem;              
+								selectItemRange(startListIndex, endListIndex);
+								_selectedItem = newSelectedItem;
+								_lastSelectionStartItem = newSelectedItem;
+								sendCommand(kListSelectionChangedCmd, _selectedItem);
+							} else if (ctrlClick) {
+								// Ctrl+Click: toggle selection for the underlying data index
+								if (isItemSelected(newSelectedItem)) {
+									markSelectedItem(newSelectedItem, false);
+								} else {
+									markSelectedItem(newSelectedItem, true);
+									_selectedItem = newSelectedItem;
+									_lastSelectionStartItem = newSelectedItem;
+								}
+								sendCommand(kListSelectionChangedCmd, _selectedItem);
+							}
+						} else {
+							// Regular click: clear selection and select only this underlying item
+							clearSelection();
+							_selectedItem = newSelectedItem;
+							markSelectedItem(newSelectedItem, true);
+							sendCommand(kListSelectionChangedCmd, _selectedItem);
+						}
+
+						// Notify clients if an item was clicked
+						if (newSelectedItem >= 0)
+							sendCommand(kListItemSingleClickedCmd, _selectedItem);
+
+						applyScrollPos();
+					}
+				}
 			}
-			sendCommand(kListSelectionChangedCmd, _selectedItem);
 		}
-	} else {
-		// Regular click: clear selection and select only this underlying item
-		clearSelection();
-		_selectedItem = newSelectedItem;
-		markSelectedItem(newSelectedItem, true);
-		sendCommand(kListSelectionChangedCmd, _selectedItem);
-	}
-
-	// Notify clients if an item was clicked
-	if (newSelectedItem >= 0)
-		sendCommand(kListItemSingleClickedCmd, _selectedItem);
 
-	markAsDirty();
-}
+		_isMouseDown = false;
+		_isDragging = false;
+	}
 
-void GroupedListWidget::handleMouseUp(int x, int y, int button, int clickCount) {
 	// If this was a double click and the mouse is still over
 	// the selected item, send the double click command
 	if (clickCount == 2 && (_selectedItem == findItem(x, y))) {
@@ -346,9 +358,8 @@ void GroupedListWidget::handleCommand(CommandSender *sender, uint32 cmd, uint32
 	switch (cmd) {
 	case kSetPositionCmd:
 		if (_currentPos != (int)data) {
-			_currentPos = data;
-			checkBounds();
-			markAsDirty();
+			_scrollPos = (float)data * (kLineHeight + _itemSpacing);
+			applyScrollPos();
 
 			// Scrollbar actions cause list focus (which triggers a redraw)
 			// NOTE: ListWidget's boss is always GUI::Dialog
@@ -412,9 +423,15 @@ void GroupedListWidget::drawWidget() {
 
 	// Draw the list items
 	const int lineHeight = kLineHeight + _itemSpacing;
+	const int firstItem = (int)(_scrollPos / lineHeight);
+	const int offset = (int)_scrollPos % lineHeight;
 	const int indentSpacing = g_gui.getFontHeight();
-	for (i = 0, pos = _currentPos; i < _entriesPerPage && pos < len; i++, pos++) {
-		const int y = _y + _topPadding + lineHeight * i;
+
+	Common::Rect innerRect(_x, _y + _topPadding, _x + _w - _scrollBarWidth, _y + _h - _bottomPadding);
+	Common::Rect oldClip = g_gui.theme()->swapClipRect(innerRect.findIntersectingRect(g_gui.theme()->getClipRect()));
+
+	for (i = 0, pos = firstItem; i <= _entriesPerPage && pos < len; i++, pos++) {
+		const int y = _y + _topPadding + lineHeight * i - offset;
 		ThemeEngine::TextInversionState inverted = ThemeEngine::kTextInversionNone;
 #if 0
 		ThemeEngine::FontStyle bold = ThemeEngine::kFontStyleBold;
@@ -492,6 +509,9 @@ void GroupedListWidget::drawWidget() {
 			g_gui.theme()->drawText(r2, buffer, itemState, _drawAlign, inverted, _leftPadding, true);
 		}
 	}
+
+	g_gui.theme()->swapClipRect(oldClip);
+
 	if (_editMode)
 		EditableWidget::drawWidget();
 }
diff --git a/gui/widgets/list.cpp b/gui/widgets/list.cpp
index b9362f7059a..8d94ffe7ae9 100644
--- a/gui/widgets/list.cpp
+++ b/gui/widgets/list.cpp
@@ -79,6 +79,11 @@ ListWidget::ListWidget(Dialog *boss, const Common::String &name, const Common::U
 	_leftPadding = _rightPadding = 0;
 	_topPadding = _bottomPadding = 0;
 	_itemSpacing = 0;
+
+	_scrollPos = 0.0f;
+	_isMouseDown = false;
+	_isDragging = false;
+	_dragStartY = _dragLastY = 0;
 }
 
 ListWidget::ListWidget(Dialog *boss, int x, int y, int w, int h, bool scale, const Common::U32String &tooltip, uint32 cmd)
@@ -90,7 +95,7 @@ ListWidget::ListWidget(Dialog *boss, int x, int y, int w, int h, bool scale, con
 	_scrollBar = new ScrollBarWidget(this, _w - _scrollBarWidth, 0, _scrollBarWidth, _h);
 	_scrollBar->setTarget(this);
 
-	setFlags(WIDGET_ENABLED | WIDGET_CLEARBG | WIDGET_RETAIN_FOCUS | WIDGET_WANT_TICKLE);
+	setFlags(WIDGET_ENABLED | WIDGET_CLEARBG | WIDGET_RETAIN_FOCUS | WIDGET_WANT_TICKLE | WIDGET_TRACK_MOUSE);
 	_type = kListWidget;
 	_editMode = false;
 	_numberingMode = kListNumberingOne;
@@ -126,6 +131,11 @@ ListWidget::ListWidget(Dialog *boss, int x, int y, int w, int h, bool scale, con
 	_itemSpacing = 0;
 
 	_scrollBarWidth = 0;
+
+	_scrollPos = 0.0f;
+	_isMouseDown = false;
+	_isDragging = false;
+	_dragStartY = _dragLastY = 0;
 }
 
 ListWidget::ListWidget(Dialog *boss, int x, int y, int w, int h, const Common::U32String &tooltip, uint32 cmd)
@@ -339,63 +349,90 @@ void ListWidget::handleTickle() {
 	_scrollBar->handleTickle();
 }
 
+void ListWidget::applyScrollPos() {
+	const int lineHeight = kLineHeight + _itemSpacing;
+	int maxScroll = MAX(0, (int)(_scrollBar->_numEntries - _scrollBar->_entriesPerPage) * lineHeight);
+	if (_scrollPos < 0)
+		_scrollPos = 0;
+	if (_scrollPos > maxScroll)
+		_scrollPos = (float)maxScroll;
+
+	_currentPos = (int)(_scrollPos / lineHeight);
+	scrollBarRecalc();
+	markAsDirty();
+}
+
 void ListWidget::handleMouseDown(int x, int y, int button, int clickCount) {
 	if (!isEnabled())
 		return;
 
-	// First check whether the selection changed
-	int newSelectedItem = findItem(x, y);
+	_isMouseDown = true;
+	_isDragging = false;
+	_dragLastY = 0;
 
-	if (newSelectedItem == -1)
-		return;
+	if (button == 1) {
+		_dragStartY = y;
+		_dragLastY = y;
+	}
 
 	if (_editMode)
 		abortEditMode();
+}
 
-	// Get modifier keys
-	int modifiers = g_system->getEventManager()->getModifierState();
-	bool ctrlClick = (modifiers & Common::KBD_CTRL) != 0;
-	bool shiftClick = (modifiers & Common::KBD_SHIFT) != 0;
-
-	// Only handle multi-select if it's enabled
-	if (_multiSelectEnabled && (shiftClick || ctrlClick)) {
-		if (shiftClick && _lastSelectionStartItem != -1) {
-			// Shift+Click: Select range from last selection start to current item
-			_selectedItem = newSelectedItem;
-			selectItemRange(_lastSelectionStartItem, newSelectedItem);
-			_lastSelectionStartItem = newSelectedItem;
-			sendCommand(kListSelectionChangedCmd, _selectedItem);
-		} else if (ctrlClick) {
-			// Ctrl+Click: Add/remove from selection
-			if (isItemSelected(newSelectedItem)) {
-				markSelectedItem(newSelectedItem, false);
-			} else {
-				markSelectedItem(newSelectedItem, true);
-				_selectedItem = newSelectedItem;
-				_lastSelectionStartItem = newSelectedItem;
-			}
-			sendCommand(kListSelectionChangedCmd, _selectedItem);
-		}
-	} else {
-		// Regular click: Clear previous selection and select only this item
-		clearSelection();
-		_selectedItem = newSelectedItem;
-		markSelectedItem(newSelectedItem, true);
-		sendCommand(kListSelectionChangedCmd, _selectedItem);
-	}
+void ListWidget::handleMouseUp(int x, int y, int button, int clickCount) {
+	if (button == 1 || button == 2) {
+		if (_isMouseDown && !_isDragging) {
+			// Perform selection
+			int newSelectedItem = findItem(x, y);
+			if (newSelectedItem != -1) {
+				// Get modifier keys
+				int modifiers = g_system->getEventManager()->getModifierState();
+				bool ctrlClick = (modifiers & Common::KBD_CTRL) != 0;
+				bool shiftClick = (modifiers & Common::KBD_SHIFT) != 0;
+
+				// Only handle multi-select if it's enabled
+				if (_multiSelectEnabled && (shiftClick || ctrlClick)) {
+					if (shiftClick && _lastSelectionStartItem != -1) {
+						// Shift+Click: Select range from last selection start to current item
+						_selectedItem = newSelectedItem;
+						selectItemRange(_lastSelectionStartItem, newSelectedItem);
+						_lastSelectionStartItem = newSelectedItem;
+						sendCommand(kListSelectionChangedCmd, _selectedItem);
+					} else if (ctrlClick) {
+						// Ctrl+Click: Add/remove from selection
+						if (isItemSelected(newSelectedItem)) {
+							markSelectedItem(newSelectedItem, false);
+						} else {
+							markSelectedItem(newSelectedItem, true);
+							_selectedItem = newSelectedItem;
+							_lastSelectionStartItem = newSelectedItem;
+						}
+						sendCommand(kListSelectionChangedCmd, _selectedItem);
+					}
+				} else {
+					// Regular click: Clear previous selection and select only this item
+					clearSelection();
+					_selectedItem = newSelectedItem;
+					markSelectedItem(newSelectedItem, true);
+					sendCommand(kListSelectionChangedCmd, _selectedItem);
+				}
 
-	// Notify clients if an item was clicked
-	if (newSelectedItem >= 0) {
-		sendCommand(kListItemSingleClickedCmd, _selectedItem);
-	}
+				// Notify clients if an item was clicked
+				if (newSelectedItem >= 0) {
+					sendCommand(kListItemSingleClickedCmd, _selectedItem);
+				}
 
 	// TODO: Determine where inside the string the user clicked and place the
 	// caret accordingly.
 	// See _editScrollOffset and EditTextWidget::handleMouseDown.
-	markAsDirty();
-}
+				markAsDirty();
+			}
+		}
+
+		_isMouseDown = false;
+		_isDragging = false;
+	}
 
-void ListWidget::handleMouseUp(int x, int y, int button, int clickCount) {
 	// If this was a double click and the mouse is still over
 	// the selected item, send the double click command
 	if (clickCount == 2 && (_selectedItem == findItem(x, y)) &&
@@ -412,6 +449,22 @@ void ListWidget::handleMouseMoved(int x, int y, int button) {
 	if (!isEnabled())
 		return;
 
+	if (_isMouseDown && _dragLastY != 0) {
+		if (!_isDragging && ABS(y - _dragStartY) > kDragThreshold)
+			_isDragging = true;
+
+		if (_isDragging) {
+			int deltaY = _dragLastY - y;
+			_dragLastY = y;
+
+			if (deltaY != 0) {
+				_scrollPos += deltaY;
+				applyScrollPos();
+			}
+			return;
+		}
+	}
+
 	// Determine if we are inside the widget
 	if (x < 0 || x > _w)
 		return;
@@ -435,9 +488,12 @@ void ListWidget::handleMouseLeft(int button) {
 
 
 int ListWidget::findItem(int x, int y) const {
-	if (y < _topPadding) return -1;
-	int item = (y - _topPadding) / (kLineHeight + _itemSpacing) + _currentPos;
-	if (isItemVisible(item) && item < (int)_list.size())
+	if (y < _topPadding || y >= _h - _bottomPadding)
+		return -1;
+
+	int item = (y - _topPadding + (int)_scrollPos) / (kLineHeight + _itemSpacing);
+
+	if (item >= 0 && item < (int)_list.size())
 		return item;
 	else
 		return -1;
@@ -764,9 +820,8 @@ void ListWidget::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
 	switch (cmd) {
 	case kSetPositionCmd:
 		if (_currentPos != (int)data) {
-			_currentPos = data;
-			checkBounds();
-			markAsDirty();
+			_scrollPos = (float)data * (kLineHeight + _itemSpacing);
+			applyScrollPos();
 
 			// Scrollbar actions cause list focus (which triggers a redraw)
 			// NOTE: ListWidget's boss is always GUI::Dialog
@@ -788,8 +843,14 @@ void ListWidget::drawWidget() {
 
 	// Draw the list items
 	const int lineHeight = kLineHeight + _itemSpacing;
-	for (i = 0, pos = _currentPos; i < _entriesPerPage && pos < len; i++, pos++) {
-		const int y = _y + _topPadding + lineHeight * i;
+	const int firstItem = (int)(_scrollPos / lineHeight);
+	const int offset = (int)_scrollPos % lineHeight;
+
+	Common::Rect innerRect(_x, _y + _topPadding, _x + _w - _scrollBarWidth, _y + _h - _bottomPadding);
+	Common::Rect oldClip = g_gui.theme()->swapClipRect(innerRect.findIntersectingRect(g_gui.theme()->getClipRect()));
+
+	for (i = 0, pos = firstItem; i <= _entriesPerPage && pos < len; i++, pos++) {
+		const int y = _y + _topPadding + lineHeight * i - offset;
 		ThemeEngine::TextInversionState inverted = ThemeEngine::kTextInversionNone;
 
 		// Draw the selected item inverted, on a highlighted background.
@@ -848,6 +909,8 @@ void ListWidget::drawWidget() {
 		}
 	}
 
+	g_gui.theme()->swapClipRect(oldClip);
+
 	if (_editMode) {
 		EditableWidget::drawWidget();
 	}
diff --git a/gui/widgets/list.h b/gui/widgets/list.h
index f99b2574465..2e271582a62 100644
--- a/gui/widgets/list.h
+++ b/gui/widgets/list.h
@@ -77,6 +77,12 @@ protected:
 	ScrollBarWidget	*_scrollBar;
 	int				_currentKeyDown;
 
+	float			_scrollPos;
+	bool			_isMouseDown;
+	bool			_isDragging;
+	int				_dragStartY;
+	int				_dragLastY;
+
 	Common::String	_quickSelectStr;
 	uint32			_quickSelectTime;
 
@@ -101,6 +107,9 @@ protected:
 
 	FilterMatcher	_filterMatcher;
 	void			*_filterMatcherArg;
+
+	static const int kDragThreshold = 5;
+
 public:
 	ListWidget(Dialog *boss, const Common::String &name, const Common::U32String &tooltip = Common::U32String(), uint32 cmd = 0);
 	ListWidget(Dialog *boss, int x, int y, int w, int h, bool scale, const Common::U32String &tooltip = Common::U32String(), uint32 cmd = 0);
@@ -157,6 +166,7 @@ public:
 	void setFilter(const Common::U32String &filter, bool redraw = true);
 
 	void handleTickle() override;
+	void applyScrollPos();
 	void handleMouseDown(int x, int y, int button, int clickCount) override;
 	void handleMouseUp(int x, int y, int button, int clickCount) override;
 	void handleMouseWheel(int x, int y, int direction) override;


Commit: cb71d7b6cf946945add11834a6d957726d27dbc3
    https://github.com/scummvm/scummvm/commit/cb71d7b6cf946945add11834a6d957726d27dbc3
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-04-20T23:33:23+02:00

Commit Message:
GUI: Implement Fluid scroll in Launcher List

Changed paths:
    gui/widgets/groupedlist.cpp
    gui/widgets/list.cpp
    gui/widgets/list.h


diff --git a/gui/widgets/groupedlist.cpp b/gui/widgets/groupedlist.cpp
index cca93701185..45b12838f6a 100644
--- a/gui/widgets/groupedlist.cpp
+++ b/gui/widgets/groupedlist.cpp
@@ -29,6 +29,7 @@
 #include "gui/widgets/scrollbar.h"
 #include "gui/dialog.h"
 #include "gui/gui-manager.h"
+#include "gui/animation/FluidScroll.h"
 
 #include "gui/ThemeEval.h"
 
@@ -265,6 +266,7 @@ void GroupedListWidget::handleMouseDown(int x, int y, int button, int clickCount
 	if (button == 1) {
 		_dragStartY = y;
 		_dragLastY = y;
+		_fluidScroller->stopAnimation();
 	}
 
 	// TODO: Determine where inside the string the user clicked and place the
@@ -276,6 +278,9 @@ void GroupedListWidget::handleMouseDown(int x, int y, int button, int clickCount
 
 void GroupedListWidget::handleMouseUp(int x, int y, int button, int clickCount) {
 	if (button == 1 || button == 2) {
+		if (_isMouseDown && button == 1 && _isDragging)
+			_fluidScroller->startFling();
+
 		if (_isMouseDown && !_isDragging) {
 			int newSelectedItem = findItem(x, y);
 			if (newSelectedItem != -1) {
@@ -351,7 +356,13 @@ void GroupedListWidget::handleMouseUp(int x, int y, int button, int clickCount)
 }
 
 void GroupedListWidget::handleMouseWheel(int x, int y, int direction) {
-	_scrollBar->handleMouseWheel(x, y, direction);
+	const float stepping = (float)_scrollBar->_singleStep * direction;
+
+	if (stepping == 0.0f)
+		return;
+
+	_fluidScroller->stopAnimation();
+	_fluidScroller->feedWheel(g_system->getMillis(), stepping);
 }
 
 void GroupedListWidget::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
@@ -359,6 +370,8 @@ void GroupedListWidget::handleCommand(CommandSender *sender, uint32 cmd, uint32
 	case kSetPositionCmd:
 		if (_currentPos != (int)data) {
 			_scrollPos = (float)data * (kLineHeight + _itemSpacing);
+			_fluidScroller->stopAnimation();
+			_scrollPos = _fluidScroller->setPosition(_scrollPos, false);
 			applyScrollPos();
 
 			// Scrollbar actions cause list focus (which triggers a redraw)
@@ -423,8 +436,8 @@ void GroupedListWidget::drawWidget() {
 
 	// Draw the list items
 	const int lineHeight = kLineHeight + _itemSpacing;
-	const int firstItem = (int)(_scrollPos / lineHeight);
-	const int offset = (int)_scrollPos % lineHeight;
+	const int firstItem = MAX(0, (int)(_scrollPos / lineHeight));
+	const int offset = _scrollPos < 0 ? (int)_scrollPos : (int)_scrollPos % lineHeight;
 	const int indentSpacing = g_gui.getFontHeight();
 
 	Common::Rect innerRect(_x, _y + _topPadding, _x + _w - _scrollBarWidth, _y + _h - _bottomPadding);
diff --git a/gui/widgets/list.cpp b/gui/widgets/list.cpp
index 8d94ffe7ae9..b7761d0d31f 100644
--- a/gui/widgets/list.cpp
+++ b/gui/widgets/list.cpp
@@ -27,6 +27,7 @@
 #include "gui/widgets/scrollbar.h"
 #include "gui/dialog.h"
 #include "gui/gui-manager.h"
+#include "gui/animation/FluidScroll.h"
 
 #include "gui/ThemeEval.h"
 
@@ -81,6 +82,7 @@ ListWidget::ListWidget(Dialog *boss, const Common::String &name, const Common::U
 	_itemSpacing = 0;
 
 	_scrollPos = 0.0f;
+	_fluidScroller = new FluidScroller();
 	_isMouseDown = false;
 	_isDragging = false;
 	_dragStartY = _dragLastY = 0;
@@ -133,11 +135,16 @@ ListWidget::ListWidget(Dialog *boss, int x, int y, int w, int h, bool scale, con
 	_scrollBarWidth = 0;
 
 	_scrollPos = 0.0f;
+	_fluidScroller = new FluidScroller();
 	_isMouseDown = false;
 	_isDragging = false;
 	_dragStartY = _dragLastY = 0;
 }
 
+ListWidget::~ListWidget() {
+	delete _fluidScroller;
+}
+
 ListWidget::ListWidget(Dialog *boss, int x, int y, int w, int h, const Common::U32String &tooltip, uint32 cmd)
 	: ListWidget(boss, x, y, w, h, false, tooltip, cmd) {
 }
@@ -337,29 +344,32 @@ void ListWidget::scrollTo(int item) {
 }
 
 void ListWidget::scrollBarRecalc() {
+	const int lineHeight = kLineHeight + _itemSpacing;
 	_scrollBar->_numEntries = _list.size();
 	_scrollBar->_entriesPerPage = _entriesPerPage;
 	_scrollBar->_currentPos = _currentPos;
+	_scrollBar->_singleStep = lineHeight;
 	_scrollBar->recalc();
+
+	int maxScroll = MAX(0, (int)(_scrollBar->_numEntries - _scrollBar->_entriesPerPage) * lineHeight);
+	_fluidScroller->setBounds((float)maxScroll, _h - _topPadding - _bottomPadding);
 }
 
 void ListWidget::handleTickle() {
 	if (_editMode)
 		EditableWidget::handleTickle();
 	_scrollBar->handleTickle();
+
+	if (_fluidScroller->update(g_system->getMillis(), _scrollPos)) {
+		applyScrollPos();
+	}
 }
 
 void ListWidget::applyScrollPos() {
 	const int lineHeight = kLineHeight + _itemSpacing;
-	int maxScroll = MAX(0, (int)(_scrollBar->_numEntries - _scrollBar->_entriesPerPage) * lineHeight);
-	if (_scrollPos < 0)
-		_scrollPos = 0;
-	if (_scrollPos > maxScroll)
-		_scrollPos = (float)maxScroll;
-
 	_currentPos = (int)(_scrollPos / lineHeight);
 	scrollBarRecalc();
-	markAsDirty();
+	g_gui.scheduleTopDialogRedraw();
 }
 
 void ListWidget::handleMouseDown(int x, int y, int button, int clickCount) {
@@ -369,6 +379,7 @@ void ListWidget::handleMouseDown(int x, int y, int button, int clickCount) {
 	_isMouseDown = true;
 	_isDragging = false;
 	_dragLastY = 0;
+	_fluidScroller->stopAnimation();
 
 	if (button == 1) {
 		_dragStartY = y;
@@ -381,6 +392,9 @@ void ListWidget::handleMouseDown(int x, int y, int button, int clickCount) {
 
 void ListWidget::handleMouseUp(int x, int y, int button, int clickCount) {
 	if (button == 1 || button == 2) {
+		if (_isMouseDown && button == 1 && _isDragging)
+			_fluidScroller->startFling();
+
 		if (_isMouseDown && !_isDragging) {
 			// Perform selection
 			int newSelectedItem = findItem(x, y);
@@ -442,7 +456,13 @@ void ListWidget::handleMouseUp(int x, int y, int button, int clickCount) {
 }
 
 void ListWidget::handleMouseWheel(int x, int y, int direction) {
-	_scrollBar->handleMouseWheel(x, y, direction);
+	const float stepping = (float)_scrollBar->_singleStep * direction;
+
+	if (stepping == 0.0f)
+		return;
+
+	_fluidScroller->stopAnimation();
+	_fluidScroller->feedWheel(g_system->getMillis(), stepping);
 }
 
 void ListWidget::handleMouseMoved(int x, int y, int button) {
@@ -458,7 +478,9 @@ void ListWidget::handleMouseMoved(int x, int y, int button) {
 			_dragLastY = y;
 
 			if (deltaY != 0) {
-				_scrollPos += deltaY;
+				_fluidScroller->feedDrag(g_system->getMillis(), deltaY);
+				_scrollPos = _fluidScroller->getVisualPosition();
+
 				applyScrollPos();
 			}
 			return;
@@ -821,6 +843,8 @@ void ListWidget::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
 	case kSetPositionCmd:
 		if (_currentPos != (int)data) {
 			_scrollPos = (float)data * (kLineHeight + _itemSpacing);
+			_fluidScroller->stopAnimation();
+			_scrollPos = _fluidScroller->setPosition(_scrollPos, false);
 			applyScrollPos();
 
 			// Scrollbar actions cause list focus (which triggers a redraw)
@@ -843,8 +867,8 @@ void ListWidget::drawWidget() {
 
 	// Draw the list items
 	const int lineHeight = kLineHeight + _itemSpacing;
-	const int firstItem = (int)(_scrollPos / lineHeight);
-	const int offset = (int)_scrollPos % lineHeight;
+	const int firstItem = MAX(0, (int)(_scrollPos / lineHeight));
+	const int offset = _scrollPos < 0 ? (int)_scrollPos : (int)_scrollPos % lineHeight;
 
 	Common::Rect innerRect(_x, _y + _topPadding, _x + _w - _scrollBarWidth, _y + _h - _bottomPadding);
 	Common::Rect oldClip = g_gui.theme()->swapClipRect(innerRect.findIntersectingRect(g_gui.theme()->getClipRect()));
diff --git a/gui/widgets/list.h b/gui/widgets/list.h
index 2e271582a62..c0196d58d44 100644
--- a/gui/widgets/list.h
+++ b/gui/widgets/list.h
@@ -30,6 +30,7 @@
 namespace GUI {
 
 class ScrollBarWidget;
+class FluidScroller;
 
 enum NumberingMode {
 	kListNumberingOff	= -1,
@@ -60,6 +61,7 @@ public:
 	};
 
 	typedef Common::Array<ListData> ListDataArray;
+	~ListWidget() override;
 
 protected:
 	Common::U32StringArray	_list;
@@ -78,6 +80,7 @@ protected:
 	int				_currentKeyDown;
 
 	float			_scrollPos;
+	FluidScroller	*_fluidScroller;
 	bool			_isMouseDown;
 	bool			_isDragging;
 	int				_dragStartY;


Commit: a2abc4c1e516f5b3ba5755eac825717b40349e0f
    https://github.com/scummvm/scummvm/commit/a2abc4c1e516f5b3ba5755eac825717b40349e0f
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-04-20T23:33:23+02:00

Commit Message:
GUI: Centralize mouse wheel handling in FluidScroller

Changed paths:
    gui/about.cpp
    gui/animation/FluidScroll.cpp
    gui/animation/FluidScroll.h
    gui/widgets/grid.cpp
    gui/widgets/groupedlist.cpp
    gui/widgets/list.cpp


diff --git a/gui/about.cpp b/gui/about.cpp
index c7968731761..c7659ec9122 100644
--- a/gui/about.cpp
+++ b/gui/about.cpp
@@ -436,21 +436,8 @@ void AboutDialog::handleMouseMoved(int x, int y, int button) {
 }
 
 void AboutDialog::handleMouseWheel(int x, int y, int direction) {
-	const int stepping = 5 * _lineHeight * direction;
-
-	if (stepping == 0)
-		return;
-
 	_autoScroll = false;
-	_fluidScroller->stopAnimation();
-	_fluidScroller->feedWheel(g_system->getMillis(), (float)stepping);
-
-	if (_scrollbar) {
-		_scrollbar->_currentPos = (int)_fluidScroller->getVisualPosition();
-		_scrollbar->recalc();
-	}
-
-	drawDialog(kDrawLayerForeground);
+	_fluidScroller->handleMouseWheel(direction, (float)_lineHeight);
 }
 
 void AboutDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
diff --git a/gui/animation/FluidScroll.cpp b/gui/animation/FluidScroll.cpp
index d714f7d0d0b..830f31bd269 100644
--- a/gui/animation/FluidScroll.cpp
+++ b/gui/animation/FluidScroll.cpp
@@ -169,6 +169,15 @@ void FluidScroller::feedWheel(uint32 time, float deltaY) {
 	startFling(velocity);
 }
 
+void FluidScroller::handleMouseWheel(int direction, float stepSize, float multiplier) {
+	float stepping = stepSize * (float)direction * multiplier;
+	if (stepping == 0.0f)
+		return;
+
+	stopAnimation();
+	feedWheel(g_system->getMillis(), stepping);
+}
+
 void FluidScroller::absorb(float velocity, float distance) {
 	_mode = kModeSpringBack;
 	_startTime = g_system->getMillis();
diff --git a/gui/animation/FluidScroll.h b/gui/animation/FluidScroll.h
index 15e14f06ed1..4c2114a6b21 100644
--- a/gui/animation/FluidScroll.h
+++ b/gui/animation/FluidScroll.h
@@ -64,6 +64,14 @@ public:
 	// Record a wheel tick and start/update a fling
 	void feedWheel(uint32 time, float deltaY);
 
+	/**
+	 * Handle mouse wheel input
+	 * @param direction Scroll direction
+	 * @param stepSize usually _scrollBar->_singleStep
+	 * @param multiplier Speed multiplier for the scroll
+	 */
+	void handleMouseWheel(int direction, float stepSize, float multiplier = 1.0f);
+
 	// Check if there is an active animation (fling or spring-back)
 	bool isAnimating() const { return _mode != kModeNone; }
 
diff --git a/gui/widgets/grid.cpp b/gui/widgets/grid.cpp
index 93bdafd1bad..0132e4060be 100644
--- a/gui/widgets/grid.cpp
+++ b/gui/widgets/grid.cpp
@@ -901,6 +901,8 @@ void GridWidget::scrollToEntry(int id, bool forceToTop) {
 		}
 	}
 	handleCommand(this, kSetPositionCmd, newScrollPos);
+	_scrollPos = newScrollPos;
+	applyScrollPos();
 }
 
 void GridWidget::updateGrid() {
@@ -1010,12 +1012,7 @@ void GridWidget::selectVisualRange(int startPos, int endPos) {
 }
 
 void GridWidget::handleMouseWheel(int x, int y, int direction) {
-	const float stepping = (float)_scrollBar->_singleStep * direction;
-	if (stepping == 0.0f)
-		return;
-
-	_fluidScroller->stopAnimation();
-	_fluidScroller->feedWheel(g_system->getMillis(), stepping);
+	_fluidScroller->handleMouseWheel(direction, (float)_scrollBar->_singleStep);
 }
 
 void GridWidget::handleMouseDown(int x, int y, int button, int clickCount) {
@@ -1287,12 +1284,13 @@ void GridWidget::openTrayAtSelected() {
 void GridWidget::scrollBarRecalc() {
 	_scrollBar->_numEntries = _innerHeight;
 	_scrollBar->_entriesPerPage = _scrollWindowHeight - 2 * _scrollWindowPaddingY;
-	_scrollBar->_currentPos = (int)_scrollPos;
+	int maxScroll = MAX(0, _scrollBar->_numEntries - _scrollBar->_entriesPerPage);
+	_scrollBar->_currentPos = CLIP<int>((int)_scrollPos, 0, maxScroll);
 	_scrollBar->_singleStep = kLineHeight;
 
 	_scrollBar->recalc();
 
-	int maxScroll = MAX(0, _scrollBar->_numEntries - _scrollBar->_entriesPerPage);
+	maxScroll = MAX(0, _scrollBar->_numEntries - _scrollBar->_entriesPerPage);
 	_fluidScroller->setBounds((float)maxScroll, _scrollBar->_entriesPerPage);
 }
 
diff --git a/gui/widgets/groupedlist.cpp b/gui/widgets/groupedlist.cpp
index 45b12838f6a..cd5cf359aad 100644
--- a/gui/widgets/groupedlist.cpp
+++ b/gui/widgets/groupedlist.cpp
@@ -356,13 +356,7 @@ void GroupedListWidget::handleMouseUp(int x, int y, int button, int clickCount)
 }
 
 void GroupedListWidget::handleMouseWheel(int x, int y, int direction) {
-	const float stepping = (float)_scrollBar->_singleStep * direction;
-
-	if (stepping == 0.0f)
-		return;
-
-	_fluidScroller->stopAnimation();
-	_fluidScroller->feedWheel(g_system->getMillis(), stepping);
+	_fluidScroller->handleMouseWheel(direction, (float)_scrollBar->_singleStep);
 }
 
 void GroupedListWidget::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
diff --git a/gui/widgets/list.cpp b/gui/widgets/list.cpp
index b7761d0d31f..5f75f3a5cbd 100644
--- a/gui/widgets/list.cpp
+++ b/gui/widgets/list.cpp
@@ -337,6 +337,8 @@ void ListWidget::scrollTo(int item) {
 
 	if (_currentPos != item) {
 		_currentPos = item;
+		_scrollPos = (float)_currentPos * (kLineHeight + _itemSpacing);
+		_fluidScroller->setPosition(_scrollPos, false);
 		checkBounds();
 		scrollBarRecalc();
 		markAsDirty();
@@ -347,7 +349,8 @@ void ListWidget::scrollBarRecalc() {
 	const int lineHeight = kLineHeight + _itemSpacing;
 	_scrollBar->_numEntries = _list.size();
 	_scrollBar->_entriesPerPage = _entriesPerPage;
-	_scrollBar->_currentPos = _currentPos;
+	int maxIndex = MAX(0, (int)_list.size() - _entriesPerPage);
+	_scrollBar->_currentPos = CLIP<int>(_currentPos, 0, maxIndex);
 	_scrollBar->_singleStep = lineHeight;
 	_scrollBar->recalc();
 
@@ -456,13 +459,7 @@ void ListWidget::handleMouseUp(int x, int y, int button, int clickCount) {
 }
 
 void ListWidget::handleMouseWheel(int x, int y, int direction) {
-	const float stepping = (float)_scrollBar->_singleStep * direction;
-
-	if (stepping == 0.0f)
-		return;
-
-	_fluidScroller->stopAnimation();
-	_fluidScroller->feedWheel(g_system->getMillis(), stepping);
+	_fluidScroller->handleMouseWheel(direction, (float)_scrollBar->_singleStep);
 }
 
 void ListWidget::handleMouseMoved(int x, int y, int button) {
@@ -994,8 +991,10 @@ void ListWidget::scrollToCurrent() {
 	}
 
 	checkBounds();
+	_scrollPos = (float)_currentPos * (kLineHeight + _itemSpacing);
 	_scrollBar->_currentPos = _currentPos;
 	_scrollBar->recalc();
+	_fluidScroller->setPosition(_scrollPos, false);
 }
 
 void ListWidget::scrollToEnd() {


Commit: 5ef242d11273bf643db3bc5de70d78b426efb7af
    https://github.com/scummvm/scummvm/commit/5ef242d11273bf643db3bc5de70d78b426efb7af
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-04-20T23:33:23+02:00

Commit Message:
GUI: Implenet Fluid Scroll in Richtext widget

- Implement Fluid Scroll in HelpDialog

Changed paths:
    gui/helpdialog.cpp
    gui/helpdialog.h
    gui/widgets/richtext.cpp
    gui/widgets/richtext.h
    gui/widgets/tab.cpp
    gui/widgets/tab.h


diff --git a/gui/helpdialog.cpp b/gui/helpdialog.cpp
index 07ce8b2b973..635d3c8c598 100644
--- a/gui/helpdialog.cpp
+++ b/gui/helpdialog.cpp
@@ -156,4 +156,8 @@ void HelpDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
 	}
 }
 
+void HelpDialog::handleTickle() {
+	_tab->handleTickle();
+}
+
 } // End of namespace GUI
diff --git a/gui/helpdialog.h b/gui/helpdialog.h
index 59ed5208ffb..3e8b0a34ff3 100644
--- a/gui/helpdialog.h
+++ b/gui/helpdialog.h
@@ -39,6 +39,7 @@ public:
 	HelpDialog();
 
 	void handleCommand(CommandSender *sender, uint32 cmd, uint32 data) override;
+	void handleTickle() override;
 
 private:
 	void addTabs(const char * const *tabs);
diff --git a/gui/widgets/richtext.cpp b/gui/widgets/richtext.cpp
index dc78201fb92..a63a254bbb9 100644
--- a/gui/widgets/richtext.cpp
+++ b/gui/widgets/richtext.cpp
@@ -33,6 +33,7 @@
 
 #include "gui/widgets/richtext.h"
 #include "gui/widgets/scrollbar.h"
+#include "gui/animation/FluidScroll.h"
 
 namespace GUI {
 
@@ -65,7 +66,7 @@ RichTextWidget::RichTextWidget(GuiObject *boss, const Common::String &name, cons
 }
 
 void RichTextWidget::init() {
-	setFlags(WIDGET_ENABLED | WIDGET_CLEARBG | WIDGET_TRACK_MOUSE | WIDGET_DYN_TOOLTIP);
+	setFlags(WIDGET_ENABLED | WIDGET_CLEARBG | WIDGET_TRACK_MOUSE | WIDGET_DYN_TOOLTIP | WIDGET_WANT_TICKLE | WIDGET_RETAIN_FOCUS);
 
 	_type = kRichTextWidget;
 
@@ -81,6 +82,10 @@ void RichTextWidget::init() {
 	_textHeight = MAX(1, _h - 2 * _innerMargin);
 
 	_limitH = 140;
+
+	_scrollPos = 0.0f;
+	_fluidScroller = new FluidScroller();
+	_isDragging = false;
 }
 
 
@@ -94,25 +99,33 @@ RichTextWidget::~RichTextWidget() {
 	if (_cachedTextSurface)
 		_cachedTextSurface->free();
 	delete _cachedTextSurface;
+	delete _fluidScroller;
 }
 
 void RichTextWidget::handleMouseWheel(int x, int y, int direction) {
-	_verticalScroll->handleMouseWheel(x, y, direction);
+	_fluidScroller->handleMouseWheel(direction, (float)_verticalScroll->_singleStep);
+	applyScrollPos();
 }
 
 void RichTextWidget::handleMouseDown(int x, int y, int button, int clickCount) {
 	_mouseDownY = _mouseDownStartY = y;
+	_fluidScroller->stopAnimation();
 }
 
 void RichTextWidget::handleMouseUp(int x, int y, int button, int clickCount) {
+	if (_isDragging)
+		_fluidScroller->startFling();
+
 	// Allow some tiny finger slipping
-	if (ABS(_mouseDownY - _mouseDownStartY) > 5) {
+	if (ABS(_mouseDownY - _mouseDownStartY) > 5 || _isDragging) {
 		_mouseDownY = _mouseDownStartY = 0;
+		_isDragging = false;
 
 		return;
 	}
 
 	_mouseDownY = _mouseDownStartY = 0;
+	_isDragging = false;
 
 	if (!_txtWnd)
 		return;
@@ -136,23 +149,31 @@ void RichTextWidget::handleMouseMoved(int x, int y, int button) {
 	if (_mouseDownStartY == 0 || _mouseDownY == y || !_txtWnd)
 		return;
 
-	int h = _txtWnd->getTextHeight();
-	int prevScrolledY = _scrolledY;
+	int deltaY = _mouseDownY - y;
 
-	_scrolledY = CLIP(_scrolledY - (y - _mouseDownY), 0, h);
+	if (!_isDragging && ABS(deltaY) > 5)
+		_isDragging = true;
 
-	_mouseDownY = y;
+	if (_isDragging) {
+		_mouseDownY = y;
+		if (deltaY != 0) {
+			_fluidScroller->feedDrag(g_system->getMillis(), deltaY);
+			applyScrollPos();
+		}
+	}
+}
 
-	if (_scrolledY == prevScrolledY)
-		return;
+void RichTextWidget::handleTickle() {
+	if (_fluidScroller->update(g_system->getMillis(), _scrollPos))
+		applyScrollPos();
+}
 
-	recalc();
+void RichTextWidget::applyScrollPos() {
+	_scrollPos = _fluidScroller->getVisualPosition();
+	_scrolledY = (int)_scrollPos;
+	int maxScroll = MAX(0, _txtWnd->getTextHeight() - _limitH);
+	_verticalScroll->_currentPos = CLIP((int)_scrolledY, 0, (int)maxScroll);
 	_verticalScroll->recalc();
-
-	// Update scrollbar position
-	_verticalScroll->_currentPos = _scrolledY;
-	_verticalScroll->checkBounds(_verticalScroll->_currentPos);
-
 	markAsDirty();
 }
 
@@ -168,6 +189,9 @@ void RichTextWidget::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 	switch (cmd) {
 	case kSetPositionCmd:
 		_scrolledY = _verticalScroll->_currentPos;
+		_scrollPos = _scrolledY;
+		_fluidScroller->stopAnimation();
+		_scrollPos = _fluidScroller->setPosition(_scrollPos, false);
 		reflowLayout();
 		g_gui.scheduleTopDialogRedraw();
 		break;
@@ -212,18 +236,16 @@ void RichTextWidget::recalc() {
 		_surface->create(_textWidth, _textHeight, g_gui.getWM()->_pixelformat);
 
 		int h = _txtWnd->getTextHeight();
-		if (h <= _limitH)
-			_scrolledY = 0;
-		if (_scrolledY > h - _limitH)
-			_scrolledY = MAX(0, h - _limitH);
+		int maxScroll = MAX(0, h - _limitH);
 		_verticalScroll->_numEntries = h;
-		_verticalScroll->_currentPos = _scrolledY;
+		_verticalScroll->_currentPos = CLIP((int)_scrolledY, 0, (int)maxScroll);
 		_verticalScroll->_entriesPerPage = _limitH;
 		_verticalScroll->_singleStep = _h / 4;
 		_verticalScroll->setPos(_w - _scrollbarWidth, 0);
 		_verticalScroll->setSize(_scrollbarWidth, _h - 1);
 		_verticalScroll->setVisible(_verticalScroll->_numEntries > _limitH); //show when there is something to scroll
 		_verticalScroll->recalc();
+		_fluidScroller->setBounds((float)maxScroll, (float)_limitH);
 	}
 }
 
@@ -297,18 +319,16 @@ void RichTextWidget::createWidget() {
 	}
 
 	int h = _txtWnd->getTextHeight();
-	if (h <= _limitH)
-		_scrolledY = 0;
-	if (_scrolledY > h - _limitH)
-		_scrolledY = MAX(0, h - _limitH);
+	int maxScroll = MAX(0, h - _limitH);
 	_verticalScroll->_numEntries = h;
-	_verticalScroll->_currentPos = _scrolledY;
+	_verticalScroll->_currentPos = CLIP((int)_scrolledY, 0, (int)maxScroll);
 	_verticalScroll->_entriesPerPage = _limitH;
 	_verticalScroll->_singleStep = _h / 4;
 	_verticalScroll->setPos(_w - _scrollbarWidth, 0);
 	_verticalScroll->setSize(_scrollbarWidth, _h - 1);
 	_verticalScroll->setVisible(_verticalScroll->_numEntries > _limitH); //show when there is something to scroll
 	_verticalScroll->recalc();
+	_fluidScroller->setBounds((float)maxScroll, (float)_limitH);
 }
 
 void RichTextWidget::reflowLayout() {
@@ -342,10 +362,12 @@ void RichTextWidget::drawWidget() {
 
 	if (_cachedTextSurface) {
 		int cachedHeight = _cachedTextSurface->h;
-		int maxY = MAX(0, cachedHeight - _textHeight);
-		int srcY = CLIP((int)_scrolledY, 0, maxY);
+		int srcY = _scrolledY < 0 ? 0 : (int)_scrolledY;
+		int destY = _scrolledY < 0 ? -(int)_scrolledY : 0;
 
-		_surface->simpleBlitFrom(*_cachedTextSurface, Common::Rect(0, srcY, _textWidth, MIN(srcY + _textHeight, cachedHeight)), Common::Point(0, 0));
+		if (srcY < cachedHeight)
+			_surface->simpleBlitFrom(*_cachedTextSurface, Common::Rect(0, srcY, _textWidth,
+			MIN(srcY + _textHeight - destY, cachedHeight)), Common::Point(0, destY));
 	} else
 		_txtWnd->draw(_surface, 0, _scrolledY, _textWidth, _textHeight, 0, 0);
 
diff --git a/gui/widgets/richtext.h b/gui/widgets/richtext.h
index 6b80fba7ad3..6e4adc7487f 100644
--- a/gui/widgets/richtext.h
+++ b/gui/widgets/richtext.h
@@ -33,6 +33,7 @@ class ManagedSurface;
 namespace GUI {
 
 class ScrollBarWidget;
+class FluidScroller;
 
 /* RichTextWidget */
 class RichTextWidget : public Widget, public CommandSender {
@@ -52,6 +53,10 @@ protected:
 	int _textWidth;
 	int _textHeight;
 
+	float _scrollPos;
+	FluidScroller *_fluidScroller;
+	bool _isDragging;
+
 	Common::Path _imageArchive;
 
 public:
@@ -69,17 +74,20 @@ public:
 	void handleMouseDown(int x, int y, int button, int clickCount) override;
 	void handleMouseUp(int x, int y, int button, int clickCount) override;
 	void handleMouseMoved(int x, int y, int button) override;
+	void handleTickle() override;
 	void handleTooltipUpdate(int x, int y) override;
 
 	void markAsDirty() override;
 
 	bool containsWidget(Widget *) const override;
+	bool wantsFocus() override { return true; }
 
 	void setImageArchive(const Common::Path &fname) { _imageArchive = fname; }
 
 protected:
 	void init();
 	void recalc();
+	void applyScrollPos();
 	void drawWidget() override;
 	void createWidget();
 	void ensureWidget();
diff --git a/gui/widgets/tab.cpp b/gui/widgets/tab.cpp
index 83354982ff0..a36fd3deff3 100644
--- a/gui/widgets/tab.cpp
+++ b/gui/widgets/tab.cpp
@@ -62,7 +62,7 @@ TabWidget::TabWidget(GuiObject *boss, const Common::String &name, ThemeEngine::T
 }
 
 void TabWidget::init() {
-	setFlags(WIDGET_ENABLED | WIDGET_TRACK_MOUSE);
+	setFlags(WIDGET_ENABLED | WIDGET_TRACK_MOUSE | WIDGET_WANT_TICKLE);
 	_type = kTabWidget;
 	_activeTab = -1;
 	_firstVisibleTab = 0;
@@ -300,6 +300,15 @@ void TabWidget::handleMouseWheel(int x, int y, int direction) {
 	}
 }
 
+void TabWidget::handleTickle() {
+	Widget *w = _firstWidget;
+	while (w) {
+		if (w->getFlags() & WIDGET_WANT_TICKLE)
+			w->handleTickle();
+		w = w->next();
+	}
+}
+
 void TabWidget::adjustTabs(int value) {
 	// Determine which tab is next
 	int tabID = _activeTab + value;
diff --git a/gui/widgets/tab.h b/gui/widgets/tab.h
index 26f531531a4..2026d71ff18 100644
--- a/gui/widgets/tab.h
+++ b/gui/widgets/tab.h
@@ -113,6 +113,7 @@ public:
 	void handleMouseLeft(int button) override { _lastRead = -1; };
 	bool handleKeyDown(Common::KeyState state) override;
 	void handleMouseWheel(int x, int y, int direction) override;
+	void handleTickle() override;
 	void handleCommand(CommandSender *sender, uint32 cmd, uint32 data) override;
 	virtual int getFirstVisible() const;
 	virtual void setFirstVisible(int tabID, bool adjustIfRoom = false);


Commit: 441f97f4e4e8fd4e5b3207bbca0f30319eae1624
    https://github.com/scummvm/scummvm/commit/441f97f4e4e8fd4e5b3207bbca0f30319eae1624
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-04-20T23:33:23+02:00

Commit Message:
GUI: Implement drag to scroll in ScrollContainer

Changed paths:
    gui/widgets/scrollcontainer.cpp
    gui/widgets/scrollcontainer.h


diff --git a/gui/widgets/scrollcontainer.cpp b/gui/widgets/scrollcontainer.cpp
index 0bc8ba2c21c..24c6c281d49 100644
--- a/gui/widgets/scrollcontainer.cpp
+++ b/gui/widgets/scrollcontainer.cpp
@@ -38,7 +38,7 @@ ScrollContainerWidget::ScrollContainerWidget(GuiObject *boss, const Common::Stri
 }
 
 void ScrollContainerWidget::init() {
-	setFlags(WIDGET_ENABLED);
+	setFlags(WIDGET_ENABLED | WIDGET_TRACK_MOUSE);
 	_type = kScrollContainerWidget;
 	_backgroundType = ThemeEngine::kWidgetBackgroundPlain;
 	_verticalScroll = new ScrollBarWidget(this, _w, 0, 16, _h);
@@ -53,6 +53,63 @@ void ScrollContainerWidget::handleMouseWheel(int x, int y, int direction) {
 	_verticalScroll->handleMouseWheel(x, y, direction);
 }
 
+void ScrollContainerWidget::handleMouseDown(int x, int y, int button, int clickCount) {
+	_mouseDownY = _mouseDownStartY = y;
+	Widget *child = _childUnderMouse;
+	if (child) {
+		int childX = (x + _scrolledX) - child->getRelX();
+		int childY = (y + _scrolledY) - child->getRelY();
+		child->handleMouseDown(childX, childY, button, clickCount);
+	}
+}
+
+void ScrollContainerWidget::handleMouseMoved(int x, int y, int button) {
+	if (_mouseDownY == y)
+		return;
+
+	if (!_isDragging && ABS(y - _mouseDownStartY) > 5)
+		_isDragging = true;
+
+	if (_isDragging) {
+		int deltaY = _mouseDownY - y;
+		_mouseDownY = y;
+		_childUnderMouse = nullptr;
+
+		if (deltaY != 0) {
+			int oldPos = _scrolledY;
+			_scrolledY += deltaY;
+
+			int h = _verticalScroll->_numEntries;
+			if (_scrolledY < 0)
+				_scrolledY = 0;
+			else if (_scrolledY > h - _limitH)
+				_scrolledY = MAX(0, h - _limitH);
+
+			if (_scrolledY != oldPos) {
+				_verticalScroll->_currentPos = _scrolledY;
+				recalc();
+				markAsDirty();
+				g_gui.scheduleTopDialogRedraw();
+			}
+		}
+	}
+}
+
+void ScrollContainerWidget::handleMouseUp(int x, int y, int button, int clickCount) {
+	Widget *child = _childUnderMouse;
+	bool isDragging = _isDragging;
+
+	_mouseDownY = _mouseDownStartY = 0;
+	_isDragging = false;
+	_childUnderMouse = nullptr;
+
+	if (!isDragging && child) {
+		int childX = (x + _scrolledX) - child->getRelX();
+		int childY = (y + _scrolledY) - child->getRelY();
+		child->handleMouseUp(childX, childY, button, clickCount);
+	}
+}
+
 void ScrollContainerWidget::recalc() {
 	_scrollbarWidth = g_gui.xmlEval()->getVar("Globals.Scrollbar.Width", 0);
 	_limitH = _h;
@@ -80,6 +137,7 @@ void ScrollContainerWidget::recalc() {
 	_verticalScroll->_singleStep = kLineHeight;
 	_verticalScroll->setPos(_w, _scrolledY);
 	_verticalScroll->setSize(_scrollbarWidth, _limitH-1);
+	_verticalScroll->recalc();
 }
 
 
@@ -119,6 +177,7 @@ void ScrollContainerWidget::handleCommand(CommandSender *sender, uint32 cmd, uin
 }
 
 void ScrollContainerWidget::reflowLayout() {
+	_childUnderMouse = nullptr;
 	Widget::reflowLayout();
 
 	if (!_dialogName.empty()) {
@@ -169,11 +228,13 @@ bool ScrollContainerWidget::containsWidget(Widget *w) const {
 }
 
 Widget *ScrollContainerWidget::findWidget(int x, int y) {
-	if (_verticalScroll->isVisible() && x >= _w)
+	if (_verticalScroll->isVisible() && x >= _w) {
+		_childUnderMouse = nullptr;
 		return _verticalScroll;
-	Widget *w = Widget::findWidgetInChain(_firstWidget, x + _scrolledX, y + _scrolledY);
-	if (w)
-		return w;
+	}
+	_childUnderMouse = Widget::findWidgetInChain(_firstWidget, x + _scrolledX, y + _scrolledY);
+	if (_childUnderMouse == _verticalScroll) 
+		_childUnderMouse = nullptr;
 	return this;
 }
 
diff --git a/gui/widgets/scrollcontainer.h b/gui/widgets/scrollcontainer.h
index ea8587ec5f1..bff30dfb5cd 100644
--- a/gui/widgets/scrollcontainer.h
+++ b/gui/widgets/scrollcontainer.h
@@ -36,6 +36,10 @@ class ScrollContainerWidget: public Widget, public CommandSender {
 	uint32 _reflowCmd;
 	ThemeEngine::WidgetBackground _backgroundType;
 	Common::String _dialogName;
+	int _mouseDownY = 0;
+	int _mouseDownStartY = 0;
+	bool _isDragging = false;
+	Widget *_childUnderMouse = nullptr;
 
 	void recalc();
 
@@ -53,6 +57,9 @@ public:
 	void setBackgroundType(ThemeEngine::WidgetBackground backgroundType);
 
 	void handleMouseWheel(int x, int y, int direction) override;
+	void handleMouseDown(int x, int y, int button, int clickCount) override;
+	void handleMouseUp(int x, int y, int button, int clickCount) override;
+	void handleMouseMoved(int x, int y, int button) override;
 
 	// We overload getChildY to make sure child widgets are positioned correctly.
 	// Essentially this compensates for the space taken up by the tab title header.
@@ -60,6 +67,7 @@ public:
 	int16	getChildY() const override;
 	uint16	getWidth() const override;
 	uint16	getHeight() const override;
+	bool wantsFocus() override { return true; }
 
 	void draw() override;
 	void markAsDirty() override;


Commit: 84dab33d07052c74417878e233bcf8eab0668a4d
    https://github.com/scummvm/scummvm/commit/84dab33d07052c74417878e233bcf8eab0668a4d
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-04-20T23:33:23+02:00

Commit Message:
GUI: Implenet Fluid Scroll in ScrollContainer

Changed paths:
    gui/widgets/scrollcontainer.cpp
    gui/widgets/scrollcontainer.h


diff --git a/gui/widgets/scrollcontainer.cpp b/gui/widgets/scrollcontainer.cpp
index 24c6c281d49..345de340810 100644
--- a/gui/widgets/scrollcontainer.cpp
+++ b/gui/widgets/scrollcontainer.cpp
@@ -27,6 +27,7 @@
 
 namespace GUI {
 
+const int ScrollContainerWidget::kDragThreshold = 5;
 ScrollContainerWidget::ScrollContainerWidget(GuiObject *boss, int x, int y, int w, int h, uint32 reflowCmd)
 	: Widget(boss, x, y, w, h), CommandSender(nullptr), _reflowCmd(reflowCmd) {
 	init();
@@ -38,23 +39,30 @@ ScrollContainerWidget::ScrollContainerWidget(GuiObject *boss, const Common::Stri
 }
 
 void ScrollContainerWidget::init() {
-	setFlags(WIDGET_ENABLED | WIDGET_TRACK_MOUSE);
+	setFlags(WIDGET_ENABLED | WIDGET_TRACK_MOUSE | WIDGET_WANT_TICKLE | WIDGET_RETAIN_FOCUS);
 	_type = kScrollContainerWidget;
 	_backgroundType = ThemeEngine::kWidgetBackgroundPlain;
 	_verticalScroll = new ScrollBarWidget(this, _w, 0, 16, _h);
 	_verticalScroll->setTarget(this);
 	_scrolledX = 0;
 	_scrolledY = 0;
+	_scrollPos = 0.0f;
 	_limitH = 140;
+	_fluidScroller = new FluidScroller();
 	recalc();
 }
 
 void ScrollContainerWidget::handleMouseWheel(int x, int y, int direction) {
-	_verticalScroll->handleMouseWheel(x, y, direction);
+	if (!isEnabled())
+		return;
+
+	_fluidScroller->handleMouseWheel(direction, (float)_verticalScroll->_singleStep);
 }
 
 void ScrollContainerWidget::handleMouseDown(int x, int y, int button, int clickCount) {
 	_mouseDownY = _mouseDownStartY = y;
+	_isMouseDown = true;
+	_fluidScroller->stopAnimation();
 	Widget *child = _childUnderMouse;
 	if (child) {
 		int childX = (x + _scrolledX) - child->getRelX();
@@ -64,10 +72,10 @@ void ScrollContainerWidget::handleMouseDown(int x, int y, int button, int clickC
 }
 
 void ScrollContainerWidget::handleMouseMoved(int x, int y, int button) {
-	if (_mouseDownY == y)
+	if (!_isMouseDown || _mouseDownY == y)
 		return;
 
-	if (!_isDragging && ABS(y - _mouseDownStartY) > 5)
+	if (!_isDragging && ABS(y - _mouseDownStartY) > kDragThreshold)
 		_isDragging = true;
 
 	if (_isDragging) {
@@ -76,30 +84,38 @@ void ScrollContainerWidget::handleMouseMoved(int x, int y, int button) {
 		_childUnderMouse = nullptr;
 
 		if (deltaY != 0) {
-			int oldPos = _scrolledY;
-			_scrolledY += deltaY;
-
-			int h = _verticalScroll->_numEntries;
-			if (_scrolledY < 0)
-				_scrolledY = 0;
-			else if (_scrolledY > h - _limitH)
-				_scrolledY = MAX(0, h - _limitH);
-
-			if (_scrolledY != oldPos) {
-				_verticalScroll->_currentPos = _scrolledY;
-				recalc();
-				markAsDirty();
-				g_gui.scheduleTopDialogRedraw();
-			}
+			_fluidScroller->feedDrag(g_system->getMillis(), deltaY);
+			_scrollPos = _fluidScroller->getVisualPosition();
+			applyScrollPos();
 		}
 	}
 }
 
+void ScrollContainerWidget::handleTickle() {
+	if (_fluidScroller->update(g_system->getMillis(), _scrollPos))
+		applyScrollPos();
+}
+
+void ScrollContainerWidget::applyScrollPos() {
+	_scrolledY = (int16)_scrollPos;
+	int h = _verticalScroll->_numEntries;
+	int maxScroll = MAX(0, h - _limitH);
+	_verticalScroll->_currentPos = CLIP<int16>(_scrolledY, 0, maxScroll);
+	_verticalScroll->setPos(_w, _scrolledY);
+	_verticalScroll->recalc();
+	markAsDirty();
+	g_gui.scheduleTopDialogRedraw();
+}
+
 void ScrollContainerWidget::handleMouseUp(int x, int y, int button, int clickCount) {
 	Widget *child = _childUnderMouse;
 	bool isDragging = _isDragging;
 
+	if (_isMouseDown && _isDragging)
+		_fluidScroller->startFling();
+
 	_mouseDownY = _mouseDownStartY = 0;
+	_isMouseDown = false;
 	_isDragging = false;
 	_childUnderMouse = nullptr;
 
@@ -129,19 +145,24 @@ void ScrollContainerWidget::recalc() {
 	int h = max - min;
 
 	if (h <= _limitH) _scrolledY = 0;
-	if (_scrolledY > h - _limitH) _scrolledY = 0;
+	else if (!_fluidScroller->isAnimating() && !_isMouseDown)
+		_scrolledY = CLIP<int16>(_scrolledY, 0, h - _limitH);
 
 	_verticalScroll->_numEntries = h;
-	_verticalScroll->_currentPos = _scrolledY;
+	int maxScroll = MAX(0, h - _limitH);
+	_verticalScroll->_currentPos = CLIP<int16>(_scrolledY, 0, maxScroll);
 	_verticalScroll->_entriesPerPage = _limitH;
 	_verticalScroll->_singleStep = kLineHeight;
 	_verticalScroll->setPos(_w, _scrolledY);
 	_verticalScroll->setSize(_scrollbarWidth, _limitH-1);
 	_verticalScroll->recalc();
+	_fluidScroller->setBounds((float)maxScroll, _limitH);
 }
 
 
-ScrollContainerWidget::~ScrollContainerWidget() {}
+ScrollContainerWidget::~ScrollContainerWidget() {
+	delete _fluidScroller;
+}
 
 int16 ScrollContainerWidget::getChildX() const {
 	return getAbsX() - _scrolledX;
@@ -168,6 +189,7 @@ void ScrollContainerWidget::handleCommand(CommandSender *sender, uint32 cmd, uin
 	switch (cmd) {
 	case kSetPositionCmd:
 		_scrolledY = _verticalScroll->_currentPos;
+		_scrollPos = _fluidScroller->setPosition((float)_scrolledY, false);
 		reflowLayout();
 		g_gui.scheduleTopDialogRedraw();
 		break;
diff --git a/gui/widgets/scrollcontainer.h b/gui/widgets/scrollcontainer.h
index bff30dfb5cd..37c4582e805 100644
--- a/gui/widgets/scrollcontainer.h
+++ b/gui/widgets/scrollcontainer.h
@@ -25,6 +25,7 @@
 #include "gui/widget.h"
 #include "common/str.h"
 #include "gui/widgets/scrollbar.h"
+#include "gui/animation/FluidScroll.h"
 
 namespace GUI {
 
@@ -37,11 +38,16 @@ class ScrollContainerWidget: public Widget, public CommandSender {
 	ThemeEngine::WidgetBackground _backgroundType;
 	Common::String _dialogName;
 	int _mouseDownY = 0;
+	static const int kDragThreshold;
 	int _mouseDownStartY = 0;
+	bool _isMouseDown = false;
 	bool _isDragging = false;
+	float _scrollPos = 0.0f;
+	FluidScroller *_fluidScroller = nullptr;
 	Widget *_childUnderMouse = nullptr;
 
 	void recalc();
+	void applyScrollPos();
 
 public:
 	ScrollContainerWidget(GuiObject *boss, int x, int y, int w, int h, uint32 reflowCmd = 0);
@@ -60,6 +66,7 @@ public:
 	void handleMouseDown(int x, int y, int button, int clickCount) override;
 	void handleMouseUp(int x, int y, int button, int clickCount) override;
 	void handleMouseMoved(int x, int y, int button) override;
+	void handleTickle() override;
 
 	// We overload getChildY to make sure child widgets are positioned correctly.
 	// Essentially this compensates for the space taken up by the tab title header.


Commit: c041092694285068d2a83f53fc7f6e460d4d1d8e
    https://github.com/scummvm/scummvm/commit/c041092694285068d2a83f53fc7f6e460d4d1d8e
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-04-20T23:33:23+02:00

Commit Message:
GUI: Centralize scroll step size logic in FluidScroller

Changed paths:
    gui/about.cpp
    gui/animation/FluidScroll.cpp
    gui/animation/FluidScroll.h
    gui/widgets/grid.cpp
    gui/widgets/groupedlist.cpp
    gui/widgets/list.cpp
    gui/widgets/richtext.cpp
    gui/widgets/scrollcontainer.cpp


diff --git a/gui/about.cpp b/gui/about.cpp
index c7659ec9122..ac719ba54d0 100644
--- a/gui/about.cpp
+++ b/gui/about.cpp
@@ -437,7 +437,7 @@ void AboutDialog::handleMouseMoved(int x, int y, int button) {
 
 void AboutDialog::handleMouseWheel(int x, int y, int direction) {
 	_autoScroll = false;
-	_fluidScroller->handleMouseWheel(direction, (float)_lineHeight);
+	_fluidScroller->handleMouseWheel(direction);
 }
 
 void AboutDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
@@ -529,7 +529,7 @@ void AboutDialog::reflowLayout() {
 	buildLines();
 
 	int maxScroll = MAX(0, (int)(_lines.size() * _lineHeight) - _textRect.height());
-	_fluidScroller->setBounds((float)maxScroll, _textRect.height());
+	_fluidScroller->setBounds((float)maxScroll, _textRect.height(), (float)_scrollbar->_singleStep);
 }
 
 
diff --git a/gui/animation/FluidScroll.cpp b/gui/animation/FluidScroll.cpp
index 830f31bd269..e8a1051ca9e 100644
--- a/gui/animation/FluidScroll.cpp
+++ b/gui/animation/FluidScroll.cpp
@@ -96,6 +96,7 @@ FluidScroller::FluidScroller() :
 	_scrollPosRaw(0.0f), 
 	_animationOffset(0.0f), 
 	_maxScroll(0.0f), 
+	_stepSize(1.0f),
 	_viewportHeight(0),
 	_lastWheelTime(0),
 	_initialVelocity(0.0f),
@@ -104,9 +105,10 @@ FluidScroller::FluidScroller() :
 	_impactVelocity(0.0f) {
 }
 
-void FluidScroller::setBounds(float maxScroll, int viewportHeight) {
+void FluidScroller::setBounds(float maxScroll, int viewportHeight, float stepSize) {
 	_maxScroll = maxScroll;
 	_viewportHeight = viewportHeight;
+	_stepSize = stepSize;
 }
 
 void FluidScroller::reset() {
@@ -169,8 +171,8 @@ void FluidScroller::feedWheel(uint32 time, float deltaY) {
 	startFling(velocity);
 }
 
-void FluidScroller::handleMouseWheel(int direction, float stepSize, float multiplier) {
-	float stepping = stepSize * (float)direction * multiplier;
+void FluidScroller::handleMouseWheel(int direction, float multiplier) {
+	float stepping = _stepSize * (float)direction * multiplier;
 	if (stepping == 0.0f)
 		return;
 
diff --git a/gui/animation/FluidScroll.h b/gui/animation/FluidScroll.h
index 4c2114a6b21..8ec0ff07244 100644
--- a/gui/animation/FluidScroll.h
+++ b/gui/animation/FluidScroll.h
@@ -40,8 +40,9 @@ public:
 	 * Configure the constraints for the content
 	 * @param maxScroll The maximum scrollable distance (total height - viewport height)
 	 * @param viewportHeight The height of the scrolling area, used for rubber-band range
+	 * @param stepSize Default scroll step (usually _singleStep)
 	 */
-	void setBounds(float maxScroll, int viewportHeight);
+	void setBounds(float maxScroll, int viewportHeight, float stepSize);
 
 	void reset();
 
@@ -67,10 +68,9 @@ public:
 	/**
 	 * Handle mouse wheel input
 	 * @param direction Scroll direction
-	 * @param stepSize usually _scrollBar->_singleStep
 	 * @param multiplier Speed multiplier for the scroll
 	 */
-	void handleMouseWheel(int direction, float stepSize, float multiplier = 1.0f);
+	void handleMouseWheel(int direction, float multiplier = 1.0f);
 
 	// Check if there is an active animation (fling or spring-back)
 	bool isAnimating() const { return _mode != kModeNone; }
@@ -125,6 +125,7 @@ private:
 	float _scrollPosRaw;    // Physical position (can go out of bounds)
 	float _animationOffset; // Anchor position used as the starting point for animation offsets
 	float _maxScroll;
+	float _stepSize;
 	int _viewportHeight;
 
 	// Fling parameter
diff --git a/gui/widgets/grid.cpp b/gui/widgets/grid.cpp
index 0132e4060be..d80081287b9 100644
--- a/gui/widgets/grid.cpp
+++ b/gui/widgets/grid.cpp
@@ -1012,7 +1012,7 @@ void GridWidget::selectVisualRange(int startPos, int endPos) {
 }
 
 void GridWidget::handleMouseWheel(int x, int y, int direction) {
-	_fluidScroller->handleMouseWheel(direction, (float)_scrollBar->_singleStep);
+	_fluidScroller->handleMouseWheel(direction);
 }
 
 void GridWidget::handleMouseDown(int x, int y, int button, int clickCount) {
@@ -1266,7 +1266,7 @@ void GridWidget::reflowLayout() {
 	}
 	scrollBarRecalc();
 	int maxScroll = MAX(0, _scrollBar->_numEntries - _scrollBar->_entriesPerPage);
-	_fluidScroller->setBounds((float)maxScroll, _scrollBar->_entriesPerPage);
+	_fluidScroller->setBounds((float)maxScroll, _scrollBar->_entriesPerPage, (float)_scrollBar->_singleStep);
 	markAsDirty();
 }
 
@@ -1291,7 +1291,7 @@ void GridWidget::scrollBarRecalc() {
 	_scrollBar->recalc();
 
 	maxScroll = MAX(0, _scrollBar->_numEntries - _scrollBar->_entriesPerPage);
-	_fluidScroller->setBounds((float)maxScroll, _scrollBar->_entriesPerPage);
+	_fluidScroller->setBounds((float)maxScroll, _scrollBar->_entriesPerPage, (float)_scrollBar->_singleStep);
 }
 
 void GridWidget::setFilter(const Common::U32String &filter) {
diff --git a/gui/widgets/groupedlist.cpp b/gui/widgets/groupedlist.cpp
index cd5cf359aad..28147a65546 100644
--- a/gui/widgets/groupedlist.cpp
+++ b/gui/widgets/groupedlist.cpp
@@ -356,7 +356,7 @@ void GroupedListWidget::handleMouseUp(int x, int y, int button, int clickCount)
 }
 
 void GroupedListWidget::handleMouseWheel(int x, int y, int direction) {
-	_fluidScroller->handleMouseWheel(direction, (float)_scrollBar->_singleStep);
+	_fluidScroller->handleMouseWheel(direction);
 }
 
 void GroupedListWidget::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
diff --git a/gui/widgets/list.cpp b/gui/widgets/list.cpp
index 5f75f3a5cbd..7a3559c4498 100644
--- a/gui/widgets/list.cpp
+++ b/gui/widgets/list.cpp
@@ -355,7 +355,7 @@ void ListWidget::scrollBarRecalc() {
 	_scrollBar->recalc();
 
 	int maxScroll = MAX(0, (int)(_scrollBar->_numEntries - _scrollBar->_entriesPerPage) * lineHeight);
-	_fluidScroller->setBounds((float)maxScroll, _h - _topPadding - _bottomPadding);
+	_fluidScroller->setBounds((float)maxScroll, _h - _topPadding - _bottomPadding, (float)_scrollBar->_singleStep);
 }
 
 void ListWidget::handleTickle() {
@@ -459,7 +459,7 @@ void ListWidget::handleMouseUp(int x, int y, int button, int clickCount) {
 }
 
 void ListWidget::handleMouseWheel(int x, int y, int direction) {
-	_fluidScroller->handleMouseWheel(direction, (float)_scrollBar->_singleStep);
+	_fluidScroller->handleMouseWheel(direction);
 }
 
 void ListWidget::handleMouseMoved(int x, int y, int button) {
diff --git a/gui/widgets/richtext.cpp b/gui/widgets/richtext.cpp
index a63a254bbb9..03a15076e41 100644
--- a/gui/widgets/richtext.cpp
+++ b/gui/widgets/richtext.cpp
@@ -103,7 +103,7 @@ RichTextWidget::~RichTextWidget() {
 }
 
 void RichTextWidget::handleMouseWheel(int x, int y, int direction) {
-	_fluidScroller->handleMouseWheel(direction, (float)_verticalScroll->_singleStep);
+	_fluidScroller->handleMouseWheel(direction);
 	applyScrollPos();
 }
 
@@ -245,7 +245,7 @@ void RichTextWidget::recalc() {
 		_verticalScroll->setSize(_scrollbarWidth, _h - 1);
 		_verticalScroll->setVisible(_verticalScroll->_numEntries > _limitH); //show when there is something to scroll
 		_verticalScroll->recalc();
-		_fluidScroller->setBounds((float)maxScroll, (float)_limitH);
+		_fluidScroller->setBounds((float)maxScroll, (float)_limitH, (float)_verticalScroll->_singleStep);
 	}
 }
 
@@ -328,7 +328,7 @@ void RichTextWidget::createWidget() {
 	_verticalScroll->setSize(_scrollbarWidth, _h - 1);
 	_verticalScroll->setVisible(_verticalScroll->_numEntries > _limitH); //show when there is something to scroll
 	_verticalScroll->recalc();
-	_fluidScroller->setBounds((float)maxScroll, (float)_limitH);
+	_fluidScroller->setBounds((float)maxScroll, (float)_limitH, (float)_verticalScroll->_singleStep);
 }
 
 void RichTextWidget::reflowLayout() {
diff --git a/gui/widgets/scrollcontainer.cpp b/gui/widgets/scrollcontainer.cpp
index 345de340810..0ac21c93241 100644
--- a/gui/widgets/scrollcontainer.cpp
+++ b/gui/widgets/scrollcontainer.cpp
@@ -56,7 +56,7 @@ void ScrollContainerWidget::handleMouseWheel(int x, int y, int direction) {
 	if (!isEnabled())
 		return;
 
-	_fluidScroller->handleMouseWheel(direction, (float)_verticalScroll->_singleStep);
+	_fluidScroller->handleMouseWheel(direction);
 }
 
 void ScrollContainerWidget::handleMouseDown(int x, int y, int button, int clickCount) {
@@ -156,7 +156,7 @@ void ScrollContainerWidget::recalc() {
 	_verticalScroll->setPos(_w, _scrolledY);
 	_verticalScroll->setSize(_scrollbarWidth, _limitH-1);
 	_verticalScroll->recalc();
-	_fluidScroller->setBounds((float)maxScroll, _limitH);
+	_fluidScroller->setBounds((float)maxScroll, _limitH, (float)_verticalScroll->_singleStep);
 }
 
 




More information about the Scummvm-git-logs mailing list