From df593892cb78eba04197d35e490ee8aad40aa3da Mon Sep 17 00:00:00 2001 From: xdustinface Date: Mon, 8 Jun 2020 12:04:03 +0200 Subject: [PATCH 1/4] qt: Set parents properly to inherit css and remove redundant loading The stylesheet is inherited from the parent so its enough to set it for the main window and use this as parent for all other windows. Except for the ShutdownDialog. This needs to have its own stylesheet set because we can't have the main window as parent of the ShutdownDialog. --- src/qt/bitcoingui.cpp | 5 ++--- src/qt/coincontroldialog.cpp | 6 +----- src/qt/masternodelist.cpp | 2 +- src/qt/rpcconsole.cpp | 3 --- src/qt/sendcoinsdialog.cpp | 2 +- src/qt/transactiondescdialog.cpp | 2 -- src/qt/transactionview.cpp | 2 +- src/qt/walletview.cpp | 3 +-- 8 files changed, 7 insertions(+), 18 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 5cac47942031..6f9cefd01975 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -151,7 +151,7 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle * setWindowIcon(networkStyle->getTrayAndWindowIcon()); setWindowTitle(windowTitle); - rpcConsole = new RPCConsole(_platformStyle, 0); + rpcConsole = new RPCConsole(_platformStyle, this); helpMessageDialog = new HelpMessageDialog(this, HelpMessageDialog::cmdline); #ifdef ENABLE_WALLET if(enableWallet) @@ -1530,8 +1530,7 @@ void BitcoinGUI::showProgress(const QString &title, int nProgress) { if (nProgress == 0) { - progressDialog = new QProgressDialog(title, "", 0, 100); - progressDialog->setStyleSheet(GUIUtil::loadStyleSheet()); + progressDialog = new QProgressDialog(title, "", 0, 100, this); progressDialog->setWindowModality(Qt::ApplicationModal); progressDialog->setMinimumDuration(0); progressDialog->setCancelButton(0); diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 3f3f4c6e6739..0829ce7efb05 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -51,9 +51,6 @@ CoinControlDialog::CoinControlDialog(const PlatformStyle *_platformStyle, QWidge { ui->setupUi(this); - /* Open CSS when configured */ - this->setStyleSheet(GUIUtil::loadStyleSheet()); - GUIUtil::setFont({ui->labelCoinControlQuantityText, ui->labelCoinControlBytesText, ui->labelCoinControlAmountText, @@ -237,9 +234,8 @@ void CoinControlDialog::buttonToggleLockClicked() CoinControlDialog::updateLabels(model, this); } else{ - QMessageBox msgBox; + QMessageBox msgBox(this); msgBox.setObjectName("lockMessageBox"); - msgBox.setStyleSheet(GUIUtil::loadStyleSheet()); msgBox.setText(tr("Please switch to \"List mode\" to use this function.")); msgBox.exec(); } diff --git a/src/qt/masternodelist.cpp b/src/qt/masternodelist.cpp index 8a8a880d0485..4ab9a9fe051c 100644 --- a/src/qt/masternodelist.cpp +++ b/src/qt/masternodelist.cpp @@ -79,7 +79,7 @@ MasternodeList::MasternodeList(const PlatformStyle* platformStyle, QWidget* pare QAction* copyProTxHashAction = new QAction(tr("Copy ProTx Hash"), this); QAction* copyCollateralOutpointAction = new QAction(tr("Copy Collateral Outpoint"), this); - contextMenuDIP3 = new QMenu(); + contextMenuDIP3 = new QMenu(this); contextMenuDIP3->addAction(copyProTxHashAction); contextMenuDIP3->addAction(copyCollateralOutpointAction); connect(ui->tableWidgetMasternodesDIP3, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showContextMenuDIP3(const QPoint&))); diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index d44ad33b621d..9e41c1535a18 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -461,9 +461,6 @@ RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent) : { ui->setupUi(this); - /* Open CSS when configured */ - this->setStyleSheet(GUIUtil::loadStyleSheet()); - GUIUtil::setFont({ui->label_9, ui->labelNetwork, ui->label_10, diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 395b4bae317c..d2cf188c0b89 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -900,7 +900,7 @@ void SendCoinsDialog::coinControlFeatureChanged(bool checked) // Coin Control: button inputs -> show actual coin control dialog void SendCoinsDialog::coinControlButtonClicked() { - CoinControlDialog dlg(platformStyle); + CoinControlDialog dlg(platformStyle, this); dlg.setModel(model); dlg.exec(); coinControlUpdateLabels(); diff --git a/src/qt/transactiondescdialog.cpp b/src/qt/transactiondescdialog.cpp index 648d680a59b5..b4c0c1585edb 100644 --- a/src/qt/transactiondescdialog.cpp +++ b/src/qt/transactiondescdialog.cpp @@ -18,8 +18,6 @@ TransactionDescDialog::TransactionDescDialog(const QModelIndex &idx, QWidget *pa { ui->setupUi(this); setWindowTitle(tr("Details for %1").arg(idx.data(TransactionTableModel::TxHashRole).toString())); - /* Open CSS when configured */ - this->setStyleSheet(GUIUtil::loadStyleSheet()); QString desc = idx.data(TransactionTableModel::LongDescriptionRole).toString(); ui->detailText->setHtml(desc); } diff --git a/src/qt/transactionview.cpp b/src/qt/transactionview.cpp index 9f57d40b28d5..982c6a3edba2 100644 --- a/src/qt/transactionview.cpp +++ b/src/qt/transactionview.cpp @@ -556,7 +556,7 @@ void TransactionView::showDetails() QModelIndexList selection = transactionView->selectionModel()->selectedRows(); if(!selection.isEmpty()) { - TransactionDescDialog *dlg = new TransactionDescDialog(selection.at(0)); + TransactionDescDialog* dlg = new TransactionDescDialog(selection.at(0), this); dlg->setAttribute(Qt::WA_DeleteOnClose); dlg->show(); } diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index 16a264bb5df6..7383f31a47cf 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -385,8 +385,7 @@ void WalletView::showProgress(const QString &title, int nProgress) { if (nProgress == 0) { - progressDialog = new QProgressDialog(title, "", 0, 100); - progressDialog->setStyleSheet(GUIUtil::loadStyleSheet()); + progressDialog = new QProgressDialog(title, "", 0, 100, this); progressDialog->setWindowModality(Qt::ApplicationModal); progressDialog->setMinimumDuration(0); progressDialog->setCancelButton(0); From 91d1dc4dcecf735a321225f0a0464df94387d7f6 Mon Sep 17 00:00:00 2001 From: xdustinface Date: Tue, 9 Jun 2020 01:22:59 +0200 Subject: [PATCH 2/4] qt: Adding Qt::Window flag to the constructor of RPCConsole This is required now as RPCConsole does not longer show as extra window with a parent set (For stylesheet inheritance). --- src/qt/rpcconsole.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 9e41c1535a18..7f8968e9f1d1 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -450,7 +450,7 @@ void RPCExecutor::request(const QString &command) } RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent) : - QWidget(parent), + QWidget(parent, Qt::Window), ui(new Ui::RPCConsole), clientModel(0), historyPtr(0), From e7ec855ff70dc5c9627337d6e9488d33669c2fb3 Mon Sep 17 00:00:00 2001 From: xdustinface Date: Mon, 8 Jun 2020 04:36:37 +0200 Subject: [PATCH 3/4] qt: Add UI debug mode (-debug-ui) If this mode is enabled the content of the css files will become pushed to the UI if there were any changes made to the stylesheet files in the custom css directory. It also forces some UI elements to show up which are actually only shown under special conditions (e.g. watch balance labels). Its required to set a custom css directory with -custom-css-dir to enable this. --- src/qt/bitcoingui.cpp | 3 +- src/qt/dash.cpp | 5 ++ src/qt/guiutil.cpp | 102 +++++++++++++++++++++++++++++++-------- src/qt/guiutil.h | 11 +++-- src/qt/overviewpage.cpp | 9 ++-- src/qt/utilitydialog.cpp | 4 +- 6 files changed, 102 insertions(+), 32 deletions(-) diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 6f9cefd01975..0418d9f88fee 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -125,8 +125,7 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle * spinnerFrame(0), platformStyle(_platformStyle) { - /* Open CSS when configured */ - this->setStyleSheet(GUIUtil::loadStyleSheet()); + GUIUtil::loadStyleSheet(this); QSettings settings; if (!restoreGeometry(settings.value("MainWindowGeometry").toByteArray())) { diff --git a/src/qt/dash.cpp b/src/qt/dash.cpp index dfcd3b8cde75..1960be60c9eb 100644 --- a/src/qt/dash.cpp +++ b/src/qt/dash.cpp @@ -775,6 +775,11 @@ int main(int argc, char *argv[]) GUIUtil::setStyleSheetDirectory(strCustomDir); } + // Validate -debug-ui + if (gArgs.GetBoolArg("-debug-ui", false)) { + QMessageBox::warning(0, QObject::tr(PACKAGE_NAME), + "Warning: UI debug mode (-debug-ui) enabled" + QString(gArgs.IsArgSet("-custom-css-dir") ? "." : " without a custom css directory set with -custom-css-dir.")); + } // Subscribe to global signals from core uiInterface.InitMessage.connect(InitMessage); diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 5f7f2073791b..abdd4c24156d 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -55,6 +55,7 @@ #include #include // for Qt::mightBeRichText #include +#include #include #include @@ -80,6 +81,7 @@ void ForceActivation(); namespace GUIUtil { +static CCriticalSection cs_css; // The default stylesheet directory static const QString defaultStylesheetDirectory = ":css"; // The actual stylesheet directory @@ -1013,43 +1015,92 @@ const std::vector listThemes() return vecThemes; } -// Open CSS when configured -QString loadStyleSheet() +void loadStyleSheet(QWidget* widget, bool fDebugWidget) { - static std::unique_ptr stylesheet; + AssertLockNotHeld(cs_css); + LOCK(cs_css); - if (stylesheet == nullptr) { - stylesheet = std::make_unique(); + static std::unique_ptr stylesheet; + static std::set setWidgets; + + bool fDebugCustomStyleSheets = gArgs.GetBoolArg("-debug-ui", false) && isStyleSheetDirectoryCustom(); + bool fStyleSheetChanged = false; + + if (stylesheet == nullptr || fDebugCustomStyleSheets) { + auto hasModified = [](const std::vector& vecFiles) -> bool { + static std::map mapLastModified; + + bool fModified = false; + for (auto file = vecFiles.begin(); file != vecFiles.end() && !fModified; ++file) { + QFileInfo info(*file); + QDateTime lastModified = info.lastModified(), prevLastModified; + auto it = mapLastModified.emplace(std::make_pair(*file, lastModified)); + prevLastModified = it.second ? QDateTime() : it.first->second; + it.first->second = lastModified; + fModified = prevLastModified != lastModified; + } + return fModified; + }; - QSettings settings; - QDir themes(":css"); - QString theme = settings.value("theme", "").toString(); + auto loadFiles = [&](const std::vector& vecFiles) -> bool { + if (fDebugCustomStyleSheets && !hasModified(vecFiles)) { + return false; + } - // Make sure settings are pointing to an existent theme - if (!isStyleSheetDirectoryCustom() && (theme.isEmpty() || !themes.exists(theme))) { - theme = defaultTheme; - settings.setValue("theme", theme); - } + stylesheet = std::make_unique(); - auto loadFile = [&](const QString& name) { - QFile qFile(stylesheetDirectory + "/" + name + (isStyleSheetDirectoryCustom() ? ".css" : "")); - if (qFile.open(QFile::ReadOnly)) { + for (const auto& file : vecFiles) { + QFile qFile(file); + if (!qFile.open(QFile::ReadOnly)) { + throw std::runtime_error(strprintf("%s: Failed to open file: %s", __func__, file.toStdString())); + } stylesheet->append(QLatin1String(qFile.readAll())); } + return true; + }; + + auto pathToFile = [&](const QString& file) -> QString { + return stylesheetDirectory + "/" + file + (isStyleSheetDirectoryCustom() ? ".css" : ""); }; + std::vector vecFiles; // If light/dark theme is used load general styles first if (dashThemeActive()) { - loadFile("general"); #ifndef Q_OS_MAC - loadFile("scrollbars"); + vecFiles.push_back(pathToFile("scrollbars")); #endif + vecFiles.push_back(pathToFile("general")); } + vecFiles.push_back(pathToFile(getActiveTheme())); - loadFile(theme); + fStyleSheetChanged = loadFiles(vecFiles); } - return *stylesheet; + 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; + } + } + + if (widget) { + widget->setStyleSheet(*stylesheet); + } + + if (!ShutdownRequested() && fDebugCustomStyleSheets) { + QTimer::singleShot(200, [] { loadStyleSheet(); }); + } } FontFamily fontFamilyFromString(const QString& strFamily) @@ -1421,12 +1472,15 @@ QFont getFont(FontWeight weight, bool fItalic, int nPointSize) font.setWeight(qWeight); font.setStyle(fItalic ? QFont::StyleItalic : QFont::StyleNormal); } - qDebug() << "GUIUtil::getFont() - " << font.toString() << " family: " << font.family() << ", style: " << font.styleName() << " match: " << font.exactMatch(); if (nPointSize != -1) { font.setPointSizeF(getScaledFontSize(nPointSize)); } + if (gArgs.GetBoolArg("-debug-ui", false)) { + qDebug() << __func__ << ": font size: " << font.pointSizeF() << " family: " << font.family() << ", style: " << font.styleName() << " match: " << font.exactMatch(); + } + return font; } @@ -1440,6 +1494,12 @@ QFont getFontBold() return getFont(FontWeight::Bold); } +QString getActiveTheme() +{ + QSettings settings; + return settings.value("theme", defaultTheme).toString(); +} + bool dashThemeActive() { QSettings settings; diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index ce6e55f4fe50..b6fa17cfbae6 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -243,8 +243,10 @@ namespace GUIUtil /** Return a list of all theme css files */ const std::vector listThemes(); - /** Load global CSS theme */ - QString loadStyleSheet(); + /** 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); enum class FontFamily { SystemDefault, @@ -317,7 +319,10 @@ namespace GUIUtil /** Get the default bold QFont */ QFont getFontBold(); - /** Check if a dash specific theme is activated (light/dark) */ + /** Return the name of the currently active theme.*/ + QString getActiveTheme(); + + /** Check if a dash specific theme is activated (light/dark).*/ bool dashThemeActive(); /** Disable the OS default focus rect for macOS because we have custom focus rects diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 258fb1635aa8..b9e95fc9d69a 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -232,10 +232,11 @@ void OverviewPage::setBalance(const CAmount& balance, const CAmount& unconfirmed ui->labelWatchImmature->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, watchImmatureBalance, false, BitcoinUnits::separatorAlways)); ui->labelWatchTotal->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, watchOnlyBalance + watchUnconfBalance + watchImmatureBalance, false, BitcoinUnits::separatorAlways)); - // only show immature (newly mined) balance if it's non-zero, so as not to complicate things + // only show immature (newly mined) balance if it's non-zero or in UI debug mode, so as not to complicate things // for the non-mining users - bool showImmature = immatureBalance != 0; - bool showWatchOnlyImmature = watchImmatureBalance != 0; + bool fDebugUI = gArgs.GetBoolArg("-debug-ui", false); + bool showImmature = fDebugUI || immatureBalance != 0; + bool showWatchOnlyImmature = fDebugUI || watchImmatureBalance != 0; // for symmetry reasons also show immature label when the watch-only one is shown ui->labelImmature->setVisible(showImmature || showWatchOnlyImmature); @@ -298,7 +299,7 @@ void OverviewPage::setWalletModel(WalletModel *model) connect(model, SIGNAL(balanceChanged(CAmount,CAmount,CAmount,CAmount,CAmount,CAmount,CAmount)), this, SLOT(setBalance(CAmount,CAmount,CAmount,CAmount,CAmount,CAmount,CAmount))); connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit())); - updateWatchOnlyLabels(model->haveWatchOnly()); + updateWatchOnlyLabels(model->haveWatchOnly() || gArgs.GetBoolArg("-debug-ui", false)); connect(model, SIGNAL(notifyWatchonlyChanged(bool)), this, SLOT(updateWatchOnlyLabels(bool))); // explicitly update PS frame and transaction list to reflect actual settings diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp index 5a6bbe5e7f24..f0e09ca89116 100644 --- a/src/qt/utilitydialog.cpp +++ b/src/qt/utilitydialog.cpp @@ -100,6 +100,7 @@ HelpMessageDialog::HelpMessageDialog(QWidget *parent, HelpMode helpMode) : strUsage += HelpMessageOpt("-resetguisettings", tr("Reset all settings changed in the GUI").toStdString()); if (showDebug) { strUsage += HelpMessageOpt("-uiplatform", strprintf("Select platform to customize UI for (one of windows, macosx, other; default: %s)", BitcoinGUI::DEFAULT_UIPLATFORM)); + strUsage += HelpMessageOpt("-debug-ui", "Enable UI debug mode which allows to see live stylesheet updates in the UI if its used together with -custom-css-dir. It also forces some widgets to show up which are usually only visible under certain circumstances. (default: false)"); } strUsage += HelpMessageOpt("-windowtitle=", _("Sets a window title which is appended to \"Dash Core - \"")); @@ -210,8 +211,7 @@ ShutdownWindow::ShutdownWindow(QWidget *parent, Qt::WindowFlags f): { setObjectName("ShutdownWindow"); - /* Open CSS when configured */ - this->setStyleSheet(GUIUtil::loadStyleSheet()); + GUIUtil::loadStyleSheet(this, false); QVBoxLayout *layout = new QVBoxLayout(); layout->addWidget(new QLabel( From 12877793d771024fa2c877f0eeaef42b3aae35fd Mon Sep 17 00:00:00 2001 From: xdustinface Date: Tue, 14 Jul 2020 08:05:16 +0200 Subject: [PATCH 4/4] qt: Adjust the description of -debug-ui --- src/qt/utilitydialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp index f0e09ca89116..0e1c2f1e35e7 100644 --- a/src/qt/utilitydialog.cpp +++ b/src/qt/utilitydialog.cpp @@ -100,7 +100,7 @@ HelpMessageDialog::HelpMessageDialog(QWidget *parent, HelpMode helpMode) : strUsage += HelpMessageOpt("-resetguisettings", tr("Reset all settings changed in the GUI").toStdString()); if (showDebug) { strUsage += HelpMessageOpt("-uiplatform", strprintf("Select platform to customize UI for (one of windows, macosx, other; default: %s)", BitcoinGUI::DEFAULT_UIPLATFORM)); - strUsage += HelpMessageOpt("-debug-ui", "Enable UI debug mode which allows to see live stylesheet updates in the UI if its used together with -custom-css-dir. It also forces some widgets to show up which are usually only visible under certain circumstances. (default: false)"); + strUsage += HelpMessageOpt("-debug-ui", "Updates the UI's stylesheets in realtime with changes made to the css files in -custom-css-dir and forces some widgets to show up which are usually only visible under certain circumstances. (default: 0)"); } strUsage += HelpMessageOpt("-windowtitle=", _("Sets a window title which is appended to \"Dash Core - \""));