Move Layer Bug

Hello,
I’m encountering some strange behavior when moving layers. This happens for me when using batchPlay (as in the example below) and moveAbove().

const move_test = async() =>
{
  const ps = require("photoshop").app;
  let doc = ps.activeDocument;
  const batchPlay = require("photoshop").action.batchPlay;

  //assumes two layers named Layer 1 & Layer 2, respectively.
  //Layer 2 is in front of Layer 1

  const result = await batchPlay(
  [
     {
        "_obj": "select",
        "_target": [
           {
              "_ref": "layer",
              "_name": "Layer 1"
           }
        ],
        "makeVisible": false,
        "_isCommand": true,
        "_options": {
           "dialogOptions": "dontDisplay"
        }
     },
     //duplicate "Layer 1"
     {
       "_obj": "duplicate",
       "_target": [{
         "_ref": "layer",
         "_enum": "ordinal",
         "_value": "targetEnum"
       }],
       "version": 5,
       "_isCommand": true,
       "_options": {
         "dialogOptions": "dontDisplay"
       }
     },
     //move "Layer 1 copy" to front
     {
       "_obj": "move",
       "_target": [{
         "_ref": "layer",
         "_enum": "ordinal",
         "_value": "targetEnum"
       }],
       "to": {
         "_ref": "layer",
         "_enum": "ordinal",
         "_value": "front"
       },
       "_isCommand": true,
       "_options": {
         "dialogOptions": "dontDisplay"
       }
     }
  ],{
     "synchronousExecution": true, //I have tried with true & false
     "modalBehavior": "fail"
  });

  console.log("result",result)
}

The front value is inconsistent.

Perhaps related, other move issues have appeared on this forum. @simonhenke mentioned issues when doc.hasBackgroundLayer is true. moveAbove() & moveBelow() also have issues:
Weird Layer.moveBelow() behavior

Thank you.

There’s definitely something weird going on. Your async/await and synchronousExecution code is fine and the background layer isn’t the problem either.

Here’s my observation:

It doesn’t work when

  • the top layer is selected
  • no layer is selected

It does work when:

  • any layer except the top one is selected
  • you split up the code into two batchPlay calls (1: select layer, 2: duplicate & move)

To me this looks like a bug and I’ve also encountered some weird behavior when moving layers in my own plugins. I always just try to work around them and do a lot of testing.

I ended up using Batch play
Here’s a descriptor to move layers:

    {
        _obj: 'move',
        _target: [{_ref: 'layer', _enum: 'ordinal', _value: 'targetEnum'}],
        to: {_ref: 'layer', _index: indexToMoveTo},
        adjustment: false,
        layerID: arrayLayerIDsToMove
    }

I think without _target it didn’t work, but it doesn’t really do anything here. Don’t ask, it’s been a while I worked with this :sweat_smile:

I think _target is necessary, layerID can be omitted though, if I’m not mistaken.

However, I don’t think it solves this thread’s issue - it’s just moving to a specific index instead of to the front.

Is there a way with _target to target multiple IDs?


But it’s easy to get the top layer index

    const doc = getDocById(docId);
    const topLayer = doc.layerTree[0];
    const topIndex = getLayerProperty(topLayer._id, 'itemIndex');

I use this logic in my Pinned Layers plugin and it seems to be working fine. Also, IIRC, this kind of descriptor, using layerID was suggested to me by @kerrishotts when I just had too many issues with renaming, activating, moving layers in the same action.

Yes, for example:

export function __selectByIDs(ids: number[]) {
  return {
    _obj: 'select',
    _target: ids.map(_id => ({_ref: 'layer', _id})),
    selectionModifier: {
      _enum: 'selectionModifierType',
      _value: 'addToSelection',
    },
    makeVisible: false,
  }
}

Yes, but in theory the {"_ref": "layer","_enum": "ordinal","_value": "front"} approach should do the same in less LoC.
However, I quickly tested both versions and can confirm, that moving layers to a specific index (for example the top one) is not affected by the bug described above, and therefore a valid workaround.
I think that was also what I did for my plugins, when I realized the “front” moving thing didn’t work.

You can run a simple test: Try to add a valid layerID there and see if it’s actually the one getting moved. I did just that, and as expected, it’s the selected layer getting moved (due to {_ref: 'layer', _enum: 'ordinal', _value: 'targetEnum'}), not the one with the specified ID.

In fact, you can write anything there, for example layerID: ["HELLO"] and it won’t break as it simply gets ignored. That’s just additional info Photoshop adds to the returned descriptor when you listen to events.

I’ll revisit my plugin code then (it was way overdue anyway), because it moves the provided IDs to the top no matter what’s selected :thinking: At least it used to :sweat_smile:

Are you sure, that the provided IDs weren’t also the selected ones already?
I mean not before running the script, but maybe you selected them during the process.

I’m pretty sure, but I’ll get back to you probably tomorrow :slight_smile: Will try to figure out what’s happening :slight_smile: It’s a quite complex logic of that plugin, although it does a pretty simple thing

Thank you all for the replies. Combining your feedback, I came up with this…

const move_multiple_above = (tgt_id, ids) => {
  return {
    _obj: 'move',
    _target: ids.map(_id => ({_ref: 'layer',_id})),
    to: {
      _ref: 'layer',
      _index: tgt_id
    },
    adjustment: false
  }
}

let target_index = 3; //where to move the selected layers
let ids = doc.activeLayers.map(x => x._id); //add selected layer ids to 'ids' array 

const result = await batchPlay(
[
  move_multiple_above(target_index,ids) //gets descriptor for moving multiple layers
],{
  "synchronousExecution": true,
  "modalBehavior": "fail"
});

It’s kinda slow, unfortunately. I really wish front worked with the move batchPlay! Does anyone have some suggestions for better performance? Thank you.

Simon, you were right. I was looking a bit at the wrong part of flow. Before running that descriptor I select the layers I need. Removed layerID and nothing changed :slight_smile: Thanks for pointing that out

1 Like