diff --git a/Changes.md b/Changes.md index 7fbf9f3f16..63c26fe895 100644 --- a/Changes.md +++ b/Changes.md @@ -15,6 +15,7 @@ Improvements - GraphGadget : - Improved highlighting of active nodes, with more accurate tracking of Loop node iterations. - Annotation `{plug}` substitutions are now evaluated in a context determined relative to the focus node. + - The strike-through for disabled nodes is now evaluated in a context determined relative to the focus node. - Spreadsheet : Added yellow underlining to the currently active row. Fixes @@ -33,6 +34,7 @@ API - Added `settings()` method, which returns a node hosting plugs specifying settings for the editor. - Added `_updateFromSettings()` method, which is called when a subclass should update to reflect changes to the settings. - SceneEditor : Added new base class to simplify the creation of scene-specific editors. +- NodeGadget, ConnectionGadget : Added `updateFromContextTracker()` virtual methods. Breaking Changes ---------------- @@ -50,6 +52,7 @@ Breaking Changes - Removed `setContext()` methods. - Deprecated `getContext()` methods. Use `context()` instead. - Loop : Removed `nextIterationContext()` method. +- NodeGadget, ConnectionGadget : Removed `activeForFocusNode()` virtual methods. Override `updateFromContextTracker()` instead. 1.4.x.x (relative to 1.4.9.0) ======= diff --git a/include/GafferUI/ConnectionGadget.h b/include/GafferUI/ConnectionGadget.h index 11cf9d00e1..66385f6755 100644 --- a/include/GafferUI/ConnectionGadget.h +++ b/include/GafferUI/ConnectionGadget.h @@ -48,6 +48,7 @@ namespace GafferUI { +IE_CORE_FORWARDDECLARE( ContextTracker ) IE_CORE_FORWARDDECLARE( Nodule ) IE_CORE_FORWARDDECLARE( ConnectionGadget ) @@ -120,8 +121,11 @@ class GAFFERUI_API ConnectionGadget : public ConnectionCreator static ConnectionGadgetPtr creator( NodulePtr srcNodule, NodulePtr dstNodule ) { return new T( srcNodule, dstNodule ); }; }; - virtual void activeForFocusNode( bool active ); - + /// May be overridden to update the UI state to reflect changes in the ContextTracker. + /// Calls to this are made by the parent GraphGadget, to avoid every individual + /// ConnectionGadget needing to connect to the ContextTracker signals itself. + virtual void updateFromContextTracker( const ContextTracker *contextTracker ); + /// Friendship to allow calling `updateFromContextTracker()`. friend class GraphGadget; bool m_active; diff --git a/include/GafferUI/NodeGadget.h b/include/GafferUI/NodeGadget.h index 2ba3606bbc..ca91657f48 100644 --- a/include/GafferUI/NodeGadget.h +++ b/include/GafferUI/NodeGadget.h @@ -46,6 +46,7 @@ namespace GafferUI { +IE_CORE_FORWARDDECLARE( ContextTracker ) IE_CORE_FORWARDDECLARE( Nodule ) IE_CORE_FORWARDDECLARE( NodeGadget ) IE_CORE_FORWARDDECLARE( ConnectionCreator ) @@ -113,8 +114,11 @@ class GAFFERUI_API NodeGadget : public Gadget static NodeGadgetPtr creator( Gaffer::NodePtr node ) { return new T( node ); }; }; - virtual void activeForFocusNode( bool active ); - + /// May be overridden to update the UI state to reflect changes in the ContextTracker. + /// Calls to this are made by the parent GraphGadget, to avoid every individual + /// NodeGadget needing to connect to the ContextTracker signals itself. + virtual void updateFromContextTracker( const ContextTracker *contextTracker ); + /// Friendship to allow calling `updateFromContextTracker()`. friend class GraphGadget; bool m_active; diff --git a/include/GafferUI/StandardNodeGadget.h b/include/GafferUI/StandardNodeGadget.h index 7cd98ce72d..3f0436407d 100644 --- a/include/GafferUI/StandardNodeGadget.h +++ b/include/GafferUI/StandardNodeGadget.h @@ -118,7 +118,7 @@ class GAFFERUI_API StandardNodeGadget : public NodeGadget const Imath::Color3f *userColor() const; - void activeForFocusNode( bool active ) override; + void updateFromContextTracker( const ContextTracker *contextTracker ) override; private : @@ -158,7 +158,7 @@ class GAFFERUI_API StandardNodeGadget : public NodeGadget bool updateUserColor(); void updateMinWidth(); void updatePadding(); - void updateNodeEnabled( const Gaffer::Plug *dirtiedPlug = nullptr ); + void updateStrikeThroughVisibility( const Gaffer::Plug *dirtiedPlug = nullptr ); void updateIcon(); bool updateShape(); void updateFocusGadgetVisibility(); @@ -169,7 +169,8 @@ class GAFFERUI_API StandardNodeGadget : public NodeGadget void error( const Gaffer::Plug *plug, const Gaffer::Plug *source, const std::string &message ); void displayError( Gaffer::ConstPlugPtr plug, const std::string &message ); - bool m_nodeEnabled; + std::optional m_nodeEnabledInContextTracker; + bool m_strikeThroughVisible; bool m_labelsVisibleOnHover; // We accept drags onto the NodeGadget itself and // use them to create a connection to the diff --git a/src/GafferUI/ConnectionGadget.cpp b/src/GafferUI/ConnectionGadget.cpp index 4f20c198fc..23618acc44 100644 --- a/src/GafferUI/ConnectionGadget.cpp +++ b/src/GafferUI/ConnectionGadget.cpp @@ -202,8 +202,9 @@ ConnectionGadget::NamedCreatorMap &ConnectionGadget::namedCreators() return *m; } -void ConnectionGadget::activeForFocusNode( bool active ) +void ConnectionGadget::updateFromContextTracker( const ContextTracker *contextTracker ) { + const bool active = contextTracker->targetNode() ? contextTracker->isTracked( m_dstNodule->plug() ) : true; if( m_active != active ) { m_active = active; diff --git a/src/GafferUI/GraphGadget.cpp b/src/GafferUI/GraphGadget.cpp index 43127cf7a3..568323dbad 100644 --- a/src/GafferUI/GraphGadget.cpp +++ b/src/GafferUI/GraphGadget.cpp @@ -1690,7 +1690,7 @@ NodeGadget *GraphGadget::addNodeGadget( Gaffer::Node *node ) { nodeGadget->setHighlighted( true ); } - nodeGadget->activeForFocusNode( m_scriptNode->getFocus() ? m_contextTracker->isActive( node ) : true ); + nodeGadget->updateFromContextTracker( m_contextTracker.get() ); } updateNodeGadgetTransform( nodeGadget.get() ); @@ -1797,7 +1797,7 @@ void GraphGadget::addConnectionGadget( Nodule *dstNodule ) if( m_scriptNode ) { - connection->activeForFocusNode( m_scriptNode->getFocus() ? m_contextTracker->isActive( dstPlug ) : true ); + connection->updateFromContextTracker( m_contextTracker.get() ); } m_connectionGadgets[dstNodule] = connection.get(); @@ -1877,16 +1877,15 @@ void GraphGadget::updateConnectionGadgetMinimisation( ConnectionGadget *gadget ) void GraphGadget::applyFocusContexts() { - const bool haveFocus = m_scriptNode->getFocus(); for( auto &g : children() ) { if( ConnectionGadget *c = runTimeCast( g.get() ) ) { - c->activeForFocusNode( haveFocus ? m_contextTracker->isActive( c->dstNodule()->plug() ) : true ); + c->updateFromContextTracker( m_contextTracker.get() ); } else if( NodeGadget *n = runTimeCast( g.get() ) ) { - n->activeForFocusNode( haveFocus ? m_contextTracker->isActive( n->node() ) : true ); + n->updateFromContextTracker( m_contextTracker.get() ); } } /// \todo This should not be necessary. It emulates an old behaviour where we always diff --git a/src/GafferUI/NodeGadget.cpp b/src/GafferUI/NodeGadget.cpp index a7a2b674b5..4ad0d669ab 100644 --- a/src/GafferUI/NodeGadget.cpp +++ b/src/GafferUI/NodeGadget.cpp @@ -37,6 +37,7 @@ #include "GafferUI/NodeGadget.h" +#include "GafferUI/ContextTracker.h" #include "GafferUI/LinearContainer.h" #include "GafferUI/NameGadget.h" #include "GafferUI/Nodule.h" @@ -216,8 +217,9 @@ std::string NodeGadget::getToolTip( const IECore::LineSegment3f &line ) const return result; } -void NodeGadget::activeForFocusNode( bool active ) +void NodeGadget::updateFromContextTracker( const ContextTracker *contextTracker ) { + const bool active = contextTracker->targetNode() ? contextTracker->isTracked( m_node ) : true; if( m_active != active ) { m_active = active; diff --git a/src/GafferUI/StandardNodeGadget.cpp b/src/GafferUI/StandardNodeGadget.cpp index 67a8c63086..d3e6609404 100644 --- a/src/GafferUI/StandardNodeGadget.cpp +++ b/src/GafferUI/StandardNodeGadget.cpp @@ -55,6 +55,7 @@ #include "Gaffer/Metadata.h" #include "Gaffer/MetadataAlgo.h" #include "Gaffer/ParallelAlgo.h" +#include "Gaffer/PlugAlgo.h" #include "Gaffer/ScriptNode.h" #include "Gaffer/StandardSet.h" #include "Gaffer/TypedObjectPlug.h" @@ -522,7 +523,7 @@ StandardNodeGadget::StandardNodeGadget( Gaffer::NodePtr node ) // can optionally use it without needing to inherit from StandardNodeGadget StandardNodeGadget::StandardNodeGadget( Gaffer::NodePtr node, bool auxiliary ) : NodeGadget( node ), - m_nodeEnabled( true ), + m_strikeThroughVisible( false ), m_labelsVisibleOnHover( true ), m_dragDestination( nullptr ), m_userColor( 0 ), @@ -659,7 +660,6 @@ StandardNodeGadget::StandardNodeGadget( Gaffer::NodePtr node, bool auxiliary ) updateUserColor(); updateMinWidth(); updatePadding(); - updateNodeEnabled(); updateIcon(); updateShape(); updateFocusGadgetVisibility(); @@ -715,7 +715,7 @@ void StandardNodeGadget::renderLayer( Layer layer, const Style *style, RenderRea { const Box3f b = bound(); - if( !m_nodeEnabled && !isSelectionRender( reason ) ) + if( m_strikeThroughVisible && !isSelectionRender( reason ) ) { /// \todo Replace renderLine() with a specific method (renderNodeStrikeThrough?) on the Style class /// so that styles can do customised drawing based on knowledge of what is being drawn. @@ -772,10 +772,27 @@ void StandardNodeGadget::setHighlighted( bool highlighted ) updateTextDimming(); } -void StandardNodeGadget::activeForFocusNode( bool active ) +void StandardNodeGadget::updateFromContextTracker( const ContextTracker *contextTracker ) { - NodeGadget::activeForFocusNode( active ); + NodeGadget::updateFromContextTracker( contextTracker ); updateTextDimming(); + if( contextTracker->isTracked( node() ) ) + { + if( auto dependencyNode = IECore::runTimeCast( node() ) ) + { + m_nodeEnabledInContextTracker = contextTracker->isEnabled( dependencyNode ); + } + else + { + m_nodeEnabledInContextTracker = true; + } + /// \todo Should we clear ErrorGadget when the context changes? + } + else + { + m_nodeEnabledInContextTracker = std::nullopt; + } + updateStrikeThroughVisibility(); } void StandardNodeGadget::updateTextDimming() @@ -1035,7 +1052,7 @@ bool StandardNodeGadget::getLabelsVisibleOnHover() const void StandardNodeGadget::plugDirtied( const Gaffer::Plug *plug ) { - updateNodeEnabled( plug ); + updateStrikeThroughVisibility( plug ); if( ErrorGadget *e = errorGadget( /* createIfMissing = */ false ) ) { e->removeError( plug ); @@ -1260,44 +1277,40 @@ void StandardNodeGadget::updatePadding() paddingRow()->setPadding( Box3f( V3f( -padding ), V3f( padding ) ) ); } -void StandardNodeGadget::updateNodeEnabled( const Gaffer::Plug *dirtiedPlug ) +void StandardNodeGadget::updateStrikeThroughVisibility( const Gaffer::Plug *dirtiedPlug ) { - DependencyNode *dependencyNode = IECore::runTimeCast( node() ); - if( !dependencyNode ) - { - return; - } - - const Gaffer::BoolPlug *enabledPlug = dependencyNode->enabledPlug(); - if( !enabledPlug ) - { - return; - } - - if( dirtiedPlug && dirtiedPlug != enabledPlug ) + bool strikeThroughVisible = false; + if( m_nodeEnabledInContextTracker ) { - return; + strikeThroughVisible = !*m_nodeEnabledInContextTracker; } - - const ValuePlug *source = enabledPlug->source(); - bool enabled = true; - if( source->direction() != Plug::Out || !IECore::runTimeCast( source->node() ) ) + else { - // Only evaluate `enabledPlug` if it won't trigger a compute. - // We don't want to hang the UI waiting, and we don't really - // know what context to perform the compute in anyway. - /// \todo We could consider doing this in the background, using - /// an upstream traversal from the focus node to determine context. - enabled = enabledPlug->getValue(); + if( auto dependencyNode = IECore::runTimeCast( node() ) ) + { + strikeThroughVisible = false; + if( auto enabledPlug = dependencyNode->enabledPlug() ) + { + if( dirtiedPlug && dirtiedPlug != enabledPlug ) + { + return; + } + // Only evaluate `enabledPlug` if it won't trigger a compute. + // We don't want to hang the UI waiting, and we don't really + // know what context to perform the compute in anyway. + if( !PlugAlgo::dependsOnCompute( enabledPlug ) ) + { + strikeThroughVisible = !enabledPlug->getValue(); + } + } + } } - if( enabled == m_nodeEnabled ) + if( strikeThroughVisible != m_strikeThroughVisible ) { - return; + m_strikeThroughVisible = strikeThroughVisible; + dirty( DirtyType::Render ); } - - m_nodeEnabled = enabled; - dirty( DirtyType::Render ); } void StandardNodeGadget::updateIcon()