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

sev- noreply at scummvm.org
Sat Jan 31 09:46:44 UTC 2026


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

Summary:
df0f3a9bd3 GUI: Preserve selection when shift-clicking
adc0b6beed GUI: Refactor removal logic, fix list shift-click anchor
c9f0f87e2d GUI: Prevent duplicate entries in grid shift-click selection
8a27ced298 GUI: Fix selection logic for filtered and grouped list views
ac0927e463 GUI: Fix grid alphabetic grouping to use user description
8aff66dc92 GUI: Fix multi-selection in grouped grid view
9ef4aac9ab GUI: Add multi-selection with arrow keys in list view
473bf9b28f GUI: Optimize multi-selection in ListWidget
5b0896697c GUI: Unify game removal logic in launcher views
3bf41f2e66 GUI: Implement bool array selection for ListWidget and GridWidget
21bbca9c5b GUI: Refactor selection methods in list and grid widgets
74592508f8 GUI: Fix list item visual deselection in multi-select mode
2a2bbb6f2d GUI: Enable continuous Shift+Click range expansion in GroupedListWidget
9fe0037314 GUI: Fix button enable logic in launcher list view
77fe2d4c5a GUI: Fix unwanted scrolling during multi-select keyboard operations
8400ccd03d GUI: Improve comments in game removal validation
212ce75f98 GUI: Show game count and limit removal confirmation list to 10 items
3037fd364b GUI: Add comments explaining button enable logic in list view
6f7909f557 GUI: Use more appropriate condition for multi-selection detection
171568aec0 GUI: Improve comments for game removal
2fbfc34b51 GUI: Simplify selection restoration after game removal
31d9733482 GUI: Refactor game removal to use common virtual method
375449cb59 GUI: Separate LauncherGrid::updateSelectionAfterRemoval() implementation
c5475e5bd0 GUI: Simplify variable naming - selectedItemsBool to selectedItems
bd1b8bb2c9 GUI: Simplify method name - LauncherDialog::performGameRemoval to removeGames
2b41731836 GUI: Unify selection access with virtual getSelectedItems() method
2c23b8bb37 GUI: Add hasAnySelection() to consolidate selection checking
8e99bd35c0 GUI: Improve translation compatibility in game removal dialog
e7ba7c338f GUI: Remove redundant bounds checks in grid selection logic
5858c62da4 GUI: Unify game removal confirmation dialog implementation
dcb0877f4d GUI: Use SWAP macro instead of manual swap in GridWidget


Commit: df0f3a9bd35b2b1087859cd7f7881c184d19c629
    https://github.com/scummvm/scummvm/commit/df0f3a9bd35b2b1087859cd7f7881c184d19c629
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-31T12:46:29+03:00

Commit Message:
GUI: Preserve selection when shift-clicking

Prevent Shift+Click from clearing existing selections.

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


diff --git a/gui/widgets/grid.cpp b/gui/widgets/grid.cpp
index 999fcb7fc04..ad682a75091 100644
--- a/gui/widgets/grid.cpp
+++ b/gui/widgets/grid.cpp
@@ -300,8 +300,6 @@ void GridItemWidget::handleMouseDown(int x, int y, int button, int clickCount) {
 				_grid->_lastSelectedEntryID = _activeEntry->entryID;
 			}
 		} else if (shiftPressed && _grid->_lastSelectedEntryID >= 0) {
-			// Clear already selected Items
-			_grid->_selectedEntries.clear();
 			// Shift+Click: Select range from last selected to current item
 			int startID = _grid->_lastSelectedEntryID;
 			int endID = _activeEntry->entryID;
diff --git a/gui/widgets/list.cpp b/gui/widgets/list.cpp
index 837cef8e0bc..90116b2deb3 100644
--- a/gui/widgets/list.cpp
+++ b/gui/widgets/list.cpp
@@ -264,8 +264,6 @@ void ListWidget::selectItemRange(int from, int to) {
 	if (from > to)
 		SWAP(from, to);
 
-	clearSelection();
-
 	for (int i = from; i <= to; ++i) {
 		addSelectedItem(i);
 	}


Commit: adc0b6beed098d0393e8bf56573331c9f027004d
    https://github.com/scummvm/scummvm/commit/adc0b6beed098d0393e8bf56573331c9f027004d
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-31T12:46:29+03:00

Commit Message:
GUI: Refactor removal logic, fix list shift-click anchor

Refactor game removal logic to reduce duplication between grid and list views.
Fix an issue in ListWidget where shift-click multi-selection updated the anchor
before selecting the range. The anchor is now updated after the range selection,
ensuring multi-selection works as expected.

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


diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 1bc97dced9f..3bd7b657f4b 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -1027,8 +1027,14 @@ public:
 	LauncherDisplayType getType() const override { return kLauncherDisplayGrid; }
 
 public:
-	void removeSelectedGames();
+	void removeGridGames();
 protected:
+	void updateSelectionAfterRemoval() override {
+		if (_grid) {
+			_grid->_selectedEntries.clear();
+			_grid->_selectedEntries.push_back(_grid->_lastSelectedEntryID);
+		}
+	}
 	void updateListing(int selPos = -1) override;
 	int getItemPos(int item) override;
 	void groupEntries(const Common::Array<LauncherEntry> &metadata);
@@ -1376,7 +1382,7 @@ void LauncherSimple::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 		const Common::Array<int> &selectedItems = _list->getSelectedItems();
 		if (selectedItems.size() > 1) {
 			// Multi-selection removal: show confirmation dialog with list of games
-			removeMultipleGames(selectedItems);
+			removeListGames(selectedItems);
 		} else {
 			LauncherDialog::handleCommand(sender, kRemoveGameCmd, 0);
 		}
@@ -1417,7 +1423,7 @@ void LauncherSimple::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 		const Common::Array<int> &selectedItems = _list->getSelectedItems();
 		if (selectedItems.size() > 1) {
 			// Multi-selection removal: show confirmation dialog with list of games
-			removeMultipleGames(selectedItems);
+			removeListGames(selectedItems);
 		} else {
 			// Single selection removal
 			LauncherDialog::handleCommand(sender, cmd, data);
@@ -1429,7 +1435,7 @@ void LauncherSimple::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 	}
 }
 
-void LauncherSimple::removeMultipleGames(const Common::Array<int> &selectedItems) {
+void LauncherSimple::removeListGames(const Common::Array<int> &selectedItems) {
 	// Build confirmation message with list of games to remove
 	Common::U32String confirmMsg = _("Do you really want to remove the following game configurations?\n\n");
 
@@ -1444,30 +1450,13 @@ void LauncherSimple::removeMultipleGames(const Common::Array<int> &selectedItems
 	MessageDialog alert(confirmMsg, _("Yes"), _("No"));
 
 	if (alert.runModal() == GUI::kMessageOK) {
-		// Remove all selected games in reverse order to avoid index shifting issues
-		Common::Array<int> sortedItems = selectedItems;
-		Common::sort(sortedItems.begin(), sortedItems.end(), Common::Greater<int>());
-
-		int selPos = -1;
-		Common::StringArray domainsToRemove;
-		for (const int &idx : sortedItems) {
-			if (idx >= 0 && idx < (int)_domains.size()) {
-				// Get position of game item if grouping is enabled
-				if (_groupBy != kGroupByNone) {
-					selPos = getItemPos(idx);
-				}
-				domainsToRemove.push_back(_domains[idx]);
-			}
-		}
-
-		// Remove games and addons
-		removeGamesWithAddons(domainsToRemove);
+		performGameRemoval(selectedItems, false);
+	}
+}
 
-		// Clear the selection and update the listing
+void LauncherSimple::updateSelectionAfterRemoval() {
+	if (_list) {
 		_list->clearSelection();
-		updateListing(selPos);
-		updateButtons();
-		g_gui.scheduleTopDialogRedraw();
 	}
 }
 
@@ -1651,7 +1640,7 @@ void LauncherGrid::handleCommand(CommandSender *sender, uint32 cmd, uint32 data)
 	case kRemoveGameCmd:
 		// Handle multi-selection removal
 		if (_grid && _grid->getSelectedEntries().size() > 1) {
-			removeSelectedGames();
+			removeGridGames();
 		} else {
 			LauncherDialog::handleCommand(sender, cmd, data);
 		}
@@ -1800,7 +1789,7 @@ void LauncherGrid::build() {
 	updateButtons();
 }
 
-void LauncherGrid::removeSelectedGames() {
+void LauncherGrid::removeGridGames() {
 	if (!_grid)
 		return;
 	const Common::Array<int> &selectedEntries = _grid->getSelectedEntries();
@@ -1808,29 +1797,54 @@ void LauncherGrid::removeSelectedGames() {
 		return;
 
 	Common::U32String message = _("Do you really want to remove the following game configurations?\n\n");
-	Common::StringArray domainsToRemove;
 
 	for (int entryID : selectedEntries) {
 		// entryID is the index in _domains and _domainTitles
 		if (entryID >= 0 && entryID < (int)_domains.size()) {
 			Common::String domainName = _domains[entryID];
 			Common::String gameTitle = (entryID < (int)_domainTitles.size()) ? _domainTitles[entryID] : domainName;
-			domainsToRemove.push_back(domainName);
 			message += Common::U32String(gameTitle) + "\n";
 		}
 	}
 
 	MessageDialog alert(message, Common::U32String(_("Yes")), Common::U32String(_("No")));
 	if (alert.runModal() == GUI::kMessageOK) {
-		// Remove games and addons
-		removeGamesWithAddons(domainsToRemove);
-		_grid->_selectedEntries.clear();
-		_grid->_selectedEntries.push_back(_grid->_lastSelectedEntryID);
-		updateListing();
-		updateButtons();
-		g_gui.scheduleTopDialogRedraw();
+		performGameRemoval(selectedEntries, true);
 	}
 }
+
 #endif // !DISABLE_LAUNCHERDISPLAY_GRID
 
+void LauncherDialog::performGameRemoval(const Common::Array<int> &selectedItems, bool isGrid) {
+	if (selectedItems.empty())
+		return;
+
+	Common::Array<int> itemsToProcess = selectedItems;
+	// For list view only, sort in reverse to avoid index shifting
+	if (!isGrid) {
+		Common::sort(itemsToProcess.begin(), itemsToProcess.end(), Common::Greater<int>());
+	}
+
+	int selPos = -1;
+	Common::StringArray domainsToRemove;
+	for (const int &idx : itemsToProcess) {
+		if (idx >= 0 && idx < (int)_domains.size()) {
+			if (_groupBy != kGroupByNone && !isGrid) {
+				selPos = getItemPos(idx);
+			}
+			domainsToRemove.push_back(_domains[idx]);
+		}
+	}
+
+	// Remove games and addons
+	removeGamesWithAddons(domainsToRemove);
+
+	// Update UI - each subclass handles its own selection update
+	updateSelectionAfterRemoval();
+	
+	updateListing(selPos);
+	updateButtons();
+	g_gui.scheduleTopDialogRedraw();
+}
+
 } // End of namespace GUI
diff --git a/gui/launcher.h b/gui/launcher.h
index f07304b50c6..7e9ef9612e9 100644
--- a/gui/launcher.h
+++ b/gui/launcher.h
@@ -192,6 +192,18 @@ protected:
 	 */
 	void removeGamesWithAddons(const Common::StringArray &domainsToRemove);
 
+	/**
+	 * Shared helper for removing games after confirmation.
+	 * Called by subclasses after building their own confirmation message.
+	 */
+	void performGameRemoval(const Common::Array<int> &selectedItems, bool isGrid);
+
+	/**
+	 * Update selection after game removal.
+	 * Each subclass handles its own UI-specific selection logic.
+	 */
+	virtual void updateSelectionAfterRemoval() = 0;
+
 	/**
 	 * Handle "Edit game..." button.
 	 */
@@ -244,8 +256,10 @@ public:
 	void handleKeyDown(Common::KeyState state) override;
 
 	LauncherDisplayType getType() const override { return kLauncherDisplayList; }
+	void removeListGames(const Common::Array<int> &selectedItems);
 
 protected:
+	void updateSelectionAfterRemoval() override;
 	void updateListing(int selPos = -1) override;
 	int getItemPos(int item) override;
 	void groupEntries(const Common::Array<LauncherEntry> &metadata);
@@ -253,7 +267,6 @@ protected:
 	void selectTarget(const Common::String &target) override;
 	int getSelected() override;
 	void build() override;
-	void removeMultipleGames(const Common::Array<int> &selectedItems);
 
 private:
 	GroupedListWidget *_list;
diff --git a/gui/widgets/list.cpp b/gui/widgets/list.cpp
index 90116b2deb3..1d6e0bc9360 100644
--- a/gui/widgets/list.cpp
+++ b/gui/widgets/list.cpp
@@ -354,8 +354,8 @@ void ListWidget::handleMouseDown(int x, int y, int button, int clickCount) {
 		if (shiftClick && _lastSelectionStartItem != -1) {
 			// Shift+Click: Select range from last selection start to current item
 			_selectedItem = newSelectedItem;
-			_lastSelectionStartItem = newSelectedItem;
 			selectItemRange(_lastSelectionStartItem, newSelectedItem);
+			_lastSelectionStartItem = newSelectedItem;
 			sendCommand(kListSelectionChangedCmd, _selectedItem);
 		} else if (ctrlClick) {
 			// Ctrl+Click: Add/remove from selection


Commit: c9f0f87e2d5be1bdd97eb76e09c80903868d6a1b
    https://github.com/scummvm/scummvm/commit/c9f0f87e2d5be1bdd97eb76e09c80903868d6a1b
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-31T12:46:29+03:00

Commit Message:
GUI: Prevent duplicate entries in grid shift-click selection

Fix grid view multi-selection logic to avoid adding duplicate entries
when shift-clicking the same or overlapping ranges. Now, each entry
in the selection range is only added once.

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


diff --git a/gui/widgets/grid.cpp b/gui/widgets/grid.cpp
index ad682a75091..824feb970c7 100644
--- a/gui/widgets/grid.cpp
+++ b/gui/widgets/grid.cpp
@@ -311,9 +311,9 @@ void GridItemWidget::handleMouseDown(int x, int y, int button, int clickCount) {
 				endID = temp;
 			}
 
-			// Add all items in range (assuming entry IDs are sequential indices)
+			// Add all items in range, avoiding duplicates
 			for (int i = startID; i <= endID; ++i) {
-				_grid->_selectedEntries.push_back(i);
+				_grid->addSelectedEntry(i);
 			}
 			// Keep entries sorted
 			Common::sort(_grid->_selectedEntries.begin(), _grid->_selectedEntries.end());
@@ -1246,4 +1246,12 @@ void GridWidget::setSelected(int id) {
 	}
 }
 
+void GridWidget::addSelectedEntry(int entryID) {
+	for (int selected : _selectedEntries) {
+		if (selected == entryID)
+			return; // Already selected, do not add again
+	}
+	_selectedEntries.push_back(entryID);
+}
+
 } // End of namespace GUI
diff --git a/gui/widgets/grid.h b/gui/widgets/grid.h
index b459c16a6b3..2b267c8bfb2 100644
--- a/gui/widgets/grid.h
+++ b/gui/widgets/grid.h
@@ -236,6 +236,7 @@ public:
 	// Multi-selection methods
 	void setMultiSelectEnabled(bool enabled) { _multiSelectEnabled = enabled; }
 	bool isMultiSelectEnabled() const { return _multiSelectEnabled; }
+	void addSelectedEntry(int entryID);
 
 public:
 	Common::Array<int> _selectedEntries; // Stores indices of selected entries


Commit: 8a27ced29830a4969e0cb014b5b151e6515716bb
    https://github.com/scummvm/scummvm/commit/8a27ced29830a4969e0cb014b5b151e6515716bb
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-31T12:46:29+03:00

Commit Message:
GUI: Fix selection logic for filtered and grouped list views

Update ListWidget and GroupedListWidget to correctly map visual (filtered/grouped) indices to real data indices for all selection operations. This ensures that selection, multi-selection, and highlighting work as expected when filtering or grouping is active in the list view.

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


diff --git a/gui/widgets/groupedlist.cpp b/gui/widgets/groupedlist.cpp
index 5b3de49f5ce..bfde9c4b280 100644
--- a/gui/widgets/groupedlist.cpp
+++ b/gui/widgets/groupedlist.cpp
@@ -266,27 +266,28 @@ void GroupedListWidget::handleMouseDown(int x, int y, int button, int clickCount
 	if (_multiSelectEnabled && (shiftClick || ctrlClick)) {
 		if (shiftClick && _lastSelectionStartItem != -1) {
 			// Shift+Click: Select range in terms of underlying data indices
-			int startDataIndex = _lastSelectionStartItem;
-			selectItemRange(startDataIndex, dataIndex);
-			_selectedItem = newSelectedItem;
+			int startListIndex = _lastSelectionStartItem;
+			int endListIndex = newSelectedItem;              
+			selectItemRange(startListIndex, endListIndex);
+            _selectedItem = newSelectedItem;
 			sendCommand(kListSelectionChangedCmd, _selectedItem);
 		} else if (ctrlClick) {
 			// Ctrl+Click: toggle selection for the underlying data index
-			if (isItemSelected(dataIndex)) {
-				removeSelectedItem(dataIndex);
+			if (isItemSelected(newSelectedItem)) {
+				removeSelectedItem(newSelectedItem);
 			} else {
-				addSelectedItem(dataIndex);
+				addSelectedItem(newSelectedItem);
 			}
 			_selectedItem = newSelectedItem;
-			_lastSelectionStartItem = dataIndex;
+			_lastSelectionStartItem = newSelectedItem;
 			sendCommand(kListSelectionChangedCmd, _selectedItem);
 		}
 	} else {
 		// Regular click: clear selection and select only this underlying item
 		clearSelection();
 		_selectedItem = newSelectedItem;
-		addSelectedItem(dataIndex);
-		_lastSelectionStartItem = dataIndex;
+		addSelectedItem(newSelectedItem);
+		_lastSelectionStartItem = newSelectedItem;
 		sendCommand(kListSelectionChangedCmd, _selectedItem);
 	}
 
@@ -394,7 +395,7 @@ void GroupedListWidget::drawWidget() {
 		int mapped = _listIndex[pos];
 		bool isRealItem = (mapped >= 0);
 		if (isRealItem) {
-			if (_selectedItem == pos || isItemSelected(mapped))
+			if (_selectedItem == pos || isItemSelected(pos))
 				inverted = _inversion;
 		}
 
diff --git a/gui/widgets/list.cpp b/gui/widgets/list.cpp
index 1d6e0bc9360..8c4baaca658 100644
--- a/gui/widgets/list.cpp
+++ b/gui/widgets/list.cpp
@@ -201,7 +201,7 @@ void ListWidget::setSelected(int item) {
 bool ListWidget::isItemSelected(int item) const {
 	// Convert to actual item index if filtering is active
 	int actualItem = item;
-	if (!_filter.empty() && item >= 0 && item < (int)_listIndex.size()) {
+	if (!_listIndex.empty() && item >= 0 && item < (int)_listIndex.size()) {
 		actualItem = _listIndex[item];
 	}
 
@@ -215,12 +215,12 @@ bool ListWidget::isItemSelected(int item) const {
 void ListWidget::addSelectedItem(int item) {
 	// Convert to actual item index if filtering is active
 	int actualItem = item;
-	if (!_filter.empty() && item >= 0 && item < (int)_listIndex.size()) {
+	if (!_listIndex.empty() && item >= 0 && item < (int)_listIndex.size()) {
 		actualItem = _listIndex[item];
 	}
 
 	// Avoid duplicates
-	if (isItemSelected(actualItem))
+	if (isItemSelected(item))
 		return;
 
 	// Insert in ascending order to keep selection sorted
@@ -241,7 +241,7 @@ void ListWidget::addSelectedItem(int item) {
 void ListWidget::removeSelectedItem(int item) {
 	// Convert to actual item index if filtering is active
 	int actualItem = item;
-	if (!_filter.empty() && item >= 0 && item < (int)_listIndex.size()) {
+	if (!_listIndex.empty() && item >= 0 && item < (int)_listIndex.size()) {
 		actualItem = _listIndex[item];
 	}
 


Commit: ac0927e46398a889a780612c5b78f595908ae271
    https://github.com/scummvm/scummvm/commit/ac0927e46398a889a780612c5b78f595908ae271
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-31T12:46:29+03:00

Commit Message:
GUI: Fix grid alphabetic grouping to use user description

Previously, grid view grouped games by internal title, causing mismatches with list view. Now, both views use the user-defined game description for alphabetical grouping, ensuring consistent group headers. Other grouping modes are unchanged.

Changed paths:
    gui/launcher.cpp


diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 3bd7b657f4b..e7f9068eec5 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -1504,7 +1504,7 @@ void LauncherGrid::groupEntries(const Common::Array<LauncherEntry> &metadata) {
 	switch (_groupBy) {
 	case kGroupByFirstLetter: {
 		for (const auto &entry : metadata) {
-			attrs.push_back(entry.title.substr(0, 1));
+			attrs.push_back(entry.description.substr(0, 1));
 		}
 		_grid->setGroupHeaderFormat(Common::U32String(""), Common::U32String("..."));
 		break;


Commit: 8aff66dc92454e36594068a1ef5efc1cb84ceb80
    https://github.com/scummvm/scummvm/commit/8aff66dc92454e36594068a1ef5efc1cb84ceb80
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-31T12:46:29+03:00

Commit Message:
GUI: Fix multi-selection in grouped grid view

Fixes an issue where Shift+Click multi-selection in the grid view would select the wrong games when grouping was active. Now, Shift+Click correctly selects only the games you see between your first and second click, matching the visual order in the grid.

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


diff --git a/gui/widgets/grid.cpp b/gui/widgets/grid.cpp
index 824feb970c7..3286d7025f3 100644
--- a/gui/widgets/grid.cpp
+++ b/gui/widgets/grid.cpp
@@ -301,19 +301,13 @@ void GridItemWidget::handleMouseDown(int x, int y, int button, int clickCount) {
 			}
 		} else if (shiftPressed && _grid->_lastSelectedEntryID >= 0) {
 			// Shift+Click: Select range from last selected to current item
-			int startID = _grid->_lastSelectedEntryID;
-			int endID = _activeEntry->entryID;
-
-			// Ensure start is before end
-			if (startID > endID) {
-				int temp = startID;
-				startID = endID;
-				endID = temp;
-			}
+			// Must select based on visual order, not by entryID range
+			int startID = _grid->getVisualPos(_grid->_lastSelectedEntryID);
+			int endID = _grid->getVisualPos(_activeEntry->entryID);
 
-			// Add all items in range, avoiding duplicates
-			for (int i = startID; i <= endID; ++i) {
-				_grid->addSelectedEntry(i);
+			// If we found both positions, select all items between them (visually)
+			if (startID >= 0 && endID >= 0) {
+				_grid->selectVisualRange(startID, endID);
 			}
 			// Keep entries sorted
 			Common::sort(_grid->_selectedEntries.begin(), _grid->_selectedEntries.end());
@@ -995,6 +989,41 @@ int GridWidget::getNewSel(int index) {
 	}
 }
 
+int GridWidget::getVisualPos(int entryID) const {
+	// Returns the visual position in _sortedEntryList for the given entryID
+	// Counts only non-header items
+	int visPos = 0;
+	for (uint i = 0; i < _sortedEntryList.size(); ++i) {
+		if (!_sortedEntryList[i]->isHeader) {
+			if (_sortedEntryList[i]->entryID == entryID) {
+				return visPos;
+			}
+			visPos++;
+		}
+	}
+	return -1;
+}
+
+void GridWidget::selectVisualRange(int startPos, int endPos) {
+	// Selects all non-header items in visual range [startPos, endPos]
+	// visPos counts only non-header items
+	if (startPos > endPos) {
+		int temp = startPos;
+		startPos = endPos;
+		endPos = temp;
+	}
+
+	int visPos = 0;
+	for (uint i = 0; i < _sortedEntryList.size(); ++i) {
+		if (!_sortedEntryList[i]->isHeader) {
+			if (visPos >= startPos && visPos <= endPos) {
+				addSelectedEntry(_sortedEntryList[i]->entryID);
+			}
+			visPos++;
+		}
+	}
+}
+
 void GridWidget::handleMouseWheel(int x, int y, int direction) {
 	_scrollBar->handleMouseWheel(x, y, direction);
 	_scrollPos = _scrollBar->_currentPos;
diff --git a/gui/widgets/grid.h b/gui/widgets/grid.h
index 2b267c8bfb2..38da2b4eb4e 100644
--- a/gui/widgets/grid.h
+++ b/gui/widgets/grid.h
@@ -214,6 +214,8 @@ public:
 
 	int getItemPos(int item);
 	int getNewSel(int index);
+	int getVisualPos(int entryID) const;
+	void selectVisualRange(int startPos, int endPos);
 	int getScrollPos() const { return _scrollPos; }
 	int getSelected() const { return ((_selectedEntry == nullptr) ? -1 : _selectedEntry->entryID); }
 	int getThumbnailHeight() const { return _thumbnailHeight; }


Commit: 9ef4aac9ab18e910a1d0869359fb5ce0c098950f
    https://github.com/scummvm/scummvm/commit/9ef4aac9ab18e910a1d0869359fb5ce0c098950f
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-31T12:46:29+03:00

Commit Message:
GUI: Add multi-selection with arrow keys in list view

Arrow up/down after a simple click now adds adjacent items to the selection,
without toggling. Updates highlight and last selection position.

Changed paths:
    gui/widgets/list.cpp


diff --git a/gui/widgets/list.cpp b/gui/widgets/list.cpp
index 8c4baaca658..04b6e663756 100644
--- a/gui/widgets/list.cpp
+++ b/gui/widgets/list.cpp
@@ -553,8 +553,14 @@ bool ListWidget::handleKeyDown(Common::KeyState state) {
 			}
 			// fall through
 		case Common::KEYCODE_DOWN:
-			if (_selectedItem < (int)_list.size() - 1)
-				_selectedItem++;
+			// Down: Add next item to selection (Ctrl+Click logic without toggle)
+			if (_selectedItem < (int)_list.size() - 1) {
+				int newItem = _selectedItem + 1;
+				addSelectedItem(newItem);
+				_selectedItem = newItem;
+				_lastSelectionStartItem = newItem;
+				dirty = true;
+			}
 			break;
 
 		case Common::KEYCODE_KP3:
@@ -586,8 +592,14 @@ bool ListWidget::handleKeyDown(Common::KeyState state) {
 			}
 			// fall through
 		case Common::KEYCODE_UP:
-			if (_selectedItem > 0)
-				_selectedItem--;
+			// Up: Add previous item to selection (Ctrl+Click logic without toggle)
+			if (_selectedItem > 0) {
+				int newItem = _selectedItem - 1;
+				addSelectedItem(newItem);
+				_selectedItem = newItem;
+				_lastSelectionStartItem = newItem;
+				dirty = true;
+			}
 			break;
 
 		case Common::KEYCODE_KP9:


Commit: 473bf9b28fbe255ae6e7fee48a96f34b05349251
    https://github.com/scummvm/scummvm/commit/473bf9b28fbe255ae6e7fee48a96f34b05349251
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-31T12:46:29+03:00

Commit Message:
GUI: Optimize multi-selection in ListWidget

Switch ListWidget multi-selection tracking from Common::Array<int> to Common::Array<bool> for add, remove, and lookup operations.
Update all selection-related methods and launcher integration

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


diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index e7f9068eec5..e39a67b5a30 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -1379,10 +1379,15 @@ void LauncherSimple::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 		LauncherDialog::handleCommand(sender, kStartCmd, 0);
 		break;
 	case kListItemRemovalRequestCmd: {
-		const Common::Array<int> &selectedItems = _list->getSelectedItems();
-		if (selectedItems.size() > 1) {
+		const Common::Array<bool> &selectedItemsBool = _list->getSelectedItemsBool();
+		// Count selected items
+		int selectedCount = 0;
+		for (int i = 0; i < (int)selectedItemsBool.size(); ++i) {
+			if (selectedItemsBool[i]) selectedCount++;
+		}
+		if (selectedCount > 1) {
 			// Multi-selection removal: show confirmation dialog with list of games
-			removeListGames(selectedItems);
+			removeListGames(selectedItemsBool);
 		} else {
 			LauncherDialog::handleCommand(sender, kRemoveGameCmd, 0);
 		}
@@ -1420,10 +1425,15 @@ void LauncherSimple::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 	}
 	case kRemoveGameCmd: {
 		// Handle multi-selection removal
-		const Common::Array<int> &selectedItems = _list->getSelectedItems();
-		if (selectedItems.size() > 1) {
+		const Common::Array<bool> &selectedItemsBool = _list->getSelectedItemsBool();
+		// Count selected items
+		int selectedCount = 0;
+		for (int i = 0; i < (int)selectedItemsBool.size(); ++i) {
+			if (selectedItemsBool[i]) selectedCount++;
+		}
+		if (selectedCount > 1) {
 			// Multi-selection removal: show confirmation dialog with list of games
-			removeListGames(selectedItems);
+			removeListGames(selectedItemsBool);
 		} else {
 			// Single selection removal
 			LauncherDialog::handleCommand(sender, cmd, data);
@@ -1435,14 +1445,17 @@ void LauncherSimple::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 	}
 }
 
-void LauncherSimple::removeListGames(const Common::Array<int> &selectedItems) {
+void LauncherSimple::removeListGames(const Common::Array<bool> &selectedItemsBool) {
 	// Build confirmation message with list of games to remove
 	Common::U32String confirmMsg = _("Do you really want to remove the following game configurations?\n\n");
 
-	for (const int &idx : selectedItems) {
-		if (idx >= 0 && idx < (int)_domains.size()) {
+	// Convert bool array to indices and build message
+	Common::Array<int> dataIndicesToRemove;
+	for (int i = 0; i < (int)selectedItemsBool.size(); ++i) {
+		if (selectedItemsBool[i]) {
+			dataIndicesToRemove.push_back(i);
 			// Get the game title from the list
-			confirmMsg += _list->getList()[idx];
+			confirmMsg += _list->getList()[i];
 			confirmMsg += Common::U32String("\n");
 		}
 	}
@@ -1450,7 +1463,7 @@ void LauncherSimple::removeListGames(const Common::Array<int> &selectedItems) {
 	MessageDialog alert(confirmMsg, _("Yes"), _("No"));
 
 	if (alert.runModal() == GUI::kMessageOK) {
-		performGameRemoval(selectedItems, false);
+		performGameRemoval(dataIndicesToRemove, false);
 	}
 }
 
@@ -1462,8 +1475,13 @@ void LauncherSimple::updateSelectionAfterRemoval() {
 
 void LauncherSimple::updateButtons() {
 	int item = _list->getSelected();
-	const Common::Array<int> &selectedItems = _list->getSelectedItems();
-	bool hasMultiSelection = selectedItems.size() > 1;
+	const Common::Array<bool> &selectedItemsBool = _list->getSelectedItemsBool();
+	// Count selected items
+	int selectedCount = 0;
+	for (int i = 0; i < (int)selectedItemsBool.size(); ++i) {
+		if (selectedItemsBool[i]) selectedCount++;
+	}
+	bool hasMultiSelection = selectedCount > 1;
 
 	bool isAddOn = false;
 	if (item >= 0) {
diff --git a/gui/launcher.h b/gui/launcher.h
index 7e9ef9612e9..2fdc034c64e 100644
--- a/gui/launcher.h
+++ b/gui/launcher.h
@@ -256,7 +256,7 @@ public:
 	void handleKeyDown(Common::KeyState state) override;
 
 	LauncherDisplayType getType() const override { return kLauncherDisplayList; }
-	void removeListGames(const Common::Array<int> &selectedItems);
+	void removeListGames(const Common::Array<bool> &selectedItems);
 
 protected:
 	void updateSelectionAfterRemoval() override;
diff --git a/gui/widgets/list.cpp b/gui/widgets/list.cpp
index 04b6e663756..f69141dcbe1 100644
--- a/gui/widgets/list.cpp
+++ b/gui/widgets/list.cpp
@@ -205,9 +205,8 @@ bool ListWidget::isItemSelected(int item) const {
 		actualItem = _listIndex[item];
 	}
 
-	for (const int &selected : _selectedItems) {
-		if (selected == actualItem)
-			return true;
+	if (actualItem >= 0 && actualItem < (int)_selectedItems.size()) {
+		return _selectedItems[actualItem];
 	}
 	return false;
 }
@@ -219,23 +218,10 @@ void ListWidget::addSelectedItem(int item) {
 		actualItem = _listIndex[item];
 	}
 
-	// Avoid duplicates
-	if (isItemSelected(item))
-		return;
-
-	// Insert in ascending order to keep selection sorted
-	bool inserted = false;
-	for (uint i = 0; i < _selectedItems.size(); ++i) {
-		if (_selectedItems[i] > actualItem) {
-			_selectedItems.insert_at(i, actualItem);
-			inserted = true;
-			break;
-		}
+	if (actualItem >= 0 && actualItem < (int)_selectedItems.size()) {
+		_selectedItems[actualItem] = true;
+		markAsDirty();
 	}
-	if (!inserted)
-		_selectedItems.push_back(actualItem);
-
-	markAsDirty();
 }
 
 void ListWidget::removeSelectedItem(int item) {
@@ -245,17 +231,15 @@ void ListWidget::removeSelectedItem(int item) {
 		actualItem = _listIndex[item];
 	}
 
-	for (uint i = 0; i < _selectedItems.size(); ++i) {
-		if (_selectedItems[i] == actualItem) {
-			_selectedItems.remove_at(i);
-			break;
-		}
+	if (actualItem >= 0 && actualItem < (int)_selectedItems.size()) {
+		_selectedItems[actualItem] = false;
+		markAsDirty();
 	}
-	markAsDirty();
 }
 
 void ListWidget::clearSelection() {
-	_selectedItems.clear();
+	// Fill all with false to clear selection
+	Common::fill(_selectedItems.begin(), _selectedItems.end(), false);
 	_lastSelectionStartItem = -1;
 	markAsDirty();
 }
@@ -270,6 +254,17 @@ void ListWidget::selectItemRange(int from, int to) {
 	markAsDirty();
 }
 
+Common::Array<int> ListWidget::getSelectedItems() const {
+	// Convert bool array to int array of selected indices
+	Common::Array<int> _selectedItemsIndices;
+	for (int i = 0; i < (int)_selectedItems.size(); ++i) {
+		if (_selectedItems[i]) {
+			_selectedItemsIndices.push_back(i);
+		}
+	}
+	return _selectedItemsIndices;
+}
+
 void ListWidget::setList(const Common::U32StringArray &list) {
 	if (_editMode && _caretVisible)
 		drawCaret(true);
@@ -286,7 +281,9 @@ void ListWidget::setList(const Common::U32StringArray &list) {
 	if (_currentPos < 0)
 		_currentPos = 0;
 	_selectedItem = -1;
-	_selectedItems.clear(); // Clear multi-selection
+	// Resize and clear bool array
+	_selectedItems.clear();
+	_selectedItems.resize(size, false);
 	_lastSelectionStartItem = -1;
 	_editMode = false;
 	g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
@@ -502,9 +499,13 @@ bool ListWidget::handleKeyDown(Common::KeyState state) {
 
 		switch (state.keycode) {
 		case Common::KEYCODE_RETURN:
-		case Common::KEYCODE_KP_ENTER:
+		case Common::KEYCODE_KP_ENTER: {
 			// Disable activation if multi-select is enabled and multiple items are selected
-			if (_multiSelectEnabled && _selectedItems.size() > 1) {
+			int selectedCount = 0;
+			for (int i = 0; i < (int)_selectedItems.size(); ++i) {
+				if (_selectedItems[i]) selectedCount++;
+			}
+			if (_multiSelectEnabled && selectedCount > 1) {
 					break;
 			}
 			if (_selectedItem >= 0) {
@@ -516,6 +517,7 @@ bool ListWidget::handleKeyDown(Common::KeyState state) {
 					sendCommand(kListItemActivatedCmd, _selectedItem);
 			}
 			break;
+		}
 
 		// Keypad & special keys
 		//   - if num lock is set, we do not handle the keypress
diff --git a/gui/widgets/list.h b/gui/widgets/list.h
index 2ea41b340ae..bb99d204f85 100644
--- a/gui/widgets/list.h
+++ b/gui/widgets/list.h
@@ -72,7 +72,7 @@ protected:
 	int				_currentPos;
 	int				_entriesPerPage;
 	int				_selectedItem;
-	Common::Array<int> _selectedItems;    /// Multiple selected items
+	Common::Array<bool> _selectedItems;    /// Multiple selected items (bool array)
 	int _lastSelectionStartItem;          /// Used for Shift+Click range selection
 	bool			_multiSelectEnabled;	/// Flag for multi-selection
 	ScrollBarWidget	*_scrollBar;
@@ -121,7 +121,8 @@ public:
 	const Common::U32String getSelectedString() const	{ return stripGUIformatting(_list[_selectedItem]); }
 
 	/// Multi-selection support
-	const Common::Array<int> &getSelectedItems() const { return _selectedItems; }
+	const Common::Array<bool> &getSelectedItemsBool() const { return _selectedItems; }
+	Common::Array<int> getSelectedItems() const;  // Returns indices of selected items
 	bool isItemSelected(int item) const;
 	void addSelectedItem(int item);
 	void removeSelectedItem(int item);


Commit: 5b0896697c518e611d69dd9d0b65e854fb6ff4b8
    https://github.com/scummvm/scummvm/commit/5b0896697c518e611d69dd9d0b65e854fb6ff4b8
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-31T12:46:29+03:00

Commit Message:
GUI: Unify game removal logic in launcher views

Consolidate single and multi-removal logic to use multi-removal functions
in both LauncherSimple and LauncherGrid. Remove unnecessary branching and
add validation to ensure at least one item is selected before removal.

Changed paths:
    gui/launcher.cpp


diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index e39a67b5a30..75cd10744e1 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -1379,17 +1379,14 @@ void LauncherSimple::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 		LauncherDialog::handleCommand(sender, kStartCmd, 0);
 		break;
 	case kListItemRemovalRequestCmd: {
+		// Use multi-removal logic for all removals (handles both single and multiple selections)
 		const Common::Array<bool> &selectedItemsBool = _list->getSelectedItemsBool();
-		// Count selected items
-		int selectedCount = 0;
-		for (int i = 0; i < (int)selectedItemsBool.size(); ++i) {
-			if (selectedItemsBool[i]) selectedCount++;
-		}
-		if (selectedCount > 1) {
-			// Multi-selection removal: show confirmation dialog with list of games
-			removeListGames(selectedItemsBool);
-		} else {
-			LauncherDialog::handleCommand(sender, kRemoveGameCmd, 0);
+		// Ensure at least one item is selected before proceeding
+		for (const auto &it : selectedItemsBool) {
+			if (it) {
+				removeListGames(selectedItemsBool);
+				break;
+			}
 		}
 		break;
 	}
@@ -1424,19 +1421,14 @@ void LauncherSimple::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 		break;
 	}
 	case kRemoveGameCmd: {
-		// Handle multi-selection removal
+		// Handle removal using multi-selection logic (works for single and multiple selections)
 		const Common::Array<bool> &selectedItemsBool = _list->getSelectedItemsBool();
-		// Count selected items
-		int selectedCount = 0;
-		for (int i = 0; i < (int)selectedItemsBool.size(); ++i) {
-			if (selectedItemsBool[i]) selectedCount++;
-		}
-		if (selectedCount > 1) {
-			// Multi-selection removal: show confirmation dialog with list of games
-			removeListGames(selectedItemsBool);
-		} else {
-			// Single selection removal
-			LauncherDialog::handleCommand(sender, cmd, data);
+		// Ensure at least one item is selected before proceeding
+		for (const auto &it : selectedItemsBool) {
+			if (it) {
+				removeListGames(selectedItemsBool);
+				break;
+			}
 		}
 		break;
 	}
@@ -1656,11 +1648,9 @@ void LauncherGrid::handleCommand(CommandSender *sender, uint32 cmd, uint32 data)
 		LauncherDialog::handleCommand(sender, kLoadGameCmd, 0);
 		break;
 	case kRemoveGameCmd:
-		// Handle multi-selection removal
-		if (_grid && _grid->getSelectedEntries().size() > 1) {
+		// Handle removal using multi-selection logic (works for single and multiple selections)
+		if (_grid && !_grid->getSelectedEntries().empty()) {
 			removeGridGames();
-		} else {
-			LauncherDialog::handleCommand(sender, cmd, data);
 		}
 		break;
 	case kItemClicked:


Commit: 3bf41f2e665acbc68439a16f5ded6ccc3e5f95c7
    https://github.com/scummvm/scummvm/commit/3bf41f2e665acbc68439a16f5ded6ccc3e5f95c7
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-31T12:46:29+03:00

Commit Message:
GUI: Implement bool array selection for ListWidget and GridWidget

- Replace int array _selectedEntries with bool array _selectedItems in GridWidget
- Rename getSelectedItemsBool() to getSelectedItems() in ListWidget
- Update performGameRemoval() to accept bool array parameter
- Simplify selection logic and improve deletion efficiency

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


diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 75cd10744e1..3d05f802220 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -1031,8 +1031,17 @@ public:
 protected:
 	void updateSelectionAfterRemoval() override {
 		if (_grid) {
-			_grid->_selectedEntries.clear();
-			_grid->_selectedEntries.push_back(_grid->_lastSelectedEntryID);
+			_grid->clearSelection();
+			const Common::Array<bool> &selectedItems = _grid->getSelectedItems();
+			
+			// Select at the same index as before
+			if (_grid->_lastSelectedEntryID < (int)selectedItems.size()) {
+				_grid->addSelectedItem(_grid->_lastSelectedEntryID);
+			} else {
+				// If out of bounds, select the last item
+				_grid->addSelectedItem(selectedItems.size() - 1);
+				_grid->_lastSelectedEntryID = selectedItems.size() - 1;
+			}
 		}
 	}
 	void updateListing(int selPos = -1) override;
@@ -1380,7 +1389,7 @@ void LauncherSimple::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 		break;
 	case kListItemRemovalRequestCmd: {
 		// Use multi-removal logic for all removals (handles both single and multiple selections)
-		const Common::Array<bool> &selectedItemsBool = _list->getSelectedItemsBool();
+		const Common::Array<bool> &selectedItemsBool = _list->getSelectedItems();
 		// Ensure at least one item is selected before proceeding
 		for (const auto &it : selectedItemsBool) {
 			if (it) {
@@ -1422,7 +1431,7 @@ void LauncherSimple::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 	}
 	case kRemoveGameCmd: {
 		// Handle removal using multi-selection logic (works for single and multiple selections)
-		const Common::Array<bool> &selectedItemsBool = _list->getSelectedItemsBool();
+		const Common::Array<bool> &selectedItemsBool = _list->getSelectedItems();
 		// Ensure at least one item is selected before proceeding
 		for (const auto &it : selectedItemsBool) {
 			if (it) {
@@ -1441,11 +1450,9 @@ void LauncherSimple::removeListGames(const Common::Array<bool> &selectedItemsBoo
 	// Build confirmation message with list of games to remove
 	Common::U32String confirmMsg = _("Do you really want to remove the following game configurations?\n\n");
 
-	// Convert bool array to indices and build message
-	Common::Array<int> dataIndicesToRemove;
+	// Build message from bool array
 	for (int i = 0; i < (int)selectedItemsBool.size(); ++i) {
 		if (selectedItemsBool[i]) {
-			dataIndicesToRemove.push_back(i);
 			// Get the game title from the list
 			confirmMsg += _list->getList()[i];
 			confirmMsg += Common::U32String("\n");
@@ -1455,7 +1462,7 @@ void LauncherSimple::removeListGames(const Common::Array<bool> &selectedItemsBoo
 	MessageDialog alert(confirmMsg, _("Yes"), _("No"));
 
 	if (alert.runModal() == GUI::kMessageOK) {
-		performGameRemoval(dataIndicesToRemove, false);
+		performGameRemoval(selectedItemsBool, false);
 	}
 }
 
@@ -1467,7 +1474,7 @@ void LauncherSimple::updateSelectionAfterRemoval() {
 
 void LauncherSimple::updateButtons() {
 	int item = _list->getSelected();
-	const Common::Array<bool> &selectedItemsBool = _list->getSelectedItemsBool();
+	const Common::Array<bool> &selectedItemsBool = _list->getSelectedItems();
 	// Count selected items
 	int selectedCount = 0;
 	for (int i = 0; i < (int)selectedItemsBool.size(); ++i) {
@@ -1649,8 +1656,14 @@ void LauncherGrid::handleCommand(CommandSender *sender, uint32 cmd, uint32 data)
 		break;
 	case kRemoveGameCmd:
 		// Handle removal using multi-selection logic (works for single and multiple selections)
-		if (_grid && !_grid->getSelectedEntries().empty()) {
-			removeGridGames();
+		if (_grid) {
+			const Common::Array<bool> &selectedItems = _grid->getSelectedItems();
+			for (const auto &item : selectedItems) {
+				if (item) {
+					removeGridGames();
+					break;
+				}
+			}
 		}
 		break;
 	case kItemClicked:
@@ -1753,8 +1766,17 @@ int LauncherGrid::getItemPos(int item) {
 void LauncherGrid::updateButtons() {
     LauncherDialog::updateButtons();
     // Enable remove button if at least one entry is selected
-    bool hasSelection = _grid && !_grid->getSelectedEntries().empty();
-    _removeButton->setEnabled(hasSelection);
+    if (_grid) {
+        const Common::Array<bool> &selectedItems = _grid->getSelectedItems();
+        bool hasSelection = false;
+        for (const auto &item : selectedItems) {
+            if (item) {
+                hasSelection = true;
+                break;
+            }
+        }
+        _removeButton->setEnabled(hasSelection);
+    }
 }
 
 void LauncherGrid::selectTarget(const Common::String &target) {
@@ -1800,57 +1822,64 @@ void LauncherGrid::build() {
 void LauncherGrid::removeGridGames() {
 	if (!_grid)
 		return;
-	const Common::Array<int> &selectedEntries = _grid->getSelectedEntries();
-	if (selectedEntries.empty())
-		return;
-
+	const Common::Array<bool> &selectedItems = _grid->getSelectedItems();
+	
+	// Build the confirmation message
 	Common::U32String message = _("Do you really want to remove the following game configurations?\n\n");
-
-	for (int entryID : selectedEntries) {
-		// entryID is the index in _domains and _domainTitles
-		if (entryID >= 0 && entryID < (int)_domains.size()) {
-			Common::String domainName = _domains[entryID];
-			Common::String gameTitle = (entryID < (int)_domainTitles.size()) ? _domainTitles[entryID] : domainName;
-			message += Common::U32String(gameTitle) + "\n";
+	for (int i = 0; i < (int)selectedItems.size(); ++i) {
+		if (selectedItems[i]) {
+			// i is the index in _domains and _domainTitles
+			if (i >= 0 && i < (int)_domains.size()) {
+				Common::String domainName = _domains[i];
+				Common::String gameTitle = (i < (int)_domainTitles.size()) ? _domainTitles[i] : domainName;
+				message += Common::U32String(gameTitle) + "\n";
+			}
 		}
 	}
 
 	MessageDialog alert(message, Common::U32String(_("Yes")), Common::U32String(_("No")));
 	if (alert.runModal() == GUI::kMessageOK) {
-		performGameRemoval(selectedEntries, true);
+		performGameRemoval(selectedItems, true);
 	}
 }
 
 #endif // !DISABLE_LAUNCHERDISPLAY_GRID
 
-void LauncherDialog::performGameRemoval(const Common::Array<int> &selectedItems, bool isGrid) {
+void LauncherDialog::performGameRemoval(const Common::Array<bool> &selectedItems, bool isGrid) {
 	if (selectedItems.empty())
 		return;
 
-	Common::Array<int> itemsToProcess = selectedItems;
-	// For list view only, sort in reverse to avoid index shifting
-	if (!isGrid) {
-		Common::sort(itemsToProcess.begin(), itemsToProcess.end(), Common::Greater<int>());
+	// Check if any items are selected
+	bool hasSelection = false;
+	for (const auto &item : selectedItems) {
+		if (item) {
+			hasSelection = true;
+			break;
+		}
 	}
+	if (!hasSelection)
+		return;
 
 	int selPos = -1;
 	Common::StringArray domainsToRemove;
-	for (const int &idx : itemsToProcess) {
-		if (idx >= 0 && idx < (int)_domains.size()) {
-			if (_groupBy != kGroupByNone && !isGrid) {
-				selPos = getItemPos(idx);
+
+	for (int idx = selectedItems.size() - 1; idx >= 0; --idx) {
+		if (selectedItems[idx]) {
+			if (idx >= 0 && idx < (int)_domains.size()) {
+				if (_groupBy != kGroupByNone && !isGrid) {
+					selPos = getItemPos(idx);
+				}
+				domainsToRemove.push_back(_domains[idx]);
 			}
-			domainsToRemove.push_back(_domains[idx]);
 		}
 	}
 
 	// Remove games and addons
 	removeGamesWithAddons(domainsToRemove);
 
+	updateListing(selPos);
 	// Update UI - each subclass handles its own selection update
 	updateSelectionAfterRemoval();
-	
-	updateListing(selPos);
 	updateButtons();
 	g_gui.scheduleTopDialogRedraw();
 }
diff --git a/gui/launcher.h b/gui/launcher.h
index 2fdc034c64e..645a8bd8856 100644
--- a/gui/launcher.h
+++ b/gui/launcher.h
@@ -196,7 +196,7 @@ protected:
 	 * Shared helper for removing games after confirmation.
 	 * Called by subclasses after building their own confirmation message.
 	 */
-	void performGameRemoval(const Common::Array<int> &selectedItems, bool isGrid);
+	void performGameRemoval(const Common::Array<bool> &selectedItems, bool isGrid);
 
 	/**
 	 * Update selection after game removal.
diff --git a/gui/widgets/grid.cpp b/gui/widgets/grid.cpp
index 3286d7025f3..cffcbe8b341 100644
--- a/gui/widgets/grid.cpp
+++ b/gui/widgets/grid.cpp
@@ -89,13 +89,8 @@ void GridItemWidget::drawWidget() {
 	const int kMarginY = _grid->_gridYSpacing / 3;
 
 	// Check if this entry is in the selected entries list
-	bool isSelected = false;
-	for (size_t i = 0; i < _grid->_selectedEntries.size(); ++i) {
-		if (_grid->_selectedEntries[i] == _activeEntry->entryID) {
-			isSelected = true;
-			break;
-		}
-	}
+	bool isSelected = (_activeEntry->entryID >= 0 && _activeEntry->entryID < (int)_grid->_selectedItems.size()) 
+					  && _grid->_selectedItems[_activeEntry->entryID];
 
 	// Draw selection highlight if this entry is selected or hovered
 	if (isSelected || _isHighlighted) {
@@ -271,8 +266,8 @@ void GridItemWidget::handleMouseDown(int x, int y, int button, int clickCount) {
 
 		// If multi-select is not enabled, use simple single-selection
 		if (!_grid->isMultiSelectEnabled()) {
-			_grid->_selectedEntries.clear();
-			_grid->_selectedEntries.push_back(_activeEntry->entryID);
+			_grid->clearSelection();
+			_grid->addSelectedItem(_activeEntry->entryID);
 			_grid->_lastSelectedEntryID = _activeEntry->entryID;
 			sendCommand(kItemClicked, _activeEntry->entryID);
 			return;
@@ -285,18 +280,10 @@ void GridItemWidget::handleMouseDown(int x, int y, int button, int clickCount) {
 
 		if (ctrlPressed) {
 			// Ctrl+Click: Toggle selection of this item
-			bool found = false;
-			for (size_t i = 0; i < _grid->_selectedEntries.size(); ++i) {
-				if (_grid->_selectedEntries[i] == _activeEntry->entryID) {
-					_grid->_selectedEntries.remove_at(i);
-					found = true;
-					break;
-				}
-			}
-			if (!found) {
-				_grid->_selectedEntries.push_back(_activeEntry->entryID);
-				// Keep entries sorted
-				Common::sort(_grid->_selectedEntries.begin(), _grid->_selectedEntries.end());
+			if (_grid->isItemSelected(_activeEntry->entryID)) {
+				_grid->removeSelectedItem(_activeEntry->entryID);
+			} else {
+				_grid->addSelectedItem(_activeEntry->entryID);
 				_grid->_lastSelectedEntryID = _activeEntry->entryID;
 			}
 		} else if (shiftPressed && _grid->_lastSelectedEntryID >= 0) {
@@ -309,13 +296,11 @@ void GridItemWidget::handleMouseDown(int x, int y, int button, int clickCount) {
 			if (startID >= 0 && endID >= 0) {
 				_grid->selectVisualRange(startID, endID);
 			}
-			// Keep entries sorted
-			Common::sort(_grid->_selectedEntries.begin(), _grid->_selectedEntries.end());
 			_grid->_lastSelectedEntryID = _activeEntry->entryID;
 		} else {
 			// Regular click: Select only this item
-			_grid->_selectedEntries.clear();
-			_grid->_selectedEntries.push_back(_activeEntry->entryID);
+			_grid->clearSelection();
+			_grid->addSelectedItem(_activeEntry->entryID);
 			_grid->_lastSelectedEntryID = _activeEntry->entryID;
 		}
 
@@ -507,10 +492,8 @@ GridWidget::GridWidget(GuiObject *boss, const Common::String &name)
 	_selectedEntry = nullptr;
 	_isGridInvalid = true;
 	_multiSelectEnabled = false;
-	_selectedEntries.clear();
+	_selectedItems.clear();
 	_lastSelectedEntryID = -1;
-	_ctrlPressed = false;
-	_shiftPressed = false;
 }
 
 GridWidget::~GridWidget() {
@@ -575,6 +558,8 @@ void GridWidget::setEntryList(Common::Array<GridItemInfo> *list) {
 	_visibleEntryList.clear();
 	_isGridInvalid = true;
 	_selectedEntry = nullptr;
+	_selectedItems.clear();
+	_selectedItems.resize(list->size(), false);
 
 	for (Common::Array<GridItemInfo>::iterator entryIter = list->begin(); entryIter != list->end(); ++entryIter) {
 		_dataEntryList.push_back(*entryIter);
@@ -1017,7 +1002,7 @@ void GridWidget::selectVisualRange(int startPos, int endPos) {
 	for (uint i = 0; i < _sortedEntryList.size(); ++i) {
 		if (!_sortedEntryList[i]->isHeader) {
 			if (visPos >= startPos && visPos <= endPos) {
-				addSelectedEntry(_sortedEntryList[i]->entryID);
+				addSelectedItem(_sortedEntryList[i]->entryID);
 			}
 			visPos++;
 		}
@@ -1030,19 +1015,10 @@ void GridWidget::handleMouseWheel(int x, int y, int direction) {
 }
 
 bool GridWidget::handleKeyDown(Common::KeyState state) {
-	if (state.flags & Common::KBD_CTRL)
-		_ctrlPressed = true;
-	if (state.flags & Common::KBD_SHIFT)
-		_shiftPressed = true;
 	return false;
 }
 
 bool GridWidget::handleKeyUp(Common::KeyState state) {
-	// Check which specific key was released based on the key code
-	if (state.keycode == Common::KEYCODE_LCTRL || state.keycode == Common::KEYCODE_RCTRL)
-		_ctrlPressed = false;
-	if (state.keycode == Common::KEYCODE_LSHIFT || state.keycode == Common::KEYCODE_RSHIFT)
-		_shiftPressed = false;
 	return false;
 }
 
@@ -1275,12 +1251,30 @@ void GridWidget::setSelected(int id) {
 	}
 }
 
-void GridWidget::addSelectedEntry(int entryID) {
-	for (int selected : _selectedEntries) {
-		if (selected == entryID)
-			return; // Already selected, do not add again
+void GridWidget::addSelectedItem(int entryID) {
+	if (entryID >= 0) {
+		// Expand array if needed
+		if (entryID >= (int)_selectedItems.size()) {
+			_selectedItems.resize(entryID + 1, false);
+		}
+		_selectedItems[entryID] = true;
+	}
+}
+bool GridWidget::isItemSelected(int entryID) const {
+	if (entryID >= 0 && entryID < (int)_selectedItems.size()) {
+		return _selectedItems[entryID];
+	}
+	return false;
+}
+
+void GridWidget::removeSelectedItem(int entryID) {
+	if (entryID >= 0 && entryID < (int)_selectedItems.size()) {
+		_selectedItems[entryID] = false;
 	}
-	_selectedEntries.push_back(entryID);
+}
+
+void GridWidget::clearSelection() {
+	Common::fill(_selectedItems.begin(), _selectedItems.end(), false);
 }
 
 } // End of namespace GUI
diff --git a/gui/widgets/grid.h b/gui/widgets/grid.h
index 38da2b4eb4e..74cadadf8d3 100644
--- a/gui/widgets/grid.h
+++ b/gui/widgets/grid.h
@@ -238,14 +238,13 @@ public:
 	// Multi-selection methods
 	void setMultiSelectEnabled(bool enabled) { _multiSelectEnabled = enabled; }
 	bool isMultiSelectEnabled() const { return _multiSelectEnabled; }
-	void addSelectedEntry(int entryID);
-
-public:
-	Common::Array<int> _selectedEntries; // Stores indices of selected entries
-	int _lastSelectedEntryID = -1;       // Track last selected entry for Shift+Click
-	bool _ctrlPressed = false;           // Track if Ctrl key is pressed
-	bool _shiftPressed = false;          // Track if Shift key is pressed
-	const Common::Array<int> &getSelectedEntries() const { return _selectedEntries; }
+	Common::Array<bool> _selectedItems;	/// Multiple selected items (bool array)
+	int _lastSelectedEntryID = -1;		/// Used for Shift+Click range selection
+	bool isItemSelected(int entryID) const;
+	void addSelectedItem(int entryID);
+	void removeSelectedItem(int entryID);
+	void clearSelection();
+	const Common::Array<bool> &getSelectedItems() const { return _selectedItems; }
 };
 
 /* GridItemWidget */
diff --git a/gui/widgets/list.cpp b/gui/widgets/list.cpp
index f69141dcbe1..14842caae2f 100644
--- a/gui/widgets/list.cpp
+++ b/gui/widgets/list.cpp
@@ -254,17 +254,6 @@ void ListWidget::selectItemRange(int from, int to) {
 	markAsDirty();
 }
 
-Common::Array<int> ListWidget::getSelectedItems() const {
-	// Convert bool array to int array of selected indices
-	Common::Array<int> _selectedItemsIndices;
-	for (int i = 0; i < (int)_selectedItems.size(); ++i) {
-		if (_selectedItems[i]) {
-			_selectedItemsIndices.push_back(i);
-		}
-	}
-	return _selectedItemsIndices;
-}
-
 void ListWidget::setList(const Common::U32StringArray &list) {
 	if (_editMode && _caretVisible)
 		drawCaret(true);
diff --git a/gui/widgets/list.h b/gui/widgets/list.h
index bb99d204f85..5b3dc22da25 100644
--- a/gui/widgets/list.h
+++ b/gui/widgets/list.h
@@ -121,8 +121,7 @@ public:
 	const Common::U32String getSelectedString() const	{ return stripGUIformatting(_list[_selectedItem]); }
 
 	/// Multi-selection support
-	const Common::Array<bool> &getSelectedItemsBool() const { return _selectedItems; }
-	Common::Array<int> getSelectedItems() const;  // Returns indices of selected items
+	const Common::Array<bool> &getSelectedItems() const { return _selectedItems; }
 	bool isItemSelected(int item) const;
 	void addSelectedItem(int item);
 	void removeSelectedItem(int item);


Commit: 21bbca9c5b583d4725e4f1ca42da8634a8848d00
    https://github.com/scummvm/scummvm/commit/21bbca9c5b583d4725e4f1ca42da8634a8848d00
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-31T12:46:29+03:00

Commit Message:
GUI: Refactor selection methods in list and grid widgets

Replace duplicate addSelectedItem() and removeSelectedItem() methods with
a single markSelectedItem(int item, bool state) method in both ListWidget
and GridWidget. Update all callers across list.cpp, grid.cpp, groupedlist.cpp,
and launcher.cpp to use the new unified method.

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


diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 3d05f802220..73b12600f2a 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -1036,10 +1036,10 @@ protected:
 			
 			// Select at the same index as before
 			if (_grid->_lastSelectedEntryID < (int)selectedItems.size()) {
-				_grid->addSelectedItem(_grid->_lastSelectedEntryID);
+				_grid->markSelectedItem(_grid->_lastSelectedEntryID, true);
 			} else {
 				// If out of bounds, select the last item
-				_grid->addSelectedItem(selectedItems.size() - 1);
+				_grid->markSelectedItem(selectedItems.size() - 1, true);
 				_grid->_lastSelectedEntryID = selectedItems.size() - 1;
 			}
 		}
diff --git a/gui/widgets/grid.cpp b/gui/widgets/grid.cpp
index cffcbe8b341..fefd1d032b0 100644
--- a/gui/widgets/grid.cpp
+++ b/gui/widgets/grid.cpp
@@ -267,7 +267,7 @@ void GridItemWidget::handleMouseDown(int x, int y, int button, int clickCount) {
 		// If multi-select is not enabled, use simple single-selection
 		if (!_grid->isMultiSelectEnabled()) {
 			_grid->clearSelection();
-			_grid->addSelectedItem(_activeEntry->entryID);
+			_grid->markSelectedItem(_activeEntry->entryID, true);
 			_grid->_lastSelectedEntryID = _activeEntry->entryID;
 			sendCommand(kItemClicked, _activeEntry->entryID);
 			return;
@@ -281,9 +281,9 @@ void GridItemWidget::handleMouseDown(int x, int y, int button, int clickCount) {
 		if (ctrlPressed) {
 			// Ctrl+Click: Toggle selection of this item
 			if (_grid->isItemSelected(_activeEntry->entryID)) {
-				_grid->removeSelectedItem(_activeEntry->entryID);
+				_grid->markSelectedItem(_activeEntry->entryID, false);
 			} else {
-				_grid->addSelectedItem(_activeEntry->entryID);
+				_grid->markSelectedItem(_activeEntry->entryID, true);
 				_grid->_lastSelectedEntryID = _activeEntry->entryID;
 			}
 		} else if (shiftPressed && _grid->_lastSelectedEntryID >= 0) {
@@ -300,7 +300,7 @@ void GridItemWidget::handleMouseDown(int x, int y, int button, int clickCount) {
 		} else {
 			// Regular click: Select only this item
 			_grid->clearSelection();
-			_grid->addSelectedItem(_activeEntry->entryID);
+			_grid->markSelectedItem(_activeEntry->entryID, true);
 			_grid->_lastSelectedEntryID = _activeEntry->entryID;
 		}
 
@@ -1002,7 +1002,7 @@ void GridWidget::selectVisualRange(int startPos, int endPos) {
 	for (uint i = 0; i < _sortedEntryList.size(); ++i) {
 		if (!_sortedEntryList[i]->isHeader) {
 			if (visPos >= startPos && visPos <= endPos) {
-				addSelectedItem(_sortedEntryList[i]->entryID);
+				markSelectedItem(_sortedEntryList[i]->entryID, true);
 			}
 			visPos++;
 		}
@@ -1251,15 +1251,16 @@ void GridWidget::setSelected(int id) {
 	}
 }
 
-void GridWidget::addSelectedItem(int entryID) {
+void GridWidget::markSelectedItem(int entryID, bool state) {
 	if (entryID >= 0) {
 		// Expand array if needed
 		if (entryID >= (int)_selectedItems.size()) {
 			_selectedItems.resize(entryID + 1, false);
 		}
-		_selectedItems[entryID] = true;
+		_selectedItems[entryID] = state;
 	}
 }
+
 bool GridWidget::isItemSelected(int entryID) const {
 	if (entryID >= 0 && entryID < (int)_selectedItems.size()) {
 		return _selectedItems[entryID];
@@ -1267,12 +1268,6 @@ bool GridWidget::isItemSelected(int entryID) const {
 	return false;
 }
 
-void GridWidget::removeSelectedItem(int entryID) {
-	if (entryID >= 0 && entryID < (int)_selectedItems.size()) {
-		_selectedItems[entryID] = false;
-	}
-}
-
 void GridWidget::clearSelection() {
 	Common::fill(_selectedItems.begin(), _selectedItems.end(), false);
 }
diff --git a/gui/widgets/grid.h b/gui/widgets/grid.h
index 74cadadf8d3..d51db77086e 100644
--- a/gui/widgets/grid.h
+++ b/gui/widgets/grid.h
@@ -241,8 +241,7 @@ public:
 	Common::Array<bool> _selectedItems;	/// Multiple selected items (bool array)
 	int _lastSelectedEntryID = -1;		/// Used for Shift+Click range selection
 	bool isItemSelected(int entryID) const;
-	void addSelectedItem(int entryID);
-	void removeSelectedItem(int entryID);
+	void markSelectedItem(int entryID, bool state);
 	void clearSelection();
 	const Common::Array<bool> &getSelectedItems() const { return _selectedItems; }
 };
diff --git a/gui/widgets/groupedlist.cpp b/gui/widgets/groupedlist.cpp
index bfde9c4b280..35fc2bbf1c6 100644
--- a/gui/widgets/groupedlist.cpp
+++ b/gui/widgets/groupedlist.cpp
@@ -274,9 +274,9 @@ void GroupedListWidget::handleMouseDown(int x, int y, int button, int clickCount
 		} else if (ctrlClick) {
 			// Ctrl+Click: toggle selection for the underlying data index
 			if (isItemSelected(newSelectedItem)) {
-				removeSelectedItem(newSelectedItem);
+				markSelectedItem(newSelectedItem, false);
 			} else {
-				addSelectedItem(newSelectedItem);
+				markSelectedItem(newSelectedItem, true);
 			}
 			_selectedItem = newSelectedItem;
 			_lastSelectionStartItem = newSelectedItem;
@@ -286,7 +286,7 @@ void GroupedListWidget::handleMouseDown(int x, int y, int button, int clickCount
 		// Regular click: clear selection and select only this underlying item
 		clearSelection();
 		_selectedItem = newSelectedItem;
-		addSelectedItem(newSelectedItem);
+		markSelectedItem(newSelectedItem, true);
 		_lastSelectionStartItem = newSelectedItem;
 		sendCommand(kListSelectionChangedCmd, _selectedItem);
 	}
diff --git a/gui/widgets/list.cpp b/gui/widgets/list.cpp
index 14842caae2f..f5363d18c36 100644
--- a/gui/widgets/list.cpp
+++ b/gui/widgets/list.cpp
@@ -211,7 +211,7 @@ bool ListWidget::isItemSelected(int item) const {
 	return false;
 }
 
-void ListWidget::addSelectedItem(int item) {
+void ListWidget::markSelectedItem(int item, bool state) {
 	// Convert to actual item index if filtering is active
 	int actualItem = item;
 	if (!_listIndex.empty() && item >= 0 && item < (int)_listIndex.size()) {
@@ -219,20 +219,7 @@ void ListWidget::addSelectedItem(int item) {
 	}
 
 	if (actualItem >= 0 && actualItem < (int)_selectedItems.size()) {
-		_selectedItems[actualItem] = true;
-		markAsDirty();
-	}
-}
-
-void ListWidget::removeSelectedItem(int item) {
-	// Convert to actual item index if filtering is active
-	int actualItem = item;
-	if (!_listIndex.empty() && item >= 0 && item < (int)_listIndex.size()) {
-		actualItem = _listIndex[item];
-	}
-
-	if (actualItem >= 0 && actualItem < (int)_selectedItems.size()) {
-		_selectedItems[actualItem] = false;
+		_selectedItems[actualItem] = state;
 		markAsDirty();
 	}
 }
@@ -249,7 +236,7 @@ void ListWidget::selectItemRange(int from, int to) {
 		SWAP(from, to);
 
 	for (int i = from; i <= to; ++i) {
-		addSelectedItem(i);
+		markSelectedItem(i, true);
 	}
 	markAsDirty();
 }
@@ -346,12 +333,12 @@ void ListWidget::handleMouseDown(int x, int y, int button, int clickCount) {
 		} else if (ctrlClick) {
 			// Ctrl+Click: Add/remove from selection
 			if (isItemSelected(newSelectedItem)) {
-				removeSelectedItem(newSelectedItem);
+				markSelectedItem(newSelectedItem, false);
 				// If the primary selection is the same item, clear it to avoid highlight
 				sendCommand(kListSelectionChangedCmd, newSelectedItem);
 				markAsDirty();
 			} else {
-				addSelectedItem(newSelectedItem);
+				markSelectedItem(newSelectedItem, true);
 				_selectedItem = newSelectedItem;
 				_lastSelectionStartItem = newSelectedItem;
 			}
@@ -361,7 +348,7 @@ void ListWidget::handleMouseDown(int x, int y, int button, int clickCount) {
 		// Regular click: Clear previous selection and select only this item
 		clearSelection();
 		_selectedItem = newSelectedItem;
-		addSelectedItem(newSelectedItem);
+		markSelectedItem(newSelectedItem, true);
 		_lastSelectionStartItem = newSelectedItem;
 		sendCommand(kListSelectionChangedCmd, _selectedItem);
 	}
@@ -547,7 +534,7 @@ bool ListWidget::handleKeyDown(Common::KeyState state) {
 			// Down: Add next item to selection (Ctrl+Click logic without toggle)
 			if (_selectedItem < (int)_list.size() - 1) {
 				int newItem = _selectedItem + 1;
-				addSelectedItem(newItem);
+				markSelectedItem(newItem, true);
 				_selectedItem = newItem;
 				_lastSelectionStartItem = newItem;
 				dirty = true;
@@ -586,7 +573,7 @@ bool ListWidget::handleKeyDown(Common::KeyState state) {
 			// Up: Add previous item to selection (Ctrl+Click logic without toggle)
 			if (_selectedItem > 0) {
 				int newItem = _selectedItem - 1;
-				addSelectedItem(newItem);
+				markSelectedItem(newItem, true);
 				_selectedItem = newItem;
 				_lastSelectionStartItem = newItem;
 				dirty = true;
diff --git a/gui/widgets/list.h b/gui/widgets/list.h
index 5b3dc22da25..1d8987c802f 100644
--- a/gui/widgets/list.h
+++ b/gui/widgets/list.h
@@ -123,8 +123,7 @@ public:
 	/// Multi-selection support
 	const Common::Array<bool> &getSelectedItems() const { return _selectedItems; }
 	bool isItemSelected(int item) const;
-	void addSelectedItem(int item);
-	void removeSelectedItem(int item);
+	void markSelectedItem(int item, bool state);
 	void clearSelection();
 	void selectItemRange(int from, int to);
 	void setNumberingMode(NumberingMode numberingMode)	{ _numberingMode = numberingMode; }


Commit: 74592508f801959a543cc4764b98760e75b0a1c1
    https://github.com/scummvm/scummvm/commit/74592508f801959a543cc4764b98760e75b0a1c1
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-31T12:46:29+03:00

Commit Message:
GUI: Fix list item visual deselection in multi-select mode

Remove _selectedItem check from highlight condition in ListWidget and
GroupedListWidget draw methods. This ensures visual state immediately
reflects the actual selection array when items are deselected via
Ctrl+Click, rather than waiting for the next redraw cycle.

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


diff --git a/gui/widgets/groupedlist.cpp b/gui/widgets/groupedlist.cpp
index 35fc2bbf1c6..4abf1b95d00 100644
--- a/gui/widgets/groupedlist.cpp
+++ b/gui/widgets/groupedlist.cpp
@@ -395,7 +395,7 @@ void GroupedListWidget::drawWidget() {
 		int mapped = _listIndex[pos];
 		bool isRealItem = (mapped >= 0);
 		if (isRealItem) {
-			if (_selectedItem == pos || isItemSelected(pos))
+			if (isItemSelected(pos))
 				inverted = _inversion;
 		}
 
diff --git a/gui/widgets/list.cpp b/gui/widgets/list.cpp
index f5363d18c36..551b499bd84 100644
--- a/gui/widgets/list.cpp
+++ b/gui/widgets/list.cpp
@@ -334,9 +334,6 @@ void ListWidget::handleMouseDown(int x, int y, int button, int clickCount) {
 			// Ctrl+Click: Add/remove from selection
 			if (isItemSelected(newSelectedItem)) {
 				markSelectedItem(newSelectedItem, false);
-				// If the primary selection is the same item, clear it to avoid highlight
-				sendCommand(kListSelectionChangedCmd, newSelectedItem);
-				markAsDirty();
 			} else {
 				markSelectedItem(newSelectedItem, true);
 				_selectedItem = newSelectedItem;
@@ -667,7 +664,7 @@ void ListWidget::drawWidget() {
 		ThemeEngine::TextInversionState inverted = ThemeEngine::kTextInversionNone;
 
 		// Draw the selected item inverted, on a highlighted background.
-		if (_selectedItem == pos || isItemSelected(pos))
+		if (isItemSelected(pos))
 			inverted = _inversion;
 
 		// Get state for drawing the item text


Commit: 2a2bbb6f2d00b9b90b17445425728b836828d1de
    https://github.com/scummvm/scummvm/commit/2a2bbb6f2d00b9b90b17445425728b836828d1de
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-31T12:46:29+03:00

Commit Message:
GUI: Enable continuous Shift+Click range expansion in GroupedListWidget

Update _lastSelectionStartItem after selections to allow sequential Shift+Clicks to extend from the previous range endpoint. Move tracking updates in Ctrl+Click to only occur when adding, not removing.

Changed paths:
    gui/widgets/groupedlist.cpp


diff --git a/gui/widgets/groupedlist.cpp b/gui/widgets/groupedlist.cpp
index 4abf1b95d00..ec31cfa6e54 100644
--- a/gui/widgets/groupedlist.cpp
+++ b/gui/widgets/groupedlist.cpp
@@ -269,7 +269,8 @@ void GroupedListWidget::handleMouseDown(int x, int y, int button, int clickCount
 			int startListIndex = _lastSelectionStartItem;
 			int endListIndex = newSelectedItem;              
 			selectItemRange(startListIndex, endListIndex);
-            _selectedItem = newSelectedItem;
+			_selectedItem = newSelectedItem;
+			_lastSelectionStartItem = newSelectedItem;
 			sendCommand(kListSelectionChangedCmd, _selectedItem);
 		} else if (ctrlClick) {
 			// Ctrl+Click: toggle selection for the underlying data index
@@ -277,9 +278,9 @@ void GroupedListWidget::handleMouseDown(int x, int y, int button, int clickCount
 				markSelectedItem(newSelectedItem, false);
 			} else {
 				markSelectedItem(newSelectedItem, true);
+				_selectedItem = newSelectedItem;
+				_lastSelectionStartItem = newSelectedItem;
 			}
-			_selectedItem = newSelectedItem;
-			_lastSelectionStartItem = newSelectedItem;
 			sendCommand(kListSelectionChangedCmd, _selectedItem);
 		}
 	} else {


Commit: 9fe00373144f6b659fa1326a01320e8a5f351b87
    https://github.com/scummvm/scummvm/commit/9fe00373144f6b659fa1326a01320e8a5f351b87
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-31T12:46:29+03:00

Commit Message:
GUI: Fix button enable logic in launcher list view

Optimize selection counting with early exit, and fix Remove/Load button
state by using explicit hasSelection flag instead of relying on stale
_selectedItem. This ensures buttons properly disable when all items are
deselected.

Changed paths:
    gui/launcher.cpp


diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 73b12600f2a..8019d29bfd9 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -1479,24 +1479,25 @@ void LauncherSimple::updateButtons() {
 	int selectedCount = 0;
 	for (int i = 0; i < (int)selectedItemsBool.size(); ++i) {
 		if (selectedItemsBool[i]) selectedCount++;
+		if (selectedCount == 2) break;
 	}
 	bool hasMultiSelection = selectedCount > 1;
-
+	bool hasSelection = selectedCount > 0;
 	bool isAddOn = false;
 	if (item >= 0) {
 		const Common::ConfigManager::Domain *domain = ConfMan.getDomain(_domains[item]);
 		isAddOn = domain && domain->contains("parent");
 	}
 
-	bool enable = (item >= 0 && !isAddOn && !hasMultiSelection);
+	bool enable = (item >= 0 && !isAddOn && !hasMultiSelection && hasSelection);
 
 	_startButton->setEnabled(enable);
 	_editButton->setEnabled(enable);
-	_removeButton->setEnabled(item >= 0 || hasMultiSelection);
+	_removeButton->setEnabled(hasSelection);
 
 	bool en = enable;
 
-	if (item >= 0 && !isAddOn && !hasMultiSelection)
+	if (item >= 0 && !isAddOn && !hasMultiSelection && hasSelection)
 		en = !(Common::checkGameGUIOption(GUIO_NOLAUNCHLOAD, ConfMan.get("guioptions", _domains[item])));
 
 	_loadButton->setEnabled(en);


Commit: 77fe2d4c5a9684874cb47115b2e1920610fbaa79
    https://github.com/scummvm/scummvm/commit/77fe2d4c5a9684874cb47115b2e1920610fbaa79
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-31T12:46:29+03:00

Commit Message:
GUI: Fix unwanted scrolling during multi-select keyboard operations

Move scrollToCurrent() from end of handleKeyDown() to specific actions
only. Prevents list from jumping to current item during Ctrl+Click or
Shift+Click multi-select operations, keeping the view stable.

Changed paths:
    gui/widgets/list.cpp


diff --git a/gui/widgets/list.cpp b/gui/widgets/list.cpp
index 551b499bd84..52a0d2bffc1 100644
--- a/gui/widgets/list.cpp
+++ b/gui/widgets/list.cpp
@@ -489,6 +489,7 @@ bool ListWidget::handleKeyDown(Common::KeyState state) {
 				} else
 					sendCommand(kListItemActivatedCmd, _selectedItem);
 			}
+				scrollToCurrent();
 			break;
 		}
 
@@ -508,6 +509,7 @@ bool ListWidget::handleKeyDown(Common::KeyState state) {
 			if (_selectedItem >= 0) {
 				sendCommand(kListItemRemovalRequestCmd, _selectedItem);
 			}
+			scrollToCurrent();
 			break;
 
 		case Common::KEYCODE_KP1:
@@ -518,6 +520,7 @@ bool ListWidget::handleKeyDown(Common::KeyState state) {
 			// fall through
 		case Common::KEYCODE_END:
 			_selectedItem = _list.size() - 1;
+			scrollToCurrent();
 			break;
 
 
@@ -534,6 +537,7 @@ bool ListWidget::handleKeyDown(Common::KeyState state) {
 				markSelectedItem(newItem, true);
 				_selectedItem = newItem;
 				_lastSelectionStartItem = newItem;
+				scrollToCurrent();
 				dirty = true;
 			}
 			break;
@@ -548,6 +552,7 @@ bool ListWidget::handleKeyDown(Common::KeyState state) {
 			_selectedItem += _entriesPerPage - 1;
 			if (_selectedItem >= (int)_list.size() )
 				_selectedItem = _list.size() - 1;
+			scrollToCurrent();
 			break;
 
 		case Common::KEYCODE_KP7:
@@ -558,6 +563,7 @@ bool ListWidget::handleKeyDown(Common::KeyState state) {
 			// fall through
 		case Common::KEYCODE_HOME:
 			_selectedItem = 0;
+			scrollToCurrent();
 			break;
 
 		case Common::KEYCODE_KP8:
@@ -573,6 +579,7 @@ bool ListWidget::handleKeyDown(Common::KeyState state) {
 				markSelectedItem(newItem, true);
 				_selectedItem = newItem;
 				_lastSelectionStartItem = newItem;
+				scrollToCurrent();
 				dirty = true;
 			}
 			break;
@@ -587,13 +594,12 @@ bool ListWidget::handleKeyDown(Common::KeyState state) {
 			_selectedItem -= _entriesPerPage - 1;
 			if (_selectedItem < 0)
 				_selectedItem = 0;
+			scrollToCurrent();
 			break;
 
 		default:
 			handled = false;
 		}
-
-		scrollToCurrent();
 	}
 
 	if (dirty || _selectedItem != oldSelectedItem)


Commit: 8400ccd03d993bbf962b71bb8308eae6d51970d8
    https://github.com/scummvm/scummvm/commit/8400ccd03d993bbf962b71bb8308eae6d51970d8
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-31T12:46:29+03:00

Commit Message:
GUI: Improve comments in game removal validation

Co-Authored-By: Eugene Sandulenko <sev at scummvm.org>

Changed paths:
    gui/launcher.cpp


diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 8019d29bfd9..0328b1eddfd 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -1390,7 +1390,7 @@ void LauncherSimple::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 	case kListItemRemovalRequestCmd: {
 		// Use multi-removal logic for all removals (handles both single and multiple selections)
 		const Common::Array<bool> &selectedItemsBool = _list->getSelectedItems();
-		// Ensure at least one item is selected before proceeding
+		// If we have one or more items selected, perform the removal
 		for (const auto &it : selectedItemsBool) {
 			if (it) {
 				removeListGames(selectedItemsBool);


Commit: 212ce75f98204bd5ab4c001e24a53aa4287425cb
    https://github.com/scummvm/scummvm/commit/212ce75f98204bd5ab4c001e24a53aa4287425cb
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-31T12:46:29+03:00

Commit Message:
GUI: Show game count and limit removal confirmation list to 10 items

Add counter to removal confirmation dialog displaying total number of
games to be removed. Display maximum 10 game names with ellipsis if
more games are selected, improving UX for bulk removals.

Changed paths:
    gui/launcher.cpp


diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 0328b1eddfd..77a1cecebb7 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -1447,18 +1447,38 @@ void LauncherSimple::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 }
 
 void LauncherSimple::removeListGames(const Common::Array<bool> &selectedItemsBool) {
-	// Build confirmation message with list of games to remove
-	Common::U32String confirmMsg = _("Do you really want to remove the following game configurations?\n\n");
+	// Count selected items
+	int selectedCount = 0;
+	for (int i = 0; i < (int)selectedItemsBool.size(); ++i) {
+		if (selectedItemsBool[i]) {
+			selectedCount++;
+		}
+	}
 
-	// Build message from bool array
+	// Build confirmation message with count
+	Common::U32String confirmMsg = Common::U32String::format(
+		_("Do you really want to remove the following %d game configuration%s?\n\n"),
+		selectedCount,
+		selectedCount == 1 ? "" : "s");
+
+	// Build message from bool array (max 10 items, then ellipsis)
+	int displayCount = 0;
 	for (int i = 0; i < (int)selectedItemsBool.size(); ++i) {
 		if (selectedItemsBool[i]) {
 			// Get the game title from the list
 			confirmMsg += _list->getList()[i];
 			confirmMsg += Common::U32String("\n");
+			displayCount++;
+			if (displayCount >= 10)
+				break;
 		}
 	}
 
+	// Add ellipsis if more than 10 items are selected
+	if (selectedCount > 10) {
+		confirmMsg += Common::U32String("...\n");
+	}
+
 	MessageDialog alert(confirmMsg, _("Yes"), _("No"));
 
 	if (alert.runModal() == GUI::kMessageOK) {
@@ -1825,8 +1845,22 @@ void LauncherGrid::removeGridGames() {
 		return;
 	const Common::Array<bool> &selectedItems = _grid->getSelectedItems();
 	
-	// Build the confirmation message
-	Common::U32String message = _("Do you really want to remove the following game configurations?\n\n");
+	// Count selected items
+	int selectedCount = 0;
+	for (int i = 0; i < (int)selectedItems.size(); ++i) {
+		if (selectedItems[i]) {
+			selectedCount++;
+		}
+	}
+
+	// Build the confirmation message with count
+	Common::U32String message = Common::U32String::format(
+		_("Do you really want to remove the following %d game configuration%s?\n\n"),
+		selectedCount,
+		selectedCount == 1 ? "" : "s");
+
+	// Add games to message (max 10 items, then ellipsis)
+	int displayCount = 0;
 	for (int i = 0; i < (int)selectedItems.size(); ++i) {
 		if (selectedItems[i]) {
 			// i is the index in _domains and _domainTitles
@@ -1834,10 +1868,18 @@ void LauncherGrid::removeGridGames() {
 				Common::String domainName = _domains[i];
 				Common::String gameTitle = (i < (int)_domainTitles.size()) ? _domainTitles[i] : domainName;
 				message += Common::U32String(gameTitle) + "\n";
+				displayCount++;
+				if (displayCount >= 10)
+					break;
 			}
 		}
 	}
 
+	// Add ellipsis if more than 10 items are selected
+	if (selectedCount > 10) {
+		message += Common::U32String("...\n");
+	}
+
 	MessageDialog alert(message, Common::U32String(_("Yes")), Common::U32String(_("No")));
 	if (alert.runModal() == GUI::kMessageOK) {
 		performGameRemoval(selectedItems, true);


Commit: 3037fd364b234db340385e92b347903ad193e86c
    https://github.com/scummvm/scummvm/commit/3037fd364b234db340385e92b347903ad193e86c
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-31T12:46:29+03:00

Commit Message:
GUI: Add comments explaining button enable logic in list view

Add comments explaining selection requirements for Start, Edit, Remove,
and Load buttons.

Changed paths:
    gui/launcher.cpp


diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 77a1cecebb7..3934e055d76 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -1502,6 +1502,7 @@ void LauncherSimple::updateButtons() {
 		if (selectedCount == 2) break;
 	}
 	bool hasMultiSelection = selectedCount > 1;
+	// Check if at least one entry is selected
 	bool hasSelection = selectedCount > 0;
 	bool isAddOn = false;
 	if (item >= 0) {
@@ -1509,14 +1510,18 @@ void LauncherSimple::updateButtons() {
 		isAddOn = domain && domain->contains("parent");
 	}
 
+	// Enable Start/Edit buttons only if a single game is selected (not add-on)
 	bool enable = (item >= 0 && !isAddOn && !hasMultiSelection && hasSelection);
 
 	_startButton->setEnabled(enable);
 	_editButton->setEnabled(enable);
+
+	// Enable Remove button if at least one game is selected
 	_removeButton->setEnabled(hasSelection);
 
 	bool en = enable;
 
+	// Enable Load button only if a single is game selected, unless the game disables it via GUI options
 	if (item >= 0 && !isAddOn && !hasMultiSelection && hasSelection)
 		en = !(Common::checkGameGUIOption(GUIO_NOLAUNCHLOAD, ConfMan.get("guioptions", _domains[item])));
 


Commit: 6f7909f55731e897fd06ce830388abf8daed819f
    https://github.com/scummvm/scummvm/commit/6f7909f55731e897fd06ce830388abf8daed819f
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-31T12:46:29+03:00

Commit Message:
GUI: Use more appropriate condition for multi-selection detection

Change selection count check from selectedCount == 2 to selectedCount > 1
to better reflect the intent of detecting multi-selection.

Changed paths:
    gui/launcher.cpp


diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 3934e055d76..613d9a47b19 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -1495,11 +1495,11 @@ void LauncherSimple::updateSelectionAfterRemoval() {
 void LauncherSimple::updateButtons() {
 	int item = _list->getSelected();
 	const Common::Array<bool> &selectedItemsBool = _list->getSelectedItems();
-	// Count selected items
+	// Count selected items (early exit once we know there's multi-selection)
 	int selectedCount = 0;
 	for (int i = 0; i < (int)selectedItemsBool.size(); ++i) {
 		if (selectedItemsBool[i]) selectedCount++;
-		if (selectedCount == 2) break;
+		if (selectedCount > 1) break;
 	}
 	bool hasMultiSelection = selectedCount > 1;
 	// Check if at least one entry is selected


Commit: 171568aec0f2d334c4241f493baaa7755b4db9f8
    https://github.com/scummvm/scummvm/commit/171568aec0f2d334c4241f493baaa7755b4db9f8
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-31T12:46:29+03:00

Commit Message:
GUI: Improve comments for game removal

Changed paths:
    gui/launcher.cpp


diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 613d9a47b19..12ad213f036 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -1430,7 +1430,7 @@ void LauncherSimple::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 		break;
 	}
 	case kRemoveGameCmd: {
-		// Handle removal using multi-selection logic (works for single and multiple selections)
+		// Remove games if we have any selection
 		const Common::Array<bool> &selectedItemsBool = _list->getSelectedItems();
 		// Ensure at least one item is selected before proceeding
 		for (const auto &it : selectedItemsBool) {
@@ -1681,7 +1681,7 @@ void LauncherGrid::handleCommand(CommandSender *sender, uint32 cmd, uint32 data)
 		LauncherDialog::handleCommand(sender, kLoadGameCmd, 0);
 		break;
 	case kRemoveGameCmd:
-		// Handle removal using multi-selection logic (works for single and multiple selections)
+		// Remove games if we have any selection
 		if (_grid) {
 			const Common::Array<bool> &selectedItems = _grid->getSelectedItems();
 			for (const auto &item : selectedItems) {


Commit: 2fbfc34b519f8ab6f5d60c8845f3b0c3f2d1ae3c
    https://github.com/scummvm/scummvm/commit/2fbfc34b519f8ab6f5d60c8845f3b0c3f2d1ae3c
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-31T12:46:29+03:00

Commit Message:
GUI: Simplify selection restoration after game removal

Add getVisualPos() method to ListWidget to convert real data indices to
visual indices in filtered/grouped lists. Simplify selection restoration
in both LauncherSimple and LauncherGrid using MIN() to handle out-of-bounds
cases cleanly. Move _lastSelectionStartItem to public for direct access.

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


diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 12ad213f036..76419d5772a 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -1034,14 +1034,9 @@ protected:
 			_grid->clearSelection();
 			const Common::Array<bool> &selectedItems = _grid->getSelectedItems();
 			
-			// Select at the same index as before
-			if (_grid->_lastSelectedEntryID < (int)selectedItems.size()) {
-				_grid->markSelectedItem(_grid->_lastSelectedEntryID, true);
-			} else {
-				// If out of bounds, select the last item
-				_grid->markSelectedItem(selectedItems.size() - 1, true);
-				_grid->_lastSelectedEntryID = selectedItems.size() - 1;
-			}
+			// Select at the same index as before, or the last item if out of bounds
+			_grid->_lastSelectedEntryID = MIN((int)selectedItems.size() - 1, _grid->_lastSelectedEntryID);
+			_grid->markSelectedItem(_grid->_lastSelectedEntryID, true);
 		}
 	}
 	void updateListing(int selPos = -1) override;
@@ -1489,6 +1484,12 @@ void LauncherSimple::removeListGames(const Common::Array<bool> &selectedItemsBoo
 void LauncherSimple::updateSelectionAfterRemoval() {
 	if (_list) {
 		_list->clearSelection();
+		const Common::Array<bool> &selectedItems = _list->getSelectedItems();
+		
+		// Get the real data index of the last selected item and adjust with bounds
+		int lastSelectedDataItem = MIN((int)selectedItems.size() - 1, _list->getSelected());
+		// Convert real data index to visual index for marking
+		_list->markSelectedItem(_list->getVisualPos(lastSelectedDataItem), true);
 	}
 }
 
diff --git a/gui/widgets/list.cpp b/gui/widgets/list.cpp
index 52a0d2bffc1..5d738108800 100644
--- a/gui/widgets/list.cpp
+++ b/gui/widgets/list.cpp
@@ -277,6 +277,23 @@ void ListWidget::append(const Common::String &s) {
 	scrollBarRecalc();
 }
 
+int ListWidget::getVisualPos(int dataIndex) const {
+	// If no filtering, visual index equals data index
+	if (_listIndex.empty()) {
+		return dataIndex;
+	}
+	
+	// Find visual index by searching _listIndex for dataIndex
+	for (uint i = 0; i < _listIndex.size(); ++i) {
+		if (_listIndex[i] == dataIndex) {
+			return i;
+		}
+	}
+	
+	// Not found
+	return -1;
+}
+
 void ListWidget::scrollTo(int item) {
 	int size = _list.size();
 	if (item >= size)
diff --git a/gui/widgets/list.h b/gui/widgets/list.h
index 1d8987c802f..7c895a357e7 100644
--- a/gui/widgets/list.h
+++ b/gui/widgets/list.h
@@ -73,7 +73,6 @@ protected:
 	int				_entriesPerPage;
 	int				_selectedItem;
 	Common::Array<bool> _selectedItems;    /// Multiple selected items (bool array)
-	int _lastSelectionStartItem;          /// Used for Shift+Click range selection
 	bool			_multiSelectEnabled;	/// Flag for multi-selection
 	ScrollBarWidget	*_scrollBar;
 	int				_currentKeyDown;
@@ -120,12 +119,16 @@ public:
 
 	const Common::U32String getSelectedString() const	{ return stripGUIformatting(_list[_selectedItem]); }
 
+	/// Get visual position (index in filtered list) from real data index
+	int getVisualPos(int dataIndex) const;
+
 	/// Multi-selection support
 	const Common::Array<bool> &getSelectedItems() const { return _selectedItems; }
 	bool isItemSelected(int item) const;
 	void markSelectedItem(int item, bool state);
 	void clearSelection();
 	void selectItemRange(int from, int to);
+	int _lastSelectionStartItem;          /// Used for Shift+Click range selection
 	void setNumberingMode(NumberingMode numberingMode)	{ _numberingMode = numberingMode; }
 
 	void scrollTo(int item);


Commit: 31d973348206a05cf499d2181e0ebe24ab70a471
    https://github.com/scummvm/scummvm/commit/31d973348206a05cf499d2181e0ebe24ab70a471
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-31T12:46:29+03:00

Commit Message:
GUI: Refactor game removal to use common virtual method

Replace removeListGames() and removeGridGames() with a single virtual
confirmRemoveGames() method in LauncherDialog. Move selection validation
logic and selection array retrieval into each subclass implementation,
eliminating duplicate checking code in command handlers. Simplify
LauncherSimple and LauncherGrid removal logic while maintaining the
same functionality.

Changed paths:
    gui/launcher.cpp
    gui/launcher.h


diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 76419d5772a..eb12ff96703 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -1026,9 +1026,8 @@ public:
 
 	LauncherDisplayType getType() const override { return kLauncherDisplayGrid; }
 
-public:
-	void removeGridGames();
 protected:
+	void confirmRemoveGames(const Common::Array<bool> &selectedItems) override;
 	void updateSelectionAfterRemoval() override {
 		if (_grid) {
 			_grid->clearSelection();
@@ -1383,15 +1382,8 @@ void LauncherSimple::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 		LauncherDialog::handleCommand(sender, kStartCmd, 0);
 		break;
 	case kListItemRemovalRequestCmd: {
-		// Use multi-removal logic for all removals (handles both single and multiple selections)
-		const Common::Array<bool> &selectedItemsBool = _list->getSelectedItems();
-		// If we have one or more items selected, perform the removal
-		for (const auto &it : selectedItemsBool) {
-			if (it) {
-				removeListGames(selectedItemsBool);
-				break;
-			}
-		}
+		// Remove games if we have any selection
+		confirmRemoveGames(_list->getSelectedItems());
 		break;
 	}
 	case kListSelectionChangedCmd:
@@ -1426,14 +1418,7 @@ void LauncherSimple::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 	}
 	case kRemoveGameCmd: {
 		// Remove games if we have any selection
-		const Common::Array<bool> &selectedItemsBool = _list->getSelectedItems();
-		// Ensure at least one item is selected before proceeding
-		for (const auto &it : selectedItemsBool) {
-			if (it) {
-				removeListGames(selectedItemsBool);
-				break;
-			}
-		}
+		confirmRemoveGames(_list->getSelectedItems());
 		break;
 	}
 	default:
@@ -1441,7 +1426,18 @@ void LauncherSimple::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 	}
 }
 
-void LauncherSimple::removeListGames(const Common::Array<bool> &selectedItemsBool) {
+void LauncherSimple::confirmRemoveGames(const Common::Array<bool> &selectedItemsBool) {
+	// Validate that at least one item is selected
+	bool hasSelection = false;
+	for (const auto &item : selectedItemsBool) {
+		if (item) {
+			hasSelection = true;
+			break;
+		}
+	}
+	if (!hasSelection)
+		return;
+	
 	// Count selected items
 	int selectedCount = 0;
 	for (int i = 0; i < (int)selectedItemsBool.size(); ++i) {
@@ -1684,13 +1680,7 @@ void LauncherGrid::handleCommand(CommandSender *sender, uint32 cmd, uint32 data)
 	case kRemoveGameCmd:
 		// Remove games if we have any selection
 		if (_grid) {
-			const Common::Array<bool> &selectedItems = _grid->getSelectedItems();
-			for (const auto &item : selectedItems) {
-				if (item) {
-					removeGridGames();
-					break;
-				}
-			}
+			confirmRemoveGames(_grid->getSelectedItems());
 		}
 		break;
 	case kItemClicked:
@@ -1846,10 +1836,20 @@ void LauncherGrid::build() {
 	updateButtons();
 }
 
-void LauncherGrid::removeGridGames() {
+void LauncherGrid::confirmRemoveGames(const Common::Array<bool> &selectedItems) {
 	if (!_grid)
 		return;
-	const Common::Array<bool> &selectedItems = _grid->getSelectedItems();
+	
+	// Validate that at least one item is selected
+	bool hasSelection = false;
+	for (const auto &item : selectedItems) {
+		if (item) {
+			hasSelection = true;
+			break;
+		}
+	}
+	if (!hasSelection)
+		return;
 	
 	// Count selected items
 	int selectedCount = 0;
diff --git a/gui/launcher.h b/gui/launcher.h
index 645a8bd8856..9c3837c5242 100644
--- a/gui/launcher.h
+++ b/gui/launcher.h
@@ -198,6 +198,13 @@ protected:
 	 */
 	void performGameRemoval(const Common::Array<bool> &selectedItems, bool isGrid);
 
+	/**
+	 * Handle game removal confirmation with selection validation.
+	 * Checks if at least one item is selected, then calls the subclass
+	 * implementation to show the removal confirmation dialog.
+	 */
+	virtual void confirmRemoveGames(const Common::Array<bool> &selectedItems) = 0;
+
 	/**
 	 * Update selection after game removal.
 	 * Each subclass handles its own UI-specific selection logic.
@@ -256,9 +263,9 @@ public:
 	void handleKeyDown(Common::KeyState state) override;
 
 	LauncherDisplayType getType() const override { return kLauncherDisplayList; }
-	void removeListGames(const Common::Array<bool> &selectedItems);
 
 protected:
+	void confirmRemoveGames(const Common::Array<bool> &selectedItems) override;
 	void updateSelectionAfterRemoval() override;
 	void updateListing(int selPos = -1) override;
 	int getItemPos(int item) override;


Commit: 375449cb599f6d26280bdda2ea3231a36b980629
    https://github.com/scummvm/scummvm/commit/375449cb599f6d26280bdda2ea3231a36b980629
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-31T12:46:29+03:00

Commit Message:
GUI: Separate LauncherGrid::updateSelectionAfterRemoval() implementation

Moves the inline method implementation from the class declaration to a
separate method body, improving code organization and consistency with
other launcher methods.

Changed paths:
    gui/launcher.cpp


diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index eb12ff96703..766b6fd69bb 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -1028,16 +1028,7 @@ public:
 
 protected:
 	void confirmRemoveGames(const Common::Array<bool> &selectedItems) override;
-	void updateSelectionAfterRemoval() override {
-		if (_grid) {
-			_grid->clearSelection();
-			const Common::Array<bool> &selectedItems = _grid->getSelectedItems();
-			
-			// Select at the same index as before, or the last item if out of bounds
-			_grid->_lastSelectedEntryID = MIN((int)selectedItems.size() - 1, _grid->_lastSelectedEntryID);
-			_grid->markSelectedItem(_grid->_lastSelectedEntryID, true);
-		}
-	}
+	void updateSelectionAfterRemoval() override;
 	void updateListing(int selPos = -1) override;
 	int getItemPos(int item) override;
 	void groupEntries(const Common::Array<LauncherEntry> &metadata);
@@ -1892,6 +1883,17 @@ void LauncherGrid::confirmRemoveGames(const Common::Array<bool> &selectedItems)
 	}
 }
 
+void LauncherGrid::updateSelectionAfterRemoval() {
+	if (_grid) {
+		_grid->clearSelection();
+		const Common::Array<bool> &selectedItems = _grid->getSelectedItems();
+		
+		// Select at the same index as before, or the last item if out of bounds
+		_grid->_lastSelectedEntryID = MIN((int)selectedItems.size() - 1, _grid->_lastSelectedEntryID);
+		_grid->markSelectedItem(_grid->_lastSelectedEntryID, true);
+	}
+}
+
 #endif // !DISABLE_LAUNCHERDISPLAY_GRID
 
 void LauncherDialog::performGameRemoval(const Common::Array<bool> &selectedItems, bool isGrid) {


Commit: c5475e5bd0c1c833b086cff1f4ad3e13c4590da6
    https://github.com/scummvm/scummvm/commit/c5475e5bd0c1c833b086cff1f4ad3e13c4590da6
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-31T12:46:29+03:00

Commit Message:
GUI: Simplify variable naming - selectedItemsBool to selectedItems

Removes redundant type suffix from bool array variable names throughout
LauncherSimple methods, improving code clarity and consistency.

Changed paths:
    gui/launcher.cpp


diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 766b6fd69bb..6d33d83ab35 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -1417,10 +1417,10 @@ void LauncherSimple::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 	}
 }
 
-void LauncherSimple::confirmRemoveGames(const Common::Array<bool> &selectedItemsBool) {
+void LauncherSimple::confirmRemoveGames(const Common::Array<bool> &selectedItems) {
 	// Validate that at least one item is selected
 	bool hasSelection = false;
-	for (const auto &item : selectedItemsBool) {
+	for (const auto &item : selectedItems) {
 		if (item) {
 			hasSelection = true;
 			break;
@@ -1431,8 +1431,8 @@ void LauncherSimple::confirmRemoveGames(const Common::Array<bool> &selectedItems
 	
 	// Count selected items
 	int selectedCount = 0;
-	for (int i = 0; i < (int)selectedItemsBool.size(); ++i) {
-		if (selectedItemsBool[i]) {
+	for (int i = 0; i < (int)selectedItems.size(); ++i) {
+		if (selectedItems[i]) {
 			selectedCount++;
 		}
 	}
@@ -1445,8 +1445,8 @@ void LauncherSimple::confirmRemoveGames(const Common::Array<bool> &selectedItems
 
 	// Build message from bool array (max 10 items, then ellipsis)
 	int displayCount = 0;
-	for (int i = 0; i < (int)selectedItemsBool.size(); ++i) {
-		if (selectedItemsBool[i]) {
+	for (int i = 0; i < (int)selectedItems.size(); ++i) {
+		if (selectedItems[i]) {
 			// Get the game title from the list
 			confirmMsg += _list->getList()[i];
 			confirmMsg += Common::U32String("\n");
@@ -1464,7 +1464,7 @@ void LauncherSimple::confirmRemoveGames(const Common::Array<bool> &selectedItems
 	MessageDialog alert(confirmMsg, _("Yes"), _("No"));
 
 	if (alert.runModal() == GUI::kMessageOK) {
-		performGameRemoval(selectedItemsBool, false);
+		performGameRemoval(selectedItems, false);
 	}
 }
 
@@ -1482,11 +1482,11 @@ void LauncherSimple::updateSelectionAfterRemoval() {
 
 void LauncherSimple::updateButtons() {
 	int item = _list->getSelected();
-	const Common::Array<bool> &selectedItemsBool = _list->getSelectedItems();
+	const Common::Array<bool> &selectedItems = _list->getSelectedItems();
 	// Count selected items (early exit once we know there's multi-selection)
 	int selectedCount = 0;
-	for (int i = 0; i < (int)selectedItemsBool.size(); ++i) {
-		if (selectedItemsBool[i]) selectedCount++;
+	for (int i = 0; i < (int)selectedItems.size(); ++i) {
+		if (selectedItems[i]) selectedCount++;
 		if (selectedCount > 1) break;
 	}
 	bool hasMultiSelection = selectedCount > 1;


Commit: bd1b8bb2c9de1ffc455817c92a2f294526312ce4
    https://github.com/scummvm/scummvm/commit/bd1b8bb2c9de1ffc455817c92a2f294526312ce4
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-31T12:46:29+03:00

Commit Message:
GUI: Simplify method name - LauncherDialog::performGameRemoval to removeGames

Renames method for better clarity and consistency with the codebase's
naming conventions.

Changed paths:
    gui/launcher.cpp
    gui/launcher.h


diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 6d33d83ab35..ba02827ab08 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -1464,7 +1464,7 @@ void LauncherSimple::confirmRemoveGames(const Common::Array<bool> &selectedItems
 	MessageDialog alert(confirmMsg, _("Yes"), _("No"));
 
 	if (alert.runModal() == GUI::kMessageOK) {
-		performGameRemoval(selectedItems, false);
+		removeGames(selectedItems, false);
 	}
 }
 
@@ -1879,7 +1879,7 @@ void LauncherGrid::confirmRemoveGames(const Common::Array<bool> &selectedItems)
 
 	MessageDialog alert(message, Common::U32String(_("Yes")), Common::U32String(_("No")));
 	if (alert.runModal() == GUI::kMessageOK) {
-		performGameRemoval(selectedItems, true);
+		removeGames(selectedItems, true);
 	}
 }
 
@@ -1896,7 +1896,7 @@ void LauncherGrid::updateSelectionAfterRemoval() {
 
 #endif // !DISABLE_LAUNCHERDISPLAY_GRID
 
-void LauncherDialog::performGameRemoval(const Common::Array<bool> &selectedItems, bool isGrid) {
+void LauncherDialog::removeGames(const Common::Array<bool> &selectedItems, bool isGrid) {
 	if (selectedItems.empty())
 		return;
 
diff --git a/gui/launcher.h b/gui/launcher.h
index 9c3837c5242..592b5226ddf 100644
--- a/gui/launcher.h
+++ b/gui/launcher.h
@@ -196,7 +196,7 @@ protected:
 	 * Shared helper for removing games after confirmation.
 	 * Called by subclasses after building their own confirmation message.
 	 */
-	void performGameRemoval(const Common::Array<bool> &selectedItems, bool isGrid);
+	void removeGames(const Common::Array<bool> &selectedItems, bool isGrid);
 
 	/**
 	 * Handle game removal confirmation with selection validation.


Commit: 2b4173183613a801bab4338564845ad88fd09af1
    https://github.com/scummvm/scummvm/commit/2b4173183613a801bab4338564845ad88fd09af1
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-31T12:46:29+03:00

Commit Message:
GUI: Unify selection access with virtual getSelectedItems() method

Adds virtual method to LauncherDialog that returns selected items from
the appropriate widget, allowing code to work with selections polymorphically.

Changed paths:
    gui/launcher.cpp
    gui/launcher.h


diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index ba02827ab08..da4ea168e76 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -1029,6 +1029,7 @@ public:
 protected:
 	void confirmRemoveGames(const Common::Array<bool> &selectedItems) override;
 	void updateSelectionAfterRemoval() override;
+	const Common::Array<bool>& getSelectedItems() const override;
 	void updateListing(int selPos = -1) override;
 	int getItemPos(int item) override;
 	void groupEntries(const Common::Array<LauncherEntry> &metadata);
@@ -1110,6 +1111,10 @@ void LauncherSimple::selectTarget(const Common::String &target) {
 
 int LauncherSimple::getSelected() { return _list->getSelected(); }
 
+const Common::Array<bool>& LauncherSimple::getSelectedItems() const {
+	return _list->getSelectedItems();
+}
+
 void LauncherSimple::build() {
 	LauncherDialog::build();
 
@@ -1374,7 +1379,7 @@ void LauncherSimple::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 		break;
 	case kListItemRemovalRequestCmd: {
 		// Remove games if we have any selection
-		confirmRemoveGames(_list->getSelectedItems());
+		confirmRemoveGames(getSelectedItems());
 		break;
 	}
 	case kListSelectionChangedCmd:
@@ -1409,7 +1414,7 @@ void LauncherSimple::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 	}
 	case kRemoveGameCmd: {
 		// Remove games if we have any selection
-		confirmRemoveGames(_list->getSelectedItems());
+		confirmRemoveGames(getSelectedItems());
 		break;
 	}
 	default:
@@ -1471,7 +1476,7 @@ void LauncherSimple::confirmRemoveGames(const Common::Array<bool> &selectedItems
 void LauncherSimple::updateSelectionAfterRemoval() {
 	if (_list) {
 		_list->clearSelection();
-		const Common::Array<bool> &selectedItems = _list->getSelectedItems();
+		const Common::Array<bool> &selectedItems = getSelectedItems();
 		
 		// Get the real data index of the last selected item and adjust with bounds
 		int lastSelectedDataItem = MIN((int)selectedItems.size() - 1, _list->getSelected());
@@ -1482,7 +1487,7 @@ void LauncherSimple::updateSelectionAfterRemoval() {
 
 void LauncherSimple::updateButtons() {
 	int item = _list->getSelected();
-	const Common::Array<bool> &selectedItems = _list->getSelectedItems();
+	const Common::Array<bool> &selectedItems = getSelectedItems();
 	// Count selected items (early exit once we know there's multi-selection)
 	int selectedCount = 0;
 	for (int i = 0; i < (int)selectedItems.size(); ++i) {
@@ -1670,9 +1675,7 @@ void LauncherGrid::handleCommand(CommandSender *sender, uint32 cmd, uint32 data)
 		break;
 	case kRemoveGameCmd:
 		// Remove games if we have any selection
-		if (_grid) {
-			confirmRemoveGames(_grid->getSelectedItems());
-		}
+		confirmRemoveGames(getSelectedItems());
 		break;
 	case kItemClicked:
 		updateButtons();
@@ -1775,7 +1778,7 @@ void LauncherGrid::updateButtons() {
     LauncherDialog::updateButtons();
     // Enable remove button if at least one entry is selected
     if (_grid) {
-        const Common::Array<bool> &selectedItems = _grid->getSelectedItems();
+        const Common::Array<bool> &selectedItems = getSelectedItems();
         bool hasSelection = false;
         for (const auto &item : selectedItems) {
             if (item) {
@@ -1802,6 +1805,10 @@ void LauncherGrid::selectTarget(const Common::String &target) {
 
 int LauncherGrid::getSelected() { return _grid->getSelected(); }
 
+const Common::Array<bool>& LauncherGrid::getSelectedItems() const {
+	return _grid->getSelectedItems();
+}
+
 void LauncherGrid::build() {
 	LauncherDialog::build();
 
@@ -1886,7 +1893,7 @@ void LauncherGrid::confirmRemoveGames(const Common::Array<bool> &selectedItems)
 void LauncherGrid::updateSelectionAfterRemoval() {
 	if (_grid) {
 		_grid->clearSelection();
-		const Common::Array<bool> &selectedItems = _grid->getSelectedItems();
+		const Common::Array<bool> &selectedItems = getSelectedItems();
 		
 		// Select at the same index as before, or the last item if out of bounds
 		_grid->_lastSelectedEntryID = MIN((int)selectedItems.size() - 1, _grid->_lastSelectedEntryID);
diff --git a/gui/launcher.h b/gui/launcher.h
index 592b5226ddf..d85972b568e 100644
--- a/gui/launcher.h
+++ b/gui/launcher.h
@@ -211,6 +211,10 @@ protected:
 	 */
 	virtual void updateSelectionAfterRemoval() = 0;
 
+
+	// Get the selected items from the current view (list or grid).
+	virtual const Common::Array<bool>& getSelectedItems() const = 0;
+
 	/**
 	 * Handle "Edit game..." button.
 	 */
@@ -267,6 +271,7 @@ public:
 protected:
 	void confirmRemoveGames(const Common::Array<bool> &selectedItems) override;
 	void updateSelectionAfterRemoval() override;
+	const Common::Array<bool>& getSelectedItems() const override;
 	void updateListing(int selPos = -1) override;
 	int getItemPos(int item) override;
 	void groupEntries(const Common::Array<LauncherEntry> &metadata);


Commit: 2c23b8bb3728d51b68f8a23cdb3d672d23fa7272
    https://github.com/scummvm/scummvm/commit/2c23b8bb3728d51b68f8a23cdb3d672d23fa7272
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-31T12:46:29+03:00

Commit Message:
GUI: Add hasAnySelection() to consolidate selection checking

Replaces repetitive loops checking for any selected item with a single
helper method, reducing code duplication and improving maintainability.

Changed paths:
    gui/launcher.cpp
    gui/launcher.h


diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index da4ea168e76..37824c7016f 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -1370,6 +1370,15 @@ void LauncherSimple::handleKeyDown(Common::KeyState state) {
 	updateButtons();
 }
 
+bool LauncherDialog::hasAnySelection(const Common::Array<bool> &selectedItems) const {
+	for (const auto &item : selectedItems) {
+		if (item) {
+			return true;
+		}
+	}
+	return false;
+}
+
 void LauncherSimple::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
 
 	switch (cmd) {
@@ -1424,14 +1433,7 @@ void LauncherSimple::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 
 void LauncherSimple::confirmRemoveGames(const Common::Array<bool> &selectedItems) {
 	// Validate that at least one item is selected
-	bool hasSelection = false;
-	for (const auto &item : selectedItems) {
-		if (item) {
-			hasSelection = true;
-			break;
-		}
-	}
-	if (!hasSelection)
+	if (!hasAnySelection(selectedItems))
 		return;
 	
 	// Count selected items
@@ -1779,14 +1781,7 @@ void LauncherGrid::updateButtons() {
     // Enable remove button if at least one entry is selected
     if (_grid) {
         const Common::Array<bool> &selectedItems = getSelectedItems();
-        bool hasSelection = false;
-        for (const auto &item : selectedItems) {
-            if (item) {
-                hasSelection = true;
-                break;
-            }
-        }
-        _removeButton->setEnabled(hasSelection);
+        _removeButton->setEnabled(hasAnySelection(selectedItems));
     }
 }
 
@@ -1839,14 +1834,7 @@ void LauncherGrid::confirmRemoveGames(const Common::Array<bool> &selectedItems)
 		return;
 	
 	// Validate that at least one item is selected
-	bool hasSelection = false;
-	for (const auto &item : selectedItems) {
-		if (item) {
-			hasSelection = true;
-			break;
-		}
-	}
-	if (!hasSelection)
+	if (!hasAnySelection(selectedItems))
 		return;
 	
 	// Count selected items
@@ -1908,14 +1896,7 @@ void LauncherDialog::removeGames(const Common::Array<bool> &selectedItems, bool
 		return;
 
 	// Check if any items are selected
-	bool hasSelection = false;
-	for (const auto &item : selectedItems) {
-		if (item) {
-			hasSelection = true;
-			break;
-		}
-	}
-	if (!hasSelection)
+	if (!hasAnySelection(selectedItems))
 		return;
 
 	int selPos = -1;
diff --git a/gui/launcher.h b/gui/launcher.h
index d85972b568e..d7c25a4fe3d 100644
--- a/gui/launcher.h
+++ b/gui/launcher.h
@@ -211,6 +211,10 @@ protected:
 	 */
 	virtual void updateSelectionAfterRemoval() = 0;
 
+	/**
+	 * Check if any items are selected in the given array.
+	 */
+	bool hasAnySelection(const Common::Array<bool> &selectedItems) const;
 
 	// Get the selected items from the current view (list or grid).
 	virtual const Common::Array<bool>& getSelectedItems() const = 0;


Commit: 8e99bd35c0d9bd59d27e5c8c0ca0683fbf0caffb
    https://github.com/scummvm/scummvm/commit/8e99bd35c0d9bd59d27e5c8c0ca0683fbf0caffb
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-31T12:46:29+03:00

Commit Message:
GUI: Improve translation compatibility in game removal dialog

Replace dynamic plural formatting with static (s) notation to simplify
translation. Minor indentation fix.

Changed paths:
    gui/launcher.cpp
    gui/widgets/list.cpp


diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 37824c7016f..57ab4d3bd14 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -1446,9 +1446,8 @@ void LauncherSimple::confirmRemoveGames(const Common::Array<bool> &selectedItems
 
 	// Build confirmation message with count
 	Common::U32String confirmMsg = Common::U32String::format(
-		_("Do you really want to remove the following %d game configuration%s?\n\n"),
-		selectedCount,
-		selectedCount == 1 ? "" : "s");
+		_("Do you really want to remove the following %d game configuration(s)?\n\n"),
+		selectedCount);
 
 	// Build message from bool array (max 10 items, then ellipsis)
 	int displayCount = 0;
@@ -1847,9 +1846,8 @@ void LauncherGrid::confirmRemoveGames(const Common::Array<bool> &selectedItems)
 
 	// Build the confirmation message with count
 	Common::U32String message = Common::U32String::format(
-		_("Do you really want to remove the following %d game configuration%s?\n\n"),
-		selectedCount,
-		selectedCount == 1 ? "" : "s");
+		_("Do you really want to remove the following %d game configuration(s)?\n\n"),
+		selectedCount);
 
 	// Add games to message (max 10 items, then ellipsis)
 	int displayCount = 0;
diff --git a/gui/widgets/list.cpp b/gui/widgets/list.cpp
index 5d738108800..30c7490bf4e 100644
--- a/gui/widgets/list.cpp
+++ b/gui/widgets/list.cpp
@@ -506,7 +506,7 @@ bool ListWidget::handleKeyDown(Common::KeyState state) {
 				} else
 					sendCommand(kListItemActivatedCmd, _selectedItem);
 			}
-				scrollToCurrent();
+			scrollToCurrent();
 			break;
 		}
 


Commit: e7ba7c338f0c67fb32738242ff70083587edba67
    https://github.com/scummvm/scummvm/commit/e7ba7c338f0c67fb32738242ff70083587edba67
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-31T12:46:29+03:00

Commit Message:
GUI: Remove redundant bounds checks in grid selection logic

Simplify isSelected check by removing redundant boundary validation.

Changed paths:
    gui/widgets/grid.cpp


diff --git a/gui/widgets/grid.cpp b/gui/widgets/grid.cpp
index fefd1d032b0..c86547d20c1 100644
--- a/gui/widgets/grid.cpp
+++ b/gui/widgets/grid.cpp
@@ -89,8 +89,7 @@ void GridItemWidget::drawWidget() {
 	const int kMarginY = _grid->_gridYSpacing / 3;
 
 	// Check if this entry is in the selected entries list
-	bool isSelected = (_activeEntry->entryID >= 0 && _activeEntry->entryID < (int)_grid->_selectedItems.size()) 
-					  && _grid->_selectedItems[_activeEntry->entryID];
+	bool isSelected = _grid->_selectedItems[_activeEntry->entryID];
 
 	// Draw selection highlight if this entry is selected or hovered
 	if (isSelected || _isHighlighted) {


Commit: 5858c62da42135f3abb9a2cf0302a7ce565f4989
    https://github.com/scummvm/scummvm/commit/5858c62da42135f3abb9a2cf0302a7ce565f4989
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-31T12:46:29+03:00

Commit Message:
GUI: Unify game removal confirmation dialog implementation

Move confirmRemoveGames() to LauncherDialog base class to eliminate code
duplication between list and grid views. Populate _domainTitles for both
views to ensure proper game titles are displayed in removal dialogs.

Changed paths:
    gui/launcher.cpp
    gui/launcher.h


diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 57ab4d3bd14..53191d434d4 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -1027,7 +1027,6 @@ public:
 	LauncherDisplayType getType() const override { return kLauncherDisplayGrid; }
 
 protected:
-	void confirmRemoveGames(const Common::Array<bool> &selectedItems) override;
 	void updateSelectionAfterRemoval() override;
 	const Common::Array<bool>& getSelectedItems() const override;
 	void updateListing(int selPos = -1) override;
@@ -1041,7 +1040,6 @@ private:
 	GridWidget       *_grid;
 	SliderWidget     *_gridItemSizeSlider;
 	StaticTextWidget *_gridItemSizeLabel;
-	Common::StringArray _domainTitles; // Store game titles for each domain
 };
 #endif // !DISABLE_LAUNCHERDISPLAY_GRID
 
@@ -1157,6 +1155,7 @@ void LauncherSimple::updateListing(int selPos) {
 
 	// Retrieve a list of all games defined in the config file
 	_domains.clear();
+	_domainTitles.clear();
 	const Common::ConfigManager::DomainMap &domains = ConfMan.getGameDomains();
 	const bool scanEntries = (numEntries == -1) || ((int)domains.size() <= numEntries);
 
@@ -1219,6 +1218,7 @@ void LauncherSimple::updateListing(int selPos) {
 
 		l.push_back(gameDesc);
 		_domains.push_back(curDomain.key);
+		_domainTitles.push_back(curDomain.description);
 	}
 
 	const int oldSel = _list->getSelected();
@@ -1431,48 +1431,6 @@ void LauncherSimple::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 	}
 }
 
-void LauncherSimple::confirmRemoveGames(const Common::Array<bool> &selectedItems) {
-	// Validate that at least one item is selected
-	if (!hasAnySelection(selectedItems))
-		return;
-	
-	// Count selected items
-	int selectedCount = 0;
-	for (int i = 0; i < (int)selectedItems.size(); ++i) {
-		if (selectedItems[i]) {
-			selectedCount++;
-		}
-	}
-
-	// Build confirmation message with count
-	Common::U32String confirmMsg = Common::U32String::format(
-		_("Do you really want to remove the following %d game configuration(s)?\n\n"),
-		selectedCount);
-
-	// Build message from bool array (max 10 items, then ellipsis)
-	int displayCount = 0;
-	for (int i = 0; i < (int)selectedItems.size(); ++i) {
-		if (selectedItems[i]) {
-			// Get the game title from the list
-			confirmMsg += _list->getList()[i];
-			confirmMsg += Common::U32String("\n");
-			displayCount++;
-			if (displayCount >= 10)
-				break;
-		}
-	}
-
-	// Add ellipsis if more than 10 items are selected
-	if (selectedCount > 10) {
-		confirmMsg += Common::U32String("...\n");
-	}
-
-	MessageDialog alert(confirmMsg, _("Yes"), _("No"));
-
-	if (alert.runModal() == GUI::kMessageOK) {
-		removeGames(selectedItems, false);
-	}
-}
 
 void LauncherSimple::updateSelectionAfterRemoval() {
 	if (_list) {
@@ -1828,10 +1786,20 @@ void LauncherGrid::build() {
 	updateButtons();
 }
 
-void LauncherGrid::confirmRemoveGames(const Common::Array<bool> &selectedItems) {
-	if (!_grid)
-		return;
-	
+
+void LauncherGrid::updateSelectionAfterRemoval() {
+	if (_grid) {
+		_grid->clearSelection();
+		
+		// Select at the same index as before, or the last item if out of bounds
+		_grid->_lastSelectedEntryID = MIN((int)getSelectedItems().size() - 1, _grid->_lastSelectedEntryID);
+		_grid->markSelectedItem(_grid->_lastSelectedEntryID, true);
+	}
+}
+
+#endif // !DISABLE_LAUNCHERDISPLAY_GRID
+
+void LauncherDialog::confirmRemoveGames(const Common::Array<bool> &selectedItems) {
 	// Validate that at least one item is selected
 	if (!hasAnySelection(selectedItems))
 		return;
@@ -1872,23 +1840,10 @@ void LauncherGrid::confirmRemoveGames(const Common::Array<bool> &selectedItems)
 
 	MessageDialog alert(message, Common::U32String(_("Yes")), Common::U32String(_("No")));
 	if (alert.runModal() == GUI::kMessageOK) {
-		removeGames(selectedItems, true);
-	}
-}
-
-void LauncherGrid::updateSelectionAfterRemoval() {
-	if (_grid) {
-		_grid->clearSelection();
-		const Common::Array<bool> &selectedItems = getSelectedItems();
-		
-		// Select at the same index as before, or the last item if out of bounds
-		_grid->_lastSelectedEntryID = MIN((int)selectedItems.size() - 1, _grid->_lastSelectedEntryID);
-		_grid->markSelectedItem(_grid->_lastSelectedEntryID, true);
+		removeGames(selectedItems, getType() == kLauncherDisplayGrid);
 	}
 }
 
-#endif // !DISABLE_LAUNCHERDISPLAY_GRID
-
 void LauncherDialog::removeGames(const Common::Array<bool> &selectedItems, bool isGrid) {
 	if (selectedItems.empty())
 		return;
diff --git a/gui/launcher.h b/gui/launcher.h
index d7c25a4fe3d..3ab87bc7556 100644
--- a/gui/launcher.h
+++ b/gui/launcher.h
@@ -145,6 +145,7 @@ protected:
 	Common::String	_title;
 	Common::String	_search;
 	MetadataParser	_metadataParser;
+	Common::StringArray _domainTitles;	// Store game titles for each domain
 
 #ifndef DISABLE_LAUNCHERDISPLAY_GRID
 	ButtonWidget		*_listButton;
@@ -200,10 +201,10 @@ protected:
 
 	/**
 	 * Handle game removal confirmation with selection validation.
-	 * Checks if at least one item is selected, then calls the subclass
-	 * implementation to show the removal confirmation dialog.
+	 * Checks if at least one item is selected, then shows the removal
+	 * confirmation dialog with a list of games to be removed.
 	 */
-	virtual void confirmRemoveGames(const Common::Array<bool> &selectedItems) = 0;
+	void confirmRemoveGames(const Common::Array<bool> &selectedItems);
 
 	/**
 	 * Update selection after game removal.
@@ -273,7 +274,6 @@ public:
 	LauncherDisplayType getType() const override { return kLauncherDisplayList; }
 
 protected:
-	void confirmRemoveGames(const Common::Array<bool> &selectedItems) override;
 	void updateSelectionAfterRemoval() override;
 	const Common::Array<bool>& getSelectedItems() const override;
 	void updateListing(int selPos = -1) override;


Commit: dcb0877f4d35d422f23b9333e66fd7feebfe04c1
    https://github.com/scummvm/scummvm/commit/dcb0877f4d35d422f23b9333e66fd7feebfe04c1
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-31T12:46:29+03:00

Commit Message:
GUI: Use SWAP macro instead of manual swap in GridWidget

Changed paths:
    gui/widgets/grid.cpp


diff --git a/gui/widgets/grid.cpp b/gui/widgets/grid.cpp
index c86547d20c1..3334e425dc7 100644
--- a/gui/widgets/grid.cpp
+++ b/gui/widgets/grid.cpp
@@ -991,11 +991,8 @@ int GridWidget::getVisualPos(int entryID) const {
 void GridWidget::selectVisualRange(int startPos, int endPos) {
 	// Selects all non-header items in visual range [startPos, endPos]
 	// visPos counts only non-header items
-	if (startPos > endPos) {
-		int temp = startPos;
-		startPos = endPos;
-		endPos = temp;
-	}
+	if (startPos > endPos)
+		SWAP(startPos, endPos);
 
 	int visPos = 0;
 	for (uint i = 0; i < _sortedEntryList.size(); ++i) {




More information about the Scummvm-git-logs mailing list