diff --git a/python/core/auto_generated/editform/qgsattributeeditorcontainer.sip.in b/python/core/auto_generated/editform/qgsattributeeditorcontainer.sip.in index 0a919b44876..283f518094e 100644 --- a/python/core/auto_generated/editform/qgsattributeeditorcontainer.sip.in +++ b/python/core/auto_generated/editform/qgsattributeeditorcontainer.sip.in @@ -52,6 +52,28 @@ Returns if this container is going to be rendered as a group box :return: ``True`` if it will be a group box, ``False`` if it will be a tab %End + bool collapsed() const; +%Docstring +For containers rendedered a group box returns if this group box is collapsed. + +:return: ``True`` if the group box, ``False`` otherwise. + +.. seealso:: :py:func:`collapsed` + +.. seealso:: :py:func:`setCollapsed` + +.. versionadded:: 3.26 +%End + void setCollapsed( bool collapsed ); +%Docstring +For containers rendedered a group box sets if this group box is ``collapsed``. + +.. seealso:: :py:func:`collapsed` + +.. seealso:: :py:func:`setCollapsed` + +.. versionadded:: 3.26 +%End QList children() const; %Docstring Gets a list of the children elements of this container @@ -112,6 +134,26 @@ show or hide this container based on an expression incorporating the field value controlled by editor widgets. .. versionadded:: 3.0 +%End + + QgsOptionalExpression collapsedExpression() const; +%Docstring +The collapsed expression is used in the attribute form to +set the collapsed status of the group box container container based on an expression incorporating +the field value controlled by editor widgets. This property is ignored if the container is not +rendered as a group box. + +.. versionadded:: 3.26 +%End + + void setCollapsedExpression( const QgsOptionalExpression &collapsedExpression ); +%Docstring +The visibility expression is used in the attribute form to +set the collapsed status of the group box of this container based on an expression incorporating +the field value controlled by editor widgets. This property is ignored if the container is not +rendered as a group box. + +.. versionadded:: 3.26 %End QColor backgroundColor() const; diff --git a/src/core/editform/qgsattributeeditorcontainer.cpp b/src/core/editform/qgsattributeeditorcontainer.cpp index aa3594c33e4..32a117d4efc 100644 --- a/src/core/editform/qgsattributeeditorcontainer.cpp +++ b/src/core/editform/qgsattributeeditorcontainer.cpp @@ -45,6 +45,19 @@ void QgsAttributeEditorContainer::setVisibilityExpression( const QgsOptionalExpr mVisibilityExpression = visibilityExpression; } +QgsOptionalExpression QgsAttributeEditorContainer::collapsedExpression() const +{ + return mCollapsedExpression; +} + +void QgsAttributeEditorContainer::setCollapsedExpression( const QgsOptionalExpression &collapsedExpression ) +{ + if ( collapsedExpression == mCollapsedExpression ) + return; + + mCollapsedExpression = collapsedExpression; +} + QColor QgsAttributeEditorContainer::backgroundColor() const { return mBackgroundColor; @@ -107,6 +120,7 @@ QgsAttributeEditorElement *QgsAttributeEditorContainer::clone( QgsAttributeEdito element->mIsGroupBox = mIsGroupBox; element->mColumnCount = mColumnCount; element->mVisibilityExpression = mVisibilityExpression; + element->mCollapsed = mCollapsed; return element; } @@ -116,6 +130,9 @@ void QgsAttributeEditorContainer::saveConfiguration( QDomElement &elem, QDomDocu Q_UNUSED( doc ) elem.setAttribute( QStringLiteral( "columnCount" ), mColumnCount ); elem.setAttribute( QStringLiteral( "groupBox" ), mIsGroupBox ? 1 : 0 ); + elem.setAttribute( QStringLiteral( "collapsed" ), mCollapsed ); + elem.setAttribute( QStringLiteral( "collapsedExpressionEnabled" ), mCollapsedExpression.enabled() ? 1 : 0 ); + elem.setAttribute( QStringLiteral( "collapsedExpression" ), mCollapsedExpression->expression() ); elem.setAttribute( QStringLiteral( "visibilityExpressionEnabled" ), mVisibilityExpression.enabled() ? 1 : 0 ); elem.setAttribute( QStringLiteral( "visibilityExpression" ), mVisibilityExpression->expression() ); if ( mBackgroundColor.isValid() ) @@ -143,6 +160,22 @@ void QgsAttributeEditorContainer::loadConfiguration( const QDomElement &element, else setIsGroupBox( mParent ); + const bool isCollapsed = element.attribute( QStringLiteral( "collapsed" ) ).toInt( &ok ); + if ( ok ) + setCollapsed( isCollapsed ); + else + setCollapsed( false ); + + const bool collapsedExpressionEnabled = element.attribute( QStringLiteral( "collapsedExpressionEnabled" ) ).toInt( &ok ); + QgsOptionalExpression collapsedExpression; + if ( ok ) + { + collapsedExpression.setEnabled( collapsedExpressionEnabled ); + collapsedExpression.setData( QgsExpression( element.attribute( QStringLiteral( "collapsedExpression" ) ) ) ); + } + setCollapsedExpression( collapsedExpression ); + + const bool visibilityExpressionEnabled = element.attribute( QStringLiteral( "visibilityExpressionEnabled" ) ).toInt( &ok ); QgsOptionalExpression visibilityExpression; if ( ok ) diff --git a/src/core/editform/qgsattributeeditorcontainer.h b/src/core/editform/qgsattributeeditorcontainer.h index da42fb1bde6..a860d5aa161 100644 --- a/src/core/editform/qgsattributeeditorcontainer.h +++ b/src/core/editform/qgsattributeeditorcontainer.h @@ -66,6 +66,25 @@ class CORE_EXPORT QgsAttributeEditorContainer : public QgsAttributeEditorElement */ virtual bool isGroupBox() const { return mIsGroupBox; } + /** + * For containers rendedered a group box returns if this group box is collapsed. + * + * \returns TRUE if the group box, FALSE otherwise. + * \see collapsed() + * \see setCollapsed() + * \since QGIS 3.26 + */ + bool collapsed() const { return mCollapsed; }; + + /** + * For containers rendedered a group box sets if this group box is \a collapsed. + * + * \see collapsed() + * \see setCollapsed() + * \since QGIS 3.26 + */ + void setCollapsed( bool collapsed ) { mCollapsed = collapsed; }; + /** * Gets a list of the children elements of this container * @@ -127,6 +146,26 @@ class CORE_EXPORT QgsAttributeEditorContainer : public QgsAttributeEditorElement */ void setVisibilityExpression( const QgsOptionalExpression &visibilityExpression ); + /** + * The collapsed expression is used in the attribute form to + * set the collapsed status of the group box container container based on an expression incorporating + * the field value controlled by editor widgets. This property is ignored if the container is not + * rendered as a group box. + * + * \since QGIS 3.26 + */ + QgsOptionalExpression collapsedExpression() const; + + /** + * The visibility expression is used in the attribute form to + * set the collapsed status of the group box of this container based on an expression incorporating + * the field value controlled by editor widgets. This property is ignored if the container is not + * rendered as a group box. + * + * \since QGIS 3.26 + */ + void setCollapsedExpression( const QgsOptionalExpression &collapsedExpression ); + /** * \brief backgroundColor * \return background color of the container @@ -149,6 +188,8 @@ class CORE_EXPORT QgsAttributeEditorContainer : public QgsAttributeEditorElement int mColumnCount; QgsOptionalExpression mVisibilityExpression; QColor mBackgroundColor; + bool mCollapsed = false; + QgsOptionalExpression mCollapsedExpression; }; diff --git a/src/gui/attributeformconfig/qgsattributeformcontaineredit.cpp b/src/gui/attributeformconfig/qgsattributeformcontaineredit.cpp index 2e17a759b84..46a51966b4a 100644 --- a/src/gui/attributeformconfig/qgsattributeformcontaineredit.cpp +++ b/src/gui/attributeformconfig/qgsattributeformcontaineredit.cpp @@ -31,28 +31,36 @@ QgsAttributeFormContainerEdit::QgsAttributeFormContainerEdit( QTreeWidgetItem *i { // only top level items can be tabs // i.e. it's always a group box if it's a nested container - mShowAsGroupBoxCheckBox->hide(); - mShowAsGroupBoxCheckBox->setEnabled( false ); + mShowAsGroupBox->hide(); + mShowAsGroupBox->setEnabled( false ); } mTitleLineEdit->setText( itemData.name() ); mShowLabelCheckBox->setChecked( itemData.showLabel() ); mShowLabelCheckBox->setEnabled( itemData.showAsGroupBox() ); // show label makes sense for group box, not for tabs - mShowAsGroupBoxCheckBox->setChecked( itemData.showAsGroupBox() ); + mShowAsGroupBox->setChecked( itemData.showAsGroupBox() ); mControlVisibilityGroupBox->setChecked( itemData.visibilityExpression().enabled() ); mVisibilityExpressionWidget->setLayer( layer ); mVisibilityExpressionWidget->setExpression( itemData.visibilityExpression()->expression() ); mColumnCountSpinBox->setValue( itemData.columnCount() ); mBackgroundColorButton->setColor( itemData.backgroundColor() ); + mCollapsedCheckBox->setChecked( itemData.collapsed() ); + mCollapsedCheckBox->setEnabled( itemData.showAsGroupBox() ); + mControlCollapsedGroupBox->setChecked( itemData.collapsedExpression().enabled() ); + mControlCollapsedGroupBox->setEnabled( itemData.showAsGroupBox() ); + mCollapsedExpressionWidget->setExpression( itemData.collapsedExpression()->expression() ); // show label makes sense for group box, not for tabs - connect( mShowAsGroupBoxCheckBox, &QCheckBox::stateChanged, mShowLabelCheckBox, &QCheckBox::setEnabled ); + connect( mShowAsGroupBox, &QCheckBox::toggled, mShowLabelCheckBox, &QCheckBox::setEnabled ); + connect( mShowAsGroupBox, &QCheckBox::toggled, mCollapsedCheckBox, &QCheckBox::setEnabled ); + connect( mShowAsGroupBox, &QCheckBox::toggled, mControlCollapsedGroupBox, &QCheckBox::setEnabled ); } void QgsAttributeFormContainerEdit::registerExpressionContextGenerator( QgsExpressionContextGenerator *generator ) { mVisibilityExpressionWidget->registerExpressionContextGenerator( generator ); + mCollapsedExpressionWidget->registerExpressionContextGenerator( generator ); } void QgsAttributeFormContainerEdit::updateItemData() @@ -60,7 +68,7 @@ void QgsAttributeFormContainerEdit::updateItemData() QgsAttributesFormProperties::DnDTreeItemData itemData = mTreeItem->data( 0, QgsAttributesFormProperties::DnDTreeRole ).value(); itemData.setColumnCount( mColumnCountSpinBox->value() ); - itemData.setShowAsGroupBox( mShowAsGroupBoxCheckBox->isEnabled() ? mShowAsGroupBoxCheckBox->isChecked() : false ); + itemData.setShowAsGroupBox( mShowAsGroupBox->isEnabled() ? mShowAsGroupBox->isChecked() : false ); itemData.setName( mTitleLineEdit->text() ); itemData.setShowLabel( mShowLabelCheckBox->isChecked() ); itemData.setBackgroundColor( mBackgroundColorButton->color() ); @@ -70,6 +78,12 @@ void QgsAttributeFormContainerEdit::updateItemData() visibilityExpression.setEnabled( mControlVisibilityGroupBox->isChecked() ); itemData.setVisibilityExpression( visibilityExpression ); + QgsOptionalExpression collapsedExpression; + collapsedExpression.setData( QgsExpression( mCollapsedExpressionWidget->expression() ) ); + collapsedExpression.setEnabled( mControlCollapsedGroupBox->isChecked() ); + itemData.setCollapsedExpression( collapsedExpression ); + itemData.setCollapsed( mCollapsedCheckBox->isEnabled() ? mCollapsedCheckBox->isChecked() : false ); + mTreeItem->setData( 0, QgsAttributesFormProperties::DnDTreeRole, itemData ); mTreeItem->setText( 0, itemData.name() ); } diff --git a/src/gui/qgsattributeform.cpp b/src/gui/qgsattributeform.cpp index 0032caef69a..b52ffaa6081 100644 --- a/src/gui/qgsattributeform.cpp +++ b/src/gui/qgsattributeform.cpp @@ -59,7 +59,6 @@ #include #include #include -#include #include #include #include @@ -1088,7 +1087,7 @@ void QgsAttributeForm::updateConstraints( QgsEditorWidgetWrapper *eww ) QgsExpressionContext context = createExpressionContext( ft ); - // Recheck visibility for all containers which are controlled by this value + // Recheck visibility/collapsed state for all containers which are controlled by this value const QVector infos = mContainerInformationDependency.value( eww->field().name() ); for ( ContainerInformation *info : infos ) { @@ -1101,7 +1100,7 @@ void QgsAttributeForm::updateContainersVisibility() { QgsExpressionContext context = createExpressionContext( mFeature ); - const QVector infos = mContainerVisibilityInformation; + const QVector infos = mContainerVisibilityCollapsedInformation; for ( ContainerInformation *info : infos ) { @@ -1224,9 +1223,9 @@ bool QgsAttributeForm::currentFormValuesFeature( QgsFeature &feature ) void QgsAttributeForm::registerContainerInformation( QgsAttributeForm::ContainerInformation *info ) { - mContainerVisibilityInformation.append( info ); + mContainerVisibilityCollapsedInformation.append( info ); - const QSet referencedColumns = info->expression.referencedColumns(); + const QSet referencedColumns = info->expression.referencedColumns().unite( info->collapsedExpression.referencedColumns() ); for ( const QString &col : referencedColumns ) { @@ -1611,9 +1610,9 @@ void QgsAttributeForm::init() tabWidget = nullptr; WidgetInfo widgetInfo = createWidgetFromDef( widgDef, formWidget, mLayer, mContext ); layout->addWidget( widgetInfo.widget, row, column, 1, 2 ); - if ( containerDef->visibilityExpression().enabled() ) + if ( containerDef->visibilityExpression().enabled() || containerDef->collapsedExpression().enabled() ) { - registerContainerInformation( new ContainerInformation( widgetInfo.widget, containerDef->visibilityExpression().data() ) ); + registerContainerInformation( new ContainerInformation( widgetInfo.widget, containerDef->visibilityExpression().enabled() ? containerDef->visibilityExpression().data() : QgsExpression(), containerDef->collapsed(), containerDef->collapsedExpression().enabled() ? containerDef->collapsedExpression().data() : QgsExpression() ) ); } column += 2; } @@ -2232,12 +2231,13 @@ QgsAttributeForm::WidgetInfo QgsAttributeForm::createWidgetFromDef( const QgsAtt QWidget *myContainer = nullptr; if ( container->isGroupBox() ) { - QGroupBox *groupBox = new QGroupBox( parent ); + QgsCollapsibleGroupBoxBasic *groupBox = new QgsCollapsibleGroupBoxBasic(); widgetName = QStringLiteral( "QGroupBox" ); if ( container->showLabel() ) groupBox->setTitle( container->name() ); myContainer = groupBox; newWidgetInfo.widget = myContainer; + groupBox->setCollapsed( container->collapsed() ); } else { @@ -2274,9 +2274,9 @@ QgsAttributeForm::WidgetInfo QgsAttributeForm::createWidgetFromDef( const QgsAtt if ( childDef->type() == QgsAttributeEditorElement::AeTypeContainer ) { QgsAttributeEditorContainer *containerDef = static_cast( childDef ); - if ( containerDef->visibilityExpression().enabled() ) + if ( containerDef->visibilityExpression().enabled() || containerDef->collapsedExpression().enabled() ) { - registerContainerInformation( new ContainerInformation( widgetInfo.widget, containerDef->visibilityExpression().data() ) ); + registerContainerInformation( new ContainerInformation( widgetInfo.widget, containerDef->visibilityExpression().enabled() ? containerDef->visibilityExpression().data() : QgsExpression(), containerDef->collapsed(), containerDef->collapsedExpression().enabled() ? containerDef->collapsedExpression().data() : QgsExpression() ) ); } } @@ -2679,9 +2679,10 @@ void QgsAttributeForm::setExtraContextScope( QgsExpressionContextScope *extraSco void QgsAttributeForm::ContainerInformation::apply( QgsExpressionContext *expressionContext ) { - bool newVisibility = expression.evaluate( expressionContext ).toBool(); - if ( newVisibility != isVisible ) + const bool newVisibility = expression.evaluate( expressionContext ).toBool(); + + if ( expression.isValid() && ! expression.hasEvalError() && newVisibility != isVisible ) { if ( tabWidget ) { @@ -2694,6 +2695,19 @@ void QgsAttributeForm::ContainerInformation::apply( QgsExpressionContext *expres isVisible = newVisibility; } + + const bool newCollapsedState = collapsedExpression.evaluate( expressionContext ).toBool(); + + if ( collapsedExpression.isValid() && ! collapsedExpression.hasEvalError() && newCollapsedState != isCollapsed ) + { + + QgsCollapsibleGroupBoxBasic *collapsibleGroupBox { qobject_cast( widget ) }; + if ( collapsibleGroupBox ) + { + collapsibleGroupBox->setCollapsed( newCollapsedState ); + isCollapsed = newCollapsedState; + } + } } void QgsAttributeForm::updateJoinedFields( const QgsEditorWidgetWrapper &eww ) diff --git a/src/gui/qgsattributeform.h b/src/gui/qgsattributeform.h index 144651a4be6..5c4c90d2395 100644 --- a/src/gui/qgsattributeform.h +++ b/src/gui/qgsattributeform.h @@ -488,10 +488,21 @@ class GUI_EXPORT QgsAttributeForm : public QWidget , isVisible( true ) {} + ContainerInformation( QWidget *widget, const QgsExpression &visibilityExpression, bool collapsed, const QgsExpression &collapsedExpression ) + : widget( widget ) + , expression( visibilityExpression ) + , isVisible( true ) + , isCollapsed( collapsed ) + , collapsedExpression( collapsedExpression ) + {} + + QgsTabWidget *tabWidget = nullptr; QWidget *widget = nullptr; QgsExpression expression; bool isVisible; + bool isCollapsed = false; + QgsExpression collapsedExpression; void apply( QgsExpressionContext *expressionContext ); }; @@ -502,8 +513,8 @@ class GUI_EXPORT QgsAttributeForm : public QWidget void reloadIcon( const QString &file, const QString &tooltip, QSvgWidget *sw ); - // Contains information about tabs and groupboxes, their visibility state visibility conditions - QVector mContainerVisibilityInformation; + // Contains information about tabs and groupboxes, their visibility/collapsed state conditions + QVector mContainerVisibilityCollapsedInformation; QMap > mContainerInformationDependency; // Variables below are used for Python diff --git a/src/gui/vector/qgsattributesformproperties.cpp b/src/gui/vector/qgsattributesformproperties.cpp index 648fdd77f5b..f02c575b4be 100644 --- a/src/gui/vector/qgsattributesformproperties.cpp +++ b/src/gui/vector/qgsattributesformproperties.cpp @@ -479,6 +479,8 @@ QTreeWidgetItem *QgsAttributesFormProperties::loadAttributeEditorTreeItem( QgsAt itemData.setShowAsGroupBox( container->isGroupBox() ); itemData.setBackgroundColor( container->backgroundColor() ); itemData.setVisibilityExpression( container->visibilityExpression() ); + itemData.setCollapsedExpression( container->collapsedExpression() ); + itemData.setCollapsed( container->collapsed() ); newWidget = tree->addItem( parent, itemData ); const QList children = container->children(); @@ -733,6 +735,8 @@ QgsAttributeEditorElement *QgsAttributesFormProperties::createAttributeEditorWid QgsAttributeEditorContainer *container = new QgsAttributeEditorContainer( item->text( 0 ), parent, itemData.backgroundColor() ); container->setColumnCount( itemData.columnCount() ); container->setIsGroupBox( forceGroup ? true : itemData.showAsGroupBox() ); + container->setCollapsed( itemData.collapsed() ); + container->setCollapsedExpression( itemData.collapsedExpression() ); container->setVisibilityExpression( itemData.visibilityExpression() ); container->setBackgroundColor( itemData.backgroundColor( ) ); @@ -1542,6 +1546,16 @@ void QgsAttributesFormProperties::DnDTreeItemData::setVisibilityExpression( cons mVisibilityExpression = visibilityExpression; } +QgsOptionalExpression QgsAttributesFormProperties::DnDTreeItemData::collapsedExpression() const +{ + return mCollapsedExpression; +} + +void QgsAttributesFormProperties::DnDTreeItemData::setCollapsedExpression( const QgsOptionalExpression &collapsedExpression ) +{ + mCollapsedExpression = collapsedExpression; +} + QgsAttributesFormProperties::RelationEditorConfiguration QgsAttributesFormProperties::DnDTreeItemData::relationEditorConfiguration() const { return mRelationEditorConfiguration; diff --git a/src/gui/vector/qgsattributesformproperties.h b/src/gui/vector/qgsattributesformproperties.h index 3d9720e3d4e..9daddc52f95 100644 --- a/src/gui/vector/qgsattributesformproperties.h +++ b/src/gui/vector/qgsattributesformproperties.h @@ -137,11 +137,33 @@ class GUI_EXPORT QgsAttributesFormProperties : public QWidget, public QgsExpress bool showAsGroupBox() const; void setShowAsGroupBox( bool showAsGroupBox ); + /** + * For containers rendedered a group box returns if this group box is collapsed. + * + * \returns TRUE if the group box, FALSE otherwise. + * \see collapsed() + * \see setCollapsed() + * \since QGIS 3.26 + */ + bool collapsed() const { return mCollapsed; }; + + /** + * For containers rendedered a group box sets if this group box is \a collapsed. + * + * \see collapsed() + * \see setCollapsed() + * \since QGIS 3.26 + */ + void setCollapsed( bool collapsed ) { mCollapsed = collapsed; }; + bool showLabel() const; void setShowLabel( bool showLabel ); QgsOptionalExpression visibilityExpression() const; - void setVisibilityExpression( const QgsOptionalExpression &visibilityExpression ); + void setVisibilityExpression( const QgsOptionalExpression &collapsedExpression ); + + QgsOptionalExpression collapsedExpression() const; + void setCollapsedExpression( const QgsOptionalExpression &collapsedExpression ); RelationEditorConfiguration relationEditorConfiguration() const; void setRelationEditorConfiguration( RelationEditorConfiguration relationEditorConfiguration ); @@ -167,6 +189,8 @@ class GUI_EXPORT QgsAttributesFormProperties : public QWidget, public QgsExpress QmlElementEditorConfiguration mQmlElementEditorConfiguration; HtmlElementEditorConfiguration mHtmlElementEditorConfiguration; QColor mBackgroundColor; + bool mCollapsed = false; + QgsOptionalExpression mCollapsedExpression; }; diff --git a/src/ui/attributeformconfig/qgsattributeformcontaineredit.ui b/src/ui/attributeformconfig/qgsattributeformcontaineredit.ui index 5694c5380c0..9cae8a00401 100644 --- a/src/ui/attributeformconfig/qgsattributeformcontaineredit.ui +++ b/src/ui/attributeformconfig/qgsattributeformcontaineredit.ui @@ -7,48 +7,14 @@ 0 0 401 - 303 + 320 Form - - - - Columns - - - - - - - Show label - - - - - - - Title - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - + Control Visibility by Expression @@ -63,27 +29,7 @@ - - - - 1 - - - 10 - - - - - - - - - - Show as group box - - - - + Style @@ -102,20 +48,96 @@ + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Show label + + + + + + + Columns + + + + + + + 1 + + + 10 + + + + + + + Show as Group Box + + + + + + + + + + Title + + + + + + + Collapsed + + + + + + + Control Collapsed by Expression + + + true + + + + + + + + + + QgsSpinBox + QSpinBox +
qgsspinbox.h
+
QgsCollapsibleGroupBox QGroupBox
qgscollapsiblegroupbox.h
1
- - QgsSpinBox - QSpinBox -
qgsspinbox.h
-
QgsFieldExpressionWidget QWidget @@ -131,10 +153,8 @@
mShowLabelCheckBox - mShowAsGroupBoxCheckBox mTitleLineEdit mColumnCountSpinBox - mControlVisibilityGroupBox mBackgroundColorButton