diff --git a/pcsx2-qt/Settings/GameCheatSettingsWidget.cpp b/pcsx2-qt/Settings/GameCheatSettingsWidget.cpp index 5c7d388473..d268c170a4 100644 --- a/pcsx2-qt/Settings/GameCheatSettingsWidget.cpp +++ b/pcsx2-qt/Settings/GameCheatSettingsWidget.cpp @@ -13,11 +13,22 @@ #include "common/HeterogeneousContainers.h" +#include + GameCheatSettingsWidget::GameCheatSettingsWidget(SettingsWindow* dialog, QWidget* parent) : m_dialog(dialog) { m_ui.setupUi(this); + m_model = new QStandardItemModel(this); + + QStringList headers; + headers.push_back(tr("Name")); + headers.push_back(tr("Author")); + headers.push_back(tr("Description")); + m_model->setHorizontalHeaderLabels(headers); + + m_ui.cheatList->setModel(m_model); reloadList(); SettingsInterface* sif = m_dialog->getSettingsInterface(); @@ -26,8 +37,8 @@ GameCheatSettingsWidget::GameCheatSettingsWidget(SettingsWindow* dialog, QWidget updateListEnabled(); connect(m_ui.enableCheats, &QCheckBox::checkStateChanged, this, &GameCheatSettingsWidget::updateListEnabled); - connect(m_ui.cheatList, &QTreeWidget::itemDoubleClicked, this, &GameCheatSettingsWidget::onCheatListItemDoubleClicked); - connect(m_ui.cheatList, &QTreeWidget::itemChanged, this, &GameCheatSettingsWidget::onCheatListItemChanged); + connect(m_ui.cheatList, &QTreeView::doubleClicked, this, &GameCheatSettingsWidget::onCheatListItemDoubleClicked); + connect(m_model, &QStandardItemModel::itemChanged, this, &GameCheatSettingsWidget::onCheatListItemChanged); connect(m_ui.reloadCheats, &QPushButton::clicked, this, &GameCheatSettingsWidget::onReloadClicked); connect(m_ui.enableAll, &QPushButton::clicked, this, [this]() { setStateForAll(true); }); connect(m_ui.disableAll, &QPushButton::clicked, this, [this]() { setStateForAll(false); }); @@ -39,28 +50,43 @@ GameCheatSettingsWidget::GameCheatSettingsWidget(SettingsWindow* dialog, QWidget GameCheatSettingsWidget::~GameCheatSettingsWidget() = default; -void GameCheatSettingsWidget::onCheatListItemDoubleClicked(QTreeWidgetItem* item, int column) +void GameCheatSettingsWidget::onCheatListItemDoubleClicked(const QModelIndex& index) { - QVariant data = item->data(0, Qt::UserRole); + QModelIndex sibling_index = index.sibling(index.row(), 0); + QStandardItem* item = m_model->itemFromIndex(sibling_index); + if (!item) + return; + + if (item->hasChildren() && index.column() != 0) + { + bool isExpanded = m_ui.cheatList->isExpanded(sibling_index); + if (isExpanded) + m_ui.cheatList->collapse(sibling_index); + else + m_ui.cheatList->expand(sibling_index); + return; + } + + QVariant data = item->data(Qt::UserRole); if (!data.isValid()) return; std::string cheat_name = data.toString().toStdString(); - const bool new_state = !(item->checkState(0) == Qt::Checked); - item->setCheckState(0, new_state ? Qt::Checked : Qt::Unchecked); + const bool new_state = !(item->checkState() == Qt::Checked); + item->setCheckState(new_state ? Qt::Checked : Qt::Unchecked); setCheatEnabled(std::move(cheat_name), new_state, true); } -void GameCheatSettingsWidget::onCheatListItemChanged(QTreeWidgetItem* item, int column) +void GameCheatSettingsWidget::onCheatListItemChanged(QStandardItem* item) { - QVariant data = item->data(0, Qt::UserRole); + QVariant data = item->data(Qt::UserRole); if (!data.isValid()) return; std::string cheat_name = data.toString().toStdString(); const bool current_enabled = (std::find(m_enabled_patches.begin(), m_enabled_patches.end(), cheat_name) != m_enabled_patches.end()); - const bool current_checked = (item->checkState(0) == Qt::Checked); + const bool current_checked = (item->checkState() == Qt::Checked); if (current_enabled == current_checked) return; @@ -125,24 +151,28 @@ void GameCheatSettingsWidget::setCheatEnabled(std::string name, bool enabled, bo void GameCheatSettingsWidget::setStateForAll(bool enabled) { - QSignalBlocker sb(m_ui.cheatList); + // Temporarily disconnect from itemChanged to prevent redundant saves + disconnect(m_model, &QStandardItemModel::itemChanged, this, &GameCheatSettingsWidget::onCheatListItemChanged); + setStateRecursively(nullptr, enabled); m_dialog->getSettingsInterface()->Save(); g_emu_thread->reloadGameSettings(); + + connect(m_model, &QStandardItemModel::itemChanged, this, &GameCheatSettingsWidget::onCheatListItemChanged); } -void GameCheatSettingsWidget::setStateRecursively(QTreeWidgetItem* parent, bool enabled) +void GameCheatSettingsWidget::setStateRecursively(QStandardItem* parent, bool enabled) { - const int count = parent ? parent->childCount() : m_ui.cheatList->topLevelItemCount(); + const int count = parent ? parent->rowCount() : m_model->rowCount(); for (int i = 0; i < count; i++) { - QTreeWidgetItem* item = parent ? parent->child(i) : m_ui.cheatList->topLevelItem(i); - QVariant data = item->data(0, Qt::UserRole); + QStandardItem* item = parent ? parent->child(i, 0) : m_model->item(i, 0); + QVariant data = item->data(Qt::UserRole); if (data.isValid()) { - if ((item->checkState(0) == Qt::Checked) != enabled) + if ((item->checkState() == Qt::Checked) != enabled) { - item->setCheckState(0, enabled ? Qt::Checked : Qt::Unchecked); + item->setCheckState(enabled ? Qt::Checked : Qt::Unchecked); setCheatEnabled(data.toString().toStdString(), enabled, false); } } @@ -162,8 +192,7 @@ void GameCheatSettingsWidget::reloadList() m_dialog->getSettingsInterface()->GetStringList(Patch::CHEATS_CONFIG_SECTION, Patch::PATCH_ENABLE_CONFIG_KEY); m_parent_map.clear(); - while (m_ui.cheatList->topLevelItemCount() > 0) - delete m_ui.cheatList->takeTopLevelItem(0); + m_model->removeRows(0, m_model->rowCount()); for (const Patch::PatchInfo& pi : m_patches) { @@ -171,14 +200,12 @@ void GameCheatSettingsWidget::reloadList() (std::find(m_enabled_patches.begin(), m_enabled_patches.end(), pi.name) != m_enabled_patches.end()); const std::string_view parent_part = pi.GetNameParentPart(); - - QTreeWidgetItem* parent = getTreeWidgetParent(parent_part); - QTreeWidgetItem* item = new QTreeWidgetItem(); - populateTreeWidgetItem(item, pi, enabled); + QStandardItem* parent = getTreeViewParent(parent_part); + QList items = populateTreeViewRow(pi, enabled); if (parent) - parent->addChild(item); + parent->appendRow(items); else - m_ui.cheatList->addTopLevelItem(item); + m_model->appendRow(items); } // Hide root indicator when there's no groups, frees up some whitespace. @@ -186,13 +213,13 @@ void GameCheatSettingsWidget::reloadList() if (num_unlabelled_codes > 0) { - QTreeWidgetItem* item = new QTreeWidgetItem(); - item->setText(0, tr("%1 unlabelled patch codes will automatically activate.").arg(num_unlabelled_codes)); - m_ui.cheatList->addTopLevelItem(item); + QStandardItem* item = new QStandardItem(); + item->setText(tr("%1 unlabelled patch codes will automatically activate.").arg(num_unlabelled_codes)); + m_model->appendRow(item); } } -QTreeWidgetItem* GameCheatSettingsWidget::getTreeWidgetParent(const std::string_view parent) +QStandardItem* GameCheatSettingsWidget::getTreeViewParent(const std::string_view parent) { if (parent.empty()) return nullptr; @@ -202,37 +229,45 @@ QTreeWidgetItem* GameCheatSettingsWidget::getTreeWidgetParent(const std::string_ return it->second; std::string_view this_part = parent; - QTreeWidgetItem* parent_to_this = nullptr; + QStandardItem* parent_to_this = nullptr; const std::string_view::size_type pos = parent.rfind('\\'); if (pos != std::string::npos && pos != (parent.size() - 1)) { // go up the chain until we find the real parent, then back down - parent_to_this = getTreeWidgetParent(parent.substr(0, pos)); + parent_to_this = getTreeViewParent(parent.substr(0, pos)); this_part = parent.substr(pos + 1); } - QTreeWidgetItem* item = new QTreeWidgetItem(); - item->setText(0, QString::fromUtf8(this_part.data(), this_part.length())); + QStandardItem* item = new QStandardItem(); + item->setText(QString::fromUtf8(this_part.data(), this_part.length())); if (parent_to_this) - parent_to_this->addChild(item); + parent_to_this->appendRow(item); else - m_ui.cheatList->addTopLevelItem(item); + m_model->appendRow(item); - // Must be called after adding. - item->setExpanded(true); + m_ui.cheatList->expand(item->index()); m_parent_map.emplace(parent, item); return item; } -void GameCheatSettingsWidget::populateTreeWidgetItem(QTreeWidgetItem* item, const Patch::PatchInfo& pi, bool enabled) +QList GameCheatSettingsWidget::populateTreeViewRow(const Patch::PatchInfo& pi, bool enabled) { + QList items; + + QStandardItem* nameItem = new QStandardItem(); const std::string_view name_part = pi.GetNamePart(); - item->setFlags(item->flags() | Qt::ItemIsUserCheckable | Qt::ItemNeverHasChildren); - item->setCheckState(0, enabled ? Qt::Checked : Qt::Unchecked); - item->setData(0, Qt::UserRole, QString::fromStdString(pi.name)); + nameItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemNeverHasChildren | Qt::ItemIsEnabled); + nameItem->setCheckState(enabled ? Qt::Checked : Qt::Unchecked); + nameItem->setData(QString::fromStdString(pi.name), Qt::UserRole); if (!name_part.empty()) - item->setText(0, QString::fromUtf8(name_part.data(), name_part.length())); - item->setText(1, QString::fromStdString(pi.author)); - item->setText(2, QString::fromStdString(pi.description)); + nameItem->setText(QString::fromUtf8(name_part.data(), name_part.length())); + + QStandardItem* authorItem = new QStandardItem(QString::fromStdString(pi.author)); + QStandardItem* descriptionItem = new QStandardItem(QString::fromStdString(pi.description)); + + items.push_back(nameItem); + items.push_back(authorItem); + items.push_back(descriptionItem); + return items; } diff --git a/pcsx2-qt/Settings/GameCheatSettingsWidget.h b/pcsx2-qt/Settings/GameCheatSettingsWidget.h index 8585f1e567..c99e26bd4d 100644 --- a/pcsx2-qt/Settings/GameCheatSettingsWidget.h +++ b/pcsx2-qt/Settings/GameCheatSettingsWidget.h @@ -3,7 +3,7 @@ #pragma once -#include +#include #include "ui_GameCheatSettingsWidget.h" @@ -36,23 +36,24 @@ protected: void resizeEvent(QResizeEvent* event) override; private Q_SLOTS: - void onCheatListItemDoubleClicked(QTreeWidgetItem* item, int column); - void onCheatListItemChanged(QTreeWidgetItem* item, int column); + void onCheatListItemDoubleClicked(const QModelIndex& index); + void onCheatListItemChanged(QStandardItem* item); void onReloadClicked(); void updateListEnabled(); void reloadList(); private: - QTreeWidgetItem* getTreeWidgetParent(const std::string_view parent); - void populateTreeWidgetItem(QTreeWidgetItem* item, const Patch::PatchInfo& pi, bool enabled); + QStandardItem* getTreeViewParent(const std::string_view parent); + QList populateTreeViewRow(const Patch::PatchInfo& pi, bool enabled); void setCheatEnabled(std::string name, bool enabled, bool save_and_reload_settings); void setStateForAll(bool enabled); - void setStateRecursively(QTreeWidgetItem* parent, bool enabled); + void setStateRecursively(QStandardItem* parent, bool enabled); Ui::GameCheatSettingsWidget m_ui; SettingsWindow* m_dialog; + QStandardItemModel* m_model = nullptr; - UnorderedStringMap m_parent_map; + UnorderedStringMap m_parent_map; std::vector m_patches; std::vector m_enabled_patches; }; diff --git a/pcsx2-qt/Settings/GameCheatSettingsWidget.ui b/pcsx2-qt/Settings/GameCheatSettingsWidget.ui index 495e6409dc..7cf891396c 100644 --- a/pcsx2-qt/Settings/GameCheatSettingsWidget.ui +++ b/pcsx2-qt/Settings/GameCheatSettingsWidget.ui @@ -41,7 +41,7 @@ - + QAbstractItemView::NoEditTriggers @@ -51,21 +51,6 @@ QAbstractItemView::SelectItems - - - Name - - - - - Author - - - - - Description - -