Stage Editor Panel — sublayer editing with ET control, mute, reorder, dirty state
Replace LayerPanel with StageEditorPanel for full sublayer composition workflow: - Edit target (ET) checkbox on root/session and each sublayer; pen icon header - Dirty indicator (*) per layer; muted layers remain visible in list - Drag-drop and Move Up/Down reordering with undo (LayerReorderCommand) - Add existing sublayer via file dialog; create new sublayer modal - Fixed LayerReorderCommand::ApplyOrder using full-stack indices on sublayer-local list (silent remove failure → duplicate insert → USD _ValidateEdit error) - Fixed muted sublayer visibility: walk GetSubLayerPaths() + FindOrOpenRelativeToLayer + m_layerRefs to keep muted layers alive and visible Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2024 Fonticons, Inc. --><path d="M64 0C28.7 0 0 28.7 0 64L0 448c0 35.3 28.7 64 64 64l256 0c35.3 0 64-28.7 64-64l0-288-128 0c-17.7 0-32-14.3-32-32L224 0 64 0zM256 0l0 128 128 0L256 0zM216 232l0 48 48 0c13.3 0 24 10.7 24 24s-10.7 24-24 24l-48 0 0 48c0 13.3-10.7 24-24 24s-24-10.7-24-24l0-48-48 0c-13.3 0-24-10.7-24-24s10.7-24 24-24l48 0 0-48c0-13.3 10.7-24 24-24s24 10.7 24 24z"/></svg>
|
||||
|
After Width: | Height: | Size: 634 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License) Copyright 2024 Fonticons, Inc. --><path d="M362.7 19.3L314.3 67.7 444.3 197.7l48.4-48.4c25-25 25-65.5 0-90.5L453.3 19.3c-25-25-65.5-25-90.5 0zm-71 71L58.6 323.5c-10.4 10.4-18 23.3-22.2 37.4L1 481.2C-1.5 489.7 .8 498.8 7 505s15.3 8.5 23.7 6.1l120.3-35.4c14.1-4.2 27-11.8 37.4-22.2L421.7 220.3 291.7 90.3z"/></svg>
|
||||
|
After Width: | Height: | Size: 552 B |
+48
-20
@@ -148,6 +148,15 @@ void LayerManager::UnmuteLayer(const std::string& layerIdentifier) {
|
||||
}
|
||||
}
|
||||
|
||||
bool LayerManager::SetEditTarget(const std::string& identifier) {
|
||||
if (!m_stage) return false;
|
||||
SdfLayerHandle layer = SdfLayer::Find(identifier);
|
||||
if (!layer) return false;
|
||||
m_stage->SetEditTarget(layer);
|
||||
Refresh();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LayerManager::IsLayerMuted(const std::string& layerIdentifier) const {
|
||||
if (m_stage) {
|
||||
return m_stage->IsLayerMuted(layerIdentifier);
|
||||
@@ -181,32 +190,51 @@ std::string LayerManager::ExtractDisplayName(const std::string& identifier) {
|
||||
|
||||
void LayerManager::BuildLayerList() {
|
||||
m_layers.clear();
|
||||
|
||||
m_layerRefs.clear();
|
||||
if (!m_stage) return;
|
||||
|
||||
|
||||
try {
|
||||
// Get the full layer stack (root + all sublayers)
|
||||
SdfLayerHandleVector layerStack = m_stage->GetLayerStack();
|
||||
|
||||
SdfLayerHandle rootLayer = m_stage->GetRootLayer();
|
||||
SdfLayerHandle rootLayer = m_stage->GetRootLayer();
|
||||
SdfLayerHandle sessionLayer = m_stage->GetSessionLayer();
|
||||
|
||||
for (const auto& layer : layerStack) {
|
||||
SdfLayerHandle editTarget = m_stage->GetEditTarget().GetLayer();
|
||||
|
||||
auto makeInfo = [&](SdfLayerHandle layer, bool isRoot, bool isSession) {
|
||||
LayerInfo info;
|
||||
info.layer = layer;
|
||||
info.identifier = layer->GetIdentifier();
|
||||
info.displayName = ExtractDisplayName(info.identifier);
|
||||
info.realPath = layer->GetRealPath();
|
||||
info.isMuted = layer->IsMuted();
|
||||
info.isAnonymous = layer->IsAnonymous();
|
||||
info.isRootLayer = (layer == rootLayer);
|
||||
info.isSessionLayer = (layer == sessionLayer);
|
||||
|
||||
m_layers.push_back(info);
|
||||
info.layer = layer;
|
||||
info.identifier = layer->GetIdentifier();
|
||||
info.displayName = ExtractDisplayName(info.identifier);
|
||||
info.realPath = layer->GetRealPath();
|
||||
info.isMuted = m_stage->IsLayerMuted(info.identifier);
|
||||
info.isAnonymous = layer->IsAnonymous();
|
||||
info.isRootLayer = isRoot;
|
||||
info.isSessionLayer = isSession;
|
||||
info.isEditTarget = (layer == editTarget);
|
||||
info.isDirty = layer->IsDirty();
|
||||
return info;
|
||||
};
|
||||
|
||||
// Root and session layers: stage always holds strong refs, handles are valid.
|
||||
if (sessionLayer) m_layers.push_back(makeInfo(sessionLayer, false, true));
|
||||
if (rootLayer) m_layers.push_back(makeInfo(rootLayer, true, false));
|
||||
|
||||
// Walk root layer's sublayer paths instead of stage->GetLayerStack().
|
||||
// GetLayerStack() drops muted layers because they don't contribute to
|
||||
// composition. FindOrOpenRelativeToLayer locates or reopens the layer
|
||||
// regardless of mute state. We store the returned RefPtr in m_layerRefs
|
||||
// so the layer object isn't garbage-collected before the next Refresh().
|
||||
if (rootLayer) {
|
||||
for (const auto& subPath : rootLayer->GetSubLayerPaths()) {
|
||||
SdfLayerRefPtr subRef =
|
||||
SdfLayer::FindOrOpenRelativeToLayer(rootLayer, subPath);
|
||||
if (subRef) {
|
||||
m_layerRefs.push_back(subRef);
|
||||
m_layers.push_back(makeInfo(SdfLayerHandle(subRef), false, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LOG_DEBUG("Built layer list with " + std::to_string(m_layers.size()) + " layers");
|
||||
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(std::string("Failed to build layer list: ") + e.what());
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ struct LayerInfo {
|
||||
bool isAnonymous;
|
||||
bool isRootLayer;
|
||||
bool isSessionLayer;
|
||||
bool isEditTarget;
|
||||
bool isDirty;
|
||||
};
|
||||
|
||||
class LayerManager {
|
||||
@@ -39,6 +41,9 @@ public:
|
||||
bool MoveSublayerUp(int index);
|
||||
bool MoveSublayerDown(int index);
|
||||
|
||||
// Edit target
|
||||
bool SetEditTarget(const std::string& identifier);
|
||||
|
||||
// Muting
|
||||
void MuteLayer(const std::string& layerIdentifier);
|
||||
void UnmuteLayer(const std::string& layerIdentifier);
|
||||
@@ -49,9 +54,11 @@ public:
|
||||
|
||||
private:
|
||||
void BuildLayerList();
|
||||
|
||||
|
||||
pxr::UsdStageRefPtr m_stage;
|
||||
std::vector<LayerInfo> m_layers;
|
||||
// Strong references keep sublayers alive even when the stage drops them after muting.
|
||||
std::vector<pxr::SdfLayerRefPtr> m_layerRefs;
|
||||
};
|
||||
|
||||
} // namespace UsdLayerManager
|
||||
@@ -46,10 +46,10 @@ LayerRemoveCommand::LayerRemoveCommand(LayerManager* mgr, int index)
|
||||
, m_description("Remove Layer")
|
||||
{
|
||||
if (!mgr) return;
|
||||
auto layers = mgr->GetLayerStack();
|
||||
if (index >= 0 && index < static_cast<int>(layers.size())) {
|
||||
m_savedPath = layers[index].identifier;
|
||||
m_description = "Remove Layer " + layers[index].displayName;
|
||||
auto sublayers = mgr->GetSublayers();
|
||||
if (index >= 0 && index < static_cast<int>(sublayers.size())) {
|
||||
m_savedPath = sublayers[index].identifier;
|
||||
m_description = "Remove Layer " + sublayers[index].displayName;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,22 +81,14 @@ void LayerReorderCommand::Undo() { ApplyOrder(m_before); }
|
||||
|
||||
void LayerReorderCommand::ApplyOrder(const std::vector<std::string>& order) {
|
||||
if (!m_mgr) return;
|
||||
// Remove all sublayers and re-insert in the desired order.
|
||||
// We only control sublayers — root and session are fixed.
|
||||
// First gather current sublayer indices (non-root, non-session).
|
||||
auto layers = m_mgr->GetLayerStack();
|
||||
|
||||
// Count sublayers and remove them from highest index down.
|
||||
std::vector<int> sublayerIndices;
|
||||
for (int i = 0; i < static_cast<int>(layers.size()); ++i) {
|
||||
if (!layers[i].isRootLayer && !layers[i].isSessionLayer)
|
||||
sublayerIndices.push_back(i);
|
||||
}
|
||||
// Remove from back to front to preserve indices.
|
||||
for (int i = static_cast<int>(sublayerIndices.size()) - 1; i >= 0; --i)
|
||||
m_mgr->RemoveSublayer(sublayerIndices[i]);
|
||||
// RemoveSublayer() operates on GetSubLayerPaths() — a sublayer-local list
|
||||
// with indices 0..N-1. GetLayerStack() prepends session and root layers, so
|
||||
// its indices are always wrong here. Use GetSublayers().size() for the count.
|
||||
int count = static_cast<int>(m_mgr->GetSublayers().size());
|
||||
for (int i = count - 1; i >= 0; --i)
|
||||
m_mgr->RemoveSublayer(i);
|
||||
|
||||
// Re-add in desired order.
|
||||
for (const auto& path : order)
|
||||
m_mgr->InsertSublayerPath(path, -1);
|
||||
}
|
||||
|
||||
@@ -57,9 +57,9 @@ bool Application::Initialize(const std::string& windowTitle, int width, int heig
|
||||
m_layerManager = std::make_unique<LayerManager>();
|
||||
m_propertyManager = std::make_unique<PropertyManager>();
|
||||
m_propertyManager->SetCommandHistory(&m_commandHistory);
|
||||
m_layerPanel = std::make_unique<LayerPanel>();
|
||||
m_layerPanel->SetLayerManager(m_layerManager.get());
|
||||
m_layerPanel->SetCommandHistory(&m_commandHistory);
|
||||
m_stageEditorPanel = std::make_unique<StageEditorPanel>();
|
||||
m_stageEditorPanel->SetLayerManager(m_layerManager.get());
|
||||
m_stageEditorPanel->SetCommandHistory(&m_commandHistory);
|
||||
m_sceneHierarchyPanel = std::make_unique<SceneHierarchyPanel>();
|
||||
m_sceneHierarchyPanel->SetPropertyManager(m_propertyManager.get());
|
||||
m_sceneHierarchyPanel->SetCommandHistory(&m_commandHistory);
|
||||
@@ -82,6 +82,7 @@ bool Application::Initialize(const std::string& windowTitle, int width, int heig
|
||||
m_sceneHierarchyPanel->SetIconManager(m_iconManager.get());
|
||||
m_viewportPanel->SetIconManager(m_iconManager.get());
|
||||
m_timelinePanel->SetIconManager(m_iconManager.get());
|
||||
m_stageEditorPanel->SetIconManager(m_iconManager.get());
|
||||
|
||||
m_sceneHierarchyPanel->SetOnPrimSelected(
|
||||
[this](const std::string& path) {
|
||||
@@ -132,7 +133,7 @@ void Application::Shutdown() {
|
||||
m_viewportPanel.reset();
|
||||
m_sceneHierarchyPanel.reset();
|
||||
m_propertyPanel.reset();
|
||||
m_layerPanel.reset();
|
||||
m_stageEditorPanel.reset();
|
||||
m_propertyManager.reset();
|
||||
m_layerManager.reset();
|
||||
|
||||
@@ -206,8 +207,8 @@ void Application::RenderUI() {
|
||||
RenderStageInfo();
|
||||
}
|
||||
|
||||
ImGui::Begin("Layer Panel", nullptr, ImGuiWindowFlags_NoCollapse);
|
||||
m_layerPanel->Render();
|
||||
ImGui::Begin("Stage Editor", nullptr, ImGuiWindowFlags_NoCollapse);
|
||||
m_stageEditorPanel->Render();
|
||||
ImGui::End();
|
||||
|
||||
m_viewportPanel->Render();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "ImGuiContext.h"
|
||||
#include "IconManager.h"
|
||||
#include "LayerPanel.h"
|
||||
#include "StageEditorPanel.h"
|
||||
#include "SceneHierarchyPanel.h"
|
||||
#include "ViewportPanel.h"
|
||||
#include "PropertyPanel.h"
|
||||
@@ -49,7 +49,7 @@ private:
|
||||
std::unique_ptr<LayerManager> m_layerManager;
|
||||
std::unique_ptr<PropertyManager> m_propertyManager;
|
||||
CommandHistory m_commandHistory;
|
||||
std::unique_ptr<LayerPanel> m_layerPanel;
|
||||
std::unique_ptr<StageEditorPanel> m_stageEditorPanel;
|
||||
std::unique_ptr<SceneHierarchyPanel> m_sceneHierarchyPanel;
|
||||
std::unique_ptr<ViewportPanel> m_viewportPanel;
|
||||
std::unique_ptr<PropertyPanel> m_propertyPanel;
|
||||
|
||||
@@ -57,6 +57,9 @@ static const char* IconFilename(Icon icon) {
|
||||
case Icon::SkipEnd: return "skip-end.svg";
|
||||
case Icon::Loop: return "loop.svg";
|
||||
case Icon::Bounce: return "bounce.svg";
|
||||
case Icon::Refresh: return "arrows-rotate.svg";
|
||||
case Icon::FilePlus: return "file-plus.svg";
|
||||
case Icon::Pen: return "pen.svg";
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
@@ -72,6 +75,7 @@ static constexpr Icon kAllIcons[] = {
|
||||
Icon::LayoutSingle, Icon::LayoutHSplit, Icon::LayoutVSplit, Icon::LayoutQuad,
|
||||
Icon::SkipBack, Icon::StepBack, Icon::PlayBack, Icon::Play, Icon::Pause,
|
||||
Icon::StepForward, Icon::SkipEnd, Icon::Loop, Icon::Bounce,
|
||||
Icon::Refresh, Icon::FilePlus, Icon::Pen,
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
@@ -50,6 +50,10 @@ enum class Icon {
|
||||
SkipEnd, // go to last frame (▶|)
|
||||
Loop, // loop toggle (↻)
|
||||
Bounce, // bounce/ping-pong (↔)
|
||||
// General actions
|
||||
Refresh, // reload / refresh
|
||||
FilePlus, // create new file
|
||||
Pen, // edit / edit target
|
||||
};
|
||||
|
||||
/// Loads SVG files from disk, rasterizes them with NanoSVG, uploads them as
|
||||
|
||||
@@ -1,253 +0,0 @@
|
||||
#include "LayerPanel.h"
|
||||
#include "../utils/Logger.h"
|
||||
#include "../core/commands/LayerCommands.h"
|
||||
#include <imgui.h>
|
||||
#include <memory>
|
||||
|
||||
namespace UsdLayerManager {
|
||||
|
||||
LayerPanel::LayerPanel()
|
||||
: m_layerManager(nullptr)
|
||||
, m_selectedLayerIndex(-1)
|
||||
, m_showCreateDialog(false) {
|
||||
m_newLayerPath[0] = '\0';
|
||||
m_newLayerName[0] = '\0';
|
||||
}
|
||||
|
||||
LayerPanel::~LayerPanel() {
|
||||
}
|
||||
|
||||
void LayerPanel::SetLayerManager(LayerManager* manager) {
|
||||
m_layerManager = manager;
|
||||
}
|
||||
|
||||
void LayerPanel::Render() {
|
||||
if (!m_layerManager) return;
|
||||
|
||||
// Header with buttons
|
||||
ImGui::Text("Layers");
|
||||
ImGui::SameLine(ImGui::GetWindowWidth() - 110);
|
||||
|
||||
if (ImGui::Button("Refresh")) {
|
||||
m_layerManager->Refresh();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Add Layer")) {
|
||||
m_showCreateDialog = true;
|
||||
m_newLayerPath[0] = '\0';
|
||||
strcpy_s(m_newLayerName, "new_layer.usd");
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
// Create layer dialog
|
||||
if (m_showCreateDialog) {
|
||||
ShowCreateLayerDialog();
|
||||
}
|
||||
|
||||
// Layer list
|
||||
auto layers = m_layerManager->GetLayerStack();
|
||||
|
||||
if (layers.empty()) {
|
||||
ImGui::TextDisabled("No layers loaded");
|
||||
return;
|
||||
}
|
||||
|
||||
// Layer table
|
||||
if (ImGui::BeginTable("LayerTable", 4,
|
||||
ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg |
|
||||
ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY)) {
|
||||
|
||||
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 20.0f);
|
||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("Muted", ImGuiTableColumnFlags_WidthFixed, 60.0f);
|
||||
ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed, 80.0f);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for (int i = 0; i < static_cast<int>(layers.size()); i++) {
|
||||
const auto& layerInfo = layers[i];
|
||||
ImGui::TableNextRow();
|
||||
|
||||
bool isSelected = (m_selectedLayerIndex == i);
|
||||
|
||||
// Selection column
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImGui::PushID(i);
|
||||
if (ImGui::Selectable("##select", isSelected, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowOverlap)) {
|
||||
m_selectedLayerIndex = i;
|
||||
}
|
||||
ImGui::PopID();
|
||||
|
||||
// Name column
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImVec4 textColor = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
if (layerInfo.isMuted) {
|
||||
textColor = ImVec4(0.5f, 0.5f, 0.5f, 1.0f);
|
||||
} else if (layerInfo.isRootLayer) {
|
||||
textColor = ImVec4(0.5f, 1.0f, 0.5f, 1.0f);
|
||||
} else if (layerInfo.isSessionLayer) {
|
||||
textColor = ImVec4(0.5f, 0.7f, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
ImGui::TextColored(textColor, "%s", layerInfo.displayName.c_str());
|
||||
|
||||
if (ImGui::IsItemHovered() && !layerInfo.realPath.empty()) {
|
||||
ImGui::SetTooltip("%s\n%s", layerInfo.identifier.c_str(), layerInfo.realPath.c_str());
|
||||
}
|
||||
|
||||
// Mute toggle
|
||||
ImGui::TableSetColumnIndex(2);
|
||||
bool muted = layerInfo.isMuted;
|
||||
ImGui::PushID(("mute_" + std::to_string(i)).c_str());
|
||||
if (ImGui::Checkbox("##muted", &muted)) {
|
||||
if (muted) {
|
||||
m_layerManager->MuteLayer(layerInfo.identifier);
|
||||
} else {
|
||||
m_layerManager->UnmuteLayer(layerInfo.identifier);
|
||||
}
|
||||
}
|
||||
ImGui::PopID();
|
||||
|
||||
// Type column
|
||||
ImGui::TableSetColumnIndex(3);
|
||||
if (layerInfo.isRootLayer) {
|
||||
ImGui::TextColored(ImVec4(0.5f, 1.0f, 0.5f, 1.0f), "Root");
|
||||
} else if (layerInfo.isSessionLayer) {
|
||||
ImGui::TextColored(ImVec4(0.5f, 0.7f, 1.0f, 1.0f), "Session");
|
||||
} else if (layerInfo.isAnonymous) {
|
||||
ImGui::Text("Anonymous");
|
||||
} else {
|
||||
ImGui::Text("Sublayer");
|
||||
}
|
||||
|
||||
// Context menu
|
||||
RenderLayerContextMenu(i);
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
void LayerPanel::RenderLayerContextMenu(int layerIndex) {
|
||||
if (ImGui::BeginPopupContextItem(("layer_ctx_" + std::to_string(layerIndex)).c_str())) {
|
||||
auto layers = m_layerManager->GetLayerStack();
|
||||
if (layerIndex < 0 || layerIndex >= static_cast<int>(layers.size())) {
|
||||
ImGui::EndPopup();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& info = layers[layerIndex];
|
||||
|
||||
if (ImGui::MenuItem(info.isMuted ? "Unmute" : "Mute")) {
|
||||
if (info.isMuted) {
|
||||
m_layerManager->UnmuteLayer(info.identifier);
|
||||
} else {
|
||||
m_layerManager->MuteLayer(info.identifier);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (!info.isRootLayer && !info.isSessionLayer) {
|
||||
if (ImGui::MenuItem("Move Up", nullptr, false, layerIndex > 0)) {
|
||||
if (m_commandHistory) {
|
||||
// Capture order before move.
|
||||
auto layers = m_layerManager->GetLayerStack();
|
||||
std::vector<std::string> before, after;
|
||||
for (auto& l : layers)
|
||||
if (!l.isRootLayer && !l.isSessionLayer)
|
||||
before.push_back(l.identifier);
|
||||
after = before;
|
||||
// Find index within sublayer-only list.
|
||||
int subIdx = -1;
|
||||
for (int i = 0; i < static_cast<int>(before.size()); ++i)
|
||||
if (before[i] == info.identifier) { subIdx = i; break; }
|
||||
if (subIdx > 0) std::swap(after[subIdx], after[subIdx - 1]);
|
||||
m_commandHistory->Push(std::make_unique<LayerReorderCommand>(
|
||||
m_layerManager, before, after));
|
||||
} else {
|
||||
m_layerManager->MoveSublayerUp(layerIndex);
|
||||
}
|
||||
}
|
||||
if (ImGui::MenuItem("Move Down", nullptr, false, layerIndex < static_cast<int>(layers.size()) - 1)) {
|
||||
if (m_commandHistory) {
|
||||
auto layersNow = m_layerManager->GetLayerStack();
|
||||
std::vector<std::string> before, after;
|
||||
for (auto& l : layersNow)
|
||||
if (!l.isRootLayer && !l.isSessionLayer)
|
||||
before.push_back(l.identifier);
|
||||
after = before;
|
||||
int subIdx = -1;
|
||||
for (int i = 0; i < static_cast<int>(before.size()); ++i)
|
||||
if (before[i] == info.identifier) { subIdx = i; break; }
|
||||
if (subIdx >= 0 && subIdx + 1 < static_cast<int>(after.size()))
|
||||
std::swap(after[subIdx], after[subIdx + 1]);
|
||||
m_commandHistory->Push(std::make_unique<LayerReorderCommand>(
|
||||
m_layerManager, before, after));
|
||||
} else {
|
||||
m_layerManager->MoveSublayerDown(layerIndex);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem("Remove")) {
|
||||
if (m_commandHistory) {
|
||||
m_commandHistory->Push(std::make_unique<LayerRemoveCommand>(
|
||||
m_layerManager, layerIndex));
|
||||
} else {
|
||||
m_layerManager->RemoveSublayer(layerIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
void LayerPanel::ShowCreateLayerDialog() {
|
||||
ImGui::SetNextWindowSize(ImVec2(400, 150), ImGuiCond_Always);
|
||||
ImGui::OpenPopup("Create New Layer");
|
||||
|
||||
if (ImGui::BeginPopupModal("Create New Layer", &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::SameLine();
|
||||
if (ImGui::Button("Browse...")) {
|
||||
// TODO: File save dialog
|
||||
}
|
||||
|
||||
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
|
||||
@@ -1,33 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "../core/LayerManager.h"
|
||||
#include "../core/CommandHistory.h"
|
||||
#include <imgui.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
namespace UsdLayerManager {
|
||||
|
||||
class LayerPanel {
|
||||
public:
|
||||
LayerPanel();
|
||||
~LayerPanel();
|
||||
|
||||
void SetLayerManager(LayerManager* manager);
|
||||
void SetCommandHistory(CommandHistory* history) { m_commandHistory = history; }
|
||||
void Render();
|
||||
|
||||
private:
|
||||
void RenderLayerContextMenu(int layerIndex);
|
||||
void ShowCreateLayerDialog();
|
||||
|
||||
LayerManager* m_layerManager;
|
||||
CommandHistory* m_commandHistory = nullptr;
|
||||
int m_selectedLayerIndex;
|
||||
bool m_showCreateDialog;
|
||||
char m_newLayerPath[256];
|
||||
char m_newLayerName[128];
|
||||
};
|
||||
|
||||
} // namespace UsdLayerManager
|
||||
@@ -0,0 +1,392 @@
|
||||
#include "StageEditorPanel.h"
|
||||
#include "../utils/FileDialog.h"
|
||||
#include "../core/commands/LayerCommands.h"
|
||||
#include <imgui.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace UsdLayerManager {
|
||||
|
||||
StageEditorPanel::StageEditorPanel() {
|
||||
m_newLayerPath[0] = '\0';
|
||||
m_newLayerName[0] = '\0';
|
||||
}
|
||||
|
||||
StageEditorPanel::~StageEditorPanel() {}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
void StageEditorPanel::Render() {
|
||||
if (!m_layerManager) return;
|
||||
|
||||
auto iconBtn = [&](const char* id, Icon icon, const char* fallback) -> bool {
|
||||
if (m_iconManager) {
|
||||
ImTextureID tex = m_iconManager->Get(icon);
|
||||
return ImGui::ImageButton(id, ImTextureRef(tex), ImVec2(18.f, 18.f));
|
||||
}
|
||||
return ImGui::Button(fallback);
|
||||
};
|
||||
|
||||
// Toolbar
|
||||
ImGui::Text("Stage Editor");
|
||||
ImGui::SameLine(ImGui::GetWindowWidth() - 100);
|
||||
|
||||
if (iconBtn("##refresh", Icon::Refresh, "R"))
|
||||
m_layerManager->Refresh();
|
||||
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Refresh");
|
||||
|
||||
ImGui::SameLine();
|
||||
if (iconBtn("##addExisting", Icon::FolderOpen, "+E")) {
|
||||
std::string path = FileDialog::OpenFile(
|
||||
"USD Files (*.usd;*.usda;*.usdc)\0*.usd;*.usda;*.usdc\0All Files (*.*)\0*.*\0",
|
||||
"Add Sublayer");
|
||||
if (!path.empty()) {
|
||||
if (m_commandHistory)
|
||||
m_commandHistory->Push(
|
||||
std::make_unique<LayerCreateCommand>(m_layerManager, path));
|
||||
else
|
||||
m_layerManager->CreateSublayer(path);
|
||||
}
|
||||
}
|
||||
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Add Existing Sublayer");
|
||||
|
||||
ImGui::SameLine();
|
||||
if (iconBtn("##createNew", Icon::FilePlus, "+N")) {
|
||||
m_showCreateDialog = true;
|
||||
m_newLayerPath[0] = '\0';
|
||||
strcpy_s(m_newLayerName, "new_layer.usd");
|
||||
}
|
||||
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Create New Sublayer");
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
auto allLayers = m_layerManager->GetLayerStack();
|
||||
if (allLayers.empty()) {
|
||||
ImGui::TextDisabled("No stage loaded");
|
||||
if (m_showCreateDialog) ShowCreateLayerDialog();
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<LayerInfo> fixed, sublayers;
|
||||
for (const auto& li : allLayers) {
|
||||
if (li.isRootLayer || li.isSessionLayer)
|
||||
fixed.push_back(li);
|
||||
else
|
||||
sublayers.push_back(li);
|
||||
}
|
||||
|
||||
RenderFixedLayers(fixed);
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::TextDisabled("Sublayers");
|
||||
ImGui::Separator();
|
||||
|
||||
RenderSublayerList(sublayers);
|
||||
|
||||
if (m_showCreateDialog) ShowCreateLayerDialog();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
void StageEditorPanel::RenderFixedLayers(const std::vector<LayerInfo>& layers) {
|
||||
if (layers.empty()) return;
|
||||
|
||||
if (!ImGui::BeginTable("FixedLayerTable", 4,
|
||||
ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersInnerV |
|
||||
ImGuiTableFlags_SizingFixedFit))
|
||||
return;
|
||||
|
||||
ImGui::TableSetupColumn("##dirty", ImGuiTableColumnFlags_WidthFixed, 14.0f);
|
||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed, 70.0f);
|
||||
ImGui::TableSetupColumn("##et", ImGuiTableColumnFlags_WidthFixed, 28.0f);
|
||||
|
||||
for (const auto& li : layers) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::PushID(li.identifier.c_str());
|
||||
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
if (li.isDirty)
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.6f, 0.2f, 1.0f), "*");
|
||||
else
|
||||
ImGui::TextDisabled(" ");
|
||||
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
ImVec4 color = li.isRootLayer
|
||||
? ImVec4(0.5f, 1.0f, 0.5f, 1.0f)
|
||||
: ImVec4(0.5f, 0.7f, 1.0f, 1.0f);
|
||||
ImGui::TextColored(color, "%s", li.displayName.c_str());
|
||||
if (ImGui::IsItemHovered() && !li.realPath.empty())
|
||||
ImGui::SetTooltip("%s\n%s", li.identifier.c_str(), li.realPath.c_str());
|
||||
|
||||
ImGui::TableSetColumnIndex(2);
|
||||
if (li.isRootLayer)
|
||||
ImGui::TextColored(ImVec4(0.5f, 1.0f, 0.5f, 1.0f), "Root");
|
||||
else
|
||||
ImGui::TextColored(ImVec4(0.5f, 0.7f, 1.0f, 1.0f), "Session");
|
||||
|
||||
ImGui::TableSetColumnIndex(3);
|
||||
{
|
||||
bool isET = li.isEditTarget;
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2.0f, 1.0f));
|
||||
if (ImGui::Checkbox("##et", &isET) && isET)
|
||||
m_layerManager->SetEditTarget(li.identifier);
|
||||
ImGui::PopStyleVar();
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip(li.isEditTarget ? "Edit target (active)" : "Set as edit target");
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
void StageEditorPanel::RenderSublayerList(const std::vector<LayerInfo>& sublayers) {
|
||||
if (sublayers.empty()) {
|
||||
ImGui::TextDisabled(" No sublayers");
|
||||
return;
|
||||
}
|
||||
|
||||
float tableHeight = ImGui::GetContentRegionAvail().y;
|
||||
|
||||
if (!ImGui::BeginTable("SublayerTable", 5,
|
||||
ImGuiTableFlags_RowBg | ImGuiTableFlags_BordersInnerV |
|
||||
ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollY,
|
||||
ImVec2(0, tableHeight)))
|
||||
return;
|
||||
|
||||
ImGui::TableSetupScrollFreeze(0, 1);
|
||||
ImGui::TableSetupColumn("##drag", ImGuiTableColumnFlags_WidthFixed, 14.0f);
|
||||
ImGui::TableSetupColumn("##dirty", ImGuiTableColumnFlags_WidthFixed, 14.0f);
|
||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("##et", ImGuiTableColumnFlags_WidthFixed, 28.0f);
|
||||
ImGui::TableSetupColumn("Muted", ImGuiTableColumnFlags_WidthFixed, 55.0f);
|
||||
|
||||
// Custom header row: pen icon for the ET column, text for the rest.
|
||||
ImGui::TableNextRow(ImGuiTableRowFlags_Headers);
|
||||
ImGui::TableSetColumnIndex(2); ImGui::TableHeader("Name");
|
||||
ImGui::TableSetColumnIndex(3);
|
||||
{
|
||||
float cellW = ImGui::GetContentRegionAvail().x;
|
||||
float iconW = 14.0f;
|
||||
float off = (cellW - iconW) * 0.5f;
|
||||
if (off > 0.0f) ImGui::SetCursorPosX(ImGui::GetCursorPosX() + off);
|
||||
if (m_iconManager)
|
||||
ImGui::Image(ImTextureRef(m_iconManager->Get(Icon::Pen)), ImVec2(iconW, iconW));
|
||||
else
|
||||
ImGui::TextUnformatted("ET");
|
||||
if (ImGui::IsItemHovered()) ImGui::SetTooltip("Edit Target");
|
||||
}
|
||||
ImGui::TableSetColumnIndex(4); ImGui::TableHeader("Muted");
|
||||
|
||||
for (int i = 0; i < static_cast<int>(sublayers.size()); i++) {
|
||||
const auto& li = sublayers[i];
|
||||
ImGui::TableNextRow();
|
||||
ImGui::PushID(i);
|
||||
|
||||
// Col 0: invisible span-all Selectable for row selection / drag / context menu.
|
||||
ImGui::TableSetColumnIndex(0);
|
||||
ImVec2 rowStart = ImGui::GetCursorScreenPos(); // save before Selectable moves cursor
|
||||
|
||||
bool isSelected = (m_selectedIdx == i);
|
||||
if (ImGui::Selectable("##row", isSelected,
|
||||
ImGuiSelectableFlags_SpanAllColumns |
|
||||
ImGuiSelectableFlags_AllowOverlap,
|
||||
ImVec2(0, 0)))
|
||||
m_selectedIdx = i;
|
||||
|
||||
// Context menu: must come immediately after the span-all Selectable so
|
||||
// BeginPopupContextItem sees it as the "last item" and responds to
|
||||
// right-click anywhere in the row.
|
||||
RenderSublayerContextMenu(i, sublayers);
|
||||
|
||||
// Drag source: also attached to the Selectable (left-button drag).
|
||||
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_SourceAllowNullID)) {
|
||||
ImGui::SetDragDropPayload("SUBLAYER_IDX", &i, sizeof(int));
|
||||
ImGui::Text("%s", li.displayName.c_str());
|
||||
ImGui::EndDragDropSource();
|
||||
}
|
||||
|
||||
// Drop target on the Selectable.
|
||||
if (ImGui::BeginDragDropTarget()) {
|
||||
if (const ImGuiPayload* payload =
|
||||
ImGui::AcceptDragDropPayload("SUBLAYER_IDX")) {
|
||||
int src = *static_cast<const int*>(payload->Data);
|
||||
int dst = i;
|
||||
if (src != dst) {
|
||||
std::vector<std::string> before, after;
|
||||
for (const auto& sl : sublayers) before.push_back(sl.identifier);
|
||||
after = before;
|
||||
std::string moved = after[src];
|
||||
after.erase(after.begin() + src);
|
||||
after.insert(after.begin() + dst, moved);
|
||||
if (m_commandHistory) {
|
||||
m_commandHistory->Push(std::make_unique<LayerReorderCommand>(
|
||||
m_layerManager, before, after));
|
||||
} else {
|
||||
LayerReorderCommand cmd(m_layerManager, before, after);
|
||||
static_cast<ICommand&>(cmd).Execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndDragDropTarget();
|
||||
}
|
||||
|
||||
// "=" drag handle: overlay at col 0 using the saved screen position so it
|
||||
// sits inside the row, not below it (TableSetColumnIndex only resets X).
|
||||
ImGui::SetCursorScreenPos(rowStart);
|
||||
ImGui::TextDisabled("=");
|
||||
|
||||
// Col 1: dirty indicator
|
||||
ImGui::TableSetColumnIndex(1);
|
||||
if (li.isDirty)
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.6f, 0.2f, 1.0f), "*");
|
||||
else
|
||||
ImGui::TextDisabled(" ");
|
||||
|
||||
// Col 2: layer name
|
||||
ImGui::TableSetColumnIndex(2);
|
||||
ImVec4 nameColor = li.isMuted
|
||||
? ImVec4(0.5f, 0.5f, 0.5f, 1.0f)
|
||||
: ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
ImGui::TextColored(nameColor, "%s", li.displayName.c_str());
|
||||
if (ImGui::IsItemHovered() && !li.realPath.empty())
|
||||
ImGui::SetTooltip("%s\n%s", li.identifier.c_str(), li.realPath.c_str());
|
||||
|
||||
// Col 3: edit target checkbox — checking sets this layer as edit target;
|
||||
// unchecking does nothing (there is always an active edit target).
|
||||
ImGui::TableSetColumnIndex(3);
|
||||
{
|
||||
bool isET = li.isEditTarget;
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2.0f, 1.0f));
|
||||
if (ImGui::Checkbox("##et", &isET) && isET)
|
||||
m_layerManager->SetEditTarget(li.identifier);
|
||||
ImGui::PopStyleVar();
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip(li.isEditTarget ? "Edit target (active)" : "Set as edit target");
|
||||
}
|
||||
|
||||
// Col 4: mute checkbox
|
||||
ImGui::TableSetColumnIndex(4);
|
||||
{
|
||||
bool muted = li.isMuted;
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2.0f, 1.0f));
|
||||
if (ImGui::Checkbox("##muted", &muted)) {
|
||||
if (muted) m_layerManager->MuteLayer(li.identifier);
|
||||
else m_layerManager->UnmuteLayer(li.identifier);
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
void StageEditorPanel::RenderSublayerContextMenu(int i,
|
||||
const std::vector<LayerInfo>& sublayers) {
|
||||
if (!ImGui::BeginPopupContextItem(("ctx_" + std::to_string(i)).c_str()))
|
||||
return;
|
||||
|
||||
const auto& li = sublayers[i];
|
||||
|
||||
if (ImGui::MenuItem(li.isMuted ? "Unmute" : "Mute")) {
|
||||
if (li.isMuted) m_layerManager->UnmuteLayer(li.identifier);
|
||||
else m_layerManager->MuteLayer(li.identifier);
|
||||
}
|
||||
|
||||
if (!li.isEditTarget && ImGui::MenuItem("Set as Edit Target"))
|
||||
m_layerManager->SetEditTarget(li.identifier);
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
bool canUp = (i > 0);
|
||||
bool canDown = (i < static_cast<int>(sublayers.size()) - 1);
|
||||
|
||||
auto buildBeforeAfter = [&](int a, int b,
|
||||
std::vector<std::string>& before,
|
||||
std::vector<std::string>& after) {
|
||||
for (const auto& sl : sublayers) before.push_back(sl.identifier);
|
||||
after = before;
|
||||
std::swap(after[a], after[b]);
|
||||
};
|
||||
|
||||
if (ImGui::MenuItem("Move Up", nullptr, false, canUp)) {
|
||||
std::vector<std::string> before, after;
|
||||
buildBeforeAfter(i, i - 1, before, after);
|
||||
if (m_commandHistory)
|
||||
m_commandHistory->Push(
|
||||
std::make_unique<LayerReorderCommand>(m_layerManager, before, after));
|
||||
else {
|
||||
LayerReorderCommand cmd(m_layerManager, before, after);
|
||||
static_cast<ICommand&>(cmd).Execute();
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem("Move Down", nullptr, false, canDown)) {
|
||||
std::vector<std::string> before, after;
|
||||
buildBeforeAfter(i, i + 1, before, after);
|
||||
if (m_commandHistory)
|
||||
m_commandHistory->Push(
|
||||
std::make_unique<LayerReorderCommand>(m_layerManager, before, after));
|
||||
else {
|
||||
LayerReorderCommand cmd(m_layerManager, before, after);
|
||||
static_cast<ICommand&>(cmd).Execute();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::MenuItem("Remove")) {
|
||||
if (m_commandHistory)
|
||||
m_commandHistory->Push(
|
||||
std::make_unique<LayerRemoveCommand>(m_layerManager, i));
|
||||
else
|
||||
m_layerManager->RemoveSublayer(i);
|
||||
}
|
||||
|
||||
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
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "../core/LayerManager.h"
|
||||
#include "../core/CommandHistory.h"
|
||||
#include "IconManager.h"
|
||||
#include <imgui.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace UsdLayerManager {
|
||||
|
||||
class StageEditorPanel {
|
||||
public:
|
||||
StageEditorPanel();
|
||||
~StageEditorPanel();
|
||||
|
||||
void SetLayerManager(LayerManager* manager) { m_layerManager = manager; }
|
||||
void SetCommandHistory(CommandHistory* history) { m_commandHistory = history; }
|
||||
void SetIconManager(IconManager* icons) { m_iconManager = icons; }
|
||||
void Render();
|
||||
|
||||
private:
|
||||
void RenderFixedLayers(const std::vector<LayerInfo>& layers);
|
||||
void RenderSublayerList(const std::vector<LayerInfo>& sublayers);
|
||||
void RenderSublayerContextMenu(int i, const std::vector<LayerInfo>& sublayers);
|
||||
void ShowCreateLayerDialog();
|
||||
|
||||
LayerManager* m_layerManager = nullptr;
|
||||
CommandHistory* m_commandHistory = nullptr;
|
||||
IconManager* m_iconManager = nullptr;
|
||||
int m_selectedIdx = -1;
|
||||
bool m_showCreateDialog = false;
|
||||
char m_newLayerPath[256];
|
||||
char m_newLayerName[128];
|
||||
};
|
||||
|
||||
} // namespace UsdLayerManager
|
||||
Reference in New Issue
Block a user