Qt: Implement save state menus

This commit is contained in:
Connor McLaughlin 2020-02-16 00:14:04 +09:00
parent 97ea851097
commit 1ce1e016ae
4 changed files with 142 additions and 61 deletions

View File

@ -22,7 +22,6 @@ MainWindow::MainWindow(QtHostInterface* host_interface) : QMainWindow(nullptr),
m_ui.setupUi(this);
setupAdditionalUi();
connectSignals();
populateLoadSaveStateMenus(QString());
resize(750, 690);
}
@ -136,7 +135,7 @@ void MainWindow::onSystemPerformanceCountersUpdated(float speed, float fps, floa
void MainWindow::onRunningGameChanged(QString filename, QString game_code, QString game_title)
{
populateLoadSaveStateMenus(game_code);
m_host_interface->populateSaveStateMenus(game_code.toStdString().c_str(), m_ui.menuLoadState, m_ui.menuSaveState);
if (game_title.isEmpty())
setWindowTitle(tr("DuckStation"));
else
@ -367,14 +366,16 @@ void MainWindow::connectSignals()
if (!entry)
{
m_ui.statusBar->clearMessage();
populateLoadSaveStateMenus(QString());
m_host_interface->populateSaveStateMenus("", m_ui.menuLoadState, m_ui.menuSaveState);
return;
}
m_ui.statusBar->showMessage(QString::fromStdString(entry->path));
populateLoadSaveStateMenus(QString::fromStdString(entry->code));
m_host_interface->populateSaveStateMenus(entry->code.c_str(), m_ui.menuLoadState, m_ui.menuSaveState);
});
m_host_interface->populateSaveStateMenus(nullptr, m_ui.menuLoadState, m_ui.menuSaveState);
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.actionDebugShowVRAM, "Debug/ShowVRAM");
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.actionDebugDumpCPUtoVRAMCopies,
"Debug/DumpCPUToVRAMCopies");
@ -443,39 +444,6 @@ void MainWindow::updateDebugMenuGPURenderer()
}
}
void MainWindow::populateLoadSaveStateMenus(QString game_code)
{
static constexpr int NUM_SAVE_STATE_SLOTS = 10;
QMenu* const load_menu = m_ui.menuLoadState;
QMenu* const save_menu = m_ui.menuSaveState;
load_menu->clear();
save_menu->clear();
load_menu->addAction(tr("Resume State"));
load_menu->addSeparator();
for (int i = 0; i < NUM_SAVE_STATE_SLOTS; i++)
{
// TODO: Do we want to test for the existance of these on disk here?
load_menu->addAction(tr("Global Save %1 (2020-01-01 00:01:02)").arg(i + 1));
save_menu->addAction(tr("Global Save %1").arg(i + 1));
}
if (!game_code.isEmpty())
{
load_menu->addSeparator();
save_menu->addSeparator();
for (int i = 0; i < NUM_SAVE_STATE_SLOTS; i++)
{
load_menu->addAction(tr("Game Save %1 (2020-01-01 00:01:02)").arg(i + 1));
save_menu->addAction(tr("Game Save %1").arg(i + 1));
}
}
}
void MainWindow::closeEvent(QCloseEvent* event)
{
m_host_interface->powerOffSystem(true, true);

View File

@ -54,7 +54,6 @@ private:
void doSettings(SettingsDialog::Category category = SettingsDialog::Category::Count);
void updateDebugMenuCPUExecutionMode();
void updateDebugMenuGPURenderer();
void populateLoadSaveStateMenus(QString game_code);
Ui::MainWindow m_ui;

View File

@ -11,8 +11,10 @@
#include "qtsettingsinterface.h"
#include "qtutils.h"
#include <QtCore/QCoreApplication>
#include <QtCore/QDateTime>
#include <QtCore/QDebug>
#include <QtCore/QEventLoop>
#include <QtWidgets/QMenu>
#include <QtWidgets/QMessageBox>
#include <memory>
Log_SetChannel(QtHostInterface);
@ -354,15 +356,22 @@ std::vector<QtHostInterface::HotkeyInfo> QtHostInterface::getHotkeyList() const
{QStringLiteral("DecreaseResolutionScale"), QStringLiteral("Decrease Resolution Scale"),
QStringLiteral("Graphics")}};
for (u32 i = 1; i <= NUM_SAVE_STATE_HOTKEYS; i++)
for (u32 global_i = 0; global_i < 2; global_i++)
{
hotkeys.push_back(
{QStringLiteral("LoadState%1").arg(i), QStringLiteral("Load State %1").arg(i), QStringLiteral("Save States")});
const bool global = ConvertToBoolUnchecked(global_i);
const u32 count = global ? GLOBAL_SAVE_STATE_SLOTS : PER_GAME_SAVE_STATE_SLOTS;
for (u32 i = 1; i <= count; i++)
{
hotkeys.push_back({QStringLiteral("Load%1State%2").arg(global ? "Global" : "Game").arg(i),
QStringLiteral("Load %1 State %2").arg(global ? tr("Global") : tr("Game")).arg(i),
QStringLiteral("Save States")});
}
for (u32 i = 1; i <= NUM_SAVE_STATE_HOTKEYS; i++)
for (u32 slot = 1; slot <= count; slot++)
{
hotkeys.push_back(
{QStringLiteral("SaveState%1").arg(i), QStringLiteral("Save State %1").arg(i), QStringLiteral("Save States")});
hotkeys.push_back({QStringLiteral("Save%1State%2").arg(global ? "Global" : "Game").arg(slot),
QStringLiteral("Save %1 State %2").arg(global ? tr("Global") : tr("Game")).arg(slot),
QStringLiteral("Save States")});
}
}
return hotkeys;
@ -408,18 +417,22 @@ void QtHostInterface::updateHotkeyInputMap()
ModifyResolutionScale(-1);
});
for (u32 i = 1; i <= NUM_SAVE_STATE_HOTKEYS; i++)
for (u32 global_i = 0; global_i < 2; global_i++)
{
hk(QStringLiteral("LoadState%1").arg(i), [this, i](bool pressed) {
const bool global = ConvertToBoolUnchecked(global_i);
const u32 count = global ? GLOBAL_SAVE_STATE_SLOTS : PER_GAME_SAVE_STATE_SLOTS;
for (u32 slot = 1; slot <= count; slot++)
{
hk(QStringLiteral("Load%1State%2").arg(global ? "Global" : "Game").arg(slot), [this, global, slot](bool pressed) {
if (!pressed)
HostInterface::LoadState(StringUtil::StdStringFromFormat("savestate_%u.bin", i).c_str());
loadState(global, slot);
});
hk(QStringLiteral("SaveState%1").arg(i), [this, i](bool pressed) {
hk(QStringLiteral("Save%1State%2").arg(global ? "Global" : "Game").arg(slot), [this, global, slot](bool pressed) {
if (!pressed)
HostInterface::SaveState(StringUtil::StdStringFromFormat("savestate_%u.bin", i).c_str());
saveState(global, slot);
});
}
}
}
void QtHostInterface::addButtonToInputMap(const QString& binding, InputButtonHandler handler)
@ -555,6 +568,106 @@ void QtHostInterface::createAudioStream()
}
}
void QtHostInterface::populateSaveStateMenus(const char* game_code, QMenu* load_menu, QMenu* save_menu)
{
const std::vector<SaveStateInfo> available_states(GetAvailableSaveStates(game_code));
load_menu->clear();
if (!available_states.empty())
{
bool last_global = available_states.front().global;
for (const SaveStateInfo& ssi : available_states)
{
const s32 slot = ssi.slot;
const bool global = ssi.global;
const QDateTime timestamp(QDateTime::fromSecsSinceEpoch(static_cast<qint64>(ssi.timestamp)));
const QString path(QString::fromStdString(ssi.path));
QString title = tr("%1 Save %2 (%3)")
.arg(global ? tr("Global") : tr("Game"))
.arg(slot)
.arg(timestamp.toString(Qt::SystemLocaleShortDate));
if (global != last_global)
{
load_menu->addSeparator();
last_global = global;
}
QAction* action = load_menu->addAction(title);
connect(action, &QAction::triggered, [this, path]() { loadState(path); });
}
}
save_menu->clear();
if (game_code && std::strlen(game_code) > 0)
{
for (s32 i = 1; i <= PER_GAME_SAVE_STATE_SLOTS; i++)
{
QAction* action = save_menu->addAction(tr("Game Save %1").arg(i));
connect(action, &QAction::triggered, [this, i]() { saveState(i, false); });
}
save_menu->addSeparator();
}
for (s32 i = 1; i <= GLOBAL_SAVE_STATE_SLOTS; i++)
{
QAction* action = save_menu->addAction(tr("Global Save %1").arg(i));
connect(action, &QAction::triggered, [this, i]() { saveState(i, true); });
}
}
void QtHostInterface::loadState(QString filename)
{
if (!isOnWorkerThread())
{
QMetaObject::invokeMethod(this, "loadState", Qt::QueuedConnection, Q_ARG(QString, filename));
return;
}
if (m_system)
LoadState(filename.toStdString().c_str());
else
doBootSystem(QString(), filename);
}
void QtHostInterface::loadState(bool global, qint32 slot)
{
if (!isOnWorkerThread())
{
QMetaObject::invokeMethod(this, "loadState", Qt::QueuedConnection, Q_ARG(bool, global), Q_ARG(qint32, slot));
return;
}
if (m_system)
{
LoadState(slot, global);
return;
}
if (!global)
{
// can't load a non-global system without a game code
return;
}
loadState(QString::fromStdString(GetGlobalSaveStateFileName(slot)));
}
void QtHostInterface::saveState(bool global, qint32 slot, bool block_until_done /* = false */)
{
if (!isOnWorkerThread())
{
QMetaObject::invokeMethod(this, "saveState", block_until_done ? Qt::BlockingQueuedConnection : Qt::QueuedConnection,
Q_ARG(bool, global), Q_ARG(qint32, slot), Q_ARG(bool, block_until_done));
return;
}
if (m_system)
SaveState(global, slot);
}
void QtHostInterface::createThread()
{
m_original_thread = QThread::currentThread();

View File

@ -16,6 +16,7 @@
class ByteStream;
class QEventLoop;
class QMenu;
class QWidget;
class GameList;
@ -61,6 +62,8 @@ public:
};
std::vector<HotkeyInfo> getHotkeyList() const;
void populateSaveStateMenus(const char* game_code, QMenu* load_menu, QMenu* save_menu);
Q_SIGNALS:
void errorReported(QString message);
void messageReported(QString message);
@ -81,6 +84,9 @@ public Q_SLOTS:
void resetSystem();
void pauseSystem(bool paused);
void changeDisc(QString new_disc_filename);
void loadState(QString filename);
void loadState(bool global, qint32 slot);
void saveState(bool global, qint32 slot, bool block_until_done = false);
private Q_SLOTS:
void doStopThread();
@ -97,11 +103,6 @@ protected:
private:
using InputButtonHandler = std::function<void(bool)>;
enum : u32
{
NUM_SAVE_STATE_HOTKEYS = 8
};
class Thread : public QThread
{
public: