11// Copyright (c) 2011-2015 The Bitcoin Core developers
2- // Copyright (c) 2020-2022 The Cosanta Core developers
32// Distributed under the MIT software license, see the accompanying
43// file COPYING or http://www.opensource.org/licenses/mit-license.php.
54
65#include < qt/bitcoinamountfield.h>
76
87#include < qt/bitcoinunits.h>
98#include < qt/guiutil.h>
10- #include < qt/qvaluecombobox.h>
119
1210#include < QApplication>
1311#include < QAbstractSpinBox>
1412#include < QHBoxLayout>
1513#include < QKeyEvent>
1614#include < QLineEdit>
1715
18- /* * QSpinBox that uses fixed-point numbers internally and uses our own
19- * formatting/parsing functions.
16+ /* *
17+ * Parse a string into a number of base monetary units and
18+ * return validity.
19+ * @note Must return 0 if !valid.
2020 */
21- class AmountSpinBox : public QAbstractSpinBox
21+ static CAmount parse ( const QString &text, int nUnit, bool *valid_out= 0 )
2222{
23- Q_OBJECT
24-
25- public:
26- explicit AmountSpinBox (QWidget *parent):
27- QAbstractSpinBox(parent),
28- currentUnit(BitcoinUnits::COSANTA),
29- singleStep(100000 ) // satoshis
23+ CAmount val = 0 ;
24+ bool valid = BitcoinUnits::parse (nUnit, text, &val);
25+ if (valid)
3026 {
31- setAlignment (Qt::AlignRight);
32-
33- connect (lineEdit (), SIGNAL (textEdited (QString)), this , SIGNAL (valueChanged ()));
27+ if (val < 0 || val > BitcoinUnits::maxMoney ())
28+ valid = false ;
3429 }
30+ if (valid_out)
31+ *valid_out = valid;
32+ return valid ? val : 0 ;
33+ }
34+
35+ /* * Amount widget validator, checks for valid CAmount value.
36+ */
37+ class AmountValidator : public QValidator
38+ {
39+ Q_OBJECT
40+ int currentUnit;
41+ public:
42+ explicit AmountValidator (QObject *parent) :
43+ QValidator(parent),
44+ currentUnit(BitcoinUnits::COSANTA) {}
3545
36- QValidator:: State validate (QString &text , int &pos) const
46+ State validate (QString &input , int &pos) const
3747 {
38- if (text .isEmpty ())
48+ if (input .isEmpty ())
3949 return QValidator::Intermediate;
4050 bool valid = false ;
41- parse (text , &valid);
51+ parse (input, currentUnit , &valid);
4252 /* Make sure we return Intermediate so that fixup() is called on defocus */
4353 return valid ? QValidator::Intermediate : QValidator::Invalid;
4454 }
4555
46- void fixup (QString &input) const
56+ void updateUnit (int nUnit)
57+ {
58+ currentUnit = nUnit;
59+ }
60+ };
61+
62+ /* * QLineEdit that uses fixed-point numbers internally and uses our own
63+ * formatting/parsing functions.
64+ */
65+ class AmountLineEdit : public QLineEdit
66+ {
67+ Q_OBJECT
68+ AmountValidator* amountValidator;
69+ public:
70+ explicit AmountLineEdit (QWidget *parent):
71+ QLineEdit(parent),
72+ currentUnit(BitcoinUnits::COSANTA)
73+ {
74+ setAlignment (Qt::AlignLeft);
75+ amountValidator = new AmountValidator (this );
76+ setValidator (amountValidator);
77+ connect (this , SIGNAL (textEdited (QString)), this , SIGNAL (valueChanged ()));
78+ }
79+
80+ void fixup (const QString &input)
4781 {
4882 bool valid = false ;
49- CAmount val = parse (input, &valid);
83+ CAmount val = parse (input, currentUnit, &valid);
5084 if (valid)
5185 {
52- input = BitcoinUnits::format (currentUnit, val, false , BitcoinUnits::separatorAlways);
53- lineEdit ()->setText (input);
86+ setText (BitcoinUnits::format (currentUnit, val, false , BitcoinUnits::separatorAlways));
5487 }
5588 }
5689
5790 CAmount value (bool *valid_out=0 ) const
5891 {
59- return parse (text (), valid_out);
92+ return parse (text (), currentUnit, valid_out);
6093 }
6194
6295 void setValue (const CAmount& value)
6396 {
64- lineEdit ()-> setText (BitcoinUnits::format (currentUnit, value, false , BitcoinUnits::separatorAlways));
97+ setText (BitcoinUnits::format (currentUnit, value, false , BitcoinUnits::separatorAlways));
6598 Q_EMIT valueChanged ();
6699 }
67100
68- void stepBy (int steps)
69- {
70- bool valid = false ;
71- CAmount val = value (&valid);
72- val = val + steps * singleStep;
73- val = qMin (qMax (val, CAmount (0 )), BitcoinUnits::maxMoney ());
74- setValue (val);
75- }
76-
77101 void setDisplayUnit (int unit)
78102 {
79103 bool valid = false ;
80104 CAmount val = value (&valid);
81105
82106 currentUnit = unit;
107+ amountValidator->updateUnit (unit);
83108
84109 if (valid)
85110 setValue (val);
86111 else
87112 clear ();
88113 }
89114
90- void setSingleStep (const CAmount& step)
91- {
92- singleStep = step;
93- }
94-
95115 QSize minimumSizeHint () const
96116 {
97- if (cachedMinimumSizeHint.isEmpty ())
98- {
99- ensurePolished ();
100-
101- const QFontMetrics fm (fontMetrics ());
102- int h = lineEdit ()->minimumSizeHint ().height ();
103- int w = fm.width (BitcoinUnits::format (BitcoinUnits::COSANTA, BitcoinUnits::maxMoney (), false , BitcoinUnits::separatorAlways));
104- w += 2 ; // cursor blinking space
105-
106- QStyleOptionSpinBox opt;
107- initStyleOption (&opt);
108- QSize hint (w, h);
109- QSize extra (35 , 6 );
110- opt.rect .setSize (hint + extra);
111- extra += hint - style ()->subControlRect (QStyle::CC_SpinBox, &opt,
112- QStyle::SC_SpinBoxEditField, this ).size ();
113- // get closer to final result by repeating the calculation
114- opt.rect .setSize (hint + extra);
115- extra += hint - style ()->subControlRect (QStyle::CC_SpinBox, &opt,
116- QStyle::SC_SpinBoxEditField, this ).size ();
117- hint += extra;
118- hint.setHeight (h);
119-
120- opt.rect = rect ();
121-
122- cachedMinimumSizeHint = style ()->sizeFromContents (QStyle::CT_SpinBox, &opt, hint, this )
123- .expandedTo (QApplication::globalStrut ());
124- }
125- return cachedMinimumSizeHint;
117+ ensurePolished ();
118+ const QFontMetrics fm (fontMetrics ());
119+ int h = 0 ;
120+ int w = fm.width (BitcoinUnits::format (BitcoinUnits::COSANTA, BitcoinUnits::maxMoney (), false , BitcoinUnits::separatorAlways));
121+ w += 2 ; // cursor blinking space
122+ w += GUIUtil::dashThemeActive () ? 24 : 0 ; // counteract padding from css
123+ return QSize (w, h);
126124 }
127125
128126private:
129127 int currentUnit;
130- CAmount singleStep;
131- mutable QSize cachedMinimumSizeHint;
132-
133- /* *
134- * Parse a string into a number of base monetary units and
135- * return validity.
136- * @note Must return 0 if !valid.
137- */
138- CAmount parse (const QString &text, bool *valid_out=0 ) const
139- {
140- CAmount val = 0 ;
141- bool valid = BitcoinUnits::parse (currentUnit, text, &val);
142- if (valid)
143- {
144- if (val < 0 || val > BitcoinUnits::maxMoney ())
145- valid = false ;
146- }
147- if (valid_out)
148- *valid_out = valid;
149- return valid ? val : 0 ;
150- }
151128
152129protected:
153130 bool event (QEvent *event)
@@ -159,30 +136,18 @@ class AmountSpinBox: public QAbstractSpinBox
159136 {
160137 // Translate a comma into a period
161138 QKeyEvent periodKeyEvent (event->type (), Qt::Key_Period, keyEvent->modifiers (), " ." , keyEvent->isAutoRepeat (), keyEvent->count ());
162- return QAbstractSpinBox::event (&periodKeyEvent);
139+ return QLineEdit::event (&periodKeyEvent);
140+ }
141+ if (keyEvent->key () == Qt::Key_Enter || keyEvent->key () == Qt::Key_Return)
142+ {
143+ clearFocus ();
163144 }
164145 }
165- return QAbstractSpinBox::event (event);
166- }
167-
168- StepEnabled stepEnabled () const
169- {
170- if (isReadOnly ()) // Disable steps when AmountSpinBox is read-only
171- return StepNone;
172- if (text ().isEmpty ()) // Allow step-up with empty field
173- return StepUpEnabled;
174-
175- StepEnabled rv = 0 ;
176- bool valid = false ;
177- CAmount val = value (&valid);
178- if (valid)
146+ if (event->type () == QEvent::FocusOut)
179147 {
180- if (val > 0 )
181- rv |= StepDownEnabled;
182- if (val < BitcoinUnits::maxMoney ())
183- rv |= StepUpEnabled;
148+ fixup (text ());
184149 }
185- return rv ;
150+ return QLineEdit::event (event) ;
186151 }
187152
188153Q_SIGNALS:
@@ -195,18 +160,17 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent) :
195160 QWidget(parent),
196161 amount(0 )
197162{
198- amount = new AmountSpinBox (this );
163+ amount = new AmountLineEdit (this );
199164 amount->setLocale (QLocale::c ());
200165 amount->installEventFilter (this );
201- amount->setMaximumWidth (240 );
166+ amount->setMaximumWidth (300 );
167+
168+ units = new BitcoinUnits (this );
202169
203170 QHBoxLayout *layout = new QHBoxLayout (this );
171+ layout->setSpacing (0 );
172+ layout->setMargin (0 );
204173 layout->addWidget (amount);
205- unit = new QValueComboBox (this );
206- unit->setModel (new BitcoinUnits (this ));
207- layout->addWidget (unit);
208- layout->addStretch (1 );
209- layout->setContentsMargins (0 ,0 ,0 ,0 );
210174
211175 setLayout (layout);
212176
@@ -215,22 +179,16 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent) :
215179
216180 // If one if the widgets changes, the combined content changes as well
217181 connect (amount, SIGNAL (valueChanged ()), this , SIGNAL (valueChanged ()));
218- connect (unit, SIGNAL (currentIndexChanged (int )), this , SLOT (unitChanged (int )));
219-
220- // Set default based on configuration
221- unitChanged (unit->currentIndex ());
222182}
223183
224184void BitcoinAmountField::clear ()
225185{
226186 amount->clear ();
227- unit->setCurrentIndex (0 );
228187}
229188
230189void BitcoinAmountField::setEnabled (bool fEnabled )
231190{
232191 amount->setEnabled (fEnabled );
233- unit->setEnabled (fEnabled );
234192}
235193
236194bool BitcoinAmountField::validate ()
@@ -262,8 +220,7 @@ bool BitcoinAmountField::eventFilter(QObject *object, QEvent *event)
262220QWidget *BitcoinAmountField::setupTabChain (QWidget *prev)
263221{
264222 QWidget::setTabOrder (prev, amount);
265- QWidget::setTabOrder (amount, unit);
266- return unit;
223+ return amount;
267224}
268225
269226CAmount BitcoinAmountField::value (bool *valid_out) const
@@ -284,20 +241,17 @@ void BitcoinAmountField::setReadOnly(bool fReadOnly)
284241void BitcoinAmountField::unitChanged (int idx)
285242{
286243 // Use description tooltip for current unit for the combobox
287- unit ->setToolTip (unit-> itemData (idx, Qt::ToolTipRole).toString ());
244+ amount ->setToolTip (units-> data (idx, Qt::ToolTipRole).toString ());
288245
289246 // Determine new unit ID
290- int newUnit = unit->itemData (idx, BitcoinUnits::UnitRole).toInt ();
247+ int newUnit = units->data (idx, BitcoinUnits::UnitRole).toInt ();
248+
249+ amount->setPlaceholderText (tr (" Amount in " ) + units->data (idx,Qt::DisplayRole).toString ());
291250
292251 amount->setDisplayUnit (newUnit);
293252}
294253
295254void BitcoinAmountField::setDisplayUnit (int newUnit)
296255{
297- unit->setValue (newUnit);
298- }
299-
300- void BitcoinAmountField::setSingleStep (const CAmount& step)
301- {
302- amount->setSingleStep (step);
256+ unitChanged (newUnit);
303257}
0 commit comments