From 28b720d5ed3cb4afeedf4a1bba67e1c02223c940 Mon Sep 17 00:00:00 2001 From: xdustinface Date: Mon, 8 Jun 2020 12:53:14 +0200 Subject: [PATCH 1/3] qt: Set the default theme properly --- src/qt/guiutil.cpp | 5 +++++ src/qt/guiutil.h | 3 +++ src/qt/optionsmodel.cpp | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 478bc7e0200d..9bef16e253a9 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -1025,6 +1025,11 @@ const std::vector listThemes() return vecThemes; } +const QString getDefaultTheme() +{ + return defaultTheme; +} + void loadStyleSheet(QWidget* widget, bool fDebugWidget) { AssertLockNotHeld(cs_css); diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index a81ceaab0822..db7fd9e6c219 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -253,6 +253,9 @@ namespace GUIUtil /** Return a list of all theme css files */ const std::vector listThemes(); + /** Return the name of the default theme `*/ + const QString getDefaultTheme(); + /** Updates the widgets stylesheet and adds it to the list of ui debug elements if fDebugWidget is true. Beeing on that list means the stylesheet of the widget gets updated if the related css files has been changed if -debug-ui mode is active. */ diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index e56da201aa2d..7fee11b74c67 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -85,7 +85,7 @@ void OptionsModel::Init(bool resetSettings) strThirdPartyTxUrls = settings.value("strThirdPartyTxUrls", "").toString(); if (!settings.contains("theme")) - settings.setValue("theme", ""); + settings.setValue("theme", GUIUtil::getDefaultTheme()); #ifdef ENABLE_WALLET if (!settings.contains("fCoinControlFeatures")) From 03ab28c66680cd2610f9ef2356f0e76a8c1750d9 Mon Sep 17 00:00:00 2001 From: xdustinface Date: Mon, 8 Jun 2020 17:18:30 +0200 Subject: [PATCH 2/3] qt: Keep track of disabled rects for macOS This allows enabling them again on theme changes --- src/qt/guiutil.cpp | 22 ++++++++++++++++++++++ src/qt/guiutil.h | 3 +++ 2 files changed, 25 insertions(+) diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 9bef16e253a9..829835e7ca67 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -132,6 +132,11 @@ static std::set setFixedPitchFontUpdates; // Contains all widgets where a non-default fontsize has been seet with GUIUtil::setFont static std::map mapFontSizeUpdates; +#ifdef Q_OS_MAC +// Contains all widgets where the macOS focus rect has been disabled. +static std::set setRectsDisabled; +#endif + static const std::map themedColors = { { ThemedColor::DEFAULT, QColor(85, 85, 85) }, { ThemedColor::UNCONFIRMED, QColor(128, 128, 128) }, @@ -1528,6 +1533,23 @@ void disableMacFocusRect(const QWidget* w) for (const auto& c : w->findChildren()) { if (c->testAttribute(Qt::WA_MacShowFocusRect)) { c->setAttribute(Qt::WA_MacShowFocusRect, !dashThemeActive()); + setRectsDisabled.emplace(c); + } + } +#endif +} + +void updateMacFocusRects() +{ +#ifdef Q_OS_MAC + QWidgetList allWidgets = QApplication::allWidgets(); + auto it = setRectsDisabled.begin(); + while (it != setRectsDisabled.end()) { + if (allWidgets.contains(*it)) { + (*it)->setAttribute(Qt::WA_MacShowFocusRect, !dashThemeActive()); + ++it; + } else { + it = setRectsDisabled.erase(it); } } #endif diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index db7fd9e6c219..c38102ddee64 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -342,6 +342,9 @@ namespace GUIUtil * set in the css files */ void disableMacFocusRect(const QWidget* w); + /** Enable/Disable the macOS focus rects depending on the current theme. */ + void updateMacFocusRects(); + /* Convert QString to OS specific boost path through UTF-8 */ fs::path qstringToBoostPath(const QString &path); From ceeccfbe7fe13cbc82e91f5e1df8e645a07be2eb Mon Sep 17 00:00:00 2001 From: xdustinface Date: Mon, 8 Jun 2020 14:31:23 +0200 Subject: [PATCH 3/3] qt: Introduce runtime theme changes Runtime theme changes means no more client restart required if the theme gets changed in the options dialog. In the RPCConsole's StyleChange event make sure following things are still correct after a runtime theme change: - Hide prompt icon for dash themes in rpc console if dash theme gets activated. - Clear rpc console on theme changes to make sure fonts/sizes/colors are correct. --- src/qt/bitcoingui.cpp | 3 +-- src/qt/dash.cpp | 2 ++ src/qt/guiutil.cpp | 45 ++++++++++++++++++++++------------------ src/qt/guiutil.h | 11 ++++++---- src/qt/optionsdialog.cpp | 18 +++++++++++++++- src/qt/optionsdialog.h | 3 +++ src/qt/optionsmodel.cpp | 6 ++---- src/qt/rpcconsole.cpp | 10 +++++++++ src/qt/rpcconsole.h | 1 + src/qt/utilitydialog.cpp | 2 +- 10 files changed, 69 insertions(+), 32 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 0418d9f88fee..63886e2346ae 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -125,8 +125,6 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle * spinnerFrame(0), platformStyle(_platformStyle) { - GUIUtil::loadStyleSheet(this); - QSettings settings; if (!restoreGeometry(settings.value("MainWindowGeometry").toByteArray())) { // Restore failed (perhaps missing setting), center the window @@ -844,6 +842,7 @@ void BitcoinGUI::optionsClicked() OptionsDialog dlg(this, enableWallet); dlg.setModel(clientModel->getOptionsModel()); + connect(&dlg, &OptionsDialog::themeChanged, [=]() { GUIUtil::loadTheme(); }); dlg.exec(); } diff --git a/src/qt/dash.cpp b/src/qt/dash.cpp index 1960be60c9eb..29d90bb47434 100644 --- a/src/qt/dash.cpp +++ b/src/qt/dash.cpp @@ -395,6 +395,8 @@ void BitcoinApplication::createWindow(const NetworkStyle *networkStyle) { window = new BitcoinGUI(platformStyle, networkStyle, 0); + GUIUtil::loadTheme(window); + pollShutdownTimer = new QTimer(window); connect(pollShutdownTimer, SIGNAL(timeout()), window, SLOT(detectShutdown())); } diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 829835e7ca67..f688a9a9531a 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -1035,7 +1035,7 @@ const QString getDefaultTheme() return defaultTheme; } -void loadStyleSheet(QWidget* widget, bool fDebugWidget) +void loadStyleSheet(QWidget* widget, bool fForceUpdate) { AssertLockNotHeld(cs_css); LOCK(cs_css); @@ -1046,7 +1046,7 @@ void loadStyleSheet(QWidget* widget, bool fDebugWidget) bool fDebugCustomStyleSheets = gArgs.GetBoolArg("-debug-ui", false) && isStyleSheetDirectoryCustom(); bool fStyleSheetChanged = false; - if (stylesheet == nullptr || fDebugCustomStyleSheets) { + if (stylesheet == nullptr || fForceUpdate || fDebugCustomStyleSheets) { auto hasModified = [](const std::vector& vecFiles) -> bool { static std::map mapLastModified; @@ -1063,7 +1063,7 @@ void loadStyleSheet(QWidget* widget, bool fDebugWidget) }; auto loadFiles = [&](const std::vector& vecFiles) -> bool { - if (fDebugCustomStyleSheets && !hasModified(vecFiles)) { + if (!fForceUpdate && fDebugCustomStyleSheets && !hasModified(vecFiles)) { return false; } @@ -1096,29 +1096,27 @@ void loadStyleSheet(QWidget* widget, bool fDebugWidget) fStyleSheetChanged = loadFiles(vecFiles); } - bool fUpdateStyleSheet = fDebugCustomStyleSheets && fStyleSheetChanged; + bool fUpdateStyleSheet = fForceUpdate || (fDebugCustomStyleSheets && fStyleSheetChanged); - if (fDebugWidget) { + if (widget) { setWidgets.insert(widget); - QWidgetList allWidgets = QApplication::allWidgets(); - auto it = setWidgets.begin(); - while (it != setWidgets.end()) { - if (!allWidgets.contains(*it)) { - it = setWidgets.erase(it); - continue; - } - if (fUpdateStyleSheet && *it != widget) { - (*it)->setStyleSheet(*stylesheet); - } - ++it; - } + widget->setStyleSheet(*stylesheet); } - if (widget) { - widget->setStyleSheet(*stylesheet); + QWidgetList allWidgets = QApplication::allWidgets(); + auto it = setWidgets.begin(); + while (it != setWidgets.end()) { + if (!allWidgets.contains(*it)) { + it = setWidgets.erase(it); + continue; + } + if (fUpdateStyleSheet && *it != widget) { + (*it)->setStyleSheet(*stylesheet); + } + ++it; } - if (!ShutdownRequested() && fDebugCustomStyleSheets) { + if (!ShutdownRequested() && fDebugCustomStyleSheets && !fForceUpdate) { QTimer::singleShot(200, [] { loadStyleSheet(); }); } } @@ -1527,6 +1525,13 @@ bool dashThemeActive() return theme != traditionalTheme; } +void loadTheme(QWidget* widget, bool fForce) +{ + loadStyleSheet(widget, fForce); + updateFonts(); + updateMacFocusRects(); +} + void disableMacFocusRect(const QWidget* w) { #ifdef Q_OS_MAC diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index c38102ddee64..5d44c428251b 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -256,10 +256,10 @@ namespace GUIUtil /** Return the name of the default theme `*/ const QString getDefaultTheme(); - /** Updates the widgets stylesheet and adds it to the list of ui debug elements - if fDebugWidget is true. Beeing on that list means the stylesheet of the - widget gets updated if the related css files has been changed if -debug-ui mode is active. */ - void loadStyleSheet(QWidget* widget = nullptr, bool fDebugWidget = true); + /** Updates the widgets stylesheet and adds it to the list of ui debug elements. + Beeing on that list means the stylesheet of the widget gets updated if the + related css files has been changed if -debug-ui mode is active. */ + void loadStyleSheet(QWidget* widget = nullptr, bool fForceUpdate = false); enum class FontFamily { SystemDefault, @@ -338,6 +338,9 @@ namespace GUIUtil /** Check if a dash specific theme is activated (light/dark).*/ bool dashThemeActive(); + /** Load the theme and update all UI elements according to the appearance settings. */ + void loadTheme(QWidget* widget = nullptr, bool fForce = true); + /** Disable the OS default focus rect for macOS because we have custom focus rects * set in the css files */ void disableMacFocusRect(const QWidget* w); diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index e8616864852e..b40d73d0e2a8 100644 --- a/src/qt/optionsdialog.cpp +++ b/src/qt/optionsdialog.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) : @@ -36,6 +37,8 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) : { ui->setupUi(this); + previousTheme = GUIUtil::getActiveTheme(); + GUIUtil::setFont({ui->statusLabel}, GUIUtil::FontWeight::Bold, 16); GUIUtil::updateFonts(); @@ -104,6 +107,7 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) : for (const QString& entry : GUIUtil::listThemes()) { ui->theme->addItem(entry, QVariant(entry)); } + connect(ui->theme, SIGNAL(valueChanged()), this, SLOT(updateTheme())); /* Language selector */ QDir translations(":translations"); @@ -188,7 +192,6 @@ void OptionsDialog::setModel(OptionsModel *_model) connect(ui->connectSocksTor, SIGNAL(clicked(bool)), this, SLOT(showRestartWarning())); /* Display */ connect(ui->digits, SIGNAL(valueChanged()), this, SLOT(showRestartWarning())); - connect(ui->theme, SIGNAL(valueChanged()), this, SLOT(showRestartWarning())); connect(ui->lang, SIGNAL(valueChanged()), this, SLOT(showRestartWarning())); connect(ui->thirdPartyTxUrls, SIGNAL(textChanged(const QString &)), this, SLOT(showRestartWarning())); } @@ -293,6 +296,9 @@ void OptionsDialog::on_okButton_clicked() void OptionsDialog::on_cancelButton_clicked() { + if (previousTheme != GUIUtil::getActiveTheme()) { + updateTheme(previousTheme); + } reject(); } @@ -373,6 +379,16 @@ void OptionsDialog::updateDefaultProxyNets() (strProxy == strDefaultProxyGUI.toStdString()) ? ui->proxyReachTor->setChecked(true) : ui->proxyReachTor->setChecked(false); } +void OptionsDialog::updateTheme(const QString& theme) +{ + QString newValue = theme.isEmpty() ? ui->theme->value().toString() : theme; + if (GUIUtil::getActiveTheme() != newValue) { + QSettings().setValue("theme", newValue); + QSettings().sync(); + Q_EMIT themeChanged(); + } +} + ProxyAddressValidator::ProxyAddressValidator(QObject *parent) : QValidator(parent) { diff --git a/src/qt/optionsdialog.h b/src/qt/optionsdialog.h index 0cadf903fb7c..2a9a65005371 100644 --- a/src/qt/optionsdialog.h +++ b/src/qt/optionsdialog.h @@ -60,15 +60,18 @@ private Q_SLOTS: void updateProxyValidationState(); /* query the networks, for which the default proxy is used */ void updateDefaultProxyNets(); + void updateTheme(const QString& toTheme = QString()); Q_SIGNALS: void proxyIpChecks(QValidatedLineEdit *pUiProxyIp, int nProxyPort); + void themeChanged(); private: Ui::OptionsDialog *ui; OptionsModel *model; QDataWidgetMapper *mapper; QButtonGroup pageButtons; + QString previousTheme; }; #endif // BITCOIN_QT_OPTIONSDIALOG_H diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 7fee11b74c67..efb95f56e80c 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -510,10 +510,8 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in break; #endif // ENABLE_WALLET case Theme: - if (settings.value("theme") != value) { - settings.setValue("theme", value); - setRestartRequired(true); - } + // Set in OptionsDialog::updateTheme slot now + // to allow instant theme changes. break; case Language: if (settings.value("language") != value) { diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 79493dab5fe6..7edd4982de4d 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -1263,6 +1263,16 @@ void RPCConsole::hideEvent(QHideEvent *event) clientModel->getPeerTableModel()->stopAutoRefresh(); } +void RPCConsole::changeEvent(QEvent* e) +{ + if (e->type() == QEvent::StyleChange) { + clear(); + ui->promptIcon->setHidden(GUIUtil::dashThemeActive()); + } + + QWidget::changeEvent(e); +} + void RPCConsole::showPeersTableContextMenu(const QPoint& point) { QModelIndex index = ui->peerWidget->indexAt(point); diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index 7a0c439ba65e..1ade453af74f 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -79,6 +79,7 @@ private Q_SLOTS: void resizeEvent(QResizeEvent *event); void showEvent(QShowEvent *event); void hideEvent(QHideEvent *event); + void changeEvent(QEvent* e); /** Show custom context menu on Peers tab */ void showPeersTableContextMenu(const QPoint& point); /** Show custom context menu on Bans tab */ diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp index db84c43c15f0..1964dfb3d4a5 100644 --- a/src/qt/utilitydialog.cpp +++ b/src/qt/utilitydialog.cpp @@ -211,7 +211,7 @@ ShutdownWindow::ShutdownWindow(QWidget *parent, Qt::WindowFlags f): { setObjectName("ShutdownWindow"); - GUIUtil::loadStyleSheet(this, false); + GUIUtil::loadStyleSheet(this); QVBoxLayout *layout = new QVBoxLayout(); layout->addWidget(new QLabel(