Bitcoin ABC 0.32.4
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()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this);
141 }
143 }
144
145private:
149 bool m_allow_empty{true};
152
158 Amount parse(const QString &text, bool *valid_out = nullptr) const {
159 Amount val = Amount::zero();
160 bool valid = BitcoinUnits::parse(currentUnit, text, &val);
161 if (valid) {
162 if (val < Amount::zero() || val > BitcoinUnits::maxMoney()) {
163 valid = false;
164 }
165 }
166 if (valid_out) {
167 *valid_out = valid;
168 }
169 return valid ? val : Amount::zero();
170 }
171
172protected:
173 bool event(QEvent *event) override {
174 if (event->type() == QEvent::KeyPress ||
175 event->type() == QEvent::KeyRelease) {
176 QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
177 if (keyEvent->key() == Qt::Key_Comma) {
178 // Translate a comma into a period.
179 QKeyEvent periodKeyEvent(
180 event->type(), Qt::Key_Period, keyEvent->modifiers(), ".",
181 keyEvent->isAutoRepeat(), keyEvent->count());
182 return QAbstractSpinBox::event(&periodKeyEvent);
183 }
184 }
185 return QAbstractSpinBox::event(event);
186 }
187
188 StepEnabled stepEnabled() const override {
189 if (isReadOnly()) {
190 // Disable steps when AmountSpinBox is read-only.
191 return StepNone;
192 }
193 if (text().isEmpty()) {
194 // Allow step-up with empty field.
195 return StepUpEnabled;
196 }
197
198 StepEnabled rv = StepNone;
199 bool valid = false;
200 Amount val = value(&valid);
201 if (valid) {
202 if (val > m_min_amount) {
203 rv |= StepDownEnabled;
204 }
205 if (val < m_max_amount) {
206 rv |= StepUpEnabled;
207 }
208 }
209 return rv;
210 }
211
212Q_SIGNALS:
214};
215
216#include <qt/bitcoinamountfield.moc>
217
219 : QWidget(parent), amount(nullptr) {
220 amount = new AmountSpinBox(this);
221 amount->setLocale(QLocale::c());
222 amount->installEventFilter(this);
223 amount->setMaximumWidth(240);
224
225 QHBoxLayout *layout = new QHBoxLayout(this);
226 layout->addWidget(amount);
227 unit = new QValueComboBox(this);
228 unit->setModel(new BitcoinUnits(this));
229 layout->addWidget(unit);
230 layout->addStretch(1);
231 layout->setContentsMargins(0, 0, 0, 0);
232
233 setLayout(layout);
234
235 setFocusPolicy(Qt::TabFocus);
236 setFocusProxy(amount);
237
238 // If one if the widgets changes, the combined content changes as well
239 connect(amount, &AmountSpinBox::valueChanged, this,
241 connect(
242 unit,
243 static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
245
246 // Set default based on configuration
247 unitChanged(unit->currentIndex());
248}
249
251 amount->clear();
252 unit->setCurrentIndex(0);
253}
254
256 amount->setEnabled(fEnabled);
257 unit->setEnabled(fEnabled);
258}
259
261 bool valid = false;
262 value(&valid);
263 setValid(valid);
264 return valid;
265}
266
268 if (valid) {
269 amount->setStyleSheet("");
270 } else {
271 amount->setStyleSheet(STYLE_INVALID);
272 }
273}
274
275bool BitcoinAmountField::eventFilter(QObject *object, QEvent *event) {
276 if (event->type() == QEvent::FocusIn) {
277 // Clear invalid flag on focus
278 setValid(true);
279 }
280 return QWidget::eventFilter(object, event);
281}
282
283QWidget *BitcoinAmountField::setupTabChain(QWidget *prev) {
284 QWidget::setTabOrder(prev, amount);
285 QWidget::setTabOrder(amount, unit);
286 return unit;
287}
288
289Amount BitcoinAmountField::value(bool *valid_out) const {
290 return amount->value(valid_out);
291}
292
295}
296
298 amount->SetAllowEmpty(allow);
299}
300
303}
304
307}
309 amount->setReadOnly(fReadOnly);
310}
311
313 // Use description tooltip for current unit for the combobox
314 unit->setToolTip(unit->itemData(idx, Qt::ToolTipRole).toString());
315
316 // Determine new unit ID
317 int newUnit = unit->itemData(idx, BitcoinUnits::UnitRole).toInt();
318
319 amount->setDisplayUnit(newUnit);
320}
321
323 unit->setValue(newUnit);
324}
325
327 amount->setSingleStep(step);
328}
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:25
int TextWidth(const QFontMetrics &fm, const QString &text)
Returns the distance in pixels appropriate for drawing a subsequent character after text.
Definition: guiutil.cpp:951
Definition: amount.h:19
static constexpr Amount zero() noexcept
Definition: amount.h:32