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

sev- noreply at scummvm.org
Tue Jan 6 22:01:54 UTC 2026


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

Summary:
cbf8a88b31 GUI: Add multi-selection, batch removal, and improved button states in launcher
9c53832087 GUI: Add multi-selection and range select to launcher grid
d32433346e GUI: Limit multi-selection to launcher dialog only
da8592a469 GUI: Disable Enter key activation when multi-selection is enabled
8e46e1d4d8 GUI: Clarify remove confirmation message
06fc2359be GUI: Unify game and add-on removal logic
fe2b8890fb GUI: Confirm multi-selection game removal
ea316313b5 GUI: Allow Enter activation with single selection


Commit: cbf8a88b31b8f3b355b0ccfbc3ba9ce510a928b9
    https://github.com/scummvm/scummvm/commit/cbf8a88b31b8f3b355b0ccfbc3ba9ce510a928b9
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-07T01:01:47+03:00

Commit Message:
GUI: Add multi-selection, batch removal, and improved button states in launcher

• Enable multi-selection in the launcher list widget (mouse, Ctrl+Click, Shift+Click).

• Update button states: disable Start, Load, and Game Options when multiple entries are selected.

• Add batch removal confirmation dialog listing selected entries.

• Refactor selection logic and highlighting in both filtered and grouped views.

• Fix group header fold indicator rendering for better UI consistency.

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


diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 1882f10c752..438c2c73cf7 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -1386,28 +1386,94 @@ void LauncherSimple::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 		}
 		break;
 	}
+	case kRemoveGameCmd: {
+		// Handle multi-selection removal
+		const Common::Array<int> &selectedItems = _list->getSelectedItems();
+		if (selectedItems.size() > 1) {
+			// Multi-selection removal: show confirmation dialog with list of games
+			removeMultipleGames(selectedItems);
+		} else {
+			// Single selection removal
+			LauncherDialog::handleCommand(sender, cmd, data);
+		}
+		break;
+	}
 	default:
 		LauncherDialog::handleCommand(sender, cmd, data);
 	}
 }
 
+void LauncherSimple::removeMultipleGames(const Common::Array<int> &selectedItems) {
+	// Build confirmation message with list of games to remove
+	Common::U32String confirmMsg = _("Are you sure you want to remove the following games?\n\n");
+
+	for (const int &idx : selectedItems) {
+		if (idx >= 0 && idx < (int)_domains.size()) {
+			// Get the game title from the list
+			confirmMsg += _list->getList()[idx];
+			confirmMsg += Common::U32String("\n");
+		}
+	}
+
+	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;
+		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);
+				}
+
+				// Remove the game domain
+				ConfMan.removeGameDomain(_domains[idx]);
+
+				// Remove all the add-ons for this game
+				const Common::ConfigManager::DomainMap &domains = ConfMan.getGameDomains();
+				for (const auto &domain : domains) {
+					if (domain._value.getValOrDefault("parent") == _domains[idx]) {
+						ConfMan.removeGameDomain(domain._key);
+					}
+				}
+			}
+		}
+
+		// Write config to disk
+		ConfMan.flushToDisk();
+
+		// Clear the selection and update the listing
+		_list->clearSelection();
+		updateListing(selPos);
+		updateButtons();
+		g_gui.scheduleTopDialogRedraw();
+	}
+}
+
 void LauncherSimple::updateButtons() {
 	int item = _list->getSelected();
+	const Common::Array<int> &selectedItems = _list->getSelectedItems();
+	bool hasMultiSelection = selectedItems.size() > 1;
+
 	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);
+	bool enable = (item >= 0 && !isAddOn && !hasMultiSelection);
 
 	_startButton->setEnabled(enable);
 	_editButton->setEnabled(enable);
-	_removeButton->setEnabled(enable);
+	_removeButton->setEnabled(item >= 0 || hasMultiSelection);
 
 	bool en = enable;
 
-	if (item >= 0  && !isAddOn)
+	if (item >= 0 && !isAddOn && !hasMultiSelection)
 		en = !(Common::checkGameGUIOption(GUIO_NOLAUNCHLOAD, ConfMan.get("guioptions", _domains[item])));
 
 	_loadButton->setEnabled(en);
diff --git a/gui/launcher.h b/gui/launcher.h
index cc98bb92d69..ec3511755ab 100644
--- a/gui/launcher.h
+++ b/gui/launcher.h
@@ -248,6 +248,7 @@ 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/groupedlist.cpp b/gui/widgets/groupedlist.cpp
index 4e62f0528e7..431ab8f34ae 100644
--- a/gui/widgets/groupedlist.cpp
+++ b/gui/widgets/groupedlist.cpp
@@ -231,30 +231,67 @@ void GroupedListWidget::handleMouseDown(int x, int y, int button, int clickCount
 
 	// First check whether the selection changed
 	int newSelectedItem = findItem(x, y);
-	if (_selectedItem != newSelectedItem && newSelectedItem != -1) {
-		if (_listIndex[newSelectedItem] > -1) {
-			if (_editMode)
-				abortEditMode();
-			_selectedItem = newSelectedItem;
+	if (newSelectedItem == -1)
+		return;
+
+	if (isGroupHeader(_listIndex[newSelectedItem])) {
+		int groupID = indexToGroupID(_listIndex[newSelectedItem]);
+		int oldSelection = getSelected();
+		_selectedItem = -1;
+		toggleGroup(groupID);
+		if (oldSelection != -1) {
+			_selectedItem = findDataIndex(oldSelection);
 			sendCommand(kListSelectionChangedCmd, _selectedItem);
-		} else if (isGroupHeader(_listIndex[newSelectedItem])) {
-			int groupID = indexToGroupID(_listIndex[newSelectedItem]);
-			int oldSelection = getSelected();
-			_selectedItem = -1;
-			toggleGroup(groupID);
-			// Try to preserve the selection, but without scrolling
-			if (oldSelection != -1) {
-				_selectedItem = findDataIndex(oldSelection);
-				sendCommand(kListSelectionChangedCmd, _selectedItem);
-			}
 		}
+		markAsDirty();
+		return;
 	}
 
 	// TODO: Determine where inside the string the user clicked and place the
 	// caret accordingly.
 	// See _editScrollOffset and EditTextWidget::handleMouseDown.
-	markAsDirty();
+	if (_editMode)
+		abortEditMode();
+
+	int dataIndex = _listIndex[newSelectedItem];
+	if (dataIndex < 0)
+		return;
+
+	// Get modifier keys
+	int modifiers = g_system->getEventManager()->getModifierState();
+	bool ctrlClick = (modifiers & Common::KBD_CTRL) != 0;
+	bool shiftClick = (modifiers & Common::KBD_SHIFT) != 0;
+
+	if (shiftClick && _lastSelectionStartItem != -1) {
+		// Shift+Click: Select range in terms of underlying data indices
+		int startDataIndex = _lastSelectionStartItem;
+		selectItemRange(startDataIndex, dataIndex);
+		_selectedItem = newSelectedItem;
+		sendCommand(kListSelectionChangedCmd, _selectedItem);
+	} else if (ctrlClick) {
+		// Ctrl+Click: toggle selection for the underlying data index
+		if (isItemSelected(dataIndex)) {
+			removeSelectedItem(dataIndex);
+		} else {
+			addSelectedItem(dataIndex);
+		}
+		_selectedItem = newSelectedItem;
+		_lastSelectionStartItem = dataIndex;
+		sendCommand(kListSelectionChangedCmd, _selectedItem);
+	} else {
+		// Regular click: clear selection and select only this underlying item
+		clearSelection();
+		_selectedItem = newSelectedItem;
+		addSelectedItem(dataIndex);
+		_lastSelectionStartItem = dataIndex;
+		sendCommand(kListSelectionChangedCmd, _selectedItem);
+	}
 
+	// Notify clients if an item was clicked
+	if (newSelectedItem >= 0)
+		sendCommand(kListItemSingleClickedCmd, _selectedItem);
+
+	markAsDirty();
 }
 
 void GroupedListWidget::handleMouseUp(int x, int y, int button, int clickCount) {
@@ -350,16 +387,23 @@ void GroupedListWidget::drawWidget() {
 		ThemeEngine::FontStyle bold = ThemeEngine::kFontStyleBold;
 #endif
 
-		// Draw the selected item inverted, on a highlighted background.
-		if (_selectedItem == pos)
-			inverted = _inversion;
+		// For grouped lists, only real items (non-headers) may be highlighted.
+		int mapped = _listIndex[pos];
+		bool isRealItem = (mapped >= 0);
+		if (isRealItem) {
+			if (_selectedItem == pos || isItemSelected(mapped))
+				inverted = _inversion;
+		}
+
+		ThemeEngine::WidgetStateInfo itemState = getItemState(pos);
 
 		Common::Rect r(getEditRect());
 		int pad = _leftPadding;
 		int rtlPad = (_x + r.left + _leftPadding) - (_x + _hlLeftPadding);
 
-		if (isGroupHeader(_listIndex[pos])) {
-			int groupID = indexToGroupID(_listIndex[pos]);
+		// Group header / grouped list indentation logic
+		if (isGroupHeader(mapped)) {
+    		int groupID = indexToGroupID(mapped);
 #if 0
 			bold = ThemeEngine::kFontStyleBold;
 #endif
@@ -376,7 +420,7 @@ void GroupedListWidget::drawWidget() {
 		if (_numberingMode != kListNumberingOff && g_gui.useRTL() == false) {
 			buffer = Common::String::format("%2d. ", (pos + _numberingMode));
 			g_gui.theme()->drawText(Common::Rect(_x + _hlLeftPadding, y, _x + r.left + _leftPadding, y + lineHeight),
-									buffer, _state, _drawAlign, inverted, _leftPadding, true);
+									buffer, itemState, _drawAlign, inverted, _leftPadding, true);
 			pad = 0;
 		}
 
@@ -402,9 +446,8 @@ void GroupedListWidget::drawWidget() {
 			buffer = _list[pos];
 		}
 
-		drawFormattedText(r1, buffer, _state, _drawAlign, inverted, pad, true, color);
+		drawFormattedText(r1, buffer, itemState, _drawAlign, inverted, pad, true, color);
 
-		// If in numbering mode & using RTL layout in GUI, we print a number suffix after drawing the text
 		if (_numberingMode != kListNumberingOff && g_gui.useRTL()) {
 			buffer = Common::String::format(" .%2d", (pos + _numberingMode));
 
@@ -413,9 +456,11 @@ void GroupedListWidget::drawWidget() {
 			r2.left = r1.right;
 			r2.right = r1.right + rtlPad;
 
-			g_gui.theme()->drawText(r2, buffer, _state, _drawAlign, inverted, _leftPadding, true);
+			g_gui.theme()->drawText(r2, buffer, itemState, _drawAlign, inverted, _leftPadding, true);
 		}
 	}
+	if (_editMode)
+		EditableWidget::drawWidget();
 }
 
 void GroupedListWidget::setFilter(const Common::U32String &filter, bool redraw) {
@@ -488,5 +533,8 @@ void GroupedListWidget::setFilter(const Common::U32String &filter, bool redraw)
 		g_gui.scheduleTopDialogRedraw();
 	}
 }
+ThemeEngine::WidgetStateInfo GroupedListWidget::getItemState(int item) const {
+	return _state;
+}
 
 } // End of namespace GUI
diff --git a/gui/widgets/groupedlist.h b/gui/widgets/groupedlist.h
index 1edabdba7d8..99ed877529b 100644
--- a/gui/widgets/groupedlist.h
+++ b/gui/widgets/groupedlist.h
@@ -76,6 +76,7 @@ protected:
 	void sortGroups();
 	void toggleGroup(int groupID);
 	void drawWidget() override;
+	ThemeEngine::WidgetStateInfo getItemState(int item) const override;
 	int findDataIndex(int) const;
 };
 
diff --git a/gui/widgets/list.cpp b/gui/widgets/list.cpp
index 246b64af5f9..3e1789ab9c4 100644
--- a/gui/widgets/list.cpp
+++ b/gui/widgets/list.cpp
@@ -51,6 +51,7 @@ ListWidget::ListWidget(Dialog *boss, const Common::String &name, const Common::U
 	_numberingMode = kListNumberingOne;
 	_currentPos = 0;
 	_selectedItem = -1;
+	_lastSelectionStartItem = -1;
 	_currentKeyDown = 0;
 
 	_quickSelectTime = 0;
@@ -94,6 +95,7 @@ ListWidget::ListWidget(Dialog *boss, int x, int y, int w, int h, bool scale, con
 	_numberingMode = kListNumberingOne;
 	_currentPos = 0;
 	_selectedItem = -1;
+	_lastSelectionStartItem = -1;
 	_currentKeyDown = 0;
 
 	_quickSelectTime = 0;
@@ -194,6 +196,80 @@ 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()) {
+		actualItem = _listIndex[item];
+	}
+
+	for (const int &selected : _selectedItems) {
+		if (selected == actualItem)
+			return true;
+	}
+	return false;
+}
+
+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()) {
+		actualItem = _listIndex[item];
+	}
+
+	// Avoid duplicates
+	if (isItemSelected(actualItem))
+		return;
+
+	// Insert in ascending order to keep selection sorted
+	bool inserted = false;
+	for (int i = 0; i < _selectedItems.size(); ++i) {
+		if (_selectedItems[i] > actualItem) {
+			_selectedItems.insert_at(i, actualItem);
+			inserted = true;
+			break;
+		}
+	}
+	if (!inserted)
+		_selectedItems.push_back(actualItem);
+
+	markAsDirty();
+}
+
+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()) {
+		actualItem = _listIndex[item];
+	}
+
+	for (int i = 0; i < _selectedItems.size(); ++i) {
+		if (_selectedItems[i] == actualItem) {
+			_selectedItems.remove_at(i);
+			break;
+		}
+	}
+	markAsDirty();
+}
+
+void ListWidget::clearSelection() {
+	_selectedItems.clear();
+	_lastSelectionStartItem = -1;
+	markAsDirty();
+}
+
+void ListWidget::selectItemRange(int from, int to) {
+	if (from > to)
+		SWAP(from, to);
+
+	clearSelection();
+
+	for (int i = from; i <= to; ++i) {
+		addSelectedItem(i);
+	}
+	markAsDirty();
+}
+
 void ListWidget::setList(const Common::U32StringArray &list) {
 	if (_editMode && _caretVisible)
 		drawCaret(true);
@@ -210,6 +286,8 @@ void ListWidget::setList(const Common::U32StringArray &list) {
 	if (_currentPos < 0)
 		_currentPos = 0;
 	_selectedItem = -1;
+	_selectedItems.clear(); // Clear multi-selection
+	_lastSelectionStartItem = -1;
 	_editMode = false;
 	g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
 	scrollBarRecalc();
@@ -260,10 +338,43 @@ void ListWidget::handleMouseDown(int x, int y, int button, int clickCount) {
 	// First check whether the selection changed
 	int newSelectedItem = findItem(x, y);
 
-	if (_selectedItem != newSelectedItem && newSelectedItem != -1) {
-		if (_editMode)
-			abortEditMode();
+	if (newSelectedItem == -1)
+		return;
+
+	if (_editMode)
+		abortEditMode();
+
+	// Get modifier keys
+	int modifiers = g_system->getEventManager()->getModifierState();
+	bool ctrlClick = (modifiers & Common::KBD_CTRL) != 0;
+	bool shiftClick = (modifiers & Common::KBD_SHIFT) != 0;
+
+	if (shiftClick && _lastSelectionStartItem != -1) {
+		// Shift+Click: Select range from last selection start to current item
+		selectItemRange(_lastSelectionStartItem, newSelectedItem);
+		_selectedItem = newSelectedItem;
+		sendCommand(kListSelectionChangedCmd, _selectedItem);
+	} else if (ctrlClick) {
+		// Ctrl+Click: Add/remove from selection
+		if (isItemSelected(newSelectedItem)) {
+			removeSelectedItem(newSelectedItem);
+			// If the primary selection is the same item, clear it to avoid highlight
+			sendCommand(kListSelectionChangedCmd, newSelectedItem);
+			markAsDirty();
+			if (_selectedItem == newSelectedItem)
+				_selectedItem = -1;
+		} else {
+			addSelectedItem(newSelectedItem);
+			_selectedItem = newSelectedItem;
+			_lastSelectionStartItem = newSelectedItem;
+		}
+		sendCommand(kListSelectionChangedCmd, _selectedItem);
+	} else {
+		// Regular click: Clear previous selection and select only this item
+		clearSelection();
 		_selectedItem = newSelectedItem;
+		addSelectedItem(newSelectedItem);
+		_lastSelectionStartItem = newSelectedItem;
 		sendCommand(kListSelectionChangedCmd, _selectedItem);
 	}
 
@@ -560,7 +671,7 @@ void ListWidget::drawWidget() {
 		ThemeEngine::TextInversionState inverted = ThemeEngine::kTextInversionNone;
 
 		// Draw the selected item inverted, on a highlighted background.
-		if (_selectedItem == pos)
+		if (_selectedItem == pos || isItemSelected(pos))
 			inverted = _inversion;
 
 		// Get state for drawing the item text
diff --git a/gui/widgets/list.h b/gui/widgets/list.h
index f9b247e770e..c81e26cfe2f 100644
--- a/gui/widgets/list.h
+++ b/gui/widgets/list.h
@@ -72,6 +72,8 @@ protected:
 	int				_currentPos;
 	int				_entriesPerPage;
 	int				_selectedItem;
+	Common::Array<int> _selectedItems;    /// Multiple selected items
+	int _lastSelectionStartItem;          /// Used for Shift+Click range selection
 	ScrollBarWidget	*_scrollBar;
 	int				_currentKeyDown;
 
@@ -117,6 +119,13 @@ public:
 
 	const Common::U32String getSelectedString() const	{ return stripGUIformatting(_list[_selectedItem]); }
 
+	/// Multi-selection support
+	const Common::Array<int> &getSelectedItems() const { return _selectedItems; }
+	bool isItemSelected(int item) const;
+	void addSelectedItem(int item);
+	void removeSelectedItem(int item);
+	void clearSelection();
+	void selectItemRange(int from, int to);
 	void setNumberingMode(NumberingMode numberingMode)	{ _numberingMode = numberingMode; }
 
 	void scrollTo(int item);


Commit: 9c538320875f8fc64c07b4e9fae13eb8c7ec8caf
    https://github.com/scummvm/scummvm/commit/9c538320875f8fc64c07b4e9fae13eb8c7ec8caf
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-07T01:01:47+03:00

Commit Message:
GUI: Add multi-selection and range select to launcher grid

Implement multi-selection in the launcher grid:
• Ctrl + Click toggles selection of individual entries
• Shift + Click selects a contiguous range from the last to the current selection
• Selected entries are consistently stored in sorted order
• Removal confirmation dialog now shows user-friendly game names
• Improved modifier key handling for reliable Ctrl and Shift detection

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


diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 438c2c73cf7..d600630ba39 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -454,10 +454,6 @@ void LauncherDialog::removeGame(int item) {
 		// This will be used to select the next item.
 		// If grouping method is None then updateListing() will
 		// ignore selPos and use the current selection instead.
-		int selPos = -1;
-		if (_groupBy != kGroupByNone) {
-			selPos = getItemPos(item);
-		}
 
 		// Remove the currently selected game from the list
 		assert(item >= 0);
@@ -475,7 +471,7 @@ void LauncherDialog::removeGame(int item) {
 		ConfMan.flushToDisk();
 
 		// Update the ListWidget/GridWidget and force a redraw
-		updateListing(selPos);
+		updateListing();
 		g_gui.scheduleTopDialogRedraw();
 	}
 }
@@ -937,6 +933,8 @@ void LauncherDialog::reflowLayout() {
 }
 
 #ifndef DISABLE_LAUNCHERDISPLAY_GRID
+void LauncherDialog::updateButtons() {
+}
 void LauncherDialog::addLayoutChooserButtons() {
 	if (_listButton) {
 		removeWidget(_listButton);
@@ -1011,6 +1009,8 @@ public:
 
 	LauncherDisplayType getType() const override { return kLauncherDisplayGrid; }
 
+public:
+	void removeSelectedGames();
 protected:
 	void updateListing(int selPos = -1) override;
 	int getItemPos(int item) override;
@@ -1023,6 +1023,7 @@ private:
 	GridWidget       *_grid;
 	SliderWidget     *_gridItemSizeSlider;
 	StaticTextWidget *_gridItemSizeLabel;
+	Common::StringArray _domainTitles; // Store game titles for each domain
 };
 #endif // !DISABLE_LAUNCHERDISPLAY_GRID
 
@@ -1631,6 +1632,14 @@ void LauncherGrid::handleCommand(CommandSender *sender, uint32 cmd, uint32 data)
 	case kLoadButtonCmd:
 		LauncherDialog::handleCommand(sender, kLoadGameCmd, 0);
 		break;
+	case kRemoveGameCmd:
+		// Handle multi-selection removal
+		if (_grid && _grid->getSelectedEntries().size() > 1) {
+			removeSelectedGames();
+		} else {
+			LauncherDialog::handleCommand(sender, cmd, data);
+		}
+		break;
 	case kItemClicked:
 		updateButtons();
 		break;
@@ -1680,6 +1689,7 @@ void LauncherGrid::handleCommand(CommandSender *sender, uint32 cmd, uint32 data)
 void LauncherGrid::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();
 
 	// Turn it into a sorted list of entries
@@ -1703,6 +1713,7 @@ void LauncherGrid::updateListing(int selPos) {
 		valid_path = (!curDomain.domain->tryGetVal("path", path) || !Common::FSNode(Common::Path::fromConfig(path)).isDirectory()) ? false : true;
 		gridList.push_back(GridItemInfo(k++, engineid, gameid, curDomain.description, curDomain.title, extra, Common::parseLanguage(language), Common::parsePlatform(platform), valid_path));
 		_domains.push_back(curDomain.key);
+		_domainTitles.push_back(curDomain.description); // Store the game description (user's name for it)
 	}
 
 	const int oldSel = _grid->getSelected();
@@ -1727,9 +1738,10 @@ int LauncherGrid::getItemPos(int item) {
 }
 
 void LauncherGrid::updateButtons() {
-	bool enable = (_grid->getSelected() >= 0);
-
-	_removeButton->setEnabled(enable);
+    LauncherDialog::updateButtons();
+    // Enable remove button if at least one entry is selected
+    bool hasSelection = _grid && !_grid->getSelectedEntries().empty();
+    _removeButton->setEnabled(hasSelection);
 }
 
 void LauncherGrid::selectTarget(const Common::String &target) {
@@ -1772,4 +1784,35 @@ void LauncherGrid::build() {
 }
 #endif // !DISABLE_LAUNCHERDISPLAY_GRID
 
+void LauncherGrid::removeSelectedGames() {
+	if (!_grid)
+		return;
+	const Common::Array<int> &selectedEntries = _grid->getSelectedEntries();
+	if (selectedEntries.empty())
+		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) {
+		for (const Common::String &domain : domainsToRemove) {
+			ConfMan.removeGameDomain(domain);
+		}
+		ConfMan.flushToDisk();
+		_grid->_selectedEntries.clear();
+		_grid->_selectedEntries.push_back(_grid->_lastSelectedEntryID);
+		updateListing();
+	}
+}
 } // End of namespace GUI
diff --git a/gui/widgets/grid.cpp b/gui/widgets/grid.cpp
index 9246f2ee1da..bb5c5101f21 100644
--- a/gui/widgets/grid.cpp
+++ b/gui/widgets/grid.cpp
@@ -88,7 +88,17 @@ void GridItemWidget::drawWidget() {
 	const int kMarginX = _grid->_gridXSpacing / 3;
 	const int kMarginY = _grid->_gridYSpacing / 3;
 
-	if ((_isHighlighted) || (_grid->getSelected() == _activeEntry->entryID)) {
+	// 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;
+		}
+	}
+
+	// Draw selection highlight if this entry is selected or hovered
+	if (isSelected || _isHighlighted) {
 		Common::Rect r(_x - kMarginX, _y - kMarginY,
 					   _x + _w + kMarginX, _y + _h + kMarginY);
 		// Draw a highlighted BG on hover
@@ -258,6 +268,56 @@ void GridItemWidget::handleMouseDown(int x, int y, int button, int clickCount) {
 		_grid->toggleGroup(_activeEntry->entryID);
 	} else if (_isHighlighted && isVisible()) {
 		_grid->_selectedEntry = _activeEntry;
+
+		// Get the current keyboard state
+		int32 keyState = g_system->getEventManager()->getModifierState();
+		bool ctrlPressed = (keyState & Common::KBD_CTRL) != 0;
+		bool shiftPressed = (keyState & Common::KBD_SHIFT) != 0;
+
+		if (ctrlPressed) {
+			// Ctrl+Click: Toggle selection of this item
+			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());
+				_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;
+
+			// Ensure start is before end
+			if (startID > endID) {
+				int temp = startID;
+				startID = endID;
+				endID = temp;
+			}
+
+			// Add all items in range (assuming entry IDs are sequential indices)
+			for (int i = startID; i <= endID; ++i) {
+				_grid->_selectedEntries.push_back(i);
+			}
+			// 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->_lastSelectedEntryID = _activeEntry->entryID;
+		}
+
 		sendCommand(kItemClicked, _activeEntry->entryID);
 	}
 }
@@ -445,6 +505,10 @@ GridWidget::GridWidget(GuiObject *boss, const Common::String &name)
 
 	_selectedEntry = nullptr;
 	_isGridInvalid = true;
+	_selectedEntries.clear();
+	_lastSelectedEntryID = -1;
+	_ctrlPressed = false;
+	_shiftPressed = false;
 }
 
 GridWidget::~GridWidget() {
@@ -928,6 +992,23 @@ void GridWidget::handleMouseWheel(int x, int y, int direction) {
 	_scrollPos = _scrollBar->_currentPos;
 }
 
+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;
+}
+
 void GridWidget::handleCommand(CommandSender *sender, uint32 cmd, uint32 data) {
 	// Work in progress
 	switch (cmd) {
diff --git a/gui/widgets/grid.h b/gui/widgets/grid.h
index 2484a5c50c7..d832865e232 100644
--- a/gui/widgets/grid.h
+++ b/gui/widgets/grid.h
@@ -223,11 +223,20 @@ public:
 
 	bool wantsFocus() override { return true; }
 
+	bool handleKeyDown(Common::KeyState state) override;
+	bool handleKeyUp(Common::KeyState state) override;
 	void openTrayAtSelected();
 	void scrollBarRecalc();
 
 	void setSelected(int id);
 	void setFilter(const Common::U32String &filter);
+
+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; }
 };
 
 /* GridItemWidget */


Commit: d32433346e59e611024c99003c08a3eefec2a91b
    https://github.com/scummvm/scummvm/commit/d32433346e59e611024c99003c08a3eefec2a91b
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-07T01:01:47+03:00

Commit Message:
GUI: Limit multi-selection to launcher dialog only

- Added _multiSelectEnabled flag to ListWidget and GridWidget (default: false)

- Added setMultiSelectEnabled() and isMultiSelectEnabled() methods

- Gated Ctrl+Click and Shift+Click logic behind _multiSelectEnabled check

- GroupedListWidget inherits this behavior from ListWidget

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 d600630ba39..f93b133a2ef 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -1116,6 +1116,7 @@ void LauncherSimple::build() {
 	_list->enableDictionarySelect(true);
 	_list->setNumberingMode(kListNumberingOff);
 	_list->setFilterMatcher(LauncherFilterMatcher, this);
+	_list->setMultiSelectEnabled(true);
 
 	// Populate the list
 	updateListing();
@@ -1772,6 +1773,7 @@ void LauncherGrid::build() {
 
 	// Add list with game titles
 	_grid = new GridWidget(this, "LauncherGrid.IconArea");
+	_grid->setMultiSelectEnabled(true);
 	// Populate the list
 	updateListing();
 
diff --git a/gui/widgets/grid.cpp b/gui/widgets/grid.cpp
index bb5c5101f21..999fcb7fc04 100644
--- a/gui/widgets/grid.cpp
+++ b/gui/widgets/grid.cpp
@@ -269,6 +269,15 @@ void GridItemWidget::handleMouseDown(int x, int y, int button, int clickCount) {
 	} else if (_isHighlighted && isVisible()) {
 		_grid->_selectedEntry = _activeEntry;
 
+		// If multi-select is not enabled, use simple single-selection
+		if (!_grid->isMultiSelectEnabled()) {
+			_grid->_selectedEntries.clear();
+			_grid->_selectedEntries.push_back(_activeEntry->entryID);
+			_grid->_lastSelectedEntryID = _activeEntry->entryID;
+			sendCommand(kItemClicked, _activeEntry->entryID);
+			return;
+		}
+
 		// Get the current keyboard state
 		int32 keyState = g_system->getEventManager()->getModifierState();
 		bool ctrlPressed = (keyState & Common::KBD_CTRL) != 0;
@@ -505,6 +514,7 @@ GridWidget::GridWidget(GuiObject *boss, const Common::String &name)
 
 	_selectedEntry = nullptr;
 	_isGridInvalid = true;
+	_multiSelectEnabled = false;
 	_selectedEntries.clear();
 	_lastSelectedEntryID = -1;
 	_ctrlPressed = false;
diff --git a/gui/widgets/grid.h b/gui/widgets/grid.h
index d832865e232..b459c16a6b3 100644
--- a/gui/widgets/grid.h
+++ b/gui/widgets/grid.h
@@ -156,6 +156,8 @@ protected:
 	int				_gridHeaderWidth;
 	int				_trayHeight;
 
+	bool			_multiSelectEnabled;	/// Flag for multi-selection
+
 public:
 	int				_gridItemHeight;
 	int				_gridItemWidth;
@@ -231,6 +233,10 @@ public:
 	void setSelected(int id);
 	void setFilter(const Common::U32String &filter);
 
+	// Multi-selection methods
+	void setMultiSelectEnabled(bool enabled) { _multiSelectEnabled = enabled; }
+	bool isMultiSelectEnabled() const { return _multiSelectEnabled; }
+
 public:
 	Common::Array<int> _selectedEntries; // Stores indices of selected entries
 	int _lastSelectedEntryID = -1;       // Track last selected entry for Shift+Click
diff --git a/gui/widgets/groupedlist.cpp b/gui/widgets/groupedlist.cpp
index 431ab8f34ae..5b3de49f5ce 100644
--- a/gui/widgets/groupedlist.cpp
+++ b/gui/widgets/groupedlist.cpp
@@ -262,22 +262,25 @@ void GroupedListWidget::handleMouseDown(int x, int y, int button, int clickCount
 	bool ctrlClick = (modifiers & Common::KBD_CTRL) != 0;
 	bool shiftClick = (modifiers & Common::KBD_SHIFT) != 0;
 
-	if (shiftClick && _lastSelectionStartItem != -1) {
-		// Shift+Click: Select range in terms of underlying data indices
-		int startDataIndex = _lastSelectionStartItem;
-		selectItemRange(startDataIndex, dataIndex);
-		_selectedItem = newSelectedItem;
-		sendCommand(kListSelectionChangedCmd, _selectedItem);
-	} else if (ctrlClick) {
-		// Ctrl+Click: toggle selection for the underlying data index
-		if (isItemSelected(dataIndex)) {
-			removeSelectedItem(dataIndex);
-		} else {
-			addSelectedItem(dataIndex);
+	// Only handle multi-select if it's enabled
+	if (_multiSelectEnabled && (shiftClick || ctrlClick)) {
+		if (shiftClick && _lastSelectionStartItem != -1) {
+			// Shift+Click: Select range in terms of underlying data indices
+			int startDataIndex = _lastSelectionStartItem;
+			selectItemRange(startDataIndex, dataIndex);
+			_selectedItem = newSelectedItem;
+			sendCommand(kListSelectionChangedCmd, _selectedItem);
+		} else if (ctrlClick) {
+			// Ctrl+Click: toggle selection for the underlying data index
+			if (isItemSelected(dataIndex)) {
+				removeSelectedItem(dataIndex);
+			} else {
+				addSelectedItem(dataIndex);
+			}
+			_selectedItem = newSelectedItem;
+			_lastSelectionStartItem = dataIndex;
+			sendCommand(kListSelectionChangedCmd, _selectedItem);
 		}
-		_selectedItem = newSelectedItem;
-		_lastSelectionStartItem = dataIndex;
-		sendCommand(kListSelectionChangedCmd, _selectedItem);
 	} else {
 		// Regular click: clear selection and select only this underlying item
 		clearSelection();
diff --git a/gui/widgets/list.cpp b/gui/widgets/list.cpp
index 3e1789ab9c4..399f6713809 100644
--- a/gui/widgets/list.cpp
+++ b/gui/widgets/list.cpp
@@ -52,6 +52,7 @@ ListWidget::ListWidget(Dialog *boss, const Common::String &name, const Common::U
 	_currentPos = 0;
 	_selectedItem = -1;
 	_lastSelectionStartItem = -1;
+	_multiSelectEnabled = false;
 	_currentKeyDown = 0;
 
 	_quickSelectTime = 0;
@@ -96,6 +97,7 @@ ListWidget::ListWidget(Dialog *boss, int x, int y, int w, int h, bool scale, con
 	_currentPos = 0;
 	_selectedItem = -1;
 	_lastSelectionStartItem = -1;
+	_multiSelectEnabled = false;
 	_currentKeyDown = 0;
 
 	_quickSelectTime = 0;
@@ -349,26 +351,28 @@ void ListWidget::handleMouseDown(int x, int y, int button, int clickCount) {
 	bool ctrlClick = (modifiers & Common::KBD_CTRL) != 0;
 	bool shiftClick = (modifiers & Common::KBD_SHIFT) != 0;
 
-	if (shiftClick && _lastSelectionStartItem != -1) {
-		// Shift+Click: Select range from last selection start to current item
-		selectItemRange(_lastSelectionStartItem, newSelectedItem);
-		_selectedItem = newSelectedItem;
-		sendCommand(kListSelectionChangedCmd, _selectedItem);
-	} else if (ctrlClick) {
-		// Ctrl+Click: Add/remove from selection
-		if (isItemSelected(newSelectedItem)) {
-			removeSelectedItem(newSelectedItem);
-			// If the primary selection is the same item, clear it to avoid highlight
-			sendCommand(kListSelectionChangedCmd, newSelectedItem);
-			markAsDirty();
-			if (_selectedItem == newSelectedItem)
-				_selectedItem = -1;
-		} else {
-			addSelectedItem(newSelectedItem);
+	// Only handle multi-select if it's enabled
+	if (_multiSelectEnabled && (shiftClick || ctrlClick)) {
+		if (shiftClick && _lastSelectionStartItem != -1) {
+			// Shift+Click: Select range from last selection start to current item
 			_selectedItem = newSelectedItem;
 			_lastSelectionStartItem = newSelectedItem;
+			selectItemRange(_lastSelectionStartItem, newSelectedItem);
+			sendCommand(kListSelectionChangedCmd, _selectedItem);
+		} else if (ctrlClick) {
+			// Ctrl+Click: Add/remove from selection
+			if (isItemSelected(newSelectedItem)) {
+				removeSelectedItem(newSelectedItem);
+				// If the primary selection is the same item, clear it to avoid highlight
+				sendCommand(kListSelectionChangedCmd, newSelectedItem);
+				markAsDirty();
+			} else {
+				addSelectedItem(newSelectedItem);
+				_selectedItem = newSelectedItem;
+				_lastSelectionStartItem = newSelectedItem;
+			}
+			sendCommand(kListSelectionChangedCmd, _selectedItem);
 		}
-		sendCommand(kListSelectionChangedCmd, _selectedItem);
 	} else {
 		// Regular click: Clear previous selection and select only this item
 		clearSelection();
diff --git a/gui/widgets/list.h b/gui/widgets/list.h
index c81e26cfe2f..2ea41b340ae 100644
--- a/gui/widgets/list.h
+++ b/gui/widgets/list.h
@@ -74,6 +74,7 @@ protected:
 	int				_selectedItem;
 	Common::Array<int> _selectedItems;    /// Multiple selected items
 	int _lastSelectionStartItem;          /// Used for Shift+Click range selection
+	bool			_multiSelectEnabled;	/// Flag for multi-selection
 	ScrollBarWidget	*_scrollBar;
 	int				_currentKeyDown;
 
@@ -143,6 +144,10 @@ public:
 	void setEditColor(ThemeEngine::FontColor color) { _editColor = color; }
 	void setFilterMatcher(FilterMatcher matcher, void *arg) { _filterMatcher = matcher; _filterMatcherArg = arg; }
 
+	// Multi-selection methods
+	void setMultiSelectEnabled(bool enabled) { _multiSelectEnabled = enabled; }
+	bool isMultiSelectEnabled() const { return _multiSelectEnabled; }
+
 	// Made startEditMode/endEditMode for SaveLoadChooser
 	void startEditMode() override;
 	void endEditMode() override;


Commit: da8592a469882f57a722b3be1cc9793f01c1e2ff
    https://github.com/scummvm/scummvm/commit/da8592a469882f57a722b3be1cc9793f01c1e2ff
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-07T01:01:47+03:00

Commit Message:
GUI: Disable Enter key activation when multi-selection is enabled

Changed paths:
    gui/widgets/list.cpp


diff --git a/gui/widgets/list.cpp b/gui/widgets/list.cpp
index 399f6713809..33efdb1a53b 100644
--- a/gui/widgets/list.cpp
+++ b/gui/widgets/list.cpp
@@ -505,6 +505,10 @@ bool ListWidget::handleKeyDown(Common::KeyState state) {
 		switch (state.keycode) {
 		case Common::KEYCODE_RETURN:
 		case Common::KEYCODE_KP_ENTER:
+			// Disable activation if multi-select is enabled
+			if (_multiSelectEnabled) {
+					break;
+			}
 			if (_selectedItem >= 0) {
 				// override continuous enter keydown
 				if (_editable && (_currentKeyDown != Common::KEYCODE_RETURN && _currentKeyDown != Common::KEYCODE_KP_ENTER)) {


Commit: 8e46e1d4d833956adca9a62bb045ebf5597eec99
    https://github.com/scummvm/scummvm/commit/8e46e1d4d833956adca9a62bb045ebf5597eec99
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-07T01:01:47+03:00

Commit Message:
GUI: Clarify remove confirmation message

Change the confirmation prompt shown when removing multiple game configurations in List view. Previous wording: "Are you sure you want to remove the following games?" Now: "Do you really want to remove the following game configurations?"

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 f93b133a2ef..033310c75f6 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -1407,7 +1407,7 @@ void LauncherSimple::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 
 void LauncherSimple::removeMultipleGames(const Common::Array<int> &selectedItems) {
 	// Build confirmation message with list of games to remove
-	Common::U32String confirmMsg = _("Are you sure you want to remove the following games?\n\n");
+	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()) {


Commit: 06fc2359be1293dd24703aa93ef690e57fa79bc0
    https://github.com/scummvm/scummvm/commit/06fc2359be1293dd24703aa93ef690e57fa79bc0
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-07T01:01:47+03:00

Commit Message:
GUI: Unify game and add-on removal logic

Move duplicated removal logic into a new LauncherDialog::removeGamesWithAddons() helper and call it from LauncherSimple::removeMultipleGames() and LauncherGrid::removeSelectedGames(). The helper removes a game domain, its add-ons, and flushes configuration to disk. Reduces duplication and ensures consistent behavior across launcher views.

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


diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 033310c75f6..673858446d3 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -476,6 +476,23 @@ void LauncherDialog::removeGame(int item) {
 	}
 }
 
+void LauncherDialog::removeGamesWithAddons(const Common::StringArray &domainsToRemove) {
+	for (const Common::String &domain : domainsToRemove) {
+		ConfMan.removeGameDomain(domain);
+
+		// Remove all the add-ons for this game
+		const Common::ConfigManager::DomainMap &domains = ConfMan.getGameDomains();
+		for (const auto &addonDomain : domains) {
+			if (addonDomain._value.getValOrDefault("parent") == domain) {
+				ConfMan.removeGameDomain(addonDomain._key);
+			}
+		}
+	}
+
+	// Write config to disk
+	ConfMan.flushToDisk();
+}
+
 void LauncherDialog::editGame(int item) {
 	// Set game specific options. Most of these should be "optional", i.e. by
 	// default set nothing and use the global ScummVM settings. E.g. the user
@@ -1425,28 +1442,19 @@ void LauncherSimple::removeMultipleGames(const Common::Array<int> &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);
 				}
-
-				// Remove the game domain
-				ConfMan.removeGameDomain(_domains[idx]);
-
-				// Remove all the add-ons for this game
-				const Common::ConfigManager::DomainMap &domains = ConfMan.getGameDomains();
-				for (const auto &domain : domains) {
-					if (domain._value.getValOrDefault("parent") == _domains[idx]) {
-						ConfMan.removeGameDomain(domain._key);
-					}
-				}
+				domainsToRemove.push_back(_domains[idx]);
 			}
 		}
 
-		// Write config to disk
-		ConfMan.flushToDisk();
+		// Remove games and addons
+		removeGamesWithAddons(domainsToRemove);
 
 		// Clear the selection and update the listing
 		_list->clearSelection();
@@ -1808,13 +1816,13 @@ void LauncherGrid::removeSelectedGames() {
 
 	MessageDialog alert(message, Common::U32String(_("Yes")), Common::U32String(_("No")));
 	if (alert.runModal() == GUI::kMessageOK) {
-		for (const Common::String &domain : domainsToRemove) {
-			ConfMan.removeGameDomain(domain);
-		}
-		ConfMan.flushToDisk();
+		// Remove games and addons
+		removeGamesWithAddons(domainsToRemove);
 		_grid->_selectedEntries.clear();
 		_grid->_selectedEntries.push_back(_grid->_lastSelectedEntryID);
 		updateListing();
+		updateButtons();
+		g_gui.scheduleTopDialogRedraw();
 	}
 }
 } // End of namespace GUI
diff --git a/gui/launcher.h b/gui/launcher.h
index ec3511755ab..f07304b50c6 100644
--- a/gui/launcher.h
+++ b/gui/launcher.h
@@ -187,6 +187,11 @@ protected:
 	 */
 	void removeGame(int item);
 
+	/**
+	 * Remove multiple games and their addons.
+	 */
+	void removeGamesWithAddons(const Common::StringArray &domainsToRemove);
+
 	/**
 	 * Handle "Edit game..." button.
 	 */


Commit: fe2b8890fbfa8cde8223af76c0b7e9ee39cb6bec
    https://github.com/scummvm/scummvm/commit/fe2b8890fbfa8cde8223af76c0b7e9ee39cb6bec
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-07T01:01:47+03:00

Commit Message:
GUI: Confirm multi-selection game removal

When Delete key is pressed in the list view and multiple items are selected, call removeMultipleGames(selectedItems) to show the multi-selection confirmation dialog. Single-item behavior is unchanged.

Changed paths:
    gui/launcher.cpp


diff --git a/gui/launcher.cpp b/gui/launcher.cpp
index 673858446d3..1ce0bc989b9 100644
--- a/gui/launcher.cpp
+++ b/gui/launcher.cpp
@@ -1372,9 +1372,16 @@ void LauncherSimple::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
 	case kListItemDoubleClickedCmd:
 		LauncherDialog::handleCommand(sender, kStartCmd, 0);
 		break;
-	case kListItemRemovalRequestCmd:
-		LauncherDialog::handleCommand(sender, kRemoveGameCmd, 0);
+	case kListItemRemovalRequestCmd: {
+		const Common::Array<int> &selectedItems = _list->getSelectedItems();
+		if (selectedItems.size() > 1) {
+			// Multi-selection removal: show confirmation dialog with list of games
+			removeMultipleGames(selectedItems);
+		} else {
+			LauncherDialog::handleCommand(sender, kRemoveGameCmd, 0);
+		}
 		break;
+	}
 	case kListSelectionChangedCmd:
 		updateButtons();
 		break;


Commit: ea316313b52c7aaabea35d191c818a108e32d418
    https://github.com/scummvm/scummvm/commit/ea316313b52c7aaabea35d191c818a108e32d418
Author: Mohit Bankar (mohitbankar1212 at gmail.com)
Date: 2026-01-07T01:01:47+03:00

Commit Message:
GUI: Allow Enter activation with single selection

Only suppress Enter activation when multi-select is enabled and more than one item is selected; if exactly one item is selected, Enter activates it even when multi-select is on.

Changed paths:
    gui/widgets/list.cpp


diff --git a/gui/widgets/list.cpp b/gui/widgets/list.cpp
index 33efdb1a53b..1a225025bd4 100644
--- a/gui/widgets/list.cpp
+++ b/gui/widgets/list.cpp
@@ -505,8 +505,8 @@ bool ListWidget::handleKeyDown(Common::KeyState state) {
 		switch (state.keycode) {
 		case Common::KEYCODE_RETURN:
 		case Common::KEYCODE_KP_ENTER:
-			// Disable activation if multi-select is enabled
-			if (_multiSelectEnabled) {
+			// Disable activation if multi-select is enabled and multiple items are selected
+			if (_multiSelectEnabled && _selectedItems.size() > 1) {
 					break;
 			}
 			if (_selectedItem >= 0) {




More information about the Scummvm-git-logs mailing list