From dfb6b92974945b428df99e2371974a48bc593407 Mon Sep 17 00:00:00 2001 From: Eric Mehl Date: Wed, 24 Jul 2024 12:21:03 -0400 Subject: [PATCH 01/10] Slider : Fix extra entries in undo queue on drag --- Changes.md | 1 + python/GafferUI/Slider.py | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Changes.md b/Changes.md index b18f9629716..93b32b85a32 100644 --- a/Changes.md +++ b/Changes.md @@ -6,6 +6,7 @@ Fixes - ImageReader : Fixed crash caused by invalid OpenEXR `multiView` attributes. - LightEditor, RenderPassEditor : Added missing icon representing use of the `CreateIfMissing` tweak mode in the history window. +- Slider : Fixed bug where two undo steps were needed to get back to the original value when dragging. 1.3.16.6 (relative to 1.3.16.5) ======== diff --git a/python/GafferUI/Slider.py b/python/GafferUI/Slider.py index 3bb51d8e10b..e5708ecb1d3 100644 --- a/python/GafferUI/Slider.py +++ b/python/GafferUI/Slider.py @@ -429,6 +429,11 @@ def __buttonPress( self, widget, event ) : def __dragBegin( self, widget, event ) : if event.buttons == GafferUI.ButtonEvent.Buttons.Left and self.getSelectedIndex() is not None : + self.__setValueInternal( + self.getSelectedIndex(), + self.__eventValue( event ), + self.ValueChangedReason.DragBegin + ) return IECore.NullObject.defaultNullObject() return None @@ -450,7 +455,11 @@ def __dragMove( self, widget, event ) : def __dragEnd( self, widget, event ) : - self.__dragMove( widget, event ) + self.__setValueInternal( + self.getSelectedIndex(), + self.__eventValue( event ), + self.ValueChangedReason.DragEnd + ) def __keyPress( self, widget, event ) : From 8b3719017075e62fd46f82c540577eedde8beadb Mon Sep 17 00:00:00 2001 From: Murray Stevenson <50844517+murraystevenson@users.noreply.github.com> Date: Fri, 19 Jul 2024 17:41:49 -0700 Subject: [PATCH 02/10] AttributeInspector : Use global attributes and metadata in fallbackValue When no inherited attribute exists, fall back to the equivalent attribute in the scene globals, with "defaultValue" metadata used to provide the default value of non-existent attributes - equivalent to how we handle default values of non-existent options in the OptionInspector. --- Changes.md | 4 +++- .../AttributeInspectorTest.py | 17 ++++++++++++++-- src/GafferSceneUI/AttributeInspector.cpp | 20 +++++++++++++++++-- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/Changes.md b/Changes.md index 8d3c0faae9c..73b80ff0ab7 100644 --- a/Changes.md +++ b/Changes.md @@ -4,7 +4,9 @@ Improvements ------------ -- LightEditor : Values of inherited attributes are now displayed in the Light Editor. These are presented as dimmed "fallback" values. +- LightEditor : + - Values of inherited attributes are now displayed in the Light Editor. These are presented as dimmed "fallback" values. Values are inherited from an ancestor of the inspected location or from attributes created in the scene globals. + - Default values are now displayed as dimmed "fallback" values for attributes that don't exist in the scene. - LightEditor, RenderPassEditor : Fallback values shown in the history window are displayed with the same dimmed text colour used for fallback values in editor columns. - EditScope : Filtered the EditScope menu to show only nodes that are active in the relevant context. diff --git a/python/GafferSceneUITest/AttributeInspectorTest.py b/python/GafferSceneUITest/AttributeInspectorTest.py index a76e3fe255d..9bb652b4e55 100644 --- a/python/GafferSceneUITest/AttributeInspectorTest.py +++ b/python/GafferSceneUITest/AttributeInspectorTest.py @@ -122,17 +122,30 @@ def testFallbackValue( self ) : group = GafferScene.Group() group["in"][0].setInput( light["out"] ) + globalGlAttributes = GafferScene.OpenGLAttributes() + globalGlAttributes["in"].setInput( group["out"] ) + globalGlAttributes["global"].setValue( True ) + globalGlAttributes["attributes"]["visualiserScale"]["enabled"].setValue( True ) + globalGlAttributes["attributes"]["visualiserScale"]["value"].setValue( 4.0 ) + + # With no "gl:visualiser:scale" attribute at /group/light, the inspection returns + # the inherited global attribute value with `sourceType` identifying it as a fallback. + + inspection = self.__inspect( globalGlAttributes["out"], "/group/light", "gl:visualiser:scale" ) + self.assertEqual( inspection.value(), IECore.FloatData( 4.0 ) ) + self.assertEqual( inspection.sourceType(), GafferSceneUI.Private.Inspector.Result.SourceType.Fallback ) + groupFilter = GafferScene.PathFilter() groupFilter["paths"].setValue( IECore.StringVectorData( [ "/group" ] ) ) glAttributes = GafferScene.OpenGLAttributes() - glAttributes["in"].setInput( group["out"] ) + glAttributes["in"].setInput( globalGlAttributes["out"] ) glAttributes["filter"].setInput( groupFilter["out"] ) glAttributes["attributes"]["visualiserScale"]["enabled"].setValue( True ) glAttributes["attributes"]["visualiserScale"]["value"].setValue( 2.0 ) # With no "gl:visualiser:scale" attribute at /group/light, the inspection returns - # the inherited attribute value with `sourceType` identifying it as a fallback. + # the inherited attribute value from /group with `sourceType` identifying it as a fallback. inspection = self.__inspect( glAttributes["out"], "/group/light", "gl:visualiser:scale" ) self.assertEqual( inspection.value(), IECore.FloatData( 2.0 ) ) diff --git a/src/GafferSceneUI/AttributeInspector.cpp b/src/GafferSceneUI/AttributeInspector.cpp index 34b2e18fc40..22bc04ffdb7 100644 --- a/src/GafferSceneUI/AttributeInspector.cpp +++ b/src/GafferSceneUI/AttributeInspector.cpp @@ -69,6 +69,9 @@ using namespace GafferSceneUI::Private; namespace { +const std::string g_attributePrefix( "attribute:" ); +const InternedString g_defaultValue( "defaultValue" ); + // This uses the same strategy that ValuePlug uses for the hash cache, // using `plug->dirtyCount()` to invalidate previous cache entries when // a plug is dirtied. @@ -247,7 +250,20 @@ IECore::ConstObjectPtr AttributeInspector::value( const GafferScene::SceneAlgo:: IECore::ConstObjectPtr AttributeInspector::fallbackValue( const GafferScene::SceneAlgo::History *history ) const { - return history->scene->fullAttributes( history->context->get( ScenePlug::scenePathContextName ) )->member( m_attribute ); + if( const auto inheritedAttribute = history->scene->fullAttributes( history->context->get( ScenePlug::scenePathContextName ) )->member( m_attribute ) ) + { + return inheritedAttribute; + } + else if( const auto globalAttribute = history->scene->globals()->member( g_attributePrefix + m_attribute.string() ) ) + { + return globalAttribute; + } + else if( const auto defaultValue = Gaffer::Metadata::value( g_attributePrefix + m_attribute.string(), g_defaultValue ) ) + { + return defaultValue; + } + + return nullptr; } Gaffer::ValuePlugPtr AttributeInspector::source( const GafferScene::SceneAlgo::History *history, std::string &editWarning ) const @@ -369,7 +385,7 @@ Inspector::EditFunctionOrFailure AttributeInspector::editFunction( Gaffer::EditS void AttributeInspector::plugDirtied( Gaffer::Plug *plug ) { - if( plug == m_scene->attributesPlug() ) + if( plug == m_scene->attributesPlug() || plug == m_scene->globalsPlug() ) { dirtiedSignal()( this ); } From 96ee27ff89546e2f16bd54c5cce61ab523e6c49f Mon Sep 17 00:00:00 2001 From: Murray Stevenson <50844517+murraystevenson@users.noreply.github.com> Date: Fri, 19 Jul 2024 16:57:25 -0700 Subject: [PATCH 03/10] GafferScene : Register openGLAttributes This is not a complete registration of the attributes creatable by OpenGLAttributes, but the subset of attributes registered by default in the Light Editor. --- .../AttributeInspectorTest.py | 14 ++- python/GafferSceneUITest/HistoryPathTest.py | 2 +- startup/GafferScene/openGLAttributes.py | 91 +++++++++++++++++++ 3 files changed, 103 insertions(+), 4 deletions(-) create mode 100644 startup/GafferScene/openGLAttributes.py diff --git a/python/GafferSceneUITest/AttributeInspectorTest.py b/python/GafferSceneUITest/AttributeInspectorTest.py index 9bb652b4e55..58a88e86a1e 100644 --- a/python/GafferSceneUITest/AttributeInspectorTest.py +++ b/python/GafferSceneUITest/AttributeInspectorTest.py @@ -122,6 +122,14 @@ def testFallbackValue( self ) : group = GafferScene.Group() group["in"][0].setInput( light["out"] ) + # With no "gl:visualiser:scale" attribute at /group/light, the inspection returns + # the registered default value with `sourceType` identifying it as a fallback. + + inspection = self.__inspect( group["out"], "/group/light", "gl:visualiser:scale" ) + self.assertEqual( inspection.value().value, Gaffer.Metadata.value( "attribute:gl:visualiser:scale", "defaultValue" ) ) + self.assertEqual( inspection.sourceType(), GafferSceneUI.Private.Inspector.Result.SourceType.Fallback ) + self.assertEqual( inspection.fallbackDescription(), "Default value" ) + globalGlAttributes = GafferScene.OpenGLAttributes() globalGlAttributes["in"].setInput( group["out"] ) globalGlAttributes["global"].setValue( True ) @@ -699,7 +707,7 @@ def testDisabledAttribute( self ) : self.__assertExpectedResult( self.__inspect( light["out"], "/light", "gl:visualiser:scale", None ), source = light["visualiserAttributes"]["scale"], - sourceType = GafferSceneUI.Private.Inspector.Result.SourceType.Other, + sourceType = GafferSceneUI.Private.Inspector.Result.SourceType.Fallback, editable = True, edit = light["visualiserAttributes"]["scale"] ) @@ -708,7 +716,7 @@ def testDisabledAttribute( self ) : self.__assertExpectedResult( self.__inspect( group["out"], "/group/light", "gl:visualiser:scale", None ), source = light["visualiserAttributes"]["scale"], - sourceType = GafferSceneUI.Private.Inspector.Result.SourceType.Other, + sourceType = GafferSceneUI.Private.Inspector.Result.SourceType.Fallback, editable = True, edit = light["visualiserAttributes"]["scale"] ) @@ -724,7 +732,7 @@ def testRegisteredAttribute( self ) : self.__assertExpectedResult( self.__inspect( editScope["out"], "/light", "gl:visualiser:scale", None ), source = light["visualiserAttributes"]["scale"], - sourceType = GafferSceneUI.Private.Inspector.Result.SourceType.Other, + sourceType = GafferSceneUI.Private.Inspector.Result.SourceType.Fallback, editable = True, edit = light["visualiserAttributes"]["scale"] ) diff --git a/python/GafferSceneUITest/HistoryPathTest.py b/python/GafferSceneUITest/HistoryPathTest.py index e7a55d1de1b..faa303ee00e 100644 --- a/python/GafferSceneUITest/HistoryPathTest.py +++ b/python/GafferSceneUITest/HistoryPathTest.py @@ -416,7 +416,7 @@ def testAttributeFallbackValues( self ) : self.assertEqual( c[0].property( "name" ), str( c[0][-1] ) ) self.assertEqual( c[0].property( "history:node" ), s["testLight"] ) self.assertEqual( c[0].property( "history:value" ), None ) - self.assertEqual( c[0].property( "history:fallbackValue" ), None ) + self.assertEqual( c[0].property( "history:fallbackValue" ), 512 ) self.assertEqual( c[0].property( "history:operation" ), Gaffer.TweakPlug.Mode.Create ) self.assertEqual( c[0].property( "history:source" ), s["testLight"]["visualiserAttributes"]["maxTextureResolution"] ) self.assertEqual( c[0].property( "history:editWarning" ), "" ) diff --git a/startup/GafferScene/openGLAttributes.py b/startup/GafferScene/openGLAttributes.py new file mode 100644 index 00000000000..1ed2b450fb6 --- /dev/null +++ b/startup/GafferScene/openGLAttributes.py @@ -0,0 +1,91 @@ +########################################################################## +# +# Copyright (c) 2024, Cinesite VFX Ltd. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above +# copyright notice, this list of conditions and the following +# disclaimer. +# +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with +# the distribution. +# +# * Neither the name of John Haddon nor the names of +# any other contributors to this software may be used to endorse or +# promote products derived from this software without specific prior +# written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +########################################################################## + +import IECore +import Gaffer + +Gaffer.Metadata.registerValue( "attribute:gl:light:drawingMode", "label", "Light Drawing Mode" ) +Gaffer.Metadata.registerValue( "attribute:gl:light:drawingMode", "defaultValue", IECore.StringData( "texture" ) ) +Gaffer.Metadata.registerValue( + "attribute:gl:light:drawingMode", + "description", + """ + Controls how lights are presented in the Viewer. + """ +) + +Gaffer.Metadata.registerValue( "attribute:gl:light:frustumScale", "label", "Light Frustum Scale" ) +Gaffer.Metadata.registerValue( "attribute:gl:light:frustumScale", "defaultValue", IECore.FloatData( 1.0 ) ) +Gaffer.Metadata.registerValue( + "attribute:gl:light:frustumScale", + "description", + """ + Allows light projections to be scaled to better suit the scene. + """ +) + +Gaffer.Metadata.registerValue( "attribute:gl:visualiser:frustum", "label", "Frustum" ) +Gaffer.Metadata.registerValue( "attribute:gl:visualiser:frustum", "defaultValue", IECore.StringData( "whenSelected" ) ) +Gaffer.Metadata.registerValue( + "attribute:gl:visualiser:frustum", + "description", + """ + Controls whether applicable locations draw a representation of + their projection or frustum. + """ +) + +Gaffer.Metadata.registerValue( "attribute:gl:visualiser:maxTextureResolution", "label", "Max Texture Resolution" ) +Gaffer.Metadata.registerValue( "attribute:gl:visualiser:maxTextureResolution", "defaultValue", IECore.IntData( 512 ) ) +Gaffer.Metadata.registerValue( + "attribute:gl:visualiser:maxTextureResolution", + "description", + """ + Visualisers that load textures will respect this setting to + limit their resolution. + """ +) + +Gaffer.Metadata.registerValue( "attribute:gl:visualiser:scale", "label", "Scale" ) +Gaffer.Metadata.registerValue( "attribute:gl:visualiser:scale", "defaultValue", IECore.FloatData( 1.0 ) ) +Gaffer.Metadata.registerValue( + "attribute:gl:visualiser:scale", + "description", + """ + Scales non-geometric visualisations in the viewport to make them + easier to work with. + """ +) From d1c5ac3fe3f75486bd0b65ba47bfca7bb04f3009 Mon Sep 17 00:00:00 2001 From: Murray Stevenson <50844517+murraystevenson@users.noreply.github.com> Date: Fri, 19 Jul 2024 18:43:43 -0700 Subject: [PATCH 04/10] Inspector : Add fallbackDescription As fallback values could originate from various sources, we provide an output `&description` parameter to `fallbackValue()` to provide a user-facing description of the source of the fallback. --- .../Private/AttributeInspector.h | 2 +- include/GafferSceneUI/Private/Inspector.h | 10 +++++-- .../GafferSceneUI/Private/OptionInspector.h | 2 +- .../Private/ParameterInspector.h | 2 +- .../Private/SetMembershipInspector.h | 2 +- .../AttributeInspectorTest.py | 2 ++ python/GafferSceneUITest/HistoryPathTest.py | 2 +- .../GafferSceneUITest/OptionInspectorTest.py | 3 ++ .../SetMembershipInspectorTest.py | 22 ++++++++++++-- src/GafferSceneUI/AttributeInspector.cpp | 30 ++++++++++++++++--- src/GafferSceneUI/Inspector.cpp | 14 +++++++-- src/GafferSceneUI/OptionInspector.cpp | 3 +- src/GafferSceneUI/ParameterInspector.cpp | 2 +- src/GafferSceneUI/SetMembershipInspector.cpp | 25 ++++++++++++++-- src/GafferSceneUIModule/InspectorBinding.cpp | 1 + 15 files changed, 102 insertions(+), 20 deletions(-) diff --git a/include/GafferSceneUI/Private/AttributeInspector.h b/include/GafferSceneUI/Private/AttributeInspector.h index 920cca95253..394982de0da 100644 --- a/include/GafferSceneUI/Private/AttributeInspector.h +++ b/include/GafferSceneUI/Private/AttributeInspector.h @@ -67,7 +67,7 @@ class GAFFERSCENEUI_API AttributeInspector : public Inspector IECore::ConstObjectPtr value( const GafferScene::SceneAlgo::History *history) const override; Gaffer::ValuePlugPtr source( const GafferScene::SceneAlgo::History *history, std::string &editWarning ) const override; EditFunctionOrFailure editFunction( Gaffer::EditScope *scope, const GafferScene::SceneAlgo::History *history) const override; - IECore::ConstObjectPtr fallbackValue( const GafferScene::SceneAlgo::History *history ) const override; + IECore::ConstObjectPtr fallbackValue( const GafferScene::SceneAlgo::History *history, std::string &description ) const override; bool attributeExists() const; diff --git a/include/GafferSceneUI/Private/Inspector.h b/include/GafferSceneUI/Private/Inspector.h index 372e3186d7e..7b80e709119 100644 --- a/include/GafferSceneUI/Private/Inspector.h +++ b/include/GafferSceneUI/Private/Inspector.h @@ -178,8 +178,10 @@ class GAFFERSCENEUI_API Inspector : public IECore::RefCounted, public Gaffer::Si virtual EditFunctionOrFailure editFunction( Gaffer::EditScope *editScope, const GafferScene::SceneAlgo::History *history ) const; /// Can be implemented by derived classes to provide a fallback value for the inspection, - /// used when no value is returned from `value()`. - virtual IECore::ConstObjectPtr fallbackValue( const GafferScene::SceneAlgo::History *history ) const; + /// used when no value is returned from `value()`. Optionally, `description` may be + /// assigned a description to be shown to the user. Typically, this description would + /// be used to disambiguate the source of the fallback value. + virtual IECore::ConstObjectPtr fallbackValue( const GafferScene::SceneAlgo::History *history, std::string &description ) const; protected : @@ -319,6 +321,9 @@ class GAFFERSCENEUI_API Inspector::Result : public IECore::RefCounted /// The relationship between `source()` and `editScope()`. SourceType sourceType() const; + /// Returns a user-facing description of the source of the + /// fallback value when `SourceType` is `Fallback`. + const std::string &fallbackDescription() const; /// Editing /// ======= @@ -347,6 +352,7 @@ class GAFFERSCENEUI_API Inspector::Result : public IECore::RefCounted const IECore::ConstObjectPtr m_value; Gaffer::ValuePlugPtr m_source; SourceType m_sourceType; + std::string m_fallbackDescription; Gaffer::EditScopePtr m_editScope; bool m_editScopeInHistory; diff --git a/include/GafferSceneUI/Private/OptionInspector.h b/include/GafferSceneUI/Private/OptionInspector.h index 617ee73e803..4fd1dd79576 100644 --- a/include/GafferSceneUI/Private/OptionInspector.h +++ b/include/GafferSceneUI/Private/OptionInspector.h @@ -65,7 +65,7 @@ class GAFFERSCENEUI_API OptionInspector : public Inspector IECore::ConstObjectPtr value( const GafferScene::SceneAlgo::History *history ) const override; Gaffer::ValuePlugPtr source( const GafferScene::SceneAlgo::History *history, std::string &editWarning ) const override; EditFunctionOrFailure editFunction( Gaffer::EditScope *scope, const GafferScene::SceneAlgo::History *history ) const override; - IECore::ConstObjectPtr fallbackValue( const GafferScene::SceneAlgo::History *history ) const override; + IECore::ConstObjectPtr fallbackValue( const GafferScene::SceneAlgo::History *history, std::string &description ) const override; private : diff --git a/include/GafferSceneUI/Private/ParameterInspector.h b/include/GafferSceneUI/Private/ParameterInspector.h index 97ac7e992d3..0ba4f86762b 100644 --- a/include/GafferSceneUI/Private/ParameterInspector.h +++ b/include/GafferSceneUI/Private/ParameterInspector.h @@ -69,7 +69,7 @@ class GAFFERSCENEUI_API ParameterInspector : public AttributeInspector IECore::ConstObjectPtr value( const GafferScene::SceneAlgo::History *history ) const override; Gaffer::ValuePlugPtr source( const GafferScene::SceneAlgo::History *history, std::string &editWarning ) const override; EditFunctionOrFailure editFunction( Gaffer::EditScope *editScope, const GafferScene::SceneAlgo::History *history ) const override; - IECore::ConstObjectPtr fallbackValue( const GafferScene::SceneAlgo::History *history ) const override; + IECore::ConstObjectPtr fallbackValue( const GafferScene::SceneAlgo::History *history, std::string &description ) const override; const IECoreScene::ShaderNetwork::Parameter m_parameter; diff --git a/include/GafferSceneUI/Private/SetMembershipInspector.h b/include/GafferSceneUI/Private/SetMembershipInspector.h index 3289b05c673..51622c6db93 100644 --- a/include/GafferSceneUI/Private/SetMembershipInspector.h +++ b/include/GafferSceneUI/Private/SetMembershipInspector.h @@ -81,7 +81,7 @@ class GAFFERSCENEUI_API SetMembershipInspector : public Inspector /// those are found. Gaffer::ValuePlugPtr source( const GafferScene::SceneAlgo::History *history, std::string &editWarning ) const override; EditFunctionOrFailure editFunction( Gaffer::EditScope *scope, const GafferScene::SceneAlgo::History *history ) const override; - IECore::ConstObjectPtr fallbackValue( const GafferScene::SceneAlgo::History *history ) const override; + IECore::ConstObjectPtr fallbackValue( const GafferScene::SceneAlgo::History *history, std::string &description ) const override; private : diff --git a/python/GafferSceneUITest/AttributeInspectorTest.py b/python/GafferSceneUITest/AttributeInspectorTest.py index 58a88e86a1e..b784cd5e659 100644 --- a/python/GafferSceneUITest/AttributeInspectorTest.py +++ b/python/GafferSceneUITest/AttributeInspectorTest.py @@ -142,6 +142,7 @@ def testFallbackValue( self ) : inspection = self.__inspect( globalGlAttributes["out"], "/group/light", "gl:visualiser:scale" ) self.assertEqual( inspection.value(), IECore.FloatData( 4.0 ) ) self.assertEqual( inspection.sourceType(), GafferSceneUI.Private.Inspector.Result.SourceType.Fallback ) + self.assertEqual( inspection.fallbackDescription(), "Global attribute" ) groupFilter = GafferScene.PathFilter() groupFilter["paths"].setValue( IECore.StringVectorData( [ "/group" ] ) ) @@ -158,6 +159,7 @@ def testFallbackValue( self ) : inspection = self.__inspect( glAttributes["out"], "/group/light", "gl:visualiser:scale" ) self.assertEqual( inspection.value(), IECore.FloatData( 2.0 ) ) self.assertEqual( inspection.sourceType(), GafferSceneUI.Private.Inspector.Result.SourceType.Fallback ) + self.assertEqual( inspection.fallbackDescription(), "Inherited from /group" ) # With a "gl:visualiser:scale" attribute created at the inspected location, it is # returned instead of the inherited fallback. diff --git a/python/GafferSceneUITest/HistoryPathTest.py b/python/GafferSceneUITest/HistoryPathTest.py index faa303ee00e..775426d2085 100644 --- a/python/GafferSceneUITest/HistoryPathTest.py +++ b/python/GafferSceneUITest/HistoryPathTest.py @@ -400,7 +400,7 @@ def testAttributeFallbackValues( self ) : self.assertEqual( c[0].property( "name" ), str( c[0][-1] ) ) self.assertEqual( c[0].property( "history:node" ), s["openGLAttributes"] ) self.assertEqual( c[0].property( "history:value" ), 1536 ) - self.assertEqual( c[0].property( "history:fallbackValue" ), 1536 ) + self.assertEqual( c[0].property( "history:fallbackValue" ), 1024 ) self.assertEqual( c[0].property( "history:operation" ), Gaffer.TweakPlug.Mode.Create ) self.assertEqual( c[0].property( "history:source" ), s["openGLAttributes"]["attributes"]["visualiserMaxTextureResolution"] ) self.assertEqual( c[0].property( "history:editWarning" ), "Edits to \"gl:visualiser:maxTextureResolution\" may affect other locations in the scene." ) diff --git a/python/GafferSceneUITest/OptionInspectorTest.py b/python/GafferSceneUITest/OptionInspectorTest.py index 5131a99ee32..3735046cfcd 100644 --- a/python/GafferSceneUITest/OptionInspectorTest.py +++ b/python/GafferSceneUITest/OptionInspectorTest.py @@ -844,6 +844,7 @@ def testDefaultValueMetadata( self ) : inspection = self.__inspect( editScope["out"], "test:enabled", editScope ) self.assertEqual( inspection.value(), IECore.BoolData( 1 ) ) self.assertEqual( inspection.sourceType(), GafferSceneUI.Private.Inspector.Result.SourceType.Fallback ) + self.assertEqual( inspection.fallbackDescription(), "Default value" ) # If the option does exist, then its value is returned as normal @@ -852,6 +853,7 @@ def testDefaultValueMetadata( self ) : inspection = self.__inspect( editScope["out"], "test:enabled", editScope ) self.assertEqual( inspection.value(), IECore.BoolData( 0 ) ) self.assertEqual( inspection.sourceType(), GafferSceneUI.Private.Inspector.Result.SourceType.Upstream ) + self.assertEqual( inspection.fallbackDescription(), "" ) # Disabling the option should revert to the fallback @@ -859,6 +861,7 @@ def testDefaultValueMetadata( self ) : inspection = self.__inspect( editScope["out"], "test:enabled", editScope ) self.assertEqual( inspection.value(), IECore.BoolData( 1 ) ) self.assertEqual( inspection.sourceType(), GafferSceneUI.Private.Inspector.Result.SourceType.Fallback ) + self.assertEqual( inspection.fallbackDescription(), "Default value" ) # Updates to "defaultValue" are reflected in new inspections diff --git a/python/GafferSceneUITest/SetMembershipInspectorTest.py b/python/GafferSceneUITest/SetMembershipInspectorTest.py index 4ff50b6ec23..9f4f3070afa 100644 --- a/python/GafferSceneUITest/SetMembershipInspectorTest.py +++ b/python/GafferSceneUITest/SetMembershipInspectorTest.py @@ -117,15 +117,33 @@ def testFallbackValue( self ) : plane = GafferScene.Plane() group = GafferScene.Group() - group["sets"].setValue( "planeSet" ) group["in"][0].setInput( plane["out"] ) - inspection = self.__inspect( group["out"], "/group/plane", "planeSet" ) + pathFilter = GafferScene.PathFilter() + pathFilter["paths"].setValue( IECore.StringVectorData( [ "/" ] ) ) + + setNode = GafferScene.Set() + setNode["in"].setInput( group["out"] ) + setNode["name"].setValue( "planeSet" ) + setNode["filter"].setInput( pathFilter["out"] ) + + inspection = self.__inspect( setNode["out"], "/group/plane", "planeSet" ) + self.assertEqual( inspection.value().value, True ) + self.assertEqual( + inspection.sourceType(), + GafferSceneUI.Private.Inspector.Result.SourceType.Fallback + ) + self.assertEqual( inspection.fallbackDescription(), "Inherited from /" ) + + pathFilter["paths"].setValue( IECore.StringVectorData( [ "/", "/group" ] ) ) + + inspection = self.__inspect( setNode["out"], "/group/plane", "planeSet" ) self.assertEqual( inspection.value().value, True ) self.assertEqual( inspection.sourceType(), GafferSceneUI.Private.Inspector.Result.SourceType.Fallback ) + self.assertEqual( inspection.fallbackDescription(), "Inherited from /group" ) def testSourceAndEdits( self ) : diff --git a/src/GafferSceneUI/AttributeInspector.cpp b/src/GafferSceneUI/AttributeInspector.cpp index 22bc04ffdb7..eb745ebfd54 100644 --- a/src/GafferSceneUI/AttributeInspector.cpp +++ b/src/GafferSceneUI/AttributeInspector.cpp @@ -248,18 +248,40 @@ IECore::ConstObjectPtr AttributeInspector::value( const GafferScene::SceneAlgo:: return nullptr; } -IECore::ConstObjectPtr AttributeInspector::fallbackValue( const GafferScene::SceneAlgo::History *history ) const +IECore::ConstObjectPtr AttributeInspector::fallbackValue( const GafferScene::SceneAlgo::History *history, std::string &description ) const { - if( const auto inheritedAttribute = history->scene->fullAttributes( history->context->get( ScenePlug::scenePathContextName ) )->member( m_attribute ) ) + ScenePlug::PathScope pathScope( Context::current() ); + ScenePlug::ScenePath currentPath( history->context->get( ScenePlug::scenePathContextName ) ); + + // No need to check inheritance for immediate children of `/` as we + // don't allow attributes to be created at the root of the scene. + if( currentPath.size() > 1 ) { - return inheritedAttribute; + // We start the inheritance search from the parent in order to return the value that + // would be inherited if the inspected attribute did not exist at the original location. + currentPath.pop_back(); + + while( !currentPath.empty() ) + { + pathScope.setPath( ¤tPath ); + auto a = history->scene->attributesPlug()->getValue(); + if( const auto attribute = a->member( m_attribute ) ) + { + description = "Inherited from " + ScenePlug::pathToString( currentPath ); + return attribute; + } + currentPath.pop_back(); + } } - else if( const auto globalAttribute = history->scene->globals()->member( g_attributePrefix + m_attribute.string() ) ) + + if( const auto globalAttribute = history->scene->globals()->member( g_attributePrefix + m_attribute.string() ) ) { + description = "Global attribute"; return globalAttribute; } else if( const auto defaultValue = Gaffer::Metadata::value( g_attributePrefix + m_attribute.string(), g_defaultValue ) ) { + description = "Default value"; return defaultValue; } diff --git a/src/GafferSceneUI/Inspector.cpp b/src/GafferSceneUI/Inspector.cpp index 1ba1531e28f..6cec443f42c 100644 --- a/src/GafferSceneUI/Inspector.cpp +++ b/src/GafferSceneUI/Inspector.cpp @@ -192,9 +192,10 @@ Inspector::ResultPtr Inspector::inspect() const ConstObjectPtr value = this->value( history.get() ); bool fallbackValue = false; + std::string fallbackDescription; if( !value ) { - value = this->fallbackValue( history.get() ); + value = this->fallbackValue( history.get(), fallbackDescription ); fallbackValue = (bool)value; } @@ -227,6 +228,7 @@ Inspector::ResultPtr Inspector::inspect() const if( fallbackValue ) { result->m_sourceType = Result::SourceType::Fallback; + result->m_fallbackDescription = fallbackDescription.empty() ? "Fallback value" : fallbackDescription; } return result; @@ -366,7 +368,7 @@ Inspector::EditFunctionOrFailure Inspector::editFunction( Gaffer::EditScope *edi return "Editing not supported"; } -IECore::ConstObjectPtr Inspector::fallbackValue( const GafferScene::SceneAlgo::History *history ) const +IECore::ConstObjectPtr Inspector::fallbackValue( const GafferScene::SceneAlgo::History *history, std::string &description ) const { return nullptr; } @@ -478,6 +480,7 @@ ConstRunTimeTypedPtr Inspector::HistoryPath::property( const InternedString &nam PlugMap::iterator it = m_plugMap.find( names()[0].string() ); std::string editWarning; + std::string fallbackDescription; Context::Scope currentScope( it->history->context.get() ); @@ -491,7 +494,7 @@ ConstRunTimeTypedPtr Inspector::HistoryPath::property( const InternedString &nam } else if( name == g_fallbackValuePropertyName ) { - return runTimeCast( m_inspector->fallbackValue( it->history.get() ) ); + return runTimeCast( m_inspector->fallbackValue( it->history.get(), fallbackDescription ) ); } else if( name == g_operationPropertyName ) { @@ -643,6 +646,11 @@ Inspector::Result::SourceType Inspector::Result::sourceType() const return m_sourceType; } +const std::string &Inspector::Result::fallbackDescription() const +{ + return m_fallbackDescription; +} + bool Inspector::Result::editable() const { return m_editFunction.which() == 0 && boost::get( m_editFunction ) != nullptr; diff --git a/src/GafferSceneUI/OptionInspector.cpp b/src/GafferSceneUI/OptionInspector.cpp index 579c6cb55be..71e152f3626 100644 --- a/src/GafferSceneUI/OptionInspector.cpp +++ b/src/GafferSceneUI/OptionInspector.cpp @@ -215,10 +215,11 @@ IECore::ConstObjectPtr OptionInspector::value( const GafferScene::SceneAlgo::His return nullptr; } -IECore::ConstObjectPtr OptionInspector::fallbackValue( const GafferScene::SceneAlgo::History *history ) const +IECore::ConstObjectPtr OptionInspector::fallbackValue( const GafferScene::SceneAlgo::History *history, std::string &description ) const { if( const auto defaultValue = Gaffer::Metadata::value( g_optionPrefix + m_option.string(), g_defaultValue ) ) { + description = "Default value"; return defaultValue; } diff --git a/src/GafferSceneUI/ParameterInspector.cpp b/src/GafferSceneUI/ParameterInspector.cpp index f35ae77d427..305e5b49aca 100644 --- a/src/GafferSceneUI/ParameterInspector.cpp +++ b/src/GafferSceneUI/ParameterInspector.cpp @@ -98,7 +98,7 @@ IECore::ConstObjectPtr ParameterInspector::value( const GafferScene::SceneAlgo:: return shader->parametersData()->member( m_parameter.name ); } -IECore::ConstObjectPtr ParameterInspector::fallbackValue( const GafferScene::SceneAlgo::History *history ) const +IECore::ConstObjectPtr ParameterInspector::fallbackValue( const GafferScene::SceneAlgo::History *history, std::string &description ) const { // No fallback values are provided for parameters. Implemented to override AttributeInspector::fallbackValue(). return nullptr; diff --git a/src/GafferSceneUI/SetMembershipInspector.cpp b/src/GafferSceneUI/SetMembershipInspector.cpp index 3116c95b931..bbf5be06165 100644 --- a/src/GafferSceneUI/SetMembershipInspector.cpp +++ b/src/GafferSceneUI/SetMembershipInspector.cpp @@ -216,14 +216,35 @@ IECore::ConstObjectPtr SetMembershipInspector::value( const GafferScene::SceneAl return matchResult & IECore::PathMatcher::Result::ExactMatch ? new BoolData( true ) : nullptr; } -IECore::ConstObjectPtr SetMembershipInspector::fallbackValue( const GafferScene::SceneAlgo::History *history ) const +IECore::ConstObjectPtr SetMembershipInspector::fallbackValue( const GafferScene::SceneAlgo::History *history, std::string &description ) const { const auto &path = history->context->get( ScenePlug::scenePathContextName ); ConstPathMatcherDataPtr setMembers = history->scene->set( m_setName ); auto matchResult = (PathMatcher::Result)setMembers->readable().match( path ); - return new BoolData( matchResult & IECore::PathMatcher::Result::AncestorMatch ); + const bool ancestorMatch = matchResult & IECore::PathMatcher::Result::AncestorMatch; + if( ancestorMatch && path.size() ) + { + ScenePlug::ScenePath currentPath = path; + do + { + // We start the inheritance search from the parent in order to return the value that + // would be inherited if the original location wasn't a member of the inspected set. + currentPath.pop_back(); + if( setMembers->readable().match( currentPath ) & PathMatcher::Result::ExactMatch ) + { + description = "Inherited from " + ScenePlug::pathToString( currentPath ); + break; + } + } while( !currentPath.empty() ); + } + else + { + description = "Default value"; + } + + return new BoolData( ancestorMatch ); } Gaffer::ValuePlugPtr SetMembershipInspector::source( const GafferScene::SceneAlgo::History *history, std::string &editWarning ) const diff --git a/src/GafferSceneUIModule/InspectorBinding.cpp b/src/GafferSceneUIModule/InspectorBinding.cpp index eb214ec031c..5bba05dd2ee 100644 --- a/src/GafferSceneUIModule/InspectorBinding.cpp +++ b/src/GafferSceneUIModule/InspectorBinding.cpp @@ -130,6 +130,7 @@ void GafferSceneUIModule::bindInspector() .def( "source", &Inspector::Result::source, return_value_policy() ) .def( "editScope", &Inspector::Result::editScope, return_value_policy() ) .def( "sourceType", &Inspector::Result::sourceType ) + .def( "fallbackDescription", &Inspector::Result::fallbackDescription, return_value_policy() ) .def( "editable", &Inspector::Result::editable ) .def( "nonEditableReason", &Inspector::Result::nonEditableReason ) .def( "acquireEdit", &acquireEditWrapper ) From 470981cd8baf71245f3bce9ef7a0889cb1234443 Mon Sep 17 00:00:00 2001 From: Murray Stevenson <50844517+murraystevenson@users.noreply.github.com> Date: Mon, 22 Jul 2024 13:04:22 -0700 Subject: [PATCH 05/10] RenderPassEditor : Use inspector fallbackDescription --- src/GafferSceneUIModule/RenderPassEditorBinding.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GafferSceneUIModule/RenderPassEditorBinding.cpp b/src/GafferSceneUIModule/RenderPassEditorBinding.cpp index 4f4c1333ec0..1f4c096d215 100644 --- a/src/GafferSceneUIModule/RenderPassEditorBinding.cpp +++ b/src/GafferSceneUIModule/RenderPassEditorBinding.cpp @@ -619,7 +619,7 @@ class OptionInspectorColumn : public PathColumn std::string toolTip; if( inspectorResult->sourceType() == Inspector::Result::SourceType::Fallback ) { - toolTip = "Source : Default value"; + toolTip = "Source : " + inspectorResult->fallbackDescription(); result.foreground = g_fallbackValueForegroundColor; } else if( const auto source = inspectorResult->source() ) From f1c47b6e062ad5d884023de71e25884bd897e9be Mon Sep 17 00:00:00 2001 From: Murray Stevenson <50844517+murraystevenson@users.noreply.github.com> Date: Fri, 19 Jul 2024 16:56:13 -0700 Subject: [PATCH 06/10] LightEditor : Use inspector fallbackDescription We can now remove the inheritance lookup from MuteColumn and SetMembership column as the inspectors now provide this information via the fallbackDescription. --- Changes.md | 1 + .../LightEditorBinding.cpp | 29 +------------------ 2 files changed, 2 insertions(+), 28 deletions(-) diff --git a/Changes.md b/Changes.md index 73b80ff0ab7..21c2a2a7099 100644 --- a/Changes.md +++ b/Changes.md @@ -7,6 +7,7 @@ Improvements - LightEditor : - Values of inherited attributes are now displayed in the Light Editor. These are presented as dimmed "fallback" values. Values are inherited from an ancestor of the inspected location or from attributes created in the scene globals. - Default values are now displayed as dimmed "fallback" values for attributes that don't exist in the scene. + - When a fallback value is displayed, the cell's tooltip includes a description of the source of the value. - LightEditor, RenderPassEditor : Fallback values shown in the history window are displayed with the same dimmed text colour used for fallback values in editor columns. - EditScope : Filtered the EditScope menu to show only nodes that are active in the relevant context. diff --git a/src/GafferSceneUIModule/LightEditorBinding.cpp b/src/GafferSceneUIModule/LightEditorBinding.cpp index fcb077c8044..667dc4864c8 100644 --- a/src/GafferSceneUIModule/LightEditorBinding.cpp +++ b/src/GafferSceneUIModule/LightEditorBinding.cpp @@ -224,7 +224,7 @@ class InspectorColumn : public PathColumn std::string toolTip; if( inspectorResult->sourceType() == Inspector::Result::SourceType::Fallback ) { - toolTip = "Source : Fallback value"; + toolTip = "Source : " + inspectorResult->fallbackDescription(); result.foreground = g_fallbackValueForegroundColor; } else if( auto source = inspectorResult->source() ) @@ -311,19 +311,6 @@ class MuteColumn : public InspectorColumn else { result.icon = value->readable() ? m_muteFadedIconData : m_unMuteFadedIconData; - - ScenePlug::ScenePath currentPath( scenePath->names() ); - while( !currentPath.empty() ) - { - pathScope.setPath( ¤tPath ); - auto a = scenePath->getScene()->attributesPlug()->getValue(); - if( a->member( "light:mute" ) ) - { - result.toolTip = new StringData( "Inherited from : " + ScenePlug::pathToString( currentPath ) ); - break; - } - currentPath.pop_back(); - } } } if( !result.icon ) @@ -442,20 +429,6 @@ class SetMembershipColumn : public InspectorColumn else { result.icon = m_setMemberIconFadedData; - - ConstPathMatcherDataPtr setMembersData = scenePath->getScene()->set( m_setName ); - const PathMatcher &setMembers = setMembersData->readable(); - - ScenePlug::ScenePath currentPath( scenePath->names() ); - while( !currentPath.empty() ) - { - if( setMembers.match( currentPath ) & PathMatcher::Result::ExactMatch ) - { - result.toolTip = new StringData( "Inherited from : " + ScenePlug::pathToString( currentPath ) + "\n\nDouble-click to toggle" ); - break; - } - currentPath.pop_back(); - } } } } From 6861d55e69afa93bdcb31fbe17a40ef8a2ea2577 Mon Sep 17 00:00:00 2001 From: Murray Stevenson <50844517+murraystevenson@users.noreply.github.com> Date: Fri, 19 Jul 2024 16:57:15 -0700 Subject: [PATCH 07/10] GafferScene : Register standardAttributes This is not a complete registration of the attributes creatable by StandardAttributes, but the subset of attributes registered by default in the Light Editor. --- .../AttributeInspectorTest.py | 2 +- startup/GafferScene/standardAttributes.py | 61 +++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 startup/GafferScene/standardAttributes.py diff --git a/python/GafferSceneUITest/AttributeInspectorTest.py b/python/GafferSceneUITest/AttributeInspectorTest.py index b784cd5e659..63846f4dbd5 100644 --- a/python/GafferSceneUITest/AttributeInspectorTest.py +++ b/python/GafferSceneUITest/AttributeInspectorTest.py @@ -802,7 +802,7 @@ def testLightFilter( self ) : self.__assertExpectedResult( self.__inspect( editScope["out"], "/lightFilter", "filteredLights" ), source = lightFilter["filteredLights"], - sourceType = GafferSceneUI.Private.Inspector.Result.SourceType.Other, + sourceType = GafferSceneUI.Private.Inspector.Result.SourceType.Fallback, editable = True, edit = lightFilter["filteredLights"] ) diff --git a/startup/GafferScene/standardAttributes.py b/startup/GafferScene/standardAttributes.py new file mode 100644 index 00000000000..ab7797bc59c --- /dev/null +++ b/startup/GafferScene/standardAttributes.py @@ -0,0 +1,61 @@ +########################################################################## +# +# Copyright (c) 2024, Cinesite VFX Ltd. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above +# copyright notice, this list of conditions and the following +# disclaimer. +# +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided with +# the distribution. +# +# * Neither the name of John Haddon nor the names of +# any other contributors to this software may be used to endorse or +# promote products derived from this software without specific prior +# written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +########################################################################## + +import IECore +import Gaffer + +Gaffer.Metadata.registerValue( "attribute:light:mute", "label", "Mute" ) +Gaffer.Metadata.registerValue( "attribute:light:mute", "defaultValue", IECore.BoolData( False ) ) +Gaffer.Metadata.registerValue( + "attribute:light:mute", + "description", + """ + Whether this light is muted. + """ +) + +Gaffer.Metadata.registerValue( "attribute:filteredLights", "label", "Filtered Lights" ) +Gaffer.Metadata.registerValue( "attribute:filteredLights", "defaultValue", IECore.StringData( "" ) ) +Gaffer.Metadata.registerValue( + "attribute:filteredLights", + "description", + """ + The lights to be filtered by this light filter. Accepts a + set expression or a space separated list of lights. + Use \"defaultLights\" to refer to all lights that + contribute to illumination by default. + """ +) From 632bfc4791e1e4aa5d495cfc45b252b50efcf27d Mon Sep 17 00:00:00 2001 From: Murray Stevenson <50844517+murraystevenson@users.noreply.github.com> Date: Fri, 19 Jul 2024 16:56:46 -0700 Subject: [PATCH 08/10] LightEditor : Remove MuteColumn tooltip With `defaultValue` metadata registered for the `light:mute` attribute, InspectorColumn can now consistently include the toggle info in the tooltip so we no longer require this additional handling. --- src/GafferSceneUIModule/LightEditorBinding.cpp | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/GafferSceneUIModule/LightEditorBinding.cpp b/src/GafferSceneUIModule/LightEditorBinding.cpp index 667dc4864c8..1734a6dfe9a 100644 --- a/src/GafferSceneUIModule/LightEditorBinding.cpp +++ b/src/GafferSceneUIModule/LightEditorBinding.cpp @@ -329,22 +329,6 @@ class MuteColumn : public InspectorColumn } result.value = nullptr; - /// \todo Remove this once AttributeInspector can provide a default value when - /// no attribute exists, then InspectorColumn would always provide the toggle tooltip. - if( auto toolTipData = runTimeCast( result.toolTip ) ) - { - std::string toolTip = toolTipData->readable(); - size_t size = toolTip.size(); - if( size < 6 || toolTip.substr( size - 6 ) != "toggle" ) - { - toolTip += "\n\nDouble-click to toggle"; - result.toolTip = new StringData( toolTip ); - } - } - else - { - result.toolTip = new StringData( "Double-click to toggle" ); - } return result; } From de9ac4776993b1af92d9b54b03c9ab22f7c86a9f Mon Sep 17 00:00:00 2001 From: Murray Stevenson <50844517+murraystevenson@users.noreply.github.com> Date: Tue, 23 Jul 2024 10:24:53 -0700 Subject: [PATCH 09/10] Inspector : Reposition `fallbackValue` and improve documentation --- include/GafferSceneUI/Private/Inspector.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/GafferSceneUI/Private/Inspector.h b/include/GafferSceneUI/Private/Inspector.h index 7b80e709119..678e1c2e0b4 100644 --- a/include/GafferSceneUI/Private/Inspector.h +++ b/include/GafferSceneUI/Private/Inspector.h @@ -155,6 +155,12 @@ class GAFFERSCENEUI_API Inspector : public IECore::RefCounted, public Gaffer::Si /// base class? virtual IECore::ConstObjectPtr value( const GafferScene::SceneAlgo::History *history ) const = 0; + /// Can be implemented by derived classes to provide a fallback value for the inspection, + /// used when no value is returned from `value()`. Called with `history->context` as the current + /// context. Optionally, `description` may be assigned a description to be shown to the user. + /// Typically, this description would be used to disambiguate the source of the fallback value. + virtual IECore::ConstObjectPtr fallbackValue( const GafferScene::SceneAlgo::History *history, std::string &description ) const; + /// Should be implemented by derived classes to return the source for /// the value authored at this point in the history. Optionally, /// `editWarning` may be assigned a warning that will be shown to the @@ -177,12 +183,6 @@ class GAFFERSCENEUI_API Inspector : public IECore::RefCounted, public Gaffer::Si /// > that edits the processor itself. virtual EditFunctionOrFailure editFunction( Gaffer::EditScope *editScope, const GafferScene::SceneAlgo::History *history ) const; - /// Can be implemented by derived classes to provide a fallback value for the inspection, - /// used when no value is returned from `value()`. Optionally, `description` may be - /// assigned a description to be shown to the user. Typically, this description would - /// be used to disambiguate the source of the fallback value. - virtual IECore::ConstObjectPtr fallbackValue( const GafferScene::SceneAlgo::History *history, std::string &description ) const; - protected : Gaffer::EditScope *targetEditScope() const; From a5df1dd6bf763d6a05c1c32a87787c4b279ca9ac Mon Sep 17 00:00:00 2001 From: John Haddon Date: Thu, 25 Jul 2024 09:48:08 +0100 Subject: [PATCH 10/10] Bump version to 1.3.16.7 --- Changes.md | 7 ++++++- SConstruct | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Changes.md b/Changes.md index 93b32b85a32..c795c434086 100644 --- a/Changes.md +++ b/Changes.md @@ -1,4 +1,9 @@ -1.3.16.x (relative to 1.3.16.6) +1.3.16.x (relative to 1.3.16.7) +======== + + + +1.3.16.7 (relative to 1.3.16.6) ======== Fixes diff --git a/SConstruct b/SConstruct index 506e53a9db5..b9b976f564a 100644 --- a/SConstruct +++ b/SConstruct @@ -63,7 +63,7 @@ if codecs.lookup( locale.getpreferredencoding() ).name != "utf-8" : gafferMilestoneVersion = 1 # for announcing major milestones - may contain all of the below gafferMajorVersion = 3 # backwards-incompatible changes gafferMinorVersion = 16 # new backwards-compatible features -gafferPatchVersion = 6 # bug fixes +gafferPatchVersion = 7 # bug fixes gafferVersionSuffix = "" # used for alpha/beta releases : "a1", "b2", etc. # All of the following must be considered when determining