Skip to content

Commit 123b401

Browse files
committed
Merge bitcoin-core#275: Support runtime appearance adjustment on macOS
c231254 qt: Make TransactionView aware of runtime palette change (Hennadii Stepanov) 2b622d4 qt: Make CoinControlDialog aware of runtime palette change (Hennadii Stepanov) 97a6b5e qt: Make OverviewPage aware of runtime palette change (Hennadii Stepanov) d05f1b2 qt: Make UnitDisplayStatusBarControl aware of runtime palette change (Hennadii Stepanov) 6b2ce65 qt: Replace base class of ClickableLabel with ThemedLabel (Hennadii Stepanov) ff530a2 qt: Use GUIUtil::ThemedLabel class (Hennadii Stepanov) d99ef32 qt: Add GUIUtil::ThemedLabel class (Hennadii Stepanov) c054720 qt: Make SignVerifyMessageDialog aware of runtime palette change (Hennadii Stepanov) 0dcc3fa qt: Make SendCoinsEntry aware of runtime palette change (Hennadii Stepanov) fa18d28 qt: Make RPCConsole aware of runtime palette change (Hennadii Stepanov) f108382 qt: Make BitcoinGUI aware of runtime palette change (Hennadii Stepanov) ce17861 qt: Make PlatformStyle aware of runtime palette change (Hennadii Stepanov) Pull request description: On macOS switching appearance (Light -> Dark or Dark -> Light) when Bitcoin Core is running makes the GUI pretty unusable. This bug is especially important when a user chose the "Auto" mode to adjust appearance automatically. This PR fixes Bitcoin Core behavior. This is an alternative to bitcoin-core#268. ACKs for top commit: Sjors: tACK c231254 on macOS 11.4 goums: ACK c231254 promag: Tested ACK c231254 on macOS Big Sur arm64. jarolrod: tACK c231254 Tree-SHA512: 122dda3e4c9703f68cec60613c536ca59d04c93f2c03398559f2361b8d279ae534800e8e677d94a33e10e769d00be54295a704e98afa2e986a06146b9f164854
2 parents 8115c2a + c231254 commit 123b401

18 files changed

+228
-41
lines changed

src/qt/bitcoingui.cpp

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -150,11 +150,11 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty
150150
frameBlocksLayout->setContentsMargins(3,0,3,0);
151151
frameBlocksLayout->setSpacing(3);
152152
unitDisplayControl = new UnitDisplayStatusBarControl(platformStyle);
153-
labelWalletEncryptionIcon = new QLabel();
154-
labelWalletHDStatusIcon = new QLabel();
155-
labelProxyIcon = new GUIUtil::ClickableLabel();
156-
connectionsControl = new GUIUtil::ClickableLabel();
157-
labelBlocksIcon = new GUIUtil::ClickableLabel();
153+
labelWalletEncryptionIcon = new GUIUtil::ThemedLabel(platformStyle);
154+
labelWalletHDStatusIcon = new GUIUtil::ThemedLabel(platformStyle);
155+
labelProxyIcon = new GUIUtil::ClickableLabel(platformStyle);
156+
connectionsControl = new GUIUtil::ClickableLabel(platformStyle);
157+
labelBlocksIcon = new GUIUtil::ClickableLabel(platformStyle);
158158
if(enableWallet)
159159
{
160160
frameBlocksLayout->addStretch();
@@ -925,7 +925,7 @@ void BitcoinGUI::updateNetworkState()
925925
tooltip = QString("<nobr>") + tooltip + QString("</nobr>");
926926
connectionsControl->setToolTip(tooltip);
927927

928-
connectionsControl->setPixmap(platformStyle->SingleColorIcon(icon).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
928+
connectionsControl->setThemedPixmap(icon, STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE);
929929
}
930930

931931
void BitcoinGUI::setNumConnections(int count)
@@ -1021,7 +1021,7 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer
10211021
// Set icon state: spinning if catching up, tick otherwise
10221022
if (secs < MAX_BLOCK_TIME_GAP) {
10231023
tooltip = tr("Up to date") + QString(".<br>") + tooltip;
1024-
labelBlocksIcon->setPixmap(platformStyle->SingleColorIcon(":/icons/synced").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
1024+
labelBlocksIcon->setThemedPixmap(QStringLiteral(":/icons/synced"), STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE);
10251025

10261026
#ifdef ENABLE_WALLET
10271027
if(walletFrame)
@@ -1047,9 +1047,9 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer
10471047
tooltip = tr("Catching up…") + QString("<br>") + tooltip;
10481048
if(count != prevBlocks)
10491049
{
1050-
labelBlocksIcon->setPixmap(platformStyle->SingleColorIcon(QString(
1051-
":/animation/spinner-%1").arg(spinnerFrame, 3, 10, QChar('0')))
1052-
.pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
1050+
labelBlocksIcon->setThemedPixmap(
1051+
QString(":/animation/spinner-%1").arg(spinnerFrame, 3, 10, QChar('0')),
1052+
STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE);
10531053
spinnerFrame = (spinnerFrame + 1) % SPINNER_FRAMES;
10541054
}
10551055
prevBlocks = count;
@@ -1138,7 +1138,17 @@ void BitcoinGUI::message(const QString& title, QString message, unsigned int sty
11381138

11391139
void BitcoinGUI::changeEvent(QEvent *e)
11401140
{
1141+
#ifdef Q_OS_MACOS
1142+
if (e->type() == QEvent::PaletteChange) {
1143+
overviewAction->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/overview")));
1144+
sendCoinsAction->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/send")));
1145+
receiveCoinsAction->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/receiving_addresses")));
1146+
historyAction->setIcon(platformStyle->SingleColorIcon(QStringLiteral(":/icons/history")));
1147+
}
1148+
#endif
1149+
11411150
QMainWindow::changeEvent(e);
1151+
11421152
#ifndef Q_OS_MAC // Ignored on Mac
11431153
if(e->type() == QEvent::WindowStateChange)
11441154
{
@@ -1256,7 +1266,7 @@ bool BitcoinGUI::handlePaymentRequest(const SendCoinsRecipient& recipient)
12561266

12571267
void BitcoinGUI::setHDStatus(bool privkeyDisabled, int hdEnabled)
12581268
{
1259-
labelWalletHDStatusIcon->setPixmap(platformStyle->SingleColorIcon(privkeyDisabled ? ":/icons/eye" : hdEnabled ? ":/icons/hd_enabled" : ":/icons/hd_disabled").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
1269+
labelWalletHDStatusIcon->setThemedPixmap(privkeyDisabled ? QStringLiteral(":/icons/eye") : hdEnabled ? QStringLiteral(":/icons/hd_enabled") : QStringLiteral(":/icons/hd_disabled"), STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE);
12601270
labelWalletHDStatusIcon->setToolTip(privkeyDisabled ? tr("Private key <b>disabled</b>") : hdEnabled ? tr("HD key generation is <b>enabled</b>") : tr("HD key generation is <b>disabled</b>"));
12611271
labelWalletHDStatusIcon->show();
12621272
// eventually disable the QLabel to set its opacity to 50%
@@ -1275,15 +1285,15 @@ void BitcoinGUI::setEncryptionStatus(int status)
12751285
break;
12761286
case WalletModel::Unlocked:
12771287
labelWalletEncryptionIcon->show();
1278-
labelWalletEncryptionIcon->setPixmap(platformStyle->SingleColorIcon(":/icons/lock_open").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
1288+
labelWalletEncryptionIcon->setThemedPixmap(QStringLiteral(":/icons/lock_open"), STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE);
12791289
labelWalletEncryptionIcon->setToolTip(tr("Wallet is <b>encrypted</b> and currently <b>unlocked</b>"));
12801290
encryptWalletAction->setChecked(true);
12811291
changePassphraseAction->setEnabled(true);
12821292
encryptWalletAction->setEnabled(false);
12831293
break;
12841294
case WalletModel::Locked:
12851295
labelWalletEncryptionIcon->show();
1286-
labelWalletEncryptionIcon->setPixmap(platformStyle->SingleColorIcon(":/icons/lock_closed").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE));
1296+
labelWalletEncryptionIcon->setThemedPixmap(QStringLiteral(":/icons/lock_closed"), STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE);
12871297
labelWalletEncryptionIcon->setToolTip(tr("Wallet is <b>encrypted</b> and currently <b>locked</b>"));
12881298
encryptWalletAction->setChecked(true);
12891299
changePassphraseAction->setEnabled(true);
@@ -1315,7 +1325,7 @@ void BitcoinGUI::updateProxyIcon()
13151325
if (proxy_enabled) {
13161326
if (!GUIUtil::HasPixmap(labelProxyIcon)) {
13171327
QString ip_port_q = QString::fromStdString(ip_port);
1318-
labelProxyIcon->setPixmap(platformStyle->SingleColorIcon(":/icons/proxy").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE));
1328+
labelProxyIcon->setThemedPixmap((":/icons/proxy"), STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE);
13191329
labelProxyIcon->setToolTip(tr("Proxy is <b>enabled</b>: %1").arg(ip_port_q));
13201330
} else {
13211331
labelProxyIcon->show();
@@ -1440,9 +1450,10 @@ bool BitcoinGUI::isPrivacyModeActivated() const
14401450
return m_mask_values_action->isChecked();
14411451
}
14421452

1443-
UnitDisplayStatusBarControl::UnitDisplayStatusBarControl(const PlatformStyle *platformStyle) :
1444-
optionsModel(nullptr),
1445-
menu(nullptr)
1453+
UnitDisplayStatusBarControl::UnitDisplayStatusBarControl(const PlatformStyle *platformStyle)
1454+
: optionsModel(nullptr),
1455+
menu(nullptr),
1456+
m_platform_style{platformStyle}
14461457
{
14471458
createContextMenu();
14481459
setToolTip(tr("Unit to show amounts in. Click to select another unit."));
@@ -1455,7 +1466,7 @@ UnitDisplayStatusBarControl::UnitDisplayStatusBarControl(const PlatformStyle *pl
14551466
}
14561467
setMinimumSize(max_width, 0);
14571468
setAlignment(Qt::AlignRight | Qt::AlignVCenter);
1458-
setStyleSheet(QString("QLabel { color : %1 }").arg(platformStyle->SingleColor().name()));
1469+
setStyleSheet(QString("QLabel { color : %1 }").arg(m_platform_style->SingleColor().name()));
14591470
}
14601471

14611472
/** So that it responds to button clicks */
@@ -1464,6 +1475,18 @@ void UnitDisplayStatusBarControl::mousePressEvent(QMouseEvent *event)
14641475
onDisplayUnitsClicked(event->pos());
14651476
}
14661477

1478+
void UnitDisplayStatusBarControl::changeEvent(QEvent* e)
1479+
{
1480+
#ifdef Q_OS_MACOS
1481+
if (e->type() == QEvent::PaletteChange) {
1482+
QString style = QString("QLabel { color : %1 }").arg(m_platform_style->SingleColor().name());
1483+
if (style != styleSheet()) {
1484+
setStyleSheet(style);
1485+
}
1486+
}
1487+
#endif
1488+
}
1489+
14671490
/** Creates context menu, its actions, and wires up all the relevant signals for mouse events. */
14681491
void UnitDisplayStatusBarControl::createContextMenu()
14691492
{

src/qt/bitcoingui.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <config/bitcoin-config.h>
1010
#endif
1111

12+
#include <qt/guiutil.h>
1213
#include <qt/optionsdialog.h>
1314

1415
#include <amount.h>
@@ -121,8 +122,8 @@ class BitcoinGUI : public QMainWindow
121122
WalletFrame* walletFrame = nullptr;
122123

123124
UnitDisplayStatusBarControl* unitDisplayControl = nullptr;
124-
QLabel* labelWalletEncryptionIcon = nullptr;
125-
QLabel* labelWalletHDStatusIcon = nullptr;
125+
GUIUtil::ThemedLabel* labelWalletEncryptionIcon = nullptr;
126+
GUIUtil::ThemedLabel* labelWalletHDStatusIcon = nullptr;
126127
GUIUtil::ClickableLabel* labelProxyIcon = nullptr;
127128
GUIUtil::ClickableLabel* connectionsControl = nullptr;
128129
GUIUtil::ClickableLabel* labelBlocksIcon = nullptr;
@@ -333,10 +334,12 @@ class UnitDisplayStatusBarControl : public QLabel
333334
protected:
334335
/** So that it responds to left-button clicks */
335336
void mousePressEvent(QMouseEvent *event) override;
337+
void changeEvent(QEvent* e) override;
336338

337339
private:
338340
OptionsModel *optionsModel;
339341
QMenu* menu;
342+
const PlatformStyle* m_platform_style;
340343

341344
/** Shows context menu with Display Unit options by the mouse coordinates */
342345
void onDisplayUnitsClicked(const QPoint& point);

src/qt/coincontroldialog.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,15 @@ void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel *
562562
label->setVisible(nChange < 0);
563563
}
564564

565+
void CoinControlDialog::changeEvent(QEvent* e)
566+
{
567+
#ifdef Q_OS_MACOS
568+
if (e->type() == QEvent::PaletteChange) {
569+
updateView();
570+
}
571+
#endif
572+
}
573+
565574
void CoinControlDialog::updateView()
566575
{
567576
if (!model || !model->getOptionsModel() || !model->getAddressTableModel())

src/qt/coincontroldialog.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ class CoinControlDialog : public QDialog
5151
static QList<CAmount> payAmounts;
5252
static bool fSubtractFeeFromAmount;
5353

54+
protected:
55+
void changeEvent(QEvent* e) override;
56+
5457
private:
5558
Ui::CoinControlDialog *ui;
5659
CCoinControl& m_coin_control;

src/qt/guiutil.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include <qt/bitcoinaddressvalidator.h>
88
#include <qt/bitcoinunits.h>
9+
#include <qt/platformstyle.h>
910
#include <qt/qvalidatedlineedit.h>
1011
#include <qt/sendcoinsrecipient.h>
1112

@@ -61,6 +62,7 @@
6162
#include <QUrlQuery>
6263
#include <QtGlobal>
6364

65+
#include <cassert>
6466
#include <chrono>
6567

6668
#if defined(Q_OS_MAC)
@@ -791,6 +793,40 @@ qreal calculateIdealFontSize(int width, const QString& text, QFont font, qreal m
791793
return font_size;
792794
}
793795

796+
ThemedLabel::ThemedLabel(const PlatformStyle* platform_style, QWidget* parent)
797+
: QLabel{parent}, m_platform_style{platform_style}
798+
{
799+
assert(m_platform_style);
800+
}
801+
802+
void ThemedLabel::setThemedPixmap(const QString& image_filename, int width, int height)
803+
{
804+
m_image_filename = image_filename;
805+
m_pixmap_width = width;
806+
m_pixmap_height = height;
807+
updateThemedPixmap();
808+
}
809+
810+
void ThemedLabel::changeEvent(QEvent* e)
811+
{
812+
#ifdef Q_OS_MACOS
813+
if (e->type() == QEvent::PaletteChange) {
814+
updateThemedPixmap();
815+
}
816+
#endif
817+
QLabel::changeEvent(e);
818+
}
819+
820+
void ThemedLabel::updateThemedPixmap()
821+
{
822+
setPixmap(m_platform_style->SingleColorIcon(m_image_filename).pixmap(m_pixmap_width, m_pixmap_height));
823+
}
824+
825+
ClickableLabel::ClickableLabel(const PlatformStyle* platform_style, QWidget* parent)
826+
: ThemedLabel{platform_style, parent}
827+
{
828+
}
829+
794830
void ClickableLabel::mouseReleaseEvent(QMouseEvent *event)
795831
{
796832
Q_EMIT clicked(event->pos());

src/qt/guiutil.h

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <chrono>
2828
#include <utility>
2929

30+
class PlatformStyle;
3031
class QValidatedLineEdit;
3132
class SendCoinsRecipient;
3233

@@ -231,10 +232,32 @@ namespace GUIUtil
231232

232233
qreal calculateIdealFontSize(int width, const QString& text, QFont font, qreal minPointSize = 4, qreal startPointSize = 14);
233234

234-
class ClickableLabel : public QLabel
235+
class ThemedLabel : public QLabel
235236
{
236237
Q_OBJECT
237238

239+
public:
240+
explicit ThemedLabel(const PlatformStyle* platform_style, QWidget* parent = nullptr);
241+
void setThemedPixmap(const QString& image_filename, int width, int height);
242+
243+
protected:
244+
void changeEvent(QEvent* e) override;
245+
246+
private:
247+
const PlatformStyle* m_platform_style;
248+
QString m_image_filename;
249+
int m_pixmap_width;
250+
int m_pixmap_height;
251+
void updateThemedPixmap();
252+
};
253+
254+
class ClickableLabel : public ThemedLabel
255+
{
256+
Q_OBJECT
257+
258+
public:
259+
explicit ClickableLabel(const PlatformStyle* platform_style, QWidget* parent = nullptr);
260+
238261
Q_SIGNALS:
239262
/** Emitted when the label is clicked. The relative mouse coordinates of the click are
240263
* passed to the signal.

src/qt/overviewpage.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ class TxViewDelegate : public QAbstractItemDelegate
7878
{
7979
QIcon iconWatchonly = qvariant_cast<QIcon>(index.data(TransactionTableModel::WatchonlyDecorationRole));
8080
QRect watchonlyRect(boundingRect.right() + 5, mainRect.top()+ypad+halfheight, 16, halfheight);
81+
iconWatchonly = platformStyle->TextColorIcon(iconWatchonly);
8182
iconWatchonly.paint(painter, watchonlyRect);
8283
address_rect_min_width += 5 + watchonlyRect.width();
8384
}
@@ -143,14 +144,15 @@ OverviewPage::OverviewPage(const PlatformStyle *platformStyle, QWidget *parent)
143144
ui(new Ui::OverviewPage),
144145
clientModel(nullptr),
145146
walletModel(nullptr),
147+
m_platform_style{platformStyle},
146148
txdelegate(new TxViewDelegate(platformStyle, this))
147149
{
148150
ui->setupUi(this);
149151

150152
m_balances.balance = -1;
151153

152154
// use a SingleColorIcon for the "out of sync warning" icon
153-
QIcon icon = platformStyle->SingleColorIcon(":/icons/warning");
155+
QIcon icon = m_platform_style->SingleColorIcon(QStringLiteral(":/icons/warning"));
154156
ui->labelTransactionsStatus->setIcon(icon);
155157
ui->labelWalletStatus->setIcon(icon);
156158

@@ -298,6 +300,17 @@ void OverviewPage::setWalletModel(WalletModel *model)
298300
updateDisplayUnit();
299301
}
300302

303+
void OverviewPage::changeEvent(QEvent* e)
304+
{
305+
#ifdef Q_OS_MACOS
306+
if (e->type() == QEvent::PaletteChange) {
307+
QIcon icon = m_platform_style->SingleColorIcon(QStringLiteral(":/icons/warning"));
308+
ui->labelTransactionsStatus->setIcon(icon);
309+
ui->labelWalletStatus->setIcon(icon);
310+
}
311+
#endif
312+
}
313+
301314
void OverviewPage::updateDisplayUnit()
302315
{
303316
if(walletModel && walletModel->getOptionsModel())

src/qt/overviewpage.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,18 @@ public Q_SLOTS:
4545
void transactionClicked(const QModelIndex &index);
4646
void outOfSyncWarningClicked();
4747

48+
protected:
49+
void changeEvent(QEvent* e) override;
50+
4851
private:
4952
Ui::OverviewPage *ui;
5053
ClientModel *clientModel;
5154
WalletModel *walletModel;
5255
interfaces::WalletBalances m_balances;
5356
bool m_privacy{false};
5457

58+
const PlatformStyle* m_platform_style;
59+
5560
TxViewDelegate *txdelegate;
5661
std::unique_ptr<TransactionFilterProxy> filter;
5762

0 commit comments

Comments
 (0)