Property panel: token[] view/edit, uniform row heights
- Add token[] attribute display: inline InputText (or Combo when allowedTokens metadata is present), collapsible [N tokens] tree for arrays with up to 32 editable entries - Force all property table rows to GetFrameHeight() minimum via TableNextRow min_row_height so text-only and widget rows match - Add AlignTextToFramePadding() before every TextUnformatted / TextDisabled / TextColored in cols 0-2 so short text sits centred in the uniform-height row rather than pinned to the top - Wrap matrix attributes in TreeNodeEx [NxN] so they collapse to a single-height row; relationship targets use inline text / collapsible tree instead of a variable-height ListBox Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+113
-22
@@ -550,6 +550,7 @@ static void DrawPropertyMiniButton(const char* label, bool authored,
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, btnColor);
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.f, 0.f, 0.f, 0.f));
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::SmallButton(label);
|
||||
ImGui::PopStyleColor(2);
|
||||
|
||||
@@ -841,19 +842,28 @@ static pxr::VtValue DrawVtValueWidget(pxr::UsdAttribute& attr,
|
||||
return VtValue();
|
||||
}
|
||||
|
||||
// ---- Matrices (multi-row, read-write) ----
|
||||
auto drawMatrix = [](const char* id, ImGuiDataType dt, void* data, int rows, int cols) -> bool {
|
||||
// ---- Matrices: collapsed single row, expand to edit ----
|
||||
// Render rows inside a TreeNode so the table row stays one line tall
|
||||
// when collapsed. Expanded rows are user-initiated so taller is fine.
|
||||
auto drawMatrix = [](const char* id, ImGuiDataType dt, void* data,
|
||||
int rows, int cols) -> bool {
|
||||
bool changed = false;
|
||||
ImGui::PushID(id);
|
||||
for (int r = 0; r < rows; ++r) {
|
||||
char rowId[8]; snprintf(rowId, sizeof(rowId), "##r%d", r);
|
||||
ImGui::InputScalarN(rowId, dt,
|
||||
static_cast<char*>(data) + r * cols * (dt == ImGuiDataType_Double ? 8 : 4),
|
||||
cols, nullptr, nullptr,
|
||||
dt == ImGuiDataType_Double ? "%.4f" : "%.4f");
|
||||
changed |= ImGui::IsItemDeactivatedAfterEdit();
|
||||
char hdr[24]; snprintf(hdr, sizeof(hdr), "[%dx%d]##%s", rows, cols, id);
|
||||
bool open = ImGui::TreeNodeEx(hdr,
|
||||
ImGuiTreeNodeFlags_AllowOverlap | ImGuiTreeNodeFlags_SpanAvailWidth);
|
||||
if (open) {
|
||||
ImGui::PushID(id);
|
||||
for (int r = 0; r < rows; ++r) {
|
||||
char rowId[8]; snprintf(rowId, sizeof(rowId), "##r%d", r);
|
||||
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||
ImGui::InputScalarN(rowId, dt,
|
||||
static_cast<char*>(data) + r * cols * (dt == ImGuiDataType_Double ? 8 : 4),
|
||||
cols, nullptr, nullptr, "%.4f");
|
||||
changed |= ImGui::IsItemDeactivatedAfterEdit();
|
||||
}
|
||||
ImGui::PopID();
|
||||
ImGui::TreePop();
|
||||
}
|
||||
ImGui::PopID();
|
||||
return changed;
|
||||
};
|
||||
|
||||
@@ -890,29 +900,92 @@ static pxr::VtValue DrawVtValueWidget(pxr::UsdAttribute& attr,
|
||||
|
||||
// ---- Token ----
|
||||
if (val.IsHolding<TfToken>()) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextUnformatted(val.UncheckedGet<TfToken>().GetText());
|
||||
return VtValue();
|
||||
}
|
||||
|
||||
// ---- String / AssetPath ----
|
||||
if (val.IsHolding<std::string>()) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextUnformatted(val.UncheckedGet<std::string>().c_str());
|
||||
return VtValue();
|
||||
}
|
||||
if (val.IsHolding<SdfAssetPath>()) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextUnformatted(val.UncheckedGet<SdfAssetPath>().GetAssetPath().c_str());
|
||||
return VtValue();
|
||||
}
|
||||
|
||||
// ---- Token array (editable inline) ----
|
||||
// ---- Token array: view + edit ----
|
||||
if (val.IsHolding<VtArray<TfToken>>()) {
|
||||
const auto& arr = val.UncheckedGet<VtArray<TfToken>>();
|
||||
ImGui::TextDisabled("[%zu tokens]", arr.size());
|
||||
VtArray<TfToken> arr = val.UncheckedGet<VtArray<TfToken>>();
|
||||
bool changed = false;
|
||||
|
||||
// Optional allowedTokens → show Combo per element instead of InputText.
|
||||
VtValue allowedMeta;
|
||||
attr.GetMetadata(TfToken("allowedTokens"), &allowedMeta);
|
||||
const VtArray<TfToken>* allowed =
|
||||
(!allowedMeta.IsEmpty() && allowedMeta.IsHolding<VtArray<TfToken>>())
|
||||
? &allowedMeta.UncheckedGet<VtArray<TfToken>>()
|
||||
: nullptr;
|
||||
|
||||
// Widget for one element (combo or free-text).
|
||||
auto drawOne = [&](int idx) {
|
||||
ImGui::PushID(idx);
|
||||
if (allowed) {
|
||||
std::string cur = arr[idx].GetString();
|
||||
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||
if (ImGui::BeginCombo("##tc", cur.c_str())) {
|
||||
for (const auto& tok : *allowed) {
|
||||
bool sel = (tok == arr[idx]);
|
||||
if (ImGui::Selectable(tok.GetText(), sel))
|
||||
{ arr[idx] = tok; changed = true; }
|
||||
if (sel) ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
} else {
|
||||
char buf[256];
|
||||
snprintf(buf, sizeof(buf), "%s", arr[idx].GetText());
|
||||
ImGui::SetNextItemWidth(-FLT_MIN);
|
||||
ImGui::InputText("##te", buf, sizeof(buf));
|
||||
if (ImGui::IsItemDeactivatedAfterEdit())
|
||||
{ arr[idx] = TfToken(buf); changed = true; }
|
||||
}
|
||||
ImGui::PopID();
|
||||
};
|
||||
|
||||
if (arr.empty()) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextDisabled("[empty]");
|
||||
} else if (arr.size() == 1) {
|
||||
drawOne(0);
|
||||
} else {
|
||||
bool open = ImGui::TreeNodeEx("##tarr",
|
||||
ImGuiTreeNodeFlags_AllowOverlap | ImGuiTreeNodeFlags_SpanAvailWidth);
|
||||
ImGui::SameLine(0, 6);
|
||||
ImGui::TextDisabled("[%zu tokens]", arr.size());
|
||||
if (open) {
|
||||
static constexpr size_t kLimit = 32;
|
||||
size_t lim = std::min(arr.size(), kLimit);
|
||||
for (size_t i = 0; i < lim; ++i) {
|
||||
ImGui::Text("[%d]", (int)i);
|
||||
ImGui::SameLine();
|
||||
drawOne((int)i);
|
||||
}
|
||||
if (arr.size() > kLimit)
|
||||
ImGui::TextDisabled(" ... %zu more", arr.size() - kLimit);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
if (changed) return VtValue(arr);
|
||||
return VtValue();
|
||||
}
|
||||
|
||||
// ---- Large arrays: just show size ----
|
||||
if (val.IsArrayValued() && val.GetArraySize() > 5) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextDisabled("[array %zu]", val.GetArraySize());
|
||||
return VtValue();
|
||||
}
|
||||
@@ -923,6 +996,7 @@ static pxr::VtValue DrawVtValueWidget(pxr::UsdAttribute& attr,
|
||||
oss << val;
|
||||
std::string s = oss.str();
|
||||
if (s.size() > 120) { s.resize(120); s += " ..."; }
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextUnformatted(s.c_str());
|
||||
}
|
||||
return VtValue();
|
||||
@@ -1056,11 +1130,12 @@ void PropertyPanel::RenderPropertiesTable(const pxr::UsdPrim& prim) {
|
||||
// No TableHeadersRow – matches usdtweak "no header" intent
|
||||
|
||||
const pxr::UsdEditTarget& editTarget = prim.GetStage()->GetEditTarget();
|
||||
const float rowH = ImGui::GetFrameHeight();
|
||||
int uid = 0;
|
||||
|
||||
// ---- Attributes (mirrors usdtweak attribute loop) ----
|
||||
for (auto& attr : prim.GetAttributes()) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_None, rowH);
|
||||
|
||||
// Col 0 – mini button "(a)"
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
@@ -1073,6 +1148,7 @@ void PropertyPanel::RenderPropertiesTable(const pxr::UsdPrim& prim) {
|
||||
|
||||
// Col 1 – display name (namespace:basename)
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextUnformatted(GetPropDisplayName(attr).c_str());
|
||||
|
||||
// Col 2 – value widget with PushItemWidth(-FLT_MIN) (label hidden)
|
||||
@@ -1109,9 +1185,12 @@ void PropertyPanel::RenderPropertiesTable(const pxr::UsdPrim& prim) {
|
||||
} else if (attr.HasAuthoredConnections()) {
|
||||
SdfPathVector conns;
|
||||
attr.GetConnections(&conns);
|
||||
if (!conns.empty())
|
||||
if (!conns.empty()) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextDisabled("-> %s", conns[0].GetString().c_str());
|
||||
}
|
||||
} else {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextDisabled("no value");
|
||||
}
|
||||
|
||||
@@ -1121,7 +1200,7 @@ void PropertyPanel::RenderPropertiesTable(const pxr::UsdPrim& prim) {
|
||||
|
||||
// ---- Relationships (mirrors usdtweak relationship loop) ----
|
||||
for (const auto& rel : prim.GetRelationships()) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_None, rowH);
|
||||
|
||||
// Col 0 – mini button "(r)"
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
@@ -1136,24 +1215,36 @@ void PropertyPanel::RenderPropertiesTable(const pxr::UsdPrim& prim) {
|
||||
ImVec4 relColor = rel.IsAuthored()
|
||||
? ImVec4(0.6f, 0.9f, 1.f, 1.f)
|
||||
: ImVec4(0.5f, 0.5f, 0.5f, 1.f);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextColored(relColor, "%s", GetPropDisplayName(rel).c_str());
|
||||
|
||||
// Col 2 – target list (mirrors DrawUsdRelationshipList in usdtweak)
|
||||
// Col 2 – target list: single row when collapsed, expand for multiple
|
||||
ImGui::TableSetColumnIndex(2);
|
||||
SdfPathVector targets;
|
||||
rel.GetTargets(&targets);
|
||||
if (targets.empty()) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextDisabled("no targets");
|
||||
} else if (targets.size() == 1) {
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextDisabled("%s", targets[0].GetString().c_str());
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("%s", targets[0].GetString().c_str());
|
||||
} else {
|
||||
ImGui::PushID(rel.GetPath().GetString().c_str());
|
||||
float listH = static_cast<float>(targets.size()) * 25.f;
|
||||
if (ImGui::BeginListBox("##rl", ImVec2(-FLT_MIN, listH))) {
|
||||
bool open = ImGui::TreeNodeEx("##rtarr",
|
||||
ImGuiTreeNodeFlags_AllowOverlap | ImGuiTreeNodeFlags_SpanAvailWidth);
|
||||
ImGui::SameLine(0, 6);
|
||||
ImGui::TextDisabled("[%zu targets]", targets.size());
|
||||
if (open) {
|
||||
for (const auto& path : targets) {
|
||||
ImGui::PushID(path.GetString().c_str());
|
||||
ImGui::TextUnformatted(path.GetString().c_str());
|
||||
ImGui::TextDisabled("%s", path.GetString().c_str());
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("%s", path.GetString().c_str());
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::EndListBox();
|
||||
ImGui::TreePop();
|
||||
}
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user