Add Collapsed pin display mode with a 3-dot state icon

Cycle each node through All -> Connected -> Collapsed -> All via a
custom-drawn icon (filled-dot count encodes how much is shown: 3/2/1).
Collapsed hides every pin on a side, linked or not, behind one
connector nub; existing links now redirect to that nub too
(EffectivePinId), so they visually converge like Hypershade's collapse
view instead of only hiding unconnected pins. Also moved the connector
nubs off the title row onto the node's bottom row. Persisted mode
switches from a bool (uiShowAllPins) to an int (uiPinDisplayMode).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-11 01:52:52 +08:00
parent c7edd145df
commit bff42082a5
4 changed files with 149 additions and 78 deletions
+7 -4
View File
@@ -138,7 +138,7 @@ ShaderGraphSnapshot MaterialManager::GetShaderGraph(const pxr::SdfPath& material
if (!materialPrim.IsValid()) return snapshot; if (!materialPrim.IsValid()) return snapshot;
static const pxr::TfToken kUiPositionKey("uiPosition"); static const pxr::TfToken kUiPositionKey("uiPosition");
static const pxr::TfToken kUiShowAllPinsKey("uiShowAllPins"); static const pxr::TfToken kUiPinDisplayModeKey("uiPinDisplayMode");
// Seed with every shader under the material, recursing into nested node // Seed with every shader under the material, recursing into nested node
// graphs (usdMtlx nests a material's nodes inside UsdShadeNodeGraph // graphs (usdMtlx nests a material's nodes inside UsdShadeNodeGraph
@@ -180,9 +180,12 @@ ShaderGraphSnapshot MaterialManager::GetShaderGraph(const pxr::SdfPath& material
node.hasAuthoredPosition = true; node.hasAuthoredPosition = true;
} }
pxr::VtValue showAllValue = child.GetCustomDataByKey(kUiShowAllPinsKey); pxr::VtValue pinModeValue = child.GetCustomDataByKey(kUiPinDisplayModeKey);
if (showAllValue.IsHolding<bool>()) if (pinModeValue.IsHolding<int>()) {
node.showAllPins = showAllValue.UncheckedGet<bool>(); int raw = pinModeValue.UncheckedGet<int>();
if (raw >= static_cast<int>(PinDisplayMode::All) && raw <= static_cast<int>(PinDisplayMode::Collapsed))
node.pinDisplayMode = static_cast<PinDisplayMode>(raw);
}
// Prefer the full Sdr-defined pin set (so unauthored pins can still be // Prefer the full Sdr-defined pin set (so unauthored pins can still be
// dragged to create a connection); fall back to authored attributes // dragged to create a connection); fall back to authored attributes
+14 -6
View File
@@ -33,6 +33,17 @@ struct ShaderPinInfo {
pxr::SdfValueTypeName typeName; pxr::SdfValueTypeName typeName;
}; };
/// Hypershade-style pin display mode for a node in the graph canvas.
/// All: every Sdr-defined pin shown (today's default). Connected: only
/// linked pins shown, the rest collapsed behind a single connectable nub per
/// side. Collapsed: every pin — linked or not — collapses behind that one
/// nub per side, so existing connections visually converge on it too.
enum class PinDisplayMode {
All = 0,
Connected = 1,
Collapsed = 2,
};
/// One UsdShadeShader prim read back from an existing material network. /// One UsdShadeShader prim read back from an existing material network.
/// inputs/outputs list the full Sdr-defined pin set (not just authored /// inputs/outputs list the full Sdr-defined pin set (not just authored
/// attributes) so unauthored pins can still be dragged to create a connection. /// attributes) so unauthored pins can still be dragged to create a connection.
@@ -48,12 +59,9 @@ struct ShaderGraphNode {
/// False when no uiPosition custom data is authored (typical for networks /// False when no uiPosition custom data is authored (typical for networks
/// referenced from .mtlx) — the editor auto-lays such nodes out instead. /// referenced from .mtlx) — the editor auto-lays such nodes out instead.
bool hasAuthoredPosition = false; bool hasAuthoredPosition = false;
/// Hypershade-style pin display mode, read from customData when the /// Pin display mode, read from customData when the "keep graph node view
/// "keep graph node view settings in USD" preference authored it; true /// settings in USD" preference authored it; All when unauthored.
/// (show every pin) when unauthored. False hides unconnected pins, PinDisplayMode pinDisplayMode = PinDisplayMode::All;
/// collapsing them into a single connectable "more pins" nub per side
/// (see MaterialEditorPanel) until a new connection reveals one by name.
bool showAllPins = true;
std::vector<ShaderPinInfo> inputs; std::vector<ShaderPinInfo> inputs;
std::vector<ShaderPinInfo> outputs; std::vector<ShaderPinInfo> outputs;
}; };
+109 -57
View File
@@ -1319,7 +1319,7 @@ void MaterialEditorPanel::SeedPinDisplayOverrides() {
const std::string key = node.path.GetString(); const std::string key = node.path.GetString();
if (m_pinDisplayOverrides.find(key) != m_pinDisplayOverrides.end()) if (m_pinDisplayOverrides.find(key) != m_pinDisplayOverrides.end())
continue; continue;
m_pinDisplayOverrides.emplace(key, PinDisplayOverride{node.showAllPins}); m_pinDisplayOverrides.emplace(key, PinDisplayOverride{node.pinDisplayMode});
} }
} }
@@ -1432,15 +1432,21 @@ void MaterialEditorPanel::RenderNodeGraphCanvas() {
// within the widest row measured across the whole node. // within the widest row measured across the whole node.
PinDisplayOverride& pinDisplay = GetPinDisplayOverride(node.path); PinDisplayOverride& pinDisplay = GetPinDisplayOverride(node.path);
// Connected-only mode collapses every unconnected pin on a side into // Connected mode collapses every unconnected pin on a side into one
// one connectable "more pins" nub on the title row (Maya Hypershade // connectable "more pins" nub (Maya Hypershade style), present only
// style) — present only while that side actually has something hidden. // while that side actually has something hidden. Collapsed mode
// collapses every pin on a side — linked or not — into that nub
// whenever the side has any pins at all (existing links redirect to
// it too; see EffectivePinId()).
bool hasHiddenInput = false, hasHiddenOutput = false; bool hasHiddenInput = false, hasHiddenOutput = false;
if (!pinDisplay.showAllPins) { if (pinDisplay.mode == PinDisplayMode::Connected) {
for (const auto& input : node.inputs) for (const auto& input : node.inputs)
if (!IsPinLinked(node.path, input.name, false)) { hasHiddenInput = true; break; } if (!IsPinLinked(node.path, input.name, false)) { hasHiddenInput = true; break; }
for (const auto& output : node.outputs) for (const auto& output : node.outputs)
if (!IsPinLinked(node.path, output.name, true)) { hasHiddenOutput = true; break; } if (!IsPinLinked(node.path, output.name, true)) { hasHiddenOutput = true; break; }
} else if (pinDisplay.mode == PinDisplayMode::Collapsed) {
hasHiddenInput = !node.inputs.empty();
hasHiddenOutput = !node.outputs.empty();
} }
const float rowSpacing = ImGui::GetStyle().ItemSpacing.x; const float rowSpacing = ImGui::GetStyle().ItemSpacing.x;
@@ -1458,35 +1464,8 @@ void MaterialEditorPanel::RenderNodeGraphCanvas() {
ImGui::PushID(node.path.GetText()); ImGui::PushID(node.path.GetText());
ImVec2 headerTop = ImGui::GetCursorScreenPos(); ImVec2 headerTop = ImGui::GetCursorScreenPos();
if (hasHiddenInput) {
// Collapsed input nub: dropping a connection here (or dragging
// one out of it) opens RenderPinPickMenu() to choose which
// hidden input it actually wires to.
uintptr_t metaInId = HashId(node.path.GetString() + ":metaIn");
m_pinIdToInfo[metaInId] = PinInfo{node.path, "", false, pxr::SdfValueTypeName(), true};
NE::BeginPin(NE::PinId(metaInId), NE::PinKind::Input);
NE::PinPivotAlignment(ImVec2(0.0f, 0.5f));
NE::PinPivotSize(ImVec2(0.0f, 0.0f));
ax::Widgets::Icon(pinIconSize, ax::Widgets::IconType::Circle, false, ImColor(180, 180, 180, 255));
NE::EndPin();
if (ImGui::IsItemHovered()) pendingTooltip = "Connect to a hidden input";
ImGui::SameLine();
}
ImGui::TextUnformatted(title.c_str()); ImGui::TextUnformatted(title.c_str());
if (hasHiddenOutput) {
ImGui::SameLine();
uintptr_t metaOutId = HashId(node.path.GetString() + ":metaOut");
m_pinIdToInfo[metaOutId] = PinInfo{node.path, "", true, pxr::SdfValueTypeName(), true};
NE::BeginPin(NE::PinId(metaOutId), NE::PinKind::Output);
NE::PinPivotAlignment(ImVec2(1.0f, 0.5f));
NE::PinPivotSize(ImVec2(0.0f, 0.0f));
ax::Widgets::Icon(pinIconSize, ax::Widgets::IconType::Circle, false, ImColor(180, 180, 180, 255));
NE::EndPin();
if (ImGui::IsItemHovered()) pendingTooltip = "Connect a hidden output";
}
// Mode toggle, drawn and hit-tested manually. ImGui's own hover // Mode toggle, drawn and hit-tested manually. ImGui's own hover
// attribution is unreliable for widgets inside the canvas (the same // attribution is unreliable for widgets inside the canvas (the same
// mis-attribution the vendored editor's FindPinAt()/m_PressedNode // mis-attribution the vendored editor's FindPinAt()/m_PressedNode
@@ -1499,12 +1478,18 @@ void MaterialEditorPanel::RenderNodeGraphCanvas() {
// captured before NE::Begin) gates out clicks landing on other panels // captured before NE::Begin) gates out clicks landing on other panels
// or popups, since the canvas-space transform is unclamped. // or popups, since the canvas-space transform is unclamped.
{ {
const char* label = pinDisplay.showAllPins ? "All" : "Conn";
const ImGuiStyle& style = ImGui::GetStyle(); const ImGuiStyle& style = ImGui::GetStyle();
const ImVec2 labelSize = ImGui::CalcTextSize(label); // 3-dot icon: filled-dot count encodes how much is visible —
// All shows all 3 dots filled, Connected 2, Collapsed 1 — so the
// icon visually "funnels down" as the node collapses further.
const float dotRadius = 3.0f;
const float dotSpacing = 9.0f;
const int dotCount = 3;
const int filledDots = 3 - static_cast<int>(pinDisplay.mode);
const ImVec2 iconSize(dotSpacing * (dotCount - 1) + dotRadius * 2.0f, dotRadius * 2.0f);
const ImVec2 btnMin = ImGui::GetCursorScreenPos(); const ImVec2 btnMin = ImGui::GetCursorScreenPos();
const ImVec2 btnMax(btnMin.x + labelSize.x + style.FramePadding.x * 2.0f, const ImVec2 btnMax(btnMin.x + iconSize.x + style.FramePadding.x * 2.0f,
btnMin.y + labelSize.y); btnMin.y + iconSize.y + style.FramePadding.y * 2.0f);
ImGui::Dummy(ImVec2(btnMax.x - btnMin.x, btnMax.y - btnMin.y)); ImGui::Dummy(ImVec2(btnMax.x - btnMin.x, btnMax.y - btnMin.y));
// IsMouseHoveringRect is pure geometry (no hover-id arbitration) // IsMouseHoveringRect is pure geometry (no hover-id arbitration)
@@ -1516,15 +1501,28 @@ void MaterialEditorPanel::RenderNodeGraphCanvas() {
: ImGuiCol_Button); : ImGuiCol_Button);
ImDrawList* dl = ImGui::GetWindowDrawList(); ImDrawList* dl = ImGui::GetWindowDrawList();
dl->AddRectFilled(btnMin, btnMax, frameColor, style.FrameRounding); dl->AddRectFilled(btnMin, btnMax, frameColor, style.FrameRounding);
dl->AddText(ImVec2(btnMin.x + style.FramePadding.x, btnMin.y),
ImGui::GetColorU32(ImGuiCol_Text), label);
const ImU32 dotColor = ImGui::GetColorU32(ImGuiCol_Text);
const ImU32 dotHollowColor = ImGui::GetColorU32(ImGuiCol_TextDisabled);
ImVec2 dotCenter(btnMin.x + style.FramePadding.x + dotRadius,
btnMin.y + style.FramePadding.y + dotRadius);
for (int i = 0; i < dotCount; ++i) {
if (i < filledDots)
dl->AddCircleFilled(dotCenter, dotRadius, dotColor);
else
dl->AddCircle(dotCenter, dotRadius, dotHollowColor, 0, 1.2f);
dotCenter.x += dotSpacing;
}
static const char* kTooltips[3] = {
"Showing all pins - click to show connected pins only",
"Showing connected pins only - click to collapse into one pin",
"Collapsed to one pin per side - click to show all pins",
};
if (btnHovered) if (btnHovered)
pendingTooltip = pinDisplay.showAllPins pendingTooltip = kTooltips[static_cast<int>(pinDisplay.mode)];
? "Showing all pins - click to show connected pins only"
: "Showing connected pins only - click to show all pins";
if (btnHovered && ImGui::IsMouseClicked(ImGuiMouseButton_Left)) if (btnHovered && ImGui::IsMouseClicked(ImGuiMouseButton_Left))
TogglePinDisplayMode(node.path); CyclePinDisplayMode(node.path);
} }
ImVec2 headerBottom = ImGui::GetItemRectMax(); ImVec2 headerBottom = ImGui::GetItemRectMax();
@@ -1581,6 +1579,38 @@ void MaterialEditorPanel::RenderNodeGraphCanvas() {
ax::Widgets::Icon(pinIconSize, ax::Widgets::IconType::Circle, linked, ImColor(GetPinColor(output.typeName))); ax::Widgets::Icon(pinIconSize, ax::Widgets::IconType::Circle, linked, ImColor(GetPinColor(output.typeName)));
NE::EndPin(); NE::EndPin();
} }
// Collapsed connector nubs on the node's bottom row (Connected or
// Collapsed mode): input nub at the left edge, output nub hugging the
// right edge. Dropping a connection on (or dragging one out of) a
// nub opens RenderPinPickMenu() to choose which pin it wires to.
if (hasHiddenInput || hasHiddenOutput) {
ImGui::Dummy(ImVec2(0.0f, 2.0f));
const float nubRowStartX = ImGui::GetCursorPosX();
if (hasHiddenInput) {
uintptr_t metaInId = HashId(node.path.GetString() + ":metaIn");
m_pinIdToInfo[metaInId] = PinInfo{node.path, "", false, pxr::SdfValueTypeName(), true};
NE::BeginPin(NE::PinId(metaInId), NE::PinKind::Input);
NE::PinPivotAlignment(ImVec2(0.0f, 0.5f));
NE::PinPivotSize(ImVec2(0.0f, 0.0f));
ax::Widgets::Icon(pinIconSize, ax::Widgets::IconType::Circle, false, ImColor(180, 180, 180, 255));
NE::EndPin();
if (ImGui::IsItemHovered()) pendingTooltip = "Connect to a hidden input";
}
if (hasHiddenOutput) {
if (hasHiddenInput) ImGui::SameLine();
ImGui::SetCursorPosX(nubRowStartX + contentWidth - pinIconSize.x);
uintptr_t metaOutId = HashId(node.path.GetString() + ":metaOut");
m_pinIdToInfo[metaOutId] = PinInfo{node.path, "", true, pxr::SdfValueTypeName(), true};
NE::BeginPin(NE::PinId(metaOutId), NE::PinKind::Output);
NE::PinPivotAlignment(ImVec2(1.0f, 0.5f));
NE::PinPivotSize(ImVec2(0.0f, 0.0f));
ax::Widgets::Icon(pinIconSize, ax::Widgets::IconType::Circle, false, ImColor(180, 180, 180, 255));
NE::EndPin();
if (ImGui::IsItemHovered()) pendingTooltip = "Connect a hidden output";
}
}
ImGui::PopID(); ImGui::PopID();
NE::EndNode(); NE::EndNode();
@@ -1601,8 +1631,9 @@ void MaterialEditorPanel::RenderNodeGraphCanvas() {
for (const auto& link : m_graph.links) { for (const auto& link : m_graph.links) {
uintptr_t linkIdValue = HashId(link.destNode.GetString() + ":" + link.destInput); uintptr_t linkIdValue = HashId(link.destNode.GetString() + ":" + link.destInput);
// The pin's own id, always populated in m_pinIdToInfo regardless of
// visibility (used here purely to color the link by the real type).
uintptr_t startPinId = HashId(link.sourceNode.GetString() + ":out:" + link.sourceOutput); uintptr_t startPinId = HashId(link.sourceNode.GetString() + ":out:" + link.sourceOutput);
uintptr_t endPinId = HashId(link.destNode.GetString() + ":in:" + link.destInput);
m_linkIdToInfo[linkIdValue] = LinkInfo{link.destNode, link.destInput}; m_linkIdToInfo[linkIdValue] = LinkInfo{link.destNode, link.destInput};
ImU32 linkColor = IM_COL32(200, 200, 200, 255); ImU32 linkColor = IM_COL32(200, 200, 200, 255);
@@ -1610,7 +1641,13 @@ void MaterialEditorPanel::RenderNodeGraphCanvas() {
if (pinIt != m_pinIdToInfo.end()) if (pinIt != m_pinIdToInfo.end())
linkColor = GetPinColor(pinIt->second.typeName); linkColor = GetPinColor(pinIt->second.typeName);
NE::Link(NE::LinkId(linkIdValue), NE::PinId(startPinId), NE::PinId(endPinId), ImColor(linkColor)); // The id actually referenced by the drawn link: redirected to a
// node's collapsed meta nub when that node is in Collapsed mode
// (the real pin isn't drawn there, so its id can't be referenced).
uintptr_t linkStartPinId = EffectivePinId(link.sourceNode, link.sourceOutput, true);
uintptr_t linkEndPinId = EffectivePinId(link.destNode, link.destInput, false);
NE::Link(NE::LinkId(linkIdValue), NE::PinId(linkStartPinId), NE::PinId(linkEndPinId), ImColor(linkColor));
} }
HandleCreateAndDelete(); HandleCreateAndDelete();
@@ -1915,17 +1952,32 @@ MaterialEditorPanel::PinDisplayOverride& MaterialEditorPanel::GetPinDisplayOverr
bool MaterialEditorPanel::IsPinVisible(const ShaderGraphNode& node, const std::string& pinName, bool isOutput) const { bool MaterialEditorPanel::IsPinVisible(const ShaderGraphNode& node, const std::string& pinName, bool isOutput) const {
auto it = m_pinDisplayOverrides.find(node.path.GetString()); auto it = m_pinDisplayOverrides.find(node.path.GetString());
if (it == m_pinDisplayOverrides.end() || it->second.showAllPins) PinDisplayMode mode = (it != m_pinDisplayOverrides.end()) ? it->second.mode : PinDisplayMode::All;
switch (mode) {
case PinDisplayMode::All: return true;
case PinDisplayMode::Connected: return IsPinLinked(node.path, pinName, isOutput);
case PinDisplayMode::Collapsed: return false;
}
return true; return true;
return IsPinLinked(node.path, pinName, isOutput);
} }
void MaterialEditorPanel::TogglePinDisplayMode(const pxr::SdfPath& nodePath) { void MaterialEditorPanel::CyclePinDisplayMode(const pxr::SdfPath& nodePath) {
PinDisplayOverride& ov = GetPinDisplayOverride(nodePath); PinDisplayOverride& ov = GetPinDisplayOverride(nodePath);
ov.showAllPins = !ov.showAllPins; switch (ov.mode) {
case PinDisplayMode::All: ov.mode = PinDisplayMode::Connected; break;
case PinDisplayMode::Connected: ov.mode = PinDisplayMode::Collapsed; break;
case PinDisplayMode::Collapsed: ov.mode = PinDisplayMode::All; break;
}
PersistPinDisplayState(nodePath); PersistPinDisplayState(nodePath);
} }
uintptr_t MaterialEditorPanel::EffectivePinId(const pxr::SdfPath& nodePath, const std::string& pinName, bool isOutput) const {
auto it = m_pinDisplayOverrides.find(nodePath.GetString());
if (it != m_pinDisplayOverrides.end() && it->second.mode == PinDisplayMode::Collapsed)
return HashId(nodePath.GetString() + (isOutput ? ":metaOut" : ":metaIn"));
return HashId(nodePath.GetString() + (isOutput ? ":out:" : ":in:") + pinName);
}
void MaterialEditorPanel::RenderPinPickMenu() { void MaterialEditorPanel::RenderPinPickMenu() {
const ShaderGraphNode* node = nullptr; const ShaderGraphNode* node = nullptr;
for (const auto& n : m_graph.nodes) for (const auto& n : m_graph.nodes)
@@ -2230,26 +2282,26 @@ void MaterialEditorPanel::PersistPinDisplayState(const pxr::SdfPath& nodePath) {
pxr::UsdPrim prim = m_stage->GetPrimAtPath(nodePath); pxr::UsdPrim prim = m_stage->GetPrimAtPath(nodePath);
if (!prim.IsValid()) return; if (!prim.IsValid()) return;
static const pxr::TfToken kShowAllKey("uiShowAllPins"); static const pxr::TfToken kPinModeKey("uiPinDisplayMode");
const bool newShowAll = GetPinDisplayOverride(nodePath).showAllPins; const int newMode = static_cast<int>(GetPinDisplayOverride(nodePath).mode);
pxr::VtValue oldShowAllVt = prim.GetCustomDataByKey(kShowAllKey); pxr::VtValue oldModeVt = prim.GetCustomDataByKey(kPinModeKey);
const bool oldShowAll = oldShowAllVt.IsHolding<bool>() ? oldShowAllVt.UncheckedGet<bool>() : true; const int oldMode = oldModeVt.IsHolding<int>() ? oldModeVt.UncheckedGet<int>() : static_cast<int>(PinDisplayMode::All);
if (oldShowAll == newShowAll) if (oldMode == newMode)
return; return;
pxr::UsdStageRefPtr stage = m_stage; pxr::UsdStageRefPtr stage = m_stage;
m_commandHistory->Push(std::make_unique<AttributeSetCommand>( m_commandHistory->Push(std::make_unique<AttributeSetCommand>(
"Change pin display for " + nodePath.GetName(), "Change pin display for " + nodePath.GetName(),
[stage, nodePath, newShowAll]() { [stage, nodePath, newMode]() {
pxr::UsdPrim p = stage->GetPrimAtPath(nodePath); pxr::UsdPrim p = stage->GetPrimAtPath(nodePath);
if (p.IsValid()) p.SetCustomDataByKey(pxr::TfToken("uiShowAllPins"), pxr::VtValue(newShowAll)); if (p.IsValid()) p.SetCustomDataByKey(pxr::TfToken("uiPinDisplayMode"), pxr::VtValue(newMode));
}, },
[stage, nodePath, oldShowAll]() { [stage, nodePath, oldMode]() {
pxr::UsdPrim p = stage->GetPrimAtPath(nodePath); pxr::UsdPrim p = stage->GetPrimAtPath(nodePath);
if (p.IsValid()) p.SetCustomDataByKey(pxr::TfToken("uiShowAllPins"), pxr::VtValue(oldShowAll)); if (p.IsValid()) p.SetCustomDataByKey(pxr::TfToken("uiPinDisplayMode"), pxr::VtValue(oldMode));
})); }));
} }
+18 -10
View File
@@ -87,7 +87,7 @@ private:
/// customData when m_keepGraphNodeViewSettingsInUsd is set. See /// customData when m_keepGraphNodeViewSettingsInUsd is set. See
/// GetPinDisplayOverride()/IsPinVisible(). /// GetPinDisplayOverride()/IsPinVisible().
struct PinDisplayOverride { struct PinDisplayOverride {
bool showAllPins = true; PinDisplayMode mode = PinDisplayMode::All;
}; };
/// A connection dragged onto/from a collapsed meta pin, awaiting the user /// A connection dragged onto/from a collapsed meta pin, awaiting the user
/// picking which actual pin on nodePath/isOutput to wire up. otherPin is /// picking which actual pin on nodePath/isOutput to wire up. otherPin is
@@ -154,22 +154,30 @@ private:
/// Seeds m_pinDisplayOverrides for any node path not already present /// Seeds m_pinDisplayOverrides for any node path not already present
/// (i.e. not touched yet this session) from the just-synced m_graph's /// (i.e. not touched yet this session) from the just-synced m_graph's
/// showAllPins (customData-backed). Never overwrites an existing entry, /// pinDisplayMode (customData-backed). Never overwrites an existing
/// so in-session toggles survive resyncs. /// entry, so in-session toggles survive resyncs.
void SeedPinDisplayOverrides(); void SeedPinDisplayOverrides();
/// Creates the entry (defaulted to "show all") on first access. /// Creates the entry (defaulted to All) on first access.
PinDisplayOverride& GetPinDisplayOverride(const pxr::SdfPath& nodePath); PinDisplayOverride& GetPinDisplayOverride(const pxr::SdfPath& nodePath);
/// True if pin should be drawn this frame: node is in "show all" mode, or /// True if pin should be drawn this frame: All mode shows everything;
/// the pin is linked (linked pins are never hidden — Link() unconditionally /// Connected mode shows only linked pins (linked pins are never hidden —
/// references pin IDs that must have been drawn). A hidden pin becomes /// Link() unconditionally references pin IDs that must have been drawn
/// visible the moment it's connected, via RenderPinPickMenu(). /// so a hidden pin becomes visible the moment it's connected, via
/// RenderPinPickMenu()); Collapsed mode hides every real pin, linked or
/// not, in favor of the single connector nub per side (see
/// EffectivePinId(), which redirects existing links to that nub).
bool IsPinVisible(const ShaderGraphNode& node, const std::string& pinName, bool isOutput) const; bool IsPinVisible(const ShaderGraphNode& node, const std::string& pinName, bool isOutput) const;
/// Flips a node between "show all" and "show connected only". /// Cycles a node's display mode: All -> Connected -> Collapsed -> All.
void TogglePinDisplayMode(const pxr::SdfPath& nodePath); void CyclePinDisplayMode(const pxr::SdfPath& nodePath);
/// Authors the node's current PinDisplayOverride into USD customData via /// Authors the node's current PinDisplayOverride into USD customData via
/// an undoable command, mirroring PersistNodePosition. No-op unless /// an undoable command, mirroring PersistNodePosition. No-op unless
/// m_keepGraphNodeViewSettingsInUsd is set. /// m_keepGraphNodeViewSettingsInUsd is set.
void PersistPinDisplayState(const pxr::SdfPath& nodePath); void PersistPinDisplayState(const pxr::SdfPath& nodePath);
/// PinId to use when drawing a link touching (nodePath, pinName,
/// isOutput): the pin's own id normally, or that node's collapsed meta
/// nub id when the node is in Collapsed mode (so existing links visually
/// converge on the nub instead of referencing an undrawn real pin).
uintptr_t EffectivePinId(const pxr::SdfPath& nodePath, const std::string& pinName, bool isOutput) const;
/// Renders the deferred "choose pin" popup for m_pendingPinPick (Maya /// Renders the deferred "choose pin" popup for m_pendingPinPick (Maya
/// Hypershade style): a connection was dragged onto/from a collapsed meta /// Hypershade style): a connection was dragged onto/from a collapsed meta
/// pin, and the user now picks which actual attribute it wires to. Must /// pin, and the user now picks which actual attribute it wires to. Must