From 1aa95936fcb3638750161fdbbbc0941fb56d8354 Mon Sep 17 00:00:00 2001 From: Magne Sjaastad Date: Tue, 4 Aug 2026 12:50:34 +0200 Subject: [PATCH] #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. --- ApplicationExeCode/Resources/ResInsight.qrc | 2 + .../Resources/ZScaleDecrease.svg | 5 + .../Resources/ZScaleIncrease.svg | 4 + .../Commands/3dView/CMakeLists_files.cmake | 1 + .../Commands/3dView/RicZScaleFeatures.cpp | 112 ++++++++++++++++++ .../Commands/3dView/RicZScaleFeatures.h | 47 ++++++++ .../ContourMap/RimEclipseContourMapView.cpp | 9 ++ .../ContourMap/RimEclipseContourMapView.h | 1 + .../ProjectDataModel/Rim3dView.cpp | 38 +++++- .../ProjectDataModel/Rim3dView.h | 15 +-- .../ProjectDataModel/RimEclipseView.cpp | 2 + .../UserInterface/RiuMainWindow.cpp | 45 ++++++- 12 files changed, 267 insertions(+), 14 deletions(-) create mode 100644 ApplicationExeCode/Resources/ZScaleDecrease.svg create mode 100644 ApplicationExeCode/Resources/ZScaleIncrease.svg create mode 100644 ApplicationLibCode/Commands/3dView/RicZScaleFeatures.cpp create mode 100644 ApplicationLibCode/Commands/3dView/RicZScaleFeatures.h diff --git a/ApplicationExeCode/Resources/ResInsight.qrc b/ApplicationExeCode/Resources/ResInsight.qrc index 6d3095250f..7bcba10994 100644 --- a/ApplicationExeCode/Resources/ResInsight.qrc +++ b/ApplicationExeCode/Resources/ResInsight.qrc @@ -186,6 +186,8 @@ WestView.svg Window16x16.png ZoomAll.svg + ZScaleDecrease.svg + ZScaleIncrease.svg Calculator.svg chain.png clipboard.png diff --git a/ApplicationExeCode/Resources/ZScaleDecrease.svg b/ApplicationExeCode/Resources/ZScaleDecrease.svg new file mode 100644 index 0000000000..c4cbeb0c5b --- /dev/null +++ b/ApplicationExeCode/Resources/ZScaleDecrease.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/ApplicationExeCode/Resources/ZScaleIncrease.svg b/ApplicationExeCode/Resources/ZScaleIncrease.svg new file mode 100644 index 0000000000..95c838b167 --- /dev/null +++ b/ApplicationExeCode/Resources/ZScaleIncrease.svg @@ -0,0 +1,4 @@ + + + + diff --git a/ApplicationLibCode/Commands/3dView/CMakeLists_files.cmake b/ApplicationLibCode/Commands/3dView/CMakeLists_files.cmake index 18caa0121b..f217ca292a 100644 --- a/ApplicationLibCode/Commands/3dView/CMakeLists_files.cmake +++ b/ApplicationLibCode/Commands/3dView/CMakeLists_files.cmake @@ -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}) diff --git a/ApplicationLibCode/Commands/3dView/RicZScaleFeatures.cpp b/ApplicationLibCode/Commands/3dView/RicZScaleFeatures.cpp new file mode 100644 index 0000000000..483204e1fc --- /dev/null +++ b/ApplicationLibCode/Commands/3dView/RicZScaleFeatures.cpp @@ -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 +// for more details. +// +///////////////////////////////////////////////////////////////////////////////// + +#include "RicZScaleFeatures.h" + +#include "RiaApplication.h" +#include "RiaDefines.h" + +#include "Rim3dView.h" + +#include + +#include + +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" ) ) ); +} diff --git a/ApplicationLibCode/Commands/3dView/RicZScaleFeatures.h b/ApplicationLibCode/Commands/3dView/RicZScaleFeatures.h new file mode 100644 index 0000000000..ac43fdc076 --- /dev/null +++ b/ApplicationLibCode/Commands/3dView/RicZScaleFeatures.h @@ -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 +// 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; +}; diff --git a/ApplicationLibCode/ProjectDataModel/ContourMap/RimEclipseContourMapView.cpp b/ApplicationLibCode/ProjectDataModel/ContourMap/RimEclipseContourMapView.cpp index 6411c193ec..68e6d06800 100644 --- a/ApplicationLibCode/ProjectDataModel/ContourMap/RimEclipseContourMapView.cpp +++ b/ApplicationLibCode/ProjectDataModel/ContourMap/RimEclipseContourMapView.cpp @@ -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; +} + //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- diff --git a/ApplicationLibCode/ProjectDataModel/ContourMap/RimEclipseContourMapView.h b/ApplicationLibCode/ProjectDataModel/ContourMap/RimEclipseContourMapView.h index e26a1a0503..a272a06e33 100644 --- a/ApplicationLibCode/ProjectDataModel/ContourMap/RimEclipseContourMapView.h +++ b/ApplicationLibCode/ProjectDataModel/ContourMap/RimEclipseContourMapView.h @@ -46,6 +46,7 @@ public: RimSurfaceInViewCollection* surfaceInViewCollection() const override; void zoomAll() override; + bool isScaleZEditable() const override; void setCompatibleDrawStyle(); diff --git a/ApplicationLibCode/ProjectDataModel/Rim3dView.cpp b/ApplicationLibCode/ProjectDataModel/Rim3dView.cpp index da2ac6223a..3fd9685e61 100644 --- a/ApplicationLibCode/ProjectDataModel/Rim3dView.cpp +++ b/ApplicationLibCode/ProjectDataModel/Rim3dView.cpp @@ -73,6 +73,7 @@ #include "cvfTransform.h" #include "cvfViewport.h" +#include #include namespace caf @@ -577,7 +578,7 @@ void Rim3dView::scheduleCreateDisplayModelAndRedraw() //-------------------------------------------------------------------------------------------------- /// //-------------------------------------------------------------------------------------------------- -std::set Rim3dView::viewsUsingThisAsComparisonView() +std::set Rim3dView::viewsUsingThisAsComparisonView() const { std::set containingViews; std::vector fieldsReferringToMe = referringPtrFields(); @@ -627,7 +628,7 @@ std::vector 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 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( attribute ) ) + { + attr->enableEditableContent = true; + attr->enableAutoComplete = false; + } + } +} diff --git a/ApplicationLibCode/ProjectDataModel/Rim3dView.h b/ApplicationLibCode/ProjectDataModel/Rim3dView.h index 429efe9008..4ddee74370 100644 --- a/ApplicationLibCode/ProjectDataModel/Rim3dView.h +++ b/ApplicationLibCode/ProjectDataModel/Rim3dView.h @@ -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 viewsUsingThisAsComparisonView(); + std::set viewsUsingThisAsComparisonView() const; void updateWindowTitle() override; std::vector validComparisonViews() const; @@ -273,6 +273,7 @@ protected: QList 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; diff --git a/ApplicationLibCode/ProjectDataModel/RimEclipseView.cpp b/ApplicationLibCode/ProjectDataModel/RimEclipseView.cpp index 7f118f2c29..149b73d64d 100644 --- a/ApplicationLibCode/ProjectDataModel/RimEclipseView.cpp +++ b/ApplicationLibCode/ProjectDataModel/RimEclipseView.cpp @@ -2206,6 +2206,8 @@ QList 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 ); diff --git a/ApplicationLibCode/UserInterface/RiuMainWindow.cpp b/ApplicationLibCode/UserInterface/RiuMainWindow.cpp index 336c271e18..f444fc7354 100644 --- a/ApplicationLibCode/UserInterface/RiuMainWindow.cpp +++ b/ApplicationLibCode/UserInterface/RiuMainWindow.cpp @@ -100,8 +100,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -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( view ) == nullptr; - if ( isRegularReservoirView && view->isScaleZEditable() ) + Rim3dView* view = RiaApplication::instance()->activeReservoirView(); + if ( view && view->isScaleZEditable() ) { m_scaleFactor->setEnabled( true ); m_scaleFactor->blockSignals( true );