diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 0a93e7f6a0c..09af0eeaa81 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -371,6 +371,7 @@ void BitcoinApplication::createSplashScreen(const NetworkStyle *networkStyle) // We don't hold a direct pointer to the splash screen after creation, but the splash // screen will take care of deleting itself when slotFinish happens. splash->show(); + PlatformStyle::applyTheme(PlatformStyle::isDarkModeEnabled()); connect(this, SIGNAL(splashFinished(QWidget*)), splash, SLOT(slotFinish(QWidget*))); connect(this, SIGNAL(requestedShutdown()), splash, SLOT(close())); } @@ -549,6 +550,7 @@ MAIN_FUNCTION QApplication::setOrganizationName(QAPP_ORG_NAME); QApplication::setOrganizationDomain(QAPP_ORG_DOMAIN); QApplication::setApplicationName(QAPP_APP_NAME_DEFAULT); + PlatformStyle::applyTheme(PlatformStyle::isDarkModeEnabled()); GUIUtil::SubstituteFonts(GetLangTerritory()); /// 4. Initialization of translations, so that intro dialog is in user's language diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 36f3948acaa..b06b7e1f310 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -40,6 +40,7 @@ #include #include +#include #include #include #include @@ -57,6 +58,7 @@ #include #include #include +#include #include #include @@ -83,6 +85,8 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle * clientModel(0), walletFrame(0), unitDisplayControl(0), + themeToggleButton(0), + themeTintMenu(0), labelWalletEncryptionIcon(0), labelWalletHDStatusIcon(0), connectionsControl(0), @@ -212,6 +216,24 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle * frameBlocksLayout->setContentsMargins(3,0,3,0); frameBlocksLayout->setSpacing(3); unitDisplayControl = new UnitDisplayStatusBarControl(platformStyle); + themeToggleButton = new QToolButton(frameBlocks); + themeToggleButton->setAutoRaise(true); + themeToggleButton->setCursor(Qt::PointingHandCursor); + themeToggleButton->setContextMenuPolicy(Qt::CustomContextMenu); + themeTintMenu = new QMenu(themeToggleButton); + QActionGroup* tintGroup = new QActionGroup(themeTintMenu); + tintGroup->setExclusive(true); + for (int tint = 0; tint < PlatformStyle::darkModeTintCount(); ++tint) { + QAction* tintAction = new QAction(PlatformStyle::darkModeTintName(tint), themeTintMenu); + tintAction->setData(tint); + tintAction->setCheckable(true); + tintGroup->addAction(tintAction); + themeTintMenu->addAction(tintAction); + } + updateDarkModeToggleText(); + connect(themeToggleButton, SIGNAL(clicked()), this, SLOT(toggleDarkMode())); + connect(themeToggleButton, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showDarkTintMenu(QPoint))); + connect(themeTintMenu, SIGNAL(triggered(QAction*)), this, SLOT(onDarkTintSelected(QAction*))); labelWalletEncryptionIcon = new QLabel(); labelWalletHDStatusIcon = new QLabel(); connectionsControl = new GUIUtil::ClickableLabel(); @@ -220,6 +242,7 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle * { frameBlocksLayout->addStretch(); frameBlocksLayout->addWidget(unitDisplayControl); + frameBlocksLayout->addWidget(themeToggleButton); frameBlocksLayout->addStretch(); frameBlocksLayout->addWidget(labelWalletEncryptionIcon); frameBlocksLayout->addWidget(labelWalletHDStatusIcon); @@ -269,6 +292,7 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle * connect(progressBar, SIGNAL(clicked(QPoint)), this, SLOT(showModalOverlay())); } #endif + PlatformStyle::applyTheme(PlatformStyle::isDarkModeEnabled()); } BitcoinGUI::~BitcoinGUI() @@ -1230,6 +1254,47 @@ void BitcoinGUI::toggleNetworkActive() } } +void BitcoinGUI::toggleDarkMode() +{ + PlatformStyle::setDarkModeEnabled(!PlatformStyle::isDarkModeEnabled()); + updateDarkModeToggleText(); +} + +void BitcoinGUI::updateDarkModeToggleText() +{ + if (!themeToggleButton) { + return; + } + const bool darkModeEnabled = PlatformStyle::isDarkModeEnabled(); + const QChar sunGlyph(0x2600); + const QChar moonGlyph(0x263E); + themeToggleButton->setStyleSheet(QString("QToolButton { color : %1; }").arg(platformStyle->SingleColor().name())); + themeToggleButton->setText(darkModeEnabled ? QString(sunGlyph) : QString(moonGlyph)); + themeToggleButton->setToolTip(darkModeEnabled ? tr("Switch to light mode (right-click for green tint options)") : tr("Switch to dark mode (right-click for green tint options)")); +} + +void BitcoinGUI::showDarkTintMenu(const QPoint& point) +{ + if (!themeTintMenu || !themeToggleButton) { + return; + } + const int currentTint = PlatformStyle::darkModeTint(); + Q_FOREACH (QAction* action, themeTintMenu->actions()) + { + action->setChecked(action->data().toInt() == currentTint); + } + themeTintMenu->exec(themeToggleButton->mapToGlobal(point)); +} + +void BitcoinGUI::onDarkTintSelected(QAction* action) +{ + if (!action) { + return; + } + PlatformStyle::setDarkModeTint(action->data().toInt()); + updateDarkModeToggleText(); +} + UnitDisplayStatusBarControl::UnitDisplayStatusBarControl(const PlatformStyle *platformStyle) : optionsModel(0), menu(0) diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 2a3c797a40f..ba6965ea16e 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -38,6 +38,7 @@ QT_BEGIN_NAMESPACE class QAction; class QProgressBar; class QProgressDialog; +class QToolButton; QT_END_NAMESPACE /** @@ -84,6 +85,8 @@ class BitcoinGUI : public QMainWindow WalletFrame *walletFrame; UnitDisplayStatusBarControl *unitDisplayControl; + QToolButton *themeToggleButton; + QMenu *themeTintMenu; QLabel *labelWalletEncryptionIcon; QLabel *labelWalletHDStatusIcon; QLabel *connectionsControl; @@ -244,6 +247,10 @@ private Q_SLOTS: /** Toggle networking */ void toggleNetworkActive(); + void toggleDarkMode(); + void updateDarkModeToggleText(); + void showDarkTintMenu(const QPoint& point); + void onDarkTintSelected(QAction* action); void showModalOverlay(); }; diff --git a/src/qt/intro.cpp b/src/qt/intro.cpp index a207ca6f995..64a130b9353 100644 --- a/src/qt/intro.cpp +++ b/src/qt/intro.cpp @@ -12,6 +12,7 @@ #include "fs.h" #include "guiutil.h" +#include "platformstyle.h" #include "util.h" @@ -121,6 +122,7 @@ Intro::Intro(QWidget *parent) : signalled(false) { ui->setupUi(this); + PlatformStyle::applyTheme(PlatformStyle::isDarkModeEnabled()); ui->welcomeLabel->setText(ui->welcomeLabel->text().arg(tr(PACKAGE_NAME))); ui->storageLabel->setText(ui->storageLabel->text().arg(tr(PACKAGE_NAME))); uint64_t pruneTarget = std::max(0, GetArg("-prune", 0)); diff --git a/src/qt/modaloverlay.cpp b/src/qt/modaloverlay.cpp index 50dcaca6325..0f554db8ed9 100644 --- a/src/qt/modaloverlay.cpp +++ b/src/qt/modaloverlay.cpp @@ -7,6 +7,7 @@ #include "ui_modaloverlay.h" #include "guiutil.h" +#include "platformstyle.h" #include "chainparams.h" @@ -22,6 +23,7 @@ layerIsVisible(false), userClosed(false) { ui->setupUi(this); + updateStyles(); connect(ui->closeButton, SIGNAL(clicked()), this, SLOT(closeClicked())); if (parent) { parent->installEventFilter(this); @@ -32,6 +34,21 @@ userClosed(false) setVisible(false); } +void ModalOverlay::updateStyles() +{ + const bool darkModeEnabled = PlatformStyle::isDarkModeEnabled(); + ui->bgWidget->setStyleSheet("#bgWidget { background: rgba(0,0,0,220); }"); + if (darkModeEnabled) { + ui->contentWidget->setStyleSheet( + "#contentWidget { background: rgba(35,44,38,240); border-radius: 6px; }\n" + "QLabel { color: rgb(214,232,220); }"); + } else { + ui->contentWidget->setStyleSheet( + "#contentWidget { background: rgba(255,255,255,240); border-radius: 6px; }\n" + "QLabel { color: rgb(40,40,40); }"); + } +} + ModalOverlay::~ModalOverlay() { delete ui; @@ -150,6 +167,8 @@ void ModalOverlay::toggleVisibility() void ModalOverlay::showHide(bool hide, bool userRequested) { + updateStyles(); + if ( (layerIsVisible && !hide) || (!layerIsVisible && hide) || (!hide && userClosed && !userRequested)) return; diff --git a/src/qt/modaloverlay.h b/src/qt/modaloverlay.h index 21ccdbd8397..5b1f0ecafd7 100644 --- a/src/qt/modaloverlay.h +++ b/src/qt/modaloverlay.h @@ -39,6 +39,8 @@ public Q_SLOTS: bool event(QEvent* ev); private: + void updateStyles(); + Ui::ModalOverlay *ui; int bestHeaderHeight; //best known height (based on the headers) QDateTime bestHeaderDate; diff --git a/src/qt/platformstyle.cpp b/src/qt/platformstyle.cpp index 90bd619c041..08f2e21a8d7 100644 --- a/src/qt/platformstyle.cpp +++ b/src/qt/platformstyle.cpp @@ -8,10 +8,17 @@ #include #include +#include +#include #include #include +#include #include #include +#include +#include +#include +#include static const struct { const char *platformId; @@ -28,6 +35,55 @@ static const struct { {"other", true, true, false} }; static const unsigned platform_styles_count = sizeof(platform_styles)/sizeof(*platform_styles); +static const char* DARK_MODE_SETTING = "fUseDarkMode"; +static const char* DARK_MODE_TINT_SETTING = "nDarkModeTint"; + +namespace { +struct DarkTintColors { + const char* name; + QColor windowColor; + QColor baseColor; + QColor alternateBaseColor; + QColor buttonColor; + QColor highlightColor; + QColor highlightedTextColor; + QColor borderColor; + QColor darkColor; + QColor shadowColor; + QColor lightColor; +}; + +const DarkTintColors DARK_TINTS[] = { + {"Forest", QColor(26, 32, 28), QColor(18, 24, 20), QColor(32, 40, 35), QColor(35, 44, 38), QColor(74, 163, 111), QColor(10, 24, 16), QColor(46, 58, 51), QColor(16, 20, 18), QColor(8, 10, 9), QColor(45, 56, 49)}, + {"Pine", QColor(22, 31, 27), QColor(15, 23, 19), QColor(28, 40, 33), QColor(31, 43, 36), QColor(59, 153, 111), QColor(9, 22, 16), QColor(40, 56, 47), QColor(13, 19, 16), QColor(7, 10, 8), QColor(40, 54, 46)}, + {"Moss", QColor(29, 34, 26), QColor(21, 25, 19), QColor(36, 42, 31), QColor(39, 47, 35), QColor(109, 162, 89), QColor(13, 24, 11), QColor(50, 59, 44), QColor(17, 20, 15), QColor(9, 10, 8), QColor(49, 58, 43)}, + {"Mint", QColor(22, 33, 30), QColor(15, 24, 21), QColor(29, 42, 38), QColor(31, 45, 40), QColor(72, 177, 140), QColor(8, 25, 19), QColor(41, 60, 53), QColor(13, 20, 18), QColor(7, 10, 9), QColor(41, 58, 51)} +}; + +int ClampDarkTintIndex(int tint) +{ + const int tintCount = static_cast(sizeof(DARK_TINTS) / sizeof(*DARK_TINTS)); + if (tint < 0 || tint >= tintCount) { + return 0; + } + return tint; +} + +int CurrentDarkTintFromSettings() +{ + QSettings settings(QAPP_ORG_NAME, QAPP_APP_NAME_DEFAULT); + if (!settings.contains(DARK_MODE_TINT_SETTING)) { + settings.setValue(DARK_MODE_TINT_SETTING, 0); + } + return ClampDarkTintIndex(settings.value(DARK_MODE_TINT_SETTING, 0).toInt()); +} + +const DarkTintColors& ActiveDarkTintColors() +{ + return DARK_TINTS[CurrentDarkTintFromSettings()]; +} + +} namespace { /* Local functions for colorizing single-color images */ @@ -145,3 +201,98 @@ const PlatformStyle *PlatformStyle::instantiate(const QString &platformId) return 0; } +QPalette PlatformStyle::createDarkModePalette() +{ + QPalette darkPalette; + + const DarkTintColors& tint = ActiveDarkTintColors(); + const QColor textColor(214, 232, 220); + const QColor mutedTextColor(137, 161, 146); + + darkPalette.setColor(QPalette::Window, tint.windowColor); + darkPalette.setColor(QPalette::WindowText, textColor); + darkPalette.setColor(QPalette::Base, tint.baseColor); + darkPalette.setColor(QPalette::AlternateBase, tint.alternateBaseColor); + darkPalette.setColor(QPalette::ToolTipBase, tint.alternateBaseColor); + darkPalette.setColor(QPalette::ToolTipText, textColor); + darkPalette.setColor(QPalette::Text, textColor); + darkPalette.setColor(QPalette::Button, tint.buttonColor); + darkPalette.setColor(QPalette::ButtonText, textColor); + darkPalette.setColor(QPalette::Mid, tint.borderColor); + darkPalette.setColor(QPalette::Dark, tint.darkColor); + darkPalette.setColor(QPalette::Shadow, tint.shadowColor); + darkPalette.setColor(QPalette::Light, tint.lightColor); + darkPalette.setColor(QPalette::Link, tint.highlightColor); + darkPalette.setColor(QPalette::Highlight, tint.highlightColor); + darkPalette.setColor(QPalette::HighlightedText, tint.highlightedTextColor); + darkPalette.setColor(QPalette::BrightText, QColor(255, 128, 128)); + darkPalette.setColor(QPalette::Disabled, QPalette::Text, mutedTextColor); + darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, mutedTextColor); + darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, mutedTextColor); + + return darkPalette; +} + +bool PlatformStyle::isDarkModeEnabled() +{ + QSettings settings(QAPP_ORG_NAME, QAPP_APP_NAME_DEFAULT); + if (!settings.contains(DARK_MODE_SETTING)) { + settings.setValue(DARK_MODE_SETTING, true); + } + return settings.value(DARK_MODE_SETTING, true).toBool(); +} + +int PlatformStyle::darkModeTint() +{ + return CurrentDarkTintFromSettings(); +} + +QString PlatformStyle::darkModeTintName(int tint) +{ + return QString::fromLatin1(DARK_TINTS[ClampDarkTintIndex(tint)].name); +} + +int PlatformStyle::darkModeTintCount() +{ + return static_cast(sizeof(DARK_TINTS) / sizeof(*DARK_TINTS)); +} + +void PlatformStyle::setDarkModeTint(int tint) +{ + QSettings settings(QAPP_ORG_NAME, QAPP_APP_NAME_DEFAULT); + settings.setValue(DARK_MODE_TINT_SETTING, ClampDarkTintIndex(tint)); + if (isDarkModeEnabled()) { + applyTheme(true); + } +} + +void PlatformStyle::applyTheme(bool darkModeEnabled) +{ + if (!qobject_cast(QCoreApplication::instance())) { + return; + } + + QStyle* fusionStyle = QStyleFactory::create("Fusion"); + if (fusionStyle) { + QApplication::setStyle(fusionStyle); + } + + if (darkModeEnabled) { + QApplication::setPalette(createDarkModePalette()); + const QString borderColor = ActiveDarkTintColors().borderColor.name(); + qApp->setStyleSheet( + QString("QDialog, QMessageBox { border: 1px solid %1; }" + "QMenu { border: 1px solid %1; }").arg(borderColor)); + } else { + const QStyle* currentStyle = QApplication::style(); + QApplication::setPalette(currentStyle ? currentStyle->standardPalette() : QPalette()); + qApp->setStyleSheet(""); + } +} + +void PlatformStyle::setDarkModeEnabled(bool enabled) +{ + QSettings settings(QAPP_ORG_NAME, QAPP_APP_NAME_DEFAULT); + settings.setValue(DARK_MODE_SETTING, enabled); + applyTheme(enabled); +} diff --git a/src/qt/platformstyle.h b/src/qt/platformstyle.h index 4e763e760ea..d75e96c045e 100644 --- a/src/qt/platformstyle.h +++ b/src/qt/platformstyle.h @@ -6,6 +6,7 @@ #define BITCOIN_QT_PLATFORMSTYLE_H #include +#include #include #include @@ -16,6 +17,30 @@ class PlatformStyle /** Get style associated with provided platform name, or 0 if not known */ static const PlatformStyle *instantiate(const QString &platformId); + /** Return whether dark mode is currently enabled in GUI settings. */ + static bool isDarkModeEnabled(); + + /** Persist dark mode preference and apply it to the running application. */ + static void setDarkModeEnabled(bool enabled); + + /** Return current dark-mode tint index. */ + static int darkModeTint(); + + /** Return display label for a dark-mode tint index. */ + static QString darkModeTintName(int tint); + + /** Return total number of available dark-mode tints. */ + static int darkModeTintCount(); + + /** Persist dark-mode tint and apply immediately when dark mode is enabled. */ + static void setDarkModeTint(int tint); + + /** Apply either dark or light theme to the running application. */ + static void applyTheme(bool darkModeEnabled); + + /** Create the default dark mode palette used by the Qt application. */ + static QPalette createDarkModePalette(); + const QString &getName() const { return name; } bool getImagesOnButtons() const { return imagesOnButtons; } @@ -52,4 +77,3 @@ class PlatformStyle }; #endif // BITCOIN_QT_PLATFORMSTYLE_H - diff --git a/src/qt/splashscreen.cpp b/src/qt/splashscreen.cpp index f4f0935d374..c8a2ceb60ed 100644 --- a/src/qt/splashscreen.cpp +++ b/src/qt/splashscreen.cpp @@ -10,6 +10,7 @@ #include "splashscreen.h" #include "networkstyle.h" +#include "platformstyle.h" #include "clientversion.h" #include "init.h" @@ -24,13 +25,139 @@ #include #include #include +#include #include +#include #include +#include #include SplashScreen::SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle) : - QWidget(0, f), curAlignment(0) + QWidget(0, f), curAlignment(0), networkStyle(networkStyle), darkModeButton(nullptr), darkModeTintMenu(nullptr) +{ + buildPixmap(PlatformStyle::isDarkModeEnabled()); + + // Set window title + setWindowTitle(tr(PACKAGE_NAME) + " " + networkStyle->getTitleAddText()); + + // Resize window and move to center of desktop, disallow resizing + const qreal ratio = pixmap.devicePixelRatio(); + QRect r(QPoint(), QSize(pixmap.size().width()/ratio, pixmap.size().height()/ratio)); + resize(r.size()); + setFixedSize(r.size()); + move(QApplication::desktop()->screenGeometry().center() - r.center()); + darkModeButton = new QPushButton(this); + darkModeButton->setContextMenuPolicy(Qt::CustomContextMenu); + darkModeTintMenu = new QMenu(darkModeButton); + QActionGroup* tintGroup = new QActionGroup(darkModeTintMenu); + tintGroup->setExclusive(true); + for (int tint = 0; tint < PlatformStyle::darkModeTintCount(); ++tint) { + QAction* tintAction = new QAction(PlatformStyle::darkModeTintName(tint), darkModeTintMenu); + tintAction->setData(tint); + tintAction->setCheckable(true); + tintGroup->addAction(tintAction); + darkModeTintMenu->addAction(tintAction); + } + connect(darkModeButton, SIGNAL(clicked()), this, SLOT(toggleDarkMode())); + connect(darkModeButton, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showDarkTintMenu(QPoint))); + connect(darkModeTintMenu, SIGNAL(triggered(QAction*)), this, SLOT(onDarkTintSelected(QAction*))); + updateDarkModeButton(); + darkModeButton->show(); + + subscribeToCoreSignals(); +} + +SplashScreen::~SplashScreen() +{ + unsubscribeFromCoreSignals(); +} + +void SplashScreen::slotFinish(QWidget *mainWin) +{ + Q_UNUSED(mainWin); + + /* If the window is minimized, hide() will be ignored. */ + /* Make sure we de-minimize the splashscreen window before hiding */ + if (isMinimized()) + showNormal(); + hide(); + deleteLater(); // No more need for this +} + +static void InitMessage(SplashScreen *splash, const std::string &message) +{ + const QColor progressColor = PlatformStyle::isDarkModeEnabled() ? QColor(214,232,220) : QColor(55,55,55); + QMetaObject::invokeMethod(splash, "showMessage", + Qt::QueuedConnection, + Q_ARG(QString, QString::fromStdString(message)), + Q_ARG(int, Qt::AlignBottom|Qt::AlignHCenter), + Q_ARG(QColor, progressColor)); +} + +static void ShowProgress(SplashScreen *splash, const std::string &title, int nProgress) +{ + InitMessage(splash, title + strprintf("%d", nProgress) + "%"); +} + +#ifdef ENABLE_WALLET +void SplashScreen::ConnectWallet(CWallet* wallet) +{ + wallet->ShowProgress.connect(boost::bind(ShowProgress, this, + boost::placeholders::_1, + boost::placeholders::_2)); + connectedWallets.push_back(wallet); +} +#endif + +void SplashScreen::subscribeToCoreSignals() +{ + // Connect signals to client + uiInterface.InitMessage.connect(boost::bind(InitMessage, this, + boost::placeholders::_1)); + uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, + boost::placeholders::_1,boost::placeholders::_2)); +#ifdef ENABLE_WALLET + uiInterface.LoadWallet.connect(boost::bind(&SplashScreen::ConnectWallet, this, + boost::placeholders::_1)); +#endif +} + +void SplashScreen::unsubscribeFromCoreSignals() +{ + // Disconnect signals from client + uiInterface.InitMessage.disconnect(boost::bind(InitMessage, this, + boost::placeholders::_1)); + uiInterface.ShowProgress.disconnect(boost::bind(ShowProgress, this, + boost::placeholders::_1, + boost::placeholders::_2)); +#ifdef ENABLE_WALLET + Q_FOREACH(CWallet* const & pwallet, connectedWallets) { + pwallet->ShowProgress.disconnect(boost::bind(ShowProgress, this, + boost::placeholders::_1, + boost::placeholders::_2)); + } +#endif +} + +void SplashScreen::showMessage(const QString &message, int alignment, const QColor &color) +{ + curMessage = message; + curAlignment = alignment; + curColor = color; + update(); +} + +void SplashScreen::toggleDarkMode() +{ + const bool darkModeEnabled = PlatformStyle::isDarkModeEnabled(); + PlatformStyle::setDarkModeEnabled(!darkModeEnabled); + buildPixmap(!darkModeEnabled); + updateDarkModeButton(); + update(); +} + +void SplashScreen::buildPixmap(bool darkModeEnabled) { // set reference point, paddings int paddingRight = 50; @@ -62,12 +189,18 @@ SplashScreen::SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle) #endif QPainter pixPaint(&pixmap); - pixPaint.setPen(QColor(100,100,100)); + const QColor textColor = darkModeEnabled ? QColor(214,232,220) : QColor(100,100,100); + pixPaint.setPen(textColor); // draw a slightly radial gradient QRadialGradient gradient(QPoint(0,0), splashSize.width()/devicePixelRatio); - gradient.setColorAt(0, Qt::white); - gradient.setColorAt(1, QColor(247,247,247)); + if (darkModeEnabled) { + gradient.setColorAt(0, QColor(22, 31, 27)); + gradient.setColorAt(1, QColor(15, 23, 19)); + } else { + gradient.setColorAt(0, Qt::white); + gradient.setColorAt(1, QColor(247,247,247)); + } QRect rGradient(QPoint(0,0), splashSize); pixPaint.fillRect(rGradient, gradient); @@ -94,7 +227,7 @@ SplashScreen::SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle) pixPaint.setFont(QFont(font, 15*fontFactor)); - // if the version string is to long, reduce size + // if the version string is too long, reduce size fm = pixPaint.fontMetrics(); int versionTextWidth = fm.width(versionText); if(versionTextWidth > titleTextWidth+paddingRight-10) { @@ -123,95 +256,44 @@ SplashScreen::SplashScreen(Qt::WindowFlags f, const NetworkStyle *networkStyle) } pixPaint.end(); - - // Set window title - setWindowTitle(titleText + " " + titleAddText); - - // Resize window and move to center of desktop, disallow resizing - QRect r(QPoint(), QSize(pixmap.size().width()/devicePixelRatio,pixmap.size().height()/devicePixelRatio)); - resize(r.size()); - setFixedSize(r.size()); - move(QApplication::desktop()->screenGeometry().center() - r.center()); - - subscribeToCoreSignals(); -} - -SplashScreen::~SplashScreen() -{ - unsubscribeFromCoreSignals(); -} - -void SplashScreen::slotFinish(QWidget *mainWin) -{ - Q_UNUSED(mainWin); - - /* If the window is minimized, hide() will be ignored. */ - /* Make sure we de-minimize the splashscreen window before hiding */ - if (isMinimized()) - showNormal(); - hide(); - deleteLater(); // No more need for this -} - -static void InitMessage(SplashScreen *splash, const std::string &message) -{ - QMetaObject::invokeMethod(splash, "showMessage", - Qt::QueuedConnection, - Q_ARG(QString, QString::fromStdString(message)), - Q_ARG(int, Qt::AlignBottom|Qt::AlignHCenter), - Q_ARG(QColor, QColor(55,55,55))); -} - -static void ShowProgress(SplashScreen *splash, const std::string &title, int nProgress) -{ - InitMessage(splash, title + strprintf("%d", nProgress) + "%"); -} - -#ifdef ENABLE_WALLET -void SplashScreen::ConnectWallet(CWallet* wallet) -{ - wallet->ShowProgress.connect(boost::bind(ShowProgress, this, - boost::placeholders::_1, - boost::placeholders::_2)); - connectedWallets.push_back(wallet); + curColor = darkModeEnabled ? QColor(214,232,220) : QColor(55,55,55); } -#endif -void SplashScreen::subscribeToCoreSignals() +void SplashScreen::updateDarkModeButton() { - // Connect signals to client - uiInterface.InitMessage.connect(boost::bind(InitMessage, this, - boost::placeholders::_1)); - uiInterface.ShowProgress.connect(boost::bind(ShowProgress, this, - boost::placeholders::_1,boost::placeholders::_2)); -#ifdef ENABLE_WALLET - uiInterface.LoadWallet.connect(boost::bind(&SplashScreen::ConnectWallet, this, - boost::placeholders::_1)); -#endif + if (darkModeButton == nullptr) { + return; + } + const bool darkModeEnabled = PlatformStyle::isDarkModeEnabled(); + const QChar sunGlyph(0x2600); + const QChar moonGlyph(0x263E); + darkModeButton->setText(darkModeEnabled ? QString(sunGlyph) : QString(moonGlyph)); + darkModeButton->setToolTip(darkModeEnabled ? tr("Switch to light mode (right-click for green tint options)") : tr("Switch to dark mode (right-click for green tint options)")); + darkModeButton->setFixedSize(24, 24); + darkModeButton->move(width() - darkModeButton->width() - 10, height() - darkModeButton->height() - 10); } -void SplashScreen::unsubscribeFromCoreSignals() +void SplashScreen::showDarkTintMenu(const QPoint& point) { - // Disconnect signals from client - uiInterface.InitMessage.disconnect(boost::bind(InitMessage, this, - boost::placeholders::_1)); - uiInterface.ShowProgress.disconnect(boost::bind(ShowProgress, this, - boost::placeholders::_1, - boost::placeholders::_2)); -#ifdef ENABLE_WALLET - Q_FOREACH(CWallet* const & pwallet, connectedWallets) { - pwallet->ShowProgress.disconnect(boost::bind(ShowProgress, this, - boost::placeholders::_1, - boost::placeholders::_2)); + if (!darkModeTintMenu || !darkModeButton) { + return; } -#endif + const int currentTint = PlatformStyle::darkModeTint(); + Q_FOREACH (QAction* action, darkModeTintMenu->actions()) + { + action->setChecked(action->data().toInt() == currentTint); + } + darkModeTintMenu->exec(darkModeButton->mapToGlobal(point)); } -void SplashScreen::showMessage(const QString &message, int alignment, const QColor &color) +void SplashScreen::onDarkTintSelected(QAction* action) { - curMessage = message; - curAlignment = alignment; - curColor = color; + if (!action) { + return; + } + PlatformStyle::setDarkModeTint(action->data().toInt()); + buildPixmap(PlatformStyle::isDarkModeEnabled()); + updateDarkModeButton(); update(); } diff --git a/src/qt/splashscreen.h b/src/qt/splashscreen.h index 95a65cc53c6..b86ccc20acc 100644 --- a/src/qt/splashscreen.h +++ b/src/qt/splashscreen.h @@ -9,6 +9,9 @@ class CWallet; class NetworkStyle; +class QPushButton; +class QMenu; +class QAction; /** Class for the splashscreen with information of the running client. * @@ -35,6 +38,13 @@ public Q_SLOTS: /** Show message and progress */ void showMessage(const QString &message, int alignment, const QColor &color); + /** Toggle dark mode while splash screen is visible. */ + void toggleDarkMode(); + /** Show tint menu for splash dark mode button. */ + void showDarkTintMenu(const QPoint& point); + /** Handle tint selection from splash dark mode button menu. */ + void onDarkTintSelected(QAction* action); + private: /** Connect core signals to splash screen */ void subscribeToCoreSignals(); @@ -42,11 +52,18 @@ public Q_SLOTS: void unsubscribeFromCoreSignals(); /** Connect wallet signals to splash screen */ void ConnectWallet(CWallet*); + /** Rebuild splash pixmap with current dark/light mode setting. */ + void buildPixmap(bool darkModeEnabled); + /** Update splash dark mode button label and placement. */ + void updateDarkModeButton(); QPixmap pixmap; QString curMessage; QColor curColor; int curAlignment; + const NetworkStyle* networkStyle; + QPushButton* darkModeButton; + QMenu* darkModeTintMenu; QList connectedWallets; };