diff --git a/openspec/changes/undo-redo-scene-editing/tasks.md b/openspec/changes/undo-redo-scene-editing/tasks.md index ffc3ceb..d4cb95f 100644 --- a/openspec/changes/undo-redo-scene-editing/tasks.md +++ b/openspec/changes/undo-redo-scene-editing/tasks.md @@ -23,7 +23,7 @@ ## 4. Attribute Edit Commands - [x] 4.1 Create `src/core/commands/AttributeSetCommand.h/.cpp` — stores `std::string description`, `std::function executeFunc`, `std::function undoFunc`; captures old value before set using `UsdAttribute::Get` at the call site -- [ ] 4.2 Modify `PropertyManager::SetPropertyValue` and `SetPropertyValueInLayer` — read old value, construct `AttributeSetCommand` with closures for old/new applies, push to `CommandHistory` +- [x] 4.2 Modify `PropertyManager::SetPropertyValue` and `SetPropertyValueInLayer` — read old value, construct `AttributeSetCommand` with closures for old/new applies, push to `CommandHistory` ## 5. Layer Operation Commands diff --git a/src/core/PropertyManager.cpp b/src/core/PropertyManager.cpp index af7f009..fd35793 100644 --- a/src/core/PropertyManager.cpp +++ b/src/core/PropertyManager.cpp @@ -1,5 +1,7 @@ #include "PropertyManager.h" #include "LayerManager.h" +#include "CommandHistory.h" +#include "commands/AttributeSetCommand.h" #include "../utils/Logger.h" #include #include @@ -123,17 +125,51 @@ bool PropertyManager::SetPropertyValue(const std::string& primPath, return false; } - // If a current layer is set, create an edit context to target it - try { - if (m_currentLayer) { - UsdEditContext editCtx(m_stage, m_currentLayer); - return ApplyValue(attr, value); - } else { - return ApplyValue(attr, value); + // Capture old value for undo + PropertyValue oldValue = ExtractValue(attr); + SdfLayerHandle targetLayer = m_currentLayer ? m_currentLayer : m_stage->GetEditTarget().GetLayer(); + + // Create command with closures that capture old/new values and layer context + if (m_commandHistory) { + auto executeFunc = [this, primPath, propName, value, targetLayer]() { + UsdPrim p = GetPrim(primPath); + if (!p.IsValid()) return; + UsdAttribute a = p.GetAttribute(TfToken(propName)); + if (!a.IsValid()) return; + + UsdEditContext editCtx(m_stage, targetLayer); + ApplyValue(a, value); + }; + + auto undoFunc = [this, primPath, propName, oldValue, targetLayer]() { + UsdPrim p = GetPrim(primPath); + if (!p.IsValid()) return; + UsdAttribute a = p.GetAttribute(TfToken(propName)); + if (!a.IsValid()) return; + + UsdEditContext editCtx(m_stage, targetLayer); + ApplyValue(a, oldValue); + }; + + std::string description = "Set " + propName + " on " + primPath; + m_commandHistory->Push(std::make_unique( + description, executeFunc, undoFunc + )); + + return true; + } else { + // Fallback: apply directly without undo support + try { + if (m_currentLayer) { + UsdEditContext editCtx(m_stage, m_currentLayer); + return ApplyValue(attr, value); + } else { + return ApplyValue(attr, value); + } + } catch (const std::exception& e) { + LOG_ERROR(std::string("Failed to set property: ") + e.what()); + return false; } - } catch (const std::exception& e) { - LOG_ERROR(std::string("Failed to set property: ") + e.what()); - return false; } } @@ -147,12 +183,47 @@ bool PropertyManager::SetPropertyValueInLayer(const std::string& primPath, UsdAttribute attr = prim.GetAttribute(TfToken(propName)); if (!attr.IsValid()) return false; - try { - UsdEditContext editCtx(m_stage, layer); - return ApplyValue(attr, value); - } catch (const std::exception& e) { - LOG_ERROR(std::string("Failed to set property: ") + e.what()); - return false; + // Capture old value for undo + PropertyValue oldValue = ExtractValue(attr); + + // Create command with closures that capture old/new values and layer context + if (m_commandHistory) { + auto executeFunc = [this, primPath, propName, value, layer]() { + UsdPrim p = GetPrim(primPath); + if (!p.IsValid()) return; + UsdAttribute a = p.GetAttribute(TfToken(propName)); + if (!a.IsValid()) return; + + UsdEditContext editCtx(m_stage, layer); + ApplyValue(a, value); + }; + + auto undoFunc = [this, primPath, propName, oldValue, layer]() { + UsdPrim p = GetPrim(primPath); + if (!p.IsValid()) return; + UsdAttribute a = p.GetAttribute(TfToken(propName)); + if (!a.IsValid()) return; + + UsdEditContext editCtx(m_stage, layer); + ApplyValue(a, oldValue); + }; + + std::string layerName = layer ? LayerManager::ExtractDisplayName(layer->GetIdentifier()) : "default"; + std::string description = "Set " + propName + " on " + primPath + " in " + layerName; + m_commandHistory->Push(std::make_unique( + description, executeFunc, undoFunc + )); + + return true; + } else { + // Fallback: apply directly without undo support + try { + UsdEditContext editCtx(m_stage, layer); + return ApplyValue(attr, value); + } catch (const std::exception& e) { + LOG_ERROR(std::string("Failed to set property: ") + e.what()); + return false; + } } } diff --git a/src/core/PropertyManager.h b/src/core/PropertyManager.h index 4126555..05dafd2 100644 --- a/src/core/PropertyManager.h +++ b/src/core/PropertyManager.h @@ -13,6 +13,8 @@ PXR_NAMESPACE_USING_DIRECTIVE namespace UsdLayerManager { +class CommandHistory; + using PropertyValue = std::variant< bool, int, float, double, std::string, pxr::GfVec3f, pxr::GfVec3d @@ -35,6 +37,7 @@ public: void SetStage(UsdStageRefPtr stage); void SetCurrentLayer(const SdfLayerHandle& layer); + void SetCommandHistory(CommandHistory* history) { m_commandHistory = history; } SdfLayerHandle GetCurrentLayer() const { return m_currentLayer; } // Prim properties @@ -61,6 +64,7 @@ private: UsdStageRefPtr m_stage; SdfLayerHandle m_currentLayer; + CommandHistory* m_commandHistory = nullptr; }; } // namespace UsdLayerManager \ No newline at end of file diff --git a/src/ui/Application.cpp b/src/ui/Application.cpp index 34ecc8c..285974e 100644 --- a/src/ui/Application.cpp +++ b/src/ui/Application.cpp @@ -56,6 +56,7 @@ bool Application::Initialize(const std::string& windowTitle, int width, int heig m_stageManager = std::make_unique(); m_layerManager = std::make_unique(); m_propertyManager = std::make_unique(); + m_propertyManager->SetCommandHistory(&m_commandHistory); m_layerPanel = std::make_unique(); m_layerPanel->SetLayerManager(m_layerManager.get()); m_layerPanel->SetCommandHistory(&m_commandHistory);