diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index c52728fce2..5a4cca3a6c 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -147,6 +147,7 @@ BITCOIN_QT_H = \ qml/bitcoinamount.h \ qml/guiconstants.h \ qml/imageprovider.h \ + qml/qrimageprovider.h \ qml/util.h \ qml/walletqmlcontroller.h \ qt/addressbookpage.h \ @@ -342,6 +343,7 @@ BITCOIN_QML_BASE_CPP = \ qml/models/walletqmlmodel.cpp \ qml/models/walletqmlmodeltransaction.cpp \ qml/imageprovider.cpp \ + qml/qrimageprovider.cpp \ qml/util.cpp \ qml/walletqmlcontroller.cpp @@ -436,6 +438,7 @@ QML_RES_QML = \ qml/controls/OptionButton.qml \ qml/controls/OptionSwitch.qml \ qml/controls/OutlineButton.qml \ + qml/controls/QRImage.qml \ qml/controls/PageIndicator.qml \ qml/controls/PageStack.qml \ qml/controls/ProgressIndicator.qml \ diff --git a/src/qml/bitcoin.cpp b/src/qml/bitcoin.cpp index 9ec1e51701..f3f78fac71 100644 --- a/src/qml/bitcoin.cpp +++ b/src/qml/bitcoin.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -313,6 +314,7 @@ int QmlGuiMain(int argc, char* argv[]) QScopedPointer network_style{NetworkStyle::instantiate(Params().GetChainType())}; assert(!network_style.isNull()); engine.addImageProvider(QStringLiteral("images"), new ImageProvider{network_style.data()}); + engine.addImageProvider(QStringLiteral("qr"), new QRImageProvider); engine.rootContext()->setContextProperty("networkTrafficTower", &network_traffic_tower); engine.rootContext()->setContextProperty("nodeModel", &node_model); diff --git a/src/qml/bitcoin_qml.qrc b/src/qml/bitcoin_qml.qrc index 8c4b231ef9..63b9c3d350 100644 --- a/src/qml/bitcoin_qml.qrc +++ b/src/qml/bitcoin_qml.qrc @@ -47,6 +47,7 @@ controls/PageIndicator.qml controls/PageStack.qml controls/ProgressIndicator.qml + controls/QRImage.qml controls/qmldir controls/SendOptionsPopup.qml controls/Setting.qml diff --git a/src/qml/controls/QRImage.qml b/src/qml/controls/QRImage.qml new file mode 100644 index 0000000000..9eda57f54c --- /dev/null +++ b/src/qml/controls/QRImage.qml @@ -0,0 +1,17 @@ +// Copyright (c) 2025 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 + +Image { + id: root + + property string code: "" + property color backgroundColor: Qt.black + property color foregroundColor: Qt.white + + fillMode: Image.PreserveAspectFit + smooth: false + source: `image://qr/${encodeURIComponent(root.code)}?&fg=${encodeURIComponent(root.foregroundColor)}&bg=${encodeURIComponent(root.backgroundColor)}` +} diff --git a/src/qml/pages/wallet/RequestPayment.qml b/src/qml/pages/wallet/RequestPayment.qml index dc44ec7526..06adfeb0ca 100644 --- a/src/qml/pages/wallet/RequestPayment.qml +++ b/src/qml/pages/wallet/RequestPayment.qml @@ -199,6 +199,7 @@ Page { clearRequest.visible = true title.text = qsTr("Payment request #" + requestCounter) address.text = "bc1q f5xe y2tf 89k9 zy6k gnru wszy 5fsa truy 9te1 bu" + qrImage.code = "bc1qf5xey2tf89k9zy6kgnruwszy5fsatruy9te1bu" continueButton.text = qsTr("Copy payment request") } } @@ -220,19 +221,26 @@ Page { clearRequest.visible = false title.text = qsTr("Request a payment") address.text = "" + qrImage.code = "" continueButton.text = qsTr("Create bitcoin address") } } } - Rectangle { - id: qrPlaceholder + Pane { Layout.alignment: Qt.AlignTop Layout.minimumWidth: 150 - Layout.maximumWidth: 150 - color: Theme.color.neutral2 - width: 150 - height: 150 + Layout.minimumHeight: 150 + padding: 0 + background: Rectangle { + color: Theme.color.neutral2 + visible: qrImage.code === "" + } + contentItem: QRImage { + id: qrImage + backgroundColor: "transparent" + foregroundColor: Theme.color.neutral9 + } } } } diff --git a/src/qml/qrimageprovider.cpp b/src/qml/qrimageprovider.cpp new file mode 100644 index 0000000000..e87300f387 --- /dev/null +++ b/src/qml/qrimageprovider.cpp @@ -0,0 +1,56 @@ +// Copyright (c) 2025 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 + +#if defined(HAVE_CONFIG_H) +#include /* for USE_QRCODE */ +#endif + +#ifdef USE_QRCODE +#include +#endif + +#include +#include +#include +#include +#include +#include + +QRImageProvider::QRImageProvider() + : QQuickImageProvider{QQuickImageProvider::Image} +{ +} + +QImage QRImageProvider::requestImage(const QString& id, QSize* size, const QSize& requested_size) +{ +#ifdef USE_QRCODE + const QUrl url{"image:///" + id}; + const QUrlQuery query{url}; + const QString data{url.path().mid(1)}; + const QColor fg{query.queryItemValue("fg")}; + const QColor bg{query.queryItemValue("bg")}; + + QRcode* code = QRcode_encodeString(data.toUtf8().constData(), 0, QR_ECLEVEL_L, QR_MODE_8, 1); + + if (code) { + QImage image{code->width, code->width, QImage::Format_ARGB32}; + unsigned char* p = code->data; + for (int y = 0; y < code->width; ++y) { + for (int x = 0; x < code->width; ++x) { + image.setPixelColor(x, y, (*p & 1) ? fg : bg); + ++p; + } + } + *size = QSize(code->width, code->width); + QRcode_free(code); + return image; + } +#endif // USE_QRCODE + QImage pixel{1, 1, QImage::Format_ARGB32}; + pixel.setPixelColor(0, 0, QColorConstants::Transparent); + *size = QSize(1, 1); + return pixel; +} diff --git a/src/qml/qrimageprovider.h b/src/qml/qrimageprovider.h new file mode 100644 index 0000000000..c81fe85f32 --- /dev/null +++ b/src/qml/qrimageprovider.h @@ -0,0 +1,24 @@ +// Copyright (c) 2025 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_QRIMAGEPROVIDER_H +#define BITCOIN_QML_QRIMAGEPROVIDER_H + +#include + +QT_BEGIN_NAMESPACE +class QImage; +class QSize; +class QString; +QT_END_NAMESPACE + +class QRImageProvider : public QQuickImageProvider +{ +public: + explicit QRImageProvider(); + + QImage requestImage(const QString& id, QSize* size, const QSize& requested_size) override; +}; + +#endif // BITCOIN_QML_QRIMAGEPROVIDER_H