mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2026-04-26 18:07:33 -05:00
Cheat Search: Add ability to delete items and fix duplicate commands when multiple row items are selected.
Changes it to select rows then extracts one item from each row to operate on. The behavior and data across each item in a row was already identical, so using rows doesn't change anything.
This commit is contained in:
parent
ea80945912
commit
954219f781
|
|
@ -283,6 +283,15 @@ void Cheats::CheatSearchSession<T>::ResetResults()
|
||||||
m_search_results.clear();
|
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>
|
template <typename T>
|
||||||
static std::function<bool(const T& new_value)>
|
static std::function<bool(const T& new_value)>
|
||||||
MakeCompareFunctionForSpecificValue(Cheats::CompareType op, const T& old_value)
|
MakeCompareFunctionForSpecificValue(Cheats::CompareType op, const T& old_value)
|
||||||
|
|
|
||||||
|
|
@ -168,6 +168,9 @@ public:
|
||||||
|
|
||||||
virtual bool WriteValue(const Core::CPUThreadGuard& guard, std::span<u32> addresses) const = 0;
|
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.
|
// Create a complete copy of this search session.
|
||||||
virtual std::unique_ptr<CheatSearchSessionBase> Clone() const = 0;
|
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;
|
bool SetValueFromString(const std::string& value_as_string, bool force_parse_as_hex) override;
|
||||||
|
|
||||||
void ResetResults() override;
|
void ResetResults() override;
|
||||||
|
void RemoveResult(size_t index) override;
|
||||||
SearchErrorCode RunSearch(const Core::CPUThreadGuard& guard) override;
|
SearchErrorCode RunSearch(const Core::CPUThreadGuard& guard) override;
|
||||||
|
|
||||||
size_t GetMemoryRangeCount() const override;
|
size_t GetMemoryRangeCount() const override;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,9 @@
|
||||||
|
|
||||||
#include "DolphinQt/CheatSearchWidget.h"
|
#include "DolphinQt/CheatSearchWidget.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <ranges>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
@ -224,6 +226,7 @@ void CheatSearchWidget::CreateWidgets()
|
||||||
|
|
||||||
m_address_table = new QTableWidget();
|
m_address_table = new QTableWidget();
|
||||||
m_address_table->setContextMenuPolicy(Qt::CustomContextMenu);
|
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_1 = new QLabel(tr("Waiting for first scan..."));
|
||||||
m_info_label_2 = new QLabel();
|
m_info_label_2 = new QLabel();
|
||||||
|
|
@ -483,6 +486,8 @@ void CheatSearchWidget::OnAddressTableContextMenu()
|
||||||
if (m_address_table->selectedItems().isEmpty())
|
if (m_address_table->selectedItems().isEmpty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
std::vector<const QTableWidgetItem*> selected_items = GetSelectedAddressTableItems();
|
||||||
|
|
||||||
QMenu* menu = new QMenu(this);
|
QMenu* menu = new QMenu(this);
|
||||||
menu->setAttribute(Qt::WA_DeleteOnClose, true);
|
menu->setAttribute(Qt::WA_DeleteOnClose, true);
|
||||||
|
|
||||||
|
|
@ -491,8 +496,8 @@ void CheatSearchWidget::OnAddressTableContextMenu()
|
||||||
const u32 address = item->data(ADDRESS_TABLE_ADDRESS_ROLE).toUInt();
|
const u32 address = item->data(ADDRESS_TABLE_ADDRESS_ROLE).toUInt();
|
||||||
emit ShowMemory(address);
|
emit ShowMemory(address);
|
||||||
});
|
});
|
||||||
menu->addAction(tr("Add to watch"), this, [this] {
|
menu->addAction(tr("Add to watch"), this, [this, selected_items] {
|
||||||
for (auto* const item : m_address_table->selectedItems())
|
for (auto* const item : selected_items)
|
||||||
{
|
{
|
||||||
const u32 address = item->data(ADDRESS_TABLE_ADDRESS_ROLE).toUInt();
|
const u32 address = item->data(ADDRESS_TABLE_ADDRESS_ROLE).toUInt();
|
||||||
const QString name = QStringLiteral("mem_%1").arg(address, 8, 16, QLatin1Char('0'));
|
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("Generate Action Replay Code(s)"), this, &CheatSearchWidget::GenerateARCodes);
|
||||||
menu->addAction(tr("Write value"), this, &CheatSearchWidget::WriteValue);
|
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());
|
menu->exec(QCursor::pos());
|
||||||
}
|
}
|
||||||
|
|
@ -533,7 +547,7 @@ void CheatSearchWidget::GenerateARCodes()
|
||||||
bool had_multiple_errors = false;
|
bool had_multiple_errors = false;
|
||||||
std::optional<Cheats::GenerateActionReplayCodeErrorCode> error_code;
|
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 u32 index = item->data(ADDRESS_TABLE_RESULT_INDEX_ROLE).toUInt();
|
||||||
const auto result = Cheats::GenerateActionReplayCode(*m_last_value_session, index);
|
const auto result = Cheats::GenerateActionReplayCode(*m_last_value_session, index);
|
||||||
|
|
@ -600,9 +614,9 @@ void CheatSearchWidget::WriteValue()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto items = m_address_table->selectedItems();
|
auto items = GetSelectedAddressTableItems();
|
||||||
std::vector<u32> addresses(items.size());
|
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();
|
return item->data(ADDRESS_TABLE_ADDRESS_ROLE).toUInt();
|
||||||
});
|
});
|
||||||
Core::CPUThreadGuard guard{m_system};
|
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."));
|
m_info_label_1->setText(tr("There was an error writing (some) values."));
|
||||||
}
|
}
|
||||||
|
UpdateTableAllCurrentValues(UpdateSource::User);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t CheatSearchWidget::GetTableRowCount() const
|
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()
|
void CheatSearchWidget::RecreateGUITable()
|
||||||
{
|
{
|
||||||
const QSignalBlocker blocker(m_address_table);
|
const QSignalBlocker blocker(m_address_table);
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@ private:
|
||||||
int GetVisibleRowsBeginIndex() const;
|
int GetVisibleRowsBeginIndex() const;
|
||||||
int GetVisibleRowsEndIndex() const;
|
int GetVisibleRowsEndIndex() const;
|
||||||
size_t GetTableRowCount() const;
|
size_t GetTableRowCount() const;
|
||||||
|
const std::vector<const QTableWidgetItem*> GetSelectedAddressTableItems() const;
|
||||||
|
|
||||||
Core::System& m_system;
|
Core::System& m_system;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user