#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

@@ -297,6 +297,7 @@
<file>regression-curve.svg</file>
<file>padlock.svg</file>
<file>padlock-unlocked.svg</file>
<file>SharedAxis.svg</file>
<file>warning.svg</file>
<file>cloud-and-server.svg</file>
<file>Cloud.svg</file>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 8 L4 5 M1 8 L4 11 M1 8 L15 8 M15 8 L12 5 M15 8 L12 11"
style="stroke: rgb(128,128,128); stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round;"/>
</svg>

After

Width:  |  Height:  |  Size: 321 B

+1 -1
View File
@@ -1 +1 @@
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path d="M4 2h7v.278c0 .406-.086.778-.258 1.117-.172.339-.42.63-.742.875v2.86c.307.145.583.328.828.546.245.219.456.464.633.735.177.27.31.565.398.882.089.318.136.646.141.985v.5H8V14l-.5 1-.5-1v-3.222H3v-.5c0-.339.047-.664.14-.977.094-.312.227-.607.4-.883A3.404 3.404 0 0 1 5 7.13V4.27a2.561 2.561 0 0 1-.734-.875A2.505 2.505 0 0 1 4 2.278V2zm1.086.778c.042.125.094.232.156.32a1.494 1.494 0 0 0 .461.43L6 3.715v4.102l-.336.117c-.411.146-.76.383-1.047.711C4.331 8.973 4.09 9.573 4 10h7c-.088-.427-.33-1.027-.617-1.355a2.456 2.456 0 0 0-1.047-.71L9 7.816V3.715l.297-.18c.094-.057.177-.122.25-.195a2.28 2.28 0 0 0 .21-.242.968.968 0 0 0 .157-.32H5.086z"/></svg>
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path style="fill: rgb(128,128,128);" d="M4 2h7v.278c0 .406-.086.778-.258 1.117-.172.339-.42.63-.742.875v2.86c.307.145.583.328.828.546.245.219.456.464.633.735.177.27.31.565.398.882.089.318.136.646.141.985v.5H8V14l-.5 1-.5-1v-3.222H3v-.5c0-.339.047-.664.14-.977.094-.312.227-.607.4-.883A3.404 3.404 0 0 1 5 7.13V4.27a2.561 2.561 0 0 1-.734-.875A2.505 2.505 0 0 1 4 2.278V2zm1.086.778c.042.125.094.232.156.32a1.494 1.494 0 0 0 .461.43L6 3.715v4.102l-.336.117c-.411.146-.76.383-1.047.711C4.331 8.973 4.09 9.573 4 10h7c-.088-.427-.33-1.027-.617-1.355a2.456 2.456 0 0 0-1.047-.71L9 7.816V3.715l.297-.18c.094-.057.177-.122.25-.195a2.28 2.28 0 0 0 .21-.242.968.968 0 0 0 .157-.32H5.086z"/></svg>

Before

Width:  |  Height:  |  Size: 759 B

After

Width:  |  Height:  |  Size: 771 B

@@ -42,6 +42,7 @@ set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RicDeleteSubPlotCtxFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicToggleYAxisLinkingFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicToggleXAxisLinkingFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicToggleAxisAutoZoomFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicNewSummaryTableFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicDuplicateSummaryTableFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicCreateDeclineCurvesFeature.cpp
@@ -0,0 +1,71 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#include "RicToggleAxisAutoZoomFeature.h"
#include "RimPlotAxisPropertiesInterface.h"
#include "cafSelectionManagerTools.h"
#include <QAction>
CAF_CMD_SOURCE_INIT( RicToggleAxisAutoZoomFeature, "RicToggleAxisAutoZoomFeature" );
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RicToggleAxisAutoZoomFeature::isCommandEnabled() const
{
auto* axisProperties = caf::firstAncestorOfTypeFromSelectedObject<RimPlotAxisPropertiesInterface>();
return ( axisProperties != nullptr );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicToggleAxisAutoZoomFeature::onActionTriggered( bool isChecked )
{
auto* axisProperties = caf::firstAncestorOfTypeFromSelectedObject<RimPlotAxisPropertiesInterface>();
if ( !axisProperties ) return;
bool enableAutoZoom = !axisProperties->isAutoZoom();
axisProperties->setAutoZoom( enableAutoZoom );
// The range is defined by the user unless auto-zoom is (re-)enabled, matching the behavior of the
// "Set Range Automatically" checkbox in the property editor. This also makes sure the pinned tag
// in the project tree is updated to reflect the new state.
axisProperties->setRangeUserDefined( !enableAutoZoom );
axisProperties->settingsChanged.send();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicToggleAxisAutoZoomFeature::setupActionLook( QAction* actionToSetup )
{
actionToSetup->setText( "Auto Zoom" );
actionToSetup->setIcon( QIcon( ":/ZoomAll.svg" ) );
actionToSetup->setCheckable( true );
auto* axisProperties = caf::firstAncestorOfTypeFromSelectedObject<RimPlotAxisPropertiesInterface>();
if ( axisProperties )
{
actionToSetup->setChecked( axisProperties->isAutoZoom() );
}
}
@@ -0,0 +1,34 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 <http://www.gnu.org/licenses/gpl.html>
// for more details.
//
/////////////////////////////////////////////////////////////////////////////////
#pragma once
#include "cafCmdFeature.h"
//==================================================================================================
///
//==================================================================================================
class RicToggleAxisAutoZoomFeature : public caf::CmdFeature
{
CAF_CMD_HEADER_INIT;
protected:
bool isCommandEnabled() const override;
void onActionTriggered( bool isChecked ) override;
void setupActionLook( QAction* actionToSetup ) override;
};
@@ -64,5 +64,5 @@ void RicToggleXAxisLinkingFeature::setupActionLook( QAction* actionToSetup )
}
actionToSetup->setText( text + " Time Axis" );
actionToSetup->setIcon( QIcon( ":/chain.png" ) );
actionToSetup->setIcon( QIcon( ":/SharedAxis.svg" ) );
}
@@ -72,5 +72,5 @@ void RicToggleYAxisLinkingFeature::setupActionLook( QAction* actionToSetup )
}
actionToSetup->setText( text + " Y Axis" );
actionToSetup->setIcon( QIcon( ":/chain.png" ) );
actionToSetup->setIcon( QIcon( ":/SharedAxis.svg" ) );
}
@@ -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;