Bitcoin ABC 0.30.5
P2P Digital Currency
bitcoinamountfield.cpp
Go to the documentation of this file.
1// Copyright (c) 2011-2015 The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
6
7#include <qt/bitcoinunits.h>
8#include <qt/guiconstants.h>
9#include <qt/guiutil.h>
10#include <qt/qvaluecombobox.h>
11
12#include <QAbstractSpinBox>
13#include <QApplication>
14#include <QHBoxLayout>
15#include <QKeyEvent>
16#include <QLineEdit>
17
22class AmountSpinBox : public QAbstractSpinBox {
23 Q_OBJECT
24
25public:
26 explicit AmountSpinBox(QWidget *parent) {
27 setAlignment(Qt::AlignRight);
28
29 connect(lineEdit(), &QLineEdit::textEdited, this,
31 }
32
33 QValidator::State validate(QString &text, int &pos) const override {
34 if (text.isEmpty()) {
35 return QValidator::Intermediate;
36 }
37 bool valid = false;
38 parse(text, &valid);
39 // Make sure we return Intermediate so that fixup() is called on
40 // defocus.
41 return valid ? QValidator::Intermediate : QValidator::Invalid;
42 }
43
44 void fixup(QString &input) const override {
45 bool valid;
46 Amount val;
47
48 if (input.isEmpty() && !m_allow_empty) {
49 valid = true;
50 val = m_min_amount;
51 } else {
52 valid = false;
53 val = parse(input, &valid);
54 }
55
56 if (valid) {
57 val = qBound(m_min_amount, val, m_max_amount);
58 input = BitcoinUnits::format(currentUnit, val, false,
60 lineEdit()->setText(input);
61 }
62 }
63
64 Amount value(bool *valid_out = nullptr) const {
65 return parse(text(), valid_out);
66 }
67
68 void setValue(const Amount value) {
69 lineEdit()->setText(BitcoinUnits::format(
71 Q_EMIT valueChanged();
72 }
73
74 void SetAllowEmpty(bool allow) { m_allow_empty = allow; }
75
77
79
80 void stepBy(int steps) override {
81 bool valid = false;
82 Amount val = value(&valid);
83 val = val + steps * singleStep;
84 val = qBound(m_min_amount, val, m_max_amount);
85 setValue(val);
86 }
87
88 void setDisplayUnit(int unit) {
89 bool valid = false;
90 Amount val(value(&valid));
91 currentUnit = unit;
92 lineEdit()->setPlaceholderText(
95 if (valid) {
96 setValue(val);
97 } else {
98 clear();
99 }
100 }
101
102 void setSingleStep(const Amount step) { singleStep = step; }
103
104 QSize minimumSizeHint() const override {
105 if (cachedMinimumSizeHint.isEmpty()) {
106 ensurePolished();
107
108 const QFontMetrics fm(fontMetrics());
109 int h = lineEdit()->minimumSizeHint().height();
110 int w = GUIUtil::TextWidth(
112 BitcoinUnits::maxMoney(), false,
114 // Cursor blinking space.
115 w += 2;
116
117 QStyleOptionSpinBox opt;
118 initStyleOption(&opt);
119 QSize hint(w, h);
120 QSize extra(35, 6);
121 opt.rect.setSize(hint + extra);
122 extra +=
123 hint - style()
124 ->subControlRect(QStyle::CC_SpinBox, &opt,
125 QStyle::SC_SpinBoxEditField, this)
126 .size();
127 // Get closer to final result by repeating the calculation.
128 opt.rect.setSize(hint + extra);
129 extra +=
130 hint - style()
131 ->subControlRect(QStyle::CC_SpinBox, &opt,
132 QStyle::SC_SpinBoxEditField, this)
133 .size();
134 hint += extra;
135 hint.setHeight(h);
136
137 opt.rect = rect();
138
140 style()
141 ->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this)
142 .expandedTo(QApplication::globalStrut());
143 }
145 }
146
147private:
151 bool m_allow_empty{true};
154
160 Amount parse(const QString &text, bool *valid_out = nullptr) const {
161 Amount val = Amount::zero();
162 bool valid = BitcoinUnits::parse(currentUnit, text, &val);
163 if (valid) {
164 if (val < Amount::zero() || val > BitcoinUnits::maxMoney()) {
165 valid = false;
166 }
167 }
168 if (valid_out) {
169 *valid_out = valid;
170 }
171 return valid ? val : Amount::zero();
172 }
173
174protected:
175 bool event(QEvent *event) override {
176 if (event->type() == QEvent::KeyPress ||
177 event->type() == QEvent::KeyRelease) {
178 QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
179 if (keyEvent->key() == Qt::Key_Comma) {
180 // Translate a comma into a period.
181 QKeyEvent periodKeyEvent(
182 event->type(), Qt::Key_Period, keyEvent->modifiers(), ".",
183 keyEvent->isAutoRepeat(), keyEvent->count());
184 return QAbstractSpinBox::event(&periodKeyEvent);
185 }
186 }
187 return QAbstractSpinBox::event(event);
188 }
189
190 StepEnabled stepEnabled() const override {
191 if (isReadOnly()) {
192 // Disable steps when AmountSpinBox is read-only.
193 return StepNone;
194 }
195 if (text().isEmpty()) {
196 // Allow step-up with empty field.
197 return StepUpEnabled;
198 }
199
200 StepEnabled rv = StepNone;
201 bool valid = false;
202 Amount val = value(&valid);
203 if (valid) {
204 if (val > m_min_amount) {
205 rv |= StepDownEnabled;
206 }
207 if (val < m_max_amount) {
208 rv |= StepUpEnabled;
209 }
210 }
211 return rv;
212 }
213
214Q_SIGNALS:
216};
217
218#include <qt/bitcoinamountfield.moc>
219
221 : QWidget(parent), amount(nullptr) {
222 amount = new AmountSpinBox(this);
223 amount->setLocale(QLocale::c());
224 amount->installEventFilter(this);
225 amount->setMaximumWidth(240);
226
227 QHBoxLayout *layout = new QHBoxLayout(this);
228 layout->addWidget(amount);
229 unit = new QValueComboBox(this);
230 unit->setModel(new BitcoinUnits(this));
231 layout->addWidget(unit);
232 layout->addStretch(1);
233 layout->setContentsMargins(0, 0, 0, 0);
234
235 setLayout(layout);
236
237 setFocusPolicy(Qt::TabFocus);
238 setFocusProxy(amount);
239
240 // If one if the widgets changes, the combined content changes as well
241 connect(amount, &AmountSpinBox::valueChanged, this,
243 connect(
244 unit,
245 static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
247
248 // Set default based on configuration
249 unitChanged(unit->currentIndex());
250}
251
253 amount->clear();
254 unit->setCurrentIndex(0);
255}
256
258 amount->setEnabled(fEnabled);
259 unit->setEnabled(fEnabled);
260}
261
263 bool valid = false;
264 value(&valid);
265 setValid(valid);
266 return valid;
267}
268
270 if (valid) {
271 amount->setStyleSheet("");
272 } else {
273 amount->setStyleSheet(STYLE_INVALID);
274 }
275}
276
277bool BitcoinAmountField::eventFilter(QObject *object, QEvent *event) {
278 if (event->type() == QEvent::FocusIn) {
279 // Clear invalid flag on focus
280 setValid(true);
281 }
282 return QWidget::eventFilter(object, event);
283}
284
285QWidget *BitcoinAmountField::setupTabChain(QWidget *prev) {
286 QWidget::setTabOrder(prev, amount);
287 QWidget::setTabOrder(amount, unit);
288 return unit;
289}
290
291Amount BitcoinAmountField::value(bool *valid_out) const {
292 return amount->value(valid_out);
293}
294
297}
298
300 amount->SetAllowEmpty(allow);
301}
302
305}
306
309}
311 amount->setReadOnly(fReadOnly);
312}
313
315 // Use description tooltip for current unit for the combobox
316 unit->setToolTip(unit->itemData(idx, Qt::ToolTipRole).toString());
317
318 // Determine new unit ID
319 int newUnit = unit->itemData(idx, BitcoinUnits::UnitRole).toInt();
320
321 amount->setDisplayUnit(newUnit);
322}
323
325 unit->setValue(newUnit);
326}
327
329 amount->setSingleStep(step);
330}
static constexpr Amount SATOSHI
Definition: amount.h:143
QSpinBox that uses fixed-point numbers internally and uses our own formatting/parsing functions.
void SetMinValue(const Amount &value)
bool event(QEvent *event) override
void setValue(const Amount value)
AmountSpinBox(QWidget *parent)
QValidator::State validate(QString &text, int &pos) const override
StepEnabled stepEnabled() const override
void fixup(QString &input) const override
void valueChanged()
void setSingleStep(const Amount step)
void stepBy(int steps) override
QSize minimumSizeHint() const override
Amount value(bool *valid_out=nullptr) const
void SetAllowEmpty(bool allow)
void SetMaxValue(const Amount &value)
void setDisplayUnit(int unit)
Amount parse(const QString &text, bool *valid_out=nullptr) const
Parse a string into a number of base monetary units and return validity.
AmountSpinBox * amount
void setEnabled(bool fEnabled)
Enable/Disable.
void SetMaxValue(const Amount &value)
Set the maximum value in satoshis.
QValueComboBox * unit
bool eventFilter(QObject *object, QEvent *event) override
Intercept focus-in event and ',' key presses.
void setReadOnly(bool fReadOnly)
Make read-only.
BitcoinAmountField(QWidget *parent=nullptr)
void setDisplayUnit(int unit)
Change unit used to display amount.
void clear()
Make field empty and ready for new input.
bool validate()
Perform input validation, mark field as invalid if entered value is not valid.
QWidget * setupTabChain(QWidget *prev)
Qt messes up the tab chain by default in some cases (issue https://bugreports.qt-project....
void setValue(const Amount value)
void setValid(bool valid)
Mark current value as invalid in UI.
void SetAllowEmpty(bool allow)
If allow empty is set to false the field will be set to the minimum allowed value if left empty.
void setSingleStep(const Amount step)
Set single step in satoshis.
void SetMinValue(const Amount &value)
Set the minimum value in satoshis.
Bitcoin unit definitions.
Definition: bitcoinunits.h:32
@ UnitRole
Unit identifier.
Definition: bitcoinunits.h:92
static Amount maxMoney()
Return maximum number of base units (Satoshis)
static QString format(int unit, const Amount amount, bool plussign=false, SeparatorStyle separators=SeparatorStyle::STANDARD, bool justify=false)
Format as string.
static bool parse(int unit, const QString &value, Amount *val_out)
Parse string to coin amount.
void setValue(const QVariant &value)
#define STYLE_INVALID
Definition: guiconstants.h:22
int TextWidth(const QFontMetrics &fm, const QString &text)
Returns the distance in pixels appropriate for drawing a subsequent character after text.
Definition: guiutil.cpp:945
Definition: amount.h:19
static constexpr Amount zero() noexcept
Definition: amount.h:32