Skip to content

Commit 411a08a

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 ddb9f1a commit 411a08a

11 files changed

+282
-2
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
qt/moc_addressbookpage.cpp \
4748
qt/moc_addresstablemodel.cpp \
@@ -121,6 +122,7 @@ BITCOIN_QT_H = \
121122
qml/models/nodemodel.h \
122123
qml/models/options_model.h \
123124
qml/models/peerlistsortproxy.h \
125+
qml/models/walletlistmodel.h \
124126
qml/appmode.h \
125127
qml/bitcoin.h \
126128
qml/guiconstants.h \
@@ -309,6 +311,7 @@ BITCOIN_QML_BASE_CPP = \
309311
qml/models/nodemodel.cpp \
310312
qml/models/options_model.cpp \
311313
qml/models/peerlistsortproxy.cpp \
314+
qml/models/walletlistmodel.cpp \
312315
qml/imageprovider.cpp \
313316
qml/util.cpp
314317

@@ -334,6 +337,7 @@ QML_RES_ICONS = \
334337
qml/res/icons/info.png \
335338
qml/res/icons/network-dark.png \
336339
qml/res/icons/network-light.png \
340+
qml/res/icons/plus.png \
337341
qml/res/icons/shutdown.png \
338342
qml/res/icons/singlesig-wallet.png \
339343
qml/res/icons/storage-dark.png \
@@ -410,7 +414,8 @@ QML_RES_QML = \
410414
qml/pages/settings/SettingsStorage.qml \
411415
qml/pages/settings/SettingsTheme.qml \
412416
qml/pages/wallet/DesktopWallets.qml \
413-
qml/pages/wallet/WalletBadge.qml
417+
qml/pages/wallet/WalletBadge.qml \
418+
qml/pages/wallet/WalletSelect.qml
414419

415420
if TARGET_ANDROID
416421
BITCOIN_QT_H += qml/androidnotifier.h

src/qml/bitcoin.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include <qml/models/nodemodel.h>
2626
#include <qml/models/options_model.h>
2727
#include <qml/models/peerlistsortproxy.h>
28+
#include <qml/models/walletlistmodel.h>
2829
#include <qml/imageprovider.h>
2930
#include <qml/util.h>
3031
#include <qml/guiconstants.h>
@@ -292,11 +293,14 @@ int QmlGuiMain(int argc, char* argv[])
292293
assert(!network_style.isNull());
293294
engine.addImageProvider(QStringLiteral("images"), new ImageProvider{network_style.data()});
294295

296+
WalletListModel wallet_list_model{*node, nullptr};
297+
295298
engine.rootContext()->setContextProperty("networkTrafficTower", &network_traffic_tower);
296299
engine.rootContext()->setContextProperty("nodeModel", &node_model);
297300
engine.rootContext()->setContextProperty("chainModel", &chain_model);
298301
engine.rootContext()->setContextProperty("peerTableModel", &peer_model);
299302
engine.rootContext()->setContextProperty("peerListModelProxy", &peer_model_sort_proxy);
303+
engine.rootContext()->setContextProperty("walletListModel", &wallet_list_model);
300304

301305
OptionsQmlModel options_model(*node, !need_onboarding.toBool());
302306
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
@@ -67,6 +67,7 @@
6767
<file>pages/settings/SettingsTheme.qml</file>
6868
<file>pages/wallet/DesktopWallets.qml</file>
6969
<file>pages/wallet/WalletBadge.qml</file>
70+
<file>pages/wallet/WalletSelect.qml</file>
7071
</qresource>
7172
<qresource prefix="/icons">
7273
<file alias="arrow-down">res/icons/arrow-down.png</file>
@@ -87,6 +88,7 @@
8788
<file alias="info">res/icons/info.png</file>
8889
<file alias="network-dark">res/icons/network-dark.png</file>
8990
<file alias="network-light">res/icons/network-light.png</file>
91+
<file alias="plus">res/icons/plus.png</file>
9092
<file alias="shutdown">res/icons/shutdown.png</file>
9193
<file alias="singlesig-wallet">res/icons/singlesig-wallet.png</file>
9294
<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
@@ -142,5 +142,9 @@ QPixmap ImageProvider::requestPixmap(const QString& id, QSize* size, const QSize
142142
return QIcon(":/icons/tooltip-arrow-light").pixmap(requested_size);
143143
}
144144

145+
if (id == "plus") {
146+
*size = requested_size;
147+
return QIcon(":/icons/plus").pixmap(requested_size);
148+
}
145149
return {};
146150
}

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: 164
2626
implicitHeight: 60
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 - 5
43+
}
2844
}
2945
centerItem: RowLayout {
3046
NavigationTab {

src/qml/pages/wallet/WalletSelect.qml

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

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)