Bitcoin ABC  0.26.3
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 
22 class AmountSpinBox : public QAbstractSpinBox {
23  Q_OBJECT
24 
25 public:
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  }
144  return cachedMinimumSizeHint;
145  }
146 
147 private:
150  mutable QSize cachedMinimumSizeHint;
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 
174 protected:
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 
214 Q_SIGNALS:
215  void valueChanged();
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 
257 void BitcoinAmountField::setEnabled(bool fEnabled) {
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 
277 bool 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 
285 QWidget *BitcoinAmountField::setupTabChain(QWidget *prev) {
286  QWidget::setTabOrder(prev, amount);
287  QWidget::setTabOrder(amount, unit);
288  return unit;
289 }
290 
291 Amount 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 }
310 void BitcoinAmountField::setReadOnly(bool fReadOnly) {
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:943
Definition: amount.h:19
static constexpr Amount zero() noexcept
Definition: amount.h:32