Skip to content

Commit

Permalink
qml: Introduce the WalletSelect component
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
johnny9 committed Aug 9, 2024
1 parent 5906e69 commit 0939f2b
Show file tree
Hide file tree
Showing 12 changed files with 292 additions and 3 deletions.
7 changes: 6 additions & 1 deletion src/Makefile.qt.include
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ QT_MOC_CPP = \
qml/models/moc_nodemodel.cpp \
qml/models/moc_options_model.cpp \
qml/models/moc_peerlistsortproxy.cpp \
qml/models/moc_walletlistmodel.cpp \
qml/moc_appmode.cpp \
qml/moc_walletcontroller.cpp \
qt/moc_addressbookpage.cpp \
Expand Down Expand Up @@ -122,6 +123,7 @@ BITCOIN_QT_H = \
qml/models/nodemodel.h \
qml/models/options_model.h \
qml/models/peerlistsortproxy.h \
qml/models/walletlistmodel.h \
qml/appmode.h \
qml/bitcoin.h \
qml/guiconstants.h \
Expand Down Expand Up @@ -311,6 +313,7 @@ BITCOIN_QML_BASE_CPP = \
qml/models/nodemodel.cpp \
qml/models/options_model.cpp \
qml/models/peerlistsortproxy.cpp \
qml/models/walletlistmodel.cpp \
qml/imageprovider.cpp \
qml/util.cpp \
qml/walletcontroller.cpp
Expand Down Expand Up @@ -339,6 +342,7 @@ QML_RES_ICONS = \
qml/res/icons/info.png \
qml/res/icons/network-dark.png \
qml/res/icons/network-light.png \
qml/res/icons/plus.png \
qml/res/icons/shutdown.png \
qml/res/icons/singlesig-wallet.png \
qml/res/icons/storage-dark.png \
Expand Down Expand Up @@ -424,7 +428,8 @@ QML_RES_QML = \
qml/pages/wallet/CreateName.qml \
qml/pages/wallet/CreatePassword.qml \
qml/pages/wallet/DesktopWallets.qml \
qml/pages/wallet/WalletBadge.qml
qml/pages/wallet/WalletBadge.qml \
qml/pages/wallet/WalletSelect.qml

if TARGET_ANDROID
BITCOIN_QT_H += qml/androidnotifier.h
Expand Down
4 changes: 4 additions & 0 deletions src/qml/bitcoin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include <qml/models/nodemodel.h>
#include <qml/models/options_model.h>
#include <qml/models/peerlistsortproxy.h>
#include <qml/models/walletlistmodel.h>
#include <qml/imageprovider.h>
#include <qml/util.h>
#include <qml/walletcontroller.h>
Expand Down Expand Up @@ -295,12 +296,15 @@ int QmlGuiMain(int argc, char* argv[])
assert(!network_style.isNull());
engine.addImageProvider(QStringLiteral("images"), new ImageProvider{network_style.data()});

WalletListModel wallet_list_model{*node, nullptr};

engine.rootContext()->setContextProperty("networkTrafficTower", &network_traffic_tower);
engine.rootContext()->setContextProperty("nodeModel", &node_model);
engine.rootContext()->setContextProperty("chainModel", &chain_model);
engine.rootContext()->setContextProperty("peerTableModel", &peer_model);
engine.rootContext()->setContextProperty("peerListModelProxy", &peer_model_sort_proxy);
engine.rootContext()->setContextProperty("walletController", &wallet_controller);
engine.rootContext()->setContextProperty("walletListModel", &wallet_list_model);

OptionsQmlModel options_model(*node, !need_onboarding.toBool());
engine.rootContext()->setContextProperty("optionsModel", &options_model);
Expand Down
2 changes: 2 additions & 0 deletions src/qml/bitcoin_qml.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
<file>pages/wallet/CreatePassword.qml</file>
<file>pages/wallet/DesktopWallets.qml</file>
<file>pages/wallet/WalletBadge.qml</file>
<file>pages/wallet/WalletSelect.qml</file>
</qresource>
<qresource prefix="/icons">
<file alias="add-wallet-dark">res/icons/add-wallet-dark.png</file>
Expand All @@ -96,6 +97,7 @@
<file alias="info">res/icons/info.png</file>
<file alias="network-dark">res/icons/network-dark.png</file>
<file alias="network-light">res/icons/network-light.png</file>
<file alias="plus">res/icons/plus.png</file>
<file alias="shutdown">res/icons/shutdown.png</file>
<file alias="singlesig-wallet">res/icons/singlesig-wallet.png</file>
<file alias="storage-dark">res/icons/storage-dark.png</file>
Expand Down
2 changes: 2 additions & 0 deletions src/qml/controls/Icon.qml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import QtQuick.Controls 2.15

Button {
id: root
width: icon.width
height: icon.height
required property color color
required property url source
property int size: 32
Expand Down
4 changes: 4 additions & 0 deletions src/qml/imageprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,5 +162,9 @@ QPixmap ImageProvider::requestPixmap(const QString& id, QSize* size, const QSize
return QIcon(":/icons/hidden").pixmap(requested_size);
}

if (id == "plus") {
*size = requested_size;
return QIcon(":/icons/plus").pixmap(requested_size);
}
return {};
}
81 changes: 81 additions & 0 deletions src/qml/models/walletlistmodel.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright (c) 2024 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <qml/models/walletlistmodel.h>

#include <interfaces/node.h>

#include <QSet>

WalletListModel::WalletListModel(interfaces::Node& node, QObject *parent)
: QAbstractListModel(parent)
, m_node(node)
{
setSelectedWallet("Singlesig Wallet");
}

void WalletListModel::listWalletDir()
{
QSet<QString> existing_names;
for (int i = 0; i < rowCount(); ++i) {
QModelIndex index = this->index(i, 0);
QString name = data(index, NameRole).toString();
existing_names.insert(name);
}

for (const std::string &name : m_node.walletLoader().listWalletDir()) {
QString qname = QString::fromStdString(name);
if (!existing_names.contains(qname)) {
addItem({ qname });
}
}
}

void WalletListModel::setSelectedWallet(QString wallet_name)
{
if (m_selected_wallet != wallet_name) {
m_selected_wallet = wallet_name;
Q_EMIT selectedWalletChanged();
}
}

QString WalletListModel::selectedWallet() const
{
return m_selected_wallet;
}

int WalletListModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return m_items.size();
}

QVariant WalletListModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid() || index.row() < 0 || index.row() >= m_items.size())
return QVariant();

const auto &item = m_items[index.row()];
switch (role) {
case Qt::DisplayRole:
case NameRole:
return item.name;
default:
return QVariant();
}
}

QHash<int, QByteArray> WalletListModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[NameRole] = "name";
return roles;
}

void WalletListModel::addItem(const Item &item)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
m_items.append(item);
endInsertRows();
}
55 changes: 55 additions & 0 deletions src/qml/models/walletlistmodel.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright (c) 2024 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_QML_MODELS_WALLETLISTMODEL_H
#define BITCOIN_QML_MODELS_WALLETLISTMODEL_H

#include <interfaces/wallet.h>
#include <QAbstractListModel>
#include <QList>

namespace interfaces {
class Node;
}

class WalletListModel : public QAbstractListModel
{
Q_OBJECT
Q_PROPERTY(QString selectedWallet READ selectedWallet WRITE setSelectedWallet NOTIFY selectedWalletChanged)

public:
WalletListModel(interfaces::Node& node, QObject *parent = nullptr);
~WalletListModel() = default;

enum Roles {
NameRole = Qt::UserRole + 1
};

int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override;

void setSelectedWallet(QString wallet_name);
QString selectedWallet() const;

public Q_SLOTS:
void listWalletDir();

Q_SIGNALS:
void selectedWalletChanged();

private:
struct Item {
QString name;
};

void addItem(const Item &item);

QList<Item> m_items;
interfaces::Node& m_node;
QString m_selected_wallet;

};

#endif // BITCOIN_QML_MODELS_WALLETLISTMODEL_H
18 changes: 17 additions & 1 deletion src/qml/pages/wallet/DesktopWallets.qml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,23 @@ Page {
leftItem: WalletBadge {
implicitWidth: 154
implicitHeight: 46
text: qsTr("Singlesig Wallet")
text: walletListModel.selectedWallet

MouseArea {
anchors.fill: parent
onClicked: {
walletListModel.listWalletDir()
walletSelect.opened ? walletSelect.close() : walletSelect.open()
}
}

WalletSelect {
id: walletSelect
model: walletListModel
closePolicy: Popup.CloseOnPressOutside
x: 0
y: parent.height
}
}
centerItem: RowLayout {
NavigationTab {
Expand Down
4 changes: 3 additions & 1 deletion src/qml/pages/wallet/WalletBadge.qml
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,10 @@ Button {
visible: root.showIcon
source: "image://images/singlesig-wallet"
color: Theme.color.neutral8
size: 30
Layout.minimumWidth: 30
Layout.preferredWidth: 30
Layout.preferredHeight: 30
Layout.maximumWidth: 30
}
ColumnLayout {
spacing: 2
Expand Down
115 changes: 115 additions & 0 deletions src/qml/pages/wallet/WalletSelect.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// Copyright (c) 2024 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15

import "../../controls"

Popup {
id: root

property alias model: listView.model
implicitHeight: layout.height + arrow.height
implicitWidth: 250
clip: true

background: Item {
anchors.fill: parent
Rectangle {
id: tooltipBg
color: Theme.color.neutral0
border.color: Theme.color.neutral4
radius: 5
border.width: 1
width: parent.width
height: parent.height - arrow.height - 1
anchors.top: arrow.bottom
anchors.horizontalCenter: root.horizontalCenter
anchors.topMargin: -1
}
Image {
id: arrow
source: Theme.image.tooltipArrow
width: 22
height: 10
anchors.left: parent.left
anchors.leftMargin: 10
anchors.top: parent.top
}
}

ButtonGroup {
id: buttonGroup
}

ColumnLayout {
id: layout
width: 220
anchors.topMargin: arrow.height
CoreText {
Layout.alignment: Qt.AlignHCenter
Layout.preferredWidth: 220
Layout.preferredHeight: 30
id: label
text: qsTr("Wallets")
visible: listView.count > 0
bold: true
color: Theme.color.neutral9
font.pixelSize: 14
topPadding: 10
bottomPadding: 5
}

ListView {
Layout.preferredWidth: 220
Layout.preferredHeight: Math.min(listView.count * 34, 300)
id: listView
interactive: true
spacing: 2
model: walletListModel

delegate: WalletBadge {
required property string name;

width: 220
height: 32
text: name
ButtonGroup.group: buttonGroup
showBalance: false
showIcon: false
onClicked: {
walletListModel.selectedWallet = name
root.close()
}
}
}

RowLayout {
id: addWallet
Layout.preferredWidth: addIcon.size + addText.width
Layout.preferredHeight: 45
Layout.alignment: Qt.AlignHCenter
Icon {
id: addIcon
Layout.alignment: Qt.AlignHCenter
source: "image://images/plus"
color: Theme.color.neutral8
size: 14
topPadding: 5
bottomPadding: 10
}
CoreText {
id: addText
Layout.alignment: Qt.AlignHCenter
text: qsTr("Add Wallet")
color: Theme.color.neutral9
font.pixelSize: 15
topPadding: 5
bottomPadding: 10
}
}
}
}
Binary file added src/qml/res/icons/plus.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/qml/res/src/plus.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 0939f2b

Please sign in to comment.