#14362 Allow arbitrary Z-scale factor

Make the Z-scale combo boxes in the toolbar and property editor editable, so any positive scale value can be entered as text. Invalid input is rejected and the previous value is restored.

Add keyboard shortcuts Ctrl+Shift+Up and Ctrl+Shift+Down to step the Z-scale of the active view through the predefined scale values.
This commit is contained in:
Magne Sjaastad authored and GitHub committed 2026-08-04 12:50:34 +02:00
1 parent 9ccca3e20a
commit 1aa95936fc
12 files changed
+267 -14

No files matched your search

@@ -186,6 +186,8 @@
<file>WestView.svg</file>
<file>Window16x16.png</file>
<file>ZoomAll.svg</file>
<file>ZScaleDecrease.svg</file>
<file>ZScaleIncrease.svg</file>
<file>Calculator.svg</file>
<file>chain.png</file>
<file>clipboard.png</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="M 8 7.3 L 3.5 2.3 L 6.8 2.3 L 6.8 0.5 L 9.2 0.5 L 9.2 2.3 L 12.5 2.3 Z" style="fill: rgb(251, 176, 0);"/>
<path d="M 8 8.7 L 3.5 13.7 L 6.8 13.7 L 6.8 15.5 L 9.2 15.5 L 9.2 13.7 L 12.5 13.7 Z" style="fill: rgb(251, 176, 0);"/>
</svg>

After

Width:  |  Height:  |  Size: 382 B

@@ -0,0 +1,4 @@
<?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="M 8 0.5 L 12.5 5.5 L 9.2 5.5 L 9.2 10.5 L 12.5 10.5 L 8 15.5 L 3.5 10.5 L 6.8 10.5 L 6.8 5.5 L 3.5 5.5 Z" style="fill: rgb(251, 176, 0);"/>
</svg>

After

Width:  |  Height:  |  Size: 293 B

@@ -3,6 +3,7 @@ set(SOURCE_GROUP_SOURCE_FILES
${CMAKE_CURRENT_LIST_DIR}/RicStoreUserDefinedCameraFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicDockIn3dViewFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicDockInPlotViewFeature.cpp
${CMAKE_CURRENT_LIST_DIR}/RicZScaleFeatures.cpp
)
list(APPEND COMMAND_CODE_SOURCE_FILES ${SOURCE_GROUP_SOURCE_FILES})
@@ -0,0 +1,112 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 "RicZScaleFeatures.h"
#include "RiaApplication.h"
#include "RiaDefines.h"
#include "Rim3dView.h"
#include <QAction>
#include <algorithm>
CAF_CMD_SOURCE_INIT( RicIncreaseZScaleFeature, "RicIncreaseZScaleFeature" );
CAF_CMD_SOURCE_INIT( RicDecreaseZScaleFeature, "RicDecreaseZScaleFeature" );
namespace
{
Rim3dView* activeViewWithEditableZScale()
{
Rim3dView* view = RiaApplication::instance()->activeReservoirView();
if ( !view || !view->isScaleZEditable() ) return nullptr;
return view;
}
} // namespace
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RicIncreaseZScaleFeature::isCommandEnabled() const
{
return activeViewWithEditableZScale() != nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicIncreaseZScaleFeature::onActionTriggered( bool isChecked )
{
if ( Rim3dView* view = activeViewWithEditableZScale() )
{
auto scaleOptions = RiaDefines::viewScaleOptions();
auto it = std::upper_bound( scaleOptions.begin(), scaleOptions.end(), view->scaleZ() );
if ( it != scaleOptions.end() )
{
view->setScaleZAndUpdate( *it );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicIncreaseZScaleFeature::setupActionLook( QAction* actionToSetup )
{
actionToSetup->setText( "Increase Z Scale" );
actionToSetup->setToolTip( "Increase Z Scale (Ctrl+Shift+Up)" );
actionToSetup->setIcon( QIcon( ":/ZScaleIncrease.svg" ) );
applyShortcutWithHintToAction( actionToSetup, QKeySequence( tr( "Ctrl+Shift+Up" ) ) );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RicDecreaseZScaleFeature::isCommandEnabled() const
{
return activeViewWithEditableZScale() != nullptr;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicDecreaseZScaleFeature::onActionTriggered( bool isChecked )
{
if ( Rim3dView* view = activeViewWithEditableZScale() )
{
auto scaleOptions = RiaDefines::viewScaleOptions();
auto it = std::lower_bound( scaleOptions.begin(), scaleOptions.end(), view->scaleZ() );
if ( it != scaleOptions.begin() )
{
view->setScaleZAndUpdate( *( it - 1 ) );
}
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void RicDecreaseZScaleFeature::setupActionLook( QAction* actionToSetup )
{
actionToSetup->setText( "Decrease Z Scale" );
actionToSetup->setToolTip( "Decrease Z Scale (Ctrl+Shift+Down)" );
actionToSetup->setIcon( QIcon( ":/ZScaleDecrease.svg" ) );
applyShortcutWithHintToAction( actionToSetup, QKeySequence( tr( "Ctrl+Shift+Down" ) ) );
}
@@ -0,0 +1,47 @@
/////////////////////////////////////////////////////////////////////////////////
//
// 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 RicIncreaseZScaleFeature : public caf::CmdFeature
{
CAF_CMD_HEADER_INIT;
protected:
bool isCommandEnabled() const override;
void onActionTriggered( bool isChecked ) override;
void setupActionLook( QAction* actionToSetup ) override;
};
//==================================================================================================
///
//==================================================================================================
class RicDecreaseZScaleFeature : public caf::CmdFeature
{
CAF_CMD_HEADER_INIT;
protected:
bool isCommandEnabled() const override;
void onActionTriggered( bool isChecked ) override;
void setupActionLook( QAction* actionToSetup ) override;
};
@@ -659,6 +659,15 @@ void RimEclipseContourMapView::zoomAll()
RimEclipseView::zoomAll();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RimEclipseContourMapView::isScaleZEditable() const
{
// A contour map is a flat view, scaling in Z direction is not applicable
return false;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
@@ -46,6 +46,7 @@ public:
RimSurfaceInViewCollection* surfaceInViewCollection() const override;
void zoomAll() override;
bool isScaleZEditable() const override;
void setCompatibleDrawStyle();
@@ -73,6 +73,7 @@
#include "cvfTransform.h"
#include "cvfViewport.h"
#include <algorithm>
#include <climits>
namespace caf
@@ -577,7 +578,7 @@ void Rim3dView::scheduleCreateDisplayModelAndRedraw()
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
std::set<Rim3dView*> Rim3dView::viewsUsingThisAsComparisonView()
std::set<Rim3dView*> Rim3dView::viewsUsingThisAsComparisonView() const
{
std::set<Rim3dView*> containingViews;
std::vector<caf::PdmFieldHandle*> fieldsReferringToMe = referringPtrFields();
@@ -627,7 +628,7 @@ std::vector<Rim3dView*> Rim3dView::validComparisonViews() const
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool Rim3dView::isScaleZEditable()
bool Rim3dView::isScaleZEditable() const
{
return ( this->viewsUsingThisAsComparisonView().empty() || ( this->viewController() && this->viewController()->isCameraLinked() ) );
}
@@ -1007,6 +1008,14 @@ void Rim3dView::fieldChangedByUi( const caf::PdmFieldHandle* changedField, const
}
else if ( changedField == &m_scaleZ )
{
// Invalid text entered in the combo box is converted to 0.0, and a negative scale is not supported
if ( m_scaleZ() <= 0.0 )
{
double fallbackValue = oldValue.toDouble() > 0.0 ? oldValue.toDouble() : 1.0;
m_scaleZ.setValueWithFieldChanged( fallbackValue );
return;
}
updateScaling();
RiuMainWindow::instance()->updateScaleValue();
@@ -1602,7 +1611,15 @@ QList<caf::PdmOptionItemInfo> Rim3dView::calculateValueOptions( const caf::PdmFi
}
else if ( fieldNeedingOptions == &m_scaleZ )
{
for ( auto scale : RiaDefines::viewScaleOptions() )
// Include the current scale value in the options, as any value can be entered as text in the combo box
auto scaleOptions = RiaDefines::viewScaleOptions();
if ( std::find( scaleOptions.begin(), scaleOptions.end(), m_scaleZ() ) == scaleOptions.end() )
{
scaleOptions.push_back( m_scaleZ() );
std::sort( scaleOptions.begin(), scaleOptions.end() );
}
for ( auto scale : scaleOptions )
{
options.push_back( caf::PdmOptionItemInfo( QString::number( scale ), scale ) );
}
@@ -1982,3 +1999,18 @@ void Rim3dView::defineObjectEditorAttribute( QString uiConfigName, caf::PdmUiEdi
treeItemAttribute->tags.push_back( std::move( iconTag ) );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void Rim3dView::defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute )
{
if ( field == &m_scaleZ )
{
if ( auto attr = dynamic_cast<caf::PdmUiComboBoxEditorAttribute*>( attribute ) )
{
attr->enableEditableContent = true;
attr->enableAutoComplete = false;
}
}
}
@@ -180,12 +180,12 @@ public:
virtual void updateGridBoxData();
virtual cvf::BoundingBox domainBoundingBox();
void setScaleZ( double scaleZ );
void setScaleZAndUpdate( double scaleZ );
void updateScaling();
void updateZScaleLabel();
bool isScaleZEditable();
double scaleZ() const;
void setScaleZ( double scaleZ );
void setScaleZAndUpdate( double scaleZ );
void updateScaling();
void updateZScaleLabel();
virtual bool isScaleZEditable() const;
double scaleZ() const;
virtual QString activeFiltersDisplayText() const;
void updateFilterLabel();
@@ -193,7 +193,7 @@ public:
bool isMasterView() const;
Rim3dView* activeComparisonView() const;
void setComparisonView( Rim3dView* compView );
std::set<Rim3dView*> viewsUsingThisAsComparisonView();
std::set<Rim3dView*> viewsUsingThisAsComparisonView() const;
void updateWindowTitle() override;
std::vector<Rim3dView*> validComparisonViews() const;
@@ -273,6 +273,7 @@ protected:
QList<caf::PdmOptionItemInfo> calculateValueOptions( const caf::PdmFieldHandle* fieldNeedingOptions ) override;
void defineObjectEditorAttribute( QString uiConfigName, caf::PdmUiEditorAttribute* attribute ) override;
void defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute ) override;
void setupBeforeSave() override;
@@ -2206,6 +2206,8 @@ QList<caf::PdmOptionItemInfo> RimEclipseView::calculateValueOptions( const caf::
//--------------------------------------------------------------------------------------------------
void RimEclipseView::defineEditorAttribute( const caf::PdmFieldHandle* field, QString uiConfigName, caf::PdmUiEditorAttribute* attribute )
{
RimGridView::defineEditorAttribute( field, uiConfigName, attribute );
if ( field == &m_eclipseCase )
{
RiuTools::enableUpDownArrowsForComboBox( attribute );
@@ -100,8 +100,10 @@
#include <QComboBox>
#include <QDateTime>
#include <QDir>
#include <QDoubleValidator>
#include <QLabel>
#include <QLayout>
#include <QLineEdit>
#include <QMap>
#include <QMenuBar>
#include <QMimeData>
@@ -547,6 +549,9 @@ void RiuMainWindow::createMenus()
viewMenu->addSeparator();
viewMenu->addAction( cmdFeatureMgr->action( "RicApplyUserDefinedCameraFeature" ) );
viewMenu->addAction( cmdFeatureMgr->action( "RicStoreUserDefinedCameraFeature" ) );
viewMenu->addSeparator();
viewMenu->addAction( cmdFeatureMgr->action( "RicIncreaseZScaleFeature" ) );
viewMenu->addAction( cmdFeatureMgr->action( "RicDecreaseZScaleFeature" ) );
connect( viewMenu, SIGNAL( aboutToShow() ), SLOT( slotRefreshViewActions() ) );
@@ -670,8 +675,40 @@ void RiuMainWindow::createToolBars()
{
m_scaleFactor->addItem( QString::number( d ), QVariant( d ) );
}
// Allow arbitrary scale values to be entered as text, see https://github.com/OPM/ResInsight/issues/14362
m_scaleFactor->setEditable( true );
m_scaleFactor->setInsertPolicy( QComboBox::NoInsert );
// Use C locale to match the text produced by QString::number()
auto validator = new QDoubleValidator( m_scaleFactor );
validator->setLocale( QLocale::c() );
validator->setBottom( 0.0 );
m_scaleFactor->setValidator( validator );
toolbar->addWidget( m_scaleFactor );
connect( m_scaleFactor, SIGNAL( currentIndexChanged( int ) ), SLOT( slotScaleChanged( int ) ) );
connect( m_scaleFactor->lineEdit(),
&QLineEdit::editingFinished,
this,
[this]()
{
Rim3dView* view = RiaApplication::instance()->activeReservoirView();
if ( !view ) return;
bool isValidNumber = false;
double scaleValue = m_scaleFactor->lineEdit()->text().toDouble( &isValidNumber );
if ( isValidNumber && scaleValue > 0.0 )
{
view->setScaleZAndUpdate( scaleValue );
}
else
{
// Restore the text in the combo box from the current view scale
updateScaleValue();
}
} );
}
{
@@ -994,7 +1031,8 @@ void RiuMainWindow::slotRefreshViewActions()
{
QStringList commandIds;
commandIds << "RicLinkVisibleViewsFeature" << "RicTogglePerspectiveViewFeature"
<< "RicViewZoomAllFeature" << "RicApplyUserDefinedCameraFeature" << "RicStoreUserDefinedCameraFeature";
<< "RicViewZoomAllFeature" << "RicApplyUserDefinedCameraFeature" << "RicStoreUserDefinedCameraFeature"
<< "RicIncreaseZScaleFeature" << "RicDecreaseZScaleFeature";
caf::CmdFeatureManager::instance()->refreshEnabledState( commandIds );
}
@@ -1884,9 +1922,8 @@ void RiuMainWindow::slotScaleChanged( int index )
//--------------------------------------------------------------------------------------------------
void RiuMainWindow::updateScaleValue()
{
Rim3dView* view = RiaApplication::instance()->activeReservoirView();
bool isRegularReservoirView = view && dynamic_cast<RimEclipseContourMapView*>( view ) == nullptr;
if ( isRegularReservoirView && view->isScaleZEditable() )
Rim3dView* view = RiaApplication::instance()->activeReservoirView();
if ( view && view->isScaleZEditable() )
{
m_scaleFactor->setEnabled( true );
m_scaleFactor->blockSignals( true );