Getting the Layer "_index"

Hi,
Is there a simple way to get the layer’s index for moving with batchPlay's "_index"?

I know about moveAbove() and moveBelow(), but I’d like to move a layer to a position in the Layers panel unrelated to other layers’ indices.

Moving and Selecting a layer use the “_index” property. From app.eventNotifier:

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

On a related note, it would also be useful to know how to get layerSectionStart and layerSectionEnd for grouping:

make {
 "_obj": "make",
 "_target": [
  {
   "_ref": "layerSection"
  }
 ],
 "from": {
  "_ref": "layer",
  "_enum": "ordinal",
  "_value": "targetEnum"
 },
 "layerSectionStart": 119,
 "layerSectionEnd": 120,
 "name": "Group 1",
 "_isCommand": true
}

Thank you.

Layers have a property called itemIndex which you can get via batchPlay.
However, this property is a bit error-prone as the true index is affected by whether the document has a background layer or not.

Essentially, the layer’s index is:

  • itemIndex ← if there’s no background layer
  • itemIndex - 1 ← if there’s a background layer

The document descriptor has the property hasBackgroundLayer which you can use for that.


layerSectionStart and layerSectionEnd ist not anytihng you need to worry about when creating a group. What you see in the descriptor is what the operation returned, but I’m pretty sure that those properties get ignored anyways when you call it like that. To group layers programmatically, you only need to have them selected. Then they’re referenced by the following part:

 "from": {
  "_ref": "layer",
  "_enum": "ordinal",
  "_value": "targetEnum"
 }

@simonhenke, how does one get the itemIndex with batchPlay?

EDIT: After watching Davide’s video ‘Adobe UXP: Things you need to know! #8 BatchPlay (part 3): the Alchemist inspector’, I came up with the following code:

const get_layer_index = () =>
{
  batchPlay = require("photoshop").action.batchPlay;
  const result = batchPlay(
  [
     {
        "_obj": "get",
        "_target": [
           {
              "_ref": "layer",
              "_enum": "ordinal",
              "_value": "targetEnum"
           },
           {
              "_ref": "document",
              "_enum": "ordinal",
              "_value": "targetEnum"
           }
        ],
        "_options": {
           "dialogOptions": "dontDisplay"
        }
     }
  ],{
     "synchronousExecution": true,
  });

  return result[0].itemIndex;
}
console.log(get_layer_index());

Yes, that should work. Few notes though:

  • You’re getting the whole layer descriptor, which is way less performant than only getting the single property you need. In order to do so, you can add { _property: *propertyName* } to the _target array. In this case { _property: "itemIndex" }

  • Specifying the target document in the _target array doesn’t do anything bad, but you can also leave it away since it’s the default.

  • Don’t forget the background-fix I’ve explained before, otherwise the code might run into problems with specific documents.

1 Like

@simonhenke , thanks again! How’s this?

const get_layer_itemIndex = () =>
{
  app = window.require("photoshop");
  batchPlay = app.action.batchPlay;
  const result = batchPlay(
  [
     {
        "_obj": "get",
        "_target": [
           {
              "_ref": "layer",
              "_enum": "ordinal",
              "_value": "targetEnum",
              "_property": "itemIndex"
           },
           {
              "_ref": "document",
              "_enum": "ordinal",
              "_value": "targetEnum"
           }
        ],
        "_options": {
           "dialogOptions": "dontDisplay"
        }
     }
  ],{
     "synchronousExecution": true,
  });

  return result[0].itemIndex;
}

let ps = window.require("photoshop").app;
let doc = ps.activeDocument;
let itemIndex = get_layer_itemIndex();
if (doc.hasBackgroundLayer)
{
  itemIndex -= 1;
}

With regards to your first note about the whole layer descriptor, even with _property in the _target array, the code above still returns the whole layer. Is this the correct behavior? I expected to just get the itemIndex as the result.

Try:

"_target": [
     { 
        "_property": "itemIndex" 
     },
     {
        "_ref": "layer",
        "_enum": "ordinal",
        "_value": "targetEnum"
     }
]

Has something changed with this? I am just testing this for the first time. The layer’s index in my testing seems to always be itemIndex - 1 whether there is a background layer or not.

OK, well I figure out something here… The background only affects the layer index with some things.

For example, using this descriptor to move the layer style, the layer index is always itemIndex -1 regardless if there is a background layer.

function moveLayerStyle(index){
    
return{
   "_obj": "move",
   "_target": [
      {
         "_ref": "layerEffects"
      },
      {
         "_ref": "layer",
         "_enum": "ordinal",
         "_value": "targetEnum"
      }
   ],
   "to": {
      "_ref": "layer",
      "_index": index
   },
   "_isCommand": true
}    
    
}

However, for moving smart filters, it is like you said. With a background layer it is itemIndex -1 and just itemIndex without a background.

function moveSmartFilters(index){
    
return{
   "_obj": "move",
   "_target": [
      {
         "_ref": "filterFX"
      },
      {
         "_ref": "layer",
         "_enum": "ordinal",
         "_value": "targetEnum"
      }
   ],
   "to": {
      "_ref": "layer",
      "_index": index
   },
   "_isCommand": true
}    
    
}