diff --git a/UI/data/themes/Acri.qss b/UI/data/themes/Acri.qss index 26eae664e..bce32d1fb 100644 --- a/UI/data/themes/Acri.qss +++ b/UI/data/themes/Acri.qss @@ -1,3 +1,37 @@ +/* OBSTheme, main QApplication palette and QML values */ +OBSTheme { + window: #181819; + windowText: rgb(225,224,225); + base: rgb(18,18,21); + alternateBase: rgb(0,0,0); + text: rgb(225,224,225); + button: #162458; + buttonText: rgb(225,224,225); + brightText: #484848; + + light: #162458; + mid: #181819; + dark: rgb(18,18,21); + shadow: rgb(0,0,0); + + highlight: #252458; + highlightText: #FFFFFF; + + link: #605ee6; + linkVisited: #605ee6; +} + +OBSTheme::disabled { + text: #484848; + buttonText: #484848; + brightText: #484848; +} + +OBSTheme::inactive { + highlight: rgb(48,47,48); + highlightText: rgb(255, 255, 255); +} + /* General style, we override only what is needed. */ QWidget { background-color: #181819; diff --git a/UI/data/themes/Dark.qss b/UI/data/themes/Dark.qss index 6140a3234..976cc7e26 100644 --- a/UI/data/themes/Dark.qss +++ b/UI/data/themes/Dark.qss @@ -28,6 +28,44 @@ /* rgb(42,130,218); /* blue */ +/* 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. + * Using it without will set all three (making 'active' a bit redundant) */ +OBSTheme { + window: rgb(58,57,58); /* dark */ + windowText: rgb(225,224,225); /* veryLight */ + base: rgb(31,30,31); /* veryDark */ + alternateBase: rgb(11,10,11); /* veryVeryDark */ + text: rgb(225,224,225); /* veryLight */ + button: rgb(88,87,88); /* kindaDark */ + buttonText: rgb(225,224,225); /* veryLight */ + brightText: rgb(200,199,200); /* lighter */ + + light: rgb(88,87,88); /* kindaDark */ + mid: rgb(58,57,58); /* dark */ + dark: rgb(31,30,31); /* veryDark */ + shadow: rgb(11,10,11); /* veryVeryDark */ + + highlight: rgb(42,130,218); /* blue */ + highlightText: rgb(0,0,0); + + link: rgb(42,130,218); /* blue */ + linkVisited: rgb(42,130,218); /* blue */ +} + +OBSTheme::disabled { + text: rgb(200,199,200); /* lighter */ + buttonText: rgb(200,199,200); /* lighter */ + brightText: rgb(200,199,200); /* lighter */ +} + +OBSTheme::inactive { + highlight: rgb(48,47,48); + highlightText: rgb(255, 255, 255); +} + + /* General style, we override only what is needed. */ QWidget { background-color: rgb(58,57,58); /* dark */ diff --git a/UI/data/themes/Rachni.qss b/UI/data/themes/Rachni.qss index be067c067..f3f53e5d3 100644 --- a/UI/data/themes/Rachni.qss +++ b/UI/data/themes/Rachni.qss @@ -40,6 +40,44 @@ /***************************************************************************/ +/************************/ +/* ---- Main Theme ---- */ +/************************/ + +OBSTheme { + window: rgb(49, 54, 59); /* Blue-gray */ + windowText: rgb(239, 240, 241); /* White */ + base: rgb(0, 139, 163); /* Dark Cyan (Primary Dark) */ + alternateBase: rgb(186, 45, 101); /* Dark Pink (Secondary Dark) */ + text: rgb(239, 240, 241); /* White */ + button: rgb(0, 188, 212); /* Cyan (Primary) */ + buttonText: rgb(239, 240, 241); /* White */ + brightText: rgb(255, 148, 194); /* Light Pink (Secondary Light) */ + + light: rgb(162, 161, 162); /* Lighter Gray */ + mid: rgb(118, 121, 124); /* Light Grey */ + dark: rgb(84, 87, 91); /* Gray */ + shadow: rgb(35, 38, 41); /* Dark Gray */ + + highlight: rgb(98, 238, 255); /* Light Cyan (Primary Light) */ + highlightText: rgb(0,0,0); + + link: rgb(98, 238, 255); /* Light Cyan (Primary Light) */ + linkVisited: rgb(98, 238, 255); /* Light Cyan (Primary Light) */ +} + +OBSTheme::disabled { + text: rgb(255, 148, 194); /* Light Pink (Secondary Light) */ + buttonText: rgb(255, 148, 194); /* Light Pink (Secondary Light) */ + brightText: rgb(255, 148, 194); /* Light Pink (Secondary Light) */ +} + +OBSTheme::inactive { + highlight: rgb(0, 188, 212); /* Cyan (Primary) */ + highlightText: rgb(239, 240, 241); /* White */ +} + + /*************************/ /* --- General style --- */ /*************************/ diff --git a/UI/obs-app.cpp b/UI/obs-app.cpp index 2db37b583..77e59ea4c 100644 --- a/UI/obs-app.cpp +++ b/UI/obs-app.cpp @@ -24,9 +24,10 @@ #include #include #include -#include +#include #include #include +#include #include #include @@ -782,6 +783,192 @@ bool OBSApp::InitLocale() return true; } +void OBSApp::AddExtraThemeColor(QPalette &pal, int group, + const char *name, uint32_t color) +{ + std::function func; + +#define DEF_PALETTE_ASSIGN(name) \ + do { \ + func = [&] (QPalette::ColorGroup group) \ + { \ + pal.setColor(group, QPalette::name, \ + QColor::fromRgb(color)); \ + }; \ + } while (false) + + if (astrcmpi(name, "alternateBase") == 0) { + DEF_PALETTE_ASSIGN(AlternateBase); + } else if (astrcmpi(name, "base") == 0) { + DEF_PALETTE_ASSIGN(Base); + } else if (astrcmpi(name, "brightText") == 0) { + DEF_PALETTE_ASSIGN(BrightText); + } else if (astrcmpi(name, "button") == 0) { + DEF_PALETTE_ASSIGN(Button); + } else if (astrcmpi(name, "buttonText") == 0) { + DEF_PALETTE_ASSIGN(ButtonText); + } else if (astrcmpi(name, "brightText") == 0) { + DEF_PALETTE_ASSIGN(BrightText); + } else if (astrcmpi(name, "dark") == 0) { + DEF_PALETTE_ASSIGN(Dark); + } else if (astrcmpi(name, "highlight") == 0) { + DEF_PALETTE_ASSIGN(Highlight); + } else if (astrcmpi(name, "highlightedText") == 0) { + DEF_PALETTE_ASSIGN(HighlightedText); + } else if (astrcmpi(name, "light") == 0) { + DEF_PALETTE_ASSIGN(Light); + } else if (astrcmpi(name, "link") == 0) { + DEF_PALETTE_ASSIGN(Link); + } else if (astrcmpi(name, "linkVisited") == 0) { + DEF_PALETTE_ASSIGN(LinkVisited); + } else if (astrcmpi(name, "mid") == 0) { + DEF_PALETTE_ASSIGN(Mid); + } else if (astrcmpi(name, "midlight") == 0) { + DEF_PALETTE_ASSIGN(Midlight); + } else if (astrcmpi(name, "shadow") == 0) { + DEF_PALETTE_ASSIGN(Shadow); + } else if (astrcmpi(name, "text") == 0 || + astrcmpi(name, "foreground") == 0) { + DEF_PALETTE_ASSIGN(Text); + } else if (astrcmpi(name, "toolTipBase") == 0) { + DEF_PALETTE_ASSIGN(ToolTipBase); + } else if (astrcmpi(name, "toolTipText") == 0) { + DEF_PALETTE_ASSIGN(ToolTipText); + } else if (astrcmpi(name, "windowText") == 0) { + DEF_PALETTE_ASSIGN(WindowText); + } else if (astrcmpi(name, "window") == 0 || + astrcmpi(name, "background") == 0) { + DEF_PALETTE_ASSIGN(Window); + } else { + return; + } + +#undef DEF_PALETTE_ASSIGN + + switch (group) { + case QPalette::Disabled: + case QPalette::Active: + case QPalette::Inactive: + func((QPalette::ColorGroup)group); + break; + default: + func((QPalette::ColorGroup)QPalette::Disabled); + func((QPalette::ColorGroup)QPalette::Active); + func((QPalette::ColorGroup)QPalette::Inactive); + } +} + +struct CFParser { + cf_parser cfp = {}; + inline ~CFParser() {cf_parser_free(&cfp);} + inline operator cf_parser*() {return &cfp;} + inline cf_parser *operator->() {return &cfp;} +}; + +void OBSApp::ParseExtraThemeData(const char *path) +{ + BPtr data = os_quick_read_utf8_file(path); + QPalette pal = palette(); + CFParser cfp; + int ret; + + cf_parser_parse(cfp, data, path); + + while (cf_go_to_token(cfp, "OBSTheme", nullptr)) { + if (!cf_next_token(cfp)) return; + + int group = -1; + + if (cf_token_is(cfp, ":")) { + ret = cf_next_token_should_be(cfp, ":", nullptr, + nullptr); + if (ret != PARSE_SUCCESS) continue; + + if (!cf_next_token(cfp)) return; + + if (cf_token_is(cfp, "disabled")) { + group = QPalette::Disabled; + } else if (cf_token_is(cfp, "active")) { + group = QPalette::Active; + } else if (cf_token_is(cfp, "inactive")) { + group = QPalette::Inactive; + } else { + continue; + } + + if (!cf_next_token(cfp)) return; + } + + if (!cf_token_is(cfp, "{")) continue; + + for (;;) { + if (!cf_next_token(cfp)) return; + + 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; + + const char *array; + uint32_t color = 0; + + if (cf_token_is(cfp, "#")) { + array = cfp->cur_token->str.array; + color = strtol(array + 1, nullptr, 16); + + } else if (cf_token_is(cfp, "rgb")) { + ret = cf_next_token_should_be(cfp, "(", ";", + nullptr); + if (ret != PARSE_SUCCESS) continue; + if (!cf_next_token(cfp)) return; + + array = cfp->cur_token->str.array; + color |= strtol(array, nullptr, 10) << 16; + + ret = cf_next_token_should_be(cfp, ",", ";", + nullptr); + if (ret != PARSE_SUCCESS) continue; + if (!cf_next_token(cfp)) return; + + array = cfp->cur_token->str.array; + color |= strtol(array, nullptr, 10) << 8; + + ret = cf_next_token_should_be(cfp, ",", ";", + nullptr); + if (ret != PARSE_SUCCESS) continue; + if (!cf_next_token(cfp)) return; + + array = cfp->cur_token->str.array; + color |= strtol(array, nullptr, 10); + + } else if (cf_token_is(cfp, "white")) { + color = 0xFFFFFF; + + } else if (cf_token_is(cfp, "black")) { + color = 0; + } + + if (!cf_go_to_token(cfp, ";", nullptr)) return; + + AddExtraThemeColor(pal, group, name->array, color); + } + + ret = cf_token_should_be(cfp, "}", "}", nullptr); + if (ret != PARSE_SUCCESS) continue; + } + + setPalette(pal); +} + bool OBSApp::SetTheme(std::string name, std::string path) { theme = name; @@ -803,12 +990,17 @@ bool OBSApp::SetTheme(std::string name, std::string path) } QString mpath = QString("file:///") + path.c_str(); + setPalette(defaultPalette); setStyleSheet(mpath); + ParseExtraThemeData(path.c_str()); + return true; } bool OBSApp::InitTheme() { + defaultPalette = palette(); + const char *themeName = config_get_string(globalConfig, "General", "CurrentTheme"); if (!themeName) { diff --git a/UI/obs-app.hpp b/UI/obs-app.hpp index 4a17e4971..f0b2224cb 100644 --- a/UI/obs-app.hpp +++ b/UI/obs-app.hpp @@ -87,6 +87,12 @@ private: inline void ResetHotkeyState(bool inFocus); + QPalette defaultPalette; + + void ParseExtraThemeData(const char *path); + void AddExtraThemeColor(QPalette &pal, int group, + const char *name, uint32_t color); + public: OBSApp(int &argc, char **argv, profiler_name_store_t *store); ~OBSApp();