Skip to content

Commit

Permalink
Merge pull request #5887 from murraystevenson/editScopeDrop
Browse files Browse the repository at this point in the history
EditScopePlugValueWidget : Support drag and drop
  • Loading branch information
johnhaddon authored Jun 12, 2024
2 parents f004765 + a2e7798 commit c52034a
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 14 deletions.
4 changes: 4 additions & 0 deletions Changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ Improvements
- Increased the size of the triangle indicators for the decay ranges.
- The decay range indicators are now scaled by the light's `gl:visualiser:scale` attribute.
- The decay range is now ignored when framing a light in the Viewer.
- EditScopePlugValueWidget :
- When viewing the output of a Box, the menu now displays the Edit Scopes contained within.
- Added support for dropping an Edit Scope node onto the widget to set it as the current Edit Scope.
- Added support for middle-dragging from the widget to access the current Edit Scope node.

API
---
Expand Down
126 changes: 112 additions & 14 deletions python/GafferUI/EditScopeUI.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,10 +119,10 @@ class EditScopePlugValueWidget( GafferUI.PlugValueWidget ) :

def __init__( self, plug, **kw ) :

frame = GafferUI.Frame( borderWidth = 0 )
GafferUI.PlugValueWidget.__init__( self, frame, plug, **kw )
self.__frame = GafferUI.Frame( borderWidth = 0 )
GafferUI.PlugValueWidget.__init__( self, self.__frame, plug, **kw )

with frame :
with self.__frame :
with GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Horizontal, spacing = 4 ) :
GafferUI.Spacer( imath.V2i( 4, 1 ), imath.V2i( 4, 1 ) )
GafferUI.Label( "Edit Scope" )
Expand All @@ -139,6 +139,15 @@ def __init__( self, plug, **kw ) :
)
GafferUI.Spacer( imath.V2i( 4, 1 ), imath.V2i( 4, 1 ) )

self.buttonPressSignal().connect( Gaffer.WeakMethod( self.__buttonPress ), scoped = False )
self.dragBeginSignal().connect( Gaffer.WeakMethod( self.__dragBegin ), scoped = False )
self.dragEndSignal().connect( Gaffer.WeakMethod( self.__dragEnd ), scoped = False )
self.dragEnterSignal().connect( Gaffer.WeakMethod( self.__dragEnter ), scoped = False )
self.dragLeaveSignal().connect( Gaffer.WeakMethod( self.__dragLeave ), scoped = False )
# We connect to the front, and unconditionally return True to ensure that we never
# run the default dropSignal handler from PlugValueWidget.
self.dropSignal().connectFront( Gaffer.WeakMethod( self.__drop ), scoped = False )

def hasLabel( self ) :

return True
Expand Down Expand Up @@ -199,6 +208,27 @@ def __connectEditScope( self, editScope, *ignored ) :

self.getPlug().setInput( editScope["out"] )

def __inputNode( self ) :

node = self.getPlug().node()
# We assume that our plug is on a node dedicated to holding settings for the
# UI, and that it has an `in` plug that is connected to the node in the graph
# that is being viewed. We start our node graph traversal at the viewed node
# (we can't start at _this_ node, as then we will visit our own input connection
# which may no longer be upstream of the viewed node).
if node["in"].getInput() is None :
return None

inputNode = node["in"].getInput().node()
if not isinstance( inputNode, Gaffer.EditScope ) and isinstance( inputNode, Gaffer.SubGraph ) :
# If we're starting from a SubGraph then attempt to begin the search from the
# first input of the node's output so we can find any Edit Scopes within.
output = node["in"].getInput().getInput()
if output is not None and inputNode.isAncestorOf( output ) :
return output.node()

return inputNode

def __buildMenu( self, result, path, currentEditScope ) :

result = IECore.MenuDefinition()
Expand Down Expand Up @@ -254,21 +284,11 @@ def __menuDefinition( self ) :

result = IECore.MenuDefinition()

node = self.getPlug().node()
# We assume that our plug is on a node dedicated to holding settings for the
# UI, and that it has an `in` plug that is connected to the node in the graph
# that is being viewed. We start our node graph traversal at the viewed node
# (we can't start at _this_ node, as then we will visit our own input connection
# which may no longer be upstream of the viewed node).
if node["in"].getInput() is not None :
node = node["in"].getInput().node()
else :
node = None

currentEditScope = None
if self.getPlug().getInput() is not None :
currentEditScope = self.getPlug().getInput().parent()

node = self.__inputNode()
if node is not None :
upstream = Gaffer.NodeAlgo.findAllUpstream( node, self.__editScopePredicate )
if self.__editScopePredicate( node ) :
Expand Down Expand Up @@ -373,6 +393,84 @@ def __userNodes( editScope ) :
nodes = Gaffer.Metadata.nodesWithMetadata( editScope, "editScope:includeInNavigationMenu" )
return [ n for n in nodes if n.ancestor( Gaffer.EditScope ).isSame( editScope ) ]

def __dropNode( self, event ) :

if isinstance( event.data, Gaffer.EditScope ) :
return event.data
elif (
isinstance( event.data, Gaffer.Set ) and event.data.size() == 1 and
isinstance( event.data[0], Gaffer.EditScope )
) :
return event.data[0]
else:
return None

def __buttonPress( self, widget, event ) :

return event.buttons & ( event.Buttons.Left | event.Buttons.Middle ) and self.__editScope() is not None

def __dragBegin( self, widget, event ) :

if not event.buttons & ( event.Buttons.Left | event.Buttons.Middle ) :
return None

data = self.__editScope()
if data is None :
return None

GafferUI.Pointer.setCurrent( "nodes" )
return data

def __dragEnd( self, widget, event ) :

GafferUI.Pointer.setCurrent( "" )

return True

def __dragEnter( self, widget, event ) :

if event.sourceWidget is self :
return False

if self.__dropNode( event ) :
self.__frame.setHighlighted( True )

return True

def __dragLeave( self, widget, event ) :

self.__frame.setHighlighted( False )

return True

def __drop( self, widget, event ) :

inputNode = self.__inputNode()
dropNode = self.__dropNode( event )
if inputNode is None :
with GafferUI.PopupWindow() as self.__popup :
with GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Horizontal, spacing = 4 ) :
GafferUI.Image( "warningSmall.png" )
GafferUI.Label( "<h4>The Edit Scope cannot be set while nothing is viewed</h4>" )
self.__popup.popup()
elif dropNode :
upstream = Gaffer.NodeAlgo.findAllUpstream( inputNode, self.__editScopePredicate )
if self.__editScopePredicate( inputNode ) :
upstream.insert( 0, inputNode )

if dropNode in upstream :
self.__connectEditScope( dropNode )
else :
with GafferUI.PopupWindow() as self.__popup :
with GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Horizontal, spacing = 4 ) :
GafferUI.Image( "warningSmall.png" )
GafferUI.Label( "<h4>{} cannot be used as it is not upstream of {}</h4>".format( dropNode.getName(), inputNode.getName() ) )
self.__popup.popup()

self.__frame.setHighlighted( False )

return True

# ProcessorWidget
# ===============

Expand Down

0 comments on commit c52034a

Please sign in to comment.