Skip to content

Commit fd12639

Browse files
xdustinfacegades
authored andcommitted
qt: Redesign BitcoinAmountField (dashpay#3569)
* qt: Remove min-width of BitcoinAmountField in ReceiveCoinsDialog * qt: Add BitcoinUnits::data(const int &row, int role) Make its data also accessible int, not only by QModelIndex. * qt: Adjust BitcoinAmountField - AmountSpinBox (QAbstractBox) is now called AmountLineEdit (QLineEdit) - Replaced the AmountSpinBox and the QValueComboBox with just an AmountLineEdit - Adjusted min-size hint calculation - Increased maximum width - Right-Align text - Removed obsolete code * qt: Removed leftover from legacy BitcoinAmountField * qt: Move amount field into a Hlayout with a spacer (ReveiveCoinsDialog) Make sure it does not get stretched above the in AmountLineEdit::minimumSizeHint() calculated size. * qt: Removed obsolete workaround related to BitcoinAmountField Its fixed now with the change to a normal line edit.
1 parent db165bb commit fd12639

File tree

7 files changed

+128
-162
lines changed

7 files changed

+128
-162
lines changed

src/qt/bitcoinamountfield.cpp

Lines changed: 85 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -1,153 +1,130 @@
11
// Copyright (c) 2011-2015 The Bitcoin Core developers
2-
// Copyright (c) 2020-2022 The Cosanta Core developers
32
// Distributed under the MIT software license, see the accompanying
43
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
54

65
#include <qt/bitcoinamountfield.h>
76

87
#include <qt/bitcoinunits.h>
98
#include <qt/guiutil.h>
10-
#include <qt/qvaluecombobox.h>
119

1210
#include <QApplication>
1311
#include <QAbstractSpinBox>
1412
#include <QHBoxLayout>
1513
#include <QKeyEvent>
1614
#include <QLineEdit>
1715

18-
/** QSpinBox that uses fixed-point numbers internally and uses our own
19-
* formatting/parsing functions.
16+
/**
17+
* Parse a string into a number of base monetary units and
18+
* return validity.
19+
* @note Must return 0 if !valid.
2020
*/
21-
class AmountSpinBox: public QAbstractSpinBox
21+
static CAmount parse(const QString &text, int nUnit, bool *valid_out=0)
2222
{
23-
Q_OBJECT
24-
25-
public:
26-
explicit AmountSpinBox(QWidget *parent):
27-
QAbstractSpinBox(parent),
28-
currentUnit(BitcoinUnits::COSANTA),
29-
singleStep(100000) // satoshis
23+
CAmount val = 0;
24+
bool valid = BitcoinUnits::parse(nUnit, text, &val);
25+
if(valid)
3026
{
31-
setAlignment(Qt::AlignRight);
32-
33-
connect(lineEdit(), SIGNAL(textEdited(QString)), this, SIGNAL(valueChanged()));
27+
if(val < 0 || val > BitcoinUnits::maxMoney())
28+
valid = false;
3429
}
30+
if(valid_out)
31+
*valid_out = valid;
32+
return valid ? val : 0;
33+
}
34+
35+
/** Amount widget validator, checks for valid CAmount value.
36+
*/
37+
class AmountValidator : public QValidator
38+
{
39+
Q_OBJECT
40+
int currentUnit;
41+
public:
42+
explicit AmountValidator(QObject *parent) :
43+
QValidator(parent),
44+
currentUnit(BitcoinUnits::COSANTA) {}
3545

36-
QValidator::State validate(QString &text, int &pos) const
46+
State validate(QString &input, int &pos) const
3747
{
38-
if(text.isEmpty())
48+
if(input.isEmpty())
3949
return QValidator::Intermediate;
4050
bool valid = false;
41-
parse(text, &valid);
51+
parse(input, currentUnit, &valid);
4252
/* Make sure we return Intermediate so that fixup() is called on defocus */
4353
return valid ? QValidator::Intermediate : QValidator::Invalid;
4454
}
4555

46-
void fixup(QString &input) const
56+
void updateUnit(int nUnit)
57+
{
58+
currentUnit = nUnit;
59+
}
60+
};
61+
62+
/** QLineEdit that uses fixed-point numbers internally and uses our own
63+
* formatting/parsing functions.
64+
*/
65+
class AmountLineEdit: public QLineEdit
66+
{
67+
Q_OBJECT
68+
AmountValidator* amountValidator;
69+
public:
70+
explicit AmountLineEdit(QWidget *parent):
71+
QLineEdit(parent),
72+
currentUnit(BitcoinUnits::COSANTA)
73+
{
74+
setAlignment(Qt::AlignLeft);
75+
amountValidator = new AmountValidator(this);
76+
setValidator(amountValidator);
77+
connect(this, SIGNAL(textEdited(QString)), this, SIGNAL(valueChanged()));
78+
}
79+
80+
void fixup(const QString &input)
4781
{
4882
bool valid = false;
49-
CAmount val = parse(input, &valid);
83+
CAmount val = parse(input, currentUnit, &valid);
5084
if(valid)
5185
{
52-
input = BitcoinUnits::format(currentUnit, val, false, BitcoinUnits::separatorAlways);
53-
lineEdit()->setText(input);
86+
setText(BitcoinUnits::format(currentUnit, val, false, BitcoinUnits::separatorAlways));
5487
}
5588
}
5689

5790
CAmount value(bool *valid_out=0) const
5891
{
59-
return parse(text(), valid_out);
92+
return parse(text(), currentUnit, valid_out);
6093
}
6194

6295
void setValue(const CAmount& value)
6396
{
64-
lineEdit()->setText(BitcoinUnits::format(currentUnit, value, false, BitcoinUnits::separatorAlways));
97+
setText(BitcoinUnits::format(currentUnit, value, false, BitcoinUnits::separatorAlways));
6598
Q_EMIT valueChanged();
6699
}
67100

68-
void stepBy(int steps)
69-
{
70-
bool valid = false;
71-
CAmount val = value(&valid);
72-
val = val + steps * singleStep;
73-
val = qMin(qMax(val, CAmount(0)), BitcoinUnits::maxMoney());
74-
setValue(val);
75-
}
76-
77101
void setDisplayUnit(int unit)
78102
{
79103
bool valid = false;
80104
CAmount val = value(&valid);
81105

82106
currentUnit = unit;
107+
amountValidator->updateUnit(unit);
83108

84109
if(valid)
85110
setValue(val);
86111
else
87112
clear();
88113
}
89114

90-
void setSingleStep(const CAmount& step)
91-
{
92-
singleStep = step;
93-
}
94-
95115
QSize minimumSizeHint() const
96116
{
97-
if(cachedMinimumSizeHint.isEmpty())
98-
{
99-
ensurePolished();
100-
101-
const QFontMetrics fm(fontMetrics());
102-
int h = lineEdit()->minimumSizeHint().height();
103-
int w = fm.width(BitcoinUnits::format(BitcoinUnits::COSANTA, BitcoinUnits::maxMoney(), false, BitcoinUnits::separatorAlways));
104-
w += 2; // cursor blinking space
105-
106-
QStyleOptionSpinBox opt;
107-
initStyleOption(&opt);
108-
QSize hint(w, h);
109-
QSize extra(35, 6);
110-
opt.rect.setSize(hint + extra);
111-
extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,
112-
QStyle::SC_SpinBoxEditField, this).size();
113-
// get closer to final result by repeating the calculation
114-
opt.rect.setSize(hint + extra);
115-
extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,
116-
QStyle::SC_SpinBoxEditField, this).size();
117-
hint += extra;
118-
hint.setHeight(h);
119-
120-
opt.rect = rect();
121-
122-
cachedMinimumSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this)
123-
.expandedTo(QApplication::globalStrut());
124-
}
125-
return cachedMinimumSizeHint;
117+
ensurePolished();
118+
const QFontMetrics fm(fontMetrics());
119+
int h = 0;
120+
int w = fm.width(BitcoinUnits::format(BitcoinUnits::COSANTA, BitcoinUnits::maxMoney(), false, BitcoinUnits::separatorAlways));
121+
w += 2; // cursor blinking space
122+
w += GUIUtil::dashThemeActive() ? 24 : 0; // counteract padding from css
123+
return QSize(w, h);
126124
}
127125

128126
private:
129127
int currentUnit;
130-
CAmount singleStep;
131-
mutable QSize cachedMinimumSizeHint;
132-
133-
/**
134-
* Parse a string into a number of base monetary units and
135-
* return validity.
136-
* @note Must return 0 if !valid.
137-
*/
138-
CAmount parse(const QString &text, bool *valid_out=0) const
139-
{
140-
CAmount val = 0;
141-
bool valid = BitcoinUnits::parse(currentUnit, text, &val);
142-
if(valid)
143-
{
144-
if(val < 0 || val > BitcoinUnits::maxMoney())
145-
valid = false;
146-
}
147-
if(valid_out)
148-
*valid_out = valid;
149-
return valid ? val : 0;
150-
}
151128

152129
protected:
153130
bool event(QEvent *event)
@@ -159,30 +136,18 @@ class AmountSpinBox: public QAbstractSpinBox
159136
{
160137
// Translate a comma into a period
161138
QKeyEvent periodKeyEvent(event->type(), Qt::Key_Period, keyEvent->modifiers(), ".", keyEvent->isAutoRepeat(), keyEvent->count());
162-
return QAbstractSpinBox::event(&periodKeyEvent);
139+
return QLineEdit::event(&periodKeyEvent);
140+
}
141+
if(keyEvent->key() == Qt::Key_Enter || keyEvent->key() == Qt::Key_Return)
142+
{
143+
clearFocus();
163144
}
164145
}
165-
return QAbstractSpinBox::event(event);
166-
}
167-
168-
StepEnabled stepEnabled() const
169-
{
170-
if (isReadOnly()) // Disable steps when AmountSpinBox is read-only
171-
return StepNone;
172-
if (text().isEmpty()) // Allow step-up with empty field
173-
return StepUpEnabled;
174-
175-
StepEnabled rv = 0;
176-
bool valid = false;
177-
CAmount val = value(&valid);
178-
if(valid)
146+
if (event->type() == QEvent::FocusOut)
179147
{
180-
if(val > 0)
181-
rv |= StepDownEnabled;
182-
if(val < BitcoinUnits::maxMoney())
183-
rv |= StepUpEnabled;
148+
fixup(text());
184149
}
185-
return rv;
150+
return QLineEdit::event(event);
186151
}
187152

188153
Q_SIGNALS:
@@ -195,18 +160,17 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent) :
195160
QWidget(parent),
196161
amount(0)
197162
{
198-
amount = new AmountSpinBox(this);
163+
amount = new AmountLineEdit(this);
199164
amount->setLocale(QLocale::c());
200165
amount->installEventFilter(this);
201-
amount->setMaximumWidth(240);
166+
amount->setMaximumWidth(300);
167+
168+
units = new BitcoinUnits(this);
202169

203170
QHBoxLayout *layout = new QHBoxLayout(this);
171+
layout->setSpacing(0);
172+
layout->setMargin(0);
204173
layout->addWidget(amount);
205-
unit = new QValueComboBox(this);
206-
unit->setModel(new BitcoinUnits(this));
207-
layout->addWidget(unit);
208-
layout->addStretch(1);
209-
layout->setContentsMargins(0,0,0,0);
210174

211175
setLayout(layout);
212176

@@ -215,22 +179,16 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent) :
215179

216180
// If one if the widgets changes, the combined content changes as well
217181
connect(amount, SIGNAL(valueChanged()), this, SIGNAL(valueChanged()));
218-
connect(unit, SIGNAL(currentIndexChanged(int)), this, SLOT(unitChanged(int)));
219-
220-
// Set default based on configuration
221-
unitChanged(unit->currentIndex());
222182
}
223183

224184
void BitcoinAmountField::clear()
225185
{
226186
amount->clear();
227-
unit->setCurrentIndex(0);
228187
}
229188

230189
void BitcoinAmountField::setEnabled(bool fEnabled)
231190
{
232191
amount->setEnabled(fEnabled);
233-
unit->setEnabled(fEnabled);
234192
}
235193

236194
bool BitcoinAmountField::validate()
@@ -262,8 +220,7 @@ bool BitcoinAmountField::eventFilter(QObject *object, QEvent *event)
262220
QWidget *BitcoinAmountField::setupTabChain(QWidget *prev)
263221
{
264222
QWidget::setTabOrder(prev, amount);
265-
QWidget::setTabOrder(amount, unit);
266-
return unit;
223+
return amount;
267224
}
268225

269226
CAmount BitcoinAmountField::value(bool *valid_out) const
@@ -284,20 +241,17 @@ void BitcoinAmountField::setReadOnly(bool fReadOnly)
284241
void BitcoinAmountField::unitChanged(int idx)
285242
{
286243
// Use description tooltip for current unit for the combobox
287-
unit->setToolTip(unit->itemData(idx, Qt::ToolTipRole).toString());
244+
amount->setToolTip(units->data(idx, Qt::ToolTipRole).toString());
288245

289246
// Determine new unit ID
290-
int newUnit = unit->itemData(idx, BitcoinUnits::UnitRole).toInt();
247+
int newUnit = units->data(idx, BitcoinUnits::UnitRole).toInt();
248+
249+
amount->setPlaceholderText(tr("Amount in ") + units->data(idx,Qt::DisplayRole).toString());
291250

292251
amount->setDisplayUnit(newUnit);
293252
}
294253

295254
void BitcoinAmountField::setDisplayUnit(int newUnit)
296255
{
297-
unit->setValue(newUnit);
298-
}
299-
300-
void BitcoinAmountField::setSingleStep(const CAmount& step)
301-
{
302-
amount->setSingleStep(step);
256+
unitChanged(newUnit);
303257
}

src/qt/bitcoinamountfield.h

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88

99
#include <amount.h>
1010

11+
#include <QValidator>
1112
#include <QWidget>
1213

13-
class AmountSpinBox;
14+
class AmountLineEdit;
15+
class BitcoinUnits;
1416

1517
QT_BEGIN_NAMESPACE
1618
class QValueComboBox;
@@ -32,9 +34,6 @@ class BitcoinAmountField: public QWidget
3234
CAmount value(bool *value=0) const;
3335
void setValue(const CAmount& value);
3436

35-
/** Set single step in satoshis **/
36-
void setSingleStep(const CAmount& step);
37-
3837
/** Make read-only **/
3938
void setReadOnly(bool fReadOnly);
4039

@@ -65,12 +64,10 @@ class BitcoinAmountField: public QWidget
6564
bool eventFilter(QObject *object, QEvent *event);
6665

6766
private:
68-
AmountSpinBox *amount;
69-
QValueComboBox *unit;
67+
AmountLineEdit *amount;
68+
BitcoinUnits *units;
7069

71-
private Q_SLOTS:
7270
void unitChanged(int idx);
73-
7471
};
7572

7673
#endif // BITCOIN_QT_BITCOINAMOUNTFIELD_H

src/qt/bitcoinunits.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,11 @@ int BitcoinUnits::rowCount(const QModelIndex &parent) const
247247

248248
QVariant BitcoinUnits::data(const QModelIndex &index, int role) const
249249
{
250-
int row = index.row();
250+
return data(index.row(), role);
251+
}
252+
253+
QVariant BitcoinUnits::data(const int &row, int role) const
254+
{
251255
if(row >= 0 && row < unitlist.size())
252256
{
253257
Unit unit = unitlist.at(row);

0 commit comments

Comments
 (0)