Merge pull request #14344 from TryTwo/cheatsearch_work2

Cheat Search: Add ability to delete items and fix duplicate commands
This commit is contained in:
Dentomologist 2026-02-19 14:36:54 -08:00 committed by GitHub
commit 02db73c8dc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 54 additions and 5 deletions

View File

@ -283,6 +283,15 @@ void Cheats::CheatSearchSession<T>::ResetResults()
m_search_results.clear();
}
template <typename T>
void Cheats::CheatSearchSession<T>::RemoveResult(size_t index)
{
if (index < m_search_results.size())
{
m_search_results.erase(m_search_results.begin() + index);
}
}
template <typename T>
static std::function<bool(const T& new_value)>
MakeCompareFunctionForSpecificValue(Cheats::CompareType op, const T& old_value)

View File

@ -168,6 +168,9 @@ public:
virtual bool WriteValue(const Core::CPUThreadGuard& guard, std::span<u32> addresses) const = 0;
// User can delete a search result.
virtual void RemoveResult(size_t index) = 0;
// Create a complete copy of this search session.
virtual std::unique_ptr<CheatSearchSessionBase> Clone() const = 0;
@ -195,6 +198,7 @@ public:
bool SetValueFromString(const std::string& value_as_string, bool force_parse_as_hex) override;
void ResetResults() override;
void RemoveResult(size_t index) override;
SearchErrorCode RunSearch(const Core::CPUThreadGuard& guard) override;
size_t GetMemoryRangeCount() const override;

View File

@ -3,7 +3,9 @@
#include "DolphinQt/CheatSearchWidget.h"
#include <algorithm>
#include <optional>
#include <ranges>
#include <string>
#include <unordered_map>
#include <utility>
@ -224,6 +226,7 @@ void CheatSearchWidget::CreateWidgets()
m_address_table = new QTableWidget();
m_address_table->setContextMenuPolicy(Qt::CustomContextMenu);
m_address_table->setSelectionBehavior(QAbstractItemView::SelectRows);
m_info_label_1 = new QLabel(tr("Waiting for first scan..."));
m_info_label_2 = new QLabel();
@ -483,6 +486,8 @@ void CheatSearchWidget::OnAddressTableContextMenu()
if (m_address_table->selectedItems().isEmpty())
return;
std::vector<const QTableWidgetItem*> selected_items = GetSelectedAddressTableItems();
QMenu* menu = new QMenu(this);
menu->setAttribute(Qt::WA_DeleteOnClose, true);
@ -491,8 +496,8 @@ void CheatSearchWidget::OnAddressTableContextMenu()
const u32 address = item->data(ADDRESS_TABLE_ADDRESS_ROLE).toUInt();
emit ShowMemory(address);
});
menu->addAction(tr("Add to watch"), this, [this] {
for (auto* const item : m_address_table->selectedItems())
menu->addAction(tr("Add to watch"), this, [this, selected_items] {
for (auto* const item : selected_items)
{
const u32 address = item->data(ADDRESS_TABLE_ADDRESS_ROLE).toUInt();
const QString name = QStringLiteral("mem_%1").arg(address, 8, 16, QLatin1Char('0'));
@ -501,6 +506,15 @@ void CheatSearchWidget::OnAddressTableContextMenu()
});
menu->addAction(tr("Generate Action Replay Code(s)"), this, &CheatSearchWidget::GenerateARCodes);
menu->addAction(tr("Write value"), this, &CheatSearchWidget::WriteValue);
menu->addAction(tr("Delete Address"), this, [this, selected_items] {
// Process in reverse so removal won't change the index of items about to be processed.
for (auto* const item : selected_items | std::views::reverse)
{
const u32 index = item->data(ADDRESS_TABLE_RESULT_INDEX_ROLE).toUInt();
m_last_value_session->RemoveResult(index);
}
RecreateGUITable();
});
menu->exec(QCursor::pos());
}
@ -533,7 +547,7 @@ void CheatSearchWidget::GenerateARCodes()
bool had_multiple_errors = false;
std::optional<Cheats::GenerateActionReplayCodeErrorCode> error_code;
for (auto* const item : m_address_table->selectedItems())
for (auto* const item : GetSelectedAddressTableItems())
{
const u32 index = item->data(ADDRESS_TABLE_RESULT_INDEX_ROLE).toUInt();
const auto result = Cheats::GenerateActionReplayCode(*m_last_value_session, index);
@ -600,9 +614,9 @@ void CheatSearchWidget::WriteValue()
return;
}
auto items = m_address_table->selectedItems();
auto items = GetSelectedAddressTableItems();
std::vector<u32> addresses(items.size());
std::transform(items.begin(), items.end(), addresses.begin(), [](QTableWidgetItem* item) {
std::transform(items.begin(), items.end(), addresses.begin(), [](const QTableWidgetItem* item) {
return item->data(ADDRESS_TABLE_ADDRESS_ROLE).toUInt();
});
Core::CPUThreadGuard guard{m_system};
@ -610,6 +624,7 @@ void CheatSearchWidget::WriteValue()
{
m_info_label_1->setText(tr("There was an error writing (some) values."));
}
UpdateTableAllCurrentValues(UpdateSource::User);
}
size_t CheatSearchWidget::GetTableRowCount() const
@ -638,6 +653,26 @@ void CheatSearchWidget::RefreshGUICurrentValues(const size_t begin_index, const
}
}
const std::vector<const QTableWidgetItem*> CheatSearchWidget::GetSelectedAddressTableItems() const
{
// Don't process each selectedItems(), as it can produce duplicate commands for one address when
// multiple items in the same row are selected. Instead, uses rows and gets one item from each
// row. All row items have identical data.
auto selected_rows = m_address_table->selectionModel()->selectedRows();
// Ascending address order.
std::sort(selected_rows.begin(), selected_rows.end(),
[](const QModelIndex& a, const QModelIndex& b) { return a.row() < b.row(); });
std::vector<const QTableWidgetItem*> selected_items;
for (const auto& index : selected_rows)
{
const int row = index.row();
selected_items.push_back(m_address_table->item(row, 0));
}
return selected_items;
}
void CheatSearchWidget::RecreateGUITable()
{
const QSignalBlocker blocker(m_address_table);

View File

@ -75,6 +75,7 @@ private:
int GetVisibleRowsBeginIndex() const;
int GetVisibleRowsEndIndex() const;
size_t GetTableRowCount() const;
const std::vector<const QTableWidgetItem*> GetSelectedAddressTableItems() const;
Core::System& m_system;