Skip to content

Commit 59d59b5

Browse files
committed
qml: Introduce the WalletSelect component
WalletSelect is a Popup that appears after clicking the main WalletBadge in the DesktopNavigation bar. It contains a ListView that allows the user to select one of the wallets listed in the wallet directory.
1 parent 0cf5bcf commit 59d59b5

12 files changed

+292
-3
lines changed

src/Makefile.qt.include

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ QT_MOC_CPP = \
4242
qml/models/moc_nodemodel.cpp \
4343
qml/models/moc_options_model.cpp \
4444
qml/models/moc_peerlistsortproxy.cpp \
45+
qml/models/moc_walletlistmodel.cpp \
4546
qml/moc_appmode.cpp \
4647
qml/moc_walletcontroller.cpp \
4748
qt/moc_addressbookpage.cpp \
@@ -122,6 +123,7 @@ BITCOIN_QT_H = \
122123
qml/models/nodemodel.h \
123124
qml/models/options_model.h \
124125
qml/models/peerlistsortproxy.h \
126+
qml/models/walletlistmodel.h \
125127
qml/appmode.h \
126128
qml/bitcoin.h \
127129
qml/guiconstants.h \
@@ -311,6 +313,7 @@ BITCOIN_QML_BASE_CPP = \
311313
qml/models/nodemodel.cpp \
312314
qml/models/options_model.cpp \
313315
qml/models/peerlistsortproxy.cpp \
316+
qml/models/walletlistmodel.cpp \
314317
qml/imageprovider.cpp \
315318
qml/util.cpp \
316319
qml/walletcontroller.cpp
@@ -339,6 +342,7 @@ QML_RES_ICONS = \
339342
qml/res/icons/info.png \
340343
qml/res/icons/network-dark.png \
341344
qml/res/icons/network-light.png \
345+
qml/res/icons/plus.png \
342346
qml/res/icons/shutdown.png \
343347
qml/res/icons/singlesig-wallet.png \
344348
qml/res/icons/storage-dark.png \
@@ -424,7 +428,8 @@ QML_RES_QML = \
424428
qml/pages/wallet/CreateName.qml \
425429
qml/pages/wallet/CreatePassword.qml \
426430
qml/pages/wallet/DesktopWallets.qml \
427-
qml/pages/wallet/WalletBadge.qml
431+
qml/pages/wallet/WalletBadge.qml \
432+
qml/pages/wallet/WalletSelect.qml
428433

429434
if TARGET_ANDROID
430435
BITCOIN_QT_H += qml/androidnotifier.h

src/qml/bitcoin.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <qml/models/nodemodel.h>
2727
#include <qml/models/options_model.h>
2828
#include <qml/models/peerlistsortproxy.h>
29+
#include <qml/models/walletlistmodel.h>
2930
#include <qml/imageprovider.h>
3031
#include <qml/util.h>
3132
#include <qml/walletcontroller.h>
@@ -295,12 +296,15 @@ int QmlGuiMain(int argc, char* argv[])
295296
assert(!network_style.isNull());
296297
engine.addImageProvider(QStringLiteral("images"), new ImageProvider{network_style.data()});
297298

299+
WalletListModel wallet_list_model{*node, nullptr};
300+
298301
engine.rootContext()->setContextProperty("networkTrafficTower", &network_traffic_tower);
299302
engine.rootContext()->setContextProperty("nodeModel", &node_model);
300303
engine.rootContext()->setContextProperty("chainModel", &chain_model);
301304
engine.rootContext()->setContextProperty("peerTableModel", &peer_model);
302305
engine.rootContext()->setContextProperty("peerListModelProxy", &peer_model_sort_proxy);
303306
engine.rootContext()->setContextProperty("walletController", &wallet_controller);
307+
engine.rootContext()->setContextProperty("walletListModel", &wallet_list_model);
304308

305309
OptionsQmlModel options_model(*node, !need_onboarding.toBool());
306310
engine.rootContext()->setContextProperty("optionsModel", &options_model);

src/qml/bitcoin_qml.qrc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
<file>pages/wallet/CreatePassword.qml</file>
7575
<file>pages/wallet/DesktopWallets.qml</file>
7676
<file>pages/wallet/WalletBadge.qml</file>
77+
<file>pages/wallet/WalletSelect.qml</file>
7778
</qresource>
7879
<qresource prefix="/icons">
7980
<file alias="add-wallet-dark">res/icons/add-wallet-dark.png</file>
@@ -96,6 +97,7 @@
9697
<file alias="info">res/icons/info.png</file>
9798
<file alias="network-dark">res/icons/network-dark.png</file>
9899
<file alias="network-light">res/icons/network-light.png</file>
100+
<file alias="plus">res/icons/plus.png</file>
99101
<file alias="shutdown">res/icons/shutdown.png</file>
100102
<file alias="singlesig-wallet">res/icons/singlesig-wallet.png</file>
101103
<file alias="storage-dark">res/icons/storage-dark.png</file>

src/qml/controls/Icon.qml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import QtQuick.Controls 2.15
77

88
Button {
99
id: root
10+
width: icon.width
11+
height: icon.height
1012
required property color color
1113
required property url source
1214
property int size: 32

src/qml/imageprovider.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,5 +162,9 @@ QPixmap ImageProvider::requestPixmap(const QString& id, QSize* size, const QSize
162162
return QIcon(":/icons/hidden").pixmap(requested_size);
163163
}
164164

165+
if (id == "plus") {
166+
*size = requested_size;
167+
return QIcon(":/icons/plus").pixmap(requested_size);
168+
}
165169
return {};
166170
}

src/qml/models/walletlistmodel.cpp

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Copyright (c) 2024 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/walletlistmodel.h>
6+
7+
#include <interfaces/node.h>
8+
9+
#include <QSet>
10+
11+
WalletListModel::WalletListModel(interfaces::Node& node, QObject *parent)
12+
: QAbstractListModel(parent)
13+
, m_node(node)
14+
{
15+
setSelectedWallet("Singlesig Wallet");
16+
}
17+
18+
void WalletListModel::listWalletDir()
19+
{
20+
QSet<QString> existing_names;
21+
for (int i = 0; i < rowCount(); ++i) {
22+
QModelIndex index = this->index(i, 0);
23+
QString name = data(index, NameRole).toString();
24+
existing_names.insert(name);
25+
}
26+
27+
for (const std::string &name : m_node.walletLoader().listWalletDir()) {
28+
QString qname = QString::fromStdString(name);
29+
if (!existing_names.contains(qname)) {
30+
addItem({ qname });
31+
}
32+
}
33+
}
34+
35+
void WalletListModel::setSelectedWallet(QString wallet_name)
36+
{
37+
if (m_selected_wallet != wallet_name) {
38+
m_selected_wallet = wallet_name;
39+
Q_EMIT selectedWalletChanged();
40+
}
41+
}
42+
43+
QString WalletListModel::selectedWallet() const
44+
{
45+
return m_selected_wallet;
46+
}
47+
48+
int WalletListModel::rowCount(const QModelIndex &parent) const
49+
{
50+
Q_UNUSED(parent);
51+
return m_items.size();
52+
}
53+
54+
QVariant WalletListModel::data(const QModelIndex &index, int role) const
55+
{
56+
if (!index.isValid() || index.row() < 0 || index.row() >= m_items.size())
57+
return QVariant();
58+
59+
const auto &item = m_items[index.row()];
60+
switch (role) {
61+
case Qt::DisplayRole:
62+
case NameRole:
63+
return item.name;
64+
default:
65+
return QVariant();
66+
}
67+
}
68+
69+
QHash<int, QByteArray> WalletListModel::roleNames() const
70+
{
71+
QHash<int, QByteArray> roles;
72+
roles[NameRole] = "name";
73+
return roles;
74+
}
75+
76+
void WalletListModel::addItem(const Item &item)
77+
{
78+
beginInsertRows(QModelIndex(), rowCount(), rowCount());
79+
m_items.append(item);
80+
endInsertRows();
81+
}

src/qml/models/walletlistmodel.h

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
// Copyright (c) 2024 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_WALLETLISTMODEL_H
6+
#define BITCOIN_QML_MODELS_WALLETLISTMODEL_H
7+
8+
#include <interfaces/wallet.h>
9+
#include <QAbstractListModel>
10+
#include <QList>
11+
12+
namespace interfaces {
13+
class Node;
14+
}
15+
16+
class WalletListModel : public QAbstractListModel
17+
{
18+
Q_OBJECT
19+
Q_PROPERTY(QString selectedWallet READ selectedWallet WRITE setSelectedWallet NOTIFY selectedWalletChanged)
20+
21+
public:
22+
WalletListModel(interfaces::Node& node, QObject *parent = nullptr);
23+
~WalletListModel() = default;
24+
25+
enum Roles {
26+
NameRole = Qt::UserRole + 1
27+
};
28+
29+
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
30+
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
31+
QHash<int, QByteArray> roleNames() const override;
32+
33+
void setSelectedWallet(QString wallet_name);
34+
QString selectedWallet() const;
35+
36+
public Q_SLOTS:
37+
void listWalletDir();
38+
39+
Q_SIGNALS:
40+
void selectedWalletChanged();
41+
42+
private:
43+
struct Item {
44+
QString name;
45+
};
46+
47+
void addItem(const Item &item);
48+
49+
QList<Item> m_items;
50+
interfaces::Node& m_node;
51+
QString m_selected_wallet;
52+
53+
};
54+
55+
#endif // BITCOIN_QML_MODELS_WALLETLISTMODEL_H

src/qml/pages/wallet/DesktopWallets.qml

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,23 @@ Page {
2424
leftItem: WalletBadge {
2525
implicitWidth: 154
2626
implicitHeight: 46
27-
text: qsTr("Singlesig Wallet")
27+
text: walletListModel.selectedWallet
28+
29+
MouseArea {
30+
anchors.fill: parent
31+
onClicked: {
32+
walletListModel.listWalletDir()
33+
walletSelect.opened ? walletSelect.close() : walletSelect.open()
34+
}
35+
}
36+
37+
WalletSelect {
38+
id: walletSelect
39+
model: walletListModel
40+
closePolicy: Popup.CloseOnPressOutside
41+
x: 0
42+
y: parent.height
43+
}
2844
}
2945
centerItem: RowLayout {
3046
NavigationTab {

src/qml/pages/wallet/WalletBadge.qml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,10 @@ Button {
105105
visible: root.showIcon
106106
source: "image://images/singlesig-wallet"
107107
color: Theme.color.neutral8
108+
size: 30
109+
Layout.minimumWidth: 30
108110
Layout.preferredWidth: 30
109-
Layout.preferredHeight: 30
111+
Layout.maximumWidth: 30
110112
}
111113
ColumnLayout {
112114
spacing: 2

src/qml/pages/wallet/WalletSelect.qml

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// Copyright (c) 2024 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+
import QtQuick 2.15
6+
import QtQuick.Controls 2.15
7+
import QtQuick.Layouts 1.15
8+
9+
import "../../controls"
10+
11+
Popup {
12+
id: root
13+
14+
property alias model: listView.model
15+
implicitHeight: layout.height + arrow.height
16+
implicitWidth: 250
17+
clip: true
18+
19+
background: Item {
20+
anchors.fill: parent
21+
Rectangle {
22+
id: tooltipBg
23+
color: Theme.color.neutral0
24+
border.color: Theme.color.neutral4
25+
radius: 5
26+
border.width: 1
27+
width: parent.width
28+
height: parent.height - arrow.height - 1
29+
anchors.top: arrow.bottom
30+
anchors.horizontalCenter: root.horizontalCenter
31+
anchors.topMargin: -1
32+
}
33+
Image {
34+
id: arrow
35+
source: Theme.image.tooltipArrow
36+
width: 22
37+
height: 10
38+
anchors.left: parent.left
39+
anchors.leftMargin: 10
40+
anchors.top: parent.top
41+
}
42+
}
43+
44+
ButtonGroup {
45+
id: buttonGroup
46+
}
47+
48+
ColumnLayout {
49+
id: layout
50+
width: 220
51+
anchors.topMargin: arrow.height
52+
CoreText {
53+
Layout.alignment: Qt.AlignHCenter
54+
Layout.preferredWidth: 220
55+
Layout.preferredHeight: 30
56+
id: label
57+
text: qsTr("Wallets")
58+
visible: listView.count > 0
59+
bold: true
60+
color: Theme.color.neutral9
61+
font.pixelSize: 14
62+
topPadding: 10
63+
bottomPadding: 5
64+
}
65+
66+
ListView {
67+
Layout.preferredWidth: 220
68+
Layout.preferredHeight: Math.min(listView.count * 34, 300)
69+
id: listView
70+
interactive: true
71+
spacing: 2
72+
model: walletListModel
73+
74+
delegate: WalletBadge {
75+
required property string name;
76+
77+
width: 220
78+
height: 32
79+
text: name
80+
ButtonGroup.group: buttonGroup
81+
showBalance: false
82+
showIcon: false
83+
onClicked: {
84+
walletListModel.selectedWallet = name
85+
root.close()
86+
}
87+
}
88+
}
89+
90+
RowLayout {
91+
id: addWallet
92+
Layout.preferredWidth: addIcon.size + addText.width
93+
Layout.preferredHeight: 45
94+
Layout.alignment: Qt.AlignHCenter
95+
Icon {
96+
id: addIcon
97+
Layout.alignment: Qt.AlignHCenter
98+
source: "image://images/plus"
99+
color: Theme.color.neutral8
100+
size: 14
101+
topPadding: 5
102+
bottomPadding: 10
103+
}
104+
CoreText {
105+
id: addText
106+
Layout.alignment: Qt.AlignHCenter
107+
text: qsTr("Add Wallet")
108+
color: Theme.color.neutral9
109+
font.pixelSize: 15
110+
topPadding: 5
111+
bottomPadding: 10
112+
}
113+
}
114+
}
115+
}

src/qml/res/icons/plus.png

278 Bytes
Loading

src/qml/res/src/plus.svg

Lines changed: 3 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)