diff --git a/CMakeLists.txt b/CMakeLists.txt index 43844a7..d72cd4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -171,7 +171,9 @@ qt_add_executable(kaizen src/frontend/Settings/InputSettings.hpp src/frontend/Settings/InputSettings.cpp src/utils/Options.cpp - src/utils/File.cpp) + src/utils/File.cpp + src/frontend/RomsList.hpp + src/frontend/RomsList.cpp) if (WIN32) set(MIO_LIB mio::mio_full_winapi) diff --git a/src/frontend/KaizenGui.cpp b/src/frontend/KaizenGui.cpp index e9eaa1c..a921f01 100644 --- a/src/frontend/KaizenGui.cpp +++ b/src/frontend/KaizenGui.cpp @@ -11,55 +11,6 @@ #include #include -RomsListTable::RomsListTable() { - verticalHeader()->hide(); - horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive); - setSizePolicy({QSizePolicy::Maximum, QSizePolicy::Maximum}); - setSelectionMode(QAbstractItemView::SingleSelection); - setSelectionBehavior(QAbstractItemView::SelectRows); - setEditTriggers(QAbstractItemView::NoEditTriggers); - setSortingEnabled(true); - setColumnCount(4); - setHorizontalHeaderItem(0, new QTableWidgetItem("Name")); - setHorizontalHeaderItem(1, new QTableWidgetItem("Regions")); - setHorizontalHeaderItem(2, new QTableWidgetItem("Last played")); - setHorizontalHeaderItem(3, new QTableWidgetItem("Time played")); - - std::jthread([&] { - populate(Options::GetRomsPath()); - emit populateFinished(); - }); -} - -void RomsListTable::populate(const std::string &romsPath) { - if (!romsPath.empty()) { - for (const auto &file : fs::recursive_directory_iterator{romsPath}) { - if (!file.is_regular_file()) - continue; - - auto filename = file.path().string(); - - bool isPlain = std::ranges::any_of(std::array{".n64", ".z64", ".v64"}, - [&](const std::string &ext) { return file.path().extension() == ext; }); - - bool isArchive = - std::ranges::any_of(std::array{".zip", ".7z", ".rar", ".tar"}, - [&](const std::string &ext) { return file.path().extension() == ext; }); - - if (!isArchive && !isPlain) - continue; - - auto rom = n64::Mem::LoadROM(isArchive, filename); - auto regions = n64::GameDB::match(rom); - - if (rom.gameNameDB.empty()) - rom.gameNameDB = fs::path(filename).stem().string(); - - romsList.push_back( - {rom.header.countryCode, rom.header.version, filename, rom.gameNameDB, regions, "Never", "0h 00m 00s"}); - } - } -} KaizenGui::KaizenGui() noexcept : QMainWindow(nullptr), settings(QSettings::UserScope) { SDL_InitSubSystem(SDL_INIT_GAMEPAD); @@ -67,6 +18,10 @@ KaizenGui::KaizenGui() noexcept : QMainWindow(nullptr), settings(QSettings::User SDL_AddGamepadMapping(gamecontrollerdb_str); hide(); + settingsWindow = new SettingsWindow(); + + romsListTable = new RomsListTable(settingsWindow->general); + vulkanWidget = new RenderWidget(); vulkanWidget->hide(); @@ -74,16 +29,16 @@ KaizenGui::KaizenGui() noexcept : QMainWindow(nullptr), settings(QSettings::User centralWidget->addWidget(romsListTable); centralWidget->addWidget(romPathNotSet); - populateRomsListThread = QThread::create([&] { - populateRomsList(Options::GetRomsPath()); - emit populateRomsListFinished(); - }); + if (Options::GetRomsPath().empty()) + centralWidget->setCurrentWidget(romPathNotSet); + else + centralWidget->setCurrentWidget(romsListTable); - populateRomsListThread->start(); + connect(romsListTable, &RomsListTable::cleared, this, [&] { centralWidget->setCurrentWidget(romPathNotSet); }); - connect(this, &KaizenGui::populateRomsListFinished, this, [&] { - for (int i = 0; i < romsList.size(); i++) { - const auto &[countryCode, version, _, name, regions, lastPlayed, timePlayed] = romsList[i]; + connect(romsListTable, &RomsListTable::populateFinished, this, [&] { + for (int i = 0; i < romsListTable->size(); i++) { + const auto &[countryCode, version, _, name, regions, lastPlayed, timePlayed] = (*romsListTable)[i]; romsListTable->insertRow(i); romsListTable->setItem( @@ -95,11 +50,12 @@ KaizenGui::KaizenGui() noexcept : QMainWindow(nullptr), settings(QSettings::User romsListTable->setItem(i, 2, new QTableWidgetItem(lastPlayed.c_str())); romsListTable->setItem(i, 3, new QTableWidgetItem(timePlayed.c_str())); } + romsListTable->resizeRowsToContents(); + centralWidget->setCurrentWidget(romsListTable); }); - connect(this, &KaizenGui::romsListNeedsPopulation, this, [&] { populateRomsListThread->start(); }); - - connect(romsListTable, &QTableWidget::cellDoubleClicked, this, [&](int row, int) { LoadROM(romsList[row].path); }); + connect(romsListTable, &QTableWidget::cellDoubleClicked, this, + [&](int row, int) { LoadROM((*romsListTable)[row].path); }); installEventFilter(this); @@ -123,7 +79,6 @@ KaizenGui::KaizenGui() noexcept : QMainWindow(nullptr), settings(QSettings::User setWindowTitle("Kaizen " KAIZEN_VERSION_STR); setMinimumSize(640, 480); setCentralWidget(centralWidget); - centralWidget->setLayout(new QVBoxLayout()); setAcceptDrops(true); @@ -193,10 +148,7 @@ KaizenGui::KaizenGui() noexcept : QMainWindow(nullptr), settings(QSettings::User connect(exit, &QAction::triggered, this, &QMainWindow::close); auto emulationMenu = menuBar()->addMenu("Emulation"); auto settingsMenu = emulationMenu->addAction("Settings"); - settingsWindow = new SettingsWindow(); connect(settingsMenu, &QAction::triggered, settingsWindow, &SettingsWindow::show); - connect(settingsWindow->general, &GeneralSettings::romFolderSelected, this, - [&] { emit romsListNeedsPopulation(); }); connect(settingsWindow->cpu, &CPUSettings::cpuTypeChanged, this, [&] { core.cpuType = Options::GetCpuType(); @@ -239,7 +191,10 @@ KaizenGui::KaizenGui() noexcept : QMainWindow(nullptr), settings(QSettings::User emulationMenu->addAction(stop); connect(stop, &QAction::triggered, this, [&] { Scheduler::GetInstance().EnqueueRelative(0, STOP); - centralWidget->setCurrentWidget(currentHomeWidget); + if (Options::GetRomsPath().empty()) + centralWidget->setCurrentWidget(romPathNotSet); + else + centralWidget->setCurrentWidget(romsListTable); }); auto helpMenu = menuBar()->addMenu("Help"); diff --git a/src/frontend/KaizenGui.hpp b/src/frontend/KaizenGui.hpp index 64672da..cbbdcbe 100644 --- a/src/frontend/KaizenGui.hpp +++ b/src/frontend/KaizenGui.hpp @@ -7,33 +7,10 @@ #include #include #include -#include +#include #include #include -class EmuThread; - -struct RomsListEntry { - u8 countryCode, version; - std::string path; - std::string name; - std::string region; - std::string lastPlayed; - std::string timePlayed; -}; - -class RomsListTable : public QTableWidget { - Q_OBJECT - std::vector romsList; - - public: - RomsListTable(); - public slots: - void populate(const std::string &); - signals: - void populateFinished(); -}; - class KaizenGui final : QMainWindow { Q_OBJECT void updateKeys(); @@ -51,12 +28,12 @@ class KaizenGui final : QMainWindow { QWidget *currentHomeWidget; QLabel *romPathNotSet = new QLabel(R"(Kaizen could not find any ROMs. Set the path in "Settings -> General")"); - RomsListTable *romsListTable = new RomsListTable(); + RomsListTable *romsListTable; QStackedWidget *centralWidget = new QStackedWidget(); SettingsWindow *settingsWindow; RenderWidget *vulkanWidget; QSettings settings; - QThread *emuThread, *populateRomsListThread; + QThread *emuThread; QTimer *statusBarTimer, *SDLeventsTimer; QLabel *fpsLabel; QLabel *cpuTypeLabel; diff --git a/src/frontend/RomsList.cpp b/src/frontend/RomsList.cpp new file mode 100644 index 0000000..e8ae531 --- /dev/null +++ b/src/frontend/RomsList.cpp @@ -0,0 +1,74 @@ +#include +#include +#include +#include +#include +#include + +RomsListTable::RomsListTable(GeneralSettings *general) { + verticalHeader()->hide(); + horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); + setSizePolicy({QSizePolicy::Maximum, QSizePolicy::Maximum}); + setSelectionMode(QAbstractItemView::SingleSelection); + setSelectionBehavior(QAbstractItemView::SelectRows); + setEditTriggers(QAbstractItemView::NoEditTriggers); + setSortingEnabled(true); + setColumnCount(4); + setHorizontalHeaderItem(0, new QTableWidgetItem("Name")); + setHorizontalHeaderItem(1, new QTableWidgetItem("Regions")); + setHorizontalHeaderItem(2, new QTableWidgetItem("Last played")); + setHorizontalHeaderItem(3, new QTableWidgetItem("Time played")); + + connect(general, &GeneralSettings::romFolderSelected, this, [&] { + std::thread popThread([&] { + populate(Options::GetRomsPath()); + emit populateFinished(); + }); + + popThread.detach(); + }); + + connect(general, &GeneralSettings::romFolderCleared, this, [&] { + for (int i = 0; i < rowCount(); i++) + removeRow(i); + romsList = {}; + emit cleared(); + }); + + std::thread popThread([&] { + populate(Options::GetRomsPath()); + emit populateFinished(); + }); + + popThread.detach(); +} + +void RomsListTable::populate(const std::string &romsPath) { + if (!romsPath.empty()) { + for (const auto &file : fs::recursive_directory_iterator{romsPath}) { + if (!file.is_regular_file()) + continue; + + auto filename = file.path().string(); + + bool isPlain = std::ranges::any_of(std::array{".n64", ".z64", ".v64"}, + [&](const std::string &ext) { return file.path().extension() == ext; }); + + bool isArchive = + std::ranges::any_of(std::array{".zip", ".7z", ".rar", ".tar"}, + [&](const std::string &ext) { return file.path().extension() == ext; }); + + if (!isArchive && !isPlain) + continue; + + auto rom = n64::Mem::LoadROM(isArchive, filename); + auto regions = n64::GameDB::match(rom); + + if (rom.gameNameDB.empty()) + rom.gameNameDB = fs::path(filename).stem().string(); + + romsList.push_back( + {rom.header.countryCode, rom.header.version, filename, rom.gameNameDB, regions, "Never", "0h 00m 00s"}); + } + } +} diff --git a/src/frontend/RomsList.hpp b/src/frontend/RomsList.hpp new file mode 100644 index 0000000..74e5416 --- /dev/null +++ b/src/frontend/RomsList.hpp @@ -0,0 +1,32 @@ +#pragma once +#include +#include + +class GeneralSettings; + +struct RomsListEntry { + u8 countryCode, version; + std::string path; + std::string name; + std::string region; + std::string lastPlayed; + std::string timePlayed; +}; + +class RomsListTable : public QTableWidget { + Q_OBJECT + + void populate(const std::string &); + + public: + std::vector romsList; + RomsListTable(GeneralSettings *); + + size_t size() { return romsList.size(); } + + RomsListEntry &operator[](const size_t i) { return romsList[i]; } + + signals: + void populateFinished(); + void cleared(); +}; diff --git a/src/frontend/Settings/GeneralSettings.cpp b/src/frontend/Settings/GeneralSettings.cpp index ff6b538..fba1725 100644 --- a/src/frontend/Settings/GeneralSettings.cpp +++ b/src/frontend/Settings/GeneralSettings.cpp @@ -3,6 +3,7 @@ #include #include #include +#include GeneralSettings::GeneralSettings() : settings(QSettings::UserScope) { selectedRomsFolderLabel = new QLabel(Options::GetRomsPath().c_str()); @@ -19,6 +20,9 @@ GeneralSettings::GeneralSettings() : settings(QSettings::UserScope) { auto dir = QFileDialog::getExistingDirectory(this, tr("Open Directory"), QCoreApplication::applicationDirPath(), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); + if (dir.isEmpty()) + return; + selectedSavesFolderLabel->setText(dir); Options::SetSavesPath(dir.toStdString()); settings.setValue("saves_path", dir); @@ -29,6 +33,9 @@ GeneralSettings::GeneralSettings() : settings(QSettings::UserScope) { auto dir = QFileDialog::getExistingDirectory(this, tr("Open Directory"), QCoreApplication::applicationDirPath(), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); + if (dir.isEmpty()) + return; + selectedRomsFolderLabel->setText(dir); Options::SetRomsPath(dir.toStdString()); settings.setValue("roms_path", dir); @@ -38,12 +45,32 @@ GeneralSettings::GeneralSettings() : settings(QSettings::UserScope) { gl = new QGridLayout(); + QPushButton *clearRoms = new QPushButton("Clear"); + connect(clearRoms, &QPushButton::clicked, this, [&] { + selectedRomsFolderLabel->clear(); + Options::SetRomsPath(""); + settings.setValue("roms_path", ""); + settings.sync(); + emit romFolderCleared(); + }); + + QPushButton *clearSaves = new QPushButton("Clear"); + connect(clearSaves, &QPushButton::clicked, this, [&] { + selectedSavesFolderLabel->clear(); + Options::SetSavesPath(""); + settings.setValue("saves_path", ""); + settings.sync(); + }); + gl->addWidget(new QLabel("ROMs path:"), 0, 0); gl->addWidget(selectedRomsFolderLabel, 0, 1); gl->addWidget(romsFolderSelectButton, 0, 2); + gl->addWidget(clearRoms, 0, 3); + gl->addWidget(new QLabel("Saves path:"), 1, 0); gl->addWidget(selectedSavesFolderLabel, 1, 1); gl->addWidget(savesFolderSelectButton, 1, 2); + gl->addWidget(clearSaves, 1, 3); setLayout(gl); } diff --git a/src/frontend/Settings/GeneralSettings.hpp b/src/frontend/Settings/GeneralSettings.hpp index 6d9e9bc..ef010ee 100644 --- a/src/frontend/Settings/GeneralSettings.hpp +++ b/src/frontend/Settings/GeneralSettings.hpp @@ -19,4 +19,5 @@ class GeneralSettings final : public QWidget { signals: void romFolderSelected(); + void romFolderCleared(); };