Skip to content

Commit 7db9909

Browse files
committed
qt: Introduce runtime theme changes (dashpay#3559)
* qt: Set the default theme properly * qt: Keep track of disabled rects for macOS This allows enabling them again on theme changes * 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.
1 parent b373864 commit 7db9909

File tree

10 files changed

+103
-33
lines changed

10 files changed

+103
-33
lines changed

src/qt/bitcoingui.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,6 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle *
131131
spinnerFrame(0),
132132
platformStyle(_platformStyle)
133133
{
134-
GUIUtil::loadStyleSheet(this);
135-
136134
QSettings settings;
137135
if (!restoreGeometry(settings.value("MainWindowGeometry").toByteArray())) {
138136
// Restore failed (perhaps missing setting), center the window
@@ -856,6 +854,7 @@ void BitcoinGUI::optionsClicked()
856854

857855
OptionsDialog dlg(this, enableWallet);
858856
dlg.setModel(clientModel->getOptionsModel());
857+
connect(&dlg, &OptionsDialog::themeChanged, [=]() { GUIUtil::loadTheme(); });
859858
dlg.exec();
860859
}
861860

src/qt/dash.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,8 @@ void BitcoinApplication::createWindow(const NetworkStyle *networkStyle)
419419
{
420420
window = new BitcoinGUI(platformStyle, networkStyle, 0);
421421

422+
GUIUtil::loadTheme(window);
423+
422424
pollShutdownTimer = new QTimer(window);
423425
connect(pollShutdownTimer, SIGNAL(timeout()), window, SLOT(detectShutdown()));
424426
}

src/qt/guiutil.cpp

Lines changed: 52 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,11 @@ static std::set<QWidget*> setFixedPitchFontUpdates;
139139
// Contains all widgets where a non-default fontsize has been seet with GUIUtil::setFont
140140
static std::map<QWidget*, int> mapFontSizeUpdates;
141141

142+
#ifdef Q_OS_MAC
143+
// Contains all widgets where the macOS focus rect has been disabled.
144+
static std::set<QWidget*> setRectsDisabled;
145+
#endif
146+
142147
static const std::map<ThemedColor, QColor> themedColors = {
143148
{ ThemedColor::DEFAULT, QColor(85, 85, 85) },
144149
{ ThemedColor::UNCONFIRMED, QColor(128, 128, 128) },
@@ -1064,7 +1069,12 @@ const std::vector<QString> listThemes()
10641069
return vecThemes;
10651070
}
10661071

1067-
void loadStyleSheet(QWidget* widget, bool fDebugWidget)
1072+
const QString getDefaultTheme()
1073+
{
1074+
return defaultTheme;
1075+
}
1076+
1077+
void loadStyleSheet(QWidget* widget, bool fForceUpdate)
10681078
{
10691079
AssertLockNotHeld(cs_css);
10701080
LOCK(cs_css);
@@ -1075,7 +1085,7 @@ void loadStyleSheet(QWidget* widget, bool fDebugWidget)
10751085
bool fDebugCustomStyleSheets = gArgs.GetBoolArg("-debug-ui", false) && isStyleSheetDirectoryCustom();
10761086
bool fStyleSheetChanged = false;
10771087

1078-
if (stylesheet == nullptr || fDebugCustomStyleSheets) {
1088+
if (stylesheet == nullptr || fForceUpdate || fDebugCustomStyleSheets) {
10791089
auto hasModified = [](const std::vector<QString>& vecFiles) -> bool {
10801090
static std::map<const QString, QDateTime> mapLastModified;
10811091

@@ -1092,7 +1102,7 @@ void loadStyleSheet(QWidget* widget, bool fDebugWidget)
10921102
};
10931103

10941104
auto loadFiles = [&](const std::vector<QString>& vecFiles) -> bool {
1095-
if (fDebugCustomStyleSheets && !hasModified(vecFiles)) {
1105+
if (!fForceUpdate && fDebugCustomStyleSheets && !hasModified(vecFiles)) {
10961106
return false;
10971107
}
10981108

@@ -1125,29 +1135,27 @@ void loadStyleSheet(QWidget* widget, bool fDebugWidget)
11251135
fStyleSheetChanged = loadFiles(vecFiles);
11261136
}
11271137

1128-
bool fUpdateStyleSheet = fDebugCustomStyleSheets && fStyleSheetChanged;
1138+
bool fUpdateStyleSheet = fForceUpdate || (fDebugCustomStyleSheets && fStyleSheetChanged);
11291139

1130-
if (fDebugWidget) {
1140+
if (widget) {
11311141
setWidgets.insert(widget);
1132-
QWidgetList allWidgets = QApplication::allWidgets();
1133-
auto it = setWidgets.begin();
1134-
while (it != setWidgets.end()) {
1135-
if (!allWidgets.contains(*it)) {
1136-
it = setWidgets.erase(it);
1137-
continue;
1138-
}
1139-
if (fUpdateStyleSheet && *it != widget) {
1140-
(*it)->setStyleSheet(*stylesheet);
1141-
}
1142-
++it;
1143-
}
1142+
widget->setStyleSheet(*stylesheet);
11441143
}
11451144

1146-
if (widget) {
1147-
widget->setStyleSheet(*stylesheet);
1145+
QWidgetList allWidgets = QApplication::allWidgets();
1146+
auto it = setWidgets.begin();
1147+
while (it != setWidgets.end()) {
1148+
if (!allWidgets.contains(*it)) {
1149+
it = setWidgets.erase(it);
1150+
continue;
1151+
}
1152+
if (fUpdateStyleSheet && *it != widget) {
1153+
(*it)->setStyleSheet(*stylesheet);
1154+
}
1155+
++it;
11481156
}
11491157

1150-
if (!ShutdownRequested() && fDebugCustomStyleSheets) {
1158+
if (!ShutdownRequested() && fDebugCustomStyleSheets && !fForceUpdate) {
11511159
QTimer::singleShot(200, [] { loadStyleSheet(); });
11521160
}
11531161
}
@@ -1556,12 +1564,36 @@ bool dashThemeActive()
15561564
return theme != traditionalTheme;
15571565
}
15581566

1567+
void loadTheme(QWidget* widget, bool fForce)
1568+
{
1569+
loadStyleSheet(widget, fForce);
1570+
updateFonts();
1571+
updateMacFocusRects();
1572+
}
1573+
15591574
void disableMacFocusRect(const QWidget* w)
15601575
{
15611576
#ifdef Q_OS_MAC
15621577
for (const auto& c : w->findChildren<QWidget*>()) {
15631578
if (c->testAttribute(Qt::WA_MacShowFocusRect)) {
15641579
c->setAttribute(Qt::WA_MacShowFocusRect, !dashThemeActive());
1580+
setRectsDisabled.emplace(c);
1581+
}
1582+
}
1583+
#endif
1584+
}
1585+
1586+
void updateMacFocusRects()
1587+
{
1588+
#ifdef Q_OS_MAC
1589+
QWidgetList allWidgets = QApplication::allWidgets();
1590+
auto it = setRectsDisabled.begin();
1591+
while (it != setRectsDisabled.end()) {
1592+
if (allWidgets.contains(*it)) {
1593+
(*it)->setAttribute(Qt::WA_MacShowFocusRect, !dashThemeActive());
1594+
++it;
1595+
} else {
1596+
it = setRectsDisabled.erase(it);
15651597
}
15661598
}
15671599
#endif

src/qt/guiutil.h

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -253,10 +253,13 @@ namespace GUIUtil
253253
/** Return a list of all theme css files */
254254
const std::vector<QString> listThemes();
255255

256-
/** Updates the widgets stylesheet and adds it to the list of ui debug elements
257-
if fDebugWidget is true. Beeing on that list means the stylesheet of the
258-
widget gets updated if the related css files has been changed if -debug-ui mode is active. */
259-
void loadStyleSheet(QWidget* widget = nullptr, bool fDebugWidget = true);
256+
/** Return the name of the default theme `*/
257+
const QString getDefaultTheme();
258+
259+
/** Updates the widgets stylesheet and adds it to the list of ui debug elements.
260+
Beeing on that list means the stylesheet of the widget gets updated if the
261+
related css files has been changed if -debug-ui mode is active. */
262+
void loadStyleSheet(QWidget* widget = nullptr, bool fForceUpdate = false);
260263

261264
enum class FontFamily {
262265
SystemDefault,
@@ -335,10 +338,16 @@ namespace GUIUtil
335338
/** Check if a dash specific theme is activated (light/dark).*/
336339
bool dashThemeActive();
337340

341+
/** Load the theme and update all UI elements according to the appearance settings. */
342+
void loadTheme(QWidget* widget = nullptr, bool fForce = true);
343+
338344
/** Disable the OS default focus rect for macOS because we have custom focus rects
339345
* set in the css files */
340346
void disableMacFocusRect(const QWidget* w);
341347

348+
/** Enable/Disable the macOS focus rects depending on the current theme. */
349+
void updateMacFocusRects();
350+
342351
/* Convert QString to OS specific boost path through UTF-8 */
343352
fs::path qstringToBoostPath(const QString &path);
344353

src/qt/optionsdialog.cpp

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <QIntValidator>
2727
#include <QLocale>
2828
#include <QMessageBox>
29+
#include <QSettings>
2930
#include <QTimer>
3031

3132
OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) :
@@ -36,6 +37,8 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) :
3637
{
3738
ui->setupUi(this);
3839

40+
previousTheme = GUIUtil::getActiveTheme();
41+
3942
GUIUtil::setFont({ui->statusLabel}, GUIUtil::FontWeight::Bold, 16);
4043

4144
GUIUtil::updateFonts();
@@ -104,6 +107,7 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) :
104107
for (const QString& entry : GUIUtil::listThemes()) {
105108
ui->theme->addItem(entry, QVariant(entry));
106109
}
110+
connect(ui->theme, SIGNAL(valueChanged()), this, SLOT(updateTheme()));
107111

108112
/* Language selector */
109113
QDir translations(":translations");
@@ -200,7 +204,6 @@ void OptionsDialog::setModel(OptionsModel *_model)
200204
connect(ui->connectSocksTor, SIGNAL(clicked(bool)), this, SLOT(showRestartWarning()));
201205
/* Display */
202206
connect(ui->digits, SIGNAL(valueChanged()), this, SLOT(showRestartWarning()));
203-
connect(ui->theme, SIGNAL(valueChanged()), this, SLOT(showRestartWarning()));
204207
connect(ui->lang, SIGNAL(valueChanged()), this, SLOT(showRestartWarning()));
205208
connect(ui->thirdPartyTxUrls, SIGNAL(textChanged(const QString &)), this, SLOT(showRestartWarning()));
206209
}
@@ -306,6 +309,9 @@ void OptionsDialog::on_okButton_clicked()
306309

307310
void OptionsDialog::on_cancelButton_clicked()
308311
{
312+
if (previousTheme != GUIUtil::getActiveTheme()) {
313+
updateTheme(previousTheme);
314+
}
309315
reject();
310316
}
311317

@@ -386,6 +392,16 @@ void OptionsDialog::updateDefaultProxyNets()
386392
(strProxy == strDefaultProxyGUI.toStdString()) ? ui->proxyReachTor->setChecked(true) : ui->proxyReachTor->setChecked(false);
387393
}
388394

395+
void OptionsDialog::updateTheme(const QString& theme)
396+
{
397+
QString newValue = theme.isEmpty() ? ui->theme->value().toString() : theme;
398+
if (GUIUtil::getActiveTheme() != newValue) {
399+
QSettings().setValue("theme", newValue);
400+
QSettings().sync();
401+
Q_EMIT themeChanged();
402+
}
403+
}
404+
389405
ProxyAddressValidator::ProxyAddressValidator(QObject *parent) :
390406
QValidator(parent)
391407
{

src/qt/optionsdialog.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,18 @@ private Q_SLOTS:
6060
void updateProxyValidationState();
6161
/* query the networks, for which the default proxy is used */
6262
void updateDefaultProxyNets();
63+
void updateTheme(const QString& toTheme = QString());
6364

6465
Q_SIGNALS:
6566
void proxyIpChecks(QValidatedLineEdit *pUiProxyIp, int nProxyPort);
67+
void themeChanged();
6668

6769
private:
6870
Ui::OptionsDialog *ui;
6971
OptionsModel *model;
7072
QDataWidgetMapper *mapper;
7173
QButtonGroup pageButtons;
74+
QString previousTheme;
7275
};
7376

7477
#endif // BITCOIN_QT_OPTIONSDIALOG_H

src/qt/optionsmodel.cpp

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ void OptionsModel::Init(bool resetSettings)
8383
strThirdPartyTxUrls = settings.value("strThirdPartyTxUrls", "").toString();
8484

8585
if (!settings.contains("theme"))
86-
settings.setValue("theme", "");
86+
settings.setValue("theme", GUIUtil::getDefaultTheme());
8787

8888
#ifdef ENABLE_WALLET
8989
if (!settings.contains("fCoinControlFeatures"))
@@ -502,10 +502,8 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
502502
break;
503503
#endif // ENABLE_WALLET
504504
case Theme:
505-
if (settings.value("theme") != value) {
506-
settings.setValue("theme", value);
507-
setRestartRequired(true);
508-
}
505+
// Set in OptionsDialog::updateTheme slot now
506+
// to allow instant theme changes.
509507
break;
510508
case Language:
511509
if (settings.value("language") != value) {

src/qt/rpcconsole.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1267,6 +1267,16 @@ void RPCConsole::hideEvent(QHideEvent *event)
12671267
clientModel->getPeerTableModel()->stopAutoRefresh();
12681268
}
12691269

1270+
void RPCConsole::changeEvent(QEvent* e)
1271+
{
1272+
if (e->type() == QEvent::StyleChange) {
1273+
clear();
1274+
ui->promptIcon->setHidden(GUIUtil::dashThemeActive());
1275+
}
1276+
1277+
QWidget::changeEvent(e);
1278+
}
1279+
12701280
void RPCConsole::showPeersTableContextMenu(const QPoint& point)
12711281
{
12721282
QModelIndex index = ui->peerWidget->indexAt(point);

src/qt/rpcconsole.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ private Q_SLOTS:
7979
void resizeEvent(QResizeEvent *event);
8080
void showEvent(QShowEvent *event);
8181
void hideEvent(QHideEvent *event);
82+
void changeEvent(QEvent* e);
8283
/** Show custom context menu on Peers tab */
8384
void showPeersTableContextMenu(const QPoint& point);
8485
/** Show custom context menu on Bans tab */

src/qt/utilitydialog.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ ShutdownWindow::ShutdownWindow(QWidget *parent, Qt::WindowFlags f):
210210
{
211211
setObjectName("ShutdownWindow");
212212

213-
GUIUtil::loadStyleSheet(this, false);
213+
GUIUtil::loadStyleSheet(this);
214214

215215
QVBoxLayout *layout = new QVBoxLayout();
216216
layout->addWidget(new QLabel(

0 commit comments

Comments
 (0)