diff --git a/lib/usd/ui/layerEditor/abstractCommandHook.h b/lib/usd/ui/layerEditor/abstractCommandHook.h index 0381e17275..e910ea8788 100644 --- a/lib/usd/ui/layerEditor/abstractCommandHook.h +++ b/lib/usd/ui/layerEditor/abstractCommandHook.h @@ -105,8 +105,53 @@ class AbstractCommandHook // or has an owned root virtual bool isProxyShapeSharedStage(const std::string& proxyShapePath) = 0; + // Increase the count tracking if command executions are delayed. + void increaseDelayedCommands() { _delayCount += 1; } + + // Decrease the count tracking if command executions are delayed. + void decreaseDelayedCommands() + { + _delayCount -= 1; + if (!areCommandsDelayed()) + executeDelayedCommands(); + } + + // Verify if commands are currently delayed. + bool areCommandsDelayed() const { return _delayCount > 0; } + protected: + virtual void executeDelayedCommands() = 0; + SessionState* _sessionState; + int _delayCount { 0 }; +}; + +/** + * @brief When executing multiple commands, it may sometimes be necessary to delay. + * the execution until all commands are issued. For example, when processing + * multiple elements in the slection, but the command itself might change the + * selection. + */ +class DelayAbstractCommandHook +{ +public: + DelayAbstractCommandHook(AbstractCommandHook& hook) + : _hook(hook) + { + _hook.increaseDelayedCommands(); + } + + ~DelayAbstractCommandHook() + { + try { + _hook.decreaseDelayedCommands(); + } catch (const std::exception&) { + // Ignore exceptions in destructor. + } + } + +private: + AbstractCommandHook& _hook; }; class UndoContext diff --git a/lib/usd/ui/layerEditor/layerTreeView.cpp b/lib/usd/ui/layerEditor/layerTreeView.cpp index c7d1192293..2f306432b9 100644 --- a/lib/usd/ui/layerEditor/layerTreeView.cpp +++ b/lib/usd/ui/layerEditor/layerTreeView.cpp @@ -293,6 +293,8 @@ LayerItemVector LayerTreeView::getSelectedLayerItems() const void LayerTreeView::onAddParentLayer(const QString& undoName) const { + DelayAbstractCommandHook delayed(*_model->sessionState()->commandHook()); + auto selection = getSelectedLayerItems(); CallMethodParams params; @@ -322,6 +324,8 @@ void LayerTreeView::onAddParentLayer(const QString& undoName) const void LayerTreeView::onMuteLayer(const QString& undoName) const { + DelayAbstractCommandHook delayed(*_model->sessionState()->commandHook()); + auto selection = getSelectedLayerItems(); CallMethodParams params; @@ -339,6 +343,7 @@ void LayerTreeView::onMuteLayer(const QString& undoName) const void LayerTreeView::onLockLayer(const QString& undoName) const { + DelayAbstractCommandHook delayed(*_model->sessionState()->commandHook()); auto selection = getSelectedLayerItems(); @@ -357,6 +362,8 @@ void LayerTreeView::onLockLayer(const QString& undoName) const void LayerTreeView::callMethodOnSelection(const QString& undoName, simpleLayerMethod method) { + DelayAbstractCommandHook delayed(*_model->sessionState()->commandHook()); + CallMethodParams params; auto selection = getSelectedLayerItems(); params.selection = &selection; @@ -486,6 +493,8 @@ void LayerTreeView::mouseReleaseEvent(QMouseEvent* event) void LayerTreeView::keyPressEvent(QKeyEvent* event) { + DelayAbstractCommandHook delayed(*_model->sessionState()->commandHook()); + if (event->type() == QEvent::KeyPress) { if (event->key() == Qt::Key_Delete) { CallMethodParams params; diff --git a/lib/usd/ui/layerEditor/mayaCommandHook.cpp b/lib/usd/ui/layerEditor/mayaCommandHook.cpp index 89c7de185b..d478e78bac 100644 --- a/lib/usd/ui/layerEditor/mayaCommandHook.cpp +++ b/lib/usd/ui/layerEditor/mayaCommandHook.cpp @@ -39,21 +39,6 @@ namespace { std::string quote(const std::string& string) { return STR(" \"") + string + STR("\""); } -MString executeMel(const std::string& commandString) -{ - // executes maya command with display and undo set to true so that it logs - MStringArray result; - MGlobal::executeCommand( - MString(commandString.c_str()), - result, - /*display*/ true, - /*undo*/ true); - if (result.length() > 0) - return result[0]; - else - return ""; -} - // maya doesn't support spaces in undo chunk names... MString cleanChunkName(QString name) { return quote(name.replace(" ", "_").toStdString()).c_str(); } @@ -195,8 +180,11 @@ UsdLayer MayaCommandHook::addAnonymousSubLayer(UsdLayer usdLayer, std::string ne cmd = "mayaUsdLayerEditor -edit -addAnonymous "; cmd += quote(newName); cmd += quote(usdLayer->GetIdentifier()); - std::string result = executeMel(cmd).asChar(); - return PXR_NS::SdfLayer::FindOrOpen(result); + std::string result = executeMel(cmd); + if (result.size() > 0) + return PXR_NS::SdfLayer::FindOrOpen(result); + else + return {}; } // mute or unmute the given layer @@ -207,7 +195,7 @@ void MayaCommandHook::muteSubLayer(UsdLayer usdLayer, bool muteIt) cmd += muteIt ? "1" : "0"; cmd += quote(proxyShapePath()); cmd += quote(usdLayer->GetIdentifier()); - executeMel(cmd).asChar(); + executeMel(cmd); } // lock, system-lock or unlock the given layer @@ -236,7 +224,7 @@ void MayaCommandHook::lockSubLayer(UsdLayer usdLayer, MayaUsd::LayerLockType loc cmd += quote(proxyShapePath()); cmd += quote(usdLayer->GetIdentifier()); - executeMel(cmd).asChar(); + executeMel(cmd); } // Help menu callback @@ -295,4 +283,50 @@ bool MayaCommandHook::isProxyShapeSharedStage(const std::string& proxyShapePath) return getBooleanAttributeOnProxyShape(proxyShapePath, "shareStage"); } +std::string MayaCommandHook::executeMel(const std::string& commandString) +{ + if (areCommandsDelayed()) { + _delayedCommands.push_back({ commandString, false }); + } else { + // executes maya command with display and undo set to true so that it logs + MStringArray result; + MGlobal::executeCommand( + MString(commandString.c_str()), + result, + /*display*/ true, + /*undo*/ true); + if (result.length() > 0) + return result[0].asChar(); + } + return ""; +} + +void MayaCommandHook::executePython(const std::string& commandString) +{ + if (areCommandsDelayed()) { + _delayedCommands.push_back({ commandString, true }); + } else { + MGlobal::executePythonCommand(commandString.c_str()); + } +} + +void MayaCommandHook::executeDelayedCommands() +{ + if (areCommandsDelayed()) + return; + + // In case the execution of commands add new commands, + // make a copy and clear the delayed commands. + std::vector cmds = _delayedCommands; + _delayedCommands.clear(); + + for (const auto& cmd : cmds) { + if (cmd.isPython) { + executePython(cmd.command); + } else { + executeMel(cmd.command); + } + } +} + } // namespace UsdLayerEditor diff --git a/lib/usd/ui/layerEditor/mayaCommandHook.h b/lib/usd/ui/layerEditor/mayaCommandHook.h index b55b8d2287..0530da1c93 100644 --- a/lib/usd/ui/layerEditor/mayaCommandHook.h +++ b/lib/usd/ui/layerEditor/mayaCommandHook.h @@ -19,6 +19,8 @@ #include "abstractCommandHook.h" +#include + namespace UsdLayerEditor { /** @@ -87,6 +89,25 @@ class MayaCommandHook : public AbstractCommandHook protected: std::string proxyShapePath(); + + std::string executeMel(const std::string& commandString); + void executePython(const std::string& commandString); + + void executeDelayedCommands() override; + + struct DelayedCommand + { + DelayedCommand(const std::string& cmd, bool isP) + : command(cmd) + , isPython(isP) + { + } + + std::string command; + bool isPython { false }; + }; + + std::vector _delayedCommands; }; } // namespace UsdLayerEditor