Scene Hierarchy ET switcher + Create Sublayer via Save dialog

- Add edit-target layer combo at top of Scene Hierarchy panel (always
  visible when stage loaded — session, root, sublayers); routes through
  LayerManager::SetEditTarget so StageEditor ET checkboxes stay in sync
- Replace custom Create Sublayer modal with native SaveFile dialog;
  LayerManager::CreateNewSublayer creates the file on disk, inserts it
  at the top of the sublayer stack, and activates it as edit target

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-16 03:49:35 +08:00
parent e5d7967455
commit a62764397c
7 changed files with 89 additions and 51 deletions
+18
View File
@@ -157,6 +157,24 @@ bool LayerManager::SetEditTarget(const std::string& identifier) {
return true; return true;
} }
bool LayerManager::CreateNewSublayer(const std::string& absolutePath) {
if (!m_stage) return false;
SdfLayerHandle rootLayer = m_stage->GetRootLayer();
if (!rootLayer) return false;
auto newLayer = SdfLayer::CreateNew(absolutePath);
if (!newLayer) {
LOG_ERROR("Failed to create layer file: " + absolutePath);
return false;
}
newLayer->Save();
rootLayer->InsertSubLayerPath(absolutePath, 0);
m_stage->SetEditTarget(newLayer);
Refresh();
return true;
}
bool LayerManager::IsLayerMuted(const std::string& layerIdentifier) const { bool LayerManager::IsLayerMuted(const std::string& layerIdentifier) const {
if (m_stage) { if (m_stage) {
return m_stage->IsLayerMuted(layerIdentifier); return m_stage->IsLayerMuted(layerIdentifier);
+3
View File
@@ -44,6 +44,9 @@ public:
// Edit target // Edit target
bool SetEditTarget(const std::string& identifier); bool SetEditTarget(const std::string& identifier);
// Create a new USD file on disk, insert as sublayer, and activate as edit target.
bool CreateNewSublayer(const std::string& absolutePath);
// Muting // Muting
void MuteLayer(const std::string& layerIdentifier); void MuteLayer(const std::string& layerIdentifier);
void UnmuteLayer(const std::string& layerIdentifier); void UnmuteLayer(const std::string& layerIdentifier);
+1
View File
@@ -69,6 +69,7 @@ bool Application::Initialize(const std::string& windowTitle, int width, int heig
m_sceneHierarchyPanel = std::make_unique<SceneHierarchyPanel>(); m_sceneHierarchyPanel = std::make_unique<SceneHierarchyPanel>();
m_sceneHierarchyPanel->SetPropertyManager(m_propertyManager.get()); m_sceneHierarchyPanel->SetPropertyManager(m_propertyManager.get());
m_sceneHierarchyPanel->SetCommandHistory(&m_commandHistory); m_sceneHierarchyPanel->SetCommandHistory(&m_commandHistory);
m_sceneHierarchyPanel->SetLayerManager(m_layerManager.get());
m_viewportPanel = std::make_unique<ViewportPanel>(); m_viewportPanel = std::make_unique<ViewportPanel>();
m_viewportPanel->SetCommandHistory(&m_commandHistory); m_viewportPanel->SetCommandHistory(&m_commandHistory);
m_propertyPanel = std::make_unique<PropertyPanel>(); m_propertyPanel = std::make_unique<PropertyPanel>();
+57
View File
@@ -119,6 +119,63 @@ void SceneHierarchyPanel::Render() {
return; return;
} }
// ── Edit-target layer switcher ────────────────────────────────────
{
struct LayerEntry { SdfLayerHandle layer; std::string label; };
std::vector<LayerEntry> layers;
auto sessionLayer = m_stage->GetSessionLayer();
if (sessionLayer)
layers.push_back({ sessionLayer, "Session" });
auto rootLayer = m_stage->GetRootLayer();
if (rootLayer) {
std::string name = rootLayer->IsAnonymous()
? "anonymous"
: std::filesystem::path(rootLayer->GetIdentifier()).filename().string();
layers.push_back({ rootLayer, "Root: " + name });
for (const auto& subPath : rootLayer->GetSubLayerPaths()) {
auto sub = SdfLayer::FindOrOpenRelativeToLayer(rootLayer, subPath);
if (!sub) continue;
std::string subName = sub->IsAnonymous()
? sub->GetIdentifier()
: std::filesystem::path(sub->GetIdentifier()).filename().string();
layers.push_back({ SdfLayerHandle(sub), subName });
}
}
std::string currentLabel = "\xe2\x80\x94"; // em dash
auto etLayer = m_stage->GetEditTarget().GetLayer();
if (etLayer) {
for (const auto& e : layers) {
if (e.layer && e.layer->GetIdentifier() == etLayer->GetIdentifier()) {
currentLabel = e.label;
break;
}
}
}
ImGui::SetNextItemWidth(-1.0f);
if (ImGui::BeginCombo("##layerswitch", currentLabel.c_str(),
ImGuiComboFlags_HeightRegular)) {
for (const auto& e : layers) {
bool isCurrent = (e.layer && etLayer &&
e.layer->GetIdentifier() == etLayer->GetIdentifier());
if (ImGui::Selectable(e.label.c_str(), isCurrent)) {
if (m_layerManager)
m_layerManager->SetEditTarget(e.layer->GetIdentifier());
else
m_stage->SetEditTarget(UsdEditTarget(e.layer));
}
if (isCurrent)
ImGui::SetItemDefaultFocus();
}
ImGui::EndCombo();
}
ImGui::Spacing();
}
auto paths = m_propertyManager->GetPrimPaths(); auto paths = m_propertyManager->GetPrimPaths();
if (paths.empty()) { if (paths.empty()) {
ImGui::TextDisabled("No prims in stage"); ImGui::TextDisabled("No prims in stage");
+3
View File
@@ -2,6 +2,7 @@
#include "../core/PropertyManager.h" #include "../core/PropertyManager.h"
#include "../core/CommandHistory.h" #include "../core/CommandHistory.h"
#include "../core/LayerManager.h"
#include "IconManager.h" #include "IconManager.h"
#include <pxr/usd/usd/stage.h> #include <pxr/usd/usd/stage.h>
#include <pxr/usd/sdf/path.h> #include <pxr/usd/sdf/path.h>
@@ -24,6 +25,7 @@ public:
void SetPropertyManager(PropertyManager* manager); void SetPropertyManager(PropertyManager* manager);
void SetCommandHistory(CommandHistory* history) { m_commandHistory = history; } void SetCommandHistory(CommandHistory* history) { m_commandHistory = history; }
void SetLayerManager(LayerManager* lm) { m_layerManager = lm; }
void SetStage(UsdStageRefPtr stage); void SetStage(UsdStageRefPtr stage);
void SetIconManager(IconManager* iconManager) { m_iconManager = iconManager; } void SetIconManager(IconManager* iconManager) { m_iconManager = iconManager; }
void Render(); void Render();
@@ -74,6 +76,7 @@ private:
PropertyManager* m_propertyManager; PropertyManager* m_propertyManager;
CommandHistory* m_commandHistory = nullptr; CommandHistory* m_commandHistory = nullptr;
LayerManager* m_layerManager = nullptr;
IconManager* m_iconManager = nullptr; IconManager* m_iconManager = nullptr;
UsdStageRefPtr m_stage; UsdStageRefPtr m_stage;
+7 -47
View File
@@ -7,10 +7,7 @@
namespace UsdLayerManager { namespace UsdLayerManager {
StageEditorPanel::StageEditorPanel() { StageEditorPanel::StageEditorPanel() {}
m_newLayerPath[0] = '\0';
m_newLayerName[0] = '\0';
}
StageEditorPanel::~StageEditorPanel() {} StageEditorPanel::~StageEditorPanel() {}
@@ -51,9 +48,12 @@ void StageEditorPanel::Render() {
ImGui::SameLine(); ImGui::SameLine();
if (iconBtn("##createNew", Icon::FilePlus, "+N")) { if (iconBtn("##createNew", Icon::FilePlus, "+N")) {
m_showCreateDialog = true; std::string path = FileDialog::SaveFile(
m_newLayerPath[0] = '\0'; "USD Files (*.usd;*.usda;*.usdc)\0*.usd;*.usda;*.usdc\0All Files (*.*)\0*.*\0",
strcpy_s(m_newLayerName, "new_layer.usd"); "Create New Sublayer",
"usd");
if (!path.empty() && m_layerManager)
m_layerManager->CreateNewSublayer(path);
} }
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Create New Sublayer"); if (ImGui::IsItemHovered()) ImGui::SetTooltip("Create New Sublayer");
@@ -62,7 +62,6 @@ void StageEditorPanel::Render() {
auto allLayers = m_layerManager->GetLayerStack(); auto allLayers = m_layerManager->GetLayerStack();
if (allLayers.empty()) { if (allLayers.empty()) {
ImGui::TextDisabled("No stage loaded"); ImGui::TextDisabled("No stage loaded");
if (m_showCreateDialog) ShowCreateLayerDialog();
return; return;
} }
@@ -82,7 +81,6 @@ void StageEditorPanel::Render() {
RenderSublayerList(sublayers); RenderSublayerList(sublayers);
if (m_showCreateDialog) ShowCreateLayerDialog();
} }
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
@@ -350,43 +348,5 @@ void StageEditorPanel::RenderSublayerContextMenu(int i,
ImGui::EndPopup(); ImGui::EndPopup();
} }
// ---------------------------------------------------------------------------
void StageEditorPanel::ShowCreateLayerDialog() {
ImGui::SetNextWindowSize(ImVec2(400, 150), ImGuiCond_Always);
ImGui::OpenPopup("Create Sublayer");
if (ImGui::BeginPopupModal("Create Sublayer", &m_showCreateDialog)) {
ImGui::Text("Layer Name:");
ImGui::InputText("##name", m_newLayerName, sizeof(m_newLayerName));
ImGui::Spacing();
ImGui::Text("Save Path:");
ImGui::InputText("##path", m_newLayerPath, sizeof(m_newLayerPath));
ImGui::Spacing();
if (ImGui::Button("Create", ImVec2(120, 0))) {
std::string path;
if (m_newLayerPath[0] != '\0')
path = std::string(m_newLayerPath) + "/" + m_newLayerName;
else
path = m_newLayerName;
if (!path.empty()) {
if (m_commandHistory)
m_commandHistory->Push(std::make_unique<LayerCreateCommand>(
m_layerManager, "./" + path));
else
m_layerManager->CreateSublayer("./" + path);
m_showCreateDialog = false;
}
}
ImGui::SameLine();
if (ImGui::Button("Cancel", ImVec2(120, 0)))
m_showCreateDialog = false;
ImGui::EndPopup();
}
}
} // namespace UsdLayerManager } // namespace UsdLayerManager
-4
View File
@@ -23,15 +23,11 @@ private:
void RenderFixedLayers(const std::vector<LayerInfo>& layers); void RenderFixedLayers(const std::vector<LayerInfo>& layers);
void RenderSublayerList(const std::vector<LayerInfo>& sublayers); void RenderSublayerList(const std::vector<LayerInfo>& sublayers);
void RenderSublayerContextMenu(int i, const std::vector<LayerInfo>& sublayers); void RenderSublayerContextMenu(int i, const std::vector<LayerInfo>& sublayers);
void ShowCreateLayerDialog();
LayerManager* m_layerManager = nullptr; LayerManager* m_layerManager = nullptr;
CommandHistory* m_commandHistory = nullptr; CommandHistory* m_commandHistory = nullptr;
IconManager* m_iconManager = nullptr; IconManager* m_iconManager = nullptr;
int m_selectedIdx = -1; int m_selectedIdx = -1;
bool m_showCreateDialog = false;
char m_newLayerPath[256];
char m_newLayerName[128];
}; };
} // namespace UsdLayerManager } // namespace UsdLayerManager