Utils: Allow validators operating on a single string in FancyLineEdit

Change-Id: I68f21e96efab05e9f5aa6c18973cbdb20c0c3417
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
This commit is contained in:
hjk
2025-04-17 17:10:56 +02:00
parent 5d45e339a7
commit c1603c2e75
24 changed files with 100 additions and 87 deletions

View File

@@ -35,9 +35,7 @@ ClassNameValidatingLineEdit::ClassNameValidatingLineEdit(QWidget *parent) :
FancyLineEdit(parent),
d(new ClassNameValidatingLineEditPrivate)
{
setValidationFunction([this](FancyLineEdit *edit) {
return validateClassName(edit);
});
setValidationFunction([this](const QString &text) { return validateClassName(text); });
updateRegExp();
}
@@ -73,18 +71,17 @@ void ClassNameValidatingLineEdit::setNamespaceDelimiter(const QString &delimiter
d->m_namespaceDelimiter = delimiter;
}
Result<> ClassNameValidatingLineEdit::validateClassName(FancyLineEdit *edit) const
Result<> ClassNameValidatingLineEdit::validateClassName(const QString &text) const
{
QTC_ASSERT(d->m_nameRegexp.isValid(), return ResultError(ResultAssert));
const QString value = edit->text();
if (!d->m_namespacesEnabled && value.contains(d->m_namespaceDelimiter))
if (!d->m_namespacesEnabled && text.contains(d->m_namespaceDelimiter))
return ResultError(Tr::tr("The class name must not contain namespace delimiters."));
if (value.isEmpty())
if (text.isEmpty())
return ResultError(Tr::tr("Please enter a class name."));
if (!d->m_nameRegexp.match(value).hasMatch())
if (!d->m_nameRegexp.match(text).hasMatch())
return ResultError(Tr::tr("The class name contains invalid characters."));
return ResultOk;

View File

@@ -42,7 +42,7 @@ signals:
void updateFileName(const QString &t);
protected:
Result<> validateClassName(FancyLineEdit *edit) const;
Result<> validateClassName(const QString &text) const;
void handleChanged(const QString &t) override;
QString fixInputString(const QString &string) override;

View File

@@ -481,11 +481,11 @@ FancyLineEdit::ValidationFunction FancyLineEdit::defaultValidationFunction()
return &FancyLineEdit::validateWithValidator;
}
Result<> FancyLineEdit::validateWithValidator(FancyLineEdit *edit)
Result<> FancyLineEdit::validateWithValidator(FancyLineEdit &edit)
{
if (const QValidator *v = edit->validator()) {
QString tmp = edit->text();
int pos = edit->cursorPosition();
if (const QValidator *v = edit.validator()) {
QString tmp = edit.text();
int pos = edit.cursorPosition();
if (v->validate(tmp, pos) != QValidator::Acceptable)
return ResultError(QString());
}
@@ -611,7 +611,7 @@ void FancyLineEdit::validate()
}
if (d->m_validationFunction.index() == 1) {
auto &validationFunction = std::get<1>(d->m_validationFunction);
SynchronousValidationFunction &validationFunction = std::get<1>(d->m_validationFunction);
if (!validationFunction)
return;
@@ -626,7 +626,31 @@ void FancyLineEdit::validate()
Result<QString> result;
if (const Result<> validates = validationFunction(this))
if (const Result<> validates = validationFunction(*this))
result = t;
else
result = ResultError(validates.error());
handleValidationResult(result, t);
}
if (d->m_validationFunction.index() == 2) {
SimpleSynchronousValidationFunction &validationFunction = std::get<2>(d->m_validationFunction);
if (!validationFunction)
return;
const QString t = text();
if (d->m_isFiltering) {
if (t != d->m_lastFilterText) {
d->m_lastFilterText = t;
emit filterChanged(t);
}
}
Result<QString> result;
if (const Result<> validates = validationFunction(t))
result = t;
else
result = ResultError(validates.error());

View File

@@ -105,8 +105,13 @@ public:
using AsyncValidationResult = Result<QString>;
using AsyncValidationFuture = QFuture<AsyncValidationResult>;
using AsyncValidationFunction = std::function<AsyncValidationFuture(QString)>;
using SynchronousValidationFunction = std::function<Result<>(FancyLineEdit *)>;
using ValidationFunction = std::variant<AsyncValidationFunction, SynchronousValidationFunction>;
using SynchronousValidationFunction = std::function<Result<>(FancyLineEdit &)>;
using SimpleSynchronousValidationFunction = std::function<Result<>(const QString &)>;
using ValidationFunction = std::variant<
AsyncValidationFunction,
SynchronousValidationFunction,
SimpleSynchronousValidationFunction
>;
enum State { Invalid, DisplayingPlaceholderText, Valid };
@@ -149,7 +154,7 @@ private:
void handleValidationResult(AsyncValidationResult result, const QString &oldText);
static Result<> validateWithValidator(FancyLineEdit *edit);
static Result<> validateWithValidator(FancyLineEdit &edit);
// Unimplemented, to force the user to make a decision on
// whether to use setHistoryCompleter() or setSpecialCompleter().
void setCompleter(QCompleter *);

View File

@@ -48,10 +48,10 @@ FileNameValidatingLineEdit::FileNameValidatingLineEdit(QWidget *parent) :
m_allowDirectories(false),
m_forceFirstCapitalLetter(false)
{
setValidationFunction([this](FancyLineEdit *edit) {
if (const Result<> res = validateFileNameExtension(edit->text(), requiredExtensions()); !res)
setValidationFunction([this](const QString &text) {
if (const Result<> res = validateFileNameExtension(text, requiredExtensions()); !res)
return res;
if (const Result<> res = validateFileName(edit->text(), allowDirectories()); !res)
if (const Result<> res = validateFileName(text, allowDirectories()); !res)
return res;
return ResultOk;
});

View File

@@ -99,8 +99,8 @@ ProjectIntroPage::ProjectIntroPage(QWidget *parent) :
d->m_nameLineEdit->setPlaceholderText(Tr::tr("Enter project name"));
d->m_nameLineEdit->setObjectName("nameLineEdit");
d->m_nameLineEdit->setFocus();
d->m_nameLineEdit->setValidationFunction([this](FancyLineEdit *edit) {
return validateProjectName(edit->text());
d->m_nameLineEdit->setValidationFunction([this](const QString &text) {
return validateProjectName(text);
});
d->m_projectLabel = new QLabel(Tr::tr("Add to project:"));

View File

@@ -111,8 +111,8 @@ GTestFramework::GTestFramework()
gtestFilter.setToolTip(Tr::tr("Set the GTest filter to be used for grouping.\nSee Google Test "
"documentation for further information on GTest filters."));
gtestFilter.setValidationFunction([](FancyLineEdit *edit) -> Result<> {
if (edit && GTestUtils::isValidGTestFilter(edit->text()))
gtestFilter.setValidationFunction([](const QString &text) -> Result<> {
if (GTestUtils::isValidGTestFilter(text))
return ResultOk;
return ResultError(QString());
});

View File

@@ -442,8 +442,8 @@ DashboardSettingsWidget::DashboardSettingsWidget(QWidget *parent, QPushButton *o
{
m_dashboardUrl.setLabelText(Tr::tr("Dashboard URL:"));
m_dashboardUrl.setDisplayStyle(StringAspect::LineEditDisplay);
m_dashboardUrl.setValidationFunction([](FancyLineEdit *edit) -> Result<> {
if (isUrlValid(edit->text()))
m_dashboardUrl.setValidationFunction([](const QString &text) -> Result<> {
if (isUrlValid(text))
return ResultOk;
return ResultError(QString());
});
@@ -502,18 +502,17 @@ public:
{
m_projectName.setLabelText(Tr::tr("Project name:"));
m_projectName.setDisplayStyle(StringAspect::LineEditDisplay);
m_projectName.setValidationFunction([](FancyLineEdit *edit) -> Result<> {
QTC_ASSERT(edit, return ResultError(ResultAssert));
if (edit->text().isEmpty())
m_projectName.setValidationFunction([](const QString &text) -> Result<> {
if (text.isEmpty())
return ResultError(Tr::tr("Project name must be non-empty."));
return ResultOk;
});
m_analysisPath.setLabelText(Tr::tr("Analysis path:"));
m_analysisPath.setDisplayStyle(StringAspect::LineEditDisplay);
m_analysisPath.setValidationFunction([](FancyLineEdit *edit) -> Result<> {
QTC_ASSERT(edit, return ResultError(ResultAssert));
m_analysisPath.setValidationFunction([](const QString &text) -> Result<> {
QString input = text;
// do NOT use fromUserInput() as this also cleans the path
const FilePath fp = FilePath::fromString(edit->text().replace('\\', '/'));
const FilePath fp = FilePath::fromString(input.replace('\\', '/'));
return analysisPathValid(fp);
});
m_localPath.setLabelText(Tr::tr("Local path:"));

View File

@@ -496,7 +496,7 @@ ShortcutInput::ShortcutInput()
m_warningLabel->setPalette(palette);
connect(m_warningLabel, &QLabel::linkActivated, this, &ShortcutInput::showConflictsRequested);
m_shortcutEdit->setValidationFunction([this](FancyLineEdit *) -> Result<> {
m_shortcutEdit->setValidationFunction([this](const QString &) -> Result<> {
QString warningMessage;
const QKeySequence key = keySequenceFromEditString(m_shortcutEdit->text());
const bool isValid = checkValidity(key, &warningMessage);

View File

@@ -174,7 +174,7 @@ FindToolBar::FindToolBar(CurrentDocumentFind *currentDocumentFind)
m_findEdit->setFiltering(true);
m_findEdit->setPlaceholderText(QString());
m_findEdit->button(Utils::FancyLineEdit::Left)->setFocusPolicy(Qt::TabFocus);
m_findEdit->setValidationFunction([this](FancyLineEdit *) -> Result<> {
m_findEdit->setValidationFunction([this](const QString &) -> Result<> {
if (m_lastResult != IFindSupport::NotFound)
return ResultOk;
return ResultError(QString());

View File

@@ -30,13 +30,13 @@ namespace Core::Internal {
static FindToolWindow *m_instance = nullptr;
static Result<> validateRegExp(FancyLineEdit *edit)
static Result<> validateRegExp(const QString &text)
{
if (edit->text().isEmpty())
if (text.isEmpty())
return ResultError(Tr::tr("Empty search term."));
if (Find::hasFindFlag(FindRegularExpression)) {
QRegularExpression regexp(edit->text());
QRegularExpression regexp(text);
if (!regexp.isValid())
return ResultError(regexp.errorString());
}

View File

@@ -224,8 +224,8 @@ public:
auto layout = new QFormLayout(&dlg);
auto funcNameEdit = new FancyLineEdit;
funcNameEdit->setValidationFunction([](FancyLineEdit *edit) -> Result<> {
if (ExtractFunctionOptions::isValidFunctionName(edit->text()))
funcNameEdit->setValidationFunction([](const QString &text) -> Result<> {
if (ExtractFunctionOptions::isValidFunctionName(text))
return ResultOk;
return ResultError(QString());
});

View File

@@ -493,11 +493,10 @@ FakeVimExCommandsMappings::FakeVimExCommandsMappings()
m_commandEdit->setPlaceholderText(QString());
connect(m_commandEdit, &FancyLineEdit::textChanged,
this, &FakeVimExCommandsMappings::commandChanged);
m_commandEdit->setValidationFunction([](FancyLineEdit *e) -> Result<> {
if (QRegularExpression(e->text()).isValid())
m_commandEdit->setValidationFunction([](const QString &text) -> Result<> {
if (QRegularExpression(text).isValid())
return ResultOk;
return ResultError(Tr::tr("The pattern \"%1\" is no valid regular expression")
.arg(e->text()));
return ResultError(Tr::tr("The pattern \"%1\" is no valid regular expression").arg(text));
});
auto resetButton = new QPushButton(Tr::tr("Reset"), m_commandBox);
resetButton->setToolTip(Tr::tr("Reset to default."));

View File

@@ -67,7 +67,7 @@ GerritDialog::GerritDialog(const std::shared_ptr<GerritServer> &s,
m_queryLineEdit->setMinimumSize(QSize(400, 0));
m_queryLineEdit->setPlaceholderText(Git::Tr::tr("Change #, hash, tr:id, owner:email or reviewer:email"));
m_queryLineEdit->setSpecialCompleter(new QCompleter(m_queryModel, this));
m_queryLineEdit->setValidationFunction([this](FancyLineEdit *) -> Result<> {
m_queryLineEdit->setValidationFunction([this](const QString &) -> Result<> {
if (m_model->state() != GerritModel::Error)
return ResultOk;
return ResultError(QString());

View File

@@ -44,12 +44,9 @@ public:
m_nameEdit = new FancyLineEdit(this);
m_nameEdit->setHistoryCompleter("Git.RemoteNames");
m_nameEdit->setValidationFunction([this](FancyLineEdit *edit) -> Result<> {
if (!edit)
return ResultError(ResultAssert);
QString input = edit->text();
edit->setText(input.replace(m_invalidRemoteNameChars, "_"));
m_nameEdit->setValidationFunction([this](FancyLineEdit &edit) -> Result<> {
QString input = edit.text();
edit.setText(input.replace(m_invalidRemoteNameChars, "_"));
// "Intermediate" patterns, may change to Acceptable when user edits further:
if (input.endsWith(".lock")) //..may not end with ".lock"
@@ -73,11 +70,11 @@ public:
m_urlEdit = new FancyLineEdit(this);
m_urlEdit->setHistoryCompleter("Git.RemoteUrls");
m_urlEdit->setValidationFunction([](FancyLineEdit *edit) -> Result<> {
if (!edit || edit->text().isEmpty())
m_urlEdit->setValidationFunction([](FancyLineEdit &edit) -> Result<> {
if (edit.text().isEmpty())
return ResultError(QString());
const GitRemote r(edit->text());
const GitRemote r(edit.text());
if (!r.isValid)
return ResultError(Tr::tr("The URL may not be valid."));

View File

@@ -61,8 +61,8 @@ GitLabCloneDialog::GitLabCloneDialog(const Project &project, QWidget *parent)
m_pathChooser->setExpectedKind(PathChooser::ExistingDirectory);
form->addRow(Tr::tr("Path"), m_pathChooser);
m_directoryLE = new FancyLineEdit(this);
m_directoryLE->setValidationFunction([this](FancyLineEdit *e) -> Result<> {
const FilePath fullPath = m_pathChooser->filePath().pathAppended(e->text());
m_directoryLE->setValidationFunction([this](const QString &text) -> Result<> {
const FilePath fullPath = m_pathChooser->filePath().pathAppended(text);
if (fullPath.exists())
return ResultError(Tr::tr("Path \"%1\" already exists.").arg(fullPath.toUserOutput()));
return ResultOk;

View File

@@ -70,8 +70,8 @@ GitLabServerWidget::GitLabServerWidget(Mode m, QWidget *parent)
m_host.setLabelText(Tr::tr("Host:"));
m_host.setDisplayStyle(m == Display ? StringAspect::LabelDisplay
: StringAspect::LineEditDisplay);
m_host.setValidationFunction([](FancyLineEdit *l) -> Result<> {
if (hostValid(l->text()))
m_host.setValidationFunction([](const QString &text) -> Result<> {
if (hostValid(text))
return ResultOk;
return ResultError(QString());
});

View File

@@ -917,8 +917,8 @@ BaseSettingsWidget::BaseSettingsWidget(const BaseSettings *settings, QWidget *pa
m_startupBehavior->addItem(startupBehaviorString(BaseSettings::StartBehavior(behavior)));
m_startupBehavior->setCurrentIndex(settings->m_startBehavior);
m_initializationOptions->setValidationFunction([](FancyLineEdit *edit) -> Result<> {
const QString value = globalMacroExpander()->expand(edit->text());
m_initializationOptions->setValidationFunction([](const QString &text) -> Result<> {
const QString value = globalMacroExpander()->expand(text);
if (value.isEmpty())
return ResultOk;

View File

@@ -231,8 +231,8 @@ IDevice::IDevice()
};
d->displayName.setValidationFunction(
[this, validateDisplayName](FancyLineEdit *edit) -> Result<> {
return validateDisplayName(d->displayName.value(), edit->text());
[this, validateDisplayName](const QString &text) -> Result<> {
return validateDisplayName(d->displayName.value(), text);
});
d->displayName.setValueAcceptor(

View File

@@ -109,7 +109,7 @@ public:
m_expander.registerVariable("INPUT", Tr::tr("The text edit input to fix up."),
[this] { return m_currentInput; });
m_expander.registerSubProvider([expander]() -> MacroExpander * { return expander; });
setValidationFunction([this, regex](FancyLineEdit *) -> Result<> {
setValidationFunction([this, regex](const QString &) -> Result<> {
if (regex.match(text()).hasMatch())
return ResultOk;
return ResultError(QString());

View File

@@ -131,7 +131,7 @@ private:
QbsBuildStep *qbsStep() const;
Result<> validateProperties(FancyLineEdit *edit);
Result<> validateProperties(const QString &text);
class Property
{
@@ -485,8 +485,8 @@ QbsBuildStepConfigWidget::QbsBuildStepConfigWidget(QbsBuildStep *step)
propertyEdit = new FancyLineEdit(this);
propertyEdit->setToolTip(QbsProjectManager::Tr::tr("Properties to pass to the project."));
propertyEdit->setValidationFunction([this](FancyLineEdit *edit) {
return validateProperties(edit);
propertyEdit->setValidationFunction([this](const QString &text) {
return validateProperties(text);
});
defaultInstallDirCheckBox = new QCheckBox(this);
@@ -654,10 +654,10 @@ QbsBuildStep *QbsBuildStepConfigWidget::qbsStep() const
return m_qbsStep;
}
Result<> QbsBuildStepConfigWidget::validateProperties(FancyLineEdit *edit)
Result<> QbsBuildStepConfigWidget::validateProperties(const QString &text)
{
ProcessArgs::SplitError err;
const QStringList argList = ProcessArgs::splitArgs(edit->text(), HostOsInfo::hostOs(), false, &err);
const QStringList argList = ProcessArgs::splitArgs(text, HostOsInfo::hostOs(), false, &err);
if (err != ProcessArgs::SplitOk)
return ResultError(QbsProjectManager::Tr::tr("Could not split properties."));

View File

@@ -132,17 +132,12 @@ void PropertyItemDelegate::setModelData(QWidget *editor,
ValidatingPropertyNameLineEdit::ValidatingPropertyNameLineEdit(const QStringList &forbidden,
QWidget *parent)
: Utils::FancyLineEdit(parent)
: FancyLineEdit(parent)
, m_forbidden(forbidden)
{
setValidationFunction([this](FancyLineEdit *edit) -> Result<> {
if (!edit)
return ResultError(QString());
setValidationFunction([this](const QString &text) -> Result<> {
static const QRegularExpression identifier("^[a-zA-Z0-9_]+$");
const QString &value = edit->text();
if (!m_forbidden.contains(value) && identifier.match(value).hasMatch())
if (!m_forbidden.contains(text) && identifier.match(text).hasMatch())
return ResultOk;
return ResultError(QString());
});
@@ -152,12 +147,12 @@ ValidatingPropertyNameLineEdit::ValidatingPropertyNameLineEdit(const QStringList
ValidatingPropertyContainerLineEdit::ValidatingPropertyContainerLineEdit(const QStringList &allowed,
QWidget *parent)
: Utils::FancyLineEdit(parent)
: FancyLineEdit(parent)
, m_allowed(allowed)
{
setSpecialCompleter(new QCompleter(allowed, this));
setValidationFunction([this](FancyLineEdit *edit) -> Result<> {
if (edit && m_allowed.contains(edit->text()))
setValidationFunction([this](const QString &text) -> Result<> {
if (m_allowed.contains(text))
return ResultOk;
return ResultError(QString());
});

View File

@@ -120,9 +120,9 @@ static QWidget *testCaseEditor(QWidget *parent, const SquishTestTreeItem *item)
const SuiteConf suiteConf = SuiteConf::readSuiteConf(suite->filePath());
const QStringList inUse = suiteConf.usedTestCases();
FancyLineEdit *editor = new FancyLineEdit(parent);
editor->setValidationFunction([inUse](FancyLineEdit *edit) -> Result<> {
editor->setValidationFunction([inUse](const QString &text) -> Result<> {
static const QRegularExpression validFileName("^[-a-zA-Z0-9_$. ]+$");
QString testName = edit->text();
QString testName = text;
if (!testName.startsWith("tst_"))
testName.prepend("tst_");
if (validFileName.match(testName).hasMatch() && !inUse.contains(testName))
@@ -137,8 +137,8 @@ static QWidget *sharedScriptEditor(QWidget *parent, const SquishTestTreeItem *it
{
const FilePath folder = static_cast<SquishTestTreeItem *>(item->parent())->filePath();
FancyLineEdit *editor = new FancyLineEdit(parent);
editor->setValidationFunction([folder](FancyLineEdit *edit) -> Result<> {
if (!edit->text().isEmpty() && !folder.pathAppended(edit->text()).exists())
editor->setValidationFunction([folder](const QString &text) -> Result<> {
if (!text.isEmpty() && !folder.pathAppended(text).exists())
return ResultOk;
return ResultError(QString());
});

View File

@@ -53,10 +53,7 @@ ValidatingContainerNameLineEdit::ValidatingContainerNameLineEdit(const QStringLi
: FancyLineEdit(parent)
, m_forbidden(forbidden)
{
setValidationFunction([this](FancyLineEdit *edit) -> Result<> {
if (!edit)
return ResultError(QString());
const QString &value = edit->text();
setValidationFunction([this](const QString &value) -> Result<> {
if (value.isEmpty())
return ResultError(QString());
const QString realName = value.at(0) == ObjectsMapTreeItem::COLON