diff --git a/src/qt/appearancewidget.cpp b/src/qt/appearancewidget.cpp index 074069a8504c..ee439b71078f 100644 --- a/src/qt/appearancewidget.cpp +++ b/src/qt/appearancewidget.cpp @@ -15,9 +15,63 @@ #include #include +#include +#include #include #include +int setFontChoice(QComboBox* cb, const OptionsModel::FontChoice& fc) +{ + int i; + for (i = cb->count(); --i >= 0; ) { + QVariant item_data = cb->itemData(i); + if (!item_data.canConvert()) continue; + if (item_data.value() == fc) { + break; + } + } + if (i == -1) { + // New item needed + QFont chosen_font = OptionsModel::getFontForChoice(fc); + QSignalBlocker block_currentindexchanged_signal(cb); // avoid triggering QFontDialog + cb->insertItem(0, QFontInfo(chosen_font).family(), QVariant::fromValue(fc)); + i = 0; + } + + cb->setCurrentIndex(i); + return i; +} + +void setupFontOptions(QComboBox* cb) +{ + QFont embedded_font{GUIUtil::fixedPitchFont(true)}; + QFont system_font{GUIUtil::fixedPitchFont(false)}; + cb->addItem(QObject::tr("Default monospace font \"%1\"").arg(QFontInfo(system_font).family()), QVariant::fromValue(OptionsModel::FontChoice{OptionsModel::FontChoiceAbstract::BestSystemFont})); + cb->addItem(QObject::tr("Embedded \"%1\"").arg(QFontInfo(embedded_font).family()), QVariant::fromValue(OptionsModel::FontChoice{OptionsModel::FontChoiceAbstract::EmbeddedFont})); + cb->addItem(QObject::tr("Use existing font"), QVariant::fromValue(OptionsModel::FontChoice{OptionsModel::FontChoiceAbstract::ApplicationFont})); + cb->addItem(QObject::tr("Custom…")); + + const auto& on_font_choice_changed = [cb](int index) { + static int previous_index = -1; + QVariant item_data = cb->itemData(index); + if (item_data.canConvert()) { + // Valid predefined choice, nothing to do + } else { + // "Custom..." was selected, show font dialog + bool ok; + QFont f = QFontDialog::getFont(&ok, GUIUtil::fixedPitchFont(false), cb->parentWidget()); + if (!ok) { + cb->setCurrentIndex(previous_index); + return; + } + index = setFontChoice(cb, OptionsModel::FontChoice{f}); + } + previous_index = index; + }; + QObject::connect(cb, QOverload::of(&QComboBox::currentIndexChanged), on_font_choice_changed); + on_font_choice_changed(cb->currentIndex()); +} + AppearanceWidget::AppearanceWidget(QWidget* parent) : QWidget(parent), ui{new Ui::AppearanceWidget()}, @@ -34,7 +88,8 @@ AppearanceWidget::AppearanceWidget(QWidget* parent) : } for (size_t idx{0}; idx < GUIUtil::g_fonts_known.size(); idx++) { - ui->fontFamily->addItem(GUIUtil::g_fonts_known[idx], QVariant((uint16_t)idx)); + const auto& [font, selectable] = GUIUtil::g_fonts_known[idx]; + if (selectable) { ui->fontFamily->addItem(font, QVariant((uint16_t)idx)); } } updateWeightSlider(); @@ -43,17 +98,25 @@ AppearanceWidget::AppearanceWidget(QWidget* parent) : mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit); mapper->setOrientation(Qt::Vertical); - connect(ui->theme, &QComboBox::currentTextChanged, this, &AppearanceWidget::updateTheme); + setupFontOptions(ui->moneyFont); + connect(ui->fontFamily, QOverload::of(&QComboBox::currentIndexChanged), this, &AppearanceWidget::updateFontFamily); + connect(ui->fontFamily, QOverload::of(&QComboBox::currentIndexChanged), [this]() { Q_EMIT appearanceChanged(); }); + + connect(ui->fontScaleSlider, &QSlider::sliderReleased, [this]() { Q_EMIT appearanceChanged(); }); connect(ui->fontScaleSlider, &QSlider::valueChanged, this, &AppearanceWidget::updateFontScale); - connect(ui->fontWeightNormalSlider, &QSlider::valueChanged, [this](auto nValue) { updateFontWeightNormal(nValue); }); + + connect(ui->fontWeightBoldSlider, &QSlider::sliderReleased, [this]() { Q_EMIT appearanceChanged(); }); connect(ui->fontWeightBoldSlider, &QSlider::valueChanged, [this](auto nValue) { updateFontWeightBold(nValue); }); - connect(ui->theme, &QComboBox::currentTextChanged, [this]() { Q_EMIT appearanceChanged(); }); - connect(ui->fontFamily, &QComboBox::currentTextChanged, [this]() { Q_EMIT appearanceChanged(); }); - connect(ui->fontScaleSlider, &QSlider::sliderReleased, [this]() { Q_EMIT appearanceChanged(); }); connect(ui->fontWeightNormalSlider, &QSlider::sliderReleased, [this]() { Q_EMIT appearanceChanged(); }); - connect(ui->fontWeightBoldSlider, &QSlider::sliderReleased, [this]() { Q_EMIT appearanceChanged(); }); + connect(ui->fontWeightNormalSlider, &QSlider::valueChanged, [this](auto nValue) { updateFontWeightNormal(nValue); }); + + connect(ui->moneyFont, &QComboBox::currentTextChanged, [this]() { Q_EMIT appearanceChanged(); }); + connect(ui->moneyFont, QOverload::of(&QComboBox::currentIndexChanged), this, &AppearanceWidget::updateMoneyFont); + + connect(ui->theme, &QComboBox::currentTextChanged, this, &AppearanceWidget::updateTheme); + connect(ui->theme, &QComboBox::currentTextChanged, [this]() { Q_EMIT appearanceChanged(); }); } AppearanceWidget::~AppearanceWidget() @@ -78,6 +141,13 @@ AppearanceWidget::~AppearanceWidget() if (prevWeightBold != GUIUtil::g_font_registry.GetWeightBold()) { GUIUtil::g_font_registry.SetWeightBold(prevWeightBold); } + // Restore monospace font if cancelled + if (model) { + const auto& current_money_font = model->data(model->index(OptionsModel::FontForMoney, 0), Qt::EditRole).value(); + if (current_money_font != prevMoneyFont) { + model->setData(model->index(OptionsModel::FontForMoney, 0), QVariant::fromValue(prevMoneyFont)); + } + } GUIUtil::setApplicationFont(); GUIUtil::updateFonts(); } @@ -103,6 +173,10 @@ void AppearanceWidget::setModel(OptionsModel* _model) mapper->toFirst(); + const auto& font_for_money = _model->data(_model->index(OptionsModel::FontForMoney, 0), Qt::EditRole).value(); + prevMoneyFont = font_for_money; // Save original value for cancel + setFontChoice(ui->moneyFont, font_for_money); + const bool override_family{_model->isOptionOverridden("-font-family")}; if (override_family) { ui->fontFamily->setEnabled(false); @@ -138,6 +212,7 @@ void AppearanceWidget::setModel(OptionsModel* _model) void AppearanceWidget::accept() { fAcceptChanges = true; + // Note: FontForMoney is now updated immediately via updateMoneyFont() } void AppearanceWidget::updateTheme(const QString& theme) @@ -154,7 +229,7 @@ void AppearanceWidget::updateTheme(const QString& theme) void AppearanceWidget::updateFontFamily(int index) { - const bool setfont_ret{GUIUtil::g_font_registry.SetFont(GUIUtil::g_fonts_known[ui->fontFamily->itemData(index).toInt()])}; + const bool setfont_ret{GUIUtil::g_font_registry.SetFont(GUIUtil::g_fonts_known[ui->fontFamily->itemData(index).toInt()].first)}; assert(setfont_ret); GUIUtil::setApplicationFont(); GUIUtil::updateFonts(); @@ -193,6 +268,19 @@ void AppearanceWidget::updateFontWeightBold(int nValue, bool fForce) GUIUtil::updateFonts(); } +void AppearanceWidget::updateMoneyFont(int index) +{ + if (!model) { + return; + } + QVariant item_data = ui->moneyFont->itemData(index); + if (!item_data.canConvert()) { + return; + } + // Update the model immediately to trigger live preview in Overview page + model->setData(model->index(OptionsModel::FontForMoney, 0), item_data); +} + void AppearanceWidget::updateWeightSlider(const bool fForce) { int nMaximum = GUIUtil::g_font_registry.GetSupportedWeights().size() - 1; diff --git a/src/qt/appearancewidget.h b/src/qt/appearancewidget.h index d55c685d08ce..1929d0b6b33a 100644 --- a/src/qt/appearancewidget.h +++ b/src/qt/appearancewidget.h @@ -9,13 +9,12 @@ #include #include +#include namespace Ui { class AppearanceWidget; } -class OptionsModel; - class QDataWidgetMapper; class QSlider; class QComboBox; @@ -42,6 +41,7 @@ private Q_SLOTS: void updateFontScale(int nScale); void updateFontWeightNormal(int nValue, bool fForce = false); void updateFontWeightBold(int nValue, bool fForce = false); + void updateMoneyFont(int index); private: Ui::AppearanceWidget* ui; @@ -53,6 +53,7 @@ private Q_SLOTS: QString prevFontFamily; QFont::Weight prevWeightNormal; QFont::Weight prevWeightBold; + OptionsModel::FontChoice prevMoneyFont{OptionsModel::FontChoiceAbstract::ApplicationFont}; void updateWeightSlider(bool fForce = false); }; diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index ff001b7389c2..6f468642f74f 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -489,7 +489,7 @@ static void SetupUIArgs(ArgsManager& argsman) { argsman.AddArg("-choosedatadir", strprintf(QObject::tr("Choose data directory on startup (default: %u)").toStdString(), DEFAULT_CHOOSE_DATADIR), ArgsManager::ALLOW_ANY, OptionsCategory::GUI); argsman.AddArg("-custom-css-dir", "Set a directory which contains custom css files. Those will be used as stylesheets for the UI.", ArgsManager::ALLOW_ANY, OptionsCategory::GUI); - argsman.AddArg("-font-family", QObject::tr("Set the font family. Possible values: %1. (default: %2)").arg(Join(GUIUtil::g_fonts_known, ", ")).arg(GUIUtil::FontRegistry::DEFAULT_FONT).toStdString(), ArgsManager::ALLOW_ANY, OptionsCategory::GUI); + argsman.AddArg("-font-family", QObject::tr("Set the font family. Possible values: %1. (default: %2)").arg(Join(GUIUtil::getFonts(/*selectable_only=*/true), ", ")).arg(GUIUtil::FontRegistry::DEFAULT_FONT).toStdString(), ArgsManager::ALLOW_ANY, OptionsCategory::GUI); argsman.AddArg("-font-scale", QObject::tr("Set a scale factor which gets applied to the base font size. Possible range %1 (smallest fonts) to %2 (largest fonts). (default: %3)").arg(-100).arg(100).arg(GUIUtil::FontRegistry::DEFAULT_FONT_SCALE).toStdString(), ArgsManager::ALLOW_ANY, OptionsCategory::GUI); argsman.AddArg("-font-weight-bold", QObject::tr("Set the font weight for bold texts. Possible range %1 to %2 (default: %3)").arg(0).arg(8).arg(GUIUtil::weightToArg(GUIUtil::FontRegistry::TARGET_WEIGHT_BOLD)).toStdString(), ArgsManager::ALLOW_ANY, OptionsCategory::GUI); argsman.AddArg("-font-weight-normal", QObject::tr("Set the font weight for normal texts. Possible range %1 to %2 (default: %3)").arg(0).arg(8).arg(GUIUtil::weightToArg(GUIUtil::FontRegistry::TARGET_WEIGHT_NORMAL)).toStdString(), ArgsManager::ALLOW_ANY, OptionsCategory::GUI); @@ -673,7 +673,7 @@ int GuiMain(int argc, char* argv[]) // Validate/set font family if (gArgs.IsArgSet("-font-family")) { QString family = gArgs.GetArg("-font-family", GUIUtil::FontRegistry::DEFAULT_FONT.toUtf8().toStdString()).c_str(); - if (!GUIUtil::g_font_registry.RegisterFont(family) || !GUIUtil::g_font_registry.SetFont(family)) { + if (!GUIUtil::g_font_registry.RegisterFont(family, /*selectable=*/true) || !GUIUtil::g_font_registry.SetFont(family)) { QMessageBox::critical(nullptr, PACKAGE_NAME, QObject::tr("Error: Font \"%1\" could not be loaded.").arg(family)); return EXIT_FAILURE; } diff --git a/src/qt/forms/appearancewidget.ui b/src/qt/forms/appearancewidget.ui index d8d42ad007a6..955e94d21969 100644 --- a/src/qt/forms/appearancewidget.ui +++ b/src/qt/forms/appearancewidget.ui @@ -479,6 +479,49 @@ + + + + + + Font in the Overview tab: + + + moneyFont + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 282 + 0 + + + + + 282 + 16777215 + + + + + + diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index bbd2c7822416..611564f65da6 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -230,14 +230,6 @@ QString dateTimeStr(qint64 nTime) return dateTimeStr(QDateTime::fromSecsSinceEpoch((qint32)nTime)); } -QFont fixedPitchFont(bool use_embedded_font) -{ - if (use_embedded_font) { - return {"Roboto Mono"}; - } - return QFontDatabase::systemFont(QFontDatabase::FixedFont); -} - // Just some dummy data to generate a convincing random-looking (but consistent) address static const uint8_t dummydata[] = {0xeb,0x15,0x23,0x1d,0xfc,0xeb,0x60,0x92,0x58,0x86,0xb6,0x7d,0x06,0x52,0x99,0x92,0x59,0x15,0xae,0xb1,0x72,0xc0,0x66,0x47}; diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index b6a1e6becbd5..e4701677cc79 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -125,9 +125,6 @@ namespace GUIUtil QString dateTimeStr(const QDateTime &datetime); QString dateTimeStr(qint64 nTime); - // Return a monospace font - QFont fixedPitchFont(bool use_embedded_font = false); - // Set up widget for address void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent, bool fAllowURI = false); diff --git a/src/qt/guiutil_font.cpp b/src/qt/guiutil_font.cpp index c96517848926..7190a32fe5ec 100644 --- a/src/qt/guiutil_font.cpp +++ b/src/qt/guiutil_font.cpp @@ -116,10 +116,12 @@ QString qstrprintf(const std::string& fmt, const Args&... args) } // anonymous namespace namespace GUIUtil { -// Fonts known by the client -std::vector g_fonts_known{ - OS_FONT_STR.toUtf8(), - MONTSERRAT_FONT_STR.toUtf8(), +//! Fonts known by the client +std::vector> g_fonts_known{ + {MONTSERRAT_FONT_STR.toUtf8(), true}, + {OS_FONT_STR.toUtf8(), true}, + {OS_MONO_FONT_STR.toUtf8(), false}, + {ROBOTO_MONO_FONT_STR.toUtf8(), false}, }; FontRegistry g_font_registry; @@ -134,12 +136,15 @@ FontInfo::FontInfo(const QString& font_name) FontInfo::~FontInfo() = default; -bool FontRegistry::RegisterFont(const QString& font, bool skip_checks) +bool FontRegistry::RegisterFont(const QString& font, bool selectable, bool skip_checks) { - const bool font_known{std::find(g_fonts_known.begin(), g_fonts_known.end(), font) != g_fonts_known.end()}; + const auto font_strs{getFonts(/*selectable_only=*/false)}; + auto font_it{std::find(font_strs.begin(), font_strs.end(), font)}; if (m_weights.count(font)) { // Font's already registered - assert(font_known); + assert(font_it != font_strs.end()); + // Overwrite selectable flag + g_fonts_known.at(std::distance(font_strs.begin(), font_it)).second = selectable; return true; } if (!skip_checks) { @@ -150,8 +155,8 @@ bool FontRegistry::RegisterFont(const QString& font, bool skip_checks) } } m_weights.emplace(font, FontInfo(font)); - if (!font_known) { - g_fonts_known.push_back(font); + if (font_it == font_strs.end()) { + g_fonts_known.emplace_back(font, selectable); } return true; } @@ -184,7 +189,7 @@ int weightToArg(const QFont::Weight weight) void FontInfo::CalcSupportedWeights(const QString& font_name) { auto getTestWidth = [](const QString& font_name, QFont::Weight weight) -> int { - QFont font = getFont(font_name, {weight, FontRegistry::DEFAULT_FONT_SIZE, false}); + QFont font = getFont({font_name, weight, FontRegistry::DEFAULT_FONT_SIZE, false}); return TextWidth(QFontMetrics(font), ("Check the width of this text to see if the weight change has an impact!")); }; QFont::Weight prevWeight = vecWeightConsider.front(); @@ -238,6 +243,24 @@ void FontInfo::CalcDefaultWeights(const QString& font_name) } } +FontAttrib::FontAttrib(QString font, QFont::Weight weight, double point_size, bool is_italic) : + m_font{font}, + m_weight{weight}, + m_point_size{point_size}, + m_is_italic{is_italic} +{ +} + +FontAttrib::FontAttrib(QFont::Weight weight, double point_size, bool is_italic) : + m_font{g_font_registry.GetFont()}, + m_weight{weight}, + m_point_size{point_size}, + m_is_italic{is_italic} +{ +} + +FontAttrib::~FontAttrib() = default; + bool loadFonts() { // Before any font changes store the applications default font to use it as SystemDefault. @@ -290,8 +313,8 @@ bool loadFonts() } #endif // QT_NO_DEBUG - for (const auto& fonts : g_fonts_known) { - assert(g_font_registry.RegisterFont(fonts, /*skip_checks=*/true)); + for (const auto& [fonts, selectable] : g_fonts_known) { + assert(g_font_registry.RegisterFont(fonts, selectable, /*skip_checks=*/true)); } return true; @@ -402,7 +425,7 @@ void updateFonts() if (nSize == -1) { nSize = itDefault.first->second; } - font = getFont({it->second.m_weight, nSize, it->second.m_is_italic}); + font = getFont({it->second.m_font, it->second.m_weight, nSize, it->second.m_is_italic}); } else { font.setPointSizeF(g_font_registry.GetScaledFontSize(itDefault.first->second)); } @@ -439,17 +462,17 @@ void updateFonts() } } -QFont getFont(const QString& font_name, const FontAttrib& font_attrib) +QFont getFont(const FontAttrib& font_attrib) { QFont font; if (!fontsLoaded()) { return font; } - if (font_name == MONTSERRAT_FONT_STR) { + if (font_attrib.m_font == MONTSERRAT_FONT_STR) { assert(mapMontserrat.count(font_attrib.m_weight)); #ifdef Q_OS_MACOS - font.setFamily(font_name); + font.setFamily(font_attrib.m_font); font.setStyleName([&]() { std::string ret{mapMontserrat.at(font_attrib.m_weight).first}; if (font_attrib.m_is_italic) { @@ -463,19 +486,25 @@ QFont getFont(const QString& font_name, const FontAttrib& font_attrib) }()); #else if (font_attrib.m_weight == QFont::Normal || font_attrib.m_weight == QFont::Bold) { - font.setFamily(font_name); + font.setFamily(font_attrib.m_font); } else { - font.setFamily(font_name + QString{" "} + QString::fromStdString(mapMontserrat.at(font_attrib.m_weight).first)); + font.setFamily(qstrprintf("%s %s", font_attrib.m_font.toStdString(), mapMontserrat.at(font_attrib.m_weight).first)); } #endif // Q_OS_MACOS - } else if (font_name == OS_FONT_STR) { + } else if (font_attrib.m_font == OS_FONT_STR) { font.setFamily(g_default_font->family()); + } else if (font_attrib.m_font == OS_MONO_FONT_STR) { + font.setFamily(QFontDatabase::systemFont(QFontDatabase::FixedFont).family()); } else { - font.setFamily(font_name); + font.setFamily(font_attrib.m_font); + } + + if (font_attrib.m_font == ROBOTO_MONO_FONT_STR || font_attrib.m_font == OS_MONO_FONT_STR) { + font.setStyleHint(QFont::Monospace); } #ifdef Q_OS_MACOS - if (font_name != MONTSERRAT_FONT_STR) + if (font_attrib.m_font != MONTSERRAT_FONT_STR) #endif // Q_OS_MACOS { font.setWeight(font_attrib.m_weight); @@ -495,9 +524,13 @@ QFont getFont(const QString& font_name, const FontAttrib& font_attrib) return font; } -QFont getFont(const FontAttrib& font_attrib) +std::vector getFonts(bool selectable_only) { - return getFont(g_font_registry.GetFont(), font_attrib); + std::vector ret; + for (const auto& [font, selectable] : g_fonts_known) { + if (selectable || !selectable_only) { ret.emplace_back(font); } + } + return ret; } QFont getFontNormal() @@ -527,4 +560,12 @@ int FontRegistry::WeightToIdx(const QFont::Weight& weight) const } return -1; } + +QFont fixedPitchFont(bool use_embedded_font) +{ + return getFont({ + use_embedded_font ? ROBOTO_MONO_FONT_STR.toUtf8() : OS_MONO_FONT_STR.toUtf8(), + g_font_registry.GetWeightNormal() + }); +} } // namespace GUIUtil diff --git a/src/qt/guiutil_font.h b/src/qt/guiutil_font.h index 045149b9e9d0..f12333b67e93 100644 --- a/src/qt/guiutil_font.h +++ b/src/qt/guiutil_font.h @@ -19,18 +19,16 @@ namespace GUIUtil { // TODO: Switch to QUtf8StringView when we switch to Qt 6 constexpr QStringView MONTSERRAT_FONT_STR{u"Montserrat"}; constexpr QStringView OS_FONT_STR{u"SystemDefault"}; +constexpr QStringView OS_MONO_FONT_STR{u"SystemMonospace"}; +constexpr QStringView ROBOTO_MONO_FONT_STR{u"Roboto Mono"}; + +extern std::vector> g_fonts_known; enum class FontWeight : uint8_t { Normal, Bold, }; -struct FontAttrib { - QFont::Weight m_weight; - double m_point_size{-1}; - bool m_is_italic{false}; -}; - struct FontInfo { QFont::Weight m_bold; QFont::Weight m_bold_default; @@ -63,7 +61,7 @@ class FontRegistry { }; public: - [[nodiscard]] bool RegisterFont(const QString& font, bool skip_checks = false); + [[nodiscard]] bool RegisterFont(const QString& font, bool selectable, bool skip_checks = false); bool IsValidWeight(const QFont::Weight& weight) const { return WeightToIdx(weight) != -1; } int WeightToIdx(const QFont::Weight& weight) const; @@ -123,7 +121,17 @@ class FontRegistry { extern FontRegistry g_font_registry; -extern std::vector g_fonts_known; +struct FontAttrib { + QString m_font; + QFont::Weight m_weight; + double m_point_size{-1}; + bool m_is_italic{false}; + + FontAttrib(QString font, QFont::Weight weight, double point_size = -1, bool is_italic = false); + // cppcheck-suppress noExplicitConstructor + FontAttrib(QFont::Weight weight, double point_size = -1, bool is_italic = false); + ~FontAttrib(); +}; /** Convert weight value from args (0-8) to QFont::Weight */ bool weightFromArg(int nArg, QFont::Weight& weight); @@ -147,8 +155,10 @@ void setFont(const std::vector& vecWidgets, const FontAttrib& font_att GUIUtil::setFont */ void updateFonts(); +/** Get list of all selectable fonts */ +std::vector getFonts(bool selectable_only); + /** Get a properly weighted QFont object with the selected font. */ -QFont getFont(const QString& font_name, const FontAttrib& font_attrib); QFont getFont(const FontAttrib& font_attrib); /** Get the default normal QFont */ @@ -156,6 +166,9 @@ QFont getFontNormal(); /** Get the default bold QFont */ QFont getFontBold(); + +/** (Bitcoin) Return a monospace font */ +QFont fixedPitchFont(bool use_embedded_font = false); } // namespace GUIUtil #endif // BITCOIN_QT_GUIUTIL_FONT_H diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp index 4955ef47008d..ba84e8e75558 100644 --- a/src/qt/optionsmodel.cpp +++ b/src/qt/optionsmodel.cpp @@ -36,6 +36,44 @@ const char *DEFAULT_GUI_PROXY_HOST = "127.0.0.1"; static QString GetDefaultProxyAddress(); +static const QLatin1String fontchoice_str_app_font{"application_font"}; +static const QLatin1String fontchoice_str_embedded{"embedded"}; +static const QLatin1String fontchoice_str_best_system{"best_system"}; +static const QString fontchoice_str_custom_prefix{QStringLiteral("custom, ")}; + +QString OptionsModel::FontChoiceToString(const OptionsModel::FontChoice& f) +{ + if (std::holds_alternative(f)) { + switch (std::get(f)) { + case FontChoiceAbstract::ApplicationFont: + return fontchoice_str_app_font; + case FontChoiceAbstract::BestSystemFont: + return fontchoice_str_best_system; + case FontChoiceAbstract::EmbeddedFont: + return fontchoice_str_embedded; + } + assert(false); + } + return fontchoice_str_custom_prefix + std::get(f).toString(); +} + +OptionsModel::FontChoice OptionsModel::FontChoiceFromString(const QString& s) +{ + if (s == fontchoice_str_app_font) { + return FontChoiceAbstract::ApplicationFont; + } else if (s == fontchoice_str_best_system) { + return FontChoiceAbstract::BestSystemFont; + } else if (s == fontchoice_str_embedded) { + return FontChoiceAbstract::EmbeddedFont; + } else if (s.startsWith(fontchoice_str_custom_prefix)) { + QFont f; + f.fromString(s.mid(fontchoice_str_custom_prefix.size())); + return f; + } else { + return FontChoiceAbstract::ApplicationFont; // default + } +} + OptionsModel::OptionsModel(QObject *parent, bool resetSettings) : QAbstractListModel(parent) { @@ -106,7 +144,7 @@ void OptionsModel::Init(bool resetSettings) } if (GUIUtil::fontsLoaded()) { if (auto font_name = QString::fromStdString(gArgs.GetArg("-font-family", settings.value("fontFamily").toString().toStdString())); - GUIUtil::g_font_registry.RegisterFont(font_name) && GUIUtil::g_font_registry.SetFont(font_name)) { + GUIUtil::g_font_registry.RegisterFont(font_name, /*selectable=*/true) && GUIUtil::g_font_registry.SetFont(font_name)) { GUIUtil::setApplicationFont(); } } @@ -356,6 +394,17 @@ void OptionsModel::Init(bool resetSettings) addOverriddenOption("-lang"); language = settings.value("language").toString(); + + if (settings.contains("FontForMoney")) { + m_font_money = FontChoiceFromString(settings.value("FontForMoney").toString()); + } else if (settings.contains("UseEmbeddedMonospacedFont")) { + if (settings.value("UseEmbeddedMonospacedFont").toBool()) { + m_font_money = FontChoiceAbstract::EmbeddedFont; + } else { + m_font_money = FontChoiceAbstract::BestSystemFont; + } + } + Q_EMIT fontForMoneyChanged(getFontForMoney()); } /** Helper function to copy contents from one QSettings to another. @@ -472,6 +521,34 @@ void OptionsModel::SetPruneTargetGB(int prune_target_gb, bool force) SetPruneEnabled(prune, force); } +QFont OptionsModel::getFontForChoice(const FontChoice& fc) +{ + QFont f; + if (std::holds_alternative(fc)) { + switch (std::get(fc)) { + case FontChoiceAbstract::ApplicationFont: + f.setFamily(GUIUtil::g_font_registry.GetFont()); + break; + case FontChoiceAbstract::EmbeddedFont: + f = GUIUtil::fixedPitchFont(true); + break; + case FontChoiceAbstract::BestSystemFont: + f = GUIUtil::fixedPitchFont(false); + break; + } + } else { + f = std::get(fc); + } + // Note: Only the font family is actually used by callers. + // Weight and size are overridden in setMonospacedFont(). + return f; +} + +QFont OptionsModel::getFontForMoney() const +{ + return getFontForChoice(m_font_money); +} + // read QSettings values and return them QVariant OptionsModel::data(const QModelIndex & index, int role) const { @@ -585,6 +662,8 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const } case Language: return settings.value("language"); + case FontForMoney: + return QVariant::fromValue(m_font_money); #ifdef ENABLE_WALLET case CoinControlFeatures: return fCoinControlFeatures; @@ -839,6 +918,16 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in setRestartRequired(true); } break; + case FontForMoney: + { + const auto& new_font = value.value(); + if (m_font_money != new_font) { + settings.setValue("FontForMoney", FontChoiceToString(new_font)); + m_font_money = new_font; + } + Q_EMIT fontForMoneyChanged(getFontForMoney()); + break; + } #ifdef ENABLE_WALLET case CoinControlFeatures: fCoinControlFeatures = value.toBool(); diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index 4ccf27c5e4f8..1220c45ca192 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -11,8 +11,10 @@ #include #include +#include #include +#include namespace interfaces { class Node; @@ -66,6 +68,7 @@ class OptionsModel : public QAbstractListModel FontWeightNormal, // int FontWeightBold, // int Language, // QString + FontForMoney, // FontChoice CoinControlFeatures, // bool SubFeeFromAmount, // bool KeepChangeAddress, // bool @@ -93,6 +96,15 @@ class OptionsModel : public QAbstractListModel OptionIDRowCount, }; + enum class FontChoiceAbstract { + ApplicationFont, + BestSystemFont, + EmbeddedFont, + }; + typedef std::variant FontChoice; + static inline const FontChoice UseBestSystemFont{FontChoiceAbstract::BestSystemFont}; + static QFont getFontForChoice(const FontChoice& fc); + void Init(bool resetSettings = false); void Reset(); @@ -108,6 +120,7 @@ class OptionsModel : public QAbstractListModel bool getMinimizeOnClose() const { return fMinimizeOnClose; } BitcoinUnit getDisplayUnit() const { return m_display_bitcoin_unit; } QString getThirdPartyTxUrls() const { return strThirdPartyTxUrls; } + QFont getFontForMoney() const; bool getCoinControlFeatures() const { return fCoinControlFeatures; } bool getSubFeeFromAmount() const { return m_sub_fee_from_amount; } bool getEnablePSBTControls() const { return m_enable_psbt_controls; } @@ -138,6 +151,7 @@ class OptionsModel : public QAbstractListModel QString language; BitcoinUnit m_display_bitcoin_unit; QString strThirdPartyTxUrls; + FontChoice m_font_money{FontChoiceAbstract::ApplicationFont}; bool fCoinControlFeatures; bool m_sub_fee_from_amount; bool m_enable_psbt_controls; @@ -146,6 +160,9 @@ class OptionsModel : public QAbstractListModel /* settings that were overridden by command-line */ QString strOverriddenByCommandLine; + static QString FontChoiceToString(const OptionsModel::FontChoice&); + static FontChoice FontChoiceFromString(const QString&); + // Add option to list of GUI options overridden through command line/config file void addOverriddenOption(const std::string &option); @@ -160,6 +177,9 @@ class OptionsModel : public QAbstractListModel void coinControlFeaturesChanged(bool); void keepChangeAddressChanged(bool); void showTrayIconChanged(bool); + void fontForMoneyChanged(const QFont&); }; +Q_DECLARE_METATYPE(OptionsModel::FontChoice) + #endif // BITCOIN_QT_OPTIONSMODEL_H diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index 8a0f4546eba4..6c9f06b956ad 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -155,10 +155,7 @@ OverviewPage::OverviewPage(QWidget* parent) : ui->labelCoinJoinHeader }, {GUIUtil::g_font_registry.GetWeightBold(), 16}); - GUIUtil::setFont({ui->labelTotalText, - ui->labelWatchTotal, - ui->labelTotal - }, {GUIUtil::g_font_registry.GetWeightBold(), 14}); + GUIUtil::setFont({ui->labelTotalText}, {GUIUtil::g_font_registry.GetWeightBold(), 14}); GUIUtil::setFont({ui->labelBalanceText, ui->labelPendingText, @@ -322,13 +319,17 @@ void OverviewPage::setWalletModel(WalletModel *model) setBalance(balances); connect(model, &WalletModel::balanceChanged, this, &OverviewPage::setBalance); - connect(model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &OverviewPage::updateDisplayUnit); - updateWatchOnlyLabels((wallet.haveWatchOnly() && !model->wallet().privateKeysDisabled()) || gArgs.GetBoolArg("-debug-ui", false)); connect(model, &WalletModel::notifyWatchonlyChanged, [this](bool showWatchOnly) { updateWatchOnlyLabels(showWatchOnly && !walletModel->wallet().privateKeysDisabled()); }); + // Money font and unit + setMonospacedFont(model->getOptionsModel()->getFontForMoney()); + connect(model->getOptionsModel(), &OptionsModel::fontForMoneyChanged, this, &OverviewPage::setMonospacedFont); + connect(model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &OverviewPage::updateDisplayUnit); + + // CoinJoin connect(model->getOptionsModel(), &OptionsModel::coinJoinRoundsChanged, this, &OverviewPage::updateCoinJoinProgress); connect(model->getOptionsModel(), &OptionsModel::coinJoinAmountChanged, this, &OverviewPage::updateCoinJoinProgress); connect(model->getOptionsModel(), &OptionsModel::AdvancedCJUIChanged, this, &OverviewPage::updateAdvancedCJUI); @@ -376,6 +377,28 @@ void OverviewPage::showOutOfSyncWarning(bool fShow) ui->labelTransactionsStatus->setVisible(fShow); } +void OverviewPage::setMonospacedFont(const QFont& f) +{ + GUIUtil::setFont({ + ui->labelTotal, + ui->labelWatchTotal, + }, {f.family(), GUIUtil::g_font_registry.GetWeightBold(), 14}); + + GUIUtil::setFont({ + ui->labelAmountRounds, + ui->labelAnonymized, + ui->labelBalance, + ui->labelUnconfirmed, + ui->labelImmature, + ui->labelSubmittedDenom, + ui->labelWatchAvailable, + ui->labelWatchPending, + ui->labelWatchImmature, + }, {f.family(), GUIUtil::g_font_registry.GetWeightBold()}); + + GUIUtil::updateFonts(); +} + void OverviewPage::updateCoinJoinProgress() { if (!walletModel || !clientModel || clientModel->node().shutdownRequested() || !clientModel->masternodeSync().isBlockchainSynced()) return; diff --git a/src/qt/overviewpage.h b/src/qt/overviewpage.h index 5f1789f27d7a..8c507e073e2a 100644 --- a/src/qt/overviewpage.h +++ b/src/qt/overviewpage.h @@ -8,7 +8,9 @@ #include #include +#include #include + #include class ClientModel; @@ -71,6 +73,7 @@ private Q_SLOTS: void handleTransactionClicked(const QModelIndex &index); void updateAlerts(const QString &warnings); void updateWatchOnlyLabels(bool showWatchOnly); + void setMonospacedFont(const QFont&); }; #endif // BITCOIN_QT_OVERVIEWPAGE_H