From 8f8210a7bfa1f06067da4ba7fb41ec504d51f6a6 Mon Sep 17 00:00:00 2001 From: Pierre Baillargeon Date: Tue, 12 Dec 2023 10:27:29 -0500 Subject: [PATCH 1/2] EMSUSD-65 use wait cursor for pyload commands - Add a USD UFE long-duration command to wrap other commands that may take a long time to run. - Add two DCC-specific functions to start and stop the wait cursor. - Add start/stop helper functions. - Add a wait cursor class to automatically start and top the wait cursor. - Wrap the load and unload payload commands in a long-duration command. - Adjust existing commands that used a wait cursor to use the global helper class instead. --- lib/mayaUsd/ufe/Global.cpp | 7 +++ lib/mayaUsd/ufe/MayaUsdContextOps.cpp | 6 -- lib/usdUfe/ufe/CMakeLists.txt | 2 + lib/usdUfe/ufe/Global.cpp | 1 + lib/usdUfe/ufe/Global.h | 4 ++ lib/usdUfe/ufe/UsdContextOps.cpp | 8 ++- lib/usdUfe/ufe/UsdUndoLongDurationCommand.cpp | 61 +++++++++++++++++++ lib/usdUfe/ufe/UsdUndoLongDurationCommand.h | 57 +++++++++++++++++ lib/usdUfe/ufe/Utils.cpp | 32 ++++++++++ lib/usdUfe/ufe/Utils.h | 20 ++++++ 10 files changed, 189 insertions(+), 9 deletions(-) create mode 100644 lib/usdUfe/ufe/UsdUndoLongDurationCommand.cpp create mode 100644 lib/usdUfe/ufe/UsdUndoLongDurationCommand.h diff --git a/lib/mayaUsd/ufe/Global.cpp b/lib/mayaUsd/ufe/Global.cpp index b599e5c6f3..24c85084f9 100644 --- a/lib/mayaUsd/ufe/Global.cpp +++ b/lib/mayaUsd/ufe/Global.cpp @@ -78,6 +78,7 @@ #include #include +#include #include #include #include @@ -107,6 +108,10 @@ MCallbackId gExitingCbId = 0; // Subject singleton for observation of all USD stages. MayaUsd::ufe::MayaStagesSubject::RefPtr g_StagesSubject; +void mayaStartWaitCursor() { MGlobal::executeCommand("waitCursor -state 1"); } + +void mayaStopWaitCursor() { MGlobal::executeCommand("waitCursor -state 0"); } + } // namespace namespace MAYAUSD_NS_DEF { @@ -165,6 +170,8 @@ MStatus initialize() dccFunctions.isAttributeLockedFn = MayaUsd::Editability::isAttributeLocked; dccFunctions.saveStageLoadRulesFn = MayaUsd::MayaUsdProxyShapeStageExtraData::saveLoadRules; dccFunctions.uniqueChildNameFn = MayaUsd::ufe::uniqueChildNameMayaStandard; + dccFunctions.startWaitCursorFn = mayaStartWaitCursor; + dccFunctions.stopWaitCursorFn = mayaStopWaitCursor; // Replace the Maya hierarchy handler with ours. auto& runTimeMgr = Ufe::RunTimeMgr::instance(); diff --git a/lib/mayaUsd/ufe/MayaUsdContextOps.cpp b/lib/mayaUsd/ufe/MayaUsdContextOps.cpp index 2dab001bfd..1fbc10de72 100644 --- a/lib/mayaUsd/ufe/MayaUsdContextOps.cpp +++ b/lib/mayaUsd/ufe/MayaUsdContextOps.cpp @@ -102,12 +102,6 @@ const constexpr char kClearAllRefsOrPayloadsLabel[] = "Clear All USD References const constexpr char kClearAllRefsOrPayloadsItem[] = "ClearAllReferencesOrPayloads"; //! \brief Change the cursor to wait state on construction and restore it on destruction. -struct WaitCursor -{ - WaitCursor() { MGlobal::executeCommand("waitCursor -state 1"); } - ~WaitCursor() { MGlobal::executeCommand("waitCursor -state 0"); } -}; - #ifdef UFE_V3_FEATURES_AVAILABLE //! \brief Create a working Material and select it: class InsertChildAndSelectCommand : public Ufe::CompositeUndoableCommand diff --git a/lib/usdUfe/ufe/CMakeLists.txt b/lib/usdUfe/ufe/CMakeLists.txt index 833315776e..db91e37f9a 100644 --- a/lib/usdUfe/ufe/CMakeLists.txt +++ b/lib/usdUfe/ufe/CMakeLists.txt @@ -26,6 +26,7 @@ target_sources(${PROJECT_NAME} UsdUndoClearReferencesCommand.cpp UsdUndoCreateGroupCommand.cpp UsdUndoInsertChildCommand.cpp + UsdUndoLongDurationCommand.cpp UsdUndoPayloadCommand.cpp UsdUndoReorderCommand.cpp UsdUndoSelectAfterCommand.cpp @@ -78,6 +79,7 @@ set(HEADERS UsdUndoClearReferencesCommand.h UsdUndoCreateGroupCommand.h UsdUndoInsertChildCommand.h + UsdUndoLongDurationCommand.h UsdUndoPayloadCommand.h UsdUndoReorderCommand.h UsdUndoSelectAfterCommand.h diff --git a/lib/usdUfe/ufe/Global.cpp b/lib/usdUfe/ufe/Global.cpp index ea84d8a636..15dc170ef0 100644 --- a/lib/usdUfe/ufe/Global.cpp +++ b/lib/usdUfe/ufe/Global.cpp @@ -69,6 +69,7 @@ Ufe::Rtid initialize( UsdUfe::setStagePathAccessorFn(dccFunctions.stagePathAccessorFn); UsdUfe::setUfePathToPrimFn(dccFunctions.ufePathToPrimFn); UsdUfe::setTimeAccessorFn(dccFunctions.timeAccessorFn); + UsdUfe::setWaitCursorFns(dccFunctions.startWaitCursorFn, dccFunctions.stopWaitCursorFn); // Optional DCC specific functions. if (dccFunctions.isAttributeLockedFn) diff --git a/lib/usdUfe/ufe/Global.h b/lib/usdUfe/ufe/Global.h index c634a85ea0..a194b7eb57 100644 --- a/lib/usdUfe/ufe/Global.h +++ b/lib/usdUfe/ufe/Global.h @@ -48,6 +48,10 @@ struct USDUFE_PUBLIC DCCFunctions SaveStageLoadRulesFn saveStageLoadRulesFn = nullptr; IsRootChildFn isRootChildFn = nullptr; UniqueChildNameFn uniqueChildNameFn = nullptr; + + // Optional: nothing will be done if no function is supplied. + WaitCursorFn startWaitCursorFn = nullptr; + WaitCursorFn stopWaitCursorFn = nullptr; }; /*! Ufe runtime handlers used to initialize the plugin. diff --git a/lib/usdUfe/ufe/UsdContextOps.cpp b/lib/usdUfe/ufe/UsdContextOps.cpp index a5344e5b5c..5a1af0287e 100644 --- a/lib/usdUfe/ufe/UsdContextOps.cpp +++ b/lib/usdUfe/ufe/UsdContextOps.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -542,10 +543,11 @@ Ufe::UndoableCommand::Ptr UsdContextOps::doOpCmd(const ItemPath& itemPath) const UsdLoadPolicy policy = (itemPath[0u] == kUSDLoadWithDescendantsItem) ? UsdLoadWithDescendants : UsdLoadWithoutDescendants; - - return std::make_shared(prim(), policy); + return UsdUndoLongDurationCommand::create( + { std::make_shared(prim(), policy) }); } else if (itemPath[0u] == kUSDUnloadItem) { - return std::make_shared(prim()); + return UsdUndoLongDurationCommand::create( + { std::make_shared(prim()) }); } else if (itemPath[0] == kUSDVariantSetsItem) { // Operation is to set a variant in a variant set. Need both the // variant set and the variant as arguments to the operation. diff --git a/lib/usdUfe/ufe/UsdUndoLongDurationCommand.cpp b/lib/usdUfe/ufe/UsdUndoLongDurationCommand.cpp new file mode 100644 index 0000000000..d1e957536e --- /dev/null +++ b/lib/usdUfe/ufe/UsdUndoLongDurationCommand.cpp @@ -0,0 +1,61 @@ +// +// Copyright 2023 Autodesk +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#include "UsdUndoLongDurationCommand.h" + +#include + +namespace USDUFE_NS_DEF { + +/* static */ std::shared_ptr +UsdUndoLongDurationCommand::create(std::initializer_list undoableCommands) +{ + return std::make_shared(undoableCommands); +} + +UsdUndoLongDurationCommand::UsdUndoLongDurationCommand() = default; + +UsdUndoLongDurationCommand::UsdUndoLongDurationCommand(std::initializer_list undoableCommands) + : Parent(undoableCommands) +{ +} + +UsdUndoLongDurationCommand::UsdUndoLongDurationCommand(const std::list& undoableCommands) + : Parent(undoableCommands) +{ +} + +UsdUndoLongDurationCommand::~UsdUndoLongDurationCommand() = default; + +void UsdUndoLongDurationCommand::execute() +{ + WaitCursor waitCursor; + Parent::execute(); +} + +void UsdUndoLongDurationCommand::undo() +{ + WaitCursor waitCursor; + Parent::undo(); +} + +void UsdUndoLongDurationCommand::redo() +{ + WaitCursor waitCursor; + Parent::redo(); +} + +}; // namespace USDUFE_NS_DEF diff --git a/lib/usdUfe/ufe/UsdUndoLongDurationCommand.h b/lib/usdUfe/ufe/UsdUndoLongDurationCommand.h new file mode 100644 index 0000000000..99fb973164 --- /dev/null +++ b/lib/usdUfe/ufe/UsdUndoLongDurationCommand.h @@ -0,0 +1,57 @@ +// +// Copyright 2023 Autodesk +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +#pragma once + +#include + +#include + +namespace USDUFE_NS_DEF { + +//! \brief UsdUndoLongDurationCommand +// +// A composite command that wraps all sub-commands within a user-visible wait cursor. +class USDUFE_PUBLIC UsdUndoLongDurationCommand : public Ufe::CompositeUndoableCommand +{ +public: + using Parent = Ufe::CompositeUndoableCommand; + + //! Create the long-duration composite command and append the argument commands to it. + //! \return Pointer to the long-duration composite undoable command. + static std::shared_ptr + create(std::initializer_list undoableCommands); + + //@{ + //! Constructors. + UsdUndoLongDurationCommand(); + UsdUndoLongDurationCommand(std::initializer_list undoableCommands); + UsdUndoLongDurationCommand(const std::list& undoableCommands); + + //@} + //! Destructor. + ~UsdUndoLongDurationCommand() override; + + //! Calls execute() on each command in the list, in forward order. + void execute() override; + + //! Calls undo() on each command in the list, in reverse order. + void undo() override; + + //! Calls redo() on each command in the list, in forward order. + void redo() override; +}; + +} // namespace USDUFE_NS_DEF diff --git a/lib/usdUfe/ufe/Utils.cpp b/lib/usdUfe/ufe/Utils.cpp index da7ec515cf..3b1f3a103f 100644 --- a/lib/usdUfe/ufe/Utils.cpp +++ b/lib/usdUfe/ufe/Utils.cpp @@ -87,6 +87,8 @@ uint32_t findLayerIndex(const UsdPrim& prim, const SdfLayerHandle& layer) return position; } +int gWaitCursorCount = 0; + UsdUfe::StageAccessorFn gStageAccessorFn = nullptr; UsdUfe::StagePathAccessorFn gStagePathAccessorFn = nullptr; UsdUfe::UfePathToPrimFn gUfePathToPrimFn = nullptr; @@ -95,6 +97,8 @@ UsdUfe::IsAttributeLockedFn gIsAttributeLockedFn = nullptr; UsdUfe::SaveStageLoadRulesFn gSaveStageLoadRulesFn = nullptr; UsdUfe::IsRootChildFn gIsRootChildFn = nullptr; UsdUfe::UniqueChildNameFn gUniqueChildNameFn = nullptr; +UsdUfe::WaitCursorFn gStartWaitCursorFn = nullptr; +UsdUfe::WaitCursorFn gStopWaitCursorFn = nullptr; } // anonymous namespace @@ -914,4 +918,32 @@ Ufe::Value vtValueToUfeValue(const PXR_NS::VtValue& vtValue) } #endif +void setWaitCursorFns(WaitCursorFn startFn, WaitCursorFn stopFn) +{ + gStartWaitCursorFn = startFn; + gStopWaitCursorFn = stopFn; +} + +void startWaitCursor() +{ + if (!gStartWaitCursorFn) + return; + + if (gWaitCursorCount == 0) + gStartWaitCursorFn(); + + ++gWaitCursorCount; +} + +void stopWaitCursor() +{ + if (!gStopWaitCursorFn) + return; + + --gWaitCursorCount; + + if (gWaitCursorCount == 0) + gStopWaitCursorFn(); +} + } // namespace USDUFE_NS_DEF diff --git a/lib/usdUfe/ufe/Utils.h b/lib/usdUfe/ufe/Utils.h index c9152d3df2..6ef5747707 100644 --- a/lib/usdUfe/ufe/Utils.h +++ b/lib/usdUfe/ufe/Utils.h @@ -52,6 +52,7 @@ typedef bool (*IsAttributeLockedFn)(const PXR_NS::UsdAttribute& attr, std::strin typedef void (*SaveStageLoadRulesFn)(const PXR_NS::UsdStageRefPtr&); typedef bool (*IsRootChildFn)(const Ufe::Path& path); typedef std::string (*UniqueChildNameFn)(const PXR_NS::UsdPrim& usdParent, const std::string& name); +typedef void (*WaitCursorFn)(); //------------------------------------------------------------------------------ // Helper functions @@ -299,4 +300,23 @@ bool isEditTargetLayerModifiable( USDUFE_PUBLIC Ufe::BBox3d combineUfeBBox(const Ufe::BBox3d& ufeBBox1, const Ufe::BBox3d& ufeBBox2); +//! Set both the start and stop wait cursor functions. +USDUFE_PUBLIC +void setWaitCursorFns(WaitCursorFn startFn, WaitCursorFn stopFn); + +//! Start the wait cursor. Can be called recursively. +USDUFE_PUBLIC +void startWaitCursor(); + +//! Stop the wait cursor. Can be called recursively. +USDUFE_PUBLIC +void stopWaitCursor(); + +//! Start and stop the wait cursor in the constructor and destructor. +struct USDUFE_PUBLIC WaitCursor +{ + WaitCursor() { startWaitCursor(); } + ~WaitCursor() { stopWaitCursor(); } +}; + } // namespace USDUFE_NS_DEF From 69ef548305093b394ed4c9d28685d1befe165b37 Mon Sep 17 00:00:00 2001 From: Pierre Baillargeon Date: Wed, 13 Dec 2023 11:32:22 -0500 Subject: [PATCH 2/2] EMSUSD-65 remove dead comment --- lib/mayaUsd/ufe/MayaUsdContextOps.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/mayaUsd/ufe/MayaUsdContextOps.cpp b/lib/mayaUsd/ufe/MayaUsdContextOps.cpp index 1fbc10de72..aa68799f97 100644 --- a/lib/mayaUsd/ufe/MayaUsdContextOps.cpp +++ b/lib/mayaUsd/ufe/MayaUsdContextOps.cpp @@ -101,7 +101,6 @@ static constexpr char kAddRefOrPayloadItem[] = "AddReferenceOrPayload"; const constexpr char kClearAllRefsOrPayloadsLabel[] = "Clear All USD References/Payloads..."; const constexpr char kClearAllRefsOrPayloadsItem[] = "ClearAllReferencesOrPayloads"; -//! \brief Change the cursor to wait state on construction and restore it on destruction. #ifdef UFE_V3_FEATURES_AVAILABLE //! \brief Create a working Material and select it: class InsertChildAndSelectCommand : public Ufe::CompositeUndoableCommand