diff --git a/src/qt/forms/receiverequestdialog.ui b/src/qt/forms/receiverequestdialog.ui index 9f896ee3b..f6d472346 100644 --- a/src/qt/forms/receiverequestdialog.ui +++ b/src/qt/forms/receiverequestdialog.ui @@ -1,168 +1,326 @@ ReceiveRequestDialog 0 0 - 487 - 597 + 413 + 229 - - - - - - 0 - 0 - - - - - 300 - 320 - - - - QR Code + + Request payment to ... + + + + QLayout::SetFixedSize + + + + + QR image + + + Qt::NoTextInteraction + + + + + + + + 75 + true + + + + Payment information + + + Qt::NoTextInteraction + + + + + + + + 75 + true + + + + URI: Qt::PlainText - - Qt::AlignCenter + + Qt::NoTextInteraction + + + + + + + bitcoin:BC1... + + + Qt::RichText true + + Qt::TextSelectableByMouse + + + + + + + + 75 + true + + + + Address: + + + Qt::NoTextInteraction + - - - - - 0 - 0 - - - - - 0 - 50 - - - - QFrame::NoFrame - - - QFrame::Plain - - + + + + bc1... + + + Qt::PlainText + + + Qt::TextSelectableByMouse + + + + + + + + 75 + true + + + + Amount: + + + Qt::NoTextInteraction + + + + + + + 0.00000000 BTC + + + Qt::PlainText + + + Qt::TextSelectableByMouse + + + + + + + + 75 + true + + + + Label: + + + Qt::NoTextInteraction + + + + + + + label content + + + Qt::PlainText + + + true + + + Qt::TextSelectableByMouse + + + + + + + + 75 + true + + + + Message: + + + Qt::NoTextInteraction + + + + + + + message content + + + Qt::PlainText + + + true + + + Qt::TextSelectableByMouse + + + + + + + + 75 + true + + + + Wallet: + + + Qt::NoTextInteraction + + + + + + + wallet name + + + Qt::PlainText + + true - Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + Qt::TextSelectableByMouse - + Copy &URI false Copy &Address false &Save Image... false Qt::Horizontal 40 20 + + Qt::StrongFocus + - QDialogButtonBox::Close + QDialogButtonBox::Ok QRImageWidget QLabel
qt/qrimagewidget.h
+ + buttonBox + btnCopyURI + btnCopyAddress + btnSaveAs + - - buttonBox - rejected() - ReceiveRequestDialog - reject() - - - 452 - 573 - - - 243 - 298 - - - buttonBox accepted() ReceiveRequestDialog accept() - 452 - 573 + 135 + 230 - 243 - 298 + 135 + 126
diff --git a/src/qt/receiverequestdialog.cpp b/src/qt/receiverequestdialog.cpp index de327ba44..2e5d3407f 100644 --- a/src/qt/receiverequestdialog.cpp +++ b/src/qt/receiverequestdialog.cpp @@ -1,108 +1,108 @@ // Copyright (c) 2011-2016 The Bitcoin Core developers // Copyright (c) 2017-2019 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include -#include -#include +#include +#include #if defined(HAVE_CONFIG_H) #include /* for USE_QRCODE */ #endif ReceiveRequestDialog::ReceiveRequestDialog(QWidget *parent) : QDialog(parent), ui(new Ui::ReceiveRequestDialog), model(nullptr) { ui->setupUi(this); - -#ifndef USE_QRCODE - ui->btnSaveAs->setVisible(false); - ui->lblQRCode->setVisible(false); -#endif - - connect(ui->btnSaveAs, &QPushButton::clicked, ui->lblQRCode, - &QRImageWidget::saveImage); - GUIUtil::handleCloseWindowShortcut(this); } ReceiveRequestDialog::~ReceiveRequestDialog() { delete ui; } void ReceiveRequestDialog::setModel(WalletModel *_model) { this->model = _model; if (_model) { connect(_model->getOptionsModel(), &OptionsModel::displayUnitChanged, this, &ReceiveRequestDialog::update); } // update the display unit if necessary update(); } void ReceiveRequestDialog::setInfo(const SendCoinsRecipient &_info) { this->info = _info; - update(); -} + setWindowTitle(tr("Request payment to %1") + .arg(info.label.isEmpty() ? info.address : info.label)); + QString uri = GUIUtil::formatBitcoinURI(info); -void ReceiveRequestDialog::update() { - if (!model) { - return; - } - QString target = info.label; - if (target.isEmpty()) { - target = info.address; +#ifdef USE_QRCODE + if (ui->qr_code->setQR(uri, info.address)) { + connect(ui->btnSaveAs, &QPushButton::clicked, ui->qr_code, + &QRImageWidget::saveImage); + } else { + ui->btnSaveAs->setEnabled(false); } - setWindowTitle(tr("Request payment to %1").arg(target)); +#else + ui->btnSaveAs->hide(); + ui->qr_code->hide(); +#endif + + ui->uri_content->setText("" + + GUIUtil::HtmlEscape(uri) + ""); + ui->address_content->setText(info.address); + + if (info.amount == Amount::zero()) { + ui->amount_tag->hide(); + ui->amount_content->hide(); + } // Amount is set in update() slot. + update(); - QString uri = GUIUtil::formatBitcoinURI(info); - ui->btnSaveAs->setEnabled(false); - QString html; - html += ""; - html += "" + tr("Payment information") + "
"; - html += "" + tr("URI") + ": "; - html += "" + GUIUtil::HtmlEscape(uri) + "
"; - html += "" + tr("Address") + - ": " + GUIUtil::HtmlEscape(info.address) + "
"; - if (info.amount != Amount::zero()) { - html += "" + tr("Amount") + ": " + - BitcoinUnits::formatHtmlWithUnit( - model->getOptionsModel()->getDisplayUnit(), info.amount) + - "
"; - } if (!info.label.isEmpty()) { - html += "" + tr("Label") + - ": " + GUIUtil::HtmlEscape(info.label) + "
"; + ui->label_content->setText(info.label); + } else { + ui->label_tag->hide(); + ui->label_content->hide(); } + if (!info.message.isEmpty()) { - html += "" + tr("Message") + - ": " + GUIUtil::HtmlEscape(info.message) + "
"; + ui->message_content->setText(info.message); + } else { + ui->message_tag->hide(); + ui->message_content->hide(); } - if (model->isMultiwallet()) { - html += "" + tr("Wallet") + - ": " + GUIUtil::HtmlEscape(model->getWalletName()) + "
"; + + if (!model->getWalletName().isEmpty()) { + ui->wallet_content->setText(model->getWalletName()); + } else { + ui->wallet_tag->hide(); + ui->wallet_content->hide(); } - ui->outUri->setText(html); +} - if (ui->lblQRCode->setQR(uri, info.address)) { - ui->btnSaveAs->setEnabled(true); +void ReceiveRequestDialog::update() { + if (!model) { + return; } + ui->amount_content->setText(BitcoinUnits::formatWithUnit( + model->getOptionsModel()->getDisplayUnit(), info.amount)); } void ReceiveRequestDialog::on_btnCopyURI_clicked() { GUIUtil::setClipboard(GUIUtil::formatBitcoinURI(info)); } void ReceiveRequestDialog::on_btnCopyAddress_clicked() { GUIUtil::setClipboard(info.address); } diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index 255463669..661124961 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -1,281 +1,315 @@ // Copyright (c) 2015-2021 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { //! Press "Yes" or "Cancel" buttons in modal send confirmation dialog. void ConfirmSend(QString *text = nullptr, bool cancel = false) { QTimer::singleShot(0, Qt::PreciseTimer, [text, cancel]() { for (QWidget *widget : QApplication::topLevelWidgets()) { if (widget->inherits("SendConfirmationDialog")) { SendConfirmationDialog *dialog = qobject_cast(widget); if (text) { *text = dialog->text(); } QAbstractButton *button = dialog->button( cancel ? QMessageBox::Cancel : QMessageBox::Yes); button->setEnabled(true); button->click(); } } }); } //! Send coins to address and return txid. TxId SendCoins(CWallet &wallet, SendCoinsDialog &sendCoinsDialog, const CTxDestination &address, Amount amount) { QVBoxLayout *entries = sendCoinsDialog.findChild("entries"); SendCoinsEntry *entry = qobject_cast(entries->itemAt(0)->widget()); entry->findChild("payTo")->setText( QString::fromStdString(EncodeCashAddr(address, Params()))); entry->findChild("payAmount")->setValue(amount); TxId txid; boost::signals2::scoped_connection c = wallet.NotifyTransactionChanged.connect( [&txid](CWallet *, const TxId &hash, ChangeType status) { if (status == CT_NEW) { txid = hash; } }); ConfirmSend(); bool invoked = QMetaObject::invokeMethod(&sendCoinsDialog, "on_sendButton_clicked"); assert(invoked); return txid; } //! Find index of txid in transaction list. QModelIndex FindTx(const QAbstractItemModel &model, const uint256 &txid) { QString hash = QString::fromStdString(txid.ToString()); int rows = model.rowCount({}); for (int row = 0; row < rows; ++row) { QModelIndex index = model.index(row, 0, {}); if (model.data(index, TransactionTableModel::TxHashRole) == hash) { return index; } } return {}; } //! Simple qt wallet tests. // // Test widgets can be debugged interactively calling show() on them and // manually running the event loop, e.g.: // // sendCoinsDialog.show(); // QEventLoop().exec(); // // This also requires overriding the default minimal Qt platform: // // QT_QPA_PLATFORM=xcb src/qt/test/test_bitcoin-qt # Linux // QT_QPA_PLATFORM=windows src/qt/test/test_bitcoin-qt # Windows // QT_QPA_PLATFORM=cocoa src/qt/test/test_bitcoin-qt # macOS void TestGUI(interfaces::Node &node) { // Set up wallet and chain with 105 blocks (5 mature blocks for spending). TestChain100Setup test; for (int i = 0; i < 5; ++i) { test.CreateAndProcessBlock( {}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey())); } node.setContext(&test.m_node); std::shared_ptr wallet = std::make_shared(node.context()->chain.get(), WalletLocation(), CreateMockWalletDatabase()); bool firstRun; wallet->LoadWallet(firstRun); { auto spk_man = wallet->GetOrCreateLegacyScriptPubKeyMan(); LOCK2(wallet->cs_wallet, spk_man->cs_KeyStore); wallet->SetAddressBook( GetDestinationForKey(test.coinbaseKey.GetPubKey(), wallet->m_default_address_type), "", "receive"); spk_man->AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey()); wallet->SetLastBlockProcessed(105, ::ChainActive().Tip()->GetBlockHash()); } { WalletRescanReserver reserver(*wallet); reserver.reserve(); CWallet::ScanResult result = wallet->ScanForWalletTransactions( Params().GetConsensus().hashGenesisBlock, 0 /* block height */, {} /* max height */, reserver, true /* fUpdate */); QCOMPARE(result.status, CWallet::ScanResult::SUCCESS); QCOMPARE(result.last_scanned_block, ::ChainActive().Tip()->GetBlockHash()); QVERIFY(result.last_failed_block.IsNull()); } wallet->SetBroadcastTransactions(true); // Create widgets for sending coins and listing transactions. std::unique_ptr platformStyle( PlatformStyle::instantiate("other")); OptionsModel optionsModel; ClientModel clientModel(node, &optionsModel); AddWallet(wallet); WalletModel walletModel(interfaces::MakeWallet(wallet), clientModel, platformStyle.get()); RemoveWallet(wallet); SendCoinsDialog sendCoinsDialog(platformStyle.get(), &walletModel); { // Check balance in send dialog QLabel *balanceLabel = sendCoinsDialog.findChild("labelBalance"); QString balanceText = balanceLabel->text(); int unit = walletModel.getOptionsModel()->getDisplayUnit(); Amount balance = walletModel.wallet().getBalance(); QString balanceComparison = BitcoinUnits::formatWithUnit( unit, balance, false, BitcoinUnits::separatorAlways); QCOMPARE(balanceText, balanceComparison); } // Send two transactions, and verify they are added to transaction list. TransactionTableModel *transactionTableModel = walletModel.getTransactionTableModel(); QCOMPARE(transactionTableModel->rowCount({}), 105); TxId txid1 = SendCoins(*wallet.get(), sendCoinsDialog, CTxDestination(PKHash()), 5 * COIN); TxId txid2 = SendCoins(*wallet.get(), sendCoinsDialog, CTxDestination(PKHash()), 10 * COIN); QCOMPARE(transactionTableModel->rowCount({}), 107); QVERIFY(FindTx(*transactionTableModel, txid1).isValid()); QVERIFY(FindTx(*transactionTableModel, txid2).isValid()); // Check current balance on OverviewPage OverviewPage overviewPage(platformStyle.get()); overviewPage.setWalletModel(&walletModel); QLabel *balanceLabel = overviewPage.findChild("labelBalance"); QString balanceText = balanceLabel->text().trimmed(); int unit = walletModel.getOptionsModel()->getDisplayUnit(); Amount balance = walletModel.wallet().getBalance(); QString balanceComparison = BitcoinUnits::formatWithUnit( unit, balance, false, BitcoinUnits::separatorAlways); QCOMPARE(balanceText, balanceComparison); // Check Request Payment button ReceiveCoinsDialog receiveCoinsDialog(platformStyle.get()); receiveCoinsDialog.setModel(&walletModel); RecentRequestsTableModel *requestTableModel = walletModel.getRecentRequestsTableModel(); // Label input QLineEdit *labelInput = receiveCoinsDialog.findChild("reqLabel"); labelInput->setText("TEST_LABEL_1"); // Amount input BitcoinAmountField *amountInput = receiveCoinsDialog.findChild("reqAmount"); amountInput->setValue(1 * SATOSHI); // Message input QLineEdit *messageInput = receiveCoinsDialog.findChild("reqMessage"); messageInput->setText("TEST_MESSAGE_1"); int initialRowCount = requestTableModel->rowCount({}); QPushButton *requestPaymentButton = receiveCoinsDialog.findChild("receiveButton"); requestPaymentButton->click(); for (QWidget *widget : QApplication::topLevelWidgets()) { if (widget->inherits("ReceiveRequestDialog")) { ReceiveRequestDialog *receiveRequestDialog = qobject_cast(widget); - QTextEdit *rlist = - receiveRequestDialog->QObject::findChild("outUri"); - QString paymentText = rlist->toPlainText(); - QStringList paymentTextList = paymentText.split('\n'); - QCOMPARE(paymentTextList.at(0), QString("Payment information")); - QVERIFY(paymentTextList.at(1).indexOf(QString("URI: ecregtest:")) != - -1); - QVERIFY(paymentTextList.at(2).indexOf(QString("Address:")) != -1); - QCOMPARE(paymentTextList.at(3), - QString("Amount: 0.01 ") + + QCOMPARE(receiveRequestDialog + ->QObject::findChild("payment_header") + ->text(), + QString("Payment information")); + QCOMPARE( + receiveRequestDialog->QObject::findChild("uri_tag") + ->text(), + QString("URI:")); + QString uri = receiveRequestDialog + ->QObject::findChild("uri_content") + ->text(); + QCOMPARE(uri.count("ecregtest:"), 2); + QCOMPARE(receiveRequestDialog + ->QObject::findChild("address_tag") + ->text(), + QString("Address:")); + + QCOMPARE(uri.count("amount=0.01"), 2); + QCOMPARE( + receiveRequestDialog->QObject::findChild("amount_tag") + ->text(), + QString("Amount:")); + QCOMPARE(receiveRequestDialog + ->QObject::findChild("amount_content") + ->text(), + QString("0.01 ") + QString::fromStdString(Currency::get().ticker)); - QCOMPARE(paymentTextList.at(4), QString("Label: TEST_LABEL_1")); - QCOMPARE(paymentTextList.at(5), QString("Message: TEST_MESSAGE_1")); + + QCOMPARE(uri.count("label=TEST_LABEL_1"), 2); + QCOMPARE( + receiveRequestDialog->QObject::findChild("label_tag") + ->text(), + QString("Label:")); + QCOMPARE(receiveRequestDialog + ->QObject::findChild("label_content") + ->text(), + QString("TEST_LABEL_1")); + + QCOMPARE(uri.count("message=TEST_MESSAGE_1"), 2); + QCOMPARE(receiveRequestDialog + ->QObject::findChild("message_tag") + ->text(), + QString("Message:")); + QCOMPARE(receiveRequestDialog + ->QObject::findChild("message_content") + ->text(), + QString("TEST_MESSAGE_1")); } } // Clear button QPushButton *clearButton = receiveCoinsDialog.findChild("clearButton"); clearButton->click(); QCOMPARE(labelInput->text(), QString("")); QCOMPARE(amountInput->value(), Amount::zero()); QCOMPARE(messageInput->text(), QString("")); // Check addition to history int currentRowCount = requestTableModel->rowCount({}); QCOMPARE(currentRowCount, initialRowCount + 1); // Check Remove button QTableView *table = receiveCoinsDialog.findChild("recentRequestsView"); table->selectRow(currentRowCount - 1); QPushButton *removeRequestButton = receiveCoinsDialog.findChild("removeRequestButton"); removeRequestButton->click(); QCOMPARE(requestTableModel->rowCount({}), currentRowCount - 1); } } // namespace void WalletTests::walletTests() { #ifdef Q_OS_MAC if (QApplication::platformName() == "minimal") { // Disable for mac on "minimal" platform to avoid crashes inside the Qt // framework when it tries to look up unimplemented cocoa functions, // and fails to handle returned nulls // (https://bugreports.qt.io/browse/QTBUG-49686). QWARN( "Skipping WalletTests on mac build with 'minimal' platform set due " "to Qt bugs. To run AppTests, invoke with 'QT_QPA_PLATFORM=cocoa " "test_bitcoin-qt' on mac, or else use a linux or windows build."); return; } #endif TestGUI(m_node); }