From 2bcb248d05e6f6b74978a769d69d399415fc1f1a Mon Sep 17 00:00:00 2001 From: johnny9 <985648+johnny9@users.noreply.github.com> Date: Thu, 21 Nov 2024 22:43:19 -0500 Subject: [PATCH 01/10] qml: Introduce LabeledTextInput control This control is primarily used in wallet forms where the user is expected to input values for sending or receiving transactions. It contains a Label describing the input as well as a TextInput where the actual string is entered. A Icon is also available on the right side of the component for extra actions. --- src/Makefile.qt.include | 1 + src/qml/bitcoin_qml.qrc | 1 + src/qml/controls/LabeledTextInput.qml | 60 +++++++++++++++++++++++++++ 3 files changed, 62 insertions(+) create mode 100644 src/qml/controls/LabeledTextInput.qml diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index e9a0dd7f72..b5e2a0f185 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -390,6 +390,7 @@ QML_RES_QML = \ qml/controls/InformationPage.qml \ qml/controls/IPAddressValueInput.qml \ qml/controls/KeyValueRow.qml \ + qml/controls/LabeledTextInput.qml \ qml/controls/NavButton.qml \ qml/controls/PageIndicator.qml \ qml/controls/NavigationBar.qml \ diff --git a/src/qml/bitcoin_qml.qrc b/src/qml/bitcoin_qml.qrc index 61d2607fca..a64abfb2f0 100644 --- a/src/qml/bitcoin_qml.qrc +++ b/src/qml/bitcoin_qml.qrc @@ -23,6 +23,7 @@ controls/ContinueButton.qml controls/CoreText.qml controls/CoreTextField.qml + controls/LabeledTextInput.qml controls/ExternalLink.qml controls/FocusBorder.qml controls/Header.qml diff --git a/src/qml/controls/LabeledTextInput.qml b/src/qml/controls/LabeledTextInput.qml new file mode 100644 index 0000000000..efa82f98f4 --- /dev/null +++ b/src/qml/controls/LabeledTextInput.qml @@ -0,0 +1,60 @@ +// 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 + +Item { + property alias labelText: label.text + property alias text: input.text + property alias placeholderText: input.placeholderText + property alias iconSource: icon.source + property alias customIcon: iconContainer.data + property alias enabled: input.enabled + + signal iconClicked + signal textEdited + + id: root + implicitHeight: label.height + input.height + + CoreText { + id: label + anchors.left: parent.left + anchors.top: parent.top + color: Theme.color.neutral7 + font.pixelSize: 15 + } + + TextField { + id: input + anchors.left: parent.left + anchors.right: iconContainer.left + anchors.bottom: parent.bottom + leftPadding: 0 + font.family: "Inter" + font.styleName: "Regular" + font.pixelSize: 18 + color: Theme.color.neutral9 + placeholderTextColor: Theme.color.neutral7 + background: Item {} + onTextEdited: root.textEdited() + } + + Item { + id: iconContainer + anchors.right: parent.right + anchors.verticalCenter: input.verticalCenter + + Icon { + id: icon + source: "" + color: Theme.color.neutral8 + size: 30 + enabled: source != "" + onClicked: root.iconClicked() + } + } +} From 1f02fabb7cc155e356074b132312db9b6ca50f31 Mon Sep 17 00:00:00 2001 From: johnny9 <985648+johnny9@users.noreply.github.com> Date: Thu, 21 Nov 2024 23:18:39 -0500 Subject: [PATCH 02/10] qml: Add copy icon The copy icon will be used to initiate a copy to clipboard when it is clicked. --- src/Makefile.qt.include | 1 + src/qml/bitcoin_qml.qrc | 1 + src/qml/imageprovider.cpp | 5 +++++ src/qml/res/icons/copy.png | Bin 0 -> 1547 bytes src/qml/res/src/copy.svg | 4 ++++ 5 files changed, 11 insertions(+) create mode 100644 src/qml/res/icons/copy.png create mode 100644 src/qml/res/src/copy.svg diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index b5e2a0f185..86131cbd7d 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -337,6 +337,7 @@ QML_RES_ICONS = \ qml/res/icons/caret-left.png \ qml/res/icons/caret-right.png \ qml/res/icons/check.png \ + qml/res/icons/copy.png \ qml/res/icons/cross.png \ qml/res/icons/error.png \ qml/res/icons/export.png \ diff --git a/src/qml/bitcoin_qml.qrc b/src/qml/bitcoin_qml.qrc index a64abfb2f0..4147492706 100644 --- a/src/qml/bitcoin_qml.qrc +++ b/src/qml/bitcoin_qml.qrc @@ -94,6 +94,7 @@ res/icons/caret-left.png res/icons/caret-right.png res/icons/check.png + res/icons/copy.png res/icons/cross.png res/icons/error.png res/icons/export.png diff --git a/src/qml/imageprovider.cpp b/src/qml/imageprovider.cpp index daf2feeae2..6cc552ed4d 100644 --- a/src/qml/imageprovider.cpp +++ b/src/qml/imageprovider.cpp @@ -77,6 +77,11 @@ QPixmap ImageProvider::requestPixmap(const QString& id, QSize* size, const QSize return QIcon(":/icons/check").pixmap(requested_size); } + if (id == "copy") { + *size = requested_size; + return QIcon(":/icons/copy").pixmap(requested_size); + } + if (id == "cross") { *size = requested_size; return QIcon(":/icons/cross").pixmap(requested_size); diff --git a/src/qml/res/icons/copy.png b/src/qml/res/icons/copy.png new file mode 100644 index 0000000000000000000000000000000000000000..440acb56cbef46c964415707dd3ff242d7b5608b GIT binary patch literal 1547 zcmb7Edo+{@6#qUZGbl@2q*x=gInoMuG3K1P!nGovtTR>-q#Sc>(S zJl1AsWR$U3uaGhkrjoZj8V;l6G00=bzx}trdw%zxd+#6jch0@%CVRL!?Nd0c006)~ zJPzY2jWN4K$Vq+cnE(L*fVWAGjvjbNM%-Qwgn02NzmBS9~-BYdJ(P`MARhgvHvXFHqe6a#X4_Z z@eEd(Yzae;jxdKCWH(n%T<99tXNP2`yB&)shhh;8MMnZ)y~zg2IU5}PKgX>b7d}kR z5%Yo~3%}Br^&>OEGq)lVW)ragx}j=Gvw4!_Vvepso2X4kj(Be^eXdGC$xBuG8VS95 zQ6fqzZgb$>owkDX9Q6v2X$IwI*S9$W1(9)gNNs1XWXkl;RQ0r8g-`>tU-42r0z6Uv z-cDV0;%P|91DufctQ9@JhNZon8U%DYP{%#Aty!PZYa?9WLt6CAqw?&@c0M{GrpKh` zuYpww@A^r%m}YZoY4v87joB7Hid)*A7BhN%e4?)OC9`>2sb{CeC1DB&=59NH5$Iw{ zCm3C0*Izz#nXXm#on1nm^9TS>Y^J7H^`k~LLqCbf%)7Q(0Ctj=1^ZoPRJs?rt1eC$ zU}x9Z4P}|qiaZtPOP8*&f0v+x{vQ$2BAkJD#lmM45iq!3;#{*B0AO%D#@;JpP(0!q zOt^R`adD^iChbR-vL(9(d4S_gz}&&=vydv5dfGWTZGLUHV~I&eO*YvTx@_cu;ktXC zu=+(gbGkZ?NBlX~OQqzTn(DCEy;!+wi*~tuGfDVEL-}{QnNtO?mWCizN;it$UHe2` zUaI69_|ce)h2{No68?}_8!ro~&ObvK;|2E0d4^fe0$8PNhOT^S`$Q_VnqBghe!~0M zEQh|(;Q5G=0PtUKZf*Ogu(0S4wlFfK?`edO=h+q%8}CGQ>gecDXfOtj>&?XU`M!SMm>W1SA`}Xn zc(UXz!s&?*@1yONQ392o9Ptjxy){}oI5;efha~j~sgmiy?AXVkk+E@LYip}S+*x!` zVaqpLOz+mrU|i}mKC*Y*%H$RklcBqH{y>*=lz6A2jJ|O7k=NjBl}dP9*e|sNlcrF} z6c_T<3Jo%FrJmLXvznh4bgY|ZRw_6g6bXXo(Pg`93yA;lf*`)pEE8XLJgYNnCxYuD zTsFkRqpf9V>UJ5rCo$)j6H01MMzst=#`uoQ+;?P+jwX&I*21;ymC>|PC7zV4w7R@( zgpWc8Q5toz8Gd(*W(W;_zXms`Ew8NPXD{2{jOmL0La};sp}RJb-WYnbdPrut!TKd(Xf z7uTE@6=WukowE@YNY)oE-JO~z3o1T*59?8nlSZ+kq9Ru2{f2i`X4$owDx>;zzz#qH z$^Zg@0Wts>^M6z3?Hr;`jLFBmdJI4`5>&~7jK|exdqoH~Ga6-Sx#H-TVDOnWKQr)a zV@-`j-yIy8opX3d4JMQ44}*;R0_cWCN>Oiea&qC4m4;LEDoSK}O4RMLVU<$!d~I#5 zFfu<2;Ma zm`}e#g)t@~pL#sVoEq{Dfo{YCHvTl)7azP<1fNxyC77ll*p9hfA9-_I3!!O*q@tO? zu|gS|WU?2ZfoZ?GsAF!MF&Ta>J|JSPkeCYa9i^A*+*hCRLszfcN`A>tUC`3h{5+Yj z)(EFYYMHqTL33u$tA#gXO`{Eyuh$_y8B&8F$Yl4ie}flsO+X%7uMT!v_-?m_$GTxE I9Q + + + From 48eed101e548f15e5b265567eaf535f0a9652b86 Mon Sep 17 00:00:00 2001 From: johnny9 <985648+johnny9@users.noreply.github.com> Date: Thu, 21 Nov 2024 23:46:13 -0500 Subject: [PATCH 03/10] qml: Add flip-vertical icon The flip-veritcal icon is primarily used when flipping the units displayed when clicked --- src/Makefile.qt.include | 1 + src/qml/bitcoin_qml.qrc | 1 + src/qml/imageprovider.cpp | 5 +++++ src/qml/res/icons/flip-vertical.png | Bin 0 -> 1528 bytes src/qml/res/src/flip-vertical.svg | 4 ++++ 5 files changed, 11 insertions(+) create mode 100644 src/qml/res/icons/flip-vertical.png create mode 100644 src/qml/res/src/flip-vertical.svg diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 86131cbd7d..893bfbd2da 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -341,6 +341,7 @@ QML_RES_ICONS = \ qml/res/icons/cross.png \ qml/res/icons/error.png \ qml/res/icons/export.png \ + qml/res/icons/flip-vertical.png \ qml/res/icons/gear.png \ qml/res/icons/gear-outline.png \ qml/res/icons/hidden.png \ diff --git a/src/qml/bitcoin_qml.qrc b/src/qml/bitcoin_qml.qrc index 4147492706..00ca4ef0ae 100644 --- a/src/qml/bitcoin_qml.qrc +++ b/src/qml/bitcoin_qml.qrc @@ -98,6 +98,7 @@ res/icons/cross.png res/icons/error.png res/icons/export.png + res/icons/flip-vertical.png res/icons/gear.png res/icons/gear-outline.png res/icons/hidden.png diff --git a/src/qml/imageprovider.cpp b/src/qml/imageprovider.cpp index 6cc552ed4d..3f5e949cbf 100644 --- a/src/qml/imageprovider.cpp +++ b/src/qml/imageprovider.cpp @@ -97,6 +97,11 @@ QPixmap ImageProvider::requestPixmap(const QString& id, QSize* size, const QSize return QIcon(":/icons/export").pixmap(requested_size); } + if (id == "flip-vertical") { + *size = requested_size; + return QIcon(":/icons/flip-vertical").pixmap(requested_size); + } + if (id == "gear") { *size = requested_size; return QIcon(":/icons/gear").pixmap(requested_size); diff --git a/src/qml/res/icons/flip-vertical.png b/src/qml/res/icons/flip-vertical.png new file mode 100644 index 0000000000000000000000000000000000000000..fb6af29aa9c07f8c29d2a5df04cec6a2f204c741 GIT binary patch literal 1528 zcmZ`(doPS_nSMU*S{gdXN4%;|C`>?L~P2m$(~q`fKa4bKldV@7X&ki4%-^4=DGlD9T1cMB6&90VxC@+ z{UKo$CwGrGt^lRKQg6Ti1r$>kU$Kd{iyx*35l)fdF0EL!U1tHm9OX1KbI0+)e3?^c z{g-Zy2M#=tOD^N)mJ06%SEt6Yh zDP8$MGqU6;w><9|TJzjmu#9T#>oa#JDEB7NM*8xcKc0mdGIvJ>CT+)K%t~OR-BU$1 zA`#|oZC~-R8D!-~!fk4P14vst?FpD7Jl{}XNk6Up_H*yk+5@QZxU=uT?=A~`01m682~_*$ZoEO7=v?z zzA;D&vP~ux90_s4xVDZm|1J&p&^BHwdx%Ich@u8VBGr8NL-WfJZnjT6^fHbD^i*kcW$u)sfPGJ zS>RQDA_;{;yu~z~tR)?OiOw2b8n5fw!y4{#$$xi!DGAwba6uWiK~GD5f^3Nzp->I> z+S~i4=ujf5g0)04^Iq?CgBS1;=fKGN%53DfJ2#aJw~xqjB=29`V&&ZZ9i&*Iq$q7MQgTNExN0$`u$ zC}6x&9Q(?2HkK=3<_wkxvqqI-`Zup;6x_xI2te9@=={OX# z=TlAl7hc&E#xBm&(F7`9^+&9Re(tW?rPO*99OvExa|!jjff~B;Y}p!s0$>2d0SJI8 zpaZ}GC`X1~^GgBTrh~kvx6djE}+A{*N_3oef zYE3!t)?~~s14%;A9GbgGp{E%hs*6ochddvF8?wP*+({_4 zO~Kzpz;m&hChwa|4DZY*)sGopXr`Y|e6_iW(k2WCdZsKh9L|)ysb5rf`zowVL}r#^ z^zbr&lPYnYR}3$2))!%lp(@6RphUr27P_v(ySi>VKVOVr;9)hhfvg-6PbQ#SOy5eo zRY(114MOOom0#W-zfbzOa3Ii_HHjZ--YFd(Kxd^&iR;h$za_zq4GA8LTj|jCY+0`$ MlYHDB?+@qx3;RB&;{X5v literal 0 HcmV?d00001 diff --git a/src/qml/res/src/flip-vertical.svg b/src/qml/res/src/flip-vertical.svg new file mode 100644 index 0000000000..5fabdcaef8 --- /dev/null +++ b/src/qml/res/src/flip-vertical.svg @@ -0,0 +1,4 @@ + + + + From 35e5f21dc8d6881e706c48f247f2c9862c9cff86 Mon Sep 17 00:00:00 2001 From: johnny9 <985648+johnny9@users.noreply.github.com> Date: Fri, 22 Nov 2024 00:01:05 -0500 Subject: [PATCH 04/10] qml: Add pending icon The pending icon will be used to show the status of transactions --- src/Makefile.qt.include | 1 + src/qml/bitcoin_qml.qrc | 1 + src/qml/imageprovider.cpp | 5 +++++ src/qml/res/icons/pending.png | Bin 0 -> 4923 bytes src/qml/res/src/pending.svg | 4 ++++ 5 files changed, 11 insertions(+) create mode 100644 src/qml/res/icons/pending.png create mode 100644 src/qml/res/src/pending.svg diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 893bfbd2da..87e1a9e5d4 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -350,6 +350,7 @@ QML_RES_ICONS = \ qml/res/icons/network-dark.png \ qml/res/icons/network-light.png \ qml/res/icons/plus.png \ + qml/res/icons/pending.png \ qml/res/icons/shutdown.png \ qml/res/icons/singlesig-wallet.png \ qml/res/icons/storage-dark.png \ diff --git a/src/qml/bitcoin_qml.qrc b/src/qml/bitcoin_qml.qrc index 00ca4ef0ae..c16acb2b1f 100644 --- a/src/qml/bitcoin_qml.qrc +++ b/src/qml/bitcoin_qml.qrc @@ -107,6 +107,7 @@ res/icons/network-dark.png res/icons/network-light.png res/icons/plus.png + res/icons/pending.png res/icons/shutdown.png res/icons/singlesig-wallet.png res/icons/storage-dark.png diff --git a/src/qml/imageprovider.cpp b/src/qml/imageprovider.cpp index 3f5e949cbf..e1753b3b3c 100644 --- a/src/qml/imageprovider.cpp +++ b/src/qml/imageprovider.cpp @@ -132,6 +132,11 @@ QPixmap ImageProvider::requestPixmap(const QString& id, QSize* size, const QSize return QIcon(":/icons/network-light").pixmap(requested_size); } + if (id == "pending") { + *size = requested_size; + return QIcon(":/icons/pending").pixmap(requested_size); + } + if (id == "shutdown") { *size = requested_size; return QIcon(":/icons/shutdown").pixmap(requested_size); diff --git a/src/qml/res/icons/pending.png b/src/qml/res/icons/pending.png new file mode 100644 index 0000000000000000000000000000000000000000..ec7858d615b1c24f1cc53c8d6861595837cd6e1e GIT binary patch literal 4923 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 literal 0 HcmV?d00001 diff --git a/src/qml/res/src/pending.svg b/src/qml/res/src/pending.svg new file mode 100644 index 0000000000..cef63eeb79 --- /dev/null +++ b/src/qml/res/src/pending.svg @@ -0,0 +1,4 @@ + + + + From b5020363c7c053a2a378ca3c778a1305156d5012 Mon Sep 17 00:00:00 2001 From: johnny9 <985648+johnny9@users.noreply.github.com> Date: Fri, 22 Nov 2024 01:01:54 -0500 Subject: [PATCH 05/10] qml: Introduce BitcoinAmount BitcoinAmount is a helper object can be used to manage inputs in sats or btc. --- src/Makefile.qt.include | 3 + src/qml/bitcoin.cpp | 2 + src/qml/bitcoinamount.cpp | 127 ++++++++++++++++++++++++++++++++++++++ src/qml/bitcoinamount.h | 52 ++++++++++++++++ 4 files changed, 184 insertions(+) create mode 100644 src/qml/bitcoinamount.cpp create mode 100644 src/qml/bitcoinamount.h diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 87e1a9e5d4..33875650ab 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -45,6 +45,7 @@ QT_MOC_CPP = \ qml/models/moc_peerlistsortproxy.cpp \ qml/models/moc_walletlistmodel.cpp \ qml/moc_appmode.cpp \ + qml/moc_bitcoinamount.cpp \ qml/moc_walletcontroller.cpp \ qt/moc_addressbookpage.cpp \ qt/moc_addresstablemodel.cpp \ @@ -128,6 +129,7 @@ BITCOIN_QT_H = \ qml/models/walletlistmodel.h \ qml/appmode.h \ qml/bitcoin.h \ + qml/bitcoinamount.h \ qml/guiconstants.h \ qml/imageprovider.h \ qml/util.h \ @@ -308,6 +310,7 @@ BITCOIN_QT_WALLET_CPP = \ BITCOIN_QML_BASE_CPP = \ qml/bitcoin.cpp \ + qml/bitcoinamount.cpp \ qml/components/blockclockdial.cpp \ qml/controls/linegraph.cpp \ qml/models/chainmodel.cpp \ diff --git a/src/qml/bitcoin.cpp b/src/qml/bitcoin.cpp index 0e5d0f9ce7..7b641c3054 100644 --- a/src/qml/bitcoin.cpp +++ b/src/qml/bitcoin.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #ifdef __ANDROID__ #include #endif @@ -317,6 +318,7 @@ int QmlGuiMain(int argc, char* argv[]) qmlRegisterType("org.bitcoincore.qt", 1, 0, "BlockClockDial"); qmlRegisterType("org.bitcoincore.qt", 1, 0, "LineGraph"); qmlRegisterUncreatableType("org.bitcoincore.qt", 1, 0, "PeerDetailsModel", ""); + qmlRegisterType("org.bitcoincore.qt", 1, 0, "BitcoinAmount"); engine.load(QUrl(QStringLiteral("qrc:///qml/pages/main.qml"))); diff --git a/src/qml/bitcoinamount.cpp b/src/qml/bitcoinamount.cpp new file mode 100644 index 0000000000..9abd30ad6d --- /dev/null +++ b/src/qml/bitcoinamount.cpp @@ -0,0 +1,127 @@ +// 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 + + +BitcoinAmount::BitcoinAmount(QObject *parent) : QObject(parent) +{ + m_unit = Unit::BTC; +} + +int BitcoinAmount::decimals(Unit unit) +{ + switch (unit) { + case Unit::BTC: return 8; + case Unit::SAT: return 0; + } // no default case, so the compiler can warn about missing cases + assert(false); +} + +QString BitcoinAmount::sanitize(const QString &text) +{ + QString result = text; + + // Remove any invalid characters + result.remove(QRegExp("[^0-9.]")); + + // Ensure only one decimal point + QStringList parts = result.split('.'); + if (parts.size() > 2) { + result = parts[0] + "." + parts[1]; + } + + // Limit decimal places to 8 + if (parts.size() == 2 && parts[1].length() > 8) { + result = parts[0] + "." + parts[1].left(8); + } + + return result; +} + +BitcoinAmount::Unit BitcoinAmount::unit() const +{ + return m_unit; +} + +void BitcoinAmount::setUnit(const Unit unit) +{ + m_unit = unit; + Q_EMIT unitChanged(); +} + +QString BitcoinAmount::unitLabel() const +{ + switch (m_unit) { + case Unit::BTC: return "₿"; + case Unit::SAT: return "Sat"; + } + assert(false); +} + +QString BitcoinAmount::amount() const +{ + return m_amount; +} + +void BitcoinAmount::setAmount(const QString& new_amount) +{ + m_amount = sanitize(new_amount); + Q_EMIT amountChanged(); +} + +long long BitcoinAmount::toSatoshis(QString& amount, const Unit unit) +{ + + int num_decimals = decimals(unit); + + QStringList parts = amount.remove(' ').split("."); + + QString whole = parts[0]; + QString decimals; + + if(parts.size() > 1) + { + decimals = parts[1]; + } + QString str = whole + decimals.leftJustified(num_decimals, '0', true); + + return str.toLongLong(); +} + +QString BitcoinAmount::convert(const QString &amount, Unit unit) +{ + if (amount == "") { + return amount; + } + + QString result = amount; + int decimalPosition = result.indexOf("."); + + if (decimalPosition == -1) { + decimalPosition = result.length(); + result.append("."); + } + + if (unit == Unit::BTC) { + int numDigitsAfterDecimal = result.length() - decimalPosition - 1; + if (numDigitsAfterDecimal < 8) { + result.append(QString(8 - numDigitsAfterDecimal, '0')); + } + result.remove(decimalPosition, 1); + } else if (unit == Unit::SAT) { + result.remove(decimalPosition, 1); + int newDecimalPosition = decimalPosition - 8; + if (newDecimalPosition < 1) { + result = QString("0").repeated(-newDecimalPosition) + result; + newDecimalPosition = 0; + } + result.insert(newDecimalPosition, "."); + } + + return result; +} diff --git a/src/qml/bitcoinamount.h b/src/qml/bitcoinamount.h new file mode 100644 index 0000000000..29615e4e6f --- /dev/null +++ b/src/qml/bitcoinamount.h @@ -0,0 +1,52 @@ +// 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_BITCOINAMOUNT_H +#define BITCOIN_QML_BITCOINAMOUNT_H + +#include +#include +#include + +class BitcoinAmount : public QObject +{ + Q_OBJECT + Q_PROPERTY(Unit unit READ unit WRITE setUnit NOTIFY unitChanged) + Q_PROPERTY(QString unitLabel READ unitLabel NOTIFY unitChanged) + Q_PROPERTY(QString amount READ amount WRITE setAmount NOTIFY amountChanged) + +public: + enum class Unit { + BTC, + SAT + }; + Q_ENUM(Unit) + + explicit BitcoinAmount(QObject *parent = nullptr); + + Unit unit() const; + void setUnit(Unit unit); + QString unitLabel() const; + QString amount() const; + void setAmount(const QString& new_amount); + +public Q_SLOTS: + QString sanitize(const QString &text); + QString convert(const QString &text, Unit unit); + +Q_SIGNALS: + void unitChanged(); + void unitLabelChanged(); + void amountChanged(); + +private: + long long toSatoshis(QString &amount, const Unit unit); + int decimals(Unit unit); + + Unit m_unit; + QString m_unitLabel; + QString m_amount; +}; + +#endif // BITCOIN_QML_BITCOINAMOUNT_H From 9fb36634c3ffa13c6afd583d9779351c2cf96f75 Mon Sep 17 00:00:00 2001 From: johnny9 <985648+johnny9@users.noreply.github.com> Date: Fri, 22 Nov 2024 01:19:33 -0500 Subject: [PATCH 06/10] qml: Introduce RequestPayment page This page contains the form for the user to fill out to create a payment request. --- src/Makefile.qt.include | 1 + src/qml/bitcoin_qml.qrc | 1 + src/qml/pages/wallet/DesktopWallets.qml | 3 +- src/qml/pages/wallet/RequestPayment.qml | 151 ++++++++++++++++++++++++ 4 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 src/qml/pages/wallet/RequestPayment.qml diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 33875650ab..c36b050572 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -444,6 +444,7 @@ QML_RES_QML = \ qml/pages/wallet/CreatePassword.qml \ qml/pages/wallet/CreateWalletWizard.qml \ qml/pages/wallet/DesktopWallets.qml \ + qml/pages/wallet/RequestPayment.qml \ qml/pages/wallet/WalletBadge.qml \ qml/pages/wallet/WalletSelect.qml diff --git a/src/qml/bitcoin_qml.qrc b/src/qml/bitcoin_qml.qrc index c16acb2b1f..16c2d88d9c 100644 --- a/src/qml/bitcoin_qml.qrc +++ b/src/qml/bitcoin_qml.qrc @@ -78,6 +78,7 @@ pages/wallet/CreatePassword.qml pages/wallet/CreateWalletWizard.qml pages/wallet/DesktopWallets.qml + pages/wallet/RequestPayment.qml pages/wallet/WalletBadge.qml pages/wallet/WalletSelect.qml diff --git a/src/qml/pages/wallet/DesktopWallets.qml b/src/qml/pages/wallet/DesktopWallets.qml index 59a7ac15e4..03c4231427 100644 --- a/src/qml/pages/wallet/DesktopWallets.qml +++ b/src/qml/pages/wallet/DesktopWallets.qml @@ -129,9 +129,8 @@ Page { id: sendTab CoreText { text: "Send" } } - Item { + RequestPayment { id: receiveTab - CoreText { text: "Receive" } } Item { id: blockClockTab diff --git a/src/qml/pages/wallet/RequestPayment.qml b/src/qml/pages/wallet/RequestPayment.qml new file mode 100644 index 0000000000..317b5190c8 --- /dev/null +++ b/src/qml/pages/wallet/RequestPayment.qml @@ -0,0 +1,151 @@ +// 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 org.bitcoincore.qt 1.0 + +import "../../controls" +import "../../components" +import "../settings" + +PageStack { + id: stackView + initialItem: pageComponent + + Component { + id: pageComponent + Page { + id: root + background: null + + header: NavigationBar2 { + id: navbar + centerItem: Item { + id: header + Layout.fillWidth: true + + CoreText { + anchors.left: parent.left + text: qsTr("Request a payment") + font.pixelSize: 21 + bold: true + } + } + } + + ScrollView { + clip: true + width: parent.width + height: parent.height + contentWidth: width + + ColumnLayout { + id: columnLayout + anchors.horizontalCenter: parent.horizontalCenter + width: Math.min(parent.width, 450) + spacing: 30 + + CoreText { + Layout.alignment: Qt.AlignHCenter + text: qsTr("All fields are optional.") + color: Theme.color.neutral7 + font.pixelSize: 15 + } + + LabeledTextInput { + id: label + Layout.fillWidth: true + labelText: qsTr("Label") + placeholderText: qsTr("Enter label...") + } + + Item { + BitcoinAmount { + id: bitcoinAmount + } + + height: 50 + Layout.fillWidth: true + CoreText { + anchors.left: parent.left + anchors.top: parent.top + color: Theme.color.neutral7 + text: "Amount" + font.pixelSize: 15 + } + + TextField { + id: amountInput + anchors.left: parent.left + anchors.bottom: parent.bottom + leftPadding: 0 + font.family: "Inter" + font.styleName: "Regular" + font.pixelSize: 18 + color: Theme.color.neutral9 + placeholderTextColor: Theme.color.neutral7 + background: Item {} + placeholderText: "0.00000000" + onTextEdited: { + amountInput.text = bitcoinAmount.sanitize(amountInput.text) + } + } + Item { + width: unitLabel.width + flipIcon.width + height: Math.max(unitLabel.height, flipIcon.height) + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + MouseArea { + anchors.fill: parent + onClicked: { + if (bitcoinAmount.unit == BitcoinAmount.BTC) { + amountInput.text = bitcoinAmount.convert(amountInput.text, BitcoinAmount.BTC) + bitcoinAmount.unit = BitcoinAmount.SAT + } else { + amountInput.text = bitcoinAmount.convert(amountInput.text, BitcoinAmount.SAT) + bitcoinAmount.unit = BitcoinAmount.BTC + } + } + } + CoreText { + id: unitLabel + anchors.right: flipIcon.left + anchors.verticalCenter: parent.verticalCenter + text: bitcoinAmount.unitLabel + font.pixelSize: 18 + color: Theme.color.neutral7 + } + Icon { + id: flipIcon + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + source: "image://images/flip-vertical" + color: Theme.color.neutral8 + size: 30 + } + } + } + + LabeledTextInput { + id: message + Layout.fillWidth: true + labelText: qsTr("Message") + placeholderText: qsTr("Enter message...") + } + + ContinueButton { + id: continueButton + Layout.preferredWidth: Math.min(300, parent.width - 2 * Layout.leftMargin) + Layout.leftMargin: 20 + Layout.rightMargin: 20 + Layout.alignment: Qt.AlignCenter + text: qsTr("Continue") + } + } + } + } + } +} From adff8f265a830ca4ef6651e0f7bbe3538efda7b5 Mon Sep 17 00:00:00 2001 From: johnny9 <985648+johnny9@users.noreply.github.com> Date: Fri, 22 Nov 2024 01:25:34 -0500 Subject: [PATCH 07/10] qml: Introduce RequestConfirmation page This page contains the details of a payment request. It is pushed onto the RequestPayment page's stack after the user has clicke don Continue. --- src/Makefile.qt.include | 1 + src/qml/bitcoin_qml.qrc | 1 + src/qml/pages/wallet/RequestConfirmation.qml | 167 +++++++++++++++++++ src/qml/pages/wallet/RequestPayment.qml | 7 + 4 files changed, 176 insertions(+) create mode 100644 src/qml/pages/wallet/RequestConfirmation.qml diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index c36b050572..775bc28a96 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -444,6 +444,7 @@ QML_RES_QML = \ qml/pages/wallet/CreatePassword.qml \ qml/pages/wallet/CreateWalletWizard.qml \ qml/pages/wallet/DesktopWallets.qml \ + qml/pages/wallet/RequestConfirmation.qml \ qml/pages/wallet/RequestPayment.qml \ qml/pages/wallet/WalletBadge.qml \ qml/pages/wallet/WalletSelect.qml diff --git a/src/qml/bitcoin_qml.qrc b/src/qml/bitcoin_qml.qrc index 16c2d88d9c..b82aeed18e 100644 --- a/src/qml/bitcoin_qml.qrc +++ b/src/qml/bitcoin_qml.qrc @@ -78,6 +78,7 @@ pages/wallet/CreatePassword.qml pages/wallet/CreateWalletWizard.qml pages/wallet/DesktopWallets.qml + pages/wallet/RequestConfirmation.qml pages/wallet/RequestPayment.qml pages/wallet/WalletBadge.qml pages/wallet/WalletSelect.qml diff --git a/src/qml/pages/wallet/RequestConfirmation.qml b/src/qml/pages/wallet/RequestConfirmation.qml new file mode 100644 index 0000000000..aed4931ab5 --- /dev/null +++ b/src/qml/pages/wallet/RequestConfirmation.qml @@ -0,0 +1,167 @@ +// 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 org.bitcoincore.qt 1.0 + +import "../../controls" +import "../../components" +import "../settings" + +Page { + id: root + background: null + property string label: "alice" + property string message: "payment for goods" + property string amount: "0.000" + + header: NavigationBar2 { + id: navbar + leftItem: NavButton { + iconSource: "image://images/caret-left" + text: qsTr("Back") + onClicked: { + root.StackView.view.pop() + } + } + centerItem: Item { + id: header + Layout.fillWidth: true + + CoreText { + anchors.left: parent.left + text: qsTr("Payment request") + font.pixelSize: 21 + bold: true + } + } + } + + ScrollView { + clip: true + width: parent.width + height: parent.height + contentWidth: width + + ColumnLayout { + id: columnLayout + anchors.horizontalCenter: parent.horizontalCenter + width: Math.min(parent.width, 450) + spacing: 30 + + Image { + width: 60 + height: 60 + Layout.alignment: Qt.AlignHCenter + source: "image://images/pending" + sourceSize.width: 60 + sourceSize.height: 60 + } + + CoreText { + Layout.alignment: Qt.AlignHCenter + text: qsTr("Created just now") + color: Theme.color.neutral7 + font.pixelSize: 18 + } + + LabeledTextInput { + id: labelInput + Layout.fillWidth: true + labelText: qsTr("Label") + visible: label != "" + enabled: false + text: label + } + + Item { + BitcoinAmount { + id: bitcoinAmount + } + + height: 50 + Layout.fillWidth: true + visible: amount != "" + CoreText { + anchors.left: parent.left + anchors.top: parent.top + color: Theme.color.neutral7 + text: qsTr("Amount") + font.pixelSize: 15 + } + + TextField { + id: bitcoinAmountText + anchors.left: parent.left + anchors.bottom: parent.bottom + leftPadding: 0 + font.family: "Inter" + font.styleName: "Regular" + font.pixelSize: 18 + color: Theme.color.neutral9 + placeholderTextColor: Theme.color.neutral7 + background: Item {} + placeholderText: "0.00000000" + text: request.amount + enabled: false + onTextChanged: { + bitcoinAmountText.text = bitcoinAmount.sanitize(bitcoinAmountText.text) + } + } + } + + + LabeledTextInput { + id: messageInput + Layout.fillWidth: true + labelText: qsTr("Message") + visible: message != "" + enabled: false + text: message + } + + Item { + height: addressLabel.height + addressText.height + Layout.fillWidth: true + CoreText { + id: addressLabel + anchors.left: parent.left + anchors.top: parent.top + color: Theme.color.neutral7 + text: qsTr("Address") + font.pixelSize: 15 + } + + CoreText { + id: addressText + anchors.left: parent.left + anchors.right: copyIcon.left + anchors.top: addressLabel.bottom + leftPadding: 0 + font.family: "Inter" + font.styleName: "Regular" + font.pixelSize: 18 + horizontalAlignment: Text.AlignLeft + color: Theme.color.neutral9 + text: "bc1q wvlv mha3 cvhy q6qz tjzu mq2d 63ff htzy xxu6 q8" + } + + Icon { + id: copyIcon + anchors.right: parent.right + anchors.verticalCenter: addressText.verticalCenter + source: "image://images/copy" + color: Theme.color.neutral8 + size: 30 + enabled: true + onClicked: { + Clipboard.setText(addressText.text) + } + } + } + } + } +} diff --git a/src/qml/pages/wallet/RequestPayment.qml b/src/qml/pages/wallet/RequestPayment.qml index 317b5190c8..0b04650f25 100644 --- a/src/qml/pages/wallet/RequestPayment.qml +++ b/src/qml/pages/wallet/RequestPayment.qml @@ -143,9 +143,16 @@ PageStack { Layout.rightMargin: 20 Layout.alignment: Qt.AlignCenter text: qsTr("Continue") + onClicked: stackView.push(confirmationComponent) } } } } } + + Component { + id: confirmationComponent + RequestConfirmation { + } + } } From 8d15b2ac2ffe502096db08d467efef0b9590d51b Mon Sep 17 00:00:00 2001 From: johnny9 <985648+johnny9@users.noreply.github.com> Date: Thu, 28 Nov 2024 23:53:50 -0500 Subject: [PATCH 08/10] qml: Allow TextFields to be selectable in RequestPayment --- src/qml/controls/LabeledTextInput.qml | 1 + src/qml/pages/wallet/RequestConfirmation.qml | 1 + src/qml/pages/wallet/RequestPayment.qml | 1 + 3 files changed, 3 insertions(+) diff --git a/src/qml/controls/LabeledTextInput.qml b/src/qml/controls/LabeledTextInput.qml index efa82f98f4..57030dd125 100644 --- a/src/qml/controls/LabeledTextInput.qml +++ b/src/qml/controls/LabeledTextInput.qml @@ -40,6 +40,7 @@ Item { color: Theme.color.neutral9 placeholderTextColor: Theme.color.neutral7 background: Item {} + selectByMouse: true onTextEdited: root.textEdited() } diff --git a/src/qml/pages/wallet/RequestConfirmation.qml b/src/qml/pages/wallet/RequestConfirmation.qml index aed4931ab5..215d677175 100644 --- a/src/qml/pages/wallet/RequestConfirmation.qml +++ b/src/qml/pages/wallet/RequestConfirmation.qml @@ -107,6 +107,7 @@ Page { placeholderText: "0.00000000" text: request.amount enabled: false + selectByMouse: true onTextChanged: { bitcoinAmountText.text = bitcoinAmount.sanitize(bitcoinAmountText.text) } diff --git a/src/qml/pages/wallet/RequestPayment.qml b/src/qml/pages/wallet/RequestPayment.qml index 0b04650f25..93f3b8d261 100644 --- a/src/qml/pages/wallet/RequestPayment.qml +++ b/src/qml/pages/wallet/RequestPayment.qml @@ -89,6 +89,7 @@ PageStack { placeholderTextColor: Theme.color.neutral7 background: Item {} placeholderText: "0.00000000" + selectByMouse: true onTextEdited: { amountInput.text = bitcoinAmount.sanitize(amountInput.text) } From 74e87a9d6ecb686a3d9cb423fa7e8bdc6a3f5139 Mon Sep 17 00:00:00 2001 From: johnny9 <985648+johnny9@users.noreply.github.com> Date: Mon, 9 Dec 2024 08:44:11 -0500 Subject: [PATCH 09/10] qml: Merge ReqeustPayment and RequestConfirmation into one form --- src/qml/pages/wallet/RequestConfirmation.qml | 168 ---------- src/qml/pages/wallet/RequestPayment.qml | 318 ++++++++++++------- 2 files changed, 199 insertions(+), 287 deletions(-) delete mode 100644 src/qml/pages/wallet/RequestConfirmation.qml diff --git a/src/qml/pages/wallet/RequestConfirmation.qml b/src/qml/pages/wallet/RequestConfirmation.qml deleted file mode 100644 index 215d677175..0000000000 --- a/src/qml/pages/wallet/RequestConfirmation.qml +++ /dev/null @@ -1,168 +0,0 @@ -// 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 org.bitcoincore.qt 1.0 - -import "../../controls" -import "../../components" -import "../settings" - -Page { - id: root - background: null - property string label: "alice" - property string message: "payment for goods" - property string amount: "0.000" - - header: NavigationBar2 { - id: navbar - leftItem: NavButton { - iconSource: "image://images/caret-left" - text: qsTr("Back") - onClicked: { - root.StackView.view.pop() - } - } - centerItem: Item { - id: header - Layout.fillWidth: true - - CoreText { - anchors.left: parent.left - text: qsTr("Payment request") - font.pixelSize: 21 - bold: true - } - } - } - - ScrollView { - clip: true - width: parent.width - height: parent.height - contentWidth: width - - ColumnLayout { - id: columnLayout - anchors.horizontalCenter: parent.horizontalCenter - width: Math.min(parent.width, 450) - spacing: 30 - - Image { - width: 60 - height: 60 - Layout.alignment: Qt.AlignHCenter - source: "image://images/pending" - sourceSize.width: 60 - sourceSize.height: 60 - } - - CoreText { - Layout.alignment: Qt.AlignHCenter - text: qsTr("Created just now") - color: Theme.color.neutral7 - font.pixelSize: 18 - } - - LabeledTextInput { - id: labelInput - Layout.fillWidth: true - labelText: qsTr("Label") - visible: label != "" - enabled: false - text: label - } - - Item { - BitcoinAmount { - id: bitcoinAmount - } - - height: 50 - Layout.fillWidth: true - visible: amount != "" - CoreText { - anchors.left: parent.left - anchors.top: parent.top - color: Theme.color.neutral7 - text: qsTr("Amount") - font.pixelSize: 15 - } - - TextField { - id: bitcoinAmountText - anchors.left: parent.left - anchors.bottom: parent.bottom - leftPadding: 0 - font.family: "Inter" - font.styleName: "Regular" - font.pixelSize: 18 - color: Theme.color.neutral9 - placeholderTextColor: Theme.color.neutral7 - background: Item {} - placeholderText: "0.00000000" - text: request.amount - enabled: false - selectByMouse: true - onTextChanged: { - bitcoinAmountText.text = bitcoinAmount.sanitize(bitcoinAmountText.text) - } - } - } - - - LabeledTextInput { - id: messageInput - Layout.fillWidth: true - labelText: qsTr("Message") - visible: message != "" - enabled: false - text: message - } - - Item { - height: addressLabel.height + addressText.height - Layout.fillWidth: true - CoreText { - id: addressLabel - anchors.left: parent.left - anchors.top: parent.top - color: Theme.color.neutral7 - text: qsTr("Address") - font.pixelSize: 15 - } - - CoreText { - id: addressText - anchors.left: parent.left - anchors.right: copyIcon.left - anchors.top: addressLabel.bottom - leftPadding: 0 - font.family: "Inter" - font.styleName: "Regular" - font.pixelSize: 18 - horizontalAlignment: Text.AlignLeft - color: Theme.color.neutral9 - text: "bc1q wvlv mha3 cvhy q6qz tjzu mq2d 63ff htzy xxu6 q8" - } - - Icon { - id: copyIcon - anchors.right: parent.right - anchors.verticalCenter: addressText.verticalCenter - source: "image://images/copy" - color: Theme.color.neutral8 - size: 30 - enabled: true - onClicked: { - Clipboard.setText(addressText.text) - } - } - } - } - } -} diff --git a/src/qml/pages/wallet/RequestPayment.qml b/src/qml/pages/wallet/RequestPayment.qml index 93f3b8d261..dc44ec7526 100644 --- a/src/qml/pages/wallet/RequestPayment.qml +++ b/src/qml/pages/wallet/RequestPayment.qml @@ -11,149 +11,229 @@ import "../../controls" import "../../components" import "../settings" -PageStack { - id: stackView - initialItem: pageComponent - - Component { - id: pageComponent - Page { - id: root - background: null - - header: NavigationBar2 { - id: navbar - centerItem: Item { - id: header - Layout.fillWidth: true +Page { + id: root + background: null + + property int requestCounter: 0 + + ScrollView { + clip: true + width: parent.width + height: parent.height + contentWidth: width + + CoreText { + id: title + anchors.left: contentRow.left + anchors.top: parent.top + anchors.topMargin: 20 + text: qsTr("Request a payment") + font.pixelSize: 21 + bold: true + } - CoreText { - anchors.left: parent.left - text: qsTr("Request a payment") - font.pixelSize: 21 - bold: true + RowLayout { + id: contentRow + anchors.top: title.bottom + anchors.topMargin: 40 + anchors.horizontalCenter: parent.horizontalCenter + spacing: 30 + ColumnLayout { + id: columnLayout + Layout.minimumWidth: 450 + Layout.maximumWidth: 470 + + spacing: 5 + + Item { + BitcoinAmount { + id: bitcoinAmount } - } - } - - ScrollView { - clip: true - width: parent.width - height: parent.height - contentWidth: width - - ColumnLayout { - id: columnLayout - anchors.horizontalCenter: parent.horizontalCenter - width: Math.min(parent.width, 450) - spacing: 30 + height: amountInput.height + Layout.fillWidth: true CoreText { - Layout.alignment: Qt.AlignHCenter - text: qsTr("All fields are optional.") - color: Theme.color.neutral7 - font.pixelSize: 15 + id: amountLabel + width: 110 + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + horizontalAlignment: Text.AlignLeft + color: Theme.color.neutral9 + text: "Amount" + font.pixelSize: 18 } - LabeledTextInput { - id: label - Layout.fillWidth: true - labelText: qsTr("Label") - placeholderText: qsTr("Enter label...") + TextField { + id: amountInput + anchors.left: amountLabel.right + anchors.verticalCenter: parent.verticalCenter + leftPadding: 0 + font.family: "Inter" + font.styleName: "Regular" + font.pixelSize: 18 + color: Theme.color.neutral9 + placeholderTextColor: Theme.color.neutral7 + background: Item {} + placeholderText: "0.00000000" + selectByMouse: true + onTextEdited: { + amountInput.text = bitcoinAmount.sanitize(amountInput.text) + } } - Item { - BitcoinAmount { - id: bitcoinAmount + width: unitLabel.width + flipIcon.width + height: Math.max(unitLabel.height, flipIcon.height) + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + MouseArea { + anchors.fill: parent + onClicked: { + if (bitcoinAmount.unit == BitcoinAmount.BTC) { + amountInput.text = bitcoinAmount.convert(amountInput.text, BitcoinAmount.BTC) + bitcoinAmount.unit = BitcoinAmount.SAT + } else { + amountInput.text = bitcoinAmount.convert(amountInput.text, BitcoinAmount.SAT) + bitcoinAmount.unit = BitcoinAmount.BTC + } + } } - - height: 50 - Layout.fillWidth: true CoreText { - anchors.left: parent.left - anchors.top: parent.top + id: unitLabel + anchors.right: flipIcon.left + anchors.verticalCenter: parent.verticalCenter + text: bitcoinAmount.unitLabel + font.pixelSize: 18 color: Theme.color.neutral7 - text: "Amount" - font.pixelSize: 15 } + Icon { + id: flipIcon + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + source: "image://images/flip-vertical" + color: Theme.color.neutral8 + size: 30 + } + } + } + + Separator { + Layout.fillWidth: true + } + + LabeledTextInput { + id: label + Layout.fillWidth: true + labelText: qsTr("Label") + placeholderText: qsTr("Enter label...") + } + + Separator { + Layout.fillWidth: true + } - TextField { - id: amountInput - anchors.left: parent.left - anchors.bottom: parent.bottom - leftPadding: 0 - font.family: "Inter" - font.styleName: "Regular" + LabeledTextInput { + id: message + Layout.fillWidth: true + labelText: qsTr("Message") + placeholderText: qsTr("Enter message...") + } + + Separator { + Layout.fillWidth: true + } + + Item { + Layout.fillWidth: true + Layout.minimumHeight: addressLabel.height + copyLabel.height + Layout.topMargin: 10 + height: addressLabel.height + copyLabel.height + CoreText { + id: addressLabel + anchors.left: parent.left + anchors.top: parent.top + horizontalAlignment: Text.AlignLeft + width: 110 + text: qsTr("Address") + font.pixelSize: 18 + color: Theme.color.neutral9 + } + CoreText { + id: copyLabel + anchors.left: parent.left + anchors.top: addressLabel.bottom + horizontalAlignment: Text.AlignLeft + width: 110 + text: qsTr("copy") + font.pixelSize: 18 + color: Theme.color.neutral7 + } + + Rectangle { + anchors.left: addressLabel.right + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + color: Theme.color.neutral2 + radius: 5 + CoreText { + id: address + anchors.fill: parent + anchors.leftMargin: 5 + horizontalAlignment: Text.AlignLeft font.pixelSize: 18 color: Theme.color.neutral9 - placeholderTextColor: Theme.color.neutral7 - background: Item {} - placeholderText: "0.00000000" - selectByMouse: true - onTextEdited: { - amountInput.text = bitcoinAmount.sanitize(amountInput.text) - } - } - Item { - width: unitLabel.width + flipIcon.width - height: Math.max(unitLabel.height, flipIcon.height) - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - MouseArea { - anchors.fill: parent - onClicked: { - if (bitcoinAmount.unit == BitcoinAmount.BTC) { - amountInput.text = bitcoinAmount.convert(amountInput.text, BitcoinAmount.BTC) - bitcoinAmount.unit = BitcoinAmount.SAT - } else { - amountInput.text = bitcoinAmount.convert(amountInput.text, BitcoinAmount.SAT) - bitcoinAmount.unit = BitcoinAmount.BTC - } - } - } - CoreText { - id: unitLabel - anchors.right: flipIcon.left - anchors.verticalCenter: parent.verticalCenter - text: bitcoinAmount.unitLabel - font.pixelSize: 18 - color: Theme.color.neutral7 - } - Icon { - id: flipIcon - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - source: "image://images/flip-vertical" - color: Theme.color.neutral8 - size: 30 - } + wrap: true } } + } - LabeledTextInput { - id: message - Layout.fillWidth: true - labelText: qsTr("Message") - placeholderText: qsTr("Enter message...") + ContinueButton { + id: continueButton + Layout.fillWidth: true + Layout.topMargin: 30 + text: qsTr("Create bitcoin address") + onClicked: { + if (!clearRequest.visible) { + requestCounter = requestCounter + 1 + clearRequest.visible = true + title.text = qsTr("Payment request #" + requestCounter) + address.text = "bc1q f5xe y2tf 89k9 zy6k gnru wszy 5fsa truy 9te1 bu" + continueButton.text = qsTr("Copy payment request") + } } + } - ContinueButton { - id: continueButton - Layout.preferredWidth: Math.min(300, parent.width - 2 * Layout.leftMargin) - Layout.leftMargin: 20 - Layout.rightMargin: 20 - Layout.alignment: Qt.AlignCenter - text: qsTr("Continue") - onClicked: stackView.push(confirmationComponent) + ContinueButton { + id: clearRequest + Layout.fillWidth: true + Layout.topMargin: 10 + visible: false + borderColor: Theme.color.neutral6 + borderHoverColor: Theme.color.orangeLight1 + borderPressedColor: Theme.color.orangeLight2 + backgroundColor: "transparent" + backgroundHoverColor: "transparent" + backgroundPressedColor: "transparent" + text: qsTr("Clear") + onClicked: { + clearRequest.visible = false + title.text = qsTr("Request a payment") + address.text = "" + continueButton.text = qsTr("Create bitcoin address") } } } - } - } - Component { - id: confirmationComponent - RequestConfirmation { + Rectangle { + id: qrPlaceholder + Layout.alignment: Qt.AlignTop + Layout.minimumWidth: 150 + Layout.maximumWidth: 150 + color: Theme.color.neutral2 + width: 150 + height: 150 + } } } } From 622a11401d781d13c33fb0f86d5fafacd22f9bf0 Mon Sep 17 00:00:00 2001 From: johnny9 <985648+johnny9@users.noreply.github.com> Date: Mon, 9 Dec 2024 09:43:53 -0500 Subject: [PATCH 10/10] qml: Remove RequestConfirmation.qml from builds --- src/Makefile.qt.include | 1 - src/qml/bitcoin_qml.qrc | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 775bc28a96..c36b050572 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -444,7 +444,6 @@ QML_RES_QML = \ qml/pages/wallet/CreatePassword.qml \ qml/pages/wallet/CreateWalletWizard.qml \ qml/pages/wallet/DesktopWallets.qml \ - qml/pages/wallet/RequestConfirmation.qml \ qml/pages/wallet/RequestPayment.qml \ qml/pages/wallet/WalletBadge.qml \ qml/pages/wallet/WalletSelect.qml diff --git a/src/qml/bitcoin_qml.qrc b/src/qml/bitcoin_qml.qrc index b82aeed18e..16c2d88d9c 100644 --- a/src/qml/bitcoin_qml.qrc +++ b/src/qml/bitcoin_qml.qrc @@ -78,7 +78,6 @@ pages/wallet/CreatePassword.qml pages/wallet/CreateWalletWizard.qml pages/wallet/DesktopWallets.qml - pages/wallet/RequestConfirmation.qml pages/wallet/RequestPayment.qml pages/wallet/WalletBadge.qml pages/wallet/WalletSelect.qml