[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