Moving layers with BatchPlay?

What would be the correct batchPlay syntax to move one layer above or below another?
I know I can do this in javascript API, but I have a long chain of batchPlay descriptors that I don’t want to break. So I need a batchPlay oneliner to move one layer above or below another by name.

The task is even more difficult then I thought initially.
Listing layers with activeDocument.layers and trying to use indexes in batchPlay

batchPlay([{
          _obj: "move",
          _target: [
            {
              _ref: "layer",
              _enum: "ordinal",
              _value: "targetEnum",
            },
          ],
          to: {
            _ref: "layer",
            _index: 2
          },
          adjustment: false,
            }
      ],{})

gives quite unpredicatable results, it looks like indexes used internally do not correspond to indexes in the layers, there are some hidden elements and groups that are not listed

If you know the name of the layer before your batchPlay chain, you could simply do:

  1. Read index by name
  2. Subtract 1 if you want to put a layer below, or keep it as is to put it above
  3. Move layer to the new index

You probably already figured this out, so step 1) might be causing the issues.
I wouldn’t say indices are unpredictable, but they can be tricky for sure. Here are two things you need to consider:

  • Groups in Photoshop occupy two indices: one “Layer Section Start” (visible in the layer stack) and one “Layer Section End” (invisible)
  • Indices are affected by the document having a background layer or not. This can shift things around by 1

That’s basically what I have tried.
Problem is that it looks like not only Groups occupy 2 indexes, if you clip parent layer to bottom layer with Cmd+Alt+G (which is also kind of grouping), indexes start behaving odd.

For example, I can see 18 layers in the list, some of which would be groups, but moving to 18th index moves to the wrong place.
Keeping in mind that there can be many nested groups and many levels of clipped layers, calculation of the correct index to move to becomes very tricky. And if your chain of descriptors creates layers, I think it makes it impossible to calculate index beforehand because layers are not yet there when you create descriptors for batchPlay.

Hello Simon, can I get some suggestions with how to do this?

Sure, layer descriptors have a property called itemIndex, that you can get like any other property. You can reference a layer by name (instead of referencing the active layer or a layer by id) via { _ref: 'layer', _name } which u pass to the _target array:

const idx = photoshop.action.batchPlay([
    {
      _obj: 'get',
      _target: [{ _property: 'itemIndex' }, { _ref: 'layer', _name: *name* }],
    },
  ], { synchronousExecution: true })[0]['itemIndex']

Be aware that indices in PS are affected by the document having a background layer or not. To get correct results, you might need to add some logic like here:

1 Like

Thank Simon
I didn’t notice this field before :frowning:

Hello @simonhenke, I’m wondering: Is this still true?

I am also trying to move a layer, and I’m having trouble figuring out how I am to define the batchPlay descriptor to properly do so. I tried doing so just by name, but nothing happened. I was about to try this itemIndex approach, but it looks like such a property does not exist for the Layer object.

When I recorded a batchPlay{} for moving a layer, I got this in return:

{
   "_obj": "move",
   "_target": [
      {
         "_ref": "layer",
         "_enum": "ordinal",
         "_value": "targetEnum"
      }
   ],
   "to": {
      "_ref": "layer",
      "_index": 14
   },
   "adjustment": false,
   "version": 5,
   "layerID": [
      513
   ],
   "_isCommand": true
}

I am perplexed by this _index property. How do I access this? I feel that this was the crux of the solution for this thread, but I am pretty sure that layer descriptors no longer have a property called itemIndex, index, or anything of the sort.

If they do still have a descriptor, how do I get a list of all the properties that I can look at?

The Layer descriptor still has the itemIndex property. Generally I wouldn’t expect batchPlay to change a lot, as it is just a layer on top of ActionManager code which hasn’t really changed much over the years - which is good, cause otherwise most plugins would break.

In my previous post I’ve shown how to access the itemIndex property. Moving to a specific index also works fine for me:

export function __moveLayerToIndex(_index: number) {
  return {
    _obj: 'move',
    _target: { _ref: 'layer', _enum: 'ordinal', _value: 'targetEnum' },
    to: { _ref: 'layer', _index },
    adjustment: false,
  }
}

That’s the descriptor I’m using. You can get rid of of stuff like version, layerID and _isCommand - as some threads here have shown, _isCommand might even cause things to not work correctly.

And for checking out layer properties, use Alchemist or just log a layer descriptor via

{
    _obj: "get",
    _target: [
      { _ref: 'layer', _enum: 'ordinal', _value: 'targetEnum' }
    ],
}


Thanks for this screenshot. I am clearly accessing the incorrect thing, as that is not what I am seeing when I attempt to access layer properties in the DOM. Of course, you said “Layer descriptor” so there is another level of information that I am missing. I am aware of action descriptors, and layer objects and properties, but not layer descriptors. You mention that you somehow use that object to “log a layer descriptor.” That looks like the kind of object that I would attach to a batchPlay(), but when I attempted that, nothing was returned.

Is there any way to access those properties in the console itself? This is what I get when I attempt to access layer properties in the console:

I believe that’s DOM result you’re seeing instead of BatchPlay command one

Oh, I see what you mean! Somehow, I had been entirely focused on the listener button. This is what I needed to look at, just clicking the inspector button and selecting a layer (for instance).

More information found here. (Davide Barranca’s UXP series vid #8)

This is that situation where he mentioned the limits of DOM vs. action manager coverage in video #6.

Thanks! I’ll see what I can do with these additional layer properties.

It’s still weird that there isn’t any index or itemIndex property on a DOM layer object, isn’t it?
Was that a design decision? Or maybe it has never been there, not sure since I didn’t use DOM a lot.

It’s very strange! Right now, I’m trying to transform a layer via the _index property, but I seem to be having some trouble. I initially got this action descriptor to work by selecting via layer ID. However, I seem to be having trouble selecting by index (I wish to transform the layer below the selected layer)

Here’s the initial part of the action descriptor. “index” is successfully defined, but it does not treat this as a valid means to select the item.

{_obj: "transform",
    _target: [
      {
        _ref: "layer",
        _index: index
      }
    ]
...
}

No idea why it doesn’t work (target descriptors are notoriously weird, and some actions just don’t like some inputs). No idea whether the following works either, but if you’ve to act upon the layer below you could try with:

{
      _ref: "layer",
      _enum: "ordinal",
      _value: "previous" // or "next", or "back", or "forward", or even "prettyPlease"
}

Let us know!

1 Like

As far I know transform action works only with active item and ignores reference such as index, id, etc.

And that item can be layer, pixel selection, vector path. So you can have different outcomes based on active item.

2 Likes