qt: Add -debug-ui command line parameter (#3558)

* 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.

* 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).

* 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.

* qt: Adjust the description of -debug-ui
This commit is contained in:
dustinface 2020-07-14 22:16:38 +02:00 committed by GitHub
parent 36e18d9b48
commit fc6fe505ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 111 additions and 52 deletions

View File

@ -125,8 +125,7 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle *
spinnerFrame(0), spinnerFrame(0),
platformStyle(_platformStyle) platformStyle(_platformStyle)
{ {
/* Open CSS when configured */ GUIUtil::loadStyleSheet(this);
this->setStyleSheet(GUIUtil::loadStyleSheet());
QSettings settings; QSettings settings;
if (!restoreGeometry(settings.value("MainWindowGeometry").toByteArray())) { if (!restoreGeometry(settings.value("MainWindowGeometry").toByteArray())) {
@ -151,7 +150,7 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle *
setWindowIcon(networkStyle->getTrayAndWindowIcon()); setWindowIcon(networkStyle->getTrayAndWindowIcon());
setWindowTitle(windowTitle); setWindowTitle(windowTitle);
rpcConsole = new RPCConsole(_platformStyle, 0); rpcConsole = new RPCConsole(_platformStyle, this);
helpMessageDialog = new HelpMessageDialog(this, HelpMessageDialog::cmdline); helpMessageDialog = new HelpMessageDialog(this, HelpMessageDialog::cmdline);
#ifdef ENABLE_WALLET #ifdef ENABLE_WALLET
if(enableWallet) if(enableWallet)
@ -1530,8 +1529,7 @@ void BitcoinGUI::showProgress(const QString &title, int nProgress)
{ {
if (nProgress == 0) if (nProgress == 0)
{ {
progressDialog = new QProgressDialog(title, "", 0, 100); progressDialog = new QProgressDialog(title, "", 0, 100, this);
progressDialog->setStyleSheet(GUIUtil::loadStyleSheet());
progressDialog->setWindowModality(Qt::ApplicationModal); progressDialog->setWindowModality(Qt::ApplicationModal);
progressDialog->setMinimumDuration(0); progressDialog->setMinimumDuration(0);
progressDialog->setCancelButton(0); progressDialog->setCancelButton(0);

View File

@ -51,9 +51,6 @@ CoinControlDialog::CoinControlDialog(const PlatformStyle *_platformStyle, QWidge
{ {
ui->setupUi(this); ui->setupUi(this);
/* Open CSS when configured */
this->setStyleSheet(GUIUtil::loadStyleSheet());
GUIUtil::setFont({ui->labelCoinControlQuantityText, GUIUtil::setFont({ui->labelCoinControlQuantityText,
ui->labelCoinControlBytesText, ui->labelCoinControlBytesText,
ui->labelCoinControlAmountText, ui->labelCoinControlAmountText,
@ -237,9 +234,8 @@ void CoinControlDialog::buttonToggleLockClicked()
CoinControlDialog::updateLabels(model, this); CoinControlDialog::updateLabels(model, this);
} }
else{ else{
QMessageBox msgBox; QMessageBox msgBox(this);
msgBox.setObjectName("lockMessageBox"); msgBox.setObjectName("lockMessageBox");
msgBox.setStyleSheet(GUIUtil::loadStyleSheet());
msgBox.setText(tr("Please switch to \"List mode\" to use this function.")); msgBox.setText(tr("Please switch to \"List mode\" to use this function."));
msgBox.exec(); msgBox.exec();
} }

View File

@ -775,6 +775,11 @@ int main(int argc, char *argv[])
GUIUtil::setStyleSheetDirectory(strCustomDir); 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 // Subscribe to global signals from core
uiInterface.InitMessage.connect(InitMessage); uiInterface.InitMessage.connect(InitMessage);

View File

@ -55,6 +55,7 @@
#include <QSettings> #include <QSettings>
#include <QTextDocument> // for Qt::mightBeRichText #include <QTextDocument> // for Qt::mightBeRichText
#include <QThread> #include <QThread>
#include <QTimer>
#include <QUrlQuery> #include <QUrlQuery>
#include <QMouseEvent> #include <QMouseEvent>
@ -80,6 +81,7 @@ void ForceActivation();
namespace GUIUtil { namespace GUIUtil {
static CCriticalSection cs_css;
// The default stylesheet directory // The default stylesheet directory
static const QString defaultStylesheetDirectory = ":css"; static const QString defaultStylesheetDirectory = ":css";
// The actual stylesheet directory // The actual stylesheet directory
@ -1023,43 +1025,92 @@ const std::vector<QString> listThemes()
return vecThemes; return vecThemes;
} }
// Open CSS when configured void loadStyleSheet(QWidget* widget, bool fDebugWidget)
QString loadStyleSheet()
{ {
AssertLockNotHeld(cs_css);
LOCK(cs_css);
static std::unique_ptr<QString> stylesheet; static std::unique_ptr<QString> stylesheet;
static std::set<QWidget*> setWidgets;
if (stylesheet == nullptr) { bool fDebugCustomStyleSheets = gArgs.GetBoolArg("-debug-ui", false) && isStyleSheetDirectoryCustom();
stylesheet = std::make_unique<QString>(); bool fStyleSheetChanged = false;
QSettings settings; if (stylesheet == nullptr || fDebugCustomStyleSheets) {
QDir themes(":css"); auto hasModified = [](const std::vector<QString>& vecFiles) -> bool {
QString theme = settings.value("theme", "").toString(); static std::map<const QString, QDateTime> mapLastModified;
// Make sure settings are pointing to an existent theme bool fModified = false;
if (!isStyleSheetDirectoryCustom() && (theme.isEmpty() || !themes.exists(theme))) { for (auto file = vecFiles.begin(); file != vecFiles.end() && !fModified; ++file) {
theme = defaultTheme; QFileInfo info(*file);
settings.setValue("theme", theme); QDateTime lastModified = info.lastModified(), prevLastModified;
} auto it = mapLastModified.emplace(std::make_pair(*file, lastModified));
prevLastModified = it.second ? QDateTime() : it.first->second;
auto loadFile = [&](const QString& name) { it.first->second = lastModified;
QFile qFile(stylesheetDirectory + "/" + name + (isStyleSheetDirectoryCustom() ? ".css" : "")); fModified = prevLastModified != lastModified;
if (qFile.open(QFile::ReadOnly)) {
stylesheet->append(QLatin1String(qFile.readAll()));
} }
return fModified;
}; };
auto loadFiles = [&](const std::vector<QString>& vecFiles) -> bool {
if (fDebugCustomStyleSheets && !hasModified(vecFiles)) {
return false;
}
stylesheet = std::make_unique<QString>();
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<QString> vecFiles;
// If light/dark theme is used load general styles first // If light/dark theme is used load general styles first
if (dashThemeActive()) { if (dashThemeActive()) {
loadFile("general");
#ifndef Q_OS_MAC #ifndef Q_OS_MAC
loadFile("scrollbars"); vecFiles.push_back(pathToFile("scrollbars"));
#endif #endif
vecFiles.push_back(pathToFile("general"));
}
vecFiles.push_back(pathToFile(getActiveTheme()));
fStyleSheetChanged = loadFiles(vecFiles);
} }
loadFile(theme); 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;
}
} }
return *stylesheet; if (widget) {
widget->setStyleSheet(*stylesheet);
}
if (!ShutdownRequested() && fDebugCustomStyleSheets) {
QTimer::singleShot(200, [] { loadStyleSheet(); });
}
} }
FontFamily fontFamilyFromString(const QString& strFamily) FontFamily fontFamilyFromString(const QString& strFamily)
@ -1431,12 +1482,15 @@ QFont getFont(FontWeight weight, bool fItalic, int nPointSize)
font.setWeight(qWeight); font.setWeight(qWeight);
font.setStyle(fItalic ? QFont::StyleItalic : QFont::StyleNormal); font.setStyle(fItalic ? QFont::StyleItalic : QFont::StyleNormal);
} }
qDebug() << "GUIUtil::getFont() - " << font.toString() << " family: " << font.family() << ", style: " << font.styleName() << " match: " << font.exactMatch();
if (nPointSize != -1) { if (nPointSize != -1) {
font.setPointSizeF(getScaledFontSize(nPointSize)); 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; return font;
} }
@ -1450,6 +1504,12 @@ QFont getFontBold()
return getFont(FontWeight::Bold); return getFont(FontWeight::Bold);
} }
QString getActiveTheme()
{
QSettings settings;
return settings.value("theme", defaultTheme).toString();
}
bool dashThemeActive() bool dashThemeActive()
{ {
QSettings settings; QSettings settings;

View File

@ -253,8 +253,10 @@ namespace GUIUtil
/** Return a list of all theme css files */ /** Return a list of all theme css files */
const std::vector<QString> listThemes(); const std::vector<QString> listThemes();
/** Load global CSS theme */ /** Updates the widgets stylesheet and adds it to the list of ui debug elements
QString loadStyleSheet(); 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 { enum class FontFamily {
SystemDefault, SystemDefault,
@ -327,7 +329,10 @@ namespace GUIUtil
/** Get the default bold QFont */ /** Get the default bold QFont */
QFont getFontBold(); 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(); bool dashThemeActive();
/** Disable the OS default focus rect for macOS because we have custom focus rects /** Disable the OS default focus rect for macOS because we have custom focus rects

View File

@ -79,7 +79,7 @@ MasternodeList::MasternodeList(const PlatformStyle* platformStyle, QWidget* pare
QAction* copyProTxHashAction = new QAction(tr("Copy ProTx Hash"), this); QAction* copyProTxHashAction = new QAction(tr("Copy ProTx Hash"), this);
QAction* copyCollateralOutpointAction = new QAction(tr("Copy Collateral Outpoint"), this); QAction* copyCollateralOutpointAction = new QAction(tr("Copy Collateral Outpoint"), this);
contextMenuDIP3 = new QMenu(); contextMenuDIP3 = new QMenu(this);
contextMenuDIP3->addAction(copyProTxHashAction); contextMenuDIP3->addAction(copyProTxHashAction);
contextMenuDIP3->addAction(copyCollateralOutpointAction); contextMenuDIP3->addAction(copyCollateralOutpointAction);
connect(ui->tableWidgetMasternodesDIP3, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showContextMenuDIP3(const QPoint&))); connect(ui->tableWidgetMasternodesDIP3, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(showContextMenuDIP3(const QPoint&)));

View File

@ -232,10 +232,11 @@ void OverviewPage::setBalance(const CAmount& balance, const CAmount& unconfirmed
ui->labelWatchImmature->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, watchImmatureBalance, false, BitcoinUnits::separatorAlways)); ui->labelWatchImmature->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, watchImmatureBalance, false, BitcoinUnits::separatorAlways));
ui->labelWatchTotal->setText(BitcoinUnits::floorHtmlWithUnit(nDisplayUnit, watchOnlyBalance + watchUnconfBalance + 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 // for the non-mining users
bool showImmature = immatureBalance != 0; bool fDebugUI = gArgs.GetBoolArg("-debug-ui", false);
bool showWatchOnlyImmature = watchImmatureBalance != 0; bool showImmature = fDebugUI || immatureBalance != 0;
bool showWatchOnlyImmature = fDebugUI || watchImmatureBalance != 0;
// for symmetry reasons also show immature label when the watch-only one is shown // for symmetry reasons also show immature label when the watch-only one is shown
ui->labelImmature->setVisible(showImmature || showWatchOnlyImmature); 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, 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())); 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))); connect(model, SIGNAL(notifyWatchonlyChanged(bool)), this, SLOT(updateWatchOnlyLabels(bool)));
// explicitly update PS frame and transaction list to reflect actual settings // explicitly update PS frame and transaction list to reflect actual settings

View File

@ -450,7 +450,7 @@ void RPCExecutor::request(const QString &command)
} }
RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent) : RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent) :
QWidget(parent), QWidget(parent, Qt::Window),
ui(new Ui::RPCConsole), ui(new Ui::RPCConsole),
clientModel(0), clientModel(0),
historyPtr(0), historyPtr(0),
@ -461,9 +461,6 @@ RPCConsole::RPCConsole(const PlatformStyle *_platformStyle, QWidget *parent) :
{ {
ui->setupUi(this); ui->setupUi(this);
/* Open CSS when configured */
this->setStyleSheet(GUIUtil::loadStyleSheet());
GUIUtil::setFont({ui->label_9, GUIUtil::setFont({ui->label_9,
ui->labelNetwork, ui->labelNetwork,
ui->label_10, ui->label_10,

View File

@ -900,7 +900,7 @@ void SendCoinsDialog::coinControlFeatureChanged(bool checked)
// Coin Control: button inputs -> show actual coin control dialog // Coin Control: button inputs -> show actual coin control dialog
void SendCoinsDialog::coinControlButtonClicked() void SendCoinsDialog::coinControlButtonClicked()
{ {
CoinControlDialog dlg(platformStyle); CoinControlDialog dlg(platformStyle, this);
dlg.setModel(model); dlg.setModel(model);
dlg.exec(); dlg.exec();
coinControlUpdateLabels(); coinControlUpdateLabels();

View File

@ -18,8 +18,6 @@ TransactionDescDialog::TransactionDescDialog(const QModelIndex &idx, QWidget *pa
{ {
ui->setupUi(this); ui->setupUi(this);
setWindowTitle(tr("Details for %1").arg(idx.data(TransactionTableModel::TxHashRole).toString())); 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(); QString desc = idx.data(TransactionTableModel::LongDescriptionRole).toString();
ui->detailText->setHtml(desc); ui->detailText->setHtml(desc);
} }

View File

@ -556,7 +556,7 @@ void TransactionView::showDetails()
QModelIndexList selection = transactionView->selectionModel()->selectedRows(); QModelIndexList selection = transactionView->selectionModel()->selectedRows();
if(!selection.isEmpty()) if(!selection.isEmpty())
{ {
TransactionDescDialog *dlg = new TransactionDescDialog(selection.at(0)); TransactionDescDialog* dlg = new TransactionDescDialog(selection.at(0), this);
dlg->setAttribute(Qt::WA_DeleteOnClose); dlg->setAttribute(Qt::WA_DeleteOnClose);
dlg->show(); dlg->show();
} }

View File

@ -100,6 +100,7 @@ HelpMessageDialog::HelpMessageDialog(QWidget *parent, HelpMode helpMode) :
strUsage += HelpMessageOpt("-resetguisettings", tr("Reset all settings changed in the GUI").toStdString()); strUsage += HelpMessageOpt("-resetguisettings", tr("Reset all settings changed in the GUI").toStdString());
if (showDebug) { if (showDebug) {
strUsage += HelpMessageOpt("-uiplatform", strprintf("Select platform to customize UI for (one of windows, macosx, other; default: %s)", BitcoinGUI::DEFAULT_UIPLATFORM)); 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=<name>", _("Sets a window title which is appended to \"Dash Core - \"")); strUsage += HelpMessageOpt("-windowtitle=<name>", _("Sets a window title which is appended to \"Dash Core - \""));
@ -210,8 +211,7 @@ ShutdownWindow::ShutdownWindow(QWidget *parent, Qt::WindowFlags f):
{ {
setObjectName("ShutdownWindow"); setObjectName("ShutdownWindow");
/* Open CSS when configured */ GUIUtil::loadStyleSheet(this, false);
this->setStyleSheet(GUIUtil::loadStyleSheet());
QVBoxLayout *layout = new QVBoxLayout(); QVBoxLayout *layout = new QVBoxLayout();
layout->addWidget(new QLabel( layout->addWidget(new QLabel(

View File

@ -385,8 +385,7 @@ void WalletView::showProgress(const QString &title, int nProgress)
{ {
if (nProgress == 0) if (nProgress == 0)
{ {
progressDialog = new QProgressDialog(title, "", 0, 100); progressDialog = new QProgressDialog(title, "", 0, 100, this);
progressDialog->setStyleSheet(GUIUtil::loadStyleSheet());
progressDialog->setWindowModality(Qt::ApplicationModal); progressDialog->setWindowModality(Qt::ApplicationModal);
progressDialog->setMinimumDuration(0); progressDialog->setMinimumDuration(0);
progressDialog->setCancelButton(0); progressDialog->setCancelButton(0);