From 76b2c2444201e406ef9d8c8609c3fc785fe34f22 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sun, 5 May 2024 22:21:53 +1000 Subject: [PATCH] Qt: Simplify game directory list --- src/duckstation-qt/CMakeLists.txt | 2 - src/duckstation-qt/duckstation-qt.vcxproj | 3 - .../duckstation-qt.vcxproj.filters | 5 - .../gamelistsearchdirectoriesmodel.cpp | 184 ------------------ .../gamelistsearchdirectoriesmodel.h | 44 ----- src/duckstation-qt/gamelistsettingswidget.cpp | 121 +++++++++--- src/duckstation-qt/gamelistsettingswidget.h | 15 +- src/duckstation-qt/gamelistsettingswidget.ui | 19 +- 8 files changed, 122 insertions(+), 271 deletions(-) delete mode 100644 src/duckstation-qt/gamelistsearchdirectoriesmodel.cpp delete mode 100644 src/duckstation-qt/gamelistsearchdirectoriesmodel.h diff --git a/src/duckstation-qt/CMakeLists.txt b/src/duckstation-qt/CMakeLists.txt index 32cba85931..29796c9ff0 100644 --- a/src/duckstation-qt/CMakeLists.txt +++ b/src/duckstation-qt/CMakeLists.txt @@ -87,8 +87,6 @@ set(SRCS gamelistmodel.h gamelistrefreshthread.cpp gamelistrefreshthread.h - gamelistsearchdirectoriesmodel.cpp - gamelistsearchdirectoriesmodel.h gamelistsettingswidget.cpp gamelistsettingswidget.h gamelistsettingswidget.ui diff --git a/src/duckstation-qt/duckstation-qt.vcxproj b/src/duckstation-qt/duckstation-qt.vcxproj index 01e89f29af..a21b7a00b9 100644 --- a/src/duckstation-qt/duckstation-qt.vcxproj +++ b/src/duckstation-qt/duckstation-qt.vcxproj @@ -23,7 +23,6 @@ - @@ -72,7 +71,6 @@ - @@ -240,7 +238,6 @@ - diff --git a/src/duckstation-qt/duckstation-qt.vcxproj.filters b/src/duckstation-qt/duckstation-qt.vcxproj.filters index 2277aa219b..3de628b98c 100644 --- a/src/duckstation-qt/duckstation-qt.vcxproj.filters +++ b/src/duckstation-qt/duckstation-qt.vcxproj.filters @@ -22,7 +22,6 @@ - @@ -118,9 +117,6 @@ moc - - moc - moc @@ -225,7 +221,6 @@ - diff --git a/src/duckstation-qt/gamelistsearchdirectoriesmodel.cpp b/src/duckstation-qt/gamelistsearchdirectoriesmodel.cpp deleted file mode 100644 index 4cf667164b..0000000000 --- a/src/duckstation-qt/gamelistsearchdirectoriesmodel.cpp +++ /dev/null @@ -1,184 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) - -#include "gamelistsearchdirectoriesmodel.h" -#include "mainwindow.h" -#include "qthost.h" -#include "qtutils.h" -#include - -GameListSearchDirectoriesModel::GameListSearchDirectoriesModel(EmuThread* host_interface) - : m_host_interface(host_interface) -{ - loadFromSettings(); -} - -GameListSearchDirectoriesModel::~GameListSearchDirectoriesModel() = default; - -int GameListSearchDirectoriesModel::columnCount(const QModelIndex& parent) const -{ - if (parent.isValid()) - return 0; - - return 2; -} - -QVariant GameListSearchDirectoriesModel::headerData(int section, Qt::Orientation orientation, - int role /*= Qt::DisplayRole*/) const -{ - if (orientation != Qt::Horizontal || role != Qt::DisplayRole) - return {}; - - if (section == 0) - return tr("Path"); - else - return tr("Recursive"); -} - -int GameListSearchDirectoriesModel::rowCount(const QModelIndex& parent) const -{ - if (parent.isValid()) - return 0; - - return static_cast(m_entries.size()); -} - -QVariant GameListSearchDirectoriesModel::data(const QModelIndex& index, int role /*= Qt::DisplayRole*/) const -{ - if (!index.isValid()) - return {}; - - const int row = index.row(); - const int column = index.column(); - if (row < 0 || row >= static_cast(m_entries.size())) - return {}; - - const Entry& entry = m_entries[row]; - if (role == Qt::CheckStateRole) - { - if (column == 1) - return entry.recursive ? Qt::Checked : Qt::Unchecked; - } - else if (role == Qt::DisplayRole) - { - if (column == 0) - return entry.path; - } - - return {}; -} - -bool GameListSearchDirectoriesModel::setData(const QModelIndex& index, const QVariant& value, int role) -{ - if (!index.isValid()) - return false; - - const int row = index.row(); - const int column = index.column(); - if (row < 0 || row >= static_cast(m_entries.size())) - return false; - - if (column != 1 || role == Qt::CheckStateRole) - return false; - - Entry& entry = m_entries[row]; - entry.recursive = value == Qt::Checked; - saveToSettings(); - g_main_window->refreshGameList(false); - return true; -} - -void GameListSearchDirectoriesModel::addEntry(const QString& path, bool recursive) -{ - auto existing = std::find_if(m_entries.begin(), m_entries.end(), [path](const Entry& e) { return e.path == path; }); - if (existing != m_entries.end()) - { - const int row = static_cast(existing - m_entries.begin()); - existing->recursive = recursive; - dataChanged(index(row, 1), index(row, 1), QVector{Qt::CheckStateRole}); - } - else - { - beginInsertRows(QModelIndex(), static_cast(m_entries.size()), static_cast(m_entries.size())); - m_entries.push_back({path, recursive}); - endInsertRows(); - } - - saveToSettings(); - g_main_window->refreshGameList(false); -} - -void GameListSearchDirectoriesModel::removeEntry(int row) -{ - if (row < 0 || row >= static_cast(m_entries.size())) - return; - - beginRemoveRows(QModelIndex(), row, row); - m_entries.erase(m_entries.begin() + row); - endRemoveRows(); - - saveToSettings(); - g_main_window->refreshGameList(false); -} - -bool GameListSearchDirectoriesModel::isEntryRecursive(int row) const -{ - return (row < 0 || row >= static_cast(m_entries.size())) ? false : m_entries[row].recursive; -} - -void GameListSearchDirectoriesModel::setEntryRecursive(int row, bool recursive) -{ - if (row < 0 || row >= static_cast(m_entries.size())) - return; - - m_entries[row].recursive = recursive; - emit dataChanged(index(row, 1), index(row, 1), {Qt::CheckStateRole}); - - saveToSettings(); - g_main_window->refreshGameList(false); -} - -void GameListSearchDirectoriesModel::openEntryInExplorer(QWidget* parent, int row) const -{ - if (row < 0 || row >= static_cast(m_entries.size())) - return; - - QtUtils::OpenURL(parent, QUrl::fromLocalFile(m_entries[row].path)); -} - -void GameListSearchDirectoriesModel::loadFromSettings() -{ - std::vector path_list = Host::GetBaseStringListSetting("GameList", "Paths"); - for (std::string& entry : path_list) - m_entries.push_back({QString::fromStdString(entry), false}); - - path_list = Host::GetBaseStringListSetting("GameList", "RecursivePaths"); - for (std::string& entry : path_list) - m_entries.push_back({QString::fromStdString(entry), true}); -} - -void GameListSearchDirectoriesModel::saveToSettings() -{ - std::vector paths; - std::vector recursive_paths; - - for (const Entry& entry : m_entries) - { - if (entry.recursive) - recursive_paths.push_back(entry.path.toStdString()); - else - paths.push_back(entry.path.toStdString()); - } - - if (paths.empty()) - Host::DeleteBaseSettingValue("GameList", "Paths"); - else - Host::SetBaseStringListSettingValue("GameList", "Paths", paths); - - if (recursive_paths.empty()) - Host::DeleteBaseSettingValue("GameList", "RecursivePaths"); - else - Host::SetBaseStringListSettingValue("GameList", "RecursivePaths", recursive_paths); - - Host::CommitBaseSettingChanges(); -} diff --git a/src/duckstation-qt/gamelistsearchdirectoriesmodel.h b/src/duckstation-qt/gamelistsearchdirectoriesmodel.h deleted file mode 100644 index b1a27680c3..0000000000 --- a/src/duckstation-qt/gamelistsearchdirectoriesmodel.h +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin -// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) - -#pragma once -#include -#include -#include - -class EmuThread; - -class GameListSearchDirectoriesModel : public QAbstractTableModel -{ - Q_OBJECT - -public: - GameListSearchDirectoriesModel(EmuThread* host_interface); - ~GameListSearchDirectoriesModel(); - - int columnCount(const QModelIndex& parent) const override; - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; - int rowCount(const QModelIndex& parent) const override; - QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; - bool setData(const QModelIndex& index, const QVariant& value, int role) override; - - void addEntry(const QString& path, bool recursive); - void removeEntry(int row); - - bool isEntryRecursive(int row) const; - void setEntryRecursive(int row, bool recursive); - - void openEntryInExplorer(QWidget* parent, int row) const; - void loadFromSettings(); - void saveToSettings(); - -private: - struct Entry - { - QString path; - bool recursive; - }; - - EmuThread* m_host_interface; - std::vector m_entries; -}; diff --git a/src/duckstation-qt/gamelistsettingswidget.cpp b/src/duckstation-qt/gamelistsettingswidget.cpp index 2593f96b89..f38132e319 100644 --- a/src/duckstation-qt/gamelistsettingswidget.cpp +++ b/src/duckstation-qt/gamelistsettingswidget.cpp @@ -1,9 +1,8 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin +// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #include "gamelistsettingswidget.h" #include "core/game_list.h" -#include "gamelistsearchdirectoriesmodel.h" #include "mainwindow.h" #include "qthost.h" #include "qtutils.h" @@ -16,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -26,8 +26,6 @@ GameListSettingsWidget::GameListSettingsWidget(SettingsWindow* dialog, QWidget* { m_ui.setupUi(this); - m_search_directories_model = new GameListSearchDirectoriesModel(g_emu_thread); - m_ui.searchDirectoryList->setModel(m_search_directories_model); m_ui.searchDirectoryList->setSelectionMode(QAbstractItemView::SingleSelection); m_ui.searchDirectoryList->setSelectionBehavior(QAbstractItemView::SelectRows); m_ui.searchDirectoryList->setAlternatingRowColors(true); @@ -37,14 +35,13 @@ GameListSettingsWidget::GameListSettingsWidget(SettingsWindow* dialog, QWidget* m_ui.searchDirectoryList->setCurrentIndex({}); m_ui.searchDirectoryList->setContextMenuPolicy(Qt::ContextMenuPolicy::CustomContextMenu); - connect(m_ui.searchDirectoryList, &QTableView::clicked, this, &GameListSettingsWidget::onDirectoryListItemClicked); - connect(m_ui.searchDirectoryList, &QTableView::customContextMenuRequested, this, + connect(m_ui.searchDirectoryList, &QTableWidget::customContextMenuRequested, this, &GameListSettingsWidget::onDirectoryListContextMenuRequested); connect(m_ui.addSearchDirectoryButton, &QPushButton::clicked, this, &GameListSettingsWidget::onAddSearchDirectoryButtonClicked); connect(m_ui.removeSearchDirectoryButton, &QPushButton::clicked, this, &GameListSettingsWidget::onRemoveSearchDirectoryButtonClicked); - connect(m_ui.searchDirectoryList->selectionModel(), &QItemSelectionModel::selectionChanged, this, + connect(m_ui.searchDirectoryList, &QTableWidget::itemSelectionChanged, this, &GameListSettingsWidget::onSearchDirectoriesSelectionChanged); connect(m_ui.addExcludedFile, &QPushButton::clicked, this, &GameListSettingsWidget::onAddExcludedFileButtonClicked); connect(m_ui.addExcludedFolder, &QPushButton::clicked, this, @@ -56,6 +53,7 @@ GameListSettingsWidget::GameListSettingsWidget(SettingsWindow* dialog, QWidget* connect(m_ui.rescanAllGames, &QPushButton::clicked, this, &GameListSettingsWidget::onRescanAllGamesClicked); connect(m_ui.scanForNewGames, &QPushButton::clicked, this, &GameListSettingsWidget::onScanForNewGamesClicked); + refreshDirectoryList(); refreshExclusionList(); } @@ -83,24 +81,95 @@ void GameListSettingsWidget::refreshExclusionList() m_ui.removeExcludedPath->setEnabled(false); } -void GameListSettingsWidget::resizeEvent(QResizeEvent* event) +bool GameListSettingsWidget::event(QEvent* event) { - QWidget::resizeEvent(event); + bool res = QWidget::event(event); - QtUtils::ResizeColumnsForTableView(m_ui.searchDirectoryList, {-1, 100}); + switch (event->type()) + { + case QEvent::LayoutRequest: + case QEvent::Resize: + QtUtils::ResizeColumnsForTableView(m_ui.searchDirectoryList, {-1, 100}); + break; + + default: + break; + } + + return res; } -void GameListSettingsWidget::onDirectoryListItemClicked(const QModelIndex& index) +void GameListSettingsWidget::addPathToTable(const std::string& path, bool recursive) { - if (!index.isValid()) - return; + const int row = m_ui.searchDirectoryList->rowCount(); + m_ui.searchDirectoryList->insertRow(row); + + QTableWidgetItem* item = new QTableWidgetItem(); + item->setText(QString::fromStdString(path)); + item->setFlags(item->flags() & ~(Qt::ItemIsEditable)); + m_ui.searchDirectoryList->setItem(row, 0, item); + + QCheckBox* cb = new QCheckBox(m_ui.searchDirectoryList); + m_ui.searchDirectoryList->setCellWidget(row, 1, cb); + cb->setChecked(recursive); + + connect(cb, &QCheckBox::checkStateChanged, this, [item](Qt::CheckState state) { + const std::string path(item->text().toStdString()); + if (state == Qt::Checked) + { + Host::RemoveValueFromBaseStringListSetting("GameList", "Paths", path.c_str()); + Host::AddValueToBaseStringListSetting("GameList", "RecursivePaths", path.c_str()); + } + else + { + Host::RemoveValueFromBaseStringListSetting("GameList", "RecursivePaths", path.c_str()); + Host::AddValueToBaseStringListSetting("GameList", "Paths", path.c_str()); + } + Host::CommitBaseSettingChanges(); + g_main_window->refreshGameList(false); + }); +} + +void GameListSettingsWidget::refreshDirectoryList() +{ + QSignalBlocker sb(m_ui.searchDirectoryList); + while (m_ui.searchDirectoryList->rowCount() > 0) + m_ui.searchDirectoryList->removeRow(0); + + std::vector path_list = Host::GetBaseStringListSetting("GameList", "Paths"); + for (const std::string& entry : path_list) + addPathToTable(entry, false); + + path_list = Host::GetBaseStringListSetting("GameList", "RecursivePaths"); + for (const std::string& entry : path_list) + addPathToTable(entry, true); + + m_ui.searchDirectoryList->sortByColumn(0, Qt::AscendingOrder); + m_ui.removeSearchDirectoryButton->setEnabled(false); +} + +void GameListSettingsWidget::addSearchDirectory(const QString& path, bool recursive) +{ + const std::string spath(path.toStdString()); + Host::RemoveValueFromBaseStringListSetting("GameList", recursive ? "Paths" : "RecursivePaths", spath.c_str()); + Host::AddValueToBaseStringListSetting("GameList", recursive ? "RecursivePaths" : "Paths", spath.c_str()); + Host::CommitBaseSettingChanges(); + refreshDirectoryList(); + g_main_window->refreshGameList(false); +} - const int row = index.row(); - const int column = index.column(); - if (column != 1) +void GameListSettingsWidget::removeSearchDirectory(const QString& path) +{ + const std::string spath(path.toStdString()); + if (!Host::RemoveValueFromBaseStringListSetting("GameList", "Paths", spath.c_str()) && + !Host::RemoveValueFromBaseStringListSetting("GameList", "RecursivePaths", spath.c_str())) + { return; + } - m_search_directories_model->setEntryRecursive(row, !m_search_directories_model->isEntryRecursive(row)); + Host::CommitBaseSettingChanges(); + refreshDirectoryList(); + g_main_window->refreshGameList(false); } void GameListSettingsWidget::onDirectoryListContextMenuRequested(const QPoint& point) @@ -112,10 +181,11 @@ void GameListSettingsWidget::onDirectoryListContextMenuRequested(const QPoint& p const int row = selection[0].row(); QMenu menu; - menu.addAction(tr("Remove"), [this, row]() { m_search_directories_model->removeEntry(row); }); + menu.addAction(tr("Remove"), [this]() { onRemoveSearchDirectoryButtonClicked(); }); menu.addSeparator(); - menu.addAction(tr("Open Directory..."), - [this, row]() { m_search_directories_model->openEntryInExplorer(this, row); }); + menu.addAction(tr("Open Directory..."), [this, row]() { + QtUtils::OpenURL(this, QUrl::fromLocalFile(m_ui.searchDirectoryList->item(row, 0)->text())); + }); menu.exec(m_ui.searchDirectoryList->mapToGlobal(point)); } @@ -137,7 +207,7 @@ void GameListSettingsWidget::addSearchDirectory(QWidget* parent_widget) return; const bool recursive = (selection == QMessageBox::Yes); - m_search_directories_model->addEntry(dir, recursive); + addSearchDirectory(dir, recursive); } void GameListSettingsWidget::onAddSearchDirectoryButtonClicked() @@ -147,12 +217,13 @@ void GameListSettingsWidget::onAddSearchDirectoryButtonClicked() void GameListSettingsWidget::onRemoveSearchDirectoryButtonClicked() { - QModelIndexList selection = m_ui.searchDirectoryList->selectionModel()->selectedIndexes(); - if (selection.size() < 1) + const int row = m_ui.searchDirectoryList->currentRow(); + QTableWidgetItem* item = (row >= 0) ? m_ui.searchDirectoryList->takeItem(row, 0) : nullptr; + if (!item) return; - const int row = selection[0].row(); - m_search_directories_model->removeEntry(row); + removeSearchDirectory(item->text()); + delete item; } void GameListSettingsWidget::onSearchDirectoriesSelectionChanged() diff --git a/src/duckstation-qt/gamelistsettingswidget.h b/src/duckstation-qt/gamelistsettingswidget.h index d1aa19eab7..1427c3a7b4 100644 --- a/src/duckstation-qt/gamelistsettingswidget.h +++ b/src/duckstation-qt/gamelistsettingswidget.h @@ -1,14 +1,13 @@ -// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin +// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin // SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0) #pragma once -#include #include +#include #include "ui_gamelistsettingswidget.h" class SettingsWindow; -class GameListSearchDirectoriesModel; class GameListSettingsWidget : public QWidget { @@ -25,7 +24,6 @@ public Q_SLOTS: void addSearchDirectory(QWidget* parent_widget); private Q_SLOTS: - void onDirectoryListItemClicked(const QModelIndex& index); void onDirectoryListContextMenuRequested(const QPoint& point); void onAddSearchDirectoryButtonClicked(); void onRemoveSearchDirectoryButtonClicked(); @@ -38,10 +36,13 @@ private Q_SLOTS: void onRescanAllGamesClicked(); protected: - void resizeEvent(QResizeEvent* event); + bool event(QEvent* event) override; private: - Ui::GameListSettingsWidget m_ui; + void addPathToTable(const std::string& path, bool recursive); + void refreshDirectoryList(); + void addSearchDirectory(const QString& path, bool recursive); + void removeSearchDirectory(const QString& path); - GameListSearchDirectoriesModel* m_search_directories_model = nullptr; + Ui::GameListSettingsWidget m_ui; }; diff --git a/src/duckstation-qt/gamelistsettingswidget.ui b/src/duckstation-qt/gamelistsettingswidget.ui index 0bd3dc6ce4..1804691837 100644 --- a/src/duckstation-qt/gamelistsettingswidget.ui +++ b/src/duckstation-qt/gamelistsettingswidget.ui @@ -59,6 +59,9 @@ + + Qt::ToolButtonTextBesideIcon + @@ -75,12 +78,26 @@ + + Qt::ToolButtonTextBesideIcon + - + + + + Search Directory + + + + + Scan Recursively + + +