[Bug] Can't rename inactive layer

To avoid another bug where renaming a layer it always makes that layer visible, I made a prototyped method to workaround by saving visibility state, renaming and restoring visibility.

But it appears, there’s also an issue that if inactive layer is renamed, an exception is thrown:

So had to add an active state check too. When I uncomment the line, it seems to work as expected.

Any update maybe? Now there’s no way to actually correctly avoid this issue when multiple layers are active, but you want to rename just one

I’m not really a big fan of the DOM implementation (yet), since I don’t know what it’s doing under the hood. Doing what you want seems no problem with batchPlay:

const ids = getTargetLayersIDs()
photoshop.action.batchPlay(__setLayerProperty({name: 'set last selected name'}, ids[ids.length - 1]),{})

// --------------

export const docRef = { _ref: 'document', _enum: 'ordinal', _value: 'targetEnum' }
export const layerRef = { _ref: 'layer', _enum: 'ordinal', _value: 'targetEnum' }
export function layerRefID(id: number) {
  return {
    _ref: 'layer',
    _id: id,
  }
}

export function getTargetLayersIDs(): number[] {
  const res = photoshop.action.batchPlay([
      {
        _obj: 'multiGet',
        _target: [docRef],
        extendedReference: [
          ['numberOfLayers', 'hasBackgroundLayer', 'targetLayersIDs'],
        ],
      },
    ],
    { synchronousExecution: true }
  )[0]
  if (res.numberOfLayers === 0 && res.hasBackgroundLayer) {
    return [1]
  }
  return res.targetLayersIDs.map((l) => l._id)
}

export function __setLayerProperty(prop: Partial<LayerDescriptor>, id?: number) {
  return {
    _obj: 'set',
    _target: id ? layerRefID(id) : layerRef,
    to: { ...prop }
  }
}

Im getting the ids of the selected layers and then update the name of the last one. All other layers remain selected and also stay the same in terms of visibility etc.

When I tried layers name last time then I could rename only the active layer regardless of ID. I hope this will be improved… maybe already was so we could get different behavior in different PS versions.

Yes, some events require the layer to be selected unfortunately ( - transform also).

Now it’s a pure mess with layer activity and visibility. Especially when you’re trying to manipulate multiple active layers. Some actions doesn’t care if ID is passed - applies something to all active layers. You can’t do something if layer is not active. Doing something on active layer changes visibility. Currently my very simple plugin has nearly 1/3 of the code just to workaround that mess :frowning:

I’ll try your approach @simonhenke and let you know. Thanks

Good luck :slight_smile:
If you’re not using typescript, don’t forget to remove the typing from my code, for example
__setLayerProperty(prop, id)
instead of
__setLayerProperty(prop: Partial<LayerDescriptor>, id?: number)

1 Like

What am I missing?

    batchPlay(
        [{
            "_obj": "set",
            "_target": [
                {
                    "_ref": "layer",
                    "_id": id
                }
            ],
            "to": {
                "_obj": "layer",
                "name": name
            }
        }],
        {
            "synchronousExecution": true,
            "modalBehavior": "execute"
        }
    );

This doesn’t change name at all. Nothing in console either. Also tried without "_obj": "layer",

Also, I’m looping here two invisible layers and this rename just makes one of the two layers visible. Same with prototoype script version.

This code works if id and name are valid. How are you getting id and name variables ?

const batchPlay = require('photoshop').action.batchPlay;

app.Layer.prototype.setName = function (name) {

    console.log(this._id, name);

    batchPlay(
        [{
            "_obj": "set",
            "_target": [
                {
                    "_ref": "layer",
                    "_id": this._id
                }
            ],
            "to": {
                "_obj": "layer",
                "name": name
            }
        }],
        {
            "synchronousExecution": true,
            "modalBehavior": "execute"
        }
    );
};

<...>

layer.setName(`${originalName}_newName`);

Console output

44 "Layer 5_newName"

Figured it… Again spent at least an hour on same thing…
No matter batchPlay or DOM, layer needs to be active :angry:

So, @simonhenke , not really a solution :frowning:

These are just things we have to get used to and consider when writing scripts/algorithms that use Photoshop functionality, as this behavior will probably not change internally any time soon.

I don’t really see the problem though, if you know that a selection is necessary, just select the layer beforehand:

export function __selectByID(_id: number): ActionDescriptor {
  return {
    _obj: 'select',
    _target: { _ref: 'layer', _id },
  }
}

export function __selectByIndex(_index: number): ActionDescriptor {
  return {
    _obj: 'select',
    _target: { _ref: 'layer', _index},
  }
}

export function __selectByName(_name: string): ActionDescriptor {
  return {
    _obj: 'select',
    _target: { _ref: 'layer', _name },
  }
}

Selecting a layer is not a heavy task that requires much computing on Photoshop’s side, so you won’t lose a lot of milliseconds.
And if you want to have everything after your script like it was before, just store the initially selected IDs and reset them afterwards via

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

That’s exactly what I ended up with (just not all batchPlay). But the issue goes much deeper if you have a mixed visibility active layers. Results for other active layers are completely unpredictable while trying to manipulate one active layer. What I do now:

  • Get needed layers
  • Set active/visible state of each layer to array
  • Do action on each layer
  • Restore state of each layer

But trouble doesn’t end here. There are still some corner cases in my plugin, where this still is an issue. To fix that, I should set all layers inactive, because some actions are performed on active layers and not on IDs that are provided, then on performing an action set that one layer active and then again set inactive after action is done. But by setting layers inactive, it again messes up visibility… It’s a bit difficult to explain in detail. It’s just a nightmare…

What kind of actions are you trying to perform?
Maybe an example workflow would help to understand the issue better.

Mostly renaming, changing colors and moving. I’ll make plugin public hopefully this weekend. Will upload obfuscated ccx to my page, but might also give full version here for you guys to check. I’m pretty sure it can be improved. This is my first plugin (also never done CEP or JSX scripts before)

Interesting :slight_smile: looking forward to it

1 Like

I do not agree. The renaming layer is the core DOM operation. And from my experiments before batchPlay… this action can cost you about 10-30ms. It doesn’t sound like a lot but for 100 layers it is 1-3s and 10-30s for thousands. If you squeeze all descriptors into single batchplay then it probably will be super fast. But again layer rename in Layer class as part of DOM in an object-oriented approach means just renaming of one layer + selection restore. And if more operations needs selection and script does a lot of other steps it can easily sum up into a long time.

Maybe I don’t get your point, but isn’t that just another reason to stick to batchPlay instead of DOM?
Also, the naming isn’t the overhead in this case, since that’s what @Karmalakas wanted to do in his script. It was more about the additional selecting (or restoring visibility etc) of the layers, which he has to do as a workaround.

Would be interesting to have some benchmark tests for how long these individual operations take.
But once again, what I was trying to say is: That’s how Photoshop behaves, so we have to deal with it and the resulting workarounds.

My point is that all core features should be able to run without selecting anything in first place. That is how it should be done. But that is nothing we can do right now.

1 Like

Yes, I fully agree. It’s a weird design decision, but part of Photoshops core architecture now…I also wish such internal things would be updated or fixed over time, but I feel like new things are mostly built on top of the existing architecture.