Skip to content

Commit

Permalink
[attribute form] Insure that a field features multiple times in a fea…
Browse files Browse the repository at this point in the history
…ture form has its constraint properly reflected (#56163)
  • Loading branch information
nirvn committed Feb 6, 2024
1 parent f2f2840 commit b6c7889
Show file tree
Hide file tree
Showing 6 changed files with 92 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,16 @@ Update constraint on a feature coming from a specific layer.
based constraints.

.. versionadded:: 3.0
%End

void updateConstraint( QgsEditorWidgetWrapper::ConstraintResult constraintResult, const QString &constraintFailureReason );
%Docstring
Update constraint manually by providing the constraint result value and failure reason(s).

:param constraintResult: the constraint result value
:param constraintFailureReason: the constraint failure reason(s) (blank is the result passes)

.. versionadded:: 3.36
%End

bool isValidConstraint() const;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,16 @@ Update constraint on a feature coming from a specific layer.
based constraints.

.. versionadded:: 3.0
%End

void updateConstraint( QgsEditorWidgetWrapper::ConstraintResult constraintResult, const QString &constraintFailureReason );
%Docstring
Update constraint manually by providing the constraint result value and failure reason(s).

:param constraintResult: the constraint result value
:param constraintFailureReason: the constraint failure reason(s) (blank is the result passes)

.. versionadded:: 3.36
%End

bool isValidConstraint() const;
Expand Down
9 changes: 9 additions & 0 deletions src/gui/editorwidgets/core/qgseditorwidgetwrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,15 @@ void QgsEditorWidgetWrapper::updateConstraint( const QgsVectorLayer *layer, int
}
}

void QgsEditorWidgetWrapper::updateConstraint( QgsEditorWidgetWrapper::ConstraintResult constraintResult, const QString &constraintFailureReason )
{
mValidConstraint = constraintResult == ConstraintResultPass;
mIsBlockingCommit = constraintResult == ConstraintResultFailHard;
mConstraintFailureReason = constraintResult != ConstraintResultPass ? constraintFailureReason : QString();
mConstraintResult = constraintResult;
updateConstraintWidgetStatus();
}

bool QgsEditorWidgetWrapper::isValidConstraint() const
{
return mValidConstraint;
Expand Down
8 changes: 8 additions & 0 deletions src/gui/editorwidgets/core/qgseditorwidgetwrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,14 @@ class GUI_EXPORT QgsEditorWidgetWrapper : public QgsWidgetWrapper
*/
void updateConstraint( const QgsVectorLayer *layer, int index, const QgsFeature &feature, QgsFieldConstraints::ConstraintOrigin constraintOrigin = QgsFieldConstraints::ConstraintOriginNotSet );

/**
* Update constraint manually by providing the constraint result value and failure reason(s).
* \param constraintResult the constraint result value
* \param constraintFailureReason the constraint failure reason(s) (blank is the result passes)
* \since QGIS 3.36
*/
void updateConstraint( QgsEditorWidgetWrapper::ConstraintResult constraintResult, const QString &constraintFailureReason );

/**
* Gets the current constraint status.
* \returns TRUE if the constraint is valid or if there's no constraint,
Expand Down
33 changes: 19 additions & 14 deletions src/gui/qgsattributeform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1057,6 +1057,19 @@ void QgsAttributeForm::onAttributeChanged( const QVariant &value, const QVariant
break;
}

// Update other widgets pointing to the same field, required to happen now to insure
// currentFormValuesFeature() gets the right value when processing constraints
const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( eww->fieldIdx() );
for ( QgsAttributeFormEditorWidget *formEditorWidget : formEditorWidgets )
{
if ( formEditorWidget->editorWidget() == eww )
continue;

// formEditorWidget and eww points to the same field, so block signals
// as there is no need to handle valueChanged again for each duplicate
whileBlocking( formEditorWidget->editorWidget() )->setValue( value );
}

updateConstraints( eww );

// Update dependent fields (only if form is not initializing)
Expand All @@ -1072,20 +1085,6 @@ void QgsAttributeForm::onAttributeChanged( const QVariant &value, const QVariant
updateLabels();
updateEditableState();

// Update other widgets pointing to the same field
const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( eww->fieldIdx() );
for ( QgsAttributeFormEditorWidget *formEditorWidget : formEditorWidgets )
{
if ( formEditorWidget->editorWidget() == eww )
continue;

// formEditorWidget and eww points to the same field, so block signals
// as there is no need to handle valueChanged again for each duplicate
formEditorWidget->editorWidget()->blockSignals( true );
formEditorWidget->editorWidget()->setValue( value );
formEditorWidget->editorWidget()->blockSignals( false );
}

if ( !signalEmitted )
{
Q_NOWARN_DEPRECATED_PUSH
Expand Down Expand Up @@ -1435,7 +1434,13 @@ void QgsAttributeForm::onConstraintStatusChanged( const QString &constraint,
const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = mFormEditorWidgets.values( eww->fieldIdx() );

for ( QgsAttributeFormEditorWidget *formEditorWidget : formEditorWidgets )
{
formEditorWidget->setConstraintStatus( constraint, description, err, result );
if ( formEditorWidget->editorWidget() != eww )
{
formEditorWidget->editorWidget()->updateConstraint( result, err );
}
}
}

QList<QgsEditorWidgetWrapper *> QgsAttributeForm::constraintDependencies( QgsEditorWidgetWrapper *w )
Expand Down
36 changes: 36 additions & 0 deletions tests/src/gui/testqgsattributeform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class TestQgsAttributeForm : public QObject
void testSameFieldSync();
void testZeroDoubles();
void testMinimumWidth();
void testFieldConstraintDuplicateField();

private:
QLabel *constraintsLabel( QgsAttributeForm *form, QgsEditorWidgetWrapper *ww )
Expand Down Expand Up @@ -1211,5 +1212,40 @@ void TestQgsAttributeForm::testMinimumWidth()

}

void TestQgsAttributeForm::testFieldConstraintDuplicateField()
{
// make a temporary vector layer
const QString def = QStringLiteral( "Point?field=col0:integer" );
QgsVectorLayer *layer = new QgsVectorLayer( def, QStringLiteral( "test" ), QStringLiteral( "memory" ) );
layer->setEditorWidgetSetup( 0, QgsEditorWidgetSetup( QStringLiteral( "Range" ), QVariantMap() ) );

// add same field twice so they get synced
QgsEditFormConfig editFormConfig = layer->editFormConfig();
editFormConfig.clearTabs();
editFormConfig.invisibleRootContainer()->addChildElement( new QgsAttributeEditorField( "col0", 0, editFormConfig.invisibleRootContainer() ) );
editFormConfig.invisibleRootContainer()->addChildElement( new QgsAttributeEditorField( "col0", 0, editFormConfig.invisibleRootContainer() ) );
editFormConfig.setLayout( Qgis::AttributeFormLayout::DragAndDrop );
layer->setEditFormConfig( editFormConfig );

// add a feature to the vector layer
QgsFeature ft( layer->dataProvider()->fields(), 1 );
ft.setAttribute( QStringLiteral( "col0" ), 1 );

// set a not null constraint
layer->setConstraintExpression( 0, QStringLiteral( "col0 > 10" ) );

// build a form for this feature
QgsAttributeForm form( layer );
form.setFeature( ft );

const QList<QgsAttributeFormEditorWidget *> formEditorWidgets = form.mFormEditorWidgets.values( 0 );
QCOMPARE( formEditorWidgets[0]->editorWidget()->constraintResult(), QgsEditorWidgetWrapper::ConstraintResultFailHard );
QCOMPARE( formEditorWidgets[1]->editorWidget()->constraintResult(), QgsEditorWidgetWrapper::ConstraintResultFailHard );

formEditorWidgets[0]->editorWidget()->setValues( 20, QVariantList() );
QCOMPARE( formEditorWidgets[0]->editorWidget()->constraintResult(), QgsEditorWidgetWrapper::ConstraintResultPass );
QCOMPARE( formEditorWidgets[1]->editorWidget()->constraintResult(), QgsEditorWidgetWrapper::ConstraintResultPass );
}

QGSTEST_MAIN( TestQgsAttributeForm )
#include "testqgsattributeform.moc"

0 comments on commit b6c7889

Please sign in to comment.