diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 5cac479420..0418d9f88f 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())) { @@ -151,7 +150,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 +1529,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 3f3f4c6e67..0829ce7efb 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/dash.cpp b/src/qt/dash.cpp index dfcd3b8cde..1960be60c9 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 0d190eed71..478bc7e020 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 @@ -1023,43 +1025,92 @@ const std::vector listThemes() return vecThemes; } -// Open CSS when configured -QString loadStyleSheet() +void loadStyleSheet(QWidget* widget, bool fDebugWidget) { + AssertLockNotHeld(cs_css); + LOCK(cs_css); + static std::unique_ptr stylesheet; + static std::set setWidgets; - if (stylesheet == nullptr) { - stylesheet = std::make_unique(); + bool fDebugCustomStyleSheets = gArgs.GetBoolArg("-debug-ui", false) && isStyleSheetDirectoryCustom(); + bool fStyleSheetChanged = false; - QSettings settings; - QDir themes(":css"); - QString theme = settings.value("theme", "").toString(); + if (stylesheet == nullptr || fDebugCustomStyleSheets) { + auto hasModified = [](const std::vector& vecFiles) -> bool { + static std::map mapLastModified; - // Make sure settings are pointing to an existent theme - if (!isStyleSheetDirectoryCustom() && (theme.isEmpty() || !themes.exists(theme))) { - theme = defaultTheme; - settings.setValue("theme", theme); - } - - auto loadFile = [&](const QString& name) { - QFile qFile(stylesheetDirectory + "/" + name + (isStyleSheetDirectoryCustom() ? ".css" : "")); - if (qFile.open(QFile::ReadOnly)) { - stylesheet->append(QLatin1String(qFile.readAll())); + 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; }; + auto loadFiles = [&](const std::vector& vecFiles) -> bool { + if (fDebugCustomStyleSheets && !hasModified(vecFiles)) { + return false; + } + + stylesheet = std::make_unique(); + + 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) @@ -1431,12 +1482,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; } @@ -1450,6 +1504,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 8e261fb96f..a81ceaab08 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -253,8 +253,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, @@ -327,7 +329,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/masternodelist.cpp b/src/qt/masternodelist.cpp index 8a8a880d04..4ab9a9fe05 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/overviewpage.cpp b/src/qt/overviewpage.cpp index f59ac28e7b..c44b1c998f 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/rpcconsole.cpp b/src/qt/rpcconsole.cpp index dde7e76182..79493dab5f 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), @@ -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 395b4bae31..d2cf188c0b 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 648d680a59..b4c0c1585e 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 9f57d40b28..982c6a3edb 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/utilitydialog.cpp b/src/qt/utilitydialog.cpp index 71b7f0ecef..db84c43c15 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", "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 - \"")); @@ -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( diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index 16a264bb5d..7383f31a47 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);