diff --git a/obs/data/locale/en-US.ini b/obs/data/locale/en-US.ini
index bb38077e1..d51ab072b 100644
--- a/obs/data/locale/en-US.ini
+++ b/obs/data/locale/en-US.ini
@@ -217,6 +217,7 @@ Basic.Settings.Confirm="You have unsaved changes. Save changes?"
# basic mode 'general' settings
Basic.Settings.General="General"
+Basic.Settings.General.Theme="Theme"
Basic.Settings.General.Language="Language"
# basic mode 'stream' settings
diff --git a/obs/forms/OBSBasicSettings.ui b/obs/forms/OBSBasicSettings.ui
index a3df70654..65813850a 100644
--- a/obs/forms/OBSBasicSettings.ui
+++ b/obs/forms/OBSBasicSettings.ui
@@ -6,7 +6,7 @@
0
0
- 770
+ 895
602
@@ -143,6 +143,16 @@
-
+ -
+
+
+ Basic.Settings.General.Theme
+
+
+
+ -
+
+
@@ -2326,8 +2336,8 @@
0
0
- 609
- 553
+ 724
+ 536
@@ -2362,7 +2372,7 @@
- 170
+ 0
0
@@ -2409,7 +2419,7 @@
- 170
+ 0
0
diff --git a/obs/forms/obs.qrc b/obs/forms/obs.qrc
index c50846d0b..be69e0bee 100644
--- a/obs/forms/obs.qrc
+++ b/obs/forms/obs.qrc
@@ -1,5 +1,5 @@
-
+
images/configuration21_16.png
images/list_remove.png
images/add.png
@@ -10,7 +10,7 @@
images/up.png
images/obs.png
-
+
images/settings/advanced.png
images/settings/network.png
images/settings/video-display-3.png
diff --git a/obs/obs-app.cpp b/obs/obs-app.cpp
index 4d352d282..1f9bb3437 100644
--- a/obs/obs-app.cpp
+++ b/obs/obs-app.cpp
@@ -29,6 +29,7 @@
#include "qt-wrappers.hpp"
#include "obs-app.hpp"
#include "window-basic-main.hpp"
+#include "window-basic-settings.hpp"
#include "window-license-agreement.hpp"
#include "crash-report.hpp"
#include "platform.hpp"
@@ -233,6 +234,44 @@ bool OBSApp::InitLocale()
return true;
}
+bool OBSApp::SetTheme(std::string name, std::string path)
+{
+ theme = name;
+
+ /* Check user dir first, then preinstalled themes. */
+ if (path == "") {
+ char userDir[512];
+ name = "themes/" + name + ".qss";
+ string temp = "obs-studio/" + name;
+ int ret = os_get_config_path(userDir, sizeof(userDir),
+ temp.c_str());
+
+ if (ret > 0 && QFile::exists(userDir)) {
+ path = string(userDir);
+ } else if (!GetDataFilePath(name.c_str(), path)) {
+ OBSErrorBox(NULL, "Failed to find %s.", name.c_str());
+ return false;
+ }
+ }
+
+ QString mpath = QString("file:///") + path.c_str();
+ setStyleSheet(mpath);
+ return true;
+}
+
+bool OBSApp::InitTheme()
+{
+ const char *themeName = config_get_string(globalConfig, "General",
+ "Theme");
+
+ if (!themeName)
+ themeName = "Default";
+
+ stringstream t;
+ t << themeName;
+ return SetTheme(t.str());
+}
+
OBSApp::OBSApp(int &argc, char **argv)
: QApplication(argc, argv)
{}
@@ -247,6 +286,8 @@ void OBSApp::AppInit()
throw "Failed to initialize global config";
if (!InitLocale())
throw "Failed to load locale";
+ if (!InitTheme())
+ throw "Failed to load theme";
}
const char *OBSApp::GetRenderModule() const
diff --git a/obs/obs-app.hpp b/obs/obs-app.hpp
index b13f8eb8e..79853f594 100644
--- a/obs/obs-app.hpp
+++ b/obs/obs-app.hpp
@@ -55,6 +55,7 @@ class OBSApp : public QApplication {
private:
std::string locale;
+ std::string theme;
ConfigFile globalConfig;
TextLookup textLookup;
QPointer mainWindow;
@@ -62,6 +63,7 @@ private:
bool InitGlobalConfig();
bool InitGlobalConfigDefaults();
bool InitLocale();
+ bool InitTheme();
public:
OBSApp(int &argc, char **argv);
@@ -78,6 +80,9 @@ public:
return locale.c_str();
}
+ inline const char *GetTheme() const {return theme.c_str();}
+ bool SetTheme(std::string name, std::string path = "");
+
inline lookup_t *GetTextLookup() const {return textLookup;}
inline const char *GetString(const char *lookupVal) const
diff --git a/obs/window-basic-main.cpp b/obs/window-basic-main.cpp
index 0a03dbbf3..69870e3c3 100644
--- a/obs/window-basic-main.cpp
+++ b/obs/window-basic-main.cpp
@@ -82,6 +82,7 @@ OBSBasic::OBSBasic(QWidget *parent)
ui (new Ui::OBSBasic)
{
ui->setupUi(this);
+ copyActionsDynamicProperties();
int width = config_get_int(App()->GlobalConfig(), "MainWindow", "cx");
@@ -197,6 +198,26 @@ static obs_data_t *GenerateSaveData()
return saveData;
}
+void OBSBasic::copyActionsDynamicProperties()
+{
+ // Themes need the QAction dynamic properties
+ for (QAction *x : ui->scenesToolbar->actions()) {
+ QWidget* temp = ui->scenesToolbar->widgetForAction(x);
+
+ for (QByteArray &y : x->dynamicPropertyNames()) {
+ temp->setProperty(y, x->property(y));
+ }
+ }
+
+ for (QAction *x : ui->sourcesToolbar->actions()) {
+ QWidget* temp = ui->sourcesToolbar->widgetForAction(x);
+
+ for (QByteArray &y : x->dynamicPropertyNames()) {
+ temp->setProperty(y, x->property(y));
+ }
+ }
+}
+
void OBSBasic::ClearVolumeControls()
{
VolControl *control;
diff --git a/obs/window-basic-main.hpp b/obs/window-basic-main.hpp
index 2bdaaaa67..08f6523c2 100644
--- a/obs/window-basic-main.hpp
+++ b/obs/window-basic-main.hpp
@@ -180,6 +180,7 @@ private:
void AddSource(const char *id);
QMenu *CreateAddSourcePopupMenu();
void AddSourcePopupMenu(const QPoint &pos);
+ void copyActionsDynamicProperties();
public:
OBSScene GetCurrentScene();
diff --git a/obs/window-basic-settings.cpp b/obs/window-basic-settings.cpp
index 4267e9346..043866cca 100644
--- a/obs/window-basic-settings.cpp
+++ b/obs/window-basic-settings.cpp
@@ -1,5 +1,6 @@
/******************************************************************************
Copyright (C) 2013-2014 by Hugh Bailey
+ Philippe Groarke
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -23,6 +24,7 @@
#include
#include
#include
+#include
#include "obs-app.hpp"
#include "platform.hpp"
@@ -134,6 +136,7 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
ui->setupUi(this);
HookWidget(ui->language, COMBO_CHANGED, GENERAL_CHANGED);
+ HookWidget(ui->theme, COMBO_CHANGED, GENERAL_CHANGED);
HookWidget(ui->outputMode, COMBO_CHANGED, OUTPUTS_CHANGED);
HookWidget(ui->streamType, COMBO_CHANGED, STREAM1_CHANGED);
HookWidget(ui->simpleOutputPath, EDIT_CHANGED, OUTPUTS_CHANGED);
@@ -336,11 +339,52 @@ void OBSBasicSettings::LoadLanguageList()
ui->language->model()->sort(0);
}
+void OBSBasicSettings::LoadThemeList()
+{
+ /* Save theme if user presses Cancel */
+ savedTheme = string(App()->GetTheme());
+
+ ui->theme->clear();
+ QSet uniqueSet;
+ string themeDir;
+ char userThemeDir[512];
+ int ret = os_get_config_path(userThemeDir, sizeof(userThemeDir),
+ "obs-studio/themes/");
+ GetDataFilePath("themes/", themeDir);
+
+ /* Check user dir first. */
+ if (ret > 0) {
+ QDirIterator it(QString(userThemeDir), QStringList() << "*.qss",
+ QDir::Files);
+ while (it.hasNext()) {
+ it.next();
+ QString name = it.fileName().section(".",0,0);
+ ui->theme->addItem(name);
+ uniqueSet.insert(name);
+ }
+ }
+
+ /* Check shipped themes. */
+ QDirIterator uIt(QString(themeDir.c_str()), QStringList() << "*.qss",
+ QDir::Files);
+ while (uIt.hasNext()) {
+ uIt.next();
+ QString name = uIt.fileName().section(".",0,0);
+ if (!uniqueSet.contains(name))
+ ui->theme->addItem(name);
+ }
+
+ int idx = ui->theme->findText(App()->GetTheme());
+ if (idx != -1)
+ ui->theme->setCurrentIndex(idx);
+}
+
void OBSBasicSettings::LoadGeneralSettings()
{
loading = true;
LoadLanguageList();
+ LoadThemeList();
loading = false;
}
@@ -984,6 +1028,16 @@ void OBSBasicSettings::SaveGeneralSettings()
if (WidgetChanged(ui->language))
config_set_string(GetGlobalConfig(), "General", "Language",
language.c_str());
+
+ int themeIndex = ui->theme->currentIndex();
+ QString themeData = ui->theme->itemText(themeIndex);
+ string theme = themeData.toStdString();
+
+ if (WidgetChanged(ui->theme)) {
+ config_set_string(GetGlobalConfig(), "General", "Theme",
+ theme.c_str());
+ App()->SetTheme(theme);
+ }
}
void OBSBasicSettings::SaveStream1Settings()
@@ -1241,6 +1295,12 @@ void OBSBasicSettings::closeEvent(QCloseEvent *event)
event->ignore();
}
+void OBSBasicSettings::on_theme_activated(int idx)
+{
+ string currT = ui->theme->itemText(idx).toStdString();
+ App()->SetTheme(currT);
+}
+
void OBSBasicSettings::on_simpleOutUseBufsize_toggled(bool checked)
{
if (!checked)
@@ -1276,6 +1336,8 @@ void OBSBasicSettings::on_buttonBox_clicked(QAbstractButton *button)
if (val == QDialogButtonBox::AcceptRole ||
val == QDialogButtonBox::RejectRole) {
+ if (val == QDialogButtonBox::RejectRole)
+ App()->SetTheme(savedTheme);
ClearChanged();
close();
}
diff --git a/obs/window-basic-settings.hpp b/obs/window-basic-settings.hpp
index f0fe34016..b36e8e75b 100644
--- a/obs/window-basic-settings.hpp
+++ b/obs/window-basic-settings.hpp
@@ -1,5 +1,6 @@
/******************************************************************************
Copyright (C) 2013 by Hugh Bailey
+ Philippe Groarke
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,6 +21,7 @@
#include
#include
#include
+#include
#include
@@ -45,6 +47,7 @@ private:
bool advancedChanged = false;
int pageIndex = 0;
bool loading = true;
+ std::string savedTheme;
OBSPropertiesView *streamProperties = nullptr;
OBSPropertiesView *streamEncoderProps = nullptr;
@@ -104,6 +107,7 @@ private:
/* general */
void LoadLanguageList();
+ void LoadThemeList();
/* output */
void LoadSimpleOutputSettings();
@@ -136,6 +140,8 @@ private:
void SaveSettings();
private slots:
+ void on_theme_activated(int idx);
+
void on_simpleOutUseBufsize_toggled(bool checked);
void on_simpleOutputVBitrate_valueChanged(int val);