Bitcoin ABC  0.22.12
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(BitcoinUnits::format(
94  if (valid) {
95  setValue(val);
96  } else {
97  clear();
98  }
99  }
100 
101  void setSingleStep(const Amount step) { singleStep = step; }
102 
103  QSize minimumSizeHint() const override {
104  if (cachedMinimumSizeHint.isEmpty()) {
105  ensurePolished();
106 
107  const QFontMetrics fm(fontMetrics());
108  int h = lineEdit()->minimumSizeHint().height();
109  int w = GUIUtil::TextWidth(
111  BitcoinUnits::maxMoney(), false,
113  // Cursor blinking space.
114  w += 2;
115 
116  QStyleOptionSpinBox opt;
117  initStyleOption(&opt);
118  QSize hint(w, h);
119  QSize extra(35, 6);
120  opt.rect.setSize(hint + extra);
121  extra +=
122  hint - style()
123  ->subControlRect(QStyle::CC_SpinBox, &opt,
124  QStyle::SC_SpinBoxEditField, this)
125  .size();
126  // Get closer to final result by repeating the calculation.
127  opt.rect.setSize(hint + extra);
128  extra +=
129  hint - style()
130  ->subControlRect(QStyle::CC_SpinBox, &opt,
131  QStyle::SC_SpinBoxEditField, this)
132  .size();
133  hint += extra;
134  hint.setHeight(h);
135 
136  opt.rect = rect();
137 
139  style()
140  ->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this)
141  .expandedTo(QApplication::globalStrut());
142  }
143  return cachedMinimumSizeHint;
144  }
145 
146 private:
149  mutable QSize cachedMinimumSizeHint;
150  bool m_allow_empty{true};
153 
159  Amount parse(const QString &text, bool *valid_out = nullptr) const {
160  Amount val = Amount::zero();
161  bool valid = BitcoinUnits::parse(currentUnit, text, &val);
162  if (valid) {
163  if (val < Amount::zero() || val > BitcoinUnits::maxMoney()) {
164  valid = false;
165  }
166  }
167  if (valid_out) {
168  *valid_out = valid;
169  }
170  return valid ? val : Amount::zero();
171  }
172 
173 protected:
174  bool event(QEvent *event) override {
175  if (event->type() == QEvent::KeyPress ||
176  event->type() == QEvent::KeyRelease) {
177  QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
178  if (keyEvent->key() == Qt::Key_Comma) {
179  // Translate a comma into a period.
180  QKeyEvent periodKeyEvent(
181  event->type(), Qt::Key_Period, keyEvent->modifiers(), ".",
182  keyEvent->isAutoRepeat(), keyEvent->count());
183  return QAbstractSpinBox::event(&periodKeyEvent);
184  }
185  }
186  return QAbstractSpinBox::event(event);
187  }
188 
189  StepEnabled stepEnabled() const override {
190  if (isReadOnly()) {
191  // Disable steps when AmountSpinBox is read-only.
192  return StepNone;
193  }
194  if (text().isEmpty()) {
195  // Allow step-up with empty field.
196  return StepUpEnabled;
197  }
198 
199  StepEnabled rv = StepNone;
200  bool valid = false;
201  Amount val = value(&valid);
202  if (valid) {
203  if (val > m_min_amount) {
204  rv |= StepDownEnabled;
205  }
206  if (val < m_max_amount) {
207  rv |= StepUpEnabled;
208  }
209  }
210  return rv;
211  }
212 
213 Q_SIGNALS:
214  void valueChanged();
215 };
216 
217 #include <qt/bitcoinamountfield.moc>
218 
220  : QWidget(parent), amount(nullptr) {
221  amount = new AmountSpinBox(this);
222  amount->setLocale(QLocale::c());
223  amount->installEventFilter(this);
224  amount->setMaximumWidth(240);
225 
226  QHBoxLayout *layout = new QHBoxLayout(this);
227  layout->addWidget(amount);
228  unit = new QValueComboBox(this);
229  unit->setModel(new BitcoinUnits(this));
230  layout->addWidget(unit);
231  layout->addStretch(1);
232  layout->setContentsMargins(0, 0, 0, 0);
233 
234  setLayout(layout);
235 
236  setFocusPolicy(Qt::TabFocus);
237  setFocusProxy(amount);
238 
239  // If one if the widgets changes, the combined content changes as well
240  connect(amount, &AmountSpinBox::valueChanged, this,
242  connect(
243  unit,
244  static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
246 
247  // Set default based on configuration
248  unitChanged(unit->currentIndex());
249 }
250 
252  amount->clear();
253  unit->setCurrentIndex(0);
254 }
255 
256 void BitcoinAmountField::setEnabled(bool fEnabled) {
257  amount->setEnabled(fEnabled);
258  unit->setEnabled(fEnabled);
259 }
260 
262  bool valid = false;
263  value(&valid);
264  setValid(valid);
265  return valid;
266 }
267 
269  if (valid) {
270  amount->setStyleSheet("");
271  } else {
272  amount->setStyleSheet(STYLE_INVALID);
273  }
274 }
275 
276 bool BitcoinAmountField::eventFilter(QObject *object, QEvent *event) {
277  if (event->type() == QEvent::FocusIn) {
278  // Clear invalid flag on focus
279  setValid(true);
280  }
281  return QWidget::eventFilter(object, event);
282 }
283 
284 QWidget *BitcoinAmountField::setupTabChain(QWidget *prev) {
285  QWidget::setTabOrder(prev, amount);
286  QWidget::setTabOrder(amount, unit);
287  return unit;
288 }
289 
290 Amount BitcoinAmountField::value(bool *valid_out) const {
291  return amount->value(valid_out);
292 }
293 
295  amount->setValue(value);
296 }
297 
299  amount->SetAllowEmpty(allow);
300 }
301 
303  amount->SetMinValue(value);
304 }
305 
307  amount->SetMaxValue(value);
308 }
309 void BitcoinAmountField::setReadOnly(bool fReadOnly) {
310  amount->setReadOnly(fReadOnly);
311 }
312 
314  // Use description tooltip for current unit for the combobox
315  unit->setToolTip(unit->itemData(idx, Qt::ToolTipRole).toString());
316 
317  // Determine new unit ID
318  int newUnit = unit->itemData(idx, BitcoinUnits::UnitRole).toInt();
319 
320  amount->setDisplayUnit(newUnit);
321 }
322 
324  unit->setValue(newUnit);
325 }
326 
328  amount->setSingleStep(step);
329 }
bool validate()
Perform input validation, mark field as invalid if entered value is not valid.
Bitcoin unit definitions.
Definition: bitcoinunits.h:32
static constexpr Amount zero()
Definition: amount.h:35
void setReadOnly(bool fReadOnly)
Make read-only.
QSpinBox that uses fixed-point numbers internally and uses our own formatting/parsing functions...
int TextWidth(const QFontMetrics &fm, const QString &text)
Returns the distance in pixels appropriate for drawing a subsequent character after text...
Definition: guiutil.cpp:877
void SetAllowEmpty(bool allow)
void valueChanged()
QSize minimumSizeHint() const override
AmountSpinBox(QWidget *parent)
Definition: amount.h:17
QWidget * setupTabChain(QWidget *prev)
Qt messes up the tab chain by default in some cases (issue https://bugreports.qt-project.org/browse/QTBUG-10907), in these cases we have to set it up manually.
void setSingleStep(const Amount step)
Set single step in satoshis.
static constexpr Amount SATOSHI
Definition: amount.h:151
StepEnabled stepEnabled() const override
void setDisplayUnit(int unit)
void fixup(QString &input) const override
BitcoinAmountField(QWidget *parent=nullptr)
QValidator::State validate(QString &text, int &pos) const override
AmountSpinBox * amount
void setEnabled(bool fEnabled)
Enable/Disable.
#define STYLE_INVALID
Definition: guiconstants.h:22
bool event(QEvent *event) override
Unit identifier.
Definition: bitcoinunits.h:88
bool eventFilter(QObject *object, QEvent *event) override
Intercept focus-in event and &#39;,&#39; key presses.
Amount value(bool *valid_out=nullptr) const
static Amount maxMoney()
Return maximum number of base units (Satoshis)
static QString format(int unit, const Amount amount, bool plussign=false, SeparatorStyle separators=separatorStandard)
Format as string.
void setValue(const Amount value)
void SetMaxValue(const Amount &value)
void setValue(const Amount value)
QValueComboBox * unit
void setValue(const QVariant &value)
void SetMinValue(const Amount &value)
Set the minimum value in satoshis.
void clear()
Make field empty and ready for new input.
void setValid(bool valid)
Mark current value as invalid in UI.
void SetMinValue(const Amount &value)
Amount parse(const QString &text, bool *valid_out=nullptr) const
Parse a string into a number of base monetary units and return validity.
void setSingleStep(const Amount step)
void stepBy(int steps) override
static bool parse(int unit, const QString &value, Amount *val_out)
Parse string to coin amount.
void setDisplayUnit(int unit)
Change unit used to display amount.
void SetMaxValue(const Amount &value)
Set the maximum value in satoshis.
void SetAllowEmpty(bool allow)
If allow empty is set to false the field will be set to the minimum allowed value if left empty...