diff --git a/CHANGES b/CHANGES index dd123c3d5..9eba68822 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,7 @@ Features: - Settings window - Bilinear resampling option - Add option to skip BIOS start screen + - List of recently opened games Bugfixes: - Qt: Fix issue with set frame sizes being the wrong height - Qt: Fix emulator crashing when full screen if a game is not running diff --git a/src/platform/qt/ConfigController.cpp b/src/platform/qt/ConfigController.cpp index 86616864a..938002487 100644 --- a/src/platform/qt/ConfigController.cpp +++ b/src/platform/qt/ConfigController.cpp @@ -131,7 +131,7 @@ void ConfigController::updateOption(const char* key) { m_optionSet[optionName]->setValue(GBAConfigGetValue(&m_config, key)); } -QString ConfigController::getOption(const char* key) { +QString ConfigController::getOption(const char* key) const { return QString(GBAConfigGetValue(&m_config, key)); } @@ -176,6 +176,35 @@ void ConfigController::setOption(const char* key, const QVariant& value) { setOption(key, stringValue.toLocal8Bit().constData()); } +QList ConfigController::getMRU() const { + QList mru; + for (int i = 0; i < MRU_LIST_SIZE; ++i) { + char mruName[7]; + snprintf(mruName, sizeof(mruName) - 1, "mru.%i", i); + mruName[sizeof(mruName) - 1] = '\0'; + QString item = getOption(mruName); + if (item.isNull()) { + continue; + } + mru.append(item); + } + return mru; +} + +void ConfigController::setMRU(const QList& mru) { + int i = 0; + for (const QString& item : mru) { + char mruName[7]; + snprintf(mruName, sizeof(mruName) - 1, "mru.%i", i); + mruName[sizeof(mruName) - 1] = '\0'; + setOption(mruName, item); + ++i; + if (i >= MRU_LIST_SIZE) { + break; + } + } +} + void ConfigController::write() { GBAConfigSave(&m_config); } diff --git a/src/platform/qt/ConfigController.h b/src/platform/qt/ConfigController.h index 95b8ba44e..f2e329765 100644 --- a/src/platform/qt/ConfigController.h +++ b/src/platform/qt/ConfigController.h @@ -57,6 +57,7 @@ Q_OBJECT public: constexpr static const char* const PORT = "qt"; + static const int MRU_LIST_SIZE = 10; ConfigController(QObject* parent = nullptr); ~ConfigController(); @@ -67,7 +68,10 @@ public: ConfigOption* addOption(const char* key); void updateOption(const char* key); - QString getOption(const char* key); + QString getOption(const char* key) const; + + QList getMRU() const; + void setMRU(const QList& mru); public slots: void setOption(const char* key, bool value); diff --git a/src/platform/qt/Window.cpp b/src/platform/qt/Window.cpp index 8093df46e..acaf95df1 100644 --- a/src/platform/qt/Window.cpp +++ b/src/platform/qt/Window.cpp @@ -45,6 +45,7 @@ Window::Window(ConfigController* config, QWidget* parent) #ifdef USE_GDB_STUB , m_gdbController(nullptr) #endif + , m_mruMenu(nullptr) { setWindowTitle(PROJECT_NAME); setFocusPolicy(Qt::StrongFocus); @@ -157,6 +158,9 @@ void Window::loadConfig() { m_screenWidget->setSizeHint(QSize(opts->width, opts->height)); } + m_mruFiles = m_config->getMRU(); + updateMRU(); + m_inputController.setConfiguration(m_config); } @@ -313,6 +317,7 @@ void Window::gameStarted(GBAThread* context) { foreach (QAction* action, m_gameActions) { action->setDisabled(false); } + appendMRU(context->fname); char title[13] = { '\0' }; GBAGetGameTitle(context->gba, title); setWindowTitle(tr(PROJECT_NAME " - %1").arg(title)); @@ -402,6 +407,8 @@ void Window::setupMenu(QMenuBar* menubar) { fileMenu->addAction(tr("Load &BIOS..."), this, SLOT(selectBIOS())); fileMenu->addAction(tr("Load &patch..."), this, SLOT(selectPatch())); + m_mruMenu = fileMenu->addMenu(tr("Recent")); + fileMenu->addSeparator(); QAction* loadState = new QAction(tr("&Load state"), fileMenu); @@ -629,6 +636,36 @@ void Window::detachWidget(QWidget* widget) { m_screenWidget->layout()->removeWidget(widget); } +void Window::appendMRU(const QString& fname) { + int index = m_mruFiles.indexOf(fname); + if (index >= 0) { + m_mruFiles.removeAt(index); + } + m_mruFiles.prepend(fname); + while (m_mruFiles.size() > ConfigController::MRU_LIST_SIZE) { + m_mruFiles.removeLast(); + } + updateMRU(); +} + +void Window::updateMRU() { + if (!m_mruMenu) { + return; + } + m_mruMenu->clear(); + int i = 0; + for (const QString& file : m_mruFiles) { + QAction* item = new QAction(file, m_mruMenu); + item->setShortcut(QString("Ctrl+%1").arg(i)); + connect(item, &QAction::triggered, [this, file]() { m_controller->loadGame(file); }); + m_mruMenu->addAction(item); + ++i; + } + m_config->setMRU(m_mruFiles); + m_config->write(); + m_mruMenu->setEnabled(i > 0); +} + WindowBackground::WindowBackground(QWidget* parent) : QLabel(parent) { diff --git a/src/platform/qt/Window.h b/src/platform/qt/Window.h index e2dd462fd..4b1ed00c5 100644 --- a/src/platform/qt/Window.h +++ b/src/platform/qt/Window.h @@ -104,6 +104,9 @@ private: void attachWidget(QWidget* widget); void detachWidget(QWidget* widget); + void appendMRU(const QString& fname); + void updateMRU(); + GameController* m_controller; Display* m_display; QList m_gameActions; @@ -115,6 +118,8 @@ private: InputController m_inputController; QList m_frameList; QTimer m_fpsTimer; + QList m_mruFiles; + QMenu* m_mruMenu; #ifdef USE_FFMPEG VideoView* m_videoView;