Skip to content

Commit e376079

Browse files
Merge #6831: backport: merge bitcoin-core/gui#79, #497 (allow different font for overview page balances)
636e7a6 refactor: remove redundant preview and simplify font handling (UdjinM6) b59b0f7 feat(qt): add live preview for monospace font in appearance settings (UdjinM6) 83af951 fix: set money font when setting wallet model instead of ctor (Kittywhiskers Van Gogh) 658905b qt: add non-monospace option and use it as default (Kittywhiskers Van Gogh) e0513ff qt: add horizontal spacer for visual consistency (Kittywhiskers Van Gogh) e5a8d05 refactor: move monospace font selector to appearance widget (Kittywhiskers Van Gogh) 41c69e9 merge bitcoin-core/gui#497: Enable users to configure their monospace font specifically (Kittywhiskers Van Gogh) da5804e merge bitcoin-core/gui#79: Embed monospaced font (Kittywhiskers Van Gogh) eac4f52 qt: adjust padding and font size of monospace font selector (Kittywhiskers Van Gogh) 9c315c3 qt: add monospaced font settings (Kittywhiskers Van Gogh) 34211b6 qt: apply monospace font to overview page balances and CoinJoin elements (Kittywhiskers Van Gogh) 6ed53d6 qt: allow overriding font registry for specific widgets (Kittywhiskers Van Gogh) 800cf66 qt: recognize system monospace font as non-selectable font (Kittywhiskers Van Gogh) 85b49b9 qt: add "Roboto Mono" as non-selectable font (Kittywhiskers Van Gogh) Pull request description: ## Additional Information * Depends on #7068 * Depends on #7084 * Dependency for #6833 | [dash#7084](#7084) (8bba1d2) | This PR (636e7a6) | | ------------------- | ------------------- | | ![image](https://github.com/user-attachments/assets/0208ffb7-d511-4211-a304-6a0aa4d6c367) | ![image](https://github.com/user-attachments/assets/d1f42ec7-f1ee-4856-9c42-04b4c0b103aa) | | ![image](https://github.com/user-attachments/assets/1ce9c72b-d6a1-43ed-b86d-d76cbd52f2b8) | ![image](https://github.com/user-attachments/assets/e8e6bae2-1825-4132-9f78-385dbda31a61) | | ![image](https://github.com/user-attachments/assets/fac6e70f-1ebb-407b-bd16-85ea8ebef84f) | ![image](https://github.com/user-attachments/assets/b8f1d5f8-63a1-4e50-8314-0bc29000959a) | * Based on review suggestions given ([comment](#6831 (comment))), [bitcoin-core/gui#497](bitcoin-core/gui#497) was also backported to allow arbitrary font specification. This also allowed us to offer a "Use existing font" option that _doesn't_ use any monospace font but instead inherits whatever font is used in the rest of the application. This is set as the default ([source](https://github.com/kwvg/dash/blob/89aa2c4b2390ada504fd5b45e16d2cd379074138/src/qt/optionsmodel.cpp#L73)), rendering monospace balance counters opt-in. * Unlike Bitcoin, Dash has a dedicated appearance widget for managing font settings specifically. We have opted to deviate from upstream by moving the money font control from "Display" to "Appearance" for the sake of consistency. * We set the money font when setting the wallet model as client model updates happen after the UI is visible to the user while wallet model updates happen _before_, there is little point in setting them to any values in the constructor as during construction, we don't have access to the options model and thus don't know _what_ font is supposed to be used to begin with. * If a font is passed through the command line, it is considered "selectable" ([source](https://github.com/kwvg/dash/blob/89aa2c4b2390ada504fd5b45e16d2cd379074138/src/qt/bitcoin.cpp#L676-L679)) because we still need to populate that font in the drop-down menu so that we don't report stale data. Though, the font cannot be _set_ to that overridden value because command-line overridden controls are auto-disabled to prevent settings corruption (see 2f30002), which makes the "selectable" state more-or-less an internal detail. An example is that the user tries to run Dash Core, the embedded monospace font, Roboto Mono, is _not_ a selectable font (i.e. you cannot ordinarily set your client to use Roboto Mono, see below). <details> <summary>Screenshot:</summary> ![image](https://github.com/user-attachments/assets/cf005084-c184-4c61-8d37-4106102108f1) </details> But if an override is done, it will be respected, the client will start with Roboto Mono and the selectable status will be overwritten so that it is presented in the drop-down menu, albeit, with the menu disabled so it doesn't contaminate UI settings. <details> <summary>Screenshot:</summary> ![image](https://github.com/user-attachments/assets/a4479dc3-60e0-4c8b-bf41-f89ce2012539) </details> ## Breaking Changes None expected. ## Checklist - [x] I have performed a self-review of my own code - [x] I have commented my code, particularly in hard-to-understand areas - [x] I have added or updated relevant unit/integration/functional/e2e tests **(note: N/A)** - [x] I have made corresponding changes to the documentation **(note: N/A)** - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ ACKs for top commit: UdjinM6: ACK 636e7a6 Tree-SHA512: a8b23c14aec0ccac126bdc7a6db0a4583da3bda52e02f72d213431bbaa80c633a432ed02c4a33d71e3e4d2503d076624196c3e6e50c9bb7fd8e3b50ae4e9ec35
2 parents 6bfe688 + 636e7a6 commit e376079

File tree

12 files changed

+372
-62
lines changed

12 files changed

+372
-62
lines changed

src/qt/appearancewidget.cpp

Lines changed: 96 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,63 @@
1515

1616
#include <QComboBox>
1717
#include <QDataWidgetMapper>
18+
#include <QFontDialog>
19+
#include <QFontInfo>
1820
#include <QSettings>
1921
#include <QSlider>
2022

23+
int setFontChoice(QComboBox* cb, const OptionsModel::FontChoice& fc)
24+
{
25+
int i;
26+
for (i = cb->count(); --i >= 0; ) {
27+
QVariant item_data = cb->itemData(i);
28+
if (!item_data.canConvert<OptionsModel::FontChoice>()) continue;
29+
if (item_data.value<OptionsModel::FontChoice>() == fc) {
30+
break;
31+
}
32+
}
33+
if (i == -1) {
34+
// New item needed
35+
QFont chosen_font = OptionsModel::getFontForChoice(fc);
36+
QSignalBlocker block_currentindexchanged_signal(cb); // avoid triggering QFontDialog
37+
cb->insertItem(0, QFontInfo(chosen_font).family(), QVariant::fromValue(fc));
38+
i = 0;
39+
}
40+
41+
cb->setCurrentIndex(i);
42+
return i;
43+
}
44+
45+
void setupFontOptions(QComboBox* cb)
46+
{
47+
QFont embedded_font{GUIUtil::fixedPitchFont(true)};
48+
QFont system_font{GUIUtil::fixedPitchFont(false)};
49+
cb->addItem(QObject::tr("Default monospace font \"%1\"").arg(QFontInfo(system_font).family()), QVariant::fromValue(OptionsModel::FontChoice{OptionsModel::FontChoiceAbstract::BestSystemFont}));
50+
cb->addItem(QObject::tr("Embedded \"%1\"").arg(QFontInfo(embedded_font).family()), QVariant::fromValue(OptionsModel::FontChoice{OptionsModel::FontChoiceAbstract::EmbeddedFont}));
51+
cb->addItem(QObject::tr("Use existing font"), QVariant::fromValue(OptionsModel::FontChoice{OptionsModel::FontChoiceAbstract::ApplicationFont}));
52+
cb->addItem(QObject::tr("Custom…"));
53+
54+
const auto& on_font_choice_changed = [cb](int index) {
55+
static int previous_index = -1;
56+
QVariant item_data = cb->itemData(index);
57+
if (item_data.canConvert<OptionsModel::FontChoice>()) {
58+
// Valid predefined choice, nothing to do
59+
} else {
60+
// "Custom..." was selected, show font dialog
61+
bool ok;
62+
QFont f = QFontDialog::getFont(&ok, GUIUtil::fixedPitchFont(false), cb->parentWidget());
63+
if (!ok) {
64+
cb->setCurrentIndex(previous_index);
65+
return;
66+
}
67+
index = setFontChoice(cb, OptionsModel::FontChoice{f});
68+
}
69+
previous_index = index;
70+
};
71+
QObject::connect(cb, QOverload<int>::of(&QComboBox::currentIndexChanged), on_font_choice_changed);
72+
on_font_choice_changed(cb->currentIndex());
73+
}
74+
2175
AppearanceWidget::AppearanceWidget(QWidget* parent) :
2276
QWidget(parent),
2377
ui{new Ui::AppearanceWidget()},
@@ -34,7 +88,8 @@ AppearanceWidget::AppearanceWidget(QWidget* parent) :
3488
}
3589

3690
for (size_t idx{0}; idx < GUIUtil::g_fonts_known.size(); idx++) {
37-
ui->fontFamily->addItem(GUIUtil::g_fonts_known[idx], QVariant((uint16_t)idx));
91+
const auto& [font, selectable] = GUIUtil::g_fonts_known[idx];
92+
if (selectable) { ui->fontFamily->addItem(font, QVariant((uint16_t)idx)); }
3893
}
3994

4095
updateWeightSlider();
@@ -43,17 +98,25 @@ AppearanceWidget::AppearanceWidget(QWidget* parent) :
4398
mapper->setSubmitPolicy(QDataWidgetMapper::ManualSubmit);
4499
mapper->setOrientation(Qt::Vertical);
45100

46-
connect(ui->theme, &QComboBox::currentTextChanged, this, &AppearanceWidget::updateTheme);
101+
setupFontOptions(ui->moneyFont);
102+
47103
connect(ui->fontFamily, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &AppearanceWidget::updateFontFamily);
104+
connect(ui->fontFamily, QOverload<int>::of(&QComboBox::currentIndexChanged), [this]() { Q_EMIT appearanceChanged(); });
105+
106+
connect(ui->fontScaleSlider, &QSlider::sliderReleased, [this]() { Q_EMIT appearanceChanged(); });
48107
connect(ui->fontScaleSlider, &QSlider::valueChanged, this, &AppearanceWidget::updateFontScale);
49-
connect(ui->fontWeightNormalSlider, &QSlider::valueChanged, [this](auto nValue) { updateFontWeightNormal(nValue); });
108+
109+
connect(ui->fontWeightBoldSlider, &QSlider::sliderReleased, [this]() { Q_EMIT appearanceChanged(); });
50110
connect(ui->fontWeightBoldSlider, &QSlider::valueChanged, [this](auto nValue) { updateFontWeightBold(nValue); });
51111

52-
connect(ui->theme, &QComboBox::currentTextChanged, [this]() { Q_EMIT appearanceChanged(); });
53-
connect(ui->fontFamily, &QComboBox::currentTextChanged, [this]() { Q_EMIT appearanceChanged(); });
54-
connect(ui->fontScaleSlider, &QSlider::sliderReleased, [this]() { Q_EMIT appearanceChanged(); });
55112
connect(ui->fontWeightNormalSlider, &QSlider::sliderReleased, [this]() { Q_EMIT appearanceChanged(); });
56-
connect(ui->fontWeightBoldSlider, &QSlider::sliderReleased, [this]() { Q_EMIT appearanceChanged(); });
113+
connect(ui->fontWeightNormalSlider, &QSlider::valueChanged, [this](auto nValue) { updateFontWeightNormal(nValue); });
114+
115+
connect(ui->moneyFont, &QComboBox::currentTextChanged, [this]() { Q_EMIT appearanceChanged(); });
116+
connect(ui->moneyFont, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &AppearanceWidget::updateMoneyFont);
117+
118+
connect(ui->theme, &QComboBox::currentTextChanged, this, &AppearanceWidget::updateTheme);
119+
connect(ui->theme, &QComboBox::currentTextChanged, [this]() { Q_EMIT appearanceChanged(); });
57120
}
58121

59122
AppearanceWidget::~AppearanceWidget()
@@ -78,6 +141,13 @@ AppearanceWidget::~AppearanceWidget()
78141
if (prevWeightBold != GUIUtil::g_font_registry.GetWeightBold()) {
79142
GUIUtil::g_font_registry.SetWeightBold(prevWeightBold);
80143
}
144+
// Restore monospace font if cancelled
145+
if (model) {
146+
const auto& current_money_font = model->data(model->index(OptionsModel::FontForMoney, 0), Qt::EditRole).value<OptionsModel::FontChoice>();
147+
if (current_money_font != prevMoneyFont) {
148+
model->setData(model->index(OptionsModel::FontForMoney, 0), QVariant::fromValue(prevMoneyFont));
149+
}
150+
}
81151
GUIUtil::setApplicationFont();
82152
GUIUtil::updateFonts();
83153
}
@@ -103,6 +173,10 @@ void AppearanceWidget::setModel(OptionsModel* _model)
103173

104174
mapper->toFirst();
105175

176+
const auto& font_for_money = _model->data(_model->index(OptionsModel::FontForMoney, 0), Qt::EditRole).value<OptionsModel::FontChoice>();
177+
prevMoneyFont = font_for_money; // Save original value for cancel
178+
setFontChoice(ui->moneyFont, font_for_money);
179+
106180
const bool override_family{_model->isOptionOverridden("-font-family")};
107181
if (override_family) {
108182
ui->fontFamily->setEnabled(false);
@@ -138,6 +212,7 @@ void AppearanceWidget::setModel(OptionsModel* _model)
138212
void AppearanceWidget::accept()
139213
{
140214
fAcceptChanges = true;
215+
// Note: FontForMoney is now updated immediately via updateMoneyFont()
141216
}
142217

143218
void AppearanceWidget::updateTheme(const QString& theme)
@@ -154,7 +229,7 @@ void AppearanceWidget::updateTheme(const QString& theme)
154229

155230
void AppearanceWidget::updateFontFamily(int index)
156231
{
157-
const bool setfont_ret{GUIUtil::g_font_registry.SetFont(GUIUtil::g_fonts_known[ui->fontFamily->itemData(index).toInt()])};
232+
const bool setfont_ret{GUIUtil::g_font_registry.SetFont(GUIUtil::g_fonts_known[ui->fontFamily->itemData(index).toInt()].first)};
158233
assert(setfont_ret);
159234
GUIUtil::setApplicationFont();
160235
GUIUtil::updateFonts();
@@ -193,6 +268,19 @@ void AppearanceWidget::updateFontWeightBold(int nValue, bool fForce)
193268
GUIUtil::updateFonts();
194269
}
195270

271+
void AppearanceWidget::updateMoneyFont(int index)
272+
{
273+
if (!model) {
274+
return;
275+
}
276+
QVariant item_data = ui->moneyFont->itemData(index);
277+
if (!item_data.canConvert<OptionsModel::FontChoice>()) {
278+
return;
279+
}
280+
// Update the model immediately to trigger live preview in Overview page
281+
model->setData(model->index(OptionsModel::FontForMoney, 0), item_data);
282+
}
283+
196284
void AppearanceWidget::updateWeightSlider(const bool fForce)
197285
{
198286
int nMaximum = GUIUtil::g_font_registry.GetSupportedWeights().size() - 1;

src/qt/appearancewidget.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,12 @@
99

1010
#include <qt/guiutil.h>
1111
#include <qt/guiutil_font.h>
12+
#include <qt/optionsmodel.h>
1213

1314
namespace Ui {
1415
class AppearanceWidget;
1516
}
1617

17-
class OptionsModel;
18-
1918
class QDataWidgetMapper;
2019
class QSlider;
2120
class QComboBox;
@@ -42,6 +41,7 @@ private Q_SLOTS:
4241
void updateFontScale(int nScale);
4342
void updateFontWeightNormal(int nValue, bool fForce = false);
4443
void updateFontWeightBold(int nValue, bool fForce = false);
44+
void updateMoneyFont(int index);
4545

4646
private:
4747
Ui::AppearanceWidget* ui;
@@ -53,6 +53,7 @@ private Q_SLOTS:
5353
QString prevFontFamily;
5454
QFont::Weight prevWeightNormal;
5555
QFont::Weight prevWeightBold;
56+
OptionsModel::FontChoice prevMoneyFont{OptionsModel::FontChoiceAbstract::ApplicationFont};
5657

5758
void updateWeightSlider(bool fForce = false);
5859
};

src/qt/bitcoin.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,7 @@ static void SetupUIArgs(ArgsManager& argsman)
489489
{
490490
argsman.AddArg("-choosedatadir", strprintf(QObject::tr("Choose data directory on startup (default: %u)").toStdString(), DEFAULT_CHOOSE_DATADIR), ArgsManager::ALLOW_ANY, OptionsCategory::GUI);
491491
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);
492-
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);
492+
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);
493493
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);
494494
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);
495495
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[])
673673
// Validate/set font family
674674
if (gArgs.IsArgSet("-font-family")) {
675675
QString family = gArgs.GetArg("-font-family", GUIUtil::FontRegistry::DEFAULT_FONT.toUtf8().toStdString()).c_str();
676-
if (!GUIUtil::g_font_registry.RegisterFont(family) || !GUIUtil::g_font_registry.SetFont(family)) {
676+
if (!GUIUtil::g_font_registry.RegisterFont(family, /*selectable=*/true) || !GUIUtil::g_font_registry.SetFont(family)) {
677677
QMessageBox::critical(nullptr, PACKAGE_NAME, QObject::tr("Error: Font \"%1\" could not be loaded.").arg(family));
678678
return EXIT_FAILURE;
679679
}

src/qt/forms/appearancewidget.ui

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,49 @@
479479
</item>
480480
</layout>
481481
</item>
482+
<item>
483+
<layout class="QHBoxLayout" name="hLayoutMoneyFont">
484+
<item>
485+
<widget class="QLabel" name="moneyFontLabel">
486+
<property name="text">
487+
<string>Font in the Overview tab: </string>
488+
</property>
489+
<property name="buddy">
490+
<cstring>moneyFont</cstring>
491+
</property>
492+
</widget>
493+
</item>
494+
<item>
495+
<spacer name="moneyFont_horizontalSpacer">
496+
<property name="orientation">
497+
<enum>Qt::Horizontal</enum>
498+
</property>
499+
<property name="sizeHint" stdset="0">
500+
<size>
501+
<width>40</width>
502+
<height>20</height>
503+
</size>
504+
</property>
505+
</spacer>
506+
</item>
507+
<item>
508+
<widget class="QComboBox" name="moneyFont">
509+
<property name="minimumSize">
510+
<size>
511+
<width>282</width>
512+
<height>0</height>
513+
</size>
514+
</property>
515+
<property name="maximumSize">
516+
<size>
517+
<width>282</width>
518+
<height>16777215</height>
519+
</size>
520+
</property>
521+
</widget>
522+
</item>
523+
</layout>
524+
</item>
482525
</layout>
483526
</widget>
484527
<resources/>

src/qt/guiutil.cpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -230,14 +230,6 @@ QString dateTimeStr(qint64 nTime)
230230
return dateTimeStr(QDateTime::fromSecsSinceEpoch((qint32)nTime));
231231
}
232232

233-
QFont fixedPitchFont(bool use_embedded_font)
234-
{
235-
if (use_embedded_font) {
236-
return {"Roboto Mono"};
237-
}
238-
return QFontDatabase::systemFont(QFontDatabase::FixedFont);
239-
}
240-
241233
// Just some dummy data to generate a convincing random-looking (but consistent) address
242234
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};
243235

src/qt/guiutil.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,6 @@ namespace GUIUtil
125125
QString dateTimeStr(const QDateTime &datetime);
126126
QString dateTimeStr(qint64 nTime);
127127

128-
// Return a monospace font
129-
QFont fixedPitchFont(bool use_embedded_font = false);
130-
131128
// Set up widget for address
132129
void setupAddressWidget(QValidatedLineEdit *widget, QWidget *parent, bool fAllowURI = false);
133130

0 commit comments

Comments
 (0)