From 667078113834ac2aa67159a17c13525ca78d51d8 Mon Sep 17 00:00:00 2001 From: johnny9 <985648+johnny9@users.noreply.github.com> Date: Sat, 18 May 2024 23:49:03 -0400 Subject: [PATCH] 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. --- src/Makefile.qt.include | 7 +- src/qml/bitcoin.cpp | 4 + src/qml/bitcoin_qml.qrc | 2 + src/qml/controls/Icon.qml | 2 + src/qml/imageprovider.cpp | 4 + src/qml/models/walletlistmodel.cpp | 81 ++++++++++++++++++ src/qml/models/walletlistmodel.h | 55 ++++++++++++ src/qml/pages/wallet/DesktopWallets.qml | 18 +++- src/qml/pages/wallet/WalletSelect.qml | 108 ++++++++++++++++++++++++ src/qml/res/icons/plus.png | Bin 0 -> 278 bytes src/qml/res/src/plus.svg | 3 + 11 files changed, 282 insertions(+), 2 deletions(-) create mode 100644 src/qml/models/walletlistmodel.cpp create mode 100644 src/qml/models/walletlistmodel.h create mode 100644 src/qml/pages/wallet/WalletSelect.qml create mode 100644 src/qml/res/icons/plus.png create mode 100644 src/qml/res/src/plus.svg diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 4cc9443ab3..e12088a47f 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -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 \ qt/moc_addressbookpage.cpp \ qt/moc_addresstablemodel.cpp \ @@ -121,6 +122,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 \ @@ -308,6 +310,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 @@ -333,6 +336,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 \ @@ -408,7 +412,8 @@ QML_RES_QML = \ qml/pages/settings/SettingsStorage.qml \ qml/pages/settings/SettingsTheme.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 diff --git a/src/qml/bitcoin.cpp b/src/qml/bitcoin.cpp index c1fc4e34d8..f05e8365d9 100644 --- a/src/qml/bitcoin.cpp +++ b/src/qml/bitcoin.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -292,11 +293,14 @@ 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("walletListModel", &wallet_list_model); OptionsQmlModel options_model{*node}; engine.rootContext()->setContextProperty("optionsModel", &options_model); diff --git a/src/qml/bitcoin_qml.qrc b/src/qml/bitcoin_qml.qrc index 210c01cfab..6506833673 100644 --- a/src/qml/bitcoin_qml.qrc +++ b/src/qml/bitcoin_qml.qrc @@ -67,6 +67,7 @@ pages/settings/SettingsTheme.qml pages/wallet/DesktopWallets.qml pages/wallet/WalletBadge.qml + pages/wallet/WalletSelect.qml res/icons/arrow-down.png @@ -87,6 +88,7 @@ res/icons/info.png res/icons/network-dark.png res/icons/network-light.png + res/icons/plus.png res/icons/shutdown.png res/icons/singlesig-wallet.png res/icons/storage-dark.png diff --git a/src/qml/controls/Icon.qml b/src/qml/controls/Icon.qml index a051b2971a..599728ff7a 100644 --- a/src/qml/controls/Icon.qml +++ b/src/qml/controls/Icon.qml @@ -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 diff --git a/src/qml/imageprovider.cpp b/src/qml/imageprovider.cpp index b3b041c9eb..360020e7ea 100644 --- a/src/qml/imageprovider.cpp +++ b/src/qml/imageprovider.cpp @@ -142,5 +142,9 @@ QPixmap ImageProvider::requestPixmap(const QString& id, QSize* size, const QSize return QIcon(":/icons/tooltip-arrow-light").pixmap(requested_size); } + if (id == "plus") { + *size = requested_size; + return QIcon(":/icons/plus").pixmap(requested_size); + } return {}; } diff --git a/src/qml/models/walletlistmodel.cpp b/src/qml/models/walletlistmodel.cpp new file mode 100644 index 0000000000..ecf97b025a --- /dev/null +++ b/src/qml/models/walletlistmodel.cpp @@ -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 + +#include + +#include + +WalletListModel::WalletListModel(interfaces::Node& node, QObject *parent) +: QAbstractListModel(parent) +, m_node(node) +{ + setSelectedWallet("Singlesig Wallet"); +} + +void WalletListModel::listWalletDir() +{ + QSet 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 WalletListModel::roleNames() const +{ + QHash roles; + roles[NameRole] = "name"; + return roles; +} + +void WalletListModel::addItem(const Item &item) +{ + beginInsertRows(QModelIndex(), rowCount(), rowCount()); + m_items.append(item); + endInsertRows(); +} diff --git a/src/qml/models/walletlistmodel.h b/src/qml/models/walletlistmodel.h new file mode 100644 index 0000000000..ae1451b21a --- /dev/null +++ b/src/qml/models/walletlistmodel.h @@ -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 +#include +#include + +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 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 m_items; + interfaces::Node& m_node; + QString m_selected_wallet; + +}; + +#endif // BITCOIN_QML_MODELS_WALLETLISTMODEL_H diff --git a/src/qml/pages/wallet/DesktopWallets.qml b/src/qml/pages/wallet/DesktopWallets.qml index 50281453ce..14d831d1c1 100644 --- a/src/qml/pages/wallet/DesktopWallets.qml +++ b/src/qml/pages/wallet/DesktopWallets.qml @@ -24,7 +24,23 @@ Page { leftItem: WalletBadge { implicitWidth: 164 implicitHeight: 60 - 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 - 5 + } } centerItem: RowLayout { NavigationTab { diff --git a/src/qml/pages/wallet/WalletSelect.qml b/src/qml/pages/wallet/WalletSelect.qml new file mode 100644 index 0000000000..80c7d8ed9d --- /dev/null +++ b/src/qml/pages/wallet/WalletSelect.qml @@ -0,0 +1,108 @@ +// 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: listView.height + arrow.height + implicitWidth: 250 + + 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.horizontalCenter: parent.horizontalCenter + anchors.top: parent.top + } + } + + ButtonGroup { + id: buttonGroup + } + + ListView { + id: listView + anchors.topMargin: arrow.height + width: 220 + height: contentHeight + interactive: false + spacing: 2 + model: walletListModel + + header: CoreText { + text: qsTr("Wallets") + visible: listView.count > 0 + bold: true + width: 220 + height: 30 + color: Theme.color.neutral9 + font.pixelSize: 14 + topPadding: 10 + bottomPadding: 5 + } + + 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() + } + + } + + footer: RowLayout { + id: addWallet + height: 45 + width: addIcon.size + addText.width + spacing + anchors.horizontalCenter: parent.horizontalCenter + 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 + } + } + } +} diff --git a/src/qml/res/icons/plus.png b/src/qml/res/icons/plus.png new file mode 100644 index 0000000000000000000000000000000000000000..6bbe9a4d923a6e98dec328cf487fdc3fbe2c9c63 GIT binary patch literal 278 zcmeAS@N?(olHy`uVBq!ia0vp^G9b*s1|*Ak?@s|zoCO|{#S9E$svykh8Km+7D9BhG zHJV@L(#+i4eh4;k>d=y%jhC@LE;NgvR- zyFmGXMvjAf0`o3LgGS44`AMs??yUOxX|kS|o!{~kn}iQ^bjYkw|GFq@4X^XNNt?F+ zFyA1&`SYvB?hg+9Uk)%v$6Dz2Ntk_+s9Ze%#ebjM7up4^cz1i)-ddoowaD~MK!5u^ zgQCj;yPYmeX3FMY*?nMn>Api + +