Merge pull request #1195 from FearlessTobi/port-gamelist-compat
yuzu: Show game compatibility in the game list (PR ported from Citra)
This commit is contained in:
		
						commit
						26aaa86ece
					
				@ -10,7 +10,7 @@ ln -sf /usr/bin/ccache /usr/lib/ccache/cc
 | 
			
		||||
ln -sf /usr/bin/ccache /usr/lib/ccache/c++
 | 
			
		||||
mkdir build && cd build
 | 
			
		||||
ccache --show-stats > ccache_before
 | 
			
		||||
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -G Ninja
 | 
			
		||||
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -G Ninja
 | 
			
		||||
ninja
 | 
			
		||||
ccache --show-stats > ccache_after
 | 
			
		||||
diff -U100 ccache_before ccache_after || true
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,7 @@ mkdir build && cd build
 | 
			
		||||
export PATH=/usr/local/opt/ccache/libexec:$PATH
 | 
			
		||||
ccache --show-stats > ccache_before
 | 
			
		||||
cmake --version
 | 
			
		||||
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release
 | 
			
		||||
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON
 | 
			
		||||
make -j4
 | 
			
		||||
ccache --show-stats > ccache_after
 | 
			
		||||
diff -U100 ccache_before ccache_after || true
 | 
			
		||||
 | 
			
		||||
@ -41,6 +41,19 @@ function(check_submodules_present)
 | 
			
		||||
endfunction()
 | 
			
		||||
check_submodules_present()
 | 
			
		||||
 | 
			
		||||
configure_file(${CMAKE_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc
 | 
			
		||||
               ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
 | 
			
		||||
               COPYONLY)
 | 
			
		||||
if (ENABLE_COMPATIBILITY_LIST_DOWNLOAD AND NOT EXISTS ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
 | 
			
		||||
    message(STATUS "Downloading compatibility list for yuzu...")
 | 
			
		||||
    file(DOWNLOAD
 | 
			
		||||
        https://api.yuzu-emu.org/gamedb/
 | 
			
		||||
        "${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json" SHOW_PROGRESS)
 | 
			
		||||
endif()
 | 
			
		||||
if (NOT EXISTS ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
 | 
			
		||||
    file(WRITE ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json "")
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
# Detect current compilation architecture and create standard definitions
 | 
			
		||||
# =======================================================================
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -41,9 +41,9 @@ before_build:
 | 
			
		||||
  - ps: |
 | 
			
		||||
        if ($env:BUILD_TYPE -eq 'msvc') {
 | 
			
		||||
          # redirect stderr and change the exit code to prevent powershell from cancelling the build if cmake prints a warning
 | 
			
		||||
          cmd /C 'cmake -G "Visual Studio 15 2017 Win64" -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 .. 2>&1 && exit 0'
 | 
			
		||||
          cmd /C 'cmake -G "Visual Studio 15 2017 Win64" -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON .. 2>&1 && exit 0'
 | 
			
		||||
        } else {
 | 
			
		||||
          C:\msys64\usr\bin\bash.exe -lc "cmake -G 'MSYS Makefiles' -DYUZU_BUILD_UNICORN=1 -DCMAKE_BUILD_TYPE=Release .. 2>&1"
 | 
			
		||||
          C:\msys64\usr\bin\bash.exe -lc "cmake -G 'MSYS Makefiles' -DYUZU_BUILD_UNICORN=1 -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON .. 2>&1"
 | 
			
		||||
        }
 | 
			
		||||
  - cd ..
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										5
									
								
								dist/compatibility_list/compatibility_list.qrc
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								dist/compatibility_list/compatibility_list.qrc
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
			
		||||
<RCC>
 | 
			
		||||
  <qresource prefix="compatibility_list">
 | 
			
		||||
      <file>compatibility_list.json</file>
 | 
			
		||||
  </qresource>
 | 
			
		||||
</RCC>
 | 
			
		||||
@ -70,6 +70,9 @@ set(UIS
 | 
			
		||||
    main.ui
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
file(GLOB COMPAT_LIST
 | 
			
		||||
     ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
 | 
			
		||||
     ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
 | 
			
		||||
file(GLOB_RECURSE ICONS ${CMAKE_SOURCE_DIR}/dist/icons/*)
 | 
			
		||||
file(GLOB_RECURSE THEMES ${CMAKE_SOURCE_DIR}/dist/qt_themes/*)
 | 
			
		||||
 | 
			
		||||
@ -77,6 +80,7 @@ qt5_wrap_ui(UI_HDRS ${UIS})
 | 
			
		||||
 | 
			
		||||
target_sources(yuzu
 | 
			
		||||
    PRIVATE
 | 
			
		||||
        ${COMPAT_LIST}
 | 
			
		||||
        ${ICONS}
 | 
			
		||||
        ${THEMES}
 | 
			
		||||
        ${UI_HDRS}
 | 
			
		||||
 | 
			
		||||
@ -7,10 +7,14 @@
 | 
			
		||||
#include <QDir>
 | 
			
		||||
#include <QFileInfo>
 | 
			
		||||
#include <QHeaderView>
 | 
			
		||||
#include <QJsonArray>
 | 
			
		||||
#include <QJsonDocument>
 | 
			
		||||
#include <QJsonObject>
 | 
			
		||||
#include <QKeyEvent>
 | 
			
		||||
#include <QMenu>
 | 
			
		||||
#include <QThreadPool>
 | 
			
		||||
#include <boost/container/flat_map.hpp>
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
#include "common/common_paths.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "common/string_util.h"
 | 
			
		||||
@ -224,6 +228,7 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent)
 | 
			
		||||
 | 
			
		||||
    item_model->insertColumns(0, COLUMN_COUNT);
 | 
			
		||||
    item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name");
 | 
			
		||||
    item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, "Compatibility");
 | 
			
		||||
    item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type");
 | 
			
		||||
    item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size");
 | 
			
		||||
 | 
			
		||||
@ -325,12 +330,62 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
 | 
			
		||||
 | 
			
		||||
    QMenu context_menu;
 | 
			
		||||
    QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location"));
 | 
			
		||||
    QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry"));
 | 
			
		||||
 | 
			
		||||
    open_save_location->setEnabled(program_id != 0);
 | 
			
		||||
    auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
 | 
			
		||||
    navigate_to_gamedb_entry->setVisible(it != compatibility_list.end() && program_id != 0);
 | 
			
		||||
 | 
			
		||||
    connect(open_save_location, &QAction::triggered,
 | 
			
		||||
            [&]() { emit OpenFolderRequested(program_id, GameListOpenTarget::SaveData); });
 | 
			
		||||
    connect(navigate_to_gamedb_entry, &QAction::triggered,
 | 
			
		||||
            [&]() { emit NavigateToGamedbEntryRequested(program_id, compatibility_list); });
 | 
			
		||||
 | 
			
		||||
    context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GameList::LoadCompatibilityList() {
 | 
			
		||||
    QFile compat_list{":compatibility_list/compatibility_list.json"};
 | 
			
		||||
 | 
			
		||||
    if (!compat_list.open(QFile::ReadOnly | QFile::Text)) {
 | 
			
		||||
        LOG_ERROR(Frontend, "Unable to open game compatibility list");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (compat_list.size() == 0) {
 | 
			
		||||
        LOG_WARNING(Frontend, "Game compatibility list is empty");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const QByteArray content = compat_list.readAll();
 | 
			
		||||
    if (content.isEmpty()) {
 | 
			
		||||
        LOG_ERROR(Frontend, "Unable to completely read game compatibility list");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const QString string_content = content;
 | 
			
		||||
    QJsonDocument json = QJsonDocument::fromJson(string_content.toUtf8());
 | 
			
		||||
    QJsonArray arr = json.array();
 | 
			
		||||
 | 
			
		||||
    for (const QJsonValue& value : arr) {
 | 
			
		||||
        QJsonObject game = value.toObject();
 | 
			
		||||
 | 
			
		||||
        if (game.contains("compatibility") && game["compatibility"].isDouble()) {
 | 
			
		||||
            int compatibility = game["compatibility"].toInt();
 | 
			
		||||
            QString directory = game["directory"].toString();
 | 
			
		||||
            QJsonArray ids = game["releases"].toArray();
 | 
			
		||||
 | 
			
		||||
            for (const QJsonValue& value : ids) {
 | 
			
		||||
                QJsonObject object = value.toObject();
 | 
			
		||||
                QString id = object["id"].toString();
 | 
			
		||||
                compatibility_list.emplace(
 | 
			
		||||
                    id.toUpper().toStdString(),
 | 
			
		||||
                    std::make_pair(QString::number(compatibility), directory));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {
 | 
			
		||||
    if (!FileUtil::Exists(dir_path.toStdString()) ||
 | 
			
		||||
        !FileUtil::IsDirectory(dir_path.toStdString())) {
 | 
			
		||||
@ -345,7 +400,7 @@ void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) {
 | 
			
		||||
 | 
			
		||||
    emit ShouldCancelWorker();
 | 
			
		||||
 | 
			
		||||
    GameListWorker* worker = new GameListWorker(vfs, dir_path, deep_scan);
 | 
			
		||||
    GameListWorker* worker = new GameListWorker(vfs, dir_path, deep_scan, compatibility_list);
 | 
			
		||||
 | 
			
		||||
    connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection);
 | 
			
		||||
    connect(worker, &GameListWorker::Finished, this, &GameList::DonePopulating,
 | 
			
		||||
@ -523,11 +578,19 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
 | 
			
		||||
 | 
			
		||||
            // The game list uses this as compatibility number for untested games
 | 
			
		||||
            QString compatibility("99");
 | 
			
		||||
            if (it != compatibility_list.end())
 | 
			
		||||
                compatibility = it->second.first;
 | 
			
		||||
 | 
			
		||||
            emit EntryReady({
 | 
			
		||||
                new GameListItemPath(
 | 
			
		||||
                    FormatGameName(physical_name), icon, QString::fromStdString(name),
 | 
			
		||||
                    QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
 | 
			
		||||
                    program_id),
 | 
			
		||||
                new GameListItemCompat(compatibility),
 | 
			
		||||
                new GameListItem(
 | 
			
		||||
                    QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
 | 
			
		||||
                new GameListItemSize(FileUtil::GetSize(physical_name)),
 | 
			
		||||
 | 
			
		||||
@ -29,6 +29,7 @@ class GameList : public QWidget {
 | 
			
		||||
public:
 | 
			
		||||
    enum {
 | 
			
		||||
        COLUMN_NAME,
 | 
			
		||||
        COLUMN_COMPATIBILITY,
 | 
			
		||||
        COLUMN_FILE_TYPE,
 | 
			
		||||
        COLUMN_SIZE,
 | 
			
		||||
        COLUMN_COUNT, // Number of columns
 | 
			
		||||
@ -68,6 +69,7 @@ public:
 | 
			
		||||
    void setFilterFocus();
 | 
			
		||||
    void setFilterVisible(bool visibility);
 | 
			
		||||
 | 
			
		||||
    void LoadCompatibilityList();
 | 
			
		||||
    void PopulateAsync(const QString& dir_path, bool deep_scan);
 | 
			
		||||
 | 
			
		||||
    void SaveInterfaceLayout();
 | 
			
		||||
@ -79,6 +81,9 @@ signals:
 | 
			
		||||
    void GameChosen(QString game_path);
 | 
			
		||||
    void ShouldCancelWorker();
 | 
			
		||||
    void OpenFolderRequested(u64 program_id, GameListOpenTarget target);
 | 
			
		||||
    void NavigateToGamedbEntryRequested(
 | 
			
		||||
        u64 program_id,
 | 
			
		||||
        std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list);
 | 
			
		||||
 | 
			
		||||
private slots:
 | 
			
		||||
    void onTextChanged(const QString& newText);
 | 
			
		||||
@ -100,6 +105,7 @@ private:
 | 
			
		||||
    QStandardItemModel* item_model = nullptr;
 | 
			
		||||
    GameListWorker* current_worker = nullptr;
 | 
			
		||||
    QFileSystemWatcher* watcher = nullptr;
 | 
			
		||||
    std::unordered_map<std::string, std::pair<QString, QString>> compatibility_list;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Q_DECLARE_METATYPE(GameListOpenTarget);
 | 
			
		||||
 | 
			
		||||
@ -8,11 +8,15 @@
 | 
			
		||||
#include <atomic>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
#include <utility>
 | 
			
		||||
#include <QCoreApplication>
 | 
			
		||||
#include <QImage>
 | 
			
		||||
#include <QObject>
 | 
			
		||||
#include <QRunnable>
 | 
			
		||||
#include <QStandardItem>
 | 
			
		||||
#include <QString>
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "common/string_util.h"
 | 
			
		||||
#include "core/file_sys/content_archive.h"
 | 
			
		||||
#include "ui_settings.h"
 | 
			
		||||
@ -29,6 +33,17 @@ static QPixmap GetDefaultIcon(u32 size) {
 | 
			
		||||
    return icon;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static auto FindMatchingCompatibilityEntry(
 | 
			
		||||
    const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list,
 | 
			
		||||
    u64 program_id) {
 | 
			
		||||
    return std::find_if(
 | 
			
		||||
        compatibility_list.begin(), compatibility_list.end(),
 | 
			
		||||
        [program_id](const std::pair<std::string, std::pair<QString, QString>>& element) {
 | 
			
		||||
            std::string pid = fmt::format("{:016X}", program_id);
 | 
			
		||||
            return element.first == pid;
 | 
			
		||||
        });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class GameListItem : public QStandardItem {
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
@ -96,6 +111,45 @@ public:
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class GameListItemCompat : public GameListItem {
 | 
			
		||||
    Q_DECLARE_TR_FUNCTIONS(GameListItemCompat)
 | 
			
		||||
public:
 | 
			
		||||
    static const int CompatNumberRole = Qt::UserRole + 1;
 | 
			
		||||
    GameListItemCompat() = default;
 | 
			
		||||
    explicit GameListItemCompat(const QString& compatiblity) {
 | 
			
		||||
        struct CompatStatus {
 | 
			
		||||
            QString color;
 | 
			
		||||
            const char* text;
 | 
			
		||||
            const char* tooltip;
 | 
			
		||||
        };
 | 
			
		||||
        // clang-format off
 | 
			
		||||
        static const std::map<QString, CompatStatus> status_data = {
 | 
			
		||||
        {"0",  {"#5c93ed", QT_TR_NOOP("Perfect"),    QT_TR_NOOP("Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without\nany workarounds needed.")}},
 | 
			
		||||
        {"1",  {"#47d35c", QT_TR_NOOP("Great"),      QT_TR_NOOP("Game functions with minor graphical or audio glitches and is playable from start to finish. May require some\nworkarounds.")}},
 | 
			
		||||
        {"2",  {"#94b242", QT_TR_NOOP("Okay"),       QT_TR_NOOP("Game functions with major graphical or audio glitches, but game is playable from start to finish with\nworkarounds.")}},
 | 
			
		||||
        {"3",  {"#f2d624", QT_TR_NOOP("Bad"),        QT_TR_NOOP("Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches\neven with workarounds.")}},
 | 
			
		||||
        {"4",  {"#FF0000", QT_TR_NOOP("Intro/Menu"), QT_TR_NOOP("Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start\nScreen.")}},
 | 
			
		||||
        {"5",  {"#828282", QT_TR_NOOP("Won't Boot"), QT_TR_NOOP("The game crashes when attempting to startup.")}},
 | 
			
		||||
        {"99", {"#000000", QT_TR_NOOP("Not Tested"), QT_TR_NOOP("The game has not yet been tested.")}}};
 | 
			
		||||
        // clang-format on
 | 
			
		||||
 | 
			
		||||
        auto iterator = status_data.find(compatiblity);
 | 
			
		||||
        if (iterator == status_data.end()) {
 | 
			
		||||
            LOG_WARNING(Frontend, "Invalid compatibility number {}", compatiblity.toStdString());
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        CompatStatus status = iterator->second;
 | 
			
		||||
        setData(compatiblity, CompatNumberRole);
 | 
			
		||||
        setText(QObject::tr(status.text));
 | 
			
		||||
        setToolTip(QObject::tr(status.tooltip));
 | 
			
		||||
        setData(CreateCirclePixmapFromColor(status.color), Qt::DecorationRole);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool operator<(const QStandardItem& other) const override {
 | 
			
		||||
        return data(CompatNumberRole) < other.data(CompatNumberRole);
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A specialization of GameListItem for size values.
 | 
			
		||||
 * This class ensures that for every numerical size value it holds (in bytes), a correct
 | 
			
		||||
@ -141,8 +195,11 @@ class GameListWorker : public QObject, public QRunnable {
 | 
			
		||||
    Q_OBJECT
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    GameListWorker(FileSys::VirtualFilesystem vfs, QString dir_path, bool deep_scan)
 | 
			
		||||
        : vfs(std::move(vfs)), dir_path(std::move(dir_path)), deep_scan(deep_scan) {}
 | 
			
		||||
    GameListWorker(
 | 
			
		||||
        FileSys::VirtualFilesystem vfs, QString dir_path, bool deep_scan,
 | 
			
		||||
        const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list)
 | 
			
		||||
        : vfs(std::move(vfs)), dir_path(std::move(dir_path)), deep_scan(deep_scan),
 | 
			
		||||
          compatibility_list(compatibility_list) {}
 | 
			
		||||
 | 
			
		||||
public slots:
 | 
			
		||||
    /// Starts the processing of directory tree information.
 | 
			
		||||
@ -170,6 +227,7 @@ private:
 | 
			
		||||
    QStringList watch_list;
 | 
			
		||||
    QString dir_path;
 | 
			
		||||
    bool deep_scan;
 | 
			
		||||
    const std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list;
 | 
			
		||||
    std::atomic_bool stop_processing;
 | 
			
		||||
 | 
			
		||||
    void AddInstalledTitlesToGameList(std::shared_ptr<FileSys::RegisteredCache> cache);
 | 
			
		||||
 | 
			
		||||
@ -16,6 +16,7 @@
 | 
			
		||||
#include <QMessageBox>
 | 
			
		||||
#include <QtGui>
 | 
			
		||||
#include <QtWidgets>
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
#include "common/common_paths.h"
 | 
			
		||||
#include "common/logging/backend.h"
 | 
			
		||||
#include "common/logging/filter.h"
 | 
			
		||||
@ -35,6 +36,7 @@
 | 
			
		||||
#include "core/gdbstub/gdbstub.h"
 | 
			
		||||
#include "core/loader/loader.h"
 | 
			
		||||
#include "core/settings.h"
 | 
			
		||||
#include "game_list_p.h"
 | 
			
		||||
#include "video_core/debug_utils/debug_utils.h"
 | 
			
		||||
#include "yuzu/about_dialog.h"
 | 
			
		||||
#include "yuzu/bootmanager.h"
 | 
			
		||||
@ -134,6 +136,7 @@ GMainWindow::GMainWindow()
 | 
			
		||||
 | 
			
		||||
    // Necessary to load titles from nand in gamelist.
 | 
			
		||||
    Service::FileSystem::CreateFactories(vfs);
 | 
			
		||||
    game_list->LoadCompatibilityList();
 | 
			
		||||
    game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan);
 | 
			
		||||
 | 
			
		||||
    // Show one-time "callout" messages to the user
 | 
			
		||||
@ -349,6 +352,8 @@ void GMainWindow::RestoreUIState() {
 | 
			
		||||
void GMainWindow::ConnectWidgetEvents() {
 | 
			
		||||
    connect(game_list, &GameList::GameChosen, this, &GMainWindow::OnGameListLoadFile);
 | 
			
		||||
    connect(game_list, &GameList::OpenFolderRequested, this, &GMainWindow::OnGameListOpenFolder);
 | 
			
		||||
    connect(game_list, &GameList::NavigateToGamedbEntryRequested, this,
 | 
			
		||||
            &GMainWindow::OnGameListNavigateToGamedbEntry);
 | 
			
		||||
 | 
			
		||||
    connect(this, &GMainWindow::EmulationStarting, render_window,
 | 
			
		||||
            &GRenderWindow::OnEmulationStarting);
 | 
			
		||||
@ -678,6 +683,20 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
 | 
			
		||||
    QDesktopServices::openUrl(QUrl::fromLocalFile(qpath));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GMainWindow::OnGameListNavigateToGamedbEntry(
 | 
			
		||||
    u64 program_id,
 | 
			
		||||
    std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list) {
 | 
			
		||||
 | 
			
		||||
    auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id);
 | 
			
		||||
 | 
			
		||||
    QString directory;
 | 
			
		||||
 | 
			
		||||
    if (it != compatibility_list.end())
 | 
			
		||||
        directory = it->second.second;
 | 
			
		||||
 | 
			
		||||
    QDesktopServices::openUrl(QUrl("https://yuzu-emu.org/game/" + directory));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GMainWindow::OnMenuLoadFile() {
 | 
			
		||||
    QString extensions;
 | 
			
		||||
    for (const auto& piece : game_list->supported_file_extensions)
 | 
			
		||||
 | 
			
		||||
@ -124,6 +124,9 @@ private slots:
 | 
			
		||||
    /// Called whenever a user selects a game in the game list widget.
 | 
			
		||||
    void OnGameListLoadFile(QString game_path);
 | 
			
		||||
    void OnGameListOpenFolder(u64 program_id, GameListOpenTarget target);
 | 
			
		||||
    void OnGameListNavigateToGamedbEntry(
 | 
			
		||||
        u64 program_id,
 | 
			
		||||
        std::unordered_map<std::string, std::pair<QString, QString>>& compatibility_list);
 | 
			
		||||
    void OnMenuLoadFile();
 | 
			
		||||
    void OnMenuLoadFolder();
 | 
			
		||||
    void OnMenuInstallToNAND();
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,7 @@
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <cmath>
 | 
			
		||||
#include <QPainter>
 | 
			
		||||
#include "yuzu/util/util.h"
 | 
			
		||||
 | 
			
		||||
QFont GetMonospaceFont() {
 | 
			
		||||
@ -24,3 +25,13 @@ QString ReadableByteSize(qulonglong size) {
 | 
			
		||||
        .arg(size / std::pow(1024, digit_groups), 0, 'f', 1)
 | 
			
		||||
        .arg(units[digit_groups]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QPixmap CreateCirclePixmapFromColor(const QColor& color) {
 | 
			
		||||
    QPixmap circle_pixmap(16, 16);
 | 
			
		||||
    circle_pixmap.fill(Qt::transparent);
 | 
			
		||||
    QPainter painter(&circle_pixmap);
 | 
			
		||||
    painter.setPen(color);
 | 
			
		||||
    painter.setBrush(color);
 | 
			
		||||
    painter.drawEllipse(0, 0, 15, 15);
 | 
			
		||||
    return circle_pixmap;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -12,3 +12,10 @@ QFont GetMonospaceFont();
 | 
			
		||||
 | 
			
		||||
/// Convert a size in bytes into a readable format (KiB, MiB, etc.)
 | 
			
		||||
QString ReadableByteSize(qulonglong size);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Creates a circle pixmap from a specified color
 | 
			
		||||
 * @param color The color the pixmap shall have
 | 
			
		||||
 * @return QPixmap circle pixmap
 | 
			
		||||
 */
 | 
			
		||||
QPixmap CreateCirclePixmapFromColor(const QColor& color);
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user