diff --git a/app/attributes/attributecontroller.cpp b/app/attributes/attributecontroller.cpp index e9344628f..4a84d714e 100644 --- a/app/attributes/attributecontroller.cpp +++ b/app/attributes/attributecontroller.cpp @@ -359,59 +359,59 @@ void AttributeController::clearAll() void AttributeController::updateOnLayerChange() { clearAll(); + QgsVectorLayer *layer = mFeatureLayerPair.layer(); + if ( !layer ) + return; // 1) DATA - QgsVectorLayer *layer = mFeatureLayerPair.layer(); - if ( layer ) + if ( layer->editFormConfig().layout() == Qgis::AttributeFormLayout::DragAndDrop ) { - if ( layer->editFormConfig().layout() == Qgis::AttributeFormLayout::DragAndDrop ) + QgsAttributeEditorContainer *root = layer->editFormConfig().invisibleRootContainer(); + if ( root->columnCount() > 1 ) { - QgsAttributeEditorContainer *root = layer->editFormConfig().invisibleRootContainer(); - if ( root->columnCount() > 1 ) - { - qDebug() << "root tab in manual config has multiple columns. not supported on mobile devices!"; - root->setColumnCount( 1 ); - } + qDebug() << "root tab in manual config has multiple columns. not supported on mobile devices!"; + root->setColumnCount( 1 ); + } - mHasTabs = allowTabs( root ); - if ( mHasTabs ) + mHasTabs = allowTabs( root ); + if ( mHasTabs ) + { + for ( QgsAttributeEditorElement *element : root->children() ) { - for ( QgsAttributeEditorElement *element : root->children() ) + if ( element->type() == Qgis::AttributeEditorType::Container ) { - if ( element->type() == Qgis::AttributeEditorType::Container ) + QgsAttributeEditorContainer *container = static_cast( element ); + if ( container->columnCount() > 1 ) { - QgsAttributeEditorContainer *container = static_cast( element ); - if ( container->columnCount() > 1 ) - { - qDebug() << "tab " << container->name() << " in manual config has multiple columns. not supported on mobile devices!"; - container->setColumnCount( 1 ); - } - createTab( container ); + qDebug() << "tab " << container->name() << " in manual config has multiple columns. not supported on mobile devices!"; + container->setColumnCount( 1 ); } + createTab( container ); } } - else - { - createTab( root ); - } } else { - // Auto-Generated Layout - // We create fake root tab - QgsAttributeEditorContainer *tab = autoLayoutTabContainer(); - - // We need to look for relations and include them into form, - // in auto-generated layout they are not included in form config - discoverRelations( tab ); - - createTab( tab ); + createTab( root ); } + } + else + { + // Auto-Generated Layout + // We create fake root tab + QgsAttributeEditorContainer *tab = autoLayoutTabContainer(); + + // We need to look for relations and include them into form, + // in auto-generated layout they are not included in form config + discoverRelations( tab ); - if ( mRememberAttributesController ) - mRememberAttributesController->storeLayerFields( layer ); + createTab( tab ); } + if ( mRememberAttributesController ) + mRememberAttributesController->storeLayerFields( layer ); + + // 2) MODELS // for all other models, ownership is managed by Qt parent system AttributeTabModel *tabModel = new AttributeTabModel( mAttributeTabProxyModel.get(), this, mTabItems.size() ); @@ -462,6 +462,9 @@ void AttributeController::updateOnLayerChange() void AttributeController::updateOnFeatureChange() { + if ( !mFeatureLayerPair.layer() ) + return; + const QgsFeature feature = mFeatureLayerPair.feature(); QMap>::iterator formItemsIterator = mFormItems.begin(); diff --git a/app/inpututils.h b/app/inpututils.h index 342972d6f..27e95b3ed 100644 --- a/app/inpututils.h +++ b/app/inpututils.h @@ -470,7 +470,7 @@ class InputUtils: public QObject Q_INVOKABLE static QString featureTitle( const FeatureLayerPair &pair, QgsProject *project ); //! Creates featureLayerPair from geometry and layer, evaluates its expressions and returns it. - Q_INVOKABLE static FeatureLayerPair createFeatureLayerPair( QgsVectorLayer *layer, const QgsGeometry &geometry, VariablesManager *variablesmanager, QgsExpressionContextScope *additionalScope = nullptr ); + Q_INVOKABLE static FeatureLayerPair createFeatureLayerPair( QgsVectorLayer *layer = nullptr, const QgsGeometry &geometry = QgsGeometry(), VariablesManager *variablesmanager = nullptr, QgsExpressionContextScope *additionalScope = nullptr ); Q_INVOKABLE static void createEditBuffer( QgsVectorLayer *layer ); diff --git a/app/qml/FormsStackManager.qml b/app/qml/FormsStackManager.qml index a76a58f12..d2fec6aea 100644 --- a/app/qml/FormsStackManager.qml +++ b/app/qml/FormsStackManager.qml @@ -83,6 +83,11 @@ Item { } function _getActiveForm() { + if (formsStack.depth === 0) { + // no active form on empty form stack + return null + } + if ( root.activeFormIndex >= 0 && root.activeFormIndex < formsStack.depth ) { return formsStack.get( activeFormIndex ) } @@ -137,7 +142,25 @@ Item { } function reload() { - formsStack.clear() // removes all objects thanks to Qt parent system + // Even after formStack.clear() is called, + // forms in the formStack will still + // receive and evaluate some signals + // and most importantly in some scenarios + // featureLayerPair could contain already + // dangling pointer to layer and crash the app + // https://github.com/MerginMaps/input/issues/2879 + for ( let i = 0; i < formsStack.depth; i++ ) { + let form = formsStack.get( i ) + form.featureLayerPair = __inputUtils.createFeatureLayerPair() + form.relationToApply = null + form.controllerToApply = null + form.project = null + form.linkedRelation = null + form.parentController = null + } + + // removes all objects thanks to Qt parent system + formsStack.clear(StackView.Immediate) } function openLinkedFeature( linkedFeature ) { diff --git a/app/qml/form/FormWrapper.qml b/app/qml/form/FormWrapper.qml index 26e5aed6b..8ccec1230 100644 --- a/app/qml/form/FormWrapper.qml +++ b/app/qml/form/FormWrapper.qml @@ -34,7 +34,7 @@ Item { property real previewHeight property real panelHeight - property bool isReadOnly: featureLayerPair ? featureLayerPair.layer.readOnly : false + property bool isReadOnly: featureLayerPair && featureLayerPair.layer ? featureLayerPair.layer.readOnly : false signal closed() signal editGeometry( var pair )