Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2011-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <qt/bitcoinamountfield.h>7#include <qt/bitcoinunits.h>8#include <qt/guiconstants.h>9#include <qt/guiutil.h>10#include <qt/qvaluecombobox.h>12#include <QApplication>13#include <QAbstractSpinBox>14#include <QHBoxLayout>15#include <QKeyEvent>16#include <QLineEdit>17#include <QVariant>19#include <cassert>21/** QSpinBox that uses fixed-point numbers internally and uses our own22* formatting/parsing functions.23*/24class AmountSpinBox: public QAbstractSpinBox25{26Q_OBJECT28public:29explicit AmountSpinBox(QWidget *parent):30QAbstractSpinBox(parent)31{32setAlignment(Qt::AlignRight);34connect(lineEdit(), &QLineEdit::textEdited, this, &AmountSpinBox::valueChanged);35}37QValidator::State validate(QString &text, int &pos) const override38{39if(text.isEmpty())40return QValidator::Intermediate;41bool valid = false;42parse(text, &valid);43/* Make sure we return Intermediate so that fixup() is called on defocus */44return valid ? QValidator::Intermediate : QValidator::Invalid;45}47void fixup(QString &input) const override48{49bool valid;50CAmount val;52if (input.isEmpty() && !m_allow_empty) {53valid = true;54val = m_min_amount;55} else {56valid = false;57val = parse(input, &valid);58}60if (valid) {61val = qBound(m_min_amount, val, m_max_amount);62input = BitcoinUnits::format(currentUnit, val, false, BitcoinUnits::SeparatorStyle::ALWAYS);63lineEdit()->setText(input);64}65}67CAmount value(bool *valid_out=nullptr) const68{69return parse(text(), valid_out);70}72void setValue(const CAmount& value)73{74lineEdit()->setText(BitcoinUnits::format(currentUnit, value, false, BitcoinUnits::SeparatorStyle::ALWAYS));75Q_EMIT valueChanged();76}78void SetAllowEmpty(bool allow)79{80m_allow_empty = allow;81}83void SetMinValue(const CAmount& value)84{85m_min_amount = value;86}88void SetMaxValue(const CAmount& value)89{90m_max_amount = value;91}93void stepBy(int steps) override94{95bool valid = false;96CAmount val = value(&valid);97val = val + steps * singleStep;98val = qBound(m_min_amount, val, m_max_amount);99setValue(val);100}102void setDisplayUnit(BitcoinUnit unit)103{104bool valid = false;105CAmount val = value(&valid);107currentUnit = unit;108lineEdit()->setPlaceholderText(BitcoinUnits::format(currentUnit, m_min_amount, false, BitcoinUnits::SeparatorStyle::ALWAYS));109if(valid)110setValue(val);111else112clear();113}115void setSingleStep(const CAmount& step)116{117singleStep = step;118}120QSize minimumSizeHint() const override121{122if(cachedMinimumSizeHint.isEmpty())123{124ensurePolished();126const QFontMetrics fm(fontMetrics());127int h = lineEdit()->minimumSizeHint().height();128int w = GUIUtil::TextWidth(fm, BitcoinUnits::format(BitcoinUnit::BTC, BitcoinUnits::maxMoney(), false, BitcoinUnits::SeparatorStyle::ALWAYS));129w += 2; // cursor blinking space131QStyleOptionSpinBox opt;132initStyleOption(&opt);133QSize hint(w, h);134QSize extra(35, 6);135opt.rect.setSize(hint + extra);136extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,137QStyle::SC_SpinBoxEditField, this).size();138// get closer to final result by repeating the calculation139opt.rect.setSize(hint + extra);140extra += hint - style()->subControlRect(QStyle::CC_SpinBox, &opt,141QStyle::SC_SpinBoxEditField, this).size();142hint += extra;143hint.setHeight(h);145opt.rect = rect();147cachedMinimumSizeHint = style()->sizeFromContents(QStyle::CT_SpinBox, &opt, hint, this);148}149return cachedMinimumSizeHint;150}152private:153BitcoinUnit currentUnit{BitcoinUnit::BTC};154CAmount singleStep{CAmount(100000)}; // satoshis155mutable QSize cachedMinimumSizeHint;156bool m_allow_empty{true};157CAmount m_min_amount{CAmount(0)};158CAmount m_max_amount{BitcoinUnits::maxMoney()};160/**161* Parse a string into a number of base monetary units and162* return validity.163* @note Must return 0 if !valid.164*/165CAmount parse(const QString &text, bool *valid_out=nullptr) const166{167CAmount val = 0;168bool valid = BitcoinUnits::parse(currentUnit, text, &val);169if(valid)170{171if(val < 0 || val > BitcoinUnits::maxMoney())172valid = false;173}174if(valid_out)175*valid_out = valid;176return valid ? val : 0;177}179protected:180bool event(QEvent *event) override181{182if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease)183{184QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);185if (keyEvent->key() == Qt::Key_Comma)186{187// Translate a comma into a period188QKeyEvent periodKeyEvent(event->type(), Qt::Key_Period, keyEvent->modifiers(), ".", keyEvent->isAutoRepeat(), keyEvent->count());189return QAbstractSpinBox::event(&periodKeyEvent);190}191}192return QAbstractSpinBox::event(event);193}195StepEnabled stepEnabled() const override196{197if (isReadOnly()) // Disable steps when AmountSpinBox is read-only198return StepNone;199if (text().isEmpty()) // Allow step-up with empty field200return StepUpEnabled;202StepEnabled rv = StepNone;203bool valid = false;204CAmount val = value(&valid);205if (valid) {206if (val > m_min_amount)207rv |= StepDownEnabled;208if (val < m_max_amount)209rv |= StepUpEnabled;210}211return rv;212}214Q_SIGNALS:215void valueChanged();216};218#include <qt/bitcoinamountfield.moc>220BitcoinAmountField::BitcoinAmountField(QWidget* parent)221: QWidget(parent)222{223amount = new AmountSpinBox(this);224amount->setLocale(QLocale::c());225amount->installEventFilter(this);226amount->setMaximumWidth(240);228QHBoxLayout *layout = new QHBoxLayout(this);229layout->addWidget(amount);230unit = new QValueComboBox(this);231unit->setModel(new BitcoinUnits(this));232layout->addWidget(unit);233layout->addStretch(1);234layout->setContentsMargins(0,0,0,0);236setLayout(layout);238setFocusPolicy(Qt::TabFocus);239setFocusProxy(amount);241// If one if the widgets changes, the combined content changes as well242connect(amount, &AmountSpinBox::valueChanged, this, &BitcoinAmountField::valueChanged);243connect(unit, qOverload<int>(&QComboBox::currentIndexChanged), this, &BitcoinAmountField::unitChanged);245// Set default based on configuration246unitChanged(unit->currentIndex());247}249void BitcoinAmountField::clear()250{
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.