diff --git a/src/app/application.cpp b/src/app/application.cpp index 4673ca787..1e0db75e8 100644 --- a/src/app/application.cpp +++ b/src/app/application.cpp @@ -56,6 +56,7 @@ #endif // Q_OS_MAC #include "addnewtorrentdialog.h" #include "gui/guiiconprovider.h" +#include "gui/uithememanager.h" #include "gui/utils.h" #include "mainwindow.h" #include "shutdownconfirmdialog.h" @@ -558,7 +559,9 @@ int Application::exec(const QStringList ¶ms) } #endif // DISABLE_WEBUI #else + UIThemeManager::initInstance(); m_window = new MainWindow; + UIThemeManager::instance()->applyStyleSheet(); #endif // DISABLE_GUI m_running = true; @@ -727,6 +730,7 @@ void Application::cleanup() shutdownBRDestroy(reinterpret_cast(m_window->effectiveWinId())); #endif // Q_OS_WIN delete m_window; + UIThemeManager::freeInstance(); } #endif // DISABLE_GUI diff --git a/src/base/preferences.cpp b/src/base/preferences.cpp index 4744696d7..0112040f2 100644 --- a/src/base/preferences.cpp +++ b/src/base/preferences.cpp @@ -105,6 +105,27 @@ void Preferences::setLocale(const QString &locale) setValue("Preferences/General/Locale", locale); } +bool Preferences::useCustomUITheme() const +{ + return value("Preferences/General/UseCustomUITheme", false).toBool() + && !customUIThemePath().isEmpty(); +} + +void Preferences::setUseCustomUITheme(const bool use) +{ + setValue("Preferences/General/UseCustomUITheme", use); +} + +QString Preferences::customUIThemePath() const +{ + return value("Preferences/General/CustomUIThemePath").toString(); +} + +void Preferences::setCustomUIThemePath(const QString &path) +{ + setValue("Preferences/General/CustomUIThemePath", path); +} + bool Preferences::deleteTorrentFilesAsDefault() const { return value("Preferences/General/DeleteTorrentsFilesAsDefault", false).toBool(); diff --git a/src/base/preferences.h b/src/base/preferences.h index 480474e9d..18af4907a 100644 --- a/src/base/preferences.h +++ b/src/base/preferences.h @@ -98,6 +98,10 @@ public: // General options QString getLocale() const; void setLocale(const QString &locale); + bool useCustomUITheme() const; + void setUseCustomUITheme(bool use); + QString customUIThemePath() const; + void setCustomUIThemePath(const QString &path); bool deleteTorrentFilesAsDefault() const; void setDeleteTorrentFilesAsDefault(bool del); bool confirmOnExit() const; diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 8c4b73ce7..80f9c3687 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -57,6 +57,7 @@ transferlistfilterswidget.h transferlistmodel.h transferlistsortmodel.h transferlistwidget.h +uithememanager.h updownratiodialog.h utils.h @@ -102,6 +103,7 @@ transferlistfilterswidget.cpp transferlistmodel.cpp transferlistsortmodel.cpp transferlistwidget.cpp +uithememanager.cpp updownratiodialog.cpp utils.cpp diff --git a/src/gui/gui.pri b/src/gui/gui.pri index 587bf1236..6d3387643 100644 --- a/src/gui/gui.pri +++ b/src/gui/gui.pri @@ -62,6 +62,7 @@ HEADERS += \ $$PWD/transferlistmodel.h \ $$PWD/transferlistsortmodel.h \ $$PWD/transferlistwidget.h \ + $$PWD/uithememanager.h \ $$PWD/updownratiodialog.h \ $$PWD/utils.h @@ -118,6 +119,7 @@ SOURCES += \ $$PWD/transferlistmodel.cpp \ $$PWD/transferlistsortmodel.cpp \ $$PWD/transferlistwidget.cpp \ + $$PWD/uithememanager.cpp \ $$PWD/updownratiodialog.cpp \ $$PWD/utils.cpp diff --git a/src/gui/optionsdialog.cpp b/src/gui/optionsdialog.cpp index 84d2e9e9a..8c5c18bc5 100644 --- a/src/gui/optionsdialog.cpp +++ b/src/gui/optionsdialog.cpp @@ -175,6 +175,8 @@ OptionsDialog::OptionsDialog(QWidget *parent) // Languages supported initializeLanguageCombo(); + initializeThemeCombo(); + // Load week days (scheduler) m_ui->comboBoxScheduleDays->addItems(translatedWeekdayNames()); @@ -216,6 +218,7 @@ OptionsDialog::OptionsDialog(QWidget *parent) // Apply button is activated when a value is changed // General tab connect(m_ui->comboI18n, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); + connect(m_ui->comboTheme, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); connect(m_ui->confirmDeletion, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkAltRowColors, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkHideZero, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); @@ -493,6 +496,38 @@ void OptionsDialog::initializeLanguageCombo() } } +void OptionsDialog::initializeThemeCombo() +{ + m_ui->comboTheme->addItem(tr("Default")); + const QString customUIThemePath = Preferences::instance()->customUIThemePath(); + if (!customUIThemePath.isEmpty()) + m_ui->comboTheme->addItem(Utils::Fs::toNativePath(customUIThemePath)); + m_ui->comboTheme->insertSeparator(m_ui->comboTheme->count()); + m_ui->comboTheme->addItem(tr("Select...")); + m_ui->comboTheme->setCurrentIndex(Preferences::instance()->useCustomUITheme() ? 1 : 0); + + connect(m_ui->comboTheme, qOverload(&QComboBox::currentIndexChanged), this, [this](const int index) + { + if (index != (m_ui->comboTheme->count() - 1)) + return; + + m_uiThemeFilePath = QFileDialog::getOpenFileName(this, tr("Select qBittorrent theme file"), {}, tr("qBittorrent Theme File (*.qbtheme)")); + m_ui->comboTheme->blockSignals(true); + if (!m_uiThemeFilePath.isEmpty()) { + if (m_ui->comboTheme->count() == 3) + m_ui->comboTheme->insertItem(1, Utils::Fs::toNativePath(m_uiThemeFilePath)); + else + m_ui->comboTheme->setItemText(1, Utils::Fs::toNativePath(m_uiThemeFilePath)); + m_ui->comboTheme->setCurrentIndex(1); + } + else { + // don't leave "Select..." as current text + m_ui->comboTheme->setCurrentIndex(Preferences::instance()->useCustomUITheme() ? 1 : 0); + } + m_ui->comboTheme->blockSignals(false); + }); +} + // Main destructor OptionsDialog::~OptionsDialog() { @@ -563,6 +598,15 @@ void OptionsDialog::saveOptions() // General preferences pref->setLocale(locale); + + if (!m_uiThemeFilePath.isEmpty() + && (m_ui->comboTheme->currentIndex() == 1)) { + // only change if current selection is still new m_uiThemeFilePath + pref->setCustomUIThemePath(m_uiThemeFilePath); + m_uiThemeFilePath.clear(); + } + pref->setUseCustomUITheme(m_ui->comboTheme->currentIndex() == 1); + pref->setConfirmTorrentDeletion(m_ui->confirmDeletion->isChecked()); pref->setAlternatingRowColors(m_ui->checkAltRowColors->isChecked()); pref->setHideZeroValues(m_ui->checkHideZero->isChecked()); diff --git a/src/gui/optionsdialog.h b/src/gui/optionsdialog.h index f66cabcba..f94b562bf 100644 --- a/src/gui/optionsdialog.h +++ b/src/gui/optionsdialog.h @@ -117,6 +117,7 @@ private: void saveOptions(); void loadOptions(); void initializeLanguageCombo(); + void initializeThemeCombo(); static QString languageToLocalizedString(const QLocale &locale); // General options QString getLocale() const; @@ -183,6 +184,7 @@ private: AdvancedSettings *m_advancedSettings; QList m_addedScanDirs; QList m_removedScanDirs; + QString m_uiThemeFilePath; }; #endif // OPTIONSDIALOG_H diff --git a/src/gui/optionsdialog.ui b/src/gui/optionsdialog.ui index ceed67c58..e9efb4dd3 100644 --- a/src/gui/optionsdialog.ui +++ b/src/gui/optionsdialog.ui @@ -130,63 +130,84 @@ - Language + Interface - - - - - - - User Interface Language: - - - - - - - - 0 - 0 - - - - QComboBox::AdjustToContents - - - 0 - - - - - - - - true - - - - (Requires restart) - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - + + + + + Language: + + + + + + + + 0 + 0 + + + + QComboBox::AdjustToContents + + + 0 + + + + + + + + true + + + + (Requires restart) + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + 200 + 20 + + + + + + + + Theme: + + + + + + + + + + + true + + + + (Requires restart) + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + diff --git a/src/gui/uithememanager.cpp b/src/gui/uithememanager.cpp new file mode 100644 index 000000000..34f73b62a --- /dev/null +++ b/src/gui/uithememanager.cpp @@ -0,0 +1,84 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2019 Prince Gupta + * + * 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 the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If + * you modify file(s), you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. + */ + +#include "uithememanager.h" + +#include +#include +#include + +#include "base/logger.h" +#include "base/preferences.h" + +UIThemeManager *UIThemeManager::m_instance = nullptr; + +void UIThemeManager::freeInstance() +{ + if (m_instance) { + delete m_instance; + m_instance = nullptr; + } +} + +void UIThemeManager::initInstance() +{ + if (!m_instance) + m_instance = new UIThemeManager; +} + +UIThemeManager::UIThemeManager() +{ + const Preferences *const pref = Preferences::instance(); + if (pref->useCustomUITheme() + && !QResource::registerResource(pref->customUIThemePath(), "/uitheme")) + LogMsg(tr("Failed to load UI theme from file: \"%1\"").arg(pref->customUIThemePath()), Log::WARNING); +} + +UIThemeManager *UIThemeManager::instance() +{ + return m_instance; +} + +void UIThemeManager::applyStyleSheet() const +{ + if (!Preferences::instance()->useCustomUITheme()) { + qApp->setStyleSheet({}); + return; + } + + QFile qssFile(":uitheme/stylesheet.qss"); + if (!qssFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + qApp->setStyleSheet({}); + LogMsg(tr("Couldn't apply theme stylesheet. stylesheet.qss couldn't be opened. Reason: %1").arg(qssFile.errorString()) + , Log::WARNING); + return; + } + + qApp->setStyleSheet(qssFile.readAll()); +} diff --git a/src/gui/uithememanager.h b/src/gui/uithememanager.h new file mode 100644 index 000000000..d1ba70f8e --- /dev/null +++ b/src/gui/uithememanager.h @@ -0,0 +1,52 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2019 Prince Gupta + * + * 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 the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If + * you modify file(s), you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. + */ + +#pragma once + +#include + +class QString; + +class UIThemeManager : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(UIThemeManager) + +public: + static void initInstance(); + static void freeInstance(); + static UIThemeManager *instance(); + + void applyStyleSheet() const; + +private: + UIThemeManager(); // singleton class + + static UIThemeManager *m_instance; +};