Property panel: display and edit primvars:displayColor[] as color swatches

DrawVtValueWidget now handles Color-role array attributes (VtArray<GfVec3f>
and VtArray<GfVec4f>) that previously fell through to the generic [array N]
fallback.

- Single element: inline ColorEdit3/4
- Multiple elements: collapsible row with up to 5 swatch previews + [N] count;
  expanded view shows per-index ColorEdit3/4 (capped at 16 entries)
- Editing any element writes the full modified VtArray back via attr.Set()

Covers primvars:displayColor, primvars:displayOpacity[], and any other
color3f[]/color4f[] primvar.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-17 07:28:04 +08:00
parent dc0c15de70
commit 251174ddc3
+95 -1
View File
@@ -577,8 +577,9 @@ static pxr::VtValue DrawVtValueWidget(pxr::UsdAttribute& attr,
{ {
using namespace pxr; using namespace pxr;
// Color role → ColorEdit3/4 // Color role → ColorEdit3/4 (scalar and array)
if (attr.GetRoleName() == TfToken("Color")) { if (attr.GetRoleName() == TfToken("Color")) {
// ---- scalar ----
if (val.IsHolding<GfVec3f>()) { if (val.IsHolding<GfVec3f>()) {
GfVec3f c = val.UncheckedGet<GfVec3f>(); GfVec3f c = val.UncheckedGet<GfVec3f>();
if (ImGui::ColorEdit3("##col", c.data())) if (ImGui::ColorEdit3("##col", c.data()))
@@ -591,6 +592,99 @@ static pxr::VtValue DrawVtValueWidget(pxr::UsdAttribute& attr,
return VtValue(c); return VtValue(c);
return VtValue(); return VtValue();
} }
// ---- color3f[] (e.g. primvars:displayColor) ----
if (val.IsHolding<VtArray<GfVec3f>>()) {
VtArray<GfVec3f> arr = val.UncheckedGet<VtArray<GfVec3f>>();
bool changed = false;
if (arr.size() == 1) {
GfVec3f c = arr[0];
if (ImGui::ColorEdit3("##dc", c.data()))
{ arr[0] = c; changed = true; }
} else if (!arr.empty()) {
// Collapsible: arrow + swatch preview on header line
bool open = ImGui::TreeNodeEx("##dcarr",
ImGuiTreeNodeFlags_AllowOverlap | ImGuiTreeNodeFlags_SpanAvailWidth);
ImGui::SameLine(0, 6);
float sz = ImGui::GetTextLineHeight();
size_t shown = std::min(arr.size(), (size_t)5);
for (size_t i = 0; i < shown; ++i) {
if (i > 0) ImGui::SameLine(0, 2);
char sid[16]; snprintf(sid, sizeof(sid), "##sv%d", (int)i);
ImGui::ColorButton(sid,
ImVec4(arr[i][0], arr[i][1], arr[i][2], 1.f),
ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_NoTooltip,
ImVec2(sz, sz));
}
ImGui::SameLine(0, 4);
ImGui::TextDisabled("[%zu]", arr.size());
if (open) {
static constexpr size_t kEditLimit = 16;
size_t lim = std::min(arr.size(), kEditLimit);
for (size_t i = 0; i < lim; ++i) {
ImGui::PushID((int)i);
ImGui::Text("[%d]", (int)i);
ImGui::SameLine();
GfVec3f c = arr[i];
ImGui::SetNextItemWidth(-FLT_MIN);
if (ImGui::ColorEdit3("##ce", c.data()))
{ arr[i] = c; changed = true; }
ImGui::PopID();
}
if (arr.size() > kEditLimit)
ImGui::TextDisabled(" ... %zu more", arr.size() - kEditLimit);
ImGui::TreePop();
}
}
if (changed) return VtValue(arr);
return VtValue();
}
// ---- color4f[] ----
if (val.IsHolding<VtArray<GfVec4f>>()) {
VtArray<GfVec4f> arr = val.UncheckedGet<VtArray<GfVec4f>>();
bool changed = false;
if (arr.size() == 1) {
GfVec4f c = arr[0];
if (ImGui::ColorEdit4("##dc4", c.data()))
{ arr[0] = c; changed = true; }
} else if (!arr.empty()) {
bool open = ImGui::TreeNodeEx("##dcarr4",
ImGuiTreeNodeFlags_AllowOverlap | ImGuiTreeNodeFlags_SpanAvailWidth);
ImGui::SameLine(0, 6);
float sz = ImGui::GetTextLineHeight();
size_t shown = std::min(arr.size(), (size_t)5);
for (size_t i = 0; i < shown; ++i) {
if (i > 0) ImGui::SameLine(0, 2);
char sid[16]; snprintf(sid, sizeof(sid), "##sv4%d", (int)i);
ImGui::ColorButton(sid,
ImVec4(arr[i][0], arr[i][1], arr[i][2], arr[i][3]),
ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_NoTooltip,
ImVec2(sz, sz));
}
ImGui::SameLine(0, 4);
ImGui::TextDisabled("[%zu]", arr.size());
if (open) {
static constexpr size_t kEditLimit = 16;
size_t lim = std::min(arr.size(), kEditLimit);
for (size_t i = 0; i < lim; ++i) {
ImGui::PushID((int)i);
ImGui::Text("[%d]", (int)i);
ImGui::SameLine();
GfVec4f c = arr[i];
ImGui::SetNextItemWidth(-FLT_MIN);
if (ImGui::ColorEdit4("##ce4", c.data()))
{ arr[i] = c; changed = true; }
ImGui::PopID();
}
if (arr.size() > kEditLimit)
ImGui::TextDisabled(" ... %zu more", arr.size() - kEditLimit);
ImGui::TreePop();
}
}
if (changed) return VtValue(arr);
return VtValue();
}
} }
// ---- Scalar types ---- // ---- Scalar types ----