dolphin/Source/Core/DolphinQt/CheatsManager.cpp
Dentomologist fdb7328c73 CheatSearch: Update Current Values at end of frame
At the end of each frame automatically update the Current Value for
visible table rows in the selected and visible CheatSearchWidget (if
any). Also update all Current Values in all CheatSearchWidgets when the
State changes to Paused.

Only updating visible table rows serves to minimize the performance cost
of this feature. If the user scrolls to an un-updated cell it will
promptly be updated by either the next VIEndFieldEvent or the State
transitioning to Paused.
2023-10-31 17:34:31 -07:00

190 lines
5.7 KiB
C++

// Copyright 2018 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "DolphinQt/CheatsManager.h"
#include <functional>
#include <QDialogButtonBox>
#include <QVBoxLayout>
#include "Core/ActionReplay.h"
#include "Core/CheatSearch.h"
#include "Core/ConfigManager.h"
#include "Core/Core.h"
#include "UICommon/GameFile.h"
#include "DolphinQt/CheatSearchFactoryWidget.h"
#include "DolphinQt/CheatSearchWidget.h"
#include "DolphinQt/Config/ARCodeWidget.h"
#include "DolphinQt/Config/GeckoCodeWidget.h"
#include "DolphinQt/QtUtils/PartiallyClosableTabWidget.h"
#include "DolphinQt/Settings.h"
#include "VideoCommon/VideoEvents.h"
CheatsManager::CheatsManager(QWidget* parent) : QDialog(parent)
{
setWindowTitle(tr("Cheats Manager"));
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this,
&CheatsManager::OnStateChanged);
CreateWidgets();
ConnectWidgets();
RefreshCodeTabs(Core::GetState(), true);
auto& settings = Settings::GetQSettings();
restoreGeometry(settings.value(QStringLiteral("cheatsmanager/geometry")).toByteArray());
}
CheatsManager::~CheatsManager()
{
auto& settings = Settings::GetQSettings();
settings.setValue(QStringLiteral("cheatsmanager/geometry"), saveGeometry());
}
void CheatsManager::OnStateChanged(Core::State state)
{
RefreshCodeTabs(state, false);
if (state == Core::State::Paused)
UpdateAllCheatSearchWidgetCurrentValues();
}
void CheatsManager::OnFrameEnd()
{
if (!isVisible())
return;
auto* const selected_cheat_search_widget =
qobject_cast<CheatSearchWidget*>(m_tab_widget->currentWidget());
if (selected_cheat_search_widget != nullptr)
selected_cheat_search_widget->UpdateTableVisibleCurrentValues();
}
void CheatsManager::UpdateAllCheatSearchWidgetCurrentValues()
{
for (int i = 0; i < m_tab_widget->count(); ++i)
{
auto* const cheat_search_widget = qobject_cast<CheatSearchWidget*>(m_tab_widget->widget(i));
if (cheat_search_widget != nullptr)
cheat_search_widget->UpdateTableAllCurrentValues();
}
}
void CheatsManager::RegisterAfterFrameEventCallback()
{
m_VI_end_field_event = VIEndFieldEvent::Register([this] { OnFrameEnd(); }, "CheatsManager");
}
void CheatsManager::RemoveAfterFrameEventCallback()
{
m_VI_end_field_event.reset();
}
void CheatsManager::hideEvent(QHideEvent* event)
{
RemoveAfterFrameEventCallback();
}
void CheatsManager::showEvent(QShowEvent* event)
{
RegisterAfterFrameEventCallback();
}
void CheatsManager::RefreshCodeTabs(Core::State state, bool force)
{
if (!force && (state == Core::State::Starting || state == Core::State::Stopping))
return;
const auto& game_id =
state != Core::State::Uninitialized ? SConfig::GetInstance().GetGameID() : std::string();
const auto& game_tdb_id = SConfig::GetInstance().GetGameTDBID();
const u16 revision = SConfig::GetInstance().GetRevision();
if (!force && m_game_id == game_id && m_game_tdb_id == game_tdb_id && m_revision == revision)
return;
m_game_id = game_id;
m_game_tdb_id = game_tdb_id;
m_revision = revision;
if (m_ar_code)
{
const int tab_index = m_tab_widget->indexOf(m_ar_code);
if (tab_index != -1)
m_tab_widget->removeTab(tab_index);
m_ar_code->deleteLater();
m_ar_code = nullptr;
}
if (m_gecko_code)
{
const int tab_index = m_tab_widget->indexOf(m_gecko_code);
if (tab_index != -1)
m_tab_widget->removeTab(tab_index);
m_gecko_code->deleteLater();
m_gecko_code = nullptr;
}
m_ar_code = new ARCodeWidget(m_game_id, m_revision, false);
m_gecko_code = new GeckoCodeWidget(m_game_id, m_game_tdb_id, m_revision, false);
m_tab_widget->insertTab(0, m_ar_code, tr("AR Code"));
m_tab_widget->insertTab(1, m_gecko_code, tr("Gecko Codes"));
m_tab_widget->setTabUnclosable(0);
m_tab_widget->setTabUnclosable(1);
connect(m_ar_code, &ARCodeWidget::OpenGeneralSettings, this, &CheatsManager::OpenGeneralSettings);
connect(m_gecko_code, &GeckoCodeWidget::OpenGeneralSettings, this,
&CheatsManager::OpenGeneralSettings);
}
void CheatsManager::CreateWidgets()
{
m_tab_widget = new PartiallyClosableTabWidget;
m_button_box = new QDialogButtonBox(QDialogButtonBox::Close);
m_cheat_search_new = new CheatSearchFactoryWidget();
m_tab_widget->addTab(m_cheat_search_new, tr("Start New Cheat Search"));
m_tab_widget->setTabUnclosable(0);
auto* layout = new QVBoxLayout;
layout->addWidget(m_tab_widget);
layout->addWidget(m_button_box);
setLayout(layout);
}
void CheatsManager::OnNewSessionCreated(const Cheats::CheatSearchSessionBase& session)
{
auto* w = new CheatSearchWidget(session.Clone());
const int tab_index = m_tab_widget->addTab(w, tr("Cheat Search"));
w->connect(w, &CheatSearchWidget::ActionReplayCodeGenerated, this,
[this](const ActionReplay::ARCode& ar_code) {
if (m_ar_code)
m_ar_code->AddCode(ar_code);
});
w->connect(w, &CheatSearchWidget::ShowMemory, [this](u32 address) { emit ShowMemory(address); });
w->connect(w, &CheatSearchWidget::RequestWatch,
[this](QString name, u32 address) { emit RequestWatch(name, address); });
m_tab_widget->setCurrentIndex(tab_index);
}
void CheatsManager::OnTabCloseRequested(int index)
{
auto* w = m_tab_widget->widget(index);
if (w)
w->deleteLater();
}
void CheatsManager::ConnectWidgets()
{
connect(m_button_box, &QDialogButtonBox::rejected, this, &QDialog::reject);
connect(m_cheat_search_new, &CheatSearchFactoryWidget::NewSessionCreated, this,
&CheatsManager::OnNewSessionCreated);
connect(m_tab_widget, &QTabWidget::tabCloseRequested, this, &CheatsManager::OnTabCloseRequested);
}