From cb8bc3413ae60879a85b9f0be9c9374e714513ab Mon Sep 17 00:00:00 2001 From: Matt Gajownik Date: Tue, 5 Jul 2022 23:47:12 +1000 Subject: [PATCH] UI: Add support for theme meta, parent theme palette Theme Meta allows individual themes to provide additional information to OBS. The primarily goal is for a theme to define a "parent", allowing to extend existing themes with additional attributes. --- UI/data/themes/Acri.qss | 5 ++ UI/data/themes/Dark.qss | 4 ++ UI/data/themes/Rachni.qss | 5 ++ UI/data/themes/System.qss | 3 + UI/data/themes/Yami.qss | 5 ++ UI/obs-app.cpp | 115 ++++++++++++++++++++++++++++++++++++-- UI/obs-app.hpp | 10 ++++ 7 files changed, 141 insertions(+), 6 deletions(-) diff --git a/UI/data/themes/Acri.qss b/UI/data/themes/Acri.qss index 0cf4d594e..dcefc85ca 100644 --- a/UI/data/themes/Acri.qss +++ b/UI/data/themes/Acri.qss @@ -1,3 +1,8 @@ +OBSThemeMeta { + dark: 'true'; + author: 'Warchamp7'; +} + /* OBSTheme, main QApplication palette and QML values */ OBSTheme { window: rgb(24,24,25); diff --git a/UI/data/themes/Dark.qss b/UI/data/themes/Dark.qss index 51ac1ac65..d5bd0ea98 100644 --- a/UI/data/themes/Dark.qss +++ b/UI/data/themes/Dark.qss @@ -28,6 +28,10 @@ /* rgb(11,10,11); /* veryVeryDark */ /* rgb(42,130,218); /* blue */ +OBSThemeMeta { + dark: 'true'; + author: 'Warchamp7'; +} /* Custom theme information. This will set the application's QPalette, as * well as pass to QML via the OBSTheme object. diff --git a/UI/data/themes/Rachni.qss b/UI/data/themes/Rachni.qss index f1c7eff80..0f2dc82c7 100644 --- a/UI/data/themes/Rachni.qss +++ b/UI/data/themes/Rachni.qss @@ -44,6 +44,11 @@ /* ---- Main Theme ---- */ /************************/ +OBSThemeMeta { + dark: 'true'; + author: 'Fenrir'; +} + OBSTheme { window: rgb(49, 54, 59); /* Blue-gray */ windowText: rgb(239, 240, 241); /* White */ diff --git a/UI/data/themes/System.qss b/UI/data/themes/System.qss index d54f541b8..69bd06c3b 100644 --- a/UI/data/themes/System.qss +++ b/UI/data/themes/System.qss @@ -8,6 +8,9 @@ /* Dark Theme is a good place to start if you need */ /* a template. */ +OBSThemeMeta { + dark: 'false'; +} /* We need to set back the icons, or the preview wont stick. */ diff --git a/UI/data/themes/Yami.qss b/UI/data/themes/Yami.qss index d4be8575f..8e82b1e7d 100644 --- a/UI/data/themes/Yami.qss +++ b/UI/data/themes/Yami.qss @@ -19,6 +19,11 @@ /* Colors */ +OBSThemeMeta { + dark: 'true'; + author: 'Warchamp7'; +} + /* Custom theme information. This will set the application's QPalette, as * well as pass to QML via the OBSTheme object. * Can also use OBSTheme::disabled, OBSTheme::active, and OBSTheme::inactive. diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index 00ff1956e..8966afa73 100644 --- a/UI/obs-app.cpp +++ b/UI/obs-app.cpp @@ -1095,10 +1095,72 @@ void OBSApp::ParseExtraThemeData(const char *path) setPalette(pal); } -bool OBSApp::SetTheme(std::string name, std::string path) +OBSThemeMeta *OBSApp::ParseThemeMeta(const char *path) { - theme = name; + BPtr data = os_quick_read_utf8_file(path); + CFParser cfp; + int ret; + if (!cf_parser_parse(cfp, data, path)) + return nullptr; + + if (cf_token_is(cfp, "OBSThemeMeta") || + cf_go_to_token(cfp, "OBSThemeMeta", nullptr)) { + OBSThemeMeta *meta = new OBSThemeMeta(); + if (!cf_next_token(cfp)) + return nullptr; + + if (!cf_token_is(cfp, "{")) + return nullptr; + + for (;;) { + if (!cf_next_token(cfp)) + return nullptr; + + ret = cf_token_is_type(cfp, CFTOKEN_NAME, "name", + nullptr); + if (ret != PARSE_SUCCESS) + break; + + DStr name; + dstr_copy_strref(name, &cfp->cur_token->str); + + ret = cf_next_token_should_be(cfp, ":", ";", nullptr); + if (ret != PARSE_SUCCESS) + continue; + + if (!cf_next_token(cfp)) + return nullptr; + + ret = cf_token_is_type(cfp, CFTOKEN_STRING, "value", + ";"); + + if (ret != PARSE_SUCCESS) + continue; + + char *str; + str = cf_literal_to_str(cfp->cur_token->str.array, + cfp->cur_token->str.len); + + if (strcmp(name->array, "dark") == 0 && str) { + meta->dark = strcmp(str, "true") == 0; + } else if (strcmp(name->array, "parent") == 0 && str) { + meta->parent = std::string(str); + } else if (strcmp(name->array, "author") == 0 && str) { + meta->author = std::string(str); + } + bfree(str); + + if (!cf_go_to_token(cfp, ";", nullptr)) + return nullptr; + } + return meta; + } + return nullptr; +} + +std::string OBSApp::GetTheme(std::string name, std::string path) +{ /* Check user dir first, then preinstalled themes. */ if (path == "") { char userDir[512]; @@ -1110,18 +1172,59 @@ bool OBSApp::SetTheme(std::string name, std::string path) path = string(userDir); } else if (!GetDataFilePath(name.c_str(), path)) { OBSErrorBox(NULL, "Failed to find %s.", name.c_str()); - return false; + return ""; } } + return path; +} + +std::string OBSApp::SetParentTheme(std::string name) +{ + string path = GetTheme(name.c_str(), ""); + if (path.empty()) + return path; setStyleSheet(defaultStyleSheet); - QString mpath = QString("file:///") + path.c_str(); setPalette(defaultPalette); + + QString mpath = QString("file:///") + path.c_str(); + ParseExtraThemeData(path.c_str()); + return path; +} + +bool OBSApp::SetTheme(std::string name, std::string path) +{ + theme = name; + + path = GetTheme(name, path); + if (path.empty()) + return false; + + themeMeta = ParseThemeMeta(path.c_str()); + string parentPath; + + if (themeMeta && !themeMeta->parent.empty()) { + parentPath = SetParentTheme(themeMeta->parent); + } + + string lpath = path; + if (parentPath.empty()) { + setStyleSheet(defaultStyleSheet); + setPalette(defaultPalette); + } else { + lpath = parentPath; + } + + QString mpath = QString("file:///") + lpath.c_str(); ParseExtraThemeData(path.c_str()); setStyle(new OBSIgnoreWheelProxyStyle); setStyleSheet(mpath); - QColor color = palette().text().color(); - themeDarkMode = !(color.redF() < 0.5); + if (themeMeta) { + themeDarkMode = themeMeta->dark; + } else { + QColor color = palette().text().color(); + themeDarkMode = !(color.redF() < 0.5); + } emit StyleChanged(); return true; diff --git a/UI/obs-app.hpp b/UI/obs-app.hpp index aaee5f939..88ff508d5 100644 --- a/UI/obs-app.hpp +++ b/UI/obs-app.hpp @@ -68,6 +68,12 @@ public: typedef std::function VoidFunc; +struct OBSThemeMeta { + bool dark; + std::string parent; + std::string author; +}; + class OBSApp : public QApplication { Q_OBJECT @@ -75,6 +81,7 @@ private: std::string locale; std::string theme; QString defaultStyleSheet; + OBSThemeMeta *themeMeta = nullptr; bool themeDarkMode = true; ConfigFile globalConfig; TextLookup textLookup; @@ -103,6 +110,7 @@ private: QPalette defaultPalette; void ParseExtraThemeData(const char *path); + static OBSThemeMeta *ParseThemeMeta(const char *path); void AddExtraThemeColor(QPalette &pal, int group, const char *name, uint32_t color); @@ -130,6 +138,8 @@ public: inline const char *GetLocale() const { return locale.c_str(); } inline const char *GetTheme() const { return theme.c_str(); } + std::string GetTheme(std::string name, std::string path); + std::string SetParentTheme(std::string name); bool SetTheme(std::string name, std::string path = ""); inline bool IsThemeDark() const { return themeDarkMode; };