diff --git a/Changes.md b/Changes.md index f0d077aa99..aa730ffc8e 100644 --- a/Changes.md +++ b/Changes.md @@ -33,6 +33,7 @@ Improvements - Increased menu item icon sizes. - A lock icon is now displayed next to read-only nodes. - RenderPassEditor : Changed the current render pass indicator to yellow to match other context-related UI elements. +- GraphEditor : Moved "Show Input Connections" and "Show Output Connections" to "Connections" sub-menu and added "Show Input Labels" and "Show Output Labels" items. Fixes ----- @@ -57,6 +58,7 @@ API - Deprecated `connect()` function. Use `connectToApplication()` instead. - SceneEditor : Added `editScope()` method. - Image : Added optional `image` argument to `createSwatch()` static method. +- StandardNodeGadget : Added support for `nodeGadget:inputNoduleLabelsVisible` and `nodeGadget:outputNoduleLabelsVisible` metadata for setting nodule labels always on. If the metadata entry is not set or `False`, labels will be visible only when they are hovered over. Build ----- diff --git a/include/GafferUI/StandardNodeGadget.h b/include/GafferUI/StandardNodeGadget.h index 76be5283e4..d593f6e40e 100644 --- a/include/GafferUI/StandardNodeGadget.h +++ b/include/GafferUI/StandardNodeGadget.h @@ -46,6 +46,7 @@ namespace GafferUI class PlugAdder; class NoduleLayout; class ConnectionCreator; +class StandardNodule; /// The standard means of representing a Node in a GraphGadget. /// Nodes are represented as rectangular boxes with the name displayed @@ -150,6 +151,7 @@ class GAFFERUI_API StandardNodeGadget : public NodeGadget bool dragMove( GadgetPtr gadget, const DragDropEvent &event ); bool dragLeave( GadgetPtr gadget, const DragDropEvent &event ); bool drop( GadgetPtr gadget, const DragDropEvent &event ); + void noduleAdded( Nodule *nodule ); ConnectionCreator *closestDragDestination( const DragDropEvent &event ) const; @@ -164,6 +166,10 @@ class GAFFERUI_API StandardNodeGadget : public NodeGadget void updateFocusGadgetVisibility(); void updateTextDimming(); + friend class StandardNodule; + // Set the visibility for all nodules based on the metadata registered for this node. + void applyNoduleLabelVisibilityMetadata(); + IE_CORE_FORWARDDECLARE( ErrorGadget ); ErrorGadget *errorGadget( bool createIfMissing = true ); void error( const Gaffer::Plug *plug, const Gaffer::Plug *source, const std::string &message ); diff --git a/include/GafferUI/StandardNodule.h b/include/GafferUI/StandardNodule.h index ec9da182f2..0eec0acbc1 100644 --- a/include/GafferUI/StandardNodule.h +++ b/include/GafferUI/StandardNodule.h @@ -89,7 +89,11 @@ class GAFFERUI_API StandardNodule : public Nodule bool dragEnd( GadgetPtr gadget, const DragDropEvent &event ); bool drop( GadgetPtr gadget, const DragDropEvent &event ); + /// \deprecated Use overloaded method without `visible` when setting `visible = true` + /// or `StandardNodeGadget::applyNoduleLabelVisibilityMetadata()` to restore + /// metadata-aware visibility. void setCompatibleLabelsVisible( const DragDropEvent &event, bool visible ); + void setCompatibleLabelsVisible( const DragDropEvent &event ); private : diff --git a/python/GafferUI/GraphEditor.py b/python/GafferUI/GraphEditor.py index abc955a31b..4c292285ea 100644 --- a/python/GafferUI/GraphEditor.py +++ b/python/GafferUI/GraphEditor.py @@ -215,7 +215,7 @@ def plugDirectionsWalk( gadget ) : if Gaffer.Plug.Direction.In in plugDirections : menuDefinition.append( - "/Show Input Connections", + "/Connections/Show Input Connections", { "checkBox" : functools.partial( cls.__getNodeInputConnectionsVisible, graphEditor.graphGadget(), node ), "command" : functools.partial( cls.__setNodeInputConnectionsVisible, graphEditor.graphGadget(), node ), @@ -225,7 +225,7 @@ def plugDirectionsWalk( gadget ) : if Gaffer.Plug.Direction.Out in plugDirections : menuDefinition.append( - "/Show Output Connections", + "/Connections/Show Output Connections", { "checkBox" : functools.partial( cls.__getNodeOutputConnectionsVisible, graphEditor.graphGadget(), node ), "command" : functools.partial( cls.__setNodeOutputConnectionsVisible, graphEditor.graphGadget(), node ), @@ -233,6 +233,26 @@ def plugDirectionsWalk( gadget ) : } ) + if Gaffer.Plug.Direction.In in plugDirections : + menuDefinition.append( + "/Connections/Show Input Labels", + { + "checkBox" : functools.partial( cls.__getNoduleLabelsVisible, node, "input" ), + "command" : functools.partial( cls.__setNoduleLabelsVisible, node, "input" ), + "active" : not readOnly, + } + ) + + if Gaffer.Plug.Direction.Out in plugDirections : + menuDefinition.append( + "/Connections/Show Output Labels", + { + "checkBox" : functools.partial( cls.__getNoduleLabelsVisible, node, "output" ), + "command" : functools.partial( cls.__setNoduleLabelsVisible, node, "output" ), + "active" : not readOnly, + } + ) + ## May be used from a slot attached to nodeContextMenuSignal() to install a # standard menu item for modifying the enabled state of a node. @classmethod @@ -729,6 +749,17 @@ def __setNodeInputConnectionsVisible( cls, graphGadget, node, value ) : with Gaffer.UndoScope( node.ancestor( Gaffer.ScriptNode ) ) : graphGadget.setNodeInputConnectionsMinimised( node, not value ) + @classmethod + def __getNoduleLabelsVisible( cls, node, direction ) : + + return Gaffer.Metadata.value( node, f"nodeGadget:{direction}NoduleLabelsVisible" ) or False + + @classmethod + def __setNoduleLabelsVisible( cls, node, direction, value ) : + + with Gaffer.UndoScope( node.ancestor( Gaffer.ScriptNode ) ) : + Gaffer.Metadata.registerValue( node, f"nodeGadget:{direction}NoduleLabelsVisible", value ) + @classmethod def __getNodeOutputConnectionsVisible( cls, graphGadget, node ) : diff --git a/python/GafferUITest/StandardNodeGadgetTest.py b/python/GafferUITest/StandardNodeGadgetTest.py index b86ec30c17..bcea061c09 100644 --- a/python/GafferUITest/StandardNodeGadgetTest.py +++ b/python/GafferUITest/StandardNodeGadgetTest.py @@ -308,5 +308,47 @@ def assertMinWidth( gadget, minWidth ) : Gaffer.Metadata.deregisterValue( n, "nodeGadget:minWidth" ) assertMinWidth( g, 10.0 ) + def testNoduleLabelVisibility( self ) : + + n = Gaffer.Node() + n.addChild( Gaffer.FloatPlug( "fIn", direction = Gaffer.Plug.Direction.In ) ) + n.addChild( Gaffer.FloatPlug( "fOut", direction = Gaffer.Plug.Direction.Out ) ) + g = GafferUI.StandardNodeGadget( n ) + + fIn = g.nodule( n["fIn"] ) + fOut = g.nodule( n["fOut"] ) + + self.assertFalse( fIn.getLabelVisible() ) + self.assertFalse( fOut.getLabelVisible() ) + + Gaffer.Metadata.registerValue( Gaffer.Node, "nodeGadget:inputNoduleLabelsVisible", True ) + self.assertTrue( fIn.getLabelVisible() ) + self.assertFalse( fOut.getLabelVisible() ) + + Gaffer.Metadata.registerValue( Gaffer.Node, "nodeGadget:outputNoduleLabelsVisible", True ) + self.assertTrue( fIn.getLabelVisible() ) + self.assertTrue( fOut.getLabelVisible() ) + + n.addChild( Gaffer.IntPlug( "iIn", direction = Gaffer.Plug.Direction.In ) ) + n.addChild( Gaffer.IntPlug( "iOut", direction = Gaffer.Plug.Direction.Out ) ) + + iIn = g.nodule( n["iIn"] ) + iOut = g.nodule( n["iOut"] ) + + self.assertTrue( iIn.getLabelVisible() ) + self.assertTrue( iOut.getLabelVisible() ) + + Gaffer.Metadata.registerValue( Gaffer.Node, "nodeGadget:inputNoduleLabelsVisible", False ) + self.assertFalse( fIn.getLabelVisible() ) + self.assertFalse( iIn.getLabelVisible() ) + self.assertTrue( fOut.getLabelVisible() ) + self.assertTrue( iOut.getLabelVisible() ) + + Gaffer.Metadata.registerValue( Gaffer.Node, "nodeGadget:outputNoduleLabelsVisible", False ) + self.assertFalse( fIn.getLabelVisible() ) + self.assertFalse( iIn.getLabelVisible() ) + self.assertFalse( fOut.getLabelVisible() ) + self.assertFalse( iOut.getLabelVisible() ) + if __name__ == "__main__": unittest.main() diff --git a/src/GafferUI/StandardNodeGadget.cpp b/src/GafferUI/StandardNodeGadget.cpp index e07a42442b..5c683b7e99 100644 --- a/src/GafferUI/StandardNodeGadget.cpp +++ b/src/GafferUI/StandardNodeGadget.cpp @@ -566,6 +566,8 @@ static IECore::InternedString g_paddingKey( "nodeGadget:padding" ); static IECore::InternedString g_colorKey( "nodeGadget:color" ); static IECore::InternedString g_shapeKey( "nodeGadget:shape" ); static IECore::InternedString g_focusGadgetVisibleKey( "nodeGadget:focusGadgetVisible" ); +static IECore::InternedString g_inputNoduleLabelsVisibleKey( "nodeGadget:inputNoduleLabelsVisible" ); +static IECore::InternedString g_outputNoduleLabelsVisibleKey( "nodeGadget:outputNoduleLabelsVisible" ); static IECore::InternedString g_iconKey( "icon" ); static IECore::InternedString g_iconScaleKey( "iconScale" ); static IECore::InternedString g_errorGadgetName( "__error" ); @@ -700,6 +702,7 @@ StandardNodeGadget::StandardNodeGadget( Gaffer::NodePtr node, bool auxiliary ) dragMoveSignal().connect( boost::bind( &StandardNodeGadget::dragMove, this, ::_1, ::_2 ) ); dragLeaveSignal().connect( boost::bind( &StandardNodeGadget::dragLeave, this, ::_1, ::_2 ) ); dropSignal().connect( boost::bind( &StandardNodeGadget::drop, this, ::_1, ::_2 ) ); + noduleAddedSignal().connect( boost::bind( &StandardNodeGadget::noduleAdded, this, ::_2 ) ); for( int e = FirstEdge; e <= LastEdge; e++ ) { @@ -722,6 +725,7 @@ StandardNodeGadget::StandardNodeGadget( Gaffer::NodePtr node, bool auxiliary ) updateIcon(); updateShape(); updateFocusGadgetVisibility(); + applyNoduleLabelVisibilityMetadata(); } StandardNodeGadget::~StandardNodeGadget() @@ -1144,10 +1148,7 @@ void StandardNodeGadget::leave( Gadget *gadget ) { if( m_labelsVisibleOnHover ) { - for( StandardNodule::RecursiveIterator it( gadget ); !it.done(); ++it ) - { - (*it)->setLabelVisible( false ); - } + applyNoduleLabelVisibilityMetadata(); } } @@ -1202,10 +1203,7 @@ bool StandardNodeGadget::dragLeave( GadgetPtr gadget, const DragDropEvent &event if( m_dragDestination != event.destinationGadget ) { m_dragDestination->setHighlighted( false ); - for( StandardNodule::RecursiveIterator it( this ); !it.done(); ++it ) - { - (*it)->setLabelVisible( false ); - } + applyNoduleLabelVisibilityMetadata(); } m_dragDestination = nullptr; @@ -1222,14 +1220,25 @@ bool StandardNodeGadget::drop( GadgetPtr gadget, const DragDropEvent &event ) connect( event, m_dragDestination ); m_dragDestination->setHighlighted( false ); - for( StandardNodule::RecursiveIterator it( this ); !it.done(); ++it ) - { - (*it)->setLabelVisible( false ); - } + applyNoduleLabelVisibilityMetadata(); + m_dragDestination = nullptr; return true; } +void StandardNodeGadget::noduleAdded( Nodule *nodule ) +{ + if( auto standardNodule = IECore::runTimeCast( nodule ) ) + { + IECore::ConstBoolDataPtr d = standardNodule->plug()->direction() == Plug::Direction::In ? + Gaffer::Metadata::value( node(), g_inputNoduleLabelsVisibleKey ) : + Gaffer::Metadata::value( node(), g_outputNoduleLabelsVisibleKey ) + ; + + standardNodule->setLabelVisible( d ? d->readable() : false ); + } +} + ConnectionCreator *StandardNodeGadget::closestDragDestination( const DragDropEvent &event ) const { if( event.buttons != DragDropEvent::Left ) @@ -1303,6 +1312,10 @@ void StandardNodeGadget::nodeMetadataChanged( IECore::InternedString key ) { updateFocusGadgetVisibility(); } + else if( key == g_inputNoduleLabelsVisibleKey || key == g_outputNoduleLabelsVisibleKey ) + { + applyNoduleLabelVisibilityMetadata(); + } } bool StandardNodeGadget::updateUserColor() @@ -1464,6 +1477,28 @@ void StandardNodeGadget::updateFocusGadgetVisibility() m_focusGadget->setVisible( !d || d->readable() ); } +void StandardNodeGadget::applyNoduleLabelVisibilityMetadata() +{ + bool inputVisible = false; + if( IECore::ConstBoolDataPtr d = Gaffer::Metadata::value( node(), g_inputNoduleLabelsVisibleKey ) ) + { + inputVisible = d->readable(); + } + + bool outputVisible = false; + if( IECore::ConstBoolDataPtr d = Gaffer::Metadata::value( node(), g_outputNoduleLabelsVisibleKey ) ) + { + outputVisible = d->readable(); + } + + for( StandardNodule::RecursiveIterator it( this ); !it.done(); ++it ) + { + (*it)->setLabelVisible( + (*it)->plug()->direction() == Plug::Direction::In ? inputVisible : outputVisible + ); + } +} + StandardNodeGadget::ErrorGadget *StandardNodeGadget::errorGadget( bool createIfMissing ) { if( ErrorGadget *result = getChild( g_errorGadgetName ) ) diff --git a/src/GafferUI/StandardNodule.cpp b/src/GafferUI/StandardNodule.cpp index 016d1fef59..4e77f77c4b 100644 --- a/src/GafferUI/StandardNodule.cpp +++ b/src/GafferUI/StandardNodule.cpp @@ -369,7 +369,7 @@ bool StandardNodule::dragEnter( GadgetPtr gadget, const DragDropEvent &event ) Nodule *prevDestination = IECore::runTimeCast( event.destinationGadget.get() ); if( !prevDestination || prevDestination->plug()->node() != plug()->node() ) { - setCompatibleLabelsVisible( event, true ); + setCompatibleLabelsVisible( event ); } dirty( DirtyType::Render ); @@ -397,19 +397,40 @@ bool StandardNodule::dragLeave( GadgetPtr gadget, const DragDropEvent &event ) { if( newDestination->plug()->node() != plug()->node() ) { - setCompatibleLabelsVisible( event, false ); + if( auto nodeGadget = ancestor() ) + { + nodeGadget->applyNoduleLabelVisibilityMetadata(); + } + else + { + setCompatibleLabelsVisible( event, false ); + } } } else if( NodeGadget *newDestination = IECore::runTimeCast( event.destinationGadget.get() ) ) { if( newDestination->node() != plug()->node() ) { - setCompatibleLabelsVisible( event, false ); + if( auto nodeGadget = ancestor() ) + { + nodeGadget->applyNoduleLabelVisibilityMetadata(); + } + else + { + setCompatibleLabelsVisible( event, false ); + } } } else { - setCompatibleLabelsVisible( event, false ); + if( auto nodeGadget = ancestor() ) + { + nodeGadget->applyNoduleLabelVisibilityMetadata(); + } + else + { + setCompatibleLabelsVisible( event, false ); + } } dirty( DirtyType::Render ); } @@ -434,7 +455,14 @@ bool StandardNodule::dragEnd( GadgetPtr gadget, const DragDropEvent &event ) bool StandardNodule::drop( GadgetPtr gadget, const DragDropEvent &event ) { setHighlighted( false ); - setCompatibleLabelsVisible( event, false ); + if( auto nodeGadget = ancestor() ) + { + nodeGadget->applyNoduleLabelVisibilityMetadata(); + } + else + { + setCompatibleLabelsVisible( event, false ); + } if( ConnectionCreator *creator = IECore::runTimeCast( event.sourceGadget.get() ) ) { @@ -474,6 +502,26 @@ void StandardNodule::setCompatibleLabelsVisible( const DragDropEvent &event, boo } } +void StandardNodule::setCompatibleLabelsVisible( const DragDropEvent &event ) +{ + NodeGadget *nodeGadget = ancestor(); + if( !nodeGadget ) + { + return; + } + + ConnectionCreator *creator = IECore::runTimeCast( event.sourceGadget.get() ); + if( !creator ) + { + return; + } + + for( StandardNodule::RecursiveIterator it( nodeGadget ); !it.done(); ++it ) + { + (*it)->setLabelVisible( creator->canCreateConnection( it->get()->plug() ) ? true : false ); + } +} + void StandardNodule::plugMetadataChanged( const Gaffer::Plug *plug, IECore::InternedString key ) { if( plug != this->plug() )