Skip to content

Commit 2e7bef0

Browse files
committed
Merge #445: Introduce Send pages for singlesig, single input/output send
727aa52 qml: Introduce Send pages for singlesig, sigle input/output send (johnny9) Pull request description: These changes implement the first iteration of the Send coins flow for our Desktop Wallet. A QML model, SendRecipient, is defined to hold the values entered into the Send form which will then be used to construct the actual transaction. The resulting transaction will be held in another new QML model, WalletQmlModelTransaction, and that will be held by the WalletModel and used to render the confirmation page, SendReview.qml. This is designed to closely mirror the objects used in the current Qt SendCoinsDialog modules. ACKs for top commit: hebasto: ACK 727aa52, I have skimmed through the code and it looks OK. Tree-SHA512: daaf9a76fa091d2dba4ab2731c984879f6d4796e62887a7c989fe5fe5ac4c16222020392b1f361dccbad19b67cd3e8855d1f5d03ab11a1e4f175e42496d6ac17
2 parents 59fa29a + 727aa52 commit 2e7bef0

17 files changed

+836
-14
lines changed

src/Makefile.qt.include

+10
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,11 @@ QT_MOC_CPP = \
4545
qml/models/moc_peerdetailsmodel.cpp \
4646
qml/models/moc_peerlistsortproxy.cpp \
4747
qml/models/moc_transaction.cpp \
48+
qml/models/moc_sendrecipient.cpp \
4849
qml/models/moc_walletlistmodel.cpp \
4950
qml/models/moc_walletqmlmodel.cpp \
51+
qml/models/moc_walletqmlmodel.cpp \
52+
qml/models/moc_walletqmlmodeltransaction.cpp \
5053
qml/moc_appmode.cpp \
5154
qml/moc_bitcoinamount.cpp \
5255
qml/moc_clipboard.cpp \
@@ -132,8 +135,10 @@ BITCOIN_QT_H = \
132135
qml/models/peerdetailsmodel.h \
133136
qml/models/peerlistsortproxy.h \
134137
qml/models/transaction.h \
138+
qml/models/sendrecipient.h \
135139
qml/models/walletlistmodel.h \
136140
qml/models/walletqmlmodel.h \
141+
qml/models/walletqmlmodeltransaction.h \
137142
qml/appmode.h \
138143
qml/clipboard.h \
139144
qml/bitcoin.h \
@@ -329,8 +334,10 @@ BITCOIN_QML_BASE_CPP = \
329334
qml/models/peerdetailsmodel.cpp \
330335
qml/models/peerlistsortproxy.cpp \
331336
qml/models/transaction.cpp \
337+
qml/models/sendrecipient.cpp \
332338
qml/models/walletlistmodel.cpp \
333339
qml/models/walletqmlmodel.cpp \
340+
qml/models/walletqmlmodeltransaction.cpp \
334341
qml/imageprovider.cpp \
335342
qml/util.cpp \
336343
qml/walletqmlcontroller.cpp
@@ -462,6 +469,9 @@ QML_RES_QML = \
462469
qml/pages/wallet/CreateWalletWizard.qml \
463470
qml/pages/wallet/DesktopWallets.qml \
464471
qml/pages/wallet/RequestPayment.qml \
472+
qml/pages/wallet/Send.qml \
473+
qml/pages/wallet/SendResult.qml \
474+
qml/pages/wallet/SendReview.qml \
465475
qml/pages/wallet/WalletBadge.qml \
466476
qml/pages/wallet/WalletSelect.qml
467477

src/qml/bitcoin.cpp

+6-1
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,18 @@
2323
#include <qml/components/blockclockdial.h>
2424
#include <qml/controls/linegraph.h>
2525
#include <qml/guiconstants.h>
26+
#include <qml/imageprovider.h>
2627
#include <qml/models/activitylistmodel.h>
2728
#include <qml/models/chainmodel.h>
2829
#include <qml/models/networktraffictower.h>
2930
#include <qml/models/nodemodel.h>
3031
#include <qml/models/options_model.h>
3132
#include <qml/models/peerdetailsmodel.h>
3233
#include <qml/models/peerlistsortproxy.h>
34+
#include <qml/models/sendrecipient.h>
3335
#include <qml/models/walletlistmodel.h>
3436
#include <qml/models/walletqmlmodel.h>
35-
#include <qml/imageprovider.h>
37+
#include <qml/models/walletqmlmodeltransaction.h>
3638
#include <qml/util.h>
3739
#include <qml/walletqmlcontroller.h>
3840
#include <qt/guiutil.h>
@@ -337,10 +339,13 @@ int QmlGuiMain(int argc, char* argv[])
337339
qmlRegisterUncreatableType<PeerDetailsModel>("org.bitcoincore.qt", 1, 0, "PeerDetailsModel", "");
338340
qmlRegisterType<BitcoinAmount>("org.bitcoincore.qt", 1, 0, "BitcoinAmount");
339341
qmlRegisterUncreatableType<Transaction>("org.bitcoincore.qt", 1, 0, "Transaction", "");
342+
qmlRegisterUncreatableType<SendRecipient>("org.bitcoincore.qt", 1, 0, "SendRecipient", "");
340343

341344
#ifdef ENABLE_WALLET
342345
qmlRegisterUncreatableType<WalletQmlModel>("org.bitcoincore.qt", 1, 0, "WalletQmlModel",
343346
"WalletQmlModel cannot be instantiated from QML");
347+
qmlRegisterUncreatableType<WalletQmlModelTransaction>("org.bitcoincore.qt", 1, 0, "WalletQmlModelTransaction",
348+
"WalletQmlModelTransaction cannot be instantiated from QML");
344349
#endif
345350

346351
engine.load(QUrl(QStringLiteral("qrc:///qml/pages/main.qml")));

src/qml/bitcoin_qml.qrc

+3-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
<file>controls/ContinueButton.qml</file>
2525
<file>controls/CoreText.qml</file>
2626
<file>controls/CoreTextField.qml</file>
27-
<file>controls/LabeledTextInput.qml</file>
2827
<file>controls/ExternalLink.qml</file>
2928
<file>controls/FocusBorder.qml</file>
3029
<file>controls/Header.qml</file>
@@ -83,6 +82,9 @@
8382
<file>pages/wallet/CreateWalletWizard.qml</file>
8483
<file>pages/wallet/DesktopWallets.qml</file>
8584
<file>pages/wallet/RequestPayment.qml</file>
85+
<file>pages/wallet/Send.qml</file>
86+
<file>pages/wallet/SendResult.qml</file>
87+
<file>pages/wallet/SendReview.qml</file>
8688
<file>pages/wallet/WalletBadge.qml</file>
8789
<file>pages/wallet/WalletSelect.qml</file>
8890
</qresource>

src/qml/bitcoinamount.cpp

+29-2
Original file line numberDiff line numberDiff line change
@@ -68,15 +68,28 @@ QString BitcoinAmount::amount() const
6868
return m_amount;
6969
}
7070

71+
QString BitcoinAmount::satoshiAmount() const
72+
{
73+
return toSatoshis(m_amount);
74+
}
75+
7176
void BitcoinAmount::setAmount(const QString& new_amount)
7277
{
7378
m_amount = sanitize(new_amount);
7479
Q_EMIT amountChanged();
7580
}
7681

77-
long long BitcoinAmount::toSatoshis(QString& amount, const Unit unit)
82+
QString BitcoinAmount::toSatoshis(const QString& text) const
7883
{
84+
if (m_unit == Unit::SAT) {
85+
return text;
86+
} else {
87+
return convert(text, m_unit);
88+
}
89+
}
7990

91+
long long BitcoinAmount::toSatoshis(QString& amount, const Unit unit)
92+
{
8093
int num_decimals = decimals(unit);
8194

8295
QStringList parts = amount.remove(' ').split(".");
@@ -93,7 +106,7 @@ long long BitcoinAmount::toSatoshis(QString& amount, const Unit unit)
93106
return str.toLongLong();
94107
}
95108

96-
QString BitcoinAmount::convert(const QString &amount, Unit unit)
109+
QString BitcoinAmount::convert(const QString& amount, Unit unit) const
97110
{
98111
if (amount == "") {
99112
return amount;
@@ -113,6 +126,10 @@ QString BitcoinAmount::convert(const QString &amount, Unit unit)
113126
result.append(QString(8 - numDigitsAfterDecimal, '0'));
114127
}
115128
result.remove(decimalPosition, 1);
129+
130+
while (result.startsWith('0') && result.length() > 1) {
131+
result.remove(0, 1);
132+
}
116133
} else if (unit == Unit::SAT) {
117134
result.remove(decimalPosition, 1);
118135
int newDecimalPosition = decimalPosition - 8;
@@ -121,6 +138,16 @@ QString BitcoinAmount::convert(const QString &amount, Unit unit)
121138
newDecimalPosition = 0;
122139
}
123140
result.insert(newDecimalPosition, ".");
141+
142+
while (result.endsWith('0') && result.contains('.')) {
143+
result.chop(1);
144+
}
145+
if (result.endsWith('.')) {
146+
result.chop(1);
147+
}
148+
if (result.startsWith('.')) {
149+
result.insert(0, "0");
150+
}
124151
}
125152

126153
return result;

src/qml/bitcoinamount.h

+7-3
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,18 @@
55
#ifndef BITCOIN_QML_BITCOINAMOUNT_H
66
#define BITCOIN_QML_BITCOINAMOUNT_H
77

8+
#include <consensus/amount.h>
9+
810
#include <QObject>
911
#include <QString>
10-
#include <qobjectdefs.h>
1112

1213
class BitcoinAmount : public QObject
1314
{
1415
Q_OBJECT
1516
Q_PROPERTY(Unit unit READ unit WRITE setUnit NOTIFY unitChanged)
1617
Q_PROPERTY(QString unitLabel READ unitLabel NOTIFY unitChanged)
1718
Q_PROPERTY(QString amount READ amount WRITE setAmount NOTIFY amountChanged)
19+
Q_PROPERTY(QString satoshiAmount READ satoshiAmount NOTIFY amountChanged)
1820

1921
public:
2022
enum class Unit {
@@ -30,10 +32,12 @@ class BitcoinAmount : public QObject
3032
QString unitLabel() const;
3133
QString amount() const;
3234
void setAmount(const QString& new_amount);
35+
QString satoshiAmount() const;
3336

3437
public Q_SLOTS:
35-
QString sanitize(const QString &text);
36-
QString convert(const QString &text, Unit unit);
38+
QString sanitize(const QString& text);
39+
QString convert(const QString& text, Unit unit) const;
40+
QString toSatoshis(const QString& text) const;
3741

3842
Q_SIGNALS:
3943
void unitChanged();

src/qml/imageprovider.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -206,5 +206,10 @@ QPixmap ImageProvider::requestPixmap(const QString& id, QSize* size, const QSize
206206
*size = requested_size;
207207
return QIcon(":/icons/plus").pixmap(requested_size);
208208
}
209+
210+
if (id == "flip-vertical") {
211+
*size = requested_size;
212+
return QIcon(":/icons/flip-vertical").pixmap(requested_size);
213+
}
209214
return {};
210215
}

src/qml/models/sendrecipient.cpp

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright (c) 2025 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+
5+
#include <qml/models/sendrecipient.h>
6+
#include <qobjectdefs.h>
7+
8+
SendRecipient::SendRecipient(QObject* parent)
9+
: QObject(parent), m_address(""), m_label(""), m_amount(""), m_message("")
10+
{
11+
}
12+
13+
QString SendRecipient::address() const
14+
{
15+
return m_address;
16+
}
17+
18+
void SendRecipient::setAddress(const QString& address)
19+
{
20+
if (m_address != address) {
21+
m_address = address;
22+
Q_EMIT addressChanged();
23+
}
24+
}
25+
26+
QString SendRecipient::label() const
27+
{
28+
return m_label;
29+
}
30+
31+
void SendRecipient::setLabel(const QString& label)
32+
{
33+
if (m_label != label) {
34+
m_label = label;
35+
Q_EMIT labelChanged();
36+
}
37+
}
38+
39+
QString SendRecipient::amount() const
40+
{
41+
return m_amount;
42+
}
43+
44+
void SendRecipient::setAmount(const QString& amount)
45+
{
46+
if (m_amount != amount) {
47+
m_amount = amount;
48+
Q_EMIT amountChanged();
49+
}
50+
}
51+
52+
QString SendRecipient::message() const
53+
{
54+
return m_message;
55+
}
56+
57+
void SendRecipient::setMessage(const QString& message)
58+
{
59+
if (m_message != message) {
60+
m_message = message;
61+
Q_EMIT messageChanged();
62+
}
63+
}
64+
65+
bool SendRecipient::subtractFeeFromAmount() const
66+
{
67+
return m_subtractFeeFromAmount;
68+
}
69+
70+
CAmount SendRecipient::cAmount() const
71+
{
72+
// TODO: Figure out who owns the parsing of SendRecipient::amount to CAmount
73+
return m_amount.toLongLong();
74+
}
75+
76+
void SendRecipient::clear()
77+
{
78+
m_address = "";
79+
m_label = "";
80+
m_amount = "";
81+
m_message = "";
82+
m_subtractFeeFromAmount = false;
83+
Q_EMIT addressChanged();
84+
Q_EMIT labelChanged();
85+
Q_EMIT amountChanged();
86+
Q_EMIT messageChanged();
87+
}

src/qml/models/sendrecipient.h

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright (c) 2025 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+
5+
#ifndef BITCOIN_QML_MODELS_SENDRECIPIENT_H
6+
#define BITCOIN_QML_MODELS_SENDRECIPIENT_H
7+
8+
#include <QObject>
9+
#include <QString>
10+
#include <qml/bitcoinamount.h>
11+
12+
class SendRecipient : public QObject
13+
{
14+
Q_OBJECT
15+
Q_PROPERTY(QString address READ address WRITE setAddress NOTIFY addressChanged)
16+
Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY labelChanged)
17+
Q_PROPERTY(QString amount READ amount WRITE setAmount NOTIFY amountChanged)
18+
Q_PROPERTY(QString message READ message WRITE setMessage NOTIFY messageChanged)
19+
20+
public:
21+
explicit SendRecipient(QObject* parent = nullptr);
22+
23+
QString address() const;
24+
void setAddress(const QString& address);
25+
26+
QString label() const;
27+
void setLabel(const QString& label);
28+
29+
QString amount() const;
30+
void setAmount(const QString& amount);
31+
32+
QString message() const;
33+
void setMessage(const QString& message);
34+
35+
CAmount cAmount() const;
36+
37+
bool subtractFeeFromAmount() const;
38+
39+
Q_INVOKABLE void clear();
40+
41+
Q_SIGNALS:
42+
void addressChanged();
43+
void labelChanged();
44+
void amountChanged();
45+
void messageChanged();
46+
47+
private:
48+
QString m_address;
49+
QString m_label;
50+
QString m_amount;
51+
QString m_message;
52+
bool m_subtractFeeFromAmount{false};
53+
};
54+
55+
#endif // BITCOIN_QML_MODELS_SENDRECIPIENT_H

0 commit comments

Comments
 (0)