Cannot enable/disable layers. Nudging layer moves selected layer not layer variable

Hi, so this started out as being a simple quest to enable and disable layers based on matching the layer name to a string. According to the API though, there is no way to enable or disable a layer -_-.

So I’ve created a JS function to check for a matching layer name then nudge it off screen. Console.logs indicate that the layer names and if statements are all correct but the only layer that actually gets nudged is whichever layer is currently selected in the document instead of the variable to which the nudge is applied to.

As an example, I have 4 layers - ‘apple’, ‘banana’, ‘grape’ and ‘orange’. If the matching string is ‘apple’, then whichever layer is selected (including groups) ends up being nudged 3 times.

Any advice? Is this a bug?

This is the function that performs the modifications:

function ProcessAssets() {
  const hairVariation = document.getElementById("hairVariation").getAttribute("value") - 1;
  const hairVariations = document.getElementsByClassName("hairVariations");

  if (hairVariation < hairVariations.length && hairVariation >= 0) {
    console.log("Selected variation: " + hairVariations[hairVariation].getAttribute("value"));

    const app = require('photoshop').app;
    const doc = app.activeDocument;
    const docLayers = doc.layers;

    for (let index = 0; index < hairVariations.length; index++) {
      let element = hairVariations[index].getAttribute("value");
      let enable = index == hairVariation;

      for (let i = 0; i < docLayers.length; i++) {
        let layer = docLayers[i];
        if (layer.name == element) {
          console.log(layer.name + " == " + element);
          if (enable) {
            if (nudged.includes(layer)) {
              layer.nudge(-20000, 0);
              nudged.pop(layer);
              console.log("Found target variation. Shifting back.");
            }
          } else {
            if (!nudged.includes(layer)) {
              layer.nudge(20000, 0);
              nudged.push(layer);
              console.log("Found unwanted variation. Shifting away.");
            }
          }
        }
      }

      if (enable) {
        console.log(element + " enabled");
      } else {
        console.log(element + " disabled");
      }
    }

  } else {
    console.log("Outside of range");
  }
}

Hi, maybe you could share a couple of lines of code ? It might be helpful.

Sure, I have added it the original post. :slight_smile:

What are you trying to acheive ? Unselect a given layer ?

Some operations in Photoshop can only be processed on the currently active layer, for example all transform events (to which nudge belongs). If you want to transform a specific layer, you’ll have to select it first (by index, id or name).

However, you can easily toggle layer visibility with batchPlay. Also, this works on any layer without selecting it beforehand:

{
  _obj: "hide",
  _target: {
    _ref: "layer",
    _name: "Layer 2",
  },
}
1 Like

Aha ok, thank you, this seems to be the right direction. Could you perhaps give an example of how one would call this from a js function?

The BatchPlay syntax is explained in the docs here and there are many examples in the forum, too - that should help to figure it out :slight_smile:

1 Like

Thanks @simonhenke for your insight. It took a lot more googling to figure it out but you got me on the right track. The API documentation is seriously lacking for UXP and batchPlay. Looking at a ton of other posts I managed to piece together this function that works.

Using the layer._id was necessary to account for layers with the same name.

function RunBatch(_show, _id) {
  const batchPlay = require("photoshop").action.batchPlay;

  const result = batchPlay(
    [{
      _obj: _show ? "show" : "hide",
      _target: {
        _ref: "layer",
        _id: _id,
      },
      "layerID": [_id],
      "makeVisible": _show,
    }], {
      "synchronousExecution": false,
      "modalBehavior": "fail"
    }
  );
}

Yes, using layer ID is best practice when it comes to targeting layers, to prevent conflicts. One exception is when you need to target a layer inside of a batchPlay call, that you created in just that call.

→ batchplay
→ read id
→ batchplay

would result in two history steps, which is often unwanted. In these cases its fine to assign a unique name and target the layer by that.

Few more notes to your function:

This property doesn’t do anything. It sometimes gets added to the descriptor after Photoshop has processed it and has more information on the event. (For example in the make layer event, it will show the newly assigned ID). Using the _id in the target object is sufficient.

Same for this one. As far as I know, makeVisible is only a parameter used in the select (layer) event. The event code/name of show or hide already takes care of that.

Both of these are default values, so you can leave them out, but of course you can also keep them.
Also, if you don’t do anything with the result (and only run some operation), you don’t have to assign it to a variable.

So this would be a minimized version of the function:

const batchPlay = require("photoshop").action.batchPlay;
function showLayer(_show, _id) {
  batchPlay(
    [{
      _obj: _show ? "show" : "hide",
      _target: {
        _ref: "layer",
        _id
      }
    }], {}
  );
}
2 Likes