diff --git a/.gitignore b/.gitignore index 1aa74e7cea7e..f722641c4454 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ src/dash-tx src/test/test_dash src/test/test_dash_fuzzy src/qt/test/test_dash-qt +src/qt/res/css/colors/* src/bench/bench_dash # autoreconf diff --git a/contrib/dash-qt.pro b/contrib/dash-qt.pro index cde8c56930a8..708978d1d574 100644 --- a/contrib/dash-qt.pro +++ b/contrib/dash-qt.pro @@ -1,6 +1,7 @@ FORMS += \ ../src/qt/forms/aboutdialog.ui \ ../src/qt/forms/addressbookpage.ui \ + ../src/qt/forms/appearancewidget.ui \ ../src/qt/forms/askpassphrasedialog.ui \ ../src/qt/forms/coincontroldialog.ui \ ../src/qt/forms/debugwindow.ui \ diff --git a/contrib/devtools/update-css-files.py b/contrib/devtools/update-css-files.py new file mode 100755 index 000000000000..ceb48e00258e --- /dev/null +++ b/contrib/devtools/update-css-files.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python3 +# +# update-css-files.py creates color analyse files in css/colors and updates the +# `` section in all css files. +# +# Copyright (c) 2020 The Dash Core developers +# Distributed under the MIT/X11 software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +from pathlib import Path +import re +import subprocess +import sys + +MATCH_REPLACE = '.+?' +MATCH_COLORS = '#(?:[0-9a-fA-F]{2}){2,4}|#(?:[0-9a-f]{1}){3}' + +def error(msg): + exit('\nERROR: {}\n'.format(msg)) + +def parse_css(file_css): + # Temporarily + state = 0 + selectors = [] + + # Results + by_attribute = {} + by_color = {} + + for line in file_css.read_text().splitlines(): + + if line == '': + continue + + # start of a comment + if state == 0 and line.startswith('/*'): + if '*/' in line: + state = 0 + else: + state = 1 + # we are in a comment section + elif state == 1: + # end of the comment + if '*/' in line: + state = 0 + else: + continue + # first line of multiple selector + elif (state == 0 or state == 2) and ',' in line: + state = 2 + # first line of single selector or end of multiple + elif (state == 0 or state == 2) and '{' in line: + state = 3 + # end of element + elif state == 4 and line == '}': + state = 0 + + if state == 0 and len(selectors): + selectors = [] + + if state == 2: + selector = line.split(",")[0].strip(' ') + selectors.append(selector) + + if state == 3: + selector = line.split("{")[0].strip(' ') + selectors.append(selector) + state = 4 + continue + + if state == 4: + matched_colors = re.findall(MATCH_COLORS, line) + + if len(matched_colors) > 1: + error("Multiple colors in a line.\n\n {}\n\nSeems to be an invalid file!".format(line)) + elif len(matched_colors) == 1: + matched_color = matched_colors[0] + element = line.split(":")[0].strip(' ') + + if not matched_color in by_color: + by_color[matched_color] = [] + + by_color[matched_color].append(element) + + entry = element + " " + matched_color + + if not entry in by_attribute: + by_attribute[entry] = [] + + by_attribute[entry].extend(selectors) + + def sort_color(color): + tmp = color[0].replace('#', '0x') + return int(tmp, 0) + + def remove_duplicates(l): + no_duplicates = [] + [no_duplicates.append(i) for i in l if not no_duplicates.count(i)] + return no_duplicates + + colors = [] + + # sort colors just by hex value + if len(by_color): + colors = sorted(by_color.items(), key=lambda x: sort_color(x)) + + for k, l in by_attribute.items(): + by_attribute[k] = remove_duplicates(l) + + for k, l in by_color.items(): + by_color[k] = remove_duplicates(l) + + return {'fileName': file_css.stem, 'byAttribute': by_attribute, 'byColor': by_color, 'colors': colors} + + +def create_color_file(content, commit): + + str_result = "Color analyse of " +\ + content['fileName'] + ".css " + \ + "by " + \ + Path(__file__).name + \ + " for commit " + \ + commit + \ + "\n\n" + + if not len(content['colors']): + return None + + str_result += "# Used colors\n\n" + for c in content['colors']: + str_result += c[0] + '\n' + + str_result += "\n# Grouped by attribute\n" + + for k, v in content['byAttribute'].items(): + str_result += '\n' + k + '\n' + for val in v: + str_result += ' ' + val + '\n' + + str_result += "\n# Grouped by color\n" + + for k, v in content['byColor'].items(): + str_result += '\n' + k + '\n' + for val in v: + str_result += ' ' + val + '\n' + + return str_result + +if __name__ == '__main__': + + if len(sys.argv) > 1: + error('No argument required!') + + try: + css_folder_path = Path(__file__).parent.absolute() / Path('../../src/qt/res/css/') + css_folder_path = css_folder_path.resolve(strict=True) + except Exception: + error("Path doesn't exist: {}".format(css_folder_path)) + + if not len(list(css_folder_path.glob('*.css'))): + error("No .css files found in {}".format(css_folder_path)) + + results = [parse_css(x) for x in css_folder_path.glob('*.css') if x.is_file()] + + colors_folder_path = css_folder_path / Path('colors/') + if not colors_folder_path.is_dir(): + try: + colors_folder_path.mkdir() + except Exception: + error("Can't create new folder: {}".format(colors_folder_path)) + + commit = subprocess.check_output(['git', '-C', css_folder_path, 'rev-parse', '--short', 'HEAD']).decode("utf-8") + + for r in results: + + # Update the css file + css_file = css_folder_path / Path(r['fileName'] + '.css') + css_content = css_file.read_text() + to_replace = re.findall(MATCH_REPLACE, css_content, re.DOTALL) + + str_result = "\n# Used colors in {}.css for commit {}\n".format(r['fileName'], commit) + for c in r['colors']: + str_result += c[0] + '\n' + + str_replace = "\n{}\n".format(str_result) + css_content = css_content.replace(to_replace[0], str_replace) + css_file.write_text(css_content) + + # Write the _color.txt files + str_result = create_color_file(r, commit) + + if str_result is not None: + color_file = colors_folder_path / Path(r['fileName'] + '_css_colors.txt') + color_file.write_text(str_result) + + print('\n{}.css -> {} created!'.format(r['fileName'], color_file)) + else: + print('\n{}.css -> No colors found..'.format(r['fileName'] + ".css")) diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 287bb896e945..7d2f9055ba3e 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -32,6 +32,7 @@ QT_TS = \ QT_FORMS_UI = \ qt/forms/addressbookpage.ui \ + qt/forms/appearancewidget.ui \ qt/forms/askpassphrasedialog.ui \ qt/forms/coincontroldialog.ui \ qt/forms/editaddressdialog.ui \ @@ -54,6 +55,7 @@ QT_FORMS_UI = \ QT_MOC_CPP = \ qt/moc_addressbookpage.cpp \ qt/moc_addresstablemodel.cpp \ + qt/moc_appearancewidget.cpp \ qt/moc_askpassphrasedialog.cpp \ qt/moc_bantablemodel.cpp \ qt/moc_bitcoinaddressvalidator.cpp \ @@ -126,6 +128,7 @@ PROTOBUF_PROTO = qt/paymentrequest.proto BITCOIN_QT_H = \ qt/addressbookpage.h \ qt/addresstablemodel.h \ + qt/appearancewidget.h \ qt/askpassphrasedialog.h \ qt/bantablemodel.h \ qt/bitcoinaddressvalidator.h \ @@ -155,7 +158,6 @@ BITCOIN_QT_H = \ qt/paymentrequestplus.h \ qt/paymentserver.h \ qt/peertablemodel.h \ - qt/platformstyle.h \ qt/qrdialog.h \ qt/qvalidatedlineedit.h \ qt/qvaluecombobox.h \ @@ -238,6 +240,7 @@ RES_ICONS = \ qt/res/icons/network_disabled.png BITCOIN_QT_BASE_CPP = \ + qt/appearancewidget.cpp \ qt/bantablemodel.cpp \ qt/bitcoinaddressvalidator.cpp \ qt/bitcoinamountfield.cpp \ @@ -253,7 +256,6 @@ BITCOIN_QT_BASE_CPP = \ qt/optionsdialog.cpp \ qt/optionsmodel.cpp \ qt/peertablemodel.cpp \ - qt/platformstyle.cpp \ qt/qvalidatedlineedit.cpp \ qt/qvaluecombobox.cpp \ qt/rpcconsole.cpp \ @@ -352,8 +354,27 @@ RES_CSS = \ qt/res/css/dark.css \ qt/res/css/general.css \ qt/res/css/light.css \ - qt/res/css/scrollbars.css \ - qt/res/css/trad.css + qt/res/css/traditional.css + +RES_FONTS = \ + qt/res/fonts/Montserrat/Montserrat-Black.otf \ + qt/res/fonts/Montserrat/Montserrat-BlackItalic.otf \ + qt/res/fonts/Montserrat/Montserrat-Bold.otf \ + qt/res/fonts/Montserrat/Montserrat-BoldItalic.otf \ + qt/res/fonts/Montserrat/Montserrat-ExtraBold.otf \ + qt/res/fonts/Montserrat/Montserrat-ExtraBoldItalic.otf \ + qt/res/fonts/Montserrat/Montserrat-ExtraLight.otf \ + qt/res/fonts/Montserrat/Montserrat-ExtraLightItalic.otf \ + qt/res/fonts/Montserrat/Montserrat-Italic.otf \ + qt/res/fonts/Montserrat/Montserrat-Light.otf \ + qt/res/fonts/Montserrat/Montserrat-LightItalic.otf \ + qt/res/fonts/Montserrat/Montserrat-Medium.otf \ + qt/res/fonts/Montserrat/Montserrat-MediumItalic.otf \ + qt/res/fonts/Montserrat/Montserrat-Regular.otf \ + qt/res/fonts/Montserrat/Montserrat-SemiBold.otf \ + qt/res/fonts/Montserrat/Montserrat-SemiBoldItalic.otf \ + qt/res/fonts/Montserrat/Montserrat-Thin.otf \ + qt/res/fonts/Montserrat/Montserrat-ThinItalic.otf RES_MOVIES = $(wildcard $(srcdir)/qt/res/movies/spinner-*.png) @@ -367,7 +388,7 @@ qt_libdashqt_a_CXXFLAGS = $(AM_CXXFLAGS) $(QT_PIE_FLAGS) qt_libdashqt_a_OBJCXXFLAGS = $(AM_OBJCXXFLAGS) $(QT_PIE_FLAGS) qt_libdashqt_a_SOURCES = $(BITCOIN_QT_CPP) $(BITCOIN_QT_H) $(QT_FORMS_UI) \ - $(QT_QRC) $(QT_QRC_LOCALE) $(QT_TS) $(PROTOBUF_PROTO) $(RES_ICONS) $(RES_IMAGES) $(RES_CSS) $(RES_MOVIES) + $(QT_QRC) $(QT_QRC_LOCALE) $(QT_TS) $(PROTOBUF_PROTO) $(RES_ICONS) $(RES_IMAGES) $(RES_CSS) $(RES_FONTS) $(RES_MOVIES) nodist_qt_libdashqt_a_SOURCES = $(QT_MOC_CPP) $(QT_MOC) $(PROTOBUF_CC) \ $(PROTOBUF_H) $(QT_QRC_CPP) $(QT_QRC_LOCALE_CPP) @@ -430,12 +451,12 @@ $(QT_QRC_LOCALE_CPP): $(QT_QRC_LOCALE) $(QT_QM) $(SED) -e '/^\*\*.*Created:/d' -e '/^\*\*.*by:/d' > $@ @rm $(@D)/temp_$( $@ -CLEAN_QT = $(nodist_qt_libdashqt_a_SOURCES) $(QT_QM) $(QT_FORMS_H) qt/*.gcda qt/*.gcno qt/temp_dash_locale.qrc +CLEAN_QT = $(nodist_qt_libdashqt_a_SOURCES) $(QT_QM) $(QT_FORMS_H) qt/*.gcda qt/*.gcno qt/temp_dash_locale.qrc qt/res/css/colors/* CLEANFILES += $(CLEAN_QT) diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index b4c25b47145f..694897fb26c2 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include @@ -24,7 +23,7 @@ #include #include -AddressBookPage::AddressBookPage(const PlatformStyle *platformStyle, Mode _mode, Tabs _tab, QWidget *parent) : +AddressBookPage::AddressBookPage(Mode _mode, Tabs _tab, QWidget *parent) : QDialog(parent), ui(new Ui::AddressBookPage), model(0), @@ -32,17 +31,7 @@ AddressBookPage::AddressBookPage(const PlatformStyle *platformStyle, Mode _mode, tab(_tab) { ui->setupUi(this); - if (!platformStyle->getImagesOnButtons()) { - ui->newAddress->setIcon(QIcon()); - ui->copyAddress->setIcon(QIcon()); - ui->deleteAddress->setIcon(QIcon()); - ui->exportButton->setIcon(QIcon()); - } else { - ui->newAddress->setIcon(QIcon(":/icons/add")); - ui->copyAddress->setIcon(QIcon(":/icons/editcopy")); - ui->deleteAddress->setIcon(QIcon(":/icons/remove")); - ui->exportButton->setIcon(QIcon(":/icons/export")); - } + ui->showAddressQRCode->setIcon(QIcon()); switch(mode) @@ -106,6 +95,8 @@ AddressBookPage::AddressBookPage(const PlatformStyle *platformStyle, Mode _mode, connect(ui->tableView, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint))); connect(ui->closeButton, SIGNAL(clicked()), this, SLOT(accept())); + + GUIUtil::updateFonts(); } AddressBookPage::~AddressBookPage() diff --git a/src/qt/addressbookpage.h b/src/qt/addressbookpage.h index 93911a1ff8dd..6d3ccde763d8 100644 --- a/src/qt/addressbookpage.h +++ b/src/qt/addressbookpage.h @@ -8,7 +8,6 @@ #include class AddressTableModel; -class PlatformStyle; namespace Ui { class AddressBookPage; @@ -38,7 +37,7 @@ class AddressBookPage : public QDialog ForEditing /**< Open address book for editing */ }; - explicit AddressBookPage(const PlatformStyle *platformStyle, Mode mode, Tabs tab, QWidget *parent); + explicit AddressBookPage(Mode mode, Tabs tab, QWidget* parent); ~AddressBookPage(); void setModel(AddressTableModel *model); diff --git a/src/qt/appearancewidget.cpp b/src/qt/appearancewidget.cpp new file mode 100644 index 000000000000..4fe3ae5fcfef --- /dev/null +++ b/src/qt/appearancewidget.cpp @@ -0,0 +1,160 @@ +// Copyright (c) 2020 The Dash Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#if defined(HAVE_CONFIG_H) +#include +#endif + +#include + +#include +#include + +#include + +#include +#include +#include +#include + +AppearanceWidget::AppearanceWidget(QWidget* parent) : + QWidget(parent), + ui(new Ui::AppearanceWidget), + fAcceptChanges(false), + prevTheme(GUIUtil::getActiveTheme()), + prevFontFamily(GUIUtil::getFontFamily()), + prevScale(GUIUtil::getFontScale()), + prevWeightNormal(GUIUtil::getFontWeightNormal()), + prevWeightBold(GUIUtil::getFontWeightBold()) +{ + ui->setupUi(this); + + for (const QString& entry : GUIUtil::listThemes()) { + ui->theme->addItem(entry, QVariant(entry)); + } + + GUIUtil::FontFamily fontSystem = GUIUtil::FontFamily::SystemDefault; + GUIUtil::FontFamily fontMontserrat = GUIUtil::FontFamily::Montserrat; + + ui->fontFamily->addItem(GUIUtil::fontFamilyToString(fontSystem), QVariant(static_cast(fontSystem))); + ui->fontFamily->addItem(GUIUtil::fontFamilyToString(fontMontserrat), QVariant(static_cast(fontMontserrat))); + + updateWeightSlider(); + + mapper = new QDataWidgetMapper(this); + mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit); + mapper->setOrientation(Qt::Vertical); + + connect(ui->theme, SIGNAL(currentTextChanged(const QString&)), this, SLOT(updateTheme(const QString&))); + connect(ui->fontFamily, SIGNAL(activated(int)), this, SLOT(updateFontFamily(int))); + connect(ui->fontScaleSlider, SIGNAL(valueChanged(int)), this, SLOT(updateFontScale(int))); + connect(ui->fontWeightNormalSlider, SIGNAL(valueChanged(int)), this, SLOT(updateFontWeightNormal(int))); + connect(ui->fontWeightBoldSlider, SIGNAL(valueChanged(int)), this, SLOT(updateFontWeightBold(int))); +} + +AppearanceWidget::~AppearanceWidget() +{ + if (fAcceptChanges) { + mapper->submit(); + } else { + if (prevTheme != GUIUtil::getActiveTheme()) { + updateTheme(prevTheme); + } + if (prevFontFamily != GUIUtil::getFontFamily()) { + GUIUtil::setFontFamily(prevFontFamily); + } + if (prevScale != GUIUtil::getFontScale()) { + GUIUtil::setFontScale(prevScale); + } + if (prevWeightNormal != GUIUtil::getFontWeightNormal()) { + GUIUtil::setFontWeightNormal(prevWeightNormal); + } + if (prevWeightBold != GUIUtil::getFontWeightBold()) { + GUIUtil::setFontWeightBold(prevWeightBold); + } + } + delete ui; +} + +void AppearanceWidget::setModel(OptionsModel* _model) +{ + this->model = _model; + + if (_model) { + mapper->setModel(_model); + mapper->addMapping(ui->theme, OptionsModel::Theme); + mapper->addMapping(ui->fontFamily, OptionsModel::FontFamily); + mapper->addMapping(ui->fontScaleSlider, OptionsModel::FontScale); + mapper->addMapping(ui->fontWeightNormalSlider, OptionsModel::FontWeightNormal); + mapper->addMapping(ui->fontWeightBoldSlider, OptionsModel::FontWeightBold); + mapper->toFirst(); + } +} + +void AppearanceWidget::accept() +{ + fAcceptChanges = true; +} + +void AppearanceWidget::updateTheme(const QString& theme) +{ + QString newValue = theme.isEmpty() ? ui->theme->currentData().toString() : theme; + if (GUIUtil::getActiveTheme() != newValue) { + QSettings().setValue("theme", newValue); + // Force loading the theme + GUIUtil::loadTheme(nullptr, true); + } +} + +void AppearanceWidget::updateFontFamily(int index) +{ + GUIUtil::setFontFamily(static_cast(ui->fontFamily->itemData(index).toInt())); + updateWeightSlider(); +} + +void AppearanceWidget::updateFontScale(int nScale) +{ + GUIUtil::setFontScale(nScale); +} + +void AppearanceWidget::updateFontWeightNormal(int nValue, bool fForce) +{ + int nSliderValue = nValue; + if (nValue > ui->fontWeightBoldSlider->value() && !fForce) { + nSliderValue = ui->fontWeightBoldSlider->value(); + } + const QSignalBlocker blocker(ui->fontWeightNormalSlider); + ui->fontWeightNormalSlider->setValue(nSliderValue); + GUIUtil::setFontWeightNormal(GUIUtil::supportedWeightFromIndex(ui->fontWeightNormalSlider->value())); +} + +void AppearanceWidget::updateFontWeightBold(int nValue, bool fForce) +{ + int nSliderValue = nValue; + if (nValue < ui->fontWeightNormalSlider->value() && !fForce) { + nSliderValue = ui->fontWeightNormalSlider->value(); + } + const QSignalBlocker blocker(ui->fontWeightBoldSlider); + ui->fontWeightBoldSlider->setValue(nSliderValue); + GUIUtil::setFontWeightBold(GUIUtil::supportedWeightFromIndex(ui->fontWeightBoldSlider->value())); +} + +void AppearanceWidget::updateWeightSlider() +{ + int nMaximum = GUIUtil::getSupportedWeights().size() - 1; + + ui->fontWeightNormalSlider->setMinimum(0); + ui->fontWeightNormalSlider->setMaximum(nMaximum); + + ui->fontWeightBoldSlider->setMinimum(0); + ui->fontWeightBoldSlider->setMaximum(nMaximum); + + if (nMaximum < 4) { + updateFontWeightNormal(0, true); + updateFontWeightBold(nMaximum, true); + } else { + updateFontWeightNormal(1, true); + updateFontWeightBold(4, true); + } +} diff --git a/src/qt/appearancewidget.h b/src/qt/appearancewidget.h new file mode 100644 index 000000000000..33e3752d3825 --- /dev/null +++ b/src/qt/appearancewidget.h @@ -0,0 +1,56 @@ +// Copyright (c) 2020 The Dash Core developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_QT_APPEARANCEWIDGET_H +#define BITCOIN_QT_APPEARANCEWIDGET_H + +#include + +#include + +namespace Ui { +class AppearanceWidget; +} + +class OptionsModel; + +class QDataWidgetMapper; +class QSlider; +class QComboBox; + +class AppearanceWidget : public QWidget +{ + Q_OBJECT + +public: + explicit AppearanceWidget(QWidget* parent = 0); + ~AppearanceWidget(); + + void setModel(OptionsModel* model); + +public Q_SLOTS: + void accept(); + +private Q_SLOTS: + void updateTheme(const QString& toTheme = QString()); + void updateFontFamily(int index); + void updateFontScale(int nScale); + void updateFontWeightNormal(int nValue, bool fForce = false); + void updateFontWeightBold(int nValue, bool fForce = false); + +private: + Ui::AppearanceWidget* ui; + QDataWidgetMapper* mapper; + OptionsModel* model; + bool fAcceptChanges; + QString prevTheme; + int prevScale; + GUIUtil::FontFamily prevFontFamily; + QFont::Weight prevWeightNormal; + QFont::Weight prevWeightBold; + + void updateWeightSlider(); +}; + +#endif // BITCOIN_QT_APPEARANCEWIDGET_H diff --git a/src/qt/askpassphrasedialog.cpp b/src/qt/askpassphrasedialog.cpp index edad38b8e6a9..74066b8b0c7c 100644 --- a/src/qt/askpassphrasedialog.cpp +++ b/src/qt/askpassphrasedialog.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -28,6 +29,12 @@ AskPassphraseDialog::AskPassphraseDialog(Mode _mode, QWidget *parent) : { ui->setupUi(this); + GUIUtil::setFont({ui->capsLabel}, GUIUtil::FontWeight::Bold); + + GUIUtil::updateFonts(); + + GUIUtil::disableMacFocusRect(this); + ui->passEdit1->setMinimumSize(ui->passEdit1->sizeHint()); ui->passEdit2->setMinimumSize(ui->passEdit2->sizeHint()); ui->passEdit3->setMinimumSize(ui->passEdit3->sizeHint()); diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index ab582e925970..d04594c92d6e 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include @@ -14,71 +13,98 @@ #include #include -/** QSpinBox that uses fixed-point numbers internally and uses our own - * formatting/parsing functions. +/** + * Parse a string into a number of base monetary units and + * return validity. + * @note Must return 0 if !valid. */ -class AmountSpinBox: public QAbstractSpinBox +static CAmount parse(const QString &text, int nUnit, bool *valid_out=0) { - Q_OBJECT - -public: - explicit AmountSpinBox(QWidget *parent): - QAbstractSpinBox(parent), - currentUnit(BitcoinUnits::DASH), - singleStep(100000) // satoshis + CAmount val = 0; + bool valid = BitcoinUnits::parse(nUnit, text, &val); + if(valid) { - setAlignment(Qt::AlignRight); - - connect(lineEdit(), SIGNAL(textEdited(QString)), this, SIGNAL(valueChanged())); + if(val < 0 || val > BitcoinUnits::maxMoney()) + valid = false; } + if(valid_out) + *valid_out = valid; + return valid ? val : 0; +} + +/** Amount widget validator, checks for valid CAmount value. + */ +class AmountValidator : public QValidator +{ + Q_OBJECT + int currentUnit; +public: + explicit AmountValidator(QObject *parent) : + QValidator(parent), + currentUnit(BitcoinUnits::DASH) {} - QValidator::State validate(QString &text, int &pos) const + State validate(QString &input, int &pos) const { - if(text.isEmpty()) + if(input.isEmpty()) return QValidator::Intermediate; bool valid = false; - parse(text, &valid); + parse(input, currentUnit, &valid); /* Make sure we return Intermediate so that fixup() is called on defocus */ return valid ? QValidator::Intermediate : QValidator::Invalid; } - void fixup(QString &input) const + void updateUnit(int nUnit) + { + currentUnit = nUnit; + } +}; + +/** QLineEdit that uses fixed-point numbers internally and uses our own + * formatting/parsing functions. + */ +class AmountLineEdit: public QLineEdit +{ + Q_OBJECT + AmountValidator* amountValidator; +public: + explicit AmountLineEdit(QWidget *parent): + QLineEdit(parent), + currentUnit(BitcoinUnits::DASH) + { + setAlignment(Qt::AlignLeft); + amountValidator = new AmountValidator(this); + setValidator(amountValidator); + connect(this, SIGNAL(textEdited(QString)), this, SIGNAL(valueChanged())); + } + + void fixup(const QString &input) { bool valid = false; - CAmount val = parse(input, &valid); + CAmount val = parse(input, currentUnit, &valid); if(valid) { - input = BitcoinUnits::format(currentUnit, val, false, BitcoinUnits::separatorAlways); - lineEdit()->setText(input); + setText(BitcoinUnits::format(currentUnit, val, false, BitcoinUnits::separatorAlways)); } } CAmount value(bool *valid_out=0) const { - return parse(text(), valid_out); + return parse(text(), currentUnit, valid_out); } void setValue(const CAmount& value) { - lineEdit()->setText(BitcoinUnits::format(currentUnit, value, false, BitcoinUnits::separatorAlways)); + setText(BitcoinUnits::format(currentUnit, value, false, BitcoinUnits::separatorAlways)); Q_EMIT valueChanged(); } - void stepBy(int steps) - { - bool valid = false; - CAmount val = value(&valid); - val = val + steps * singleStep; - val = qMin(qMax(val, CAmount(0)), BitcoinUnits::maxMoney()); - setValue(val); - } - void setDisplayUnit(int unit) { bool valid = false; CAmount val = value(&valid); currentUnit = unit; + amountValidator->updateUnit(unit); if(valid) setValue(val); @@ -86,67 +112,19 @@ class AmountSpinBox: public QAbstractSpinBox clear(); } - void setSingleStep(const CAmount& step) - { - singleStep = step; - } - QSize minimumSizeHint() const { - if(cachedMinimumSizeHint.isEmpty()) - { - ensurePolished(); - - const QFontMetrics fm(fontMetrics()); - int h = lineEdit()->minimumSizeHint().height(); - int w = fm.width(BitcoinUnits::format(BitcoinUnits::DASH, BitcoinUnits::maxMoney(), false, BitcoinUnits::separatorAlways)); - w += 2; // cursor blinking space - - QStyleOptionSpinBox opt; - initStyleOption(&opt); - QSize hint(w, h); - QSize extra(35, 6); - opt.rect.setSize(hint + extra); - extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt, - QStyle::SC_SpinBoxEditField, this).size(); - // get closer to final result by repeating the calculation - opt.rect.setSize(hint + extra); - extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt, - QStyle::SC_SpinBoxEditField, this).size(); - hint += extra; - hint.setHeight(h); - - opt.rect = rect(); - - cachedMinimumSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this) - .expandedTo(QApplication::globalStrut()); - } - return cachedMinimumSizeHint; + ensurePolished(); + const QFontMetrics fm(fontMetrics()); + int h = 0; + int w = fm.width(BitcoinUnits::format(BitcoinUnits::DASH, BitcoinUnits::maxMoney(), false, BitcoinUnits::separatorAlways)); + w += 2; // cursor blinking space + w += GUIUtil::dashThemeActive() ? 24 : 0; // counteract padding from css + return QSize(w, h); } private: int currentUnit; - CAmount singleStep; - mutable QSize cachedMinimumSizeHint; - - /** - * Parse a string into a number of base monetary units and - * return validity. - * @note Must return 0 if !valid. - */ - CAmount parse(const QString &text, bool *valid_out=0) const - { - CAmount val = 0; - bool valid = BitcoinUnits::parse(currentUnit, text, &val); - if(valid) - { - if(val < 0 || val > BitcoinUnits::maxMoney()) - valid = false; - } - if(valid_out) - *valid_out = valid; - return valid ? val : 0; - } protected: bool event(QEvent *event) @@ -158,30 +136,18 @@ class AmountSpinBox: public QAbstractSpinBox { // Translate a comma into a period QKeyEvent periodKeyEvent(event->type(), Qt::Key_Period, keyEvent->modifiers(), ".", keyEvent->isAutoRepeat(), keyEvent->count()); - return QAbstractSpinBox::event(&periodKeyEvent); + return QLineEdit::event(&periodKeyEvent); + } + if(keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return) + { + clearFocus(); } } - return QAbstractSpinBox::event(event); - } - - StepEnabled stepEnabled() const - { - if (isReadOnly()) // Disable steps when AmountSpinBox is read-only - return StepNone; - if (text().isEmpty()) // Allow step-up with empty field - return StepUpEnabled; - - StepEnabled rv = 0; - bool valid = false; - CAmount val = value(&valid); - if(valid) + if (event->type() == QEvent::FocusOut) { - if(val > 0) - rv |= StepDownEnabled; - if(val < BitcoinUnits::maxMoney()) - rv |= StepUpEnabled; + fixup(text()); } - return rv; + return QLineEdit::event(event); } Q_SIGNALS: @@ -194,18 +160,17 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent) : QWidget(parent), amount(0) { - amount = new AmountSpinBox(this); + amount = new AmountLineEdit(this); amount->setLocale(QLocale::c()); amount->installEventFilter(this); - amount->setMaximumWidth(170); + amount->setMaximumWidth(300); + + units = new BitcoinUnits(this); QHBoxLayout *layout = new QHBoxLayout(this); + layout->setSpacing(0); + layout->setMargin(0); layout->addWidget(amount); - unit = new QValueComboBox(this); - unit->setModel(new BitcoinUnits(this)); - layout->addWidget(unit); - layout->addStretch(1); - layout->setContentsMargins(0,0,0,0); setLayout(layout); @@ -214,22 +179,16 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent) : // If one if the widgets changes, the combined content changes as well connect(amount, SIGNAL(valueChanged()), this, SIGNAL(valueChanged())); - connect(unit, SIGNAL(currentIndexChanged(int)), this, SLOT(unitChanged(int))); - - // Set default based on configuration - unitChanged(unit->currentIndex()); } void BitcoinAmountField::clear() { amount->clear(); - unit->setCurrentIndex(0); } void BitcoinAmountField::setEnabled(bool fEnabled) { amount->setEnabled(fEnabled); - unit->setEnabled(fEnabled); } bool BitcoinAmountField::validate() @@ -261,8 +220,7 @@ bool BitcoinAmountField::eventFilter(QObject *object, QEvent *event) QWidget *BitcoinAmountField::setupTabChain(QWidget *prev) { QWidget::setTabOrder(prev, amount); - QWidget::setTabOrder(amount, unit); - return unit; + return amount; } CAmount BitcoinAmountField::value(bool *valid_out) const @@ -283,20 +241,17 @@ void BitcoinAmountField::setReadOnly(bool fReadOnly) void BitcoinAmountField::unitChanged(int idx) { // Use description tooltip for current unit for the combobox - unit->setToolTip(unit->itemData(idx, Qt::ToolTipRole).toString()); + amount->setToolTip(units->data(idx, Qt::ToolTipRole).toString()); // Determine new unit ID - int newUnit = unit->itemData(idx, BitcoinUnits::UnitRole).toInt(); + int newUnit = units->data(idx, BitcoinUnits::UnitRole).toInt(); + + amount->setPlaceholderText(tr("Amount in ") + units->data(idx,Qt::DisplayRole).toString()); amount->setDisplayUnit(newUnit); } void BitcoinAmountField::setDisplayUnit(int newUnit) { - unit->setValue(newUnit); -} - -void BitcoinAmountField::setSingleStep(const CAmount& step) -{ - amount->setSingleStep(step); + unitChanged(newUnit); } diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h index 659ecb416ba7..8d7d44221327 100644 --- a/src/qt/bitcoinamountfield.h +++ b/src/qt/bitcoinamountfield.h @@ -7,9 +7,11 @@ #include +#include #include -class AmountSpinBox; +class AmountLineEdit; +class BitcoinUnits; QT_BEGIN_NAMESPACE class QValueComboBox; @@ -31,9 +33,6 @@ class BitcoinAmountField: public QWidget CAmount value(bool *value=0) const; void setValue(const CAmount& value); - /** Set single step in satoshis **/ - void setSingleStep(const CAmount& step); - /** Make read-only **/ void setReadOnly(bool fReadOnly); @@ -64,12 +63,10 @@ class BitcoinAmountField: public QWidget bool eventFilter(QObject *object, QEvent *event); private: - AmountSpinBox *amount; - QValueComboBox *unit; + AmountLineEdit *amount; + BitcoinUnits *units; -private Q_SLOTS: void unitChanged(int idx); - }; #endif // BITCOIN_QT_BITCOINAMOUNTFIELD_H diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index 1dcd16873840..4327e9aac253 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include @@ -40,6 +39,7 @@ #include #include +#include #include #include #include @@ -55,6 +55,7 @@ #include #include #include +#include #include #if QT_VERSION < 0x050000 @@ -78,7 +79,7 @@ const std::string BitcoinGUI::DEFAULT_UIPLATFORM = * collisions in the future with additional wallets */ const QString BitcoinGUI::DEFAULT_WALLET = "~Default"; -BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle *networkStyle, QWidget *parent) : +BitcoinGUI::BitcoinGUI(const NetworkStyle *networkStyle, QWidget *parent) : QMainWindow(parent), enableWallet(false), clientModel(0), @@ -124,13 +125,10 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle * rpcConsole(0), helpMessageDialog(0), modalOverlay(0), + tabGroup(0), prevBlocks(0), - spinnerFrame(0), - platformStyle(_platformStyle) + spinnerFrame(0) { - /* Open CSS when configured */ - this->setStyleSheet(GUIUtil::loadStyleSheet()); - QSettings settings; if (!restoreGeometry(settings.value("MainWindowGeometry").toByteArray())) { // Restore failed (perhaps missing setting), center the window @@ -160,13 +158,13 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle * setUnifiedTitleAndToolBarOnMac(true); #endif - rpcConsole = new RPCConsole(_platformStyle, 0); + rpcConsole = new RPCConsole(this); helpMessageDialog = new HelpMessageDialog(this, HelpMessageDialog::cmdline); #ifdef ENABLE_WALLET if(enableWallet) { /** Create wallet frame*/ - walletFrame = new WalletFrame(_platformStyle, this); + walletFrame = new WalletFrame(this); } else #endif // ENABLE_WALLET { @@ -205,7 +203,7 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle * QHBoxLayout *frameBlocksLayout = new QHBoxLayout(frameBlocks); frameBlocksLayout->setContentsMargins(3,0,3,0); frameBlocksLayout->setSpacing(3); - unitDisplayControl = new UnitDisplayStatusBarControl(platformStyle); + unitDisplayControl = new UnitDisplayStatusBarControl(); labelWalletEncryptionIcon = new QLabel(); labelWalletHDStatusIcon = new QLabel(); labelConnectionsIcon = new GUIUtil::ClickableLabel(); @@ -228,6 +226,7 @@ BitcoinGUI::BitcoinGUI(const PlatformStyle *_platformStyle, const NetworkStyle * // Progress bar and label for blocks download progressBarLabel = new QLabel(); progressBarLabel->setVisible(true); + progressBarLabel->setObjectName("lblStatusBarProgress"); progressBar = new GUIUtil::ProgressBar(); progressBar->setAlignment(Qt::AlignCenter); progressBar->setVisible(true); @@ -291,13 +290,15 @@ BitcoinGUI::~BitcoinGUI() #endif delete rpcConsole; + delete tabGroup; } void BitcoinGUI::createActions() { - QActionGroup *tabGroup = new QActionGroup(this); + tabGroup = new QButtonGroup(this); - overviewAction = new QAction(tr("&Overview"), this); + overviewAction = new QToolButton(this); + overviewAction->setText(tr("&Overview")); overviewAction->setStatusTip(tr("Show general overview of wallet")); overviewAction->setToolTip(overviewAction->statusTip()); overviewAction->setCheckable(true); @@ -306,9 +307,10 @@ void BitcoinGUI::createActions() #else overviewAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_1)); #endif - tabGroup->addAction(overviewAction); + tabGroup->addButton(overviewAction); - sendCoinsAction = new QAction(tr("&Send"), this); + sendCoinsAction = new QToolButton(this); + sendCoinsAction->setText(tr("&Send")); sendCoinsAction->setStatusTip(tr("Send coins to a Dash address")); sendCoinsAction->setToolTip(sendCoinsAction->statusTip()); sendCoinsAction->setCheckable(true); @@ -317,13 +319,14 @@ void BitcoinGUI::createActions() #else sendCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_2)); #endif - tabGroup->addAction(sendCoinsAction); + tabGroup->addButton(sendCoinsAction); - sendCoinsMenuAction = new QAction(QIcon(":/icons/send"), sendCoinsAction->text(), this); + sendCoinsMenuAction = new QAction(sendCoinsAction->text(), this); sendCoinsMenuAction->setStatusTip(sendCoinsAction->statusTip()); sendCoinsMenuAction->setToolTip(sendCoinsMenuAction->statusTip()); - privateSendCoinsAction = new QAction(tr("&PrivateSend"), this); + privateSendCoinsAction = new QToolButton(this); + privateSendCoinsAction->setText(tr("&PrivateSend")); privateSendCoinsAction->setStatusTip(tr("PrivateSend coins to a Dash address")); privateSendCoinsAction->setToolTip(privateSendCoinsAction->statusTip()); privateSendCoinsAction->setCheckable(true); @@ -332,13 +335,14 @@ void BitcoinGUI::createActions() #else privateSendCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_3)); #endif - tabGroup->addAction(privateSendCoinsAction); + tabGroup->addButton(privateSendCoinsAction); - privateSendCoinsMenuAction = new QAction(QIcon(":/icons/send"), privateSendCoinsAction->text(), this); + privateSendCoinsMenuAction = new QAction(privateSendCoinsAction->text(), this); privateSendCoinsMenuAction->setStatusTip(privateSendCoinsAction->statusTip()); privateSendCoinsMenuAction->setToolTip(privateSendCoinsMenuAction->statusTip()); - receiveCoinsAction = new QAction(tr("&Receive"), this); + receiveCoinsAction = new QToolButton(this); + receiveCoinsAction->setText(tr("&Receive")); receiveCoinsAction->setStatusTip(tr("Request payments (generates QR codes and dash: URIs)")); receiveCoinsAction->setToolTip(receiveCoinsAction->statusTip()); receiveCoinsAction->setCheckable(true); @@ -347,13 +351,14 @@ void BitcoinGUI::createActions() #else receiveCoinsAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_4)); #endif - tabGroup->addAction(receiveCoinsAction); + tabGroup->addButton(receiveCoinsAction); - receiveCoinsMenuAction = new QAction(QIcon(":/icons/receiving_addresses"), receiveCoinsAction->text(), this); + receiveCoinsMenuAction = new QAction(receiveCoinsAction->text(), this); receiveCoinsMenuAction->setStatusTip(receiveCoinsAction->statusTip()); receiveCoinsMenuAction->setToolTip(receiveCoinsMenuAction->statusTip()); - historyAction = new QAction(tr("&Transactions"), this); + historyAction = new QToolButton(this); + historyAction->setText(tr("&Transactions")); historyAction->setStatusTip(tr("Browse transaction history")); historyAction->setToolTip(historyAction->statusTip()); historyAction->setCheckable(true); @@ -362,12 +367,13 @@ void BitcoinGUI::createActions() #else historyAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_5)); #endif - tabGroup->addAction(historyAction); + tabGroup->addButton(historyAction); #ifdef ENABLE_WALLET QSettings settings; if (settings.value("fShowMasternodesTab").toBool()) { - masternodeAction = new QAction(tr("&Masternodes"), this); + masternodeAction = new QToolButton(this); + masternodeAction->setText(tr("&Masternodes")); masternodeAction->setStatusTip(tr("Browse masternodes")); masternodeAction->setToolTip(masternodeAction->statusTip()); masternodeAction->setCheckable(true); @@ -376,79 +382,85 @@ void BitcoinGUI::createActions() #else masternodeAction->setShortcut(QKeySequence(Qt::ALT + Qt::Key_6)); #endif - tabGroup->addAction(masternodeAction); - connect(masternodeAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); - connect(masternodeAction, SIGNAL(triggered()), this, SLOT(gotoMasternodePage())); + tabGroup->addButton(masternodeAction); + connect(masternodeAction, SIGNAL(clicked()), this, SLOT(showNormalIfMinimized())); + connect(masternodeAction, SIGNAL(clicked()), this, SLOT(gotoMasternodePage())); } // These showNormalIfMinimized are needed because Send Coins and Receive Coins // can be triggered from the tray menu, and need to show the GUI to be useful. - connect(overviewAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); - connect(overviewAction, SIGNAL(triggered()), this, SLOT(gotoOverviewPage())); - connect(sendCoinsAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); - connect(sendCoinsAction, SIGNAL(triggered()), this, SLOT(gotoSendCoinsPage())); + connect(overviewAction, SIGNAL(clicked()), this, SLOT(showNormalIfMinimized())); + connect(overviewAction, SIGNAL(clicked()), this, SLOT(gotoOverviewPage())); + connect(sendCoinsAction, SIGNAL(clicked()), this, SLOT(showNormalIfMinimized())); + connect(sendCoinsAction, SIGNAL(clicked()), this, SLOT(gotoSendCoinsPage())); connect(sendCoinsMenuAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); connect(sendCoinsMenuAction, SIGNAL(triggered()), this, SLOT(gotoSendCoinsPage())); - connect(privateSendCoinsAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); - connect(privateSendCoinsAction, SIGNAL(triggered()), this, SLOT(gotoPrivateSendCoinsPage())); + connect(privateSendCoinsAction, SIGNAL(clicked()), this, SLOT(showNormalIfMinimized())); + connect(privateSendCoinsAction, SIGNAL(clicked()), this, SLOT(gotoPrivateSendCoinsPage())); connect(privateSendCoinsMenuAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); connect(privateSendCoinsMenuAction, SIGNAL(triggered()), this, SLOT(gotoPrivateSendCoinsPage())); - connect(receiveCoinsAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); - connect(receiveCoinsAction, SIGNAL(triggered()), this, SLOT(gotoReceiveCoinsPage())); + connect(receiveCoinsAction, SIGNAL(clicked()), this, SLOT(showNormalIfMinimized())); + connect(receiveCoinsAction, SIGNAL(clicked()), this, SLOT(gotoReceiveCoinsPage())); connect(receiveCoinsMenuAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); connect(receiveCoinsMenuAction, SIGNAL(triggered()), this, SLOT(gotoReceiveCoinsPage())); - connect(historyAction, SIGNAL(triggered()), this, SLOT(showNormalIfMinimized())); - connect(historyAction, SIGNAL(triggered()), this, SLOT(gotoHistoryPage())); + connect(historyAction, SIGNAL(clicked()), this, SLOT(showNormalIfMinimized())); + connect(historyAction, SIGNAL(clicked()), this, SLOT(gotoHistoryPage())); + + for (auto button : tabGroup->buttons()) { + GUIUtil::setFont({button}, GUIUtil::FontWeight::Normal, 16); + } + // Give the selected tab button a bolder font. + connect(tabGroup, SIGNAL(buttonToggled(QAbstractButton *, bool)), this, SLOT(highlightTabButton(QAbstractButton *, bool))); #endif // ENABLE_WALLET - quitAction = new QAction(QIcon(":/icons/quit"), tr("E&xit"), this); + quitAction = new QAction(tr("E&xit"), this); quitAction->setStatusTip(tr("Quit application")); quitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q)); quitAction->setMenuRole(QAction::QuitRole); - aboutAction = new QAction(QIcon(":/icons/about"), tr("&About %1").arg(tr(PACKAGE_NAME)), this); + aboutAction = new QAction(tr("&About %1").arg(tr(PACKAGE_NAME)), this); aboutAction->setStatusTip(tr("Show information about Dash Core")); aboutAction->setMenuRole(QAction::AboutRole); aboutAction->setEnabled(false); - aboutQtAction = new QAction(QIcon(":/icons/about_qt"), tr("About &Qt"), this); + aboutQtAction = new QAction(tr("About &Qt"), this); aboutQtAction->setStatusTip(tr("Show information about Qt")); aboutQtAction->setMenuRole(QAction::AboutQtRole); - optionsAction = new QAction(QIcon(":/icons/options"), tr("&Options..."), this); + optionsAction = new QAction(tr("&Options..."), this); optionsAction->setStatusTip(tr("Modify configuration options for %1").arg(tr(PACKAGE_NAME))); optionsAction->setMenuRole(QAction::PreferencesRole); optionsAction->setEnabled(false); - toggleHideAction = new QAction(QIcon(":/icons/about"), tr("&Show / Hide"), this); + toggleHideAction = new QAction(tr("&Show / Hide"), this); toggleHideAction->setStatusTip(tr("Show or hide the main Window")); - encryptWalletAction = new QAction(QIcon(":/icons/lock_closed"), tr("&Encrypt Wallet..."), this); + encryptWalletAction = new QAction(tr("&Encrypt Wallet..."), this); encryptWalletAction->setStatusTip(tr("Encrypt the private keys that belong to your wallet")); encryptWalletAction->setCheckable(true); - backupWalletAction = new QAction(QIcon(":/icons/filesave"), tr("&Backup Wallet..."), this); + backupWalletAction = new QAction(tr("&Backup Wallet..."), this); backupWalletAction->setStatusTip(tr("Backup wallet to another location")); - changePassphraseAction = new QAction(QIcon(":/icons/key"), tr("&Change Passphrase..."), this); + changePassphraseAction = new QAction(tr("&Change Passphrase..."), this); changePassphraseAction->setStatusTip(tr("Change the passphrase used for wallet encryption")); unlockWalletAction = new QAction(tr("&Unlock Wallet..."), this); unlockWalletAction->setToolTip(tr("Unlock wallet")); lockWalletAction = new QAction(tr("&Lock Wallet"), this); - signMessageAction = new QAction(QIcon(":/icons/edit"), tr("Sign &message..."), this); + signMessageAction = new QAction(tr("Sign &message..."), this); signMessageAction->setStatusTip(tr("Sign messages with your Dash addresses to prove you own them")); - verifyMessageAction = new QAction(QIcon(":/icons/transaction_0"), tr("&Verify message..."), this); + verifyMessageAction = new QAction(tr("&Verify message..."), this); verifyMessageAction->setStatusTip(tr("Verify messages to ensure they were signed with specified Dash addresses")); - openInfoAction = new QAction(QApplication::style()->standardIcon(QStyle::SP_MessageBoxInformation), tr("&Information"), this); + openInfoAction = new QAction(tr("&Information"), this); openInfoAction->setStatusTip(tr("Show diagnostic information")); - openRPCConsoleAction = new QAction(QIcon(":/icons/debugwindow"), tr("&Debug console"), this); + openRPCConsoleAction = new QAction(tr("&Debug console"), this); openRPCConsoleAction->setStatusTip(tr("Open debugging console")); - openGraphAction = new QAction(QIcon(":/icons/connect_4"), tr("&Network Monitor"), this); + openGraphAction = new QAction(tr("&Network Monitor"), this); openGraphAction->setStatusTip(tr("Show network monitor")); - openPeersAction = new QAction(QIcon(":/icons/connect_4"), tr("&Peers list"), this); + openPeersAction = new QAction(tr("&Peers list"), this); openPeersAction->setStatusTip(tr("Show peers info")); - openRepairAction = new QAction(QIcon(":/icons/options"), tr("Wallet &Repair"), this); + openRepairAction = new QAction(tr("Wallet &Repair"), this); openRepairAction->setStatusTip(tr("Show wallet repair options")); - openConfEditorAction = new QAction(QIcon(":/icons/edit"), tr("Open Wallet &Configuration File"), this); + openConfEditorAction = new QAction(tr("Open Wallet &Configuration File"), this); openConfEditorAction->setStatusTip(tr("Open configuration file")); // override TextHeuristicRole set by default which confuses this action with application settings openConfEditorAction->setMenuRole(QAction::NoRole); - showBackupsAction = new QAction(QIcon(":/icons/browse"), tr("Show Automatic &Backups"), this); + showBackupsAction = new QAction(tr("Show Automatic &Backups"), this); showBackupsAction->setStatusTip(tr("Show automatically created wallet backups")); // initially disable the debug window menu items openInfoAction->setEnabled(false); @@ -457,19 +469,19 @@ void BitcoinGUI::createActions() openPeersAction->setEnabled(false); openRepairAction->setEnabled(false); - usedSendingAddressesAction = new QAction(QIcon(":/icons/address-book"), tr("&Sending addresses..."), this); + usedSendingAddressesAction = new QAction(tr("&Sending addresses..."), this); usedSendingAddressesAction->setStatusTip(tr("Show the list of used sending addresses and labels")); - usedReceivingAddressesAction = new QAction(QIcon(":/icons/address-book"), tr("&Receiving addresses..."), this); + usedReceivingAddressesAction = new QAction(tr("&Receiving addresses..."), this); usedReceivingAddressesAction->setStatusTip(tr("Show the list of used receiving addresses and labels")); - openAction = new QAction(QApplication::style()->standardIcon(QStyle::SP_DirOpenIcon), tr("Open &URI..."), this); + openAction = new QAction(tr("Open &URI..."), this); openAction->setStatusTip(tr("Open a dash: URI or payment request")); - showHelpMessageAction = new QAction(QApplication::style()->standardIcon(QStyle::SP_MessageBoxInformation), tr("&Command-line options"), this); + showHelpMessageAction = new QAction(tr("&Command-line options"), this); showHelpMessageAction->setMenuRole(QAction::NoRole); showHelpMessageAction->setStatusTip(tr("Show the %1 help message to get a list with possible Dash command-line options").arg(tr(PACKAGE_NAME))); - showPrivateSendHelpAction = new QAction(QApplication::style()->standardIcon(QStyle::SP_MessageBoxInformation), tr("&PrivateSend information"), this); + showPrivateSendHelpAction = new QAction(tr("&PrivateSend information"), this); showPrivateSendHelpAction->setMenuRole(QAction::NoRole); showPrivateSendHelpAction->setStatusTip(tr("Show the PrivateSend basic information")); @@ -587,32 +599,33 @@ void BitcoinGUI::createToolBars() { QToolBar *toolbar = new QToolBar(tr("Tabs toolbar")); toolbar->setContextMenuPolicy(Qt::PreventContextMenu); - toolbar->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - toolbar->addAction(overviewAction); - toolbar->addAction(sendCoinsAction); - toolbar->addAction(privateSendCoinsAction); - toolbar->addAction(receiveCoinsAction); - toolbar->addAction(historyAction); + toolbar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + toolbar->setToolButtonStyle(Qt::ToolButtonTextOnly); + + overviewAction->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + sendCoinsAction->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + privateSendCoinsAction->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + receiveCoinsAction->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + historyAction->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + + toolbar->addWidget(overviewAction); + toolbar->addWidget(sendCoinsAction); + toolbar->addWidget(privateSendCoinsAction); + toolbar->addWidget(receiveCoinsAction); + toolbar->addWidget(historyAction); + QSettings settings; if (settings.value("fShowMasternodesTab").toBool() && masternodeAction) { - toolbar->addAction(masternodeAction); + masternodeAction->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + toolbar->addWidget(masternodeAction); } toolbar->setMovable(false); // remove unused icon in upper left corner overviewAction->setChecked(true); - // Add Dash logo on the right side - QWidget* spacer = new QWidget(); - spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - toolbar->addWidget(spacer); - QLabel *logoLabel = new QLabel(); - QString logoImage = ":/images/dash_logo_toolbar"; - if (!GUIUtil::dashThemeActive()) { - logoImage = ":/images/dash_logo_toolbar_blue"; - } + logoLabel->setObjectName("lblToolbarLogo"); + logoLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); - QPixmap logoPixmap(logoImage); - logoLabel->setPixmap(logoPixmap); toolbar->addWidget(logoLabel); /** Create additional container for toolbar and walletFrame and make it the central widget. @@ -687,13 +700,13 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel) } #endif // ENABLE_WALLET unitDisplayControl->setOptionsModel(_clientModel->getOptionsModel()); - + OptionsModel* optionsModel = _clientModel->getOptionsModel(); if(optionsModel) { // be aware of the tray icon disable state change reported by the OptionsModel object. connect(optionsModel,SIGNAL(hideTrayIconChanged(bool)),this,SLOT(setTrayIconVisible(bool))); - + // initialize the disable state of the tray icon with the current value in the model. setTrayIconVisible(optionsModel->getHideTrayIcon()); } @@ -921,6 +934,11 @@ void BitcoinGUI::openClicked() } } +void BitcoinGUI::highlightTabButton(QAbstractButton *button, bool checked) +{ + GUIUtil::setFont({button}, checked ? GUIUtil::FontWeight::Bold : GUIUtil::FontWeight::Normal, 16); +} + void BitcoinGUI::gotoOverviewPage() { overviewAction->setChecked(true); @@ -991,7 +1009,7 @@ void BitcoinGUI::updateNetworkState() icon = ":/icons/network_disabled"; } - labelConnectionsIcon->setPixmap(platformStyle->SingleColorIcon(icon).pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + labelConnectionsIcon->setPixmap(QIcon(icon).pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); } void BitcoinGUI::setNumConnections(int count) @@ -1107,7 +1125,7 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer tooltip = tr("Catching up...") + QString("
") + tooltip; if(count != prevBlocks) { - labelBlocksIcon->setPixmap(platformStyle->SingleColorIcon(QString( + labelBlocksIcon->setPixmap(QIcon(QString( ":/movies/spinner-%1").arg(spinnerFrame, 3, 10, QChar('0'))) .pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); spinnerFrame = (spinnerFrame + 1) % SPINNER_FRAMES; @@ -1166,7 +1184,7 @@ void BitcoinGUI::setAdditionalDataSyncProgress(double nSyncProgress) labelBlocksIcon->setPixmap(QIcon(":/icons/synced").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); } else { - labelBlocksIcon->setPixmap(platformStyle->SingleColorIcon(QString( + labelBlocksIcon->setPixmap(QIcon(QString( ":/movies/spinner-%1").arg(spinnerFrame, 3, 10, QChar('0'))) .pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); spinnerFrame = (spinnerFrame + 1) % SPINNER_FRAMES; @@ -1436,7 +1454,7 @@ bool BitcoinGUI::handlePaymentRequest(const SendCoinsRecipient& recipient) void BitcoinGUI::setHDStatus(int hdEnabled) { - labelWalletHDStatusIcon->setPixmap(platformStyle->SingleColorIcon(hdEnabled ? ":/icons/hd_enabled" : ":/icons/hd_disabled").pixmap(STATUSBAR_ICONSIZE,STATUSBAR_ICONSIZE)); + labelWalletHDStatusIcon->setPixmap(QIcon(hdEnabled ? ":/icons/hd_enabled" : ":/icons/hd_disabled").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); labelWalletHDStatusIcon->setToolTip(hdEnabled ? tr("HD key generation is enabled") : tr("HD key generation is disabled")); // eventually disable the QLabel to set its opacity to 50% @@ -1520,8 +1538,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); @@ -1599,7 +1616,7 @@ void BitcoinGUI::handleRestart(QStringList args) Q_EMIT requestedRestart(args); } -UnitDisplayStatusBarControl::UnitDisplayStatusBarControl(const PlatformStyle *platformStyle) : +UnitDisplayStatusBarControl::UnitDisplayStatusBarControl() : optionsModel(0), menu(0) { @@ -1607,7 +1624,7 @@ UnitDisplayStatusBarControl::UnitDisplayStatusBarControl(const PlatformStyle *pl setToolTip(tr("Unit to show amounts in. Click to select another unit.")); QList units = BitcoinUnits::availableUnits(); int max_width = 0; - const QFontMetrics fm(font()); + const QFontMetrics fm(GUIUtil::getFontNormal()); for (const BitcoinUnits::Unit unit : units) { max_width = qMax(max_width, fm.width(BitcoinUnits::name(unit))); diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index e927210773ff..4c0e3e6f0f6e 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -27,7 +27,6 @@ class ClientModel; class NetworkStyle; class Notificator; class OptionsModel; -class PlatformStyle; class RPCConsole; class SendCoinsRecipient; class UnitDisplayStatusBarControl; @@ -40,6 +39,7 @@ QT_BEGIN_NAMESPACE class QAction; class QProgressBar; class QProgressDialog; +class QToolButton; QT_END_NAMESPACE /** @@ -54,7 +54,7 @@ class BitcoinGUI : public QMainWindow static const QString DEFAULT_WALLET; static const std::string DEFAULT_UIPLATFORM; - explicit BitcoinGUI(const PlatformStyle *platformStyle, const NetworkStyle *networkStyle, QWidget *parent = 0); + explicit BitcoinGUI(const NetworkStyle* networkStyle, QWidget* parent = 0); ~BitcoinGUI(); /** Set the client model. @@ -95,20 +95,20 @@ class BitcoinGUI : public QMainWindow QProgressDialog *progressDialog; QMenuBar *appMenuBar; - QAction *overviewAction; - QAction *historyAction; - QAction *masternodeAction; + QToolButton *overviewAction; + QToolButton *historyAction; + QToolButton *masternodeAction; QAction *quitAction; - QAction *sendCoinsAction; + QToolButton *sendCoinsAction; QAction *sendCoinsMenuAction; - QAction *privateSendCoinsAction; + QToolButton *privateSendCoinsAction; QAction *privateSendCoinsMenuAction; QAction *usedSendingAddressesAction; QAction *usedReceivingAddressesAction; QAction *signMessageAction; QAction *verifyMessageAction; QAction *aboutAction; - QAction *receiveCoinsAction; + QToolButton *receiveCoinsAction; QAction *receiveCoinsMenuAction; QAction *optionsAction; QAction *toggleHideAction; @@ -136,6 +136,7 @@ class BitcoinGUI : public QMainWindow RPCConsole *rpcConsole; HelpMessageDialog *helpMessageDialog; ModalOverlay *modalOverlay; + QButtonGroup *tabGroup; #ifdef Q_OS_MAC CAppNapInhibitor* m_app_nap_inhibitor = nullptr; @@ -145,8 +146,6 @@ class BitcoinGUI : public QMainWindow int prevBlocks; int spinnerFrame; - const PlatformStyle *platformStyle; - struct IncomingTransactionMessage { QString date; int unit; @@ -251,6 +250,9 @@ private Q_SLOTS: /** Show open dialog */ void openClicked(); + + /** Highlight checked tab button */ + void highlightTabButton(QAbstractButton *button, bool checked); #endif // ENABLE_WALLET /** Show configuration dialog */ void optionsClicked(); @@ -308,7 +310,7 @@ class UnitDisplayStatusBarControl : public QLabel Q_OBJECT public: - explicit UnitDisplayStatusBarControl(const PlatformStyle *platformStyle); + explicit UnitDisplayStatusBarControl(); /** Lets the control know about the Options Model (and its signals) */ void setOptionsModel(OptionsModel *optionsModel); diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp index 54607806f1a9..b6c9e2fc57b6 100644 --- a/src/qt/bitcoinunits.cpp +++ b/src/qt/bitcoinunits.cpp @@ -246,7 +246,11 @@ int BitcoinUnits::rowCount(const QModelIndex &parent) const QVariant BitcoinUnits::data(const QModelIndex &index, int role) const { - int row = index.row(); + return data(index.row(), role); +} + +QVariant BitcoinUnits::data(const int &row, int role) const +{ if(row >= 0 && row < unitlist.size()) { Unit unit = unitlist.at(row); diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h index cc16cacc0229..54d132d98db5 100644 --- a/src/qt/bitcoinunits.h +++ b/src/qt/bitcoinunits.h @@ -111,6 +111,7 @@ class BitcoinUnits: public QAbstractListModel }; int rowCount(const QModelIndex &parent) const; QVariant data(const QModelIndex &index, int role) const; + QVariant data(const int &row, int role) const; ///@} static QString removeSpaces(QString text) diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 0b4b2e47c22c..9c545e1caf94 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include #include @@ -43,16 +42,25 @@ bool CCoinControlWidgetItem::operator<(const QTreeWidgetItem &other) const { return QTreeWidgetItem::operator<(other); } -CoinControlDialog::CoinControlDialog(const PlatformStyle *_platformStyle, QWidget *parent) : +CoinControlDialog::CoinControlDialog(QWidget* parent) : QDialog(parent), ui(new Ui::CoinControlDialog), - model(0), - platformStyle(_platformStyle) + model(0) { ui->setupUi(this); - /* Open CSS when configured */ - this->setStyleSheet(GUIUtil::loadStyleSheet()); + GUIUtil::setFont({ui->labelCoinControlQuantityText, + ui->labelCoinControlBytesText, + ui->labelCoinControlAmountText, + ui->labelCoinControlLowOutputText, + ui->labelCoinControlFeeText, + ui->labelCoinControlAfterFeeText, + ui->labelCoinControlChangeText + }, GUIUtil::FontWeight::Bold); + + GUIUtil::updateFonts(); + + GUIUtil::disableMacFocusRect(this); // context menu actions QAction *copyAddressAction = new QAction(tr("Copy address"), this); @@ -232,9 +240,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/coincontroldialog.h b/src/qt/coincontroldialog.h index ca87d97fc4f7..30c3b5bcfbd2 100644 --- a/src/qt/coincontroldialog.h +++ b/src/qt/coincontroldialog.h @@ -16,7 +16,6 @@ #include #include -class PlatformStyle; class WalletModel; class CCoinControl; @@ -43,7 +42,7 @@ class CoinControlDialog : public QDialog Q_OBJECT public: - explicit CoinControlDialog(const PlatformStyle *platformStyle, QWidget *parent = 0); + explicit CoinControlDialog(QWidget* parent = 0); ~CoinControlDialog(); void setModel(WalletModel *model); @@ -67,8 +66,6 @@ class CoinControlDialog : public QDialog QAction *lockAction; QAction *unlockAction; - const PlatformStyle *platformStyle; - void sortView(int, Qt::SortOrder); void updateView(); diff --git a/src/qt/dash.cpp b/src/qt/dash.cpp index 342a950b7a69..62ac18343522 100644 --- a/src/qt/dash.cpp +++ b/src/qt/dash.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -259,7 +258,6 @@ public Q_SLOTS: WalletModel *walletModel; #endif int returnValue; - const PlatformStyle *platformStyle; std::unique_ptr shutdownWindow; void startThread(); @@ -363,16 +361,6 @@ BitcoinApplication::BitcoinApplication(int &argc, char **argv): returnValue(0) { setQuitOnLastWindowClosed(false); - - // UI per-platform customization - // This must be done inside the BitcoinApplication constructor, or after it, because - // PlatformStyle::instantiate requires a QApplication - std::string platformName; - platformName = gArgs.GetArg("-uiplatform", BitcoinGUI::DEFAULT_UIPLATFORM); - platformStyle = PlatformStyle::instantiate(QString::fromStdString(platformName)); - if (!platformStyle) // Fall back to "other" if specified name not found - platformStyle = PlatformStyle::instantiate("other"); - assert(platformStyle); } BitcoinApplication::~BitcoinApplication() @@ -399,8 +387,6 @@ BitcoinApplication::~BitcoinApplication() } delete optionsModel; optionsModel = 0; - delete platformStyle; - platformStyle = 0; } #ifdef ENABLE_WALLET @@ -417,7 +403,9 @@ void BitcoinApplication::createOptionsModel(bool resetSettings) void BitcoinApplication::createWindow(const NetworkStyle *networkStyle) { - window = new BitcoinGUI(platformStyle, networkStyle, 0); + window = new BitcoinGUI(networkStyle, 0); + + GUIUtil::loadTheme(window); pollShutdownTimer = new QTimer(window); connect(pollShutdownTimer, SIGNAL(timeout()), window, SLOT(detectShutdown())); @@ -503,7 +491,7 @@ void BitcoinApplication::initializeResult(bool success) if(success) { // Log this only after AppInitMain finishes, as then logging setup is guaranteed complete - qWarning() << "Platform customization:" << platformStyle->getName(); + qWarning() << "Platform customization:" << gArgs.GetArg("-uiplatform", BitcoinGUI::DEFAULT_UIPLATFORM).c_str(); #ifdef ENABLE_WALLET PaymentServer::LoadRootCAs(); paymentServer->setOptionsModel(optionsModel); @@ -516,7 +504,7 @@ void BitcoinApplication::initializeResult(bool success) // TODO: Expose secondary wallets if (HasWallets()) { - walletModel = new WalletModel(platformStyle, GetWallets()[0], optionsModel); + walletModel = new WalletModel(GetWallets()[0], optionsModel); window->addWallet(BitcoinGUI::DEFAULT_WALLET, walletModel); window->setCurrentWallet(BitcoinGUI::DEFAULT_WALLET); @@ -537,6 +525,9 @@ void BitcoinApplication::initializeResult(bool success) } Q_EMIT splashFinished(window); + // Let the users setup their prefered appearance if there are no settings for it defined yet. + GUIUtil::setupAppearance(window, clientModel->getOptionsModel()); + #ifdef ENABLE_WALLET // Now that initialization/startup is done, process any command-line // dash: URIs or payment requests: @@ -727,6 +718,96 @@ int main(int argc, char *argv[]) app.parameterSetup(); // Load GUI settings from QSettings app.createOptionsModel(gArgs.GetBoolArg("-resetguisettings", false)); + // Load custom application fonts and setup font management + if (!GUIUtil::loadFonts()) { + QMessageBox::critical(0, QObject::tr(PACKAGE_NAME), + QObject::tr("Error: Failed to load application fonts.")); + return EXIT_FAILURE; + } + // Validate/set font family + if (gArgs.IsArgSet("-font-family")) { + GUIUtil::FontFamily family; + QString strFamily = gArgs.GetArg("-font-family", GUIUtil::fontFamilyToString(GUIUtil::getFontFamilyDefault()).toStdString()).c_str(); + try { + family = GUIUtil::fontFamilyFromString(strFamily); + } catch (const std::exception& e) { + QMessageBox::critical(0, QObject::tr(PACKAGE_NAME), + QObject::tr("Error: Specified font-family invalid. Valid values: %1.").arg("SystemDefault, Montserrat")); + return EXIT_FAILURE; + } + GUIUtil::setFontFamily(family); + } + // Validate/set normal font weight + if (gArgs.IsArgSet("-font-weight-normal")) { + QFont::Weight weight; + if (!GUIUtil::weightFromArg(gArgs.GetArg("-font-weight-normal", GUIUtil::weightToArg(GUIUtil::getFontWeightNormal())), weight)) { + QMessageBox::critical(0, QObject::tr(PACKAGE_NAME), + QObject::tr("Error: Specified font-weight-normal invalid. Valid range %1 to %2.").arg(0).arg(8)); + return EXIT_FAILURE; + } + GUIUtil::setFontWeightNormal(weight); + } + // Validate/set bold font weight + if (gArgs.IsArgSet("-font-weight-bold")) { + QFont::Weight weight; + if (!GUIUtil::weightFromArg(gArgs.GetArg("-font-weight-bold", GUIUtil::weightToArg(GUIUtil::getFontWeightBold())), weight)) { + QMessageBox::critical(0, QObject::tr(PACKAGE_NAME), + QObject::tr("Error: Specified font-weight-bold invalid. Valid range %1 to %2.").arg(0).arg(8)); + return EXIT_FAILURE; + } + GUIUtil::setFontWeightBold(weight); + } + // Validate/set font scale + if (gArgs.IsArgSet("-font-scale")) { + const int nScaleMin = -100, nScaleMax = 100; + int nScale = gArgs.GetArg("-font-scale", GUIUtil::getFontScale()); + if (nScale < nScaleMin || nScale > nScaleMax) { + QMessageBox::critical(0, QObject::tr(PACKAGE_NAME), + QObject::tr("Error: Specified font-scale invalid. Valid range %1 to %2.").arg(nScaleMin).arg(nScaleMax)); + return EXIT_FAILURE; + } + GUIUtil::setFontScale(nScale); + } + // Validate/set custom css directory + if (gArgs.IsArgSet("-custom-css-dir")) { + fs::path customDir = fs::path(gArgs.GetArg("-custom-css-dir", "")); + QString strCustomDir = QString::fromStdString(customDir.string()); + std::vector vecRequiredFiles = GUIUtil::listStyleSheets(); + QString strFile; + + if (!fs::is_directory(customDir)) { + QMessageBox::critical(0, QObject::tr(PACKAGE_NAME), + QObject::tr("Error: Invalid -custom-css-dir path.") + "\n\n" + strCustomDir); + return EXIT_FAILURE; + } + + for (auto itCustomDir = fs::directory_iterator(customDir); itCustomDir != fs::directory_iterator(); ++itCustomDir) { + if (fs::is_regular_file(*itCustomDir) && itCustomDir->path().extension() == ".css") { + strFile = QString::fromStdString(itCustomDir->path().filename().string()); + auto itFile = std::find(vecRequiredFiles.begin(), vecRequiredFiles.end(), strFile); + if (itFile != vecRequiredFiles.end()) { + vecRequiredFiles.erase(itFile); + } + } + } + + if (vecRequiredFiles.size()) { + QString strMissingFiles; + for (const auto& strMissingFile : vecRequiredFiles) { + strMissingFiles += strMissingFile + "\n"; + } + QMessageBox::critical(0, QObject::tr(PACKAGE_NAME), + QObject::tr("Error: %1 CSS file(s) missing in -custom-css-dir path.").arg(vecRequiredFiles.size()) + "\n\n" + strMissingFiles); + return EXIT_FAILURE; + } + + 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/dash.qrc b/src/qt/dash.qrc index b66c470d61ac..4877def6f61a 100644 --- a/src/qt/dash.qrc +++ b/src/qt/dash.qrc @@ -56,12 +56,29 @@ res/css/general.css - res/css/scrollbars.css - - res/css/dark.css res/css/light.css - res/css/trad.css + res/css/traditional.css + + + res/fonts/Montserrat/Montserrat-Black.otf + res/fonts/Montserrat/Montserrat-BlackItalic.otf + res/fonts/Montserrat/Montserrat-Bold.otf + res/fonts/Montserrat/Montserrat-BoldItalic.otf + res/fonts/Montserrat/Montserrat-ExtraBold.otf + res/fonts/Montserrat/Montserrat-ExtraBoldItalic.otf + res/fonts/Montserrat/Montserrat-ExtraLight.otf + res/fonts/Montserrat/Montserrat-ExtraLightItalic.otf + res/fonts/Montserrat/Montserrat-Italic.otf + res/fonts/Montserrat/Montserrat-Light.otf + res/fonts/Montserrat/Montserrat-LightItalic.otf + res/fonts/Montserrat/Montserrat-Medium.otf + res/fonts/Montserrat/Montserrat-MediumItalic.otf + res/fonts/Montserrat/Montserrat-Regular.otf + res/fonts/Montserrat/Montserrat-SemiBold.otf + res/fonts/Montserrat/Montserrat-SemiBoldItalic.otf + res/fonts/Montserrat/Montserrat-Thin.otf + res/fonts/Montserrat/Montserrat-ThinItalic.otf res/images/arrow_down_normal.png diff --git a/src/qt/editaddressdialog.cpp b/src/qt/editaddressdialog.cpp index dba5be2d3a0d..8763fae67cd1 100644 --- a/src/qt/editaddressdialog.cpp +++ b/src/qt/editaddressdialog.cpp @@ -21,6 +21,8 @@ EditAddressDialog::EditAddressDialog(Mode _mode, QWidget *parent) : { ui->setupUi(this); + GUIUtil::disableMacFocusRect(this); + GUIUtil::setupAddressWidget(ui->addressEdit, this); switch(mode) diff --git a/src/qt/forms/appearancewidget.ui b/src/qt/forms/appearancewidget.ui new file mode 100644 index 000000000000..d8d42ad007a6 --- /dev/null +++ b/src/qt/forms/appearancewidget.ui @@ -0,0 +1,486 @@ + + + AppearanceWidget + + + + 0 + 0 + 445 + 156 + + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QLayout::SetDefaultConstraint + + + 0 + + + + + QLayout::SetFixedSize + + + + + + 60 + 0 + + + + + 60 + 16777215 + + + + Lighter + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + 150 + 0 + + + + + 150 + 16777215 + + + + 0 + + + 5 + + + 0 + + + Qt::Horizontal + + + + + + + + 60 + 0 + + + + + 60 + 16777215 + + + + Bolder + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + 0 + 0 + + + + Font Weight Normal: + + + + + + + QLayout::SetFixedSize + + + + + + 60 + 0 + + + + + 60 + 16777215 + + + + Smaller + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + 150 + 0 + + + + + 150 + 16777215 + + + + -30 + + + 30 + + + 10 + + + 0 + + + Qt::Horizontal + + + QSlider::NoTicks + + + 3 + + + + + + + + 60 + 0 + + + + + 60 + 16777215 + + + + Bigger + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + 0 + 0 + + + + Font Scale: + + + + + + + QLayout::SetFixedSize + + + + + + 282 + 0 + + + + + 282 + 16777215 + + + + + + + + + + + 0 + 0 + + + + Font Family: + + + + + + + QLayout::SetFixedSize + + + + + + 282 + 0 + + + + + 282 + 16777215 + + + + + + + + + + + 0 + 0 + + + + Theme: + + + + + + + + 0 + 0 + + + + Font Weight Bold: + + + + + + + QLayout::SetFixedSize + + + + + + 60 + 0 + + + + + 60 + 16777215 + + + + Lighter + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + 150 + 0 + + + + + 150 + 16777215 + + + + 3 + + + 8 + + + 3 + + + Qt::Horizontal + + + + + + + + 60 + 0 + + + + + 60 + 16777215 + + + + Bolder + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + Qt::Horizontal + + + + 2 + 20 + + + + + + + + Qt::Horizontal + + + + 2 + 20 + + + + + + + + Qt::Horizontal + + + + 2 + 20 + + + + + + + + Qt::Horizontal + + + + 2 + 20 + + + + + + + + Qt::Horizontal + + + + 2 + 20 + + + + + + + + + + + diff --git a/src/qt/forms/askpassphrasedialog.ui b/src/qt/forms/askpassphrasedialog.ui index 69803989cd13..67fee3cd3dea 100644 --- a/src/qt/forms/askpassphrasedialog.ui +++ b/src/qt/forms/askpassphrasedialog.ui @@ -101,12 +101,6 @@ - - - 75 - true - - diff --git a/src/qt/forms/coincontroldialog.ui b/src/qt/forms/coincontroldialog.ui index e5cf02c62c41..7d226fd6a226 100644 --- a/src/qt/forms/coincontroldialog.ui +++ b/src/qt/forms/coincontroldialog.ui @@ -38,12 +38,6 @@ - - - 75 - true - - Quantity: @@ -67,12 +61,6 @@ - - - 75 - true - - Bytes: @@ -112,12 +100,6 @@ - - - 75 - true - - Amount: @@ -144,12 +126,6 @@ false - - - 75 - true - - Dust: @@ -192,12 +168,6 @@ - - - 75 - true - - Fee: @@ -237,12 +207,6 @@ - - - 75 - true - - After Fee: @@ -269,12 +233,6 @@ false - - - 75 - true - - Change: diff --git a/src/qt/forms/debugwindow.ui b/src/qt/forms/debugwindow.ui index ee3f74dbe159..9df65c113916 100644 --- a/src/qt/forms/debugwindow.ui +++ b/src/qt/forms/debugwindow.ui @@ -15,26 +15,83 @@ - + + + 0 + + + 0 + + + + + &Information + + + true + + + + + + + &Console + + + true + + + + + + + &Network Traffic + + + true + + + + + + + &Peers + + + true + + + + + + + &Wallet Repair + + + true + + + + + + + + + + 0 + 0 + + 0 - - - &Information - + 12 - - - 75 - true - - General @@ -165,17 +222,11 @@ - - - - 75 - true - - - - Network - - + + + Network + + @@ -248,12 +299,6 @@ - - - 75 - true - - Block chain @@ -307,12 +352,6 @@ - - - 75 - true - - Memory Pool @@ -442,10 +481,7 @@ - - - &Console - + 3 @@ -479,14 +515,14 @@ 24 - - - Decrease font size + + + - + :/icons/fontsmaller:/icons/fontsmaller @@ -518,7 +554,7 @@ - + :/icons/fontbigger:/icons/fontbigger @@ -553,7 +589,7 @@ - + :/icons/console_remove:/icons/console_remove @@ -608,7 +644,7 @@ - + :/icons/prompticon :/icons/prompticon:/icons/prompticon @@ -637,10 +673,7 @@ - - - &Network Traffic - + @@ -890,10 +923,7 @@ - - - &Peers - + @@ -936,11 +966,6 @@ 32 - - - 12 - - IBeamCursor @@ -996,13 +1021,6 @@ 32 - - - 10 - 75 - true - - IBeamCursor @@ -1464,10 +1482,7 @@ - - - &Wallet Repair - + @@ -1572,12 +1587,6 @@ 41 - - - 50 - false - - The buttons below will restart the wallet with command-line options to repair the wallet, fix issues with corrupt blockhain files or missing/obsolete transactions. @@ -1674,13 +1683,6 @@ 16 - - - 10 - 75 - true - - Wallet repair options. @@ -1746,8 +1748,6 @@ - - - + diff --git a/src/qt/forms/modaloverlay.ui b/src/qt/forms/modaloverlay.ui index 70743727de8e..4bf301e81a41 100644 --- a/src/qt/forms/modaloverlay.ui +++ b/src/qt/forms/modaloverlay.ui @@ -140,12 +140,6 @@ - - - 75 - true - - Attempting to spend Dash that are affected by not-yet-displayed transactions will not be accepted by the network. @@ -203,12 +197,6 @@ - - - 75 - true - - Number of blocks left @@ -223,12 +211,6 @@ - - - 75 - true - - Last block time @@ -249,12 +231,6 @@ - - - 75 - true - - Progress @@ -283,12 +259,6 @@ - - - 75 - true - - Progress increase per hour @@ -303,12 +273,6 @@ - - - 75 - true - - Estimated time left until synced diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui index 03d446e415cc..43f67725d869 100644 --- a/src/qt/forms/optionsdialog.ui +++ b/src/qt/forms/optionsdialog.ui @@ -6,10 +6,16 @@ 0 0 - 576 - 402 + 583 + 537 + + + 0 + 0 + + Options @@ -18,14 +24,87 @@ - - + + 0 - - - &Main - + + 0 + + + + + &Main + + + true + + + + + + + W&allet + + + true + + + + + + + &Network + + + true + + + + + + + &Window + + + true + + + + + + + &Display + + + true + + + + + + + &Appearance + + + true + + + + + + + + + + 0 + 0 + + + + 5 + + @@ -132,10 +211,7 @@ - - - W&allet - + @@ -300,10 +376,7 @@ - - - &Network - + @@ -618,10 +691,7 @@ - - - &Window - + @@ -668,10 +738,7 @@ - - - &Display - + @@ -726,20 +793,6 @@ https://www.transifex.com/projects/p/dash/ - - - - - - User Interface Theme: - - - - - - - - @@ -824,6 +877,26 @@ https://www.transifex.com/projects/p/dash/ + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + @@ -908,12 +981,6 @@ https://www.transifex.com/projects/p/dash/ 0 - - - 75 - true - - diff --git a/src/qt/forms/overviewpage.ui b/src/qt/forms/overviewpage.ui index af5626b88f56..b19e91a685c5 100644 --- a/src/qt/forms/overviewpage.ui +++ b/src/qt/forms/overviewpage.ui @@ -56,12 +56,6 @@ - - - 75 - true - - Balances @@ -102,12 +96,6 @@ - - - 75 - true - - IBeamCursor @@ -127,12 +115,6 @@ - - - 75 - true - - IBeamCursor @@ -152,12 +134,6 @@ - - - 75 - true - - IBeamCursor @@ -210,12 +186,6 @@ - - - 75 - true - - IBeamCursor @@ -255,12 +225,6 @@ - - - 75 - true - - IBeamCursor @@ -280,12 +244,6 @@ - - - 75 - true - - IBeamCursor @@ -322,12 +280,6 @@ - - - 75 - true - - IBeamCursor @@ -347,12 +299,6 @@ - - - 75 - true - - IBeamCursor @@ -408,12 +354,6 @@ - - - 75 - true - - PrivateSend @@ -501,12 +441,6 @@ - - - 75 - true - - 0 DASH @@ -541,6 +475,9 @@ n/a + + true + @@ -548,30 +485,63 @@ - - - (Last Message) - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - true + + + 0 - + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 0 + 0 + + + + Start/Stop Mixing + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + - - - - 0 - 0 - + + + Qt::Vertical - - Start/Stop Mixing + + + 20 + 40 + - + @@ -608,12 +578,6 @@ - - - 75 - true - - Recent transactions diff --git a/src/qt/forms/qrdialog.ui b/src/qt/forms/qrdialog.ui index 8366b7c857a6..dae494ccc825 100644 --- a/src/qt/forms/qrdialog.ui +++ b/src/qt/forms/qrdialog.ui @@ -13,12 +13,6 @@ - - - 75 - true - - QR-Code Title diff --git a/src/qt/forms/receivecoinsdialog.ui b/src/qt/forms/receivecoinsdialog.ui index 5ee764446afc..3293e9e6f48e 100644 --- a/src/qt/forms/receivecoinsdialog.ui +++ b/src/qt/forms/receivecoinsdialog.ui @@ -97,19 +97,6 @@ - - - - - 80 - 0 - - - - An optional amount to request. Leave this empty or zero to not request a specific amount. - - - @@ -166,6 +153,36 @@ + + + + 0 + + + 0 + + + + + An optional amount to request. Leave this empty or zero to not request a specific amount. + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + @@ -201,12 +218,6 @@ - - - 75 - true - - Requested payments history @@ -297,8 +308,6 @@ showRequestButton removeRequestButton - - - + diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui index 4f00c759ad94..432f3018f513 100644 --- a/src/qt/forms/sendcoinsdialog.ui +++ b/src/qt/forms/sendcoinsdialog.ui @@ -77,15 +77,6 @@ 0 - - - 75 - true - - - - font-weight:bold; - Coin Control Features @@ -126,15 +117,6 @@ - - - 75 - true - - - - font-weight:bold; - Insufficient funds! @@ -218,12 +200,6 @@ - - - 75 - true - - Quantity: @@ -253,12 +229,6 @@ - - - 75 - true - - Bytes: @@ -301,12 +271,6 @@ - - - 75 - true - - Amount: @@ -333,12 +297,6 @@ - - - 75 - true - - Dust: @@ -381,12 +339,6 @@ - - - 75 - true - - Fee: @@ -432,12 +384,6 @@ - - - 75 - true - - After Fee: @@ -464,12 +410,6 @@ - - - 75 - true - - Change: @@ -714,15 +654,6 @@ 0 - - - 75 - true - - - - font-weight:bold; - Transaction Fee: @@ -762,12 +693,6 @@ Using the fallbackfee can result in sending a transaction that will take several hours or days (or never) to confirm. Consider choosing your fee manually or wait until you have validated the complete chain. - - - 75 - true - - Note: Not enough data for fee estimation, using the fallback fee instead. @@ -853,8 +778,11 @@ 6 + + 3 + - + @@ -930,6 +858,12 @@ + + 20 + + + 3 + @@ -960,6 +894,9 @@ + + 30 + @@ -991,7 +928,10 @@ 6 - 2 + 0 + + + 3 @@ -1051,19 +991,6 @@ - - - - Qt::Vertical - - - - 1 - 1 - - - - @@ -1211,7 +1138,7 @@ 3 - + Balance: @@ -1220,7 +1147,7 @@ - + 0 0 diff --git a/src/qt/forms/signverifymessagedialog.ui b/src/qt/forms/signverifymessagedialog.ui index 90446e553dca..278c9c7733c0 100644 --- a/src/qt/forms/signverifymessagedialog.ui +++ b/src/qt/forms/signverifymessagedialog.ui @@ -18,14 +18,47 @@ - + + + 0 + + + 0 + + + + + &Sign Message + + + true + + + + + + + &Verify Message + + + true + + + + + + + + + + 0 + 0 + + 0 - - - &Sign Message - + @@ -110,11 +143,6 @@ - - - true - - true @@ -178,12 +206,6 @@ - - - 75 - true - - @@ -209,10 +231,7 @@ - - - &Verify Message - + @@ -309,12 +328,6 @@ - - - 75 - true - - diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 12af1fa506eb..9432057d6508 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -5,8 +5,11 @@ #include +#include #include +#include #include +#include #include #include @@ -16,6 +19,7 @@ #include #include