Skip to content

Commit

Permalink
Merge pull request #1444 from Autodesk/spinell/MAYA-110682/drag_drop_…
Browse files Browse the repository at this point in the history
…layer_lose_edit_target

MAYA-110682 - On drag and drop of a layer with an editTarget, the edi…
  • Loading branch information
Krystian Ligenza authored Jun 15, 2021
2 parents 191dc21 + 6d4ab59 commit 7250d4a
Show file tree
Hide file tree
Showing 6 changed files with 249 additions and 2 deletions.
127 changes: 127 additions & 0 deletions lib/mayaUsd/commands/layerEditorCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ const char kRemoveSubPathFlag[] = "rs";
const char kRemoveSubPathFlagL[] = "removeSubPath";
const char kReplaceSubPathFlag[] = "rp";
const char kReplaceSubPathFlagL[] = "replaceSubPath";
const char kMoveSubPathFlag[] = "mv";
const char kMoveSubPathFlagL[] = "moveSubPath";
const char kDiscardEditsFlag[] = "de";
const char kDiscardEditsFlagL[] = "discardEdits";
const char kClearLayerFlag[] = "cl";
Expand All @@ -64,6 +66,7 @@ enum class CmdId
{
kInsert,
kRemove,
kMove,
kReplace,
kDiscardEdit,
kClearLayer,
Expand Down Expand Up @@ -301,6 +304,109 @@ class RemoveSubPath : public InsertRemoveSubPathBase
}
};

// Move a sublayer into another layer.
// @param path The layer's path to move.
// @param newParentLayer The new parent layer's path
// @param newIndex The index where the moved layer will be in the new parent.
class MoveSubPath : public BaseCmd
{
public:
MoveSubPath(const std::string& path, const std::string& newParentLayer, unsigned newIndex)
: BaseCmd(CmdId::kMove)
, _path(path)
, _newParentLayer(newParentLayer)
, _newIndex(newIndex)
{
}

bool doIt(SdfLayerHandle layer) override
{
auto proxy = layer->GetSubLayerPaths();
auto subPathIndex = proxy.Find(_path);
if (subPathIndex == size_t(-1)) {
std::string message = std::string("path ") + _path + std::string(" not found on layer ")
+ layer->GetIdentifier();
MPxCommand::displayError(message.c_str());
return false;
}

_oldIndex = subPathIndex; // save for undo

SdfLayerHandle newParentLayer;

if (layer->GetIdentifier() == _newParentLayer) {

if (_newIndex > layer->GetNumSubLayerPaths() - 1) {
std::string message = std::string("Index ") + std::to_string(_newIndex)
+ std::string(" out-of-bound for ") + layer->GetIdentifier();
MPxCommand::displayError(message.c_str());
return false;
}

newParentLayer = layer;
} else {
newParentLayer = SdfLayer::Find(_newParentLayer);
if (!newParentLayer) {
std::string message
= std::string("Layer ") + _newParentLayer + std::string(" not found!");
return false;
}

if (_newIndex > newParentLayer->GetNumSubLayerPaths()) {
std::string message = std::string("Index ") + std::to_string(_newIndex)
+ std::string(" out-of-bound for ") + newParentLayer->GetIdentifier();
MPxCommand::displayError(message.c_str());
return false;
}

// make sure the subpath is not already in the new parent layer.
// Otherwise, the SdfLayer::InsertSubLayerPath() below will do nothing
// and the subpath will be removed from it's current parent.
if (newParentLayer->GetSubLayerPaths().Find(_path) != size_t(-1)) {
std::string message = std::string("SubPath ") + _path
+ std::string(" already exist in layer ") + newParentLayer->GetIdentifier();
MPxCommand::displayError(message.c_str());
return false;
}
}

// When the subLayer is moved inside the current parent,
// Remove it from it's current location and insert it into it's
// new location. The order of remove / insert is important
// oterwise InsertSubLayerPath() will fail because the subLayer
// already exists.
layer->RemoveSubLayerPath(subPathIndex);
newParentLayer->InsertSubLayerPath(_path, _newIndex);

return true;
}

bool undoIt(SdfLayerHandle layer) override
{
if (layer->GetIdentifier() == _newParentLayer) {
// When the subLayer is moved inside the current parent,
// Remove it from it's current location and insert it into it's
// new location. The order of remove / insert is important
// oterwise InsertSubLayerPath() will fail because the subLayer
// already exists.
layer->RemoveSubLayerPath(_newIndex);
layer->InsertSubLayerPath(_path, _oldIndex);
} else {
auto newParentLayer = SdfLayer::Find(_newParentLayer);
newParentLayer->RemoveSubLayerPath(_newIndex);
layer->InsertSubLayerPath(_path, _oldIndex);
}

return true;
}

private:
std::string _path;
std::string _newParentLayer;
unsigned int _newIndex;
unsigned int _oldIndex { 0 };
};

class ReplaceSubPath : public BaseCmd
{
public:
Expand Down Expand Up @@ -548,6 +654,12 @@ MSyntax LayerEditorCommand::createSyntax()
syntax.makeFlagMultiUse(kRemoveSubPathFlag);
syntax.addFlag(kReplaceSubPathFlag, kReplaceSubPathFlagL, MSyntax::kString, MSyntax::kString);
syntax.makeFlagMultiUse(kReplaceSubPathFlag);
syntax.addFlag(
kMoveSubPathFlag,
kMoveSubPathFlagL,
MSyntax::kString, // path to move
MSyntax::kString, // new parent layer
MSyntax::kUnsigned); // layer index inside the new parent
syntax.addFlag(kDiscardEditsFlag, kDiscardEditsFlagL);
syntax.addFlag(kClearLayerFlag, kClearLayerFlagL);
// parameter: new layer name
Expand Down Expand Up @@ -630,6 +742,21 @@ MStatus LayerEditorCommand::parseArgs(const MArgList& argList)
}
}

if (argParser.isFlagSet(kMoveSubPathFlag)) {
MString subPath;
argParser.getFlagArgument(kMoveSubPathFlag, 0, subPath);

MString newParentLayer;
argParser.getFlagArgument(kMoveSubPathFlag, 1, newParentLayer);

unsigned int index { 0 };
argParser.getFlagArgument(kMoveSubPathFlag, 2, index);

auto cmd = std::make_shared<Impl::MoveSubPath>(
subPath.asUTF8(), newParentLayer.asUTF8(), index);
_subCommands.push_back(std::move(cmd));
}

if (argParser.isFlagSet(kDiscardEditsFlag)) {
auto cmd = std::make_shared<Impl::DiscardEdit>();
_subCommands.push_back(std::move(cmd));
Expand Down
5 changes: 5 additions & 0 deletions lib/usd/ui/layerEditor/abstractCommandHook.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ class AbstractCommandHook
// replaces a path in the layer stack
virtual void replaceSubLayerPath(UsdLayer usdLayer, Path oldPath, Path newPath) = 0;

// move a path at a given index inside the same layer or another layer.
virtual void
moveSubLayerPath(Path path, UsdLayer oldParentUsdLayer, UsdLayer newParentUsdLayer, int index)
= 0;

// discard edit on a layer
virtual void discardEdits(UsdLayer usdLayer) = 0;

Expand Down
5 changes: 3 additions & 2 deletions lib/usd/ui/layerEditor/layerTreeModel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,14 +177,15 @@ bool LayerTreeModel::dropMimeData(
auto oldParent = layerItem->parentLayerItem()->layer();
int index = (int)oldParent->GetSubLayerPaths().Find(layerItem->subLayerPath());
auto itemSubLayerPath = layerItem->subLayerPath();
context.hook()->removeSubLayerPath(oldParent, itemSubLayerPath);

// When we are moving an item (underneath the same parent)
// to a new location higher up we have to adjust the row
// (new location) to account for the remove we just did.
if (oldParent == parentItem->layer() && (index < row)) {
row -= 1;
}
context.hook()->insertSubLayerPath(parentItem->layer(), itemSubLayerPath, row);
context.hook()->moveSubLayerPath(
itemSubLayerPath, oldParent, parentItem->layer(), row);
}
}
}
Expand Down
15 changes: 15 additions & 0 deletions lib/usd/ui/layerEditor/mayaCommandHook.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,21 @@ void MayaCommandHook::removeSubLayerPath(UsdLayer usdLayer, Path path)
executeMel(cmd);
}

void MayaCommandHook::moveSubLayerPath(
Path path,
UsdLayer oldParentUsdLayer,
UsdLayer newParentUsdLayer,
int index)
{
std::string cmd;
cmd = "mayaUsdLayerEditor -edit -moveSubPath ";
cmd += quote(path);
cmd += quote(newParentUsdLayer->GetIdentifier());
cmd += std::to_string(index);
cmd += quote(oldParentUsdLayer->GetIdentifier());
executeMel(cmd);
}

// replaces a path in the layer stack
void MayaCommandHook::replaceSubLayerPath(UsdLayer usdLayer, Path oldPath, Path newPath)
{
Expand Down
5 changes: 5 additions & 0 deletions lib/usd/ui/layerEditor/mayaCommandHook.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ class MayaCommandHook : public AbstractCommandHook
// replaces a path in the layer stack
void replaceSubLayerPath(UsdLayer usdLayer, Path oldPath, Path newPath) override;

// move a path at a given index inside the same layer or another layer.
void
moveSubLayerPath(Path path, UsdLayer oldParentUsdLayer, UsdLayer newParentUsdLayer, int index)
override;

// discard edit on a layer
void discardEdits(UsdLayer usdLayer) override;

Expand Down
94 changes: 94 additions & 0 deletions test/lib/testMayaUsdLayerEditorCommands.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,100 @@ def setAndRemoveEditTargetRecursive(layer, index, editLayer):
cmds.undo()
self.assertEqual(path.normcase(stage.GetEditTarget().GetLayer().identifier), sharedLayer)

def testMoveSubPath(self):
""" test 'mayaUsdLayerEditor' command 'moveSubPath' paramater """

def moveSubPath(parentLayer, subPath, newParentLayer, index, originalSubLayerPaths, newSubLayerPaths):
cmds.mayaUsdLayerEditor(parentLayer.identifier, edit=True, moveSubPath=[subPath, newParentLayer.identifier, index])
self.assertEqual(newParentLayer.subLayerPaths, newSubLayerPaths)

cmds.undo()
self.assertEqual(newParentLayer.subLayerPaths, originalSubLayerPaths)

cmds.redo()
self.assertEqual(newParentLayer.subLayerPaths, newSubLayerPaths)

cmds.undo()
self.assertEqual(newParentLayer.subLayerPaths, originalSubLayerPaths)

def moveElement(list, item, index):
l = list[:] #copy the list
l.remove(item)
l.insert(index, item)
return l

shapePath, stage = getCleanMayaStage()
rootLayer = stage.GetRootLayer()

layerId1 = cmds.mayaUsdLayerEditor(rootLayer.identifier, edit=True, addAnonymous="MyLayer1")[0]
layerId2 = cmds.mayaUsdLayerEditor(rootLayer.identifier, edit=True, addAnonymous="MyLayer2")[0]
layerId3 = cmds.mayaUsdLayerEditor(rootLayer.identifier, edit=True, addAnonymous="MyLayer3")[0]

originalSubLayerPaths = [layerId3, layerId2, layerId1]
self.assertEqual(rootLayer.subLayerPaths, originalSubLayerPaths)

# Test moving each layers under the root layer to any valid index in the root layer
# and test out-of-bound index
for layer in originalSubLayerPaths:
for i in range(len(originalSubLayerPaths)):
expectedSubPath = moveElement(originalSubLayerPaths, layer, i)
moveSubPath(rootLayer, layer, rootLayer, i, originalSubLayerPaths, expectedSubPath)

with self.assertRaises(RuntimeError):
cmds.mayaUsdLayerEditor(rootLayer.identifier, edit=True, moveSubPath=[layer, rootLayer.identifier, len(originalSubLayerPaths)])
self.assertEqual(rootLayer.subLayerPaths, originalSubLayerPaths)

#
# Test moving sublayer (layer2 and layer3) inside a new parent layer (layer1)
#

layer1 = Sdf.Layer.Find(layerId1)
self.assertTrue(len(layer1.subLayerPaths) == 0)

# layer1 has no subLayer so index 1 is invalide
with self.assertRaises(RuntimeError):
cmds.mayaUsdLayerEditor(rootLayer.identifier, edit=True, moveSubPath=[layerId3, layerId1, 1])
self.assertTrue(len(layer1.subLayerPaths) == 0)
self.assertEqual(rootLayer.subLayerPaths, originalSubLayerPaths)

# Move layer3 from the root layer inside layer1 at index 0
cmds.mayaUsdLayerEditor(rootLayer.identifier, edit=True, moveSubPath=[layerId3, layerId1, 0])
self.assertEqual(rootLayer.subLayerPaths, [layerId2, layerId1])
self.assertEqual(layer1.subLayerPaths, [layerId3])

# Move layer2 from the root layer inside layer1 at index 0
cmds.mayaUsdLayerEditor(rootLayer.identifier, edit=True, moveSubPath=[layerId2, layerId1, 0])
self.assertEqual(rootLayer.subLayerPaths, [layerId1])
self.assertEqual(layer1.subLayerPaths, [layerId2, layerId3])

cmds.undo()
self.assertEqual(rootLayer.subLayerPaths, [layerId2, layerId1])
self.assertEqual(layer1.subLayerPaths, [layerId3])

# Move layer2 from the root layer inside layer1 at index 1
cmds.mayaUsdLayerEditor(rootLayer.identifier, edit=True, moveSubPath=[layerId2, layerId1, 1])
self.assertEqual(rootLayer.subLayerPaths, [layerId1])
self.assertEqual(layer1.subLayerPaths, [layerId3, layerId2])

cmds.undo()
self.assertEqual(rootLayer.subLayerPaths, [layerId2, layerId1])
self.assertEqual(layer1.subLayerPaths, [layerId3])

# Undo moving layer3 inside layer1
cmds.undo()
self.assertEqual(rootLayer.subLayerPaths, [layerId3, layerId2, layerId1])
self.assertEqual(layer1.subLayerPaths, [])

# Insert layer3 inside layer1 and try to move the layer3 from the root layer
# inside layer1. This should fail.
cmds.mayaUsdLayerEditor(layerId1, edit=True, insertSubPath=[0, layerId3])
self.assertEqual(layer1.subLayerPaths, [layerId3])

with self.assertRaises(RuntimeError):
cmds.mayaUsdLayerEditor(rootLayer.identifier, edit=True, moveSubPath=[layerId3, layerId1, 0])
self.assertEqual(rootLayer.subLayerPaths, originalSubLayerPaths)
self.assertEqual(layer1.subLayerPaths, [layerId3])

def testMuteLayer(self):
""" test 'mayaUsdLayerEditor' command 'muteLayer' paramater """

Expand Down

0 comments on commit 7250d4a

Please sign in to comment.