From 3508b0a72ea3a16c22200cbaa32f4739204d1eda Mon Sep 17 00:00:00 2001 From: Magne Sjaastad Date: Tue, 25 Aug 2026 19:48:41 +0200 Subject: [PATCH] #14558 3D filters: Reconnect filter signals after project load Reading a caf::PdmPtrField sets the pointer to null before the reference string is resolved, and clearing a ptr field disconnects all signals between the referenced object and the field owner. The signal connections established by RimFilterInViewCollection::setSourceCollections() in the view constructor were therefore lost when a project was loaded, and adding a cell filter, property filter or data filter no longer refreshed the Filters node in the project tree. Reconnect in initAfterRead, which runs after the references have been resolved. --- .../CellFilters/RimFilterInViewCollection.cpp | 8 ++ .../CellFilters/RimFilterInViewCollection.h | 1 + ApplicationLibCode/UnitTests/CMakeLists.txt | 1 + .../RimFilterInViewCollection-Test.cpp | 96 +++++++++++++++++++ 4 files changed, 106 insertions(+) create mode 100644 ApplicationLibCode/UnitTests/RimFilterInViewCollection-Test.cpp diff --git a/ApplicationLibCode/ProjectDataModel/CellFilters/RimFilterInViewCollection.cpp b/ApplicationLibCode/ProjectDataModel/CellFilters/RimFilterInViewCollection.cpp index 775098c36d..6f31e9b273 100644 --- a/ApplicationLibCode/ProjectDataModel/CellFilters/RimFilterInViewCollection.cpp +++ b/ApplicationLibCode/ProjectDataModel/CellFilters/RimFilterInViewCollection.cpp @@ -144,6 +144,14 @@ QString RimFilterInViewCollection::activeFiltersDisplayText() const return parts.join( QStringLiteral( ", " ) ); } +//-------------------------------------------------------------------------------------------------- +// +//-------------------------------------------------------------------------------------------------- +void RimFilterInViewCollection::initAfterRead() +{ + connectSourceSignals(); +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ProjectDataModel/CellFilters/RimFilterInViewCollection.h b/ApplicationLibCode/ProjectDataModel/CellFilters/RimFilterInViewCollection.h index f41dde0820..aaef999562 100644 --- a/ApplicationLibCode/ProjectDataModel/CellFilters/RimFilterInViewCollection.h +++ b/ApplicationLibCode/ProjectDataModel/CellFilters/RimFilterInViewCollection.h @@ -57,6 +57,7 @@ public: QString activeFiltersDisplayText() const; protected: + void initAfterRead() override; void defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName = "" ) override; void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override; void appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const override; diff --git a/ApplicationLibCode/UnitTests/CMakeLists.txt b/ApplicationLibCode/UnitTests/CMakeLists.txt index f54e29435a..69cd1c87c6 100644 --- a/ApplicationLibCode/UnitTests/CMakeLists.txt +++ b/ApplicationLibCode/UnitTests/CMakeLists.txt @@ -88,6 +88,7 @@ set(SOURCE_UNITTEST_FILES ${CMAKE_CURRENT_LIST_DIR}/RimWellPathGeometryDef-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/RimWellRftPlot-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/RimDataFilterCollection-Test.cpp + ${CMAKE_CURRENT_LIST_DIR}/RimFilterInViewCollection-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/RimSummaryCaseCollection-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/RimSummaryCaseMainCollection-Test.cpp ${CMAKE_CURRENT_LIST_DIR}/RimSummaryTimeAxisProperties-Test.cpp diff --git a/ApplicationLibCode/UnitTests/RimFilterInViewCollection-Test.cpp b/ApplicationLibCode/UnitTests/RimFilterInViewCollection-Test.cpp new file mode 100644 index 0000000000..aa60a444f6 --- /dev/null +++ b/ApplicationLibCode/UnitTests/RimFilterInViewCollection-Test.cpp @@ -0,0 +1,96 @@ +///////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2026 Equinor ASA +// +// ResInsight is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// ResInsight is distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// See the GNU General Public License at +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "gtest/gtest.h" + +#include "RimCellFilterCollection.h" +#include "RimDataFilterInViewCollection.h" +#include "RimEclipsePropertyFilterCollection.h" +#include "RimEclipseView.h" +#include "RimFilterInViewCollection.h" + +#include "cafPdmDefaultObjectFactory.h" +#include "cafPdmXmlObjectHandle.h" +#include "cafSignal.h" + +#include + +namespace +{ +bool observesSignal( const caf::SignalObserver* observer, const caf::AbstractSignal* signal ) +{ + for ( const auto* observed : observer->observedSignals() ) + { + if ( observed == signal ) return true; + } + return false; +} + +RimFilterInViewCollection* filterFacade( RimEclipseView* view ) +{ + auto facades = view->descendantsIncludingThisOfType(); + return facades.empty() ? nullptr : facades.front(); +} +} // namespace + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +TEST( RimFilterInViewCollection, facadeObservesCellFiltersInNewView ) +{ + auto view = std::make_unique(); + + auto* facade = filterFacade( view.get() ); + ASSERT_TRUE( facade != nullptr ); + + EXPECT_EQ( view->cellFilterCollection(), facade->cellFilters() ); + EXPECT_TRUE( observesSignal( facade, &facade->cellFilters()->filtersChanged ) ); + EXPECT_TRUE( observesSignal( facade, &facade->propertyFilters()->filtersChanged ) ); + EXPECT_TRUE( observesSignal( facade, &facade->dataFiltersInView()->wrappersChanged ) ); +} + +//-------------------------------------------------------------------------------------------------- +/// +//-------------------------------------------------------------------------------------------------- +TEST( RimFilterInViewCollection, facadeObservesCellFiltersAfterProjectRead ) +{ + auto sourceView = std::make_unique(); + QString xml = sourceView->xmlCapability()->writeObjectToXmlString(); + + auto readView = std::make_unique(); + + const auto* facadeBeforeRead = filterFacade( readView.get() ); + const auto* collectionBeforeRead = readView->cellFilterCollection(); + + readView->xmlCapability()->readObjectFromXmlString( xml, caf::PdmDefaultObjectFactory::instance() ); + readView->xmlCapability()->resolveReferencesRecursively(); + readView->xmlCapability()->initAfterReadRecursively(); + + auto* facade = filterFacade( readView.get() ); + ASSERT_TRUE( facade != nullptr ); + + EXPECT_EQ( facadeBeforeRead, facade ); + EXPECT_EQ( collectionBeforeRead, readView->cellFilterCollection() ); + EXPECT_EQ( readView->cellFilterCollection(), facade->cellFilters() ); + + // Reading the ptr fields disconnects the signals set up by the view constructor. Without a + // reconnect, the tree node is not refreshed when a filter is added to a loaded project. + EXPECT_TRUE( observesSignal( facade, &facade->cellFilters()->filtersChanged ) ); + EXPECT_TRUE( observesSignal( facade, &facade->propertyFilters()->filtersChanged ) ); + EXPECT_TRUE( observesSignal( facade, &facade->dataFiltersInView()->wrappersChanged ) ); +}