From d85563efafc6df6f0ab9ca6ee909d9e394276605 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Tue, 7 May 2019 12:26:12 +0000 Subject: [PATCH 1/3] GUI: QValidatedLineEdit: Add support for a warning-but-valid state --- src/qt/guiconstants.h | 2 ++ src/qt/qvalidatedlineedit.cpp | 48 +++++++++++++++++++++++++++++++---- src/qt/qvalidatedlineedit.h | 8 +++++- 3 files changed, 52 insertions(+), 6 deletions(-) diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index fcdf6056c95..329601ed974 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -26,6 +26,8 @@ static const bool DEFAULT_SPLASHSCREEN = true; /* Invalid field background style */ #define STYLE_INVALID "background:#FF8080" +/* "Warning" field background style */ +#define STYLE_INCORRECT "background:#FFFF80" /* Transaction list -- unconfirmed transaction */ #define COLOR_UNCONFIRMED QColor(128, 128, 128) diff --git a/src/qt/qvalidatedlineedit.cpp b/src/qt/qvalidatedlineedit.cpp index bd4df75d23f..3c3009ae6ac 100644 --- a/src/qt/qvalidatedlineedit.cpp +++ b/src/qt/qvalidatedlineedit.cpp @@ -15,22 +15,34 @@ QValidatedLineEdit::QValidatedLineEdit(QWidget *parent) : connect(this, &QValidatedLineEdit::textChanged, this, &QValidatedLineEdit::markValid); } +QValidatedLineEdit::~QValidatedLineEdit() +{ + delete m_warning_validator; +} + void QValidatedLineEdit::setText(const QString& text) { QLineEdit::setText(text); checkValidity(); } -void QValidatedLineEdit::setValid(bool _valid) +void QValidatedLineEdit::setValid(bool _valid, bool with_warning) { if(_valid == this->valid) { - return; + if (with_warning == m_has_warning || !valid) { + return; + } } if(_valid) { - setStyleSheet(""); + m_has_warning = with_warning; + if (with_warning) { + setStyleSheet("QValidatedLineEdit { " STYLE_INCORRECT "}"); + } else { + setStyleSheet(""); + } } else { @@ -84,13 +96,14 @@ void QValidatedLineEdit::setEnabled(bool enabled) void QValidatedLineEdit::checkValidity() { + const bool has_warning = checkWarning(); if (text().isEmpty()) { setValid(true); } else if (hasAcceptableInput()) { - setValid(true); + setValid(/* valid= */ true, has_warning); // Check contents on focus out if (checkValidator) @@ -98,7 +111,7 @@ void QValidatedLineEdit::checkValidity() QString address = text(); int pos = 0; if (checkValidator->validate(address, pos) == QValidator::Acceptable) - setValid(true); + setValid(/* valid= */ true, has_warning); else setValid(false); } @@ -128,3 +141,28 @@ bool QValidatedLineEdit::isValid() return valid; } + +void QValidatedLineEdit::setWarningValidator(const QValidator *v) +{ + delete m_warning_validator; + m_warning_validator = v; + checkValidity(); +} + +bool QValidatedLineEdit::checkWarning() const +{ + if (m_warning_validator && !text().isEmpty()) { + QString address = text(); + int pos = 0; + if (m_warning_validator->validate(address, pos) != QValidator::Acceptable) { + return true; + } + } + + return false; +} + +bool QValidatedLineEdit::hasWarning() const +{ + return m_has_warning; +} diff --git a/src/qt/qvalidatedlineedit.h b/src/qt/qvalidatedlineedit.h index 12d35aa2645..a9154a10629 100644 --- a/src/qt/qvalidatedlineedit.h +++ b/src/qt/qvalidatedlineedit.h @@ -16,9 +16,12 @@ class QValidatedLineEdit : public QLineEdit public: explicit QValidatedLineEdit(QWidget *parent); + ~QValidatedLineEdit(); void clear(); void setCheckValidator(const QValidator *v); bool isValid(); + void setWarningValidator(const QValidator *); + bool hasWarning() const; protected: void focusInEvent(QFocusEvent *evt) override; @@ -27,10 +30,12 @@ class QValidatedLineEdit : public QLineEdit private: bool valid; const QValidator *checkValidator; + bool m_has_warning{false}; + const QValidator *m_warning_validator{nullptr}; public Q_SLOTS: void setText(const QString&); - void setValid(bool valid); + void setValid(bool valid, bool with_warning=false); void setEnabled(bool enabled); Q_SIGNALS: @@ -39,6 +44,7 @@ public Q_SLOTS: private Q_SLOTS: void markValid(); void checkValidity(); + bool checkWarning() const; }; #endif // BITCOIN_QT_QVALIDATEDLINEEDIT_H From e0a0b288e0996d2d75803c103228514723dc7688 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Thu, 13 Feb 2020 00:26:10 +0000 Subject: [PATCH 2/3] GUI: Support returning positions from BitcoinAddress{Entry,Check}Validator::validate --- src/qt/bitcoinaddressvalidator.cpp | 24 +++++++++++++++++------- src/qt/bitcoinaddressvalidator.h | 9 ++++++--- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/qt/bitcoinaddressvalidator.cpp b/src/qt/bitcoinaddressvalidator.cpp index a2adca6eac7..ec6acb19986 100644 --- a/src/qt/bitcoinaddressvalidator.cpp +++ b/src/qt/bitcoinaddressvalidator.cpp @@ -6,6 +6,8 @@ #include +#include + /* Base58 characters are: "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" @@ -20,10 +22,8 @@ BitcoinAddressEntryValidator::BitcoinAddressEntryValidator(QObject *parent) : { } -QValidator::State BitcoinAddressEntryValidator::validate(QString &input, int &pos) const +QValidator::State BitcoinAddressEntryValidator::validate(QString &input, std::vector&error_locations) const { - Q_UNUSED(pos); - // Empty address is "intermediate" input if (input.isEmpty()) return QValidator::Intermediate; @@ -73,6 +73,7 @@ QValidator::State BitcoinAddressEntryValidator::validate(QString &input, int &po } else { + error_locations.push_back(idx); state = QValidator::Invalid; } } @@ -80,16 +81,25 @@ QValidator::State BitcoinAddressEntryValidator::validate(QString &input, int &po return state; } +QValidator::State BitcoinAddressEntryValidator::validate(QString &input, int &pos) const +{ + std::vector error_locations; + const auto ret = validate(input, error_locations); + if (!error_locations.empty()) pos = error_locations.at(0); + return ret; +} + BitcoinAddressCheckValidator::BitcoinAddressCheckValidator(QObject *parent) : - QValidator(parent) + BitcoinAddressEntryValidator(parent) { } -QValidator::State BitcoinAddressCheckValidator::validate(QString &input, int &pos) const +QValidator::State BitcoinAddressCheckValidator::validate(QString &input, std::vector&error_locations) const { - Q_UNUSED(pos); // Validate the passed Bitcoin address - if (IsValidDestinationString(input.toStdString())) { + std::string error_msg; + CTxDestination dest = DecodeDestination(input.toStdString(), error_msg, &error_locations); + if (IsValidDestination(dest)) { return QValidator::Acceptable; } diff --git a/src/qt/bitcoinaddressvalidator.h b/src/qt/bitcoinaddressvalidator.h index ae698c72a6b..2b40019a792 100644 --- a/src/qt/bitcoinaddressvalidator.h +++ b/src/qt/bitcoinaddressvalidator.h @@ -7,6 +7,8 @@ #include +#include + /** Base58 entry widget validator, checks for valid characters and * removes some whitespace. */ @@ -17,19 +19,20 @@ class BitcoinAddressEntryValidator : public QValidator public: explicit BitcoinAddressEntryValidator(QObject *parent); - State validate(QString &input, int &pos) const override; + virtual State validate(QString &input, std::vector&error_locations) const; + virtual State validate(QString &input, int &pos) const override; }; /** Bitcoin address widget validator, checks for a valid bitcoin address. */ -class BitcoinAddressCheckValidator : public QValidator +class BitcoinAddressCheckValidator : public BitcoinAddressEntryValidator { Q_OBJECT public: explicit BitcoinAddressCheckValidator(QObject *parent); - State validate(QString &input, int &pos) const override; + State validate(QString &input, std::vector&error_locations) const override; }; #endif // BITCOIN_QT_BITCOINADDRESSVALIDATOR_H From 539beeaae85133a1b3d331a951262c9a70fe0573 Mon Sep 17 00:00:00 2001 From: Luke Dashjr Date: Thu, 13 Feb 2020 02:24:11 +0000 Subject: [PATCH 3/3] GUI: Point out position of invalid characters in Bech32 addresses --- src/qt/qvalidatedlineedit.cpp | 48 ++++++++++++++++++++++++++++++----- src/qt/qvalidatedlineedit.h | 2 +- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/src/qt/qvalidatedlineedit.cpp b/src/qt/qvalidatedlineedit.cpp index 3c3009ae6ac..8e6fe00d2cf 100644 --- a/src/qt/qvalidatedlineedit.cpp +++ b/src/qt/qvalidatedlineedit.cpp @@ -7,6 +7,13 @@ #include #include +#include +#include +#include +#include +#include +#include + QValidatedLineEdit::QValidatedLineEdit(QWidget *parent) : QLineEdit(parent), valid(true), @@ -26,15 +33,17 @@ void QValidatedLineEdit::setText(const QString& text) checkValidity(); } -void QValidatedLineEdit::setValid(bool _valid, bool with_warning) +void QValidatedLineEdit::setValid(bool _valid, bool with_warning, const std::vector&error_locations) { - if(_valid == this->valid) + if(_valid && this->valid) { - if (with_warning == m_has_warning || !valid) { + if (with_warning == m_has_warning) { return; } } + QList attributes; + if(_valid) { m_has_warning = with_warning; @@ -47,7 +56,22 @@ void QValidatedLineEdit::setValid(bool _valid, bool with_warning) else { setStyleSheet("QValidatedLineEdit { " STYLE_INVALID "}"); + if (!error_locations.empty()) { + QTextCharFormat format; + format.setFontUnderline(true); + format.setUnderlineStyle(QTextCharFormat::SpellCheckUnderline); + format.setUnderlineColor(Qt::yellow); + format.setForeground(Qt::yellow); + format.setFontWeight(QFont::Bold); + for (auto error_pos : error_locations) { + attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, error_pos - cursorPosition(), /*length=*/ 1, format)); + } + } } + + QInputMethodEvent event(QString(), attributes); + QCoreApplication::sendEvent(this, &event); + this->valid = _valid; } @@ -109,11 +133,21 @@ void QValidatedLineEdit::checkValidity() if (checkValidator) { QString address = text(); - int pos = 0; - if (checkValidator->validate(address, pos) == QValidator::Acceptable) + QValidator::State validation_result; + std::vector error_locations; + const BitcoinAddressEntryValidator * const address_validator = dynamic_cast(checkValidator); + if (address_validator) { + validation_result = address_validator->validate(address, error_locations); + } else { + int pos = 0; + validation_result = checkValidator->validate(address, pos); + error_locations.push_back(pos); + } + if (validation_result == QValidator::Acceptable) { setValid(/* valid= */ true, has_warning); - else - setValid(false); + } else { + setValid(/* valid= */ false, /* with_warning= */ false, error_locations); + } } } else diff --git a/src/qt/qvalidatedlineedit.h b/src/qt/qvalidatedlineedit.h index a9154a10629..055fd1a65e5 100644 --- a/src/qt/qvalidatedlineedit.h +++ b/src/qt/qvalidatedlineedit.h @@ -35,7 +35,7 @@ class QValidatedLineEdit : public QLineEdit public Q_SLOTS: void setText(const QString&); - void setValid(bool valid, bool with_warning=false); + void setValid(bool valid, bool with_warning=false, const std::vector&error_locations=std::vector()); void setEnabled(bool enabled); Q_SIGNALS: