#14539 Add shared-axis, pinned-range indicators and Auto Zoom command for axis objects

Replace the chain/padlock icon with a simple two-headed-arrow icon
(SharedAxis.svg) for linked time/property axes.

Add a pinned-range project-tree tag shown when an axis range is
user-defined, using the standard gray pinned.svg icon. The tag
refreshes immediately regardless of the code path that changes the
range/auto-zoom state (Zoom All, data source changes, axis linking,
UI edits), and clicking the tag releases the user-defined range so
the axis auto-zooms again.

Add RicToggleAxisAutoZoomFeature, a checkable context-menu command
on Y-axis and time-axis objects that toggles auto-zoom and keeps the
pinned/user-defined-range tag in sync in both directions.

Move axis-specific context-menu entries out of RimContextCommandBuilder
into appendMenuItems() overrides on RimPlotAxisProperties and
RimSummaryTimeAxisProperties, per the project convention of keeping
object-specific menu logic in the object.

Fix RimSummaryPlot::timeAxisSettingsChanged() to actually re-apply
auto-scale to the plot widget when auto-zoom is re-enabled on the
time axis; previously only the property was updated, and no
recompute of the plotted range was triggered.
This commit is contained in:
Magne Sjaastad committed 2026-09-09 12:08:10 +02:00
1 parent 110bdbe0c7
commit 3284fd200a
15 files changed
+247 -38

No files matched your search

@@ -95,8 +95,6 @@
#include "RimParameterResultCrossPlot.h"
#include "RimPerforationCollection.h"
#include "RimPerforationInterval.h"
#include "RimPlotAxisProperties.h"
#include "RimPlotAxisPropertiesInterface.h"
#include "RimPlotDataFilterCollection.h"
#include "RimPlotDataFilterItem.h"
#include "RimPltPlotCollection.h"
@@ -130,7 +128,6 @@
#include "RimSummaryPlot.h"
#include "RimSummaryTable.h"
#include "RimSummaryTableCollection.h"
#include "RimSummaryTimeAxisProperties.h"
#include "RimValveCollection.h"
#include "RimValveTemplate.h"
#include "RimValveTemplateCollection.h"
@@ -970,18 +967,6 @@ caf::CmdFeatureMenuBuilder RimContextCommandBuilder::commandsFromSelection()
menuBuilder << "RicCreatePlotFromTemplateByShortcutFeature";
menuBuilder << "RicCreateCrossPlotFeature";
}
else if ( dynamic_cast<RimPlotAxisPropertiesInterface*>( firstUiItem ) )
{
if ( dynamic_cast<RimPlotAxisProperties*>( firstUiItem ) )
{
menuBuilder << "RicToggleYAxisLinkingFeature";
}
if ( dynamic_cast<RimSummaryTimeAxisProperties*>( firstUiItem ) )
{
menuBuilder << "RicToggleXAxisLinkingFeature";
}
menuBuilder << "RicNewPlotAxisPropertiesFeature";
}
else if ( dynamic_cast<RimRftCase*>( firstUiItem ) )
{
menuBuilder << "RicNewMultiPhaseRftSegmentPlotFeature";
@@ -27,6 +27,7 @@
#include "RimPlotAxisAnnotation.h"
#include "RimSummaryMultiPlot.h"
#include "cafCmdFeatureMenuBuilder.h"
#include "cafPdmUiSliderEditor.h"
#include "cafPdmUiTreeAttributes.h"
@@ -415,7 +416,13 @@ bool RimPlotAxisProperties::isAutoZoom() const
//--------------------------------------------------------------------------------------------------
void RimPlotAxisProperties::setAutoZoom( bool enableAutoZoom )
{
if ( m_isAutoZoom() == enableAutoZoom ) return;
m_isAutoZoom = enableAutoZoom;
// Make sure the project tree tag reflecting the auto-zoom state is updated immediately, also when this method
// is called from code paths other than the UI.
updateConnectedEditors();
}
//--------------------------------------------------------------------------------------------------
@@ -760,25 +767,58 @@ caf::FontTools::FontSize RimPlotAxisProperties::plotFontSize() const
return RiaPreferences::current()->defaultPlotFontSize();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimPlotAxisProperties::appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const
{
menuBuilder << "RicToggleYAxisLinkingFeature";
menuBuilder << "RicToggleAxisAutoZoomFeature";
menuBuilder << "RicNewPlotAxisPropertiesFeature";
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimPlotAxisProperties::defineObjectEditorAttribute( QString uiConfigName, caf::PdmUiEditorAttribute* attribute )
{
auto summaryMultiPlot = firstAncestorOfType<RimSummaryMultiPlot>();
auto* treeItemAttribute = dynamic_cast<caf::PdmUiTreeViewItemAttribute*>( attribute );
if ( !treeItemAttribute ) return;
treeItemAttribute->tags.clear();
auto summaryMultiPlot = firstAncestorOfType<RimSummaryMultiPlot>();
if ( summaryMultiPlot && summaryMultiPlot->isSubPlotAxesLinked() )
{
auto* treeItemAttribute = dynamic_cast<caf::PdmUiTreeViewItemAttribute*>( attribute );
if ( treeItemAttribute )
{
treeItemAttribute->tags.clear();
auto tag = caf::PdmUiTreeViewItemAttribute::createTag();
tag->icon = caf::IconProvider( ":/chain.png" );
auto tag = caf::PdmUiTreeViewItemAttribute::createTag();
tag->icon = caf::IconProvider( ":/SharedAxis.svg" );
treeItemAttribute->tags.push_back( std::move( tag ) );
}
treeItemAttribute->tags.push_back( std::move( tag ) );
}
if ( isRangeUserDefined() )
{
// Indicate that the axis range is fixed by the user, and will not be updated by auto-zoom.
// Clicking the tag releases the user-defined range, so the axis follows the data again.
auto tag = caf::PdmUiTreeViewItemAttribute::createTag();
tag->icon = caf::IconProvider( ":/pinned.svg" );
tag->clicked.connect( this, &RimPlotAxisProperties::onPinnedTagClicked );
treeItemAttribute->tags.push_back( std::move( tag ) );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimPlotAxisProperties::onPinnedTagClicked( const caf::SignalEmitter* emitter, size_t index )
{
// Release the user-defined range and let the axis follow the data again, same as re-enabling
// "Set Range Automatically" from the property editor.
setAutoZoom( true );
setRangeUserDefined( false );
settingsChanged.send();
}
//--------------------------------------------------------------------------------------------------
@@ -32,6 +32,11 @@
#include <QString>
namespace caf
{
class SignalEmitter;
}
class RimPlotAxisAnnotation;
//==================================================================================================
@@ -140,6 +145,8 @@ public:
void enableAutoValueForAllFields( bool enable );
void appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const override;
protected:
void initAfterRead() override;
caf::PdmFieldHandle* userDescriptionField() override;
@@ -153,6 +160,7 @@ private:
void updateOptionSensitivity();
caf::FontTools::FontSize plotFontSize() const;
void defineObjectEditorAttribute( QString uiConfigName, caf::PdmUiEditorAttribute* attribute ) override;
void onPinnedTagClicked( const caf::SignalEmitter* emitter, size_t index );
private:
caf::PdmField<bool> m_isActive;
@@ -70,7 +70,14 @@ bool RimPlotAxisPropertiesInterface::isRangeUserDefined() const
//--------------------------------------------------------------------------------------------------
void RimPlotAxisPropertiesInterface::setRangeUserDefined( bool isUserDefined )
{
if ( m_isRangeUserDefined() == isUserDefined ) return;
m_isRangeUserDefined = isUserDefined;
// Make sure the project tree tag reflecting the range state (see defineObjectEditorAttribute() in derived
// classes) is updated immediately, also when this method is called from code paths other than the UI (e.g.
// zoom-all, data source changes, axis linking).
updateConnectedEditors();
}
//--------------------------------------------------------------------------------------------------
@@ -2171,10 +2171,18 @@ void RimSummaryPlot::timeAxisSettingsChanged( const caf::SignalEmitter* emitter
return;
}
if ( !timeAxisProps->isAutoZoom() && plotWidget() )
if ( plotWidget() )
{
// If not auto zoom - the new date and time ranges must be set and axes updated
plotWidget()->setAxisScale( RimSummaryPlot::plotAxisForTime(), timeAxisProps->visibleRangeMin(), timeAxisProps->visibleRangeMax() );
if ( timeAxisProps->isAutoZoom() )
{
// Auto zoom (re-)enabled - let the plot widget recompute the visible range from the data
updateZoomForTimeAxis( timeAxisProps );
}
else
{
// If not auto zoom - the new date and time ranges must be set and axes updated
plotWidget()->setAxisScale( RimSummaryPlot::plotAxisForTime(), timeAxisProps->visibleRangeMin(), timeAxisProps->visibleRangeMax() );
}
plotWidget()->updateAxes();
}
@@ -34,6 +34,7 @@
#include "cafPdmUiTreeAttributes.h"
#include "cafAssert.h"
#include "cafCmdFeatureMenuBuilder.h"
#include "qwt_date.h"
@@ -217,24 +218,58 @@ caf::FontTools::FontSize RimSummaryTimeAxisProperties::plotFontSize() const
return RiaPreferences::current()->defaultPlotFontSize();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryTimeAxisProperties::appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const
{
menuBuilder << "RicToggleXAxisLinkingFeature";
menuBuilder << "RicToggleAxisAutoZoomFeature";
menuBuilder << "RicNewPlotAxisPropertiesFeature";
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryTimeAxisProperties::defineObjectEditorAttribute( QString uiConfigName, caf::PdmUiEditorAttribute* attribute )
{
auto* treeItemAttribute = dynamic_cast<caf::PdmUiTreeViewItemAttribute*>( attribute );
if ( !treeItemAttribute ) return;
treeItemAttribute->tags.clear();
auto summaryMultiPlot = firstAncestorOfType<RimSummaryMultiPlot>();
if ( summaryMultiPlot && summaryMultiPlot->isTimeAxisLinked() )
{
auto* treeItemAttribute = dynamic_cast<caf::PdmUiTreeViewItemAttribute*>( attribute );
if ( treeItemAttribute )
{
treeItemAttribute->tags.clear();
auto tag = caf::PdmUiTreeViewItemAttribute::createTag();
tag->icon = caf::IconProvider( ":/chain.png" );
auto tag = caf::PdmUiTreeViewItemAttribute::createTag();
tag->icon = caf::IconProvider( ":/SharedAxis.svg" );
treeItemAttribute->tags.push_back( std::move( tag ) );
}
treeItemAttribute->tags.push_back( std::move( tag ) );
}
if ( isRangeUserDefined() )
{
// Indicate that the axis range is fixed by the user, and will not be updated by auto-zoom.
// Clicking the tag releases the user-defined range, so the axis follows the data again.
auto tag = caf::PdmUiTreeViewItemAttribute::createTag();
tag->icon = caf::IconProvider( ":/pinned.svg" );
tag->clicked.connect( this, &RimSummaryTimeAxisProperties::onPinnedTagClicked );
treeItemAttribute->tags.push_back( std::move( tag ) );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RimSummaryTimeAxisProperties::onPinnedTagClicked( const caf::SignalEmitter* emitter, size_t index )
{
// Release the user-defined range and let the axis follow the data again, same as re-enabling
// "Set Range Automatically" from the property editor.
setAutoZoom( true );
setRangeUserDefined( false );
settingsChanged.send();
}
//--------------------------------------------------------------------------------------------------
@@ -314,7 +349,13 @@ bool RimSummaryTimeAxisProperties::isAutoZoom() const
//--------------------------------------------------------------------------------------------------
void RimSummaryTimeAxisProperties::setAutoZoom( bool enableAutoZoom )
{
if ( m_isAutoZoom() == enableAutoZoom ) return;
m_isAutoZoom = enableAutoZoom;
// Make sure the project tree tag reflecting the auto-zoom state is updated immediately, also when this method
// is called from code paths other than the UI.
updateConnectedEditors();
}
//--------------------------------------------------------------------------------------------------
@@ -33,6 +33,11 @@
#include <QDateTime>
#include <QString>
namespace caf
{
class SignalEmitter;
}
//==================================================================================================
///
///
@@ -151,6 +156,8 @@ public:
const QString objectName() const override;
const QString axisTitleText() const override;
void appendMenuItems( caf::CmdFeatureMenuBuilder& menuBuilder ) const override;
protected:
void fieldChangedByUi( const caf::PdmFieldHandle* changedField, const QVariant& oldValue, const QVariant& newValue ) override;
QList<caf::PdmOptionItemInfo> calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) override;
@@ -166,6 +173,7 @@ protected:
private:
void defineObjectEditorAttribute( QString uiConfigName, caf::PdmUiEditorAttribute* attribute ) override;
void onPinnedTagClicked( const caf::SignalEmitter* emitter, size_t index );
private:
caf::PdmField<QString> m_title;