Skip to content

Commit

Permalink
Inspector : Add support for disabling edits from inspection results
Browse files Browse the repository at this point in the history
Derived inspectors can provide their own custom disable behaviour by implementing `disableEditFunction()`.
  • Loading branch information
murraystevenson committed Sep 6, 2024
1 parent a7480a8 commit 8ee485e
Show file tree
Hide file tree
Showing 10 changed files with 342 additions and 55 deletions.
17 changes: 17 additions & 0 deletions include/GafferSceneUI/Private/Inspector.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,14 @@ 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;

using DisableEditFunction = std::function<void ()>;
using DisableEditFunctionOrFailure = boost::variant<DisableEditFunction, std::string>;
/// Can be implemented to return a function that will disable an edit
/// at the specified plug. If this is not possible, should return an
/// error explaining why (this is typically due to `readOnly` metadata).
/// Called with `history->context` as the current context.
virtual DisableEditFunctionOrFailure disableEditFunction( Gaffer::ValuePlug *plug, const GafferScene::SceneAlgo::History *history ) const;

protected :

Gaffer::EditScope *targetEditScope() const;
Expand Down Expand Up @@ -343,6 +351,13 @@ class GAFFERSCENEUI_API Inspector::Result : public IECore::RefCounted
/// by `acquireEdit()`. This should be displayed to the user.
std::string editWarning() const;

/// Returns `true` if `disableEdit()` will disable the edit
/// at `source()`, and `false` otherwise.
bool canDisableEdit() const;
/// Disables the edit at `source()`. Throws if
/// `!canDisableEdit()`
void disableEdit() const;

private :

Result( const IECore::ConstObjectPtr &value, const Gaffer::EditScopePtr &editScope );
Expand All @@ -359,6 +374,8 @@ class GAFFERSCENEUI_API Inspector::Result : public IECore::RefCounted
EditFunctionOrFailure m_editFunction;
std::string m_editWarning;

DisableEditFunctionOrFailure m_disableEditFunction;

};

} // namespace Private
Expand Down
1 change: 1 addition & 0 deletions include/GafferSceneUI/Private/SetMembershipInspector.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,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;
DisableEditFunctionOrFailure disableEditFunction( Gaffer::ValuePlug *plug, const GafferScene::SceneAlgo::History *history ) const override;

private :

Expand Down
33 changes: 4 additions & 29 deletions python/GafferSceneUI/_InspectorColumn.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,43 +176,18 @@ def __disablableInspectionTweaks( pathListing ) :

with inspectionContext :
inspection = column.inspector().inspect()
if inspection is not None and inspection.editable() :
source = inspection.source()
editScope = inspection.editScope()
if (
(
(
isinstance( source, ( Gaffer.TweakPlug, Gaffer.NameValuePlug ) ) and
source["enabled"].getValue()
) or
isinstance( column.inspector(), GafferSceneUI.Private.SetMembershipInspector )
) and
( editScope is None or editScope.isAncestorOf( source ) )
) :
tweaks.append( ( pathString, column.inspector() ) )
else :
return []
if inspection is not None and inspection.canDisableEdit() :
tweaks.append( inspection )
else :
return []

return tweaks

def __disableEdits( pathListing ) :

edits = __disablableInspectionTweaks( pathListing )
path = pathListing.getPath().copy()
with Gaffer.UndoScope( pathListing.ancestor( GafferUI.Editor ).scriptNode() ) :
for pathString, inspector in edits :
path.setFromString( pathString )
with path.inspectionContext() :
inspection = inspector.inspect()
if inspection is not None and inspection.editable() :
source = inspection.source()

if isinstance( source, ( Gaffer.TweakPlug, Gaffer.NameValuePlug ) ) :
source["enabled"].setValue( False )
elif isinstance( inspector, GafferSceneUI.Private.SetMembershipInspector ) :
inspector.editSetMembership( inspection, pathString, GafferScene.EditScopeAlgo.SetMembership.Unchanged )
for inspection in __disablableInspectionTweaks( pathListing ) :
inspection.disableEdit()

def __removableAttributeInspections( pathListing ) :

Expand Down
52 changes: 52 additions & 0 deletions python/GafferSceneUITest/AttributeInspectorTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -846,5 +846,57 @@ def testAcquireEditCreateIfNecessary( self ) :
self.assertIsNotNone( edit )
self.assertEqual( inspection.acquireEdit( createIfNecessary = False ), edit )

def testDisableEdit( self ) :

s = Gaffer.ScriptNode()

s["light"] = GafferSceneTest.TestLight()
s["light"]["visualiserAttributes"]["scale"]["enabled"].setValue( True )

s["group"] = GafferScene.Group()
s["editScope1"] = Gaffer.EditScope()

s["group"]["in"][0].setInput( s["light"]["out"] )

s["editScope1"].setup( s["group"]["out"] )
s["editScope1"]["in"].setInput( s["group"]["out"] )

Gaffer.MetadataAlgo.setReadOnly( s["light"]["visualiserAttributes"]["scale"]["enabled"], True )
inspection = self.__inspect( s["group"]["out"], "/group/light", "gl:visualiser:scale", None )
self.assertFalse( inspection.canDisableEdit() )
self.assertRaisesRegex( IECore.Exception, "Cannot disable edit : light.visualiserAttributes.scale.enabled is locked.", inspection.disableEdit )

Gaffer.MetadataAlgo.setReadOnly( s["light"]["visualiserAttributes"]["scale"]["enabled"], False )
Gaffer.MetadataAlgo.setReadOnly( s["light"]["visualiserAttributes"]["scale"], True )
inspection = self.__inspect( s["group"]["out"], "/group/light", "gl:visualiser:scale", None )
self.assertFalse( inspection.canDisableEdit() )
self.assertRaisesRegex( IECore.Exception, "Cannot disable edit : light.visualiserAttributes.scale is locked.", inspection.disableEdit )

Gaffer.MetadataAlgo.setReadOnly( s["light"]["visualiserAttributes"]["scale"], False )
inspection = self.__inspect( s["group"]["out"], "/group/light", "gl:visualiser:scale", None )
self.assertTrue( inspection.canDisableEdit() )
inspection.disableEdit()
self.assertFalse( s["light"]["visualiserAttributes"]["scale"]["enabled"].getValue() )

lightEdit = GafferScene.EditScopeAlgo.acquireAttributeEdit(
s["editScope1"], "/group/light", "gl:visualiser:scale", createIfNecessary = True
)
lightEdit["enabled"].setValue( True )
lightEdit["value"].setValue( 2.0 )

Gaffer.MetadataAlgo.setReadOnly( s["editScope1"], True )
inspection = self.__inspect( s["editScope1"]["out"], "/group/light", "gl:visualiser:scale", s["editScope1"] )
self.assertFalse( inspection.canDisableEdit() )
self.assertRaisesRegex( IECore.Exception, "Cannot disable edit : editScope1 is locked.", inspection.disableEdit )

Gaffer.MetadataAlgo.setReadOnly( s["editScope1"], False )
inspection = self.__inspect( s["editScope1"]["out"], "/group/light", "gl:visualiser:scale", s["editScope1"] )
self.assertTrue( inspection.canDisableEdit() )
inspection.disableEdit()
self.assertFalse( lightEdit["enabled"].getValue() )

inspection = self.__inspect( s["editScope1"]["out"], "/group/light", "gl:visualiser:scale", s["editScope1"] )
self.assertFalse( inspection.canDisableEdit() )

if __name__ == "__main__" :
unittest.main()
44 changes: 44 additions & 0 deletions python/GafferSceneUITest/OptionInspectorTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -918,5 +918,49 @@ def testAcquireEditCreateIfNecessary( self ) :
self.assertIsNotNone( edit )
self.assertEqual( inspection.acquireEdit( createIfNecessary = False ), edit )

def testDisableEdit( self ) :

s = Gaffer.ScriptNode()

s["standardOptions"] = GafferScene.StandardOptions()
s["standardOptions"]["options"]["renderCamera"]["enabled"].setValue( True )
s["standardOptions"]["options"]["renderCamera"]["value"].setValue( "/defaultCamera" )

s["group"] = GafferScene.Group()
s["editScope1"] = Gaffer.EditScope()

s["group"]["in"][0].setInput( s["standardOptions"]["out"] )

s["editScope1"].setup( s["group"]["out"] )
s["editScope1"]["in"].setInput( s["group"]["out"] )

Gaffer.MetadataAlgo.setReadOnly( s["standardOptions"]["options"], True )
inspection = self.__inspect( s["group"]["out"], "render:camera", None )
self.assertFalse( inspection.canDisableEdit() )
self.assertRaisesRegex( IECore.Exception, "Cannot disable edit : standardOptions.options is locked.", inspection.disableEdit )

Gaffer.MetadataAlgo.setReadOnly( s["standardOptions"]["options"], False )
inspection = self.__inspect( s["group"]["out"], "render:camera", None )
self.assertTrue( inspection.canDisableEdit() )
inspection.disableEdit()
self.assertFalse( s["standardOptions"]["options"]["renderCamera"]["enabled"].getValue() )

cameraEdit = GafferScene.EditScopeAlgo.acquireOptionEdit(
s["editScope1"], "render:camera", createIfNecessary = True
)
cameraEdit["enabled"].setValue( True )
cameraEdit["value"].setValue( "/bar" )

Gaffer.MetadataAlgo.setReadOnly( s["editScope1"], True )
inspection = self.__inspect( s["editScope1"]["out"], "render:camera", s["editScope1"] )
self.assertFalse( inspection.canDisableEdit() )
self.assertRaisesRegex( IECore.Exception, "Cannot disable edit : editScope1 is locked.", inspection.disableEdit )

Gaffer.MetadataAlgo.setReadOnly( s["editScope1"], False )
inspection = self.__inspect( s["editScope1"]["out"], "render:camera", s["editScope1"] )
self.assertTrue( inspection.canDisableEdit() )
inspection.disableEdit()
self.assertFalse( cameraEdit["enabled"].getValue() )

if __name__ == "__main__" :
unittest.main()
56 changes: 56 additions & 0 deletions python/GafferSceneUITest/ParameterInspectorTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,62 @@ def testAcquireEditCreateIfNecessary( self ) :
self.assertIsNotNone( edit )
self.assertEqual( inspection.acquireEdit( createIfNecessary = False ), edit )

def testDisableEdit( self ) :

s = Gaffer.ScriptNode()

s["light"] = GafferSceneTest.TestLight()

s["lightFilter"] = GafferScene.PathFilter()
s["lightFilter"]["paths"].setValue( IECore.StringVectorData( [ "/light" ] ) )

s["shaderTweaks"] = GafferScene.ShaderTweaks()
s["shaderTweaks"]["in"].setInput( s["light"]["out"] )
s["shaderTweaks"]["filter"].setInput( s["lightFilter"]["out"] )
exposureTweak = Gaffer.TweakPlug( "exposure", 10 )
s["shaderTweaks"]["tweaks"].addChild( exposureTweak )

s["editScope"] = Gaffer.EditScope()
s["editScope"].setup( s["shaderTweaks"]["out"] )
s["editScope"]["in"].setInput( s["shaderTweaks"]["out"] )

Gaffer.MetadataAlgo.setReadOnly( exposureTweak["enabled"], True )
inspection = self.__inspect( s["shaderTweaks"]["out"], "/light", "exposure", None )
self.assertFalse( inspection.canDisableEdit() )
self.assertRaisesRegex( IECore.Exception, "Cannot disable edit : shaderTweaks.tweaks.tweak.enabled is locked.", inspection.disableEdit )

Gaffer.MetadataAlgo.setReadOnly( exposureTweak["enabled"], False )
Gaffer.MetadataAlgo.setReadOnly( exposureTweak, True )
inspection = self.__inspect( s["shaderTweaks"]["out"], "/light", "exposure", None )
self.assertFalse( inspection.canDisableEdit() )
self.assertRaisesRegex( IECore.Exception, "Cannot disable edit : shaderTweaks.tweaks.tweak is locked.", inspection.disableEdit )

Gaffer.MetadataAlgo.setReadOnly( exposureTweak, False )
inspection = self.__inspect( s["shaderTweaks"]["out"], "/light", "exposure", None )
self.assertTrue( inspection.canDisableEdit() )
inspection.disableEdit()
self.assertFalse( exposureTweak["enabled"].getValue() )

lightEdit = GafferScene.EditScopeAlgo.acquireParameterEdit(
s["editScope"], "/light", "light", ( "", "exposure" ), createIfNecessary = True
)
lightEdit["enabled"].setValue( True )
lightEdit["value"].setValue( 2.0 )

Gaffer.MetadataAlgo.setReadOnly( s["editScope"], True )
inspection = self.__inspect( s["editScope"]["out"], "/light", "exposure", s["editScope"] )
self.assertFalse( inspection.canDisableEdit() )
self.assertRaisesRegex( IECore.Exception, "Cannot disable edit : editScope is locked.", inspection.disableEdit )

Gaffer.MetadataAlgo.setReadOnly( s["editScope"], False )
inspection = self.__inspect( s["editScope"]["out"], "/light", "exposure", s["editScope"] )
self.assertTrue( inspection.canDisableEdit() )
inspection.disableEdit()
self.assertFalse( lightEdit["enabled"].getValue() )

inspection = self.__inspect( s["editScope"]["out"], "/light", "exposure", s["editScope"] )
self.assertFalse( inspection.canDisableEdit() )

def testDisabledTweaks( self ) :

light = GafferSceneTest.TestLight()
Expand Down
62 changes: 62 additions & 0 deletions python/GafferSceneUITest/SetMembershipInspectorTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -618,5 +618,67 @@ def testAcquireEditCreateIfNecessary( self ) :
self.assertIsNotNone( edit )
self.assertEqual( inspection.acquireEdit( createIfNecessary = False ), edit )

def testDisableEdit( self ) :

s = Gaffer.ScriptNode()

s["plane"] = GafferScene.Plane()
s["plane"]["sets"].setValue( "planeSetA planeSetB" )

s["group"] = GafferScene.Group()

s["editScope1"] = Gaffer.EditScope()

s["group"]["in"][0].setInput( s["plane"]["out"] )

Gaffer.MetadataAlgo.setReadOnly( s["plane"]["sets"], True )
inspection = self.__inspect( s["group"]["out"], "/group/plane", "planeSetA", None )
self.assertFalse( inspection.canDisableEdit() )

Gaffer.MetadataAlgo.setReadOnly( s["plane"]["sets"], False )
inspection = self.__inspect( s["group"]["out"], "/group/plane", "planeSetA", None )
self.assertTrue( inspection.canDisableEdit() )

inspection.disableEdit()
self.assertEqual( s["plane"]["sets"].getValue(), "planeSetB" )

inspection = self.__inspect( s["group"]["out"], "/group/plane", "planeSetB", None )
self.assertTrue( inspection.canDisableEdit() )

inspection.disableEdit()
self.assertEqual( s["plane"]["sets"].getValue(), "" )

s["editScope1"].setup( s["group"]["out"] )
s["editScope1"]["in"].setInput( s["group"]["out"] )

for membership in ( GafferScene.EditScopeAlgo.SetMembership.Added, GafferScene.EditScopeAlgo.SetMembership.Removed ) :

GafferScene.EditScopeAlgo.setSetMembership(
s["editScope1"],
IECore.PathMatcher( [ "group/plane" ] ),
"planeSetEditScope",
membership
)

self.assertEqual(
GafferScene.EditScopeAlgo.getSetMembership( s["editScope1"], "/group/plane", "planeSetEditScope"),
membership
)

Gaffer.MetadataAlgo.setReadOnly( s["editScope1"], True )
inspection = self.__inspect( s["editScope1"]["out"], "/group/plane", "planeSetEditScope", None )
self.assertFalse( inspection.canDisableEdit() )
self.assertRaisesRegex( IECore.Exception, "Cannot disable edit : editScope1 is locked.", inspection.disableEdit )

Gaffer.MetadataAlgo.setReadOnly( s["editScope1"], False )
inspection = self.__inspect( s["editScope1"]["out"], "/group/plane", "planeSetEditScope", None )
self.assertTrue( inspection.canDisableEdit() )

inspection.disableEdit()
self.assertEqual(
GafferScene.EditScopeAlgo.getSetMembership( s["editScope1"], "/group/plane", "planeSetEditScope"),
GafferScene.EditScopeAlgo.SetMembership.Unchanged
)

if __name__ == "__main__" :
unittest.main()
Loading

0 comments on commit 8ee485e

Please sign in to comment.