diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 0418d9f88f..63886e2346 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 1960be60c9..29d90bb474 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 478bc7e020..f688a9a953 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) }, @@ -1025,7 +1030,12 @@ const std::vector listThemes() return vecThemes; } -void loadStyleSheet(QWidget* widget, bool fDebugWidget) +const QString getDefaultTheme() +{ + return defaultTheme; +} + +void loadStyleSheet(QWidget* widget, bool fForceUpdate) { AssertLockNotHeld(cs_css); LOCK(cs_css); @@ -1036,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; @@ -1053,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; } @@ -1086,29 +1096,27 @@ void loadStyleSheet(QWidget* widget, bool fDebugWidget) fStyleSheetChanged = loadFiles(vecFiles); } - bool fUpdateStyleSheet = fDebugCustomStyleSheets && fStyleSheetChanged; - - if (fDebugWidget) { - 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; - } - } + bool fUpdateStyleSheet = fForceUpdate || (fDebugCustomStyleSheets && fStyleSheetChanged); if (widget) { + setWidgets.insert(widget); widget->setStyleSheet(*stylesheet); } - if (!ShutdownRequested() && fDebugCustomStyleSheets) { + 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 && !fForceUpdate) { QTimer::singleShot(200, [] { loadStyleSheet(); }); } } @@ -1517,12 +1525,36 @@ 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 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 a81ceaab08..5d44c42825 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -253,10 +253,13 @@ namespace GUIUtil /** Return a list of all theme css files */ const std::vector listThemes(); - /** 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); + /** Return the name of the default theme `*/ + const QString getDefaultTheme(); + + /** 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, @@ -335,10 +338,16 @@ 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); + /** 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); diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp index e861686485..b40d73d0e2 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 0cadf903fb..2a9a650053 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 e56da201aa..efb95f56e8 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")) @@ -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 79493dab5f..7edd4982de 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 7a0c439ba6..1ade453af7 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 db84c43c15..1964dfb3d4 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(