diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e79a21..6d8c38c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) project(QRookie - VERSION 0.3.5 + VERSION 0.4.0 DESCRIPTION "Download and install Quest games from ROOKIE Public Mirror (like VRP Rookie Sideloader but for linux)" HOMEPAGE_URL https://github.com/glaumar/QRookie LANGUAGES CXX) diff --git a/content/Tabs/Devices/Device.qml b/content/Tabs/Devices/Device.qml index face478..f7c5021 100644 --- a/content/Tabs/Devices/Device.qml +++ b/content/Tabs/Devices/Device.qml @@ -237,6 +237,8 @@ RowLayout { Kirigami.FormLayout { id: device_info_layout + wideMode: true + Layout.alignment: Qt.AlignBottom Layout.fillWidth: true visible: app.deviceManager.hasConnectedDevice diff --git a/content/Tabs/Games/Games.qml b/content/Tabs/Games/Games.qml index 226a0e1..08eb628 100644 --- a/content/Tabs/Games/Games.qml +++ b/content/Tabs/Games/Games.qml @@ -139,11 +139,11 @@ ColumnLayout { property var headerPadding: header.parent.height title: qsTr("Settings") + + implicitWidth: 300 implicitHeight: headerHeight + headerPadding + - auto_install_setting.height + - auto_clean_cache_setting.height - implicitWidth: 300 + settings_form.height Kirigami.FormLayout { id: settings_form @@ -179,6 +179,29 @@ ColumnLayout { ToolTip.text: qsTr("Automatically clean the cache when the game is decompressed.") ToolTip.visible: hovered } + + ComboBox { + id: theme_setting + + width: 200 + Kirigami.FormData.label: qsTr("Select Theme *:") + onActivated: { + app.vrp.settings.theme = currentValue; + + settings_sheet.close(); + restart_dialog.open(); + } + Component.onCompleted: currentIndex = indexOfValue(app.vrp.settings.theme) + + model: app.vrp.compatibleThemes + } + + Item { + id: restart_label + + height: 30 + Kirigami.FormData.label: qsTr("* Requires restart") + } } } @@ -243,4 +266,44 @@ ColumnLayout { } } + + Dialog { + id: restart_dialog + + title: "Restart Required" + modal: true + x: (parent.width - width) / 2 + y: (parent.height - height) / 2 + + ColumnLayout { + width: parent.width + + Label { + text: qsTr("QRookie needs to be restarted in order to apply the new theme.") + } + + DialogButtonBox { + Layout.alignment: Qt.AlignRight + Layout.topMargin: 10 + + Button { + text: qsTr("Later") + DialogButtonBox.buttonRole: DialogButtonBox.RejectRole + } + Button { + text: qsTr("Restart") + DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole + } + onRejected: { + restart_dialog.close(); + settings_sheet.open(); + } + onAccepted: { + restart_dialog.close(); + app.vrp.restartMainApp(); + } + } + } + } + } diff --git a/flake.nix b/flake.nix index 7b77973..2923b29 100644 --- a/flake.nix +++ b/flake.nix @@ -22,7 +22,7 @@ { packages.default = pkgs.stdenv.mkDerivation { pname = "qrookie"; - version = "0.3.5"; + version = "0.4.0"; src = ./.; diff --git a/io.github.glaumar.QRookie.metainfo.xml b/io.github.glaumar.QRookie.metainfo.xml index 89d09aa..ae78319 100644 --- a/io.github.glaumar.QRookie.metainfo.xml +++ b/io.github.glaumar.QRookie.metainfo.xml @@ -37,6 +37,13 @@ + + +
    +
  • feat(UI,backend): add theme switcher
  • +
+
+
    diff --git a/macOs/Info.plist b/macOs/Info.plist index ab0cef7..1e43c92 100644 --- a/macOs/Info.plist +++ b/macOs/Info.plist @@ -7,7 +7,7 @@ CFBundleIdentifier io.github.glaumar.QRookie CFBundleVersion - v0.3.5 + v0.4.0 CFBundleExecutable QRookie CFBundlePackageType diff --git a/src/app_settings.cpp b/src/app_settings.cpp index bbaefdd..c8023b7 100644 --- a/src/app_settings.cpp +++ b/src/app_settings.cpp @@ -32,6 +32,7 @@ AppSettings::AppSettings(QObject *parent) , data_path_(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)) , keystore_path_(QString()) , last_wireless_addr_(QString()) + , theme_(QString()) { loadAppSettings(); } @@ -68,6 +69,19 @@ void AppSettings::loadAppSettings() } last_wireless_addr_ = settings_->value("last_wireless_addr", last_wireless_addr_).toString(); + + theme_ = settings_->value("theme", theme_).toString(); + if (theme_.isEmpty()) { +#ifdef Q_OS_LINUX + theme_ = "org.kde.breeze"; +#elif defined(Q_OS_MAC) + theme_ = "Material"; +#elif defined(Q_OS_WIN) + theme_ = "WindowsVista"; +#else + theme_ = "Universal"; +#endif + } } void AppSettings::setAutoInstall(bool auto_install) @@ -125,4 +139,11 @@ void AppSettings::setLastWirelessAddr(const QString &addr) last_wireless_addr_ = addr; settings_->setValue("last_wireless_addr", last_wireless_addr_); emit lastWirelessAddrChanged(addr); -} \ No newline at end of file +} + +void AppSettings::setTheme(const QString &theme) +{ + theme_ = theme; + settings_->setValue("theme", theme_); + emit themeChanged(theme); +} diff --git a/src/app_settings.h b/src/app_settings.h index 30011e0..4e25877 100644 --- a/src/app_settings.h +++ b/src/app_settings.h @@ -31,12 +31,14 @@ class AppSettings : public QObject Q_PROPERTY(QString cachePath READ cachePath WRITE setCachePath NOTIFY cachePathChanged) Q_PROPERTY(QString dataPath READ dataPath WRITE setDataPath NOTIFY dataPathChanged) Q_PROPERTY(QString lastWirelessAddr READ lastWirelessAddr WRITE setLastWirelessAddr NOTIFY lastWirelessAddrChanged) + Q_PROPERTY(QString theme READ theme WRITE setTheme NOTIFY themeChanged) public: explicit AppSettings(QObject *parent = nullptr); ~AppSettings(); static AppSettings *instance(); + static int const EXIT_RESTART = 1000; bool autoInstall() const { @@ -80,6 +82,12 @@ class AppSettings : public QObject } void setLastWirelessAddr(const QString &addr); + QString theme() const + { + return theme_; + } + void setTheme(const QString &theme); + signals: void autoInstallChanged(bool auto_install); void autoCleanCacheChanged(bool auto_clean_cache); @@ -88,6 +96,7 @@ class AppSettings : public QObject void dataPathChanged(QString data_path); void keyStorePathChanged(QString keystore_path); void lastWirelessAddrChanged(QString addr); + void themeChanged(QString theme); private: void loadAppSettings(); @@ -100,6 +109,7 @@ class AppSettings : public QObject QString data_path_; QString keystore_path_; QString last_wireless_addr_; + QString theme_; }; #endif /* QROOKIE_APP_SETTINGS */ diff --git a/src/main.cpp b/src/main.cpp index 57266f0..dc93a29 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,87 +23,90 @@ #include #include +#include "app_settings.h" #include "device_manager.h" #include "qrookie.h" #include "vrp_manager.h" int main(int argc, char *argv[]) { - QGuiApplication app(argc, argv); - app.setApplicationName(APPLICATION_NAME); - app.setApplicationVersion(APPLICATION_VERSION); - app.setDesktopFileName(APPLICATION_ID); - -#ifdef Q_OS_LINUX - if (qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE")) { - QQuickStyle::setStyle(QStringLiteral("org.kde.breeze")); - } -#elif defined(Q_OS_MAC) - if (qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE")) { - QQuickStyle::setStyle(QStringLiteral("Material")); - } - - QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); - QStringList icon_dirs = QIcon::themeSearchPaths(); - - // add XDG_DATA_DIRS/icons to the icon theme search path (for nix-darwin) - if (env.contains("XDG_DATA_DIRS")) { - QString xdg_data_dirs = env.value("XDG_DATA_DIRS"); - QStringList data_dirs = xdg_data_dirs.split(QDir::listSeparator()); - // QStringList icon_dirs; - for (auto dir : data_dirs) { - auto icon_dir = dir + "/icons"; - if (QDir(icon_dir).exists()) { - icon_dirs.append(icon_dir); + int currentExitCode = 0; + + do { + QGuiApplication app(argc, argv); + app.setApplicationName(APPLICATION_NAME); + app.setApplicationVersion(APPLICATION_VERSION); + app.setDesktopFileName(APPLICATION_ID); + +#if defined(Q_OS_LINUX) || defined(Q_OS_MAC) + if (qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE")) { + const QString theme = AppSettings::instance()->theme(); + QQuickStyle::setStyle(theme); + } +#endif +#if defined(Q_OS_MAC) + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + QStringList icon_dirs = QIcon::themeSearchPaths(); + + // add XDG_DATA_DIRS/icons to the icon theme search path (for nix-darwin) + if (env.contains("XDG_DATA_DIRS")) { + QString xdg_data_dirs = env.value("XDG_DATA_DIRS"); + QStringList data_dirs = xdg_data_dirs.split(QDir::listSeparator()); + // QStringList icon_dirs; + for (auto dir : data_dirs) { + auto icon_dir = dir + "/icons"; + if (QDir(icon_dir).exists()) { + icon_dirs.append(icon_dir); + } } } - } #ifdef MACOS_BUNDLE - QString res_dir = QCoreApplication::applicationDirPath() + "/../Resources"; + QString res_dir = QCoreApplication::applicationDirPath() + "/../Resources"; - // add ../Resources to the PATH (for adb,7za,zipalign,apktool,apksigner) - QStringList path = env.value("PATH").split(QDir::listSeparator()); - path.prepend(res_dir); - qputenv("PATH", path.join(QDir::listSeparator()).toUtf8()); + // add ../Resources to the PATH (for adb,7za,zipalign,apktool,apksigner) + QStringList path = env.value("PATH").split(QDir::listSeparator()); + path.prepend(res_dir); + qputenv("PATH", path.join(QDir::listSeparator()).toUtf8()); - // add ../Resources/icons to the icon theme search path - icon_dirs += res_dir + "/icons"; + // add ../Resources/icons to the icon theme search path + icon_dirs += res_dir + "/icons"; #endif // MACOS_BUNDLE - QIcon::setThemeSearchPaths(icon_dirs); - QIcon::setThemeName("breeze"); -#endif + QIcon::setThemeSearchPaths(icon_dirs); + QIcon::setThemeName("breeze"); +#endif // Q_OS_MAC - qmlRegisterType("VrpManager", 1, 0, "VrpManager"); - qmlRegisterType("DeviceManager", 1, 0, "DeviceManager"); - QCoro::Qml::registerTypes(); + qmlRegisterType("VrpManager", 1, 0, "VrpManager"); + qmlRegisterType("DeviceManager", 1, 0, "DeviceManager"); + QCoro::Qml::registerTypes(); - QQmlApplicationEngine engine; + QQmlApplicationEngine engine; #ifdef MACOS_BUNDLE - engine.addImportPath(res_dir + "/kirigami"); + engine.addImportPath(res_dir + "/kirigami"); #endif - const QUrl url(u"qrc:/qt/qml/Main/main.qml"_qs); - QObject::connect( - &engine, - &QQmlApplicationEngine::objectCreated, - &app, - [url](QObject *obj, const QUrl &objUrl) { - if (!obj && url == objUrl) - QCoreApplication::exit(-1); - }, - Qt::QueuedConnection); + const QUrl url(u"qrc:/qt/qml/Main/main.qml"_qs); + QObject::connect( + &engine, + &QQmlApplicationEngine::objectCreated, + &app, + [url](QObject *obj, const QUrl &objUrl) { + if (!obj && url == objUrl) + QCoreApplication::exit(-1); + }, + Qt::QueuedConnection); - engine.addImportPath(QCoreApplication::applicationDirPath() + "/qml"); - engine.addImportPath(":/"); + engine.addImportPath(QCoreApplication::applicationDirPath() + "/qml"); + engine.addImportPath(":/"); - engine.load(url); + engine.load(url); - if (engine.rootObjects().isEmpty()) { - return -1; - } + if (engine.rootObjects().isEmpty()) { + return -1; + } - return app.exec(); + return currentExitCode = app.exec(); + } while (currentExitCode == AppSettings::EXIT_RESTART); } diff --git a/src/vrp_manager.cpp b/src/vrp_manager.cpp index 6dec2df..2462951 100644 --- a/src/vrp_manager.cpp +++ b/src/vrp_manager.cpp @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -609,3 +610,24 @@ QCoro::Task VrpManager::openGameFolder(const QString release_name) } co_return true; } + +QStringList VrpManager::compatibleThemes() const +{ +#ifdef Q_OS_LINUX + return {"org.kde.breeze", "Universal"}; +#elif defined(Q_OS_MAC) + return {"Material", "Universal"}; +#elif defined(Q_OS_WIN) + return {"WindowsVista", "Universal"}; +#else + return {"Universal"}; +#endif +} + +void VrpManager::restartMainApp() +{ + QString program = qApp->arguments()[0]; + QStringList arguments = qApp->arguments().mid(1); + qApp->exit(AppSettings::EXIT_RESTART); + QProcess::startDetached(program, arguments); +} diff --git a/src/vrp_manager.h b/src/vrp_manager.h index 84bfa14..7c7106d 100644 --- a/src/vrp_manager.h +++ b/src/vrp_manager.h @@ -41,6 +41,7 @@ class VrpManager : public QObject Q_ENUMS(SortType) Q_PROPERTY(QVariantList gamesInfo READ gamesInfo NOTIFY gamesInfoChanged) + Q_PROPERTY(QStringList compatibleThemes READ compatibleThemes CONSTANT) Q_PROPERTY(AppSettings *settings READ settings) public: @@ -152,6 +153,10 @@ class VrpManager : public QObject return openGameFolder(release_name); } + QStringList compatibleThemes() const; + + Q_INVOKABLE void restartMainApp(); + signals: void gamesInfoChanged(); void statusChanged(QString release_name, Status status);