Skip to content

Commit 44276f8

Browse files
committed
qml: Add CoinsListModel to WalletQmlModel
The coins list model will be used to list out of the locked and selectable coins in the coin selection form. A toggle method is provided that will select or unselect the coin in the associated WalletQmlModel.
1 parent 99d6fb7 commit 44276f8

File tree

6 files changed

+298
-16
lines changed

6 files changed

+298
-16
lines changed

src/Makefile.qt.include

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ QT_MOC_CPP = \
3939
qml/controls/moc_linegraph.cpp \
4040
qml/models/moc_activitylistmodel.cpp \
4141
qml/models/moc_chainmodel.cpp \
42+
qml/models/moc_coinslistmodel.cpp \
4243
qml/models/moc_networktraffictower.cpp \
4344
qml/models/moc_nodemodel.cpp \
4445
qml/models/moc_options_model.cpp \
@@ -129,6 +130,7 @@ BITCOIN_QT_H = \
129130
qml/controls/linegraph.h \
130131
qml/models/activitylistmodel.h \
131132
qml/models/chainmodel.h \
133+
qml/models/coinslistmodel.h \
132134
qml/models/networktraffictower.h \
133135
qml/models/nodemodel.h \
134136
qml/models/options_model.h \
@@ -328,6 +330,7 @@ BITCOIN_QML_BASE_CPP = \
328330
qml/controls/linegraph.cpp \
329331
qml/models/activitylistmodel.cpp \
330332
qml/models/chainmodel.cpp \
333+
qml/models/coinslistmodel.cpp \
331334
qml/models/networktraffictower.cpp \
332335
qml/models/nodemodel.cpp \
333336
qml/models/options_model.cpp \

src/qml/models/coinslistmodel.cpp

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
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/coinslistmodel.h>
6+
7+
#include <consensus/amount.h>
8+
#include <interfaces/wallet.h>
9+
#include <key_io.h>
10+
#include <primitives/transaction.h>
11+
#include <qml/models/walletqmlmodel.h>
12+
#include <qt/bitcoinunits.h>
13+
#include <vector>
14+
15+
CoinsListModel::CoinsListModel(WalletQmlModel* parent)
16+
: QAbstractListModel(parent), m_wallet_model(parent)
17+
{
18+
}
19+
20+
CoinsListModel::~CoinsListModel() = default;
21+
22+
int CoinsListModel::rowCount(const QModelIndex& parent) const
23+
{
24+
return m_coins.size();
25+
}
26+
27+
QVariant CoinsListModel::data(const QModelIndex& index, int role) const
28+
{
29+
if (!index.isValid() || index.row() < 0 || index.row() >= static_cast<int>(m_coins.size()))
30+
return QVariant();
31+
32+
const auto& [destination, outpoint, coin] = m_coins.at(index.row());
33+
switch (role) {
34+
case AddressRole:
35+
return QString::fromStdString(EncodeDestination(destination));
36+
case AmountRole:
37+
return BitcoinUnits::format(BitcoinUnits::Unit::BTC, coin.txout.nValue);
38+
case LabelRole:
39+
return QString::fromStdString("");
40+
case LockedRole:
41+
return m_wallet_model->isLockedCoin(outpoint);
42+
case SelectedRole:
43+
return m_wallet_model->isSelectedCoin(outpoint);
44+
default:
45+
return QVariant();
46+
}
47+
}
48+
49+
QHash<int, QByteArray> CoinsListModel::roleNames() const
50+
{
51+
QHash<int, QByteArray> roles;
52+
roles[AmountRole] = "amount";
53+
roles[AddressRole] = "address";
54+
roles[DateTimeRole] = "date";
55+
roles[LabelRole] = "label";
56+
roles[LockedRole] = "locked";
57+
roles[SelectedRole] = "selected";
58+
return roles;
59+
}
60+
61+
void CoinsListModel::update()
62+
{
63+
if (m_wallet_model == nullptr) {
64+
return;
65+
}
66+
auto coins_map = m_wallet_model->listCoins();
67+
beginResetModel();
68+
m_coins.clear();
69+
for (const auto& [destination, vec] : coins_map) {
70+
for (const auto& [outpoint, tx_out] : vec) {
71+
auto tuple = std::make_tuple(destination, outpoint, tx_out);
72+
m_coins.push_back(tuple);
73+
}
74+
}
75+
endResetModel();
76+
}
77+
78+
void CoinsListModel::setSortBy(const QString& roleName)
79+
{
80+
if (m_sort_by != roleName) {
81+
m_sort_by = roleName;
82+
// sort(RoleNameToIndex(roleName));
83+
Q_EMIT sortByChanged(roleName);
84+
}
85+
}
86+
87+
void CoinsListModel::toggleCoinSelection(const int index)
88+
{
89+
if (index < 0 || index >= static_cast<int>(m_coins.size())) {
90+
return;
91+
}
92+
const auto& [destination, outpoint, coin] = m_coins.at(index);
93+
94+
if (m_wallet_model->isSelectedCoin(outpoint)) {
95+
m_wallet_model->unselectCoin(outpoint);
96+
m_total_amount -= coin.txout.nValue;
97+
} else {
98+
m_wallet_model->selectCoin(outpoint);
99+
m_total_amount += coin.txout.nValue;
100+
}
101+
Q_EMIT selectedCoinsCountChanged();
102+
}
103+
104+
unsigned int CoinsListModel::lockedCoinsCount() const
105+
{
106+
std::vector<COutPoint> lockedCoins;
107+
m_wallet_model->listLockedCoins(lockedCoins);
108+
return lockedCoins.size();
109+
}
110+
111+
unsigned int CoinsListModel::selectedCoinsCount() const
112+
{
113+
return m_wallet_model->listSelectedCoins().size();
114+
}
115+
116+
QString CoinsListModel::totalSelected() const
117+
{
118+
return BitcoinUnits::format(BitcoinUnits::Unit::BTC, m_total_amount);
119+
}
120+
121+
QString CoinsListModel::changeAmount() const
122+
{
123+
CAmount change = m_total_amount - m_wallet_model->sendRecipient()->cAmount();
124+
change = std::abs(change);
125+
return BitcoinUnits::format(BitcoinUnits::Unit::BTC, change);
126+
}
127+
128+
bool CoinsListModel::overRequiredAmount() const
129+
{
130+
return m_total_amount > m_wallet_model->sendRecipient()->cAmount();
131+
}

src/qml/models/coinslistmodel.h

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
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_COINSLISTMODEL_H
6+
#define BITCOIN_QML_MODELS_COINSLISTMODEL_H
7+
8+
#include <consensus/amount.h>
9+
#include <interfaces/handler.h>
10+
#include <interfaces/wallet.h>
11+
#include <qml/models/transaction.h>
12+
13+
#include <QAbstractListModel>
14+
#include <QString>
15+
16+
class WalletQmlModel;
17+
18+
class CoinsListModel : public QAbstractListModel
19+
{
20+
Q_OBJECT
21+
Q_PROPERTY(int lockedCoinsCount READ lockedCoinsCount NOTIFY lockedCoinsCountChanged)
22+
Q_PROPERTY(int selectedCoinsCount READ selectedCoinsCount NOTIFY selectedCoinsCountChanged)
23+
Q_PROPERTY(QString totalSelected READ totalSelected NOTIFY selectedCoinsCountChanged)
24+
Q_PROPERTY(QString changeAmount READ changeAmount NOTIFY selectedCoinsCountChanged)
25+
Q_PROPERTY(bool overRequiredAmount READ overRequiredAmount NOTIFY selectedCoinsCountChanged)
26+
27+
public:
28+
explicit CoinsListModel(WalletQmlModel* parent = nullptr);
29+
~CoinsListModel();
30+
31+
enum CoinsRoles {
32+
AddressRole = Qt::UserRole + 1,
33+
AmountRole,
34+
DateTimeRole,
35+
LabelRole,
36+
LockedRole,
37+
SelectedRole
38+
};
39+
40+
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
41+
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
42+
QHash<int, QByteArray> roleNames() const override;
43+
44+
public Q_SLOTS:
45+
void update();
46+
void setSortBy(const QString& roleName);
47+
void toggleCoinSelection(const int index);
48+
unsigned int lockedCoinsCount() const;
49+
unsigned int selectedCoinsCount() const;
50+
QString totalSelected() const;
51+
QString changeAmount() const;
52+
bool overRequiredAmount() const;
53+
54+
Q_SIGNALS:
55+
void sortByChanged(const QString& roleName);
56+
void lockedCoinsCountChanged();
57+
void selectedCoinsCountChanged();
58+
59+
private:
60+
WalletQmlModel* m_wallet_model;
61+
std::unique_ptr<interfaces::Handler> m_handler_transaction_changed;
62+
std::vector<std::tuple<CTxDestination, COutPoint, interfaces::WalletTxOut>> m_coins;
63+
QString m_sort_by;
64+
CAmount m_total_amount;
65+
};
66+
67+
#endif // BITCOIN_QML_MODELS_COINSLISTMODEL_H

src/qml/models/walletqmlmodel.cpp

Lines changed: 78 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@
44

55
#include <qml/models/walletqmlmodel.h>
66

7-
#include <qml/models/activitylistmodel.h>
8-
9-
#include <qml/models/sendrecipient.h>
10-
#include <qml/models/walletqmlmodeltransaction.h>
11-
127
#include <consensus/amount.h>
8+
#include <interfaces/wallet.h>
139
#include <key_io.h>
1410
#include <outputtype.h>
11+
#include <qml/models/activitylistmodel.h>
12+
#include <qml/models/sendrecipient.h>
13+
#include <qml/models/walletqmlmodeltransaction.h>
1514
#include <qt/bitcoinunits.h>
1615
#include <wallet/coincontrol.h>
1716
#include <wallet/wallet.h>
@@ -23,16 +22,28 @@ WalletQmlModel::WalletQmlModel(std::unique_ptr<interfaces::Wallet> wallet, QObje
2322
{
2423
m_wallet = std::move(wallet);
2524
m_activity_list_model = new ActivityListModel(this);
25+
m_coins_list_model = new CoinsListModel(this);
2626
m_current_recipient = new SendRecipient(this);
2727
}
2828

2929
WalletQmlModel::WalletQmlModel(QObject* parent)
3030
: QObject(parent)
3131
{
3232
m_activity_list_model = new ActivityListModel(this);
33+
m_coins_list_model = new CoinsListModel(this);
3334
m_current_recipient = new SendRecipient(this);
3435
}
3536

37+
WalletQmlModel::~WalletQmlModel()
38+
{
39+
delete m_activity_list_model;
40+
delete m_coins_list_model;
41+
delete m_current_recipient;
42+
if (m_current_transaction) {
43+
delete m_current_transaction;
44+
}
45+
}
46+
3647
QString WalletQmlModel::balance() const
3748
{
3849
if (!m_wallet) {
@@ -76,11 +87,6 @@ bool WalletQmlModel::tryGetTxStatus(const uint256& txid,
7687
return m_wallet->tryGetTxStatus(txid, tx_status, num_blocks, block_time);
7788
}
7889

79-
WalletQmlModel::~WalletQmlModel()
80-
{
81-
delete m_activity_list_model;
82-
}
83-
8490
std::unique_ptr<interfaces::Handler> WalletQmlModel::handleTransactionChanged(TransactionChangedFn fn)
8591
{
8692
if (!m_wallet) {
@@ -97,8 +103,7 @@ bool WalletQmlModel::prepareTransaction()
97103

98104
CScript scriptPubKey = GetScriptForDestination(DecodeDestination(m_current_recipient->address().toStdString()));
99105
wallet::CRecipient recipient = {scriptPubKey, m_current_recipient->cAmount(), m_current_recipient->subtractFeeFromAmount()};
100-
wallet::CCoinControl coinControl;
101-
coinControl.m_feerate = CFeeRate(1000);
106+
m_coin_control.m_feerate = CFeeRate(1000);
102107

103108
CAmount balance = m_wallet->getBalance();
104109
if (balance < recipient.nAmount) {
@@ -108,7 +113,7 @@ bool WalletQmlModel::prepareTransaction()
108113
std::vector<wallet::CRecipient> vecSend{recipient};
109114
int nChangePosRet = -1;
110115
CAmount nFeeRequired = 0;
111-
const auto& res = m_wallet->createTransaction(vecSend, coinControl, true, nChangePosRet, nFeeRequired);
116+
const auto& res = m_wallet->createTransaction(vecSend, m_coin_control, true, nChangePosRet, nFeeRequired);
112117
if (res) {
113118
if (m_current_transaction) {
114119
delete m_current_transaction;
@@ -139,3 +144,63 @@ void WalletQmlModel::sendTransaction()
139144
interfaces::WalletOrderForm order_form;
140145
m_wallet->commitTransaction(newTx, value_map, order_form);
141146
}
147+
148+
interfaces::Wallet::CoinsList WalletQmlModel::listCoins() const
149+
{
150+
if (!m_wallet) {
151+
return {};
152+
}
153+
return m_wallet->listCoins();
154+
}
155+
156+
bool WalletQmlModel::lockCoin(const COutPoint& output)
157+
{
158+
if (!m_wallet) {
159+
return false;
160+
}
161+
return m_wallet->lockCoin(output, true);
162+
}
163+
164+
bool WalletQmlModel::unlockCoin(const COutPoint& output)
165+
{
166+
if (!m_wallet) {
167+
return false;
168+
}
169+
return m_wallet->unlockCoin(output);
170+
}
171+
172+
bool WalletQmlModel::isLockedCoin(const COutPoint& output)
173+
{
174+
if (!m_wallet) {
175+
return false;
176+
}
177+
return m_wallet->isLockedCoin(output);
178+
}
179+
180+
void WalletQmlModel::listLockedCoins(std::vector<COutPoint>& outputs)
181+
{
182+
if (!m_wallet) {
183+
return;
184+
}
185+
m_wallet->listLockedCoins(outputs);
186+
}
187+
188+
void WalletQmlModel::selectCoin(const COutPoint& output)
189+
{
190+
m_coin_control.Select(output);
191+
}
192+
193+
void WalletQmlModel::unselectCoin(const COutPoint& output)
194+
{
195+
m_coin_control.UnSelect(output);
196+
}
197+
198+
bool WalletQmlModel::isSelectedCoin(const COutPoint& output)
199+
{
200+
return m_coin_control.IsSelected(output);
201+
}
202+
203+
std::vector<COutPoint> WalletQmlModel::listSelectedCoins() const
204+
{
205+
return m_coin_control.ListSelected();
206+
}

0 commit comments

Comments
 (0)