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);