(Again) Create Layer on top of every other layer through pure batchPlay

hi,
try this code

async function test() {

    await require("photoshop").core.executeAsModal(CreateLayerToTop, {
        "commandName": "test"
    });

}

async function CreateLayerToTop() {

    let cl = await app.activeDocument.activeLayers[0];

    if (cl.parent != null) {

        while (cl.parent != null) {

            await require("photoshop").action.batchPlay([
                Layer_Add(),
            ], {
                synchronousExecution: false
            });

            await require("photoshop").action.batchPlay([
                Layer_Move_Top(),
            ], {
                synchronousExecution: false
            });

            cl = await app.activeDocument.activeLayers[0];

        }

    }else{
        await require("photoshop").action.batchPlay([
            Layer_Add(),
        ], {
            synchronousExecution: false
        });

        await require("photoshop").action.batchPlay([
            Layer_Move_Top(),
        ], {
            synchronousExecution: false
        });
    }




    function Layer_Add() {

        const json = {
            "_obj": "make",
            "_target": [{
                "_ref": "layer"
            }],
            "_isCommand": true
        }
        return json;

    }

    function Layer_Move_Top() {

        const json = {
            "_obj": "move",
            "_target": [{
                "_ref": "layer",
                "_enum": "ordinal",
                "_value": "targetEnum"
            }],
            "to": {
                "_ref": "layer",
                "_enum": "ordinal",
                "_value": "front"
            },
            "_isCommand": true
        }
        return json;

    }

}
1 Like

Thanks @MrL !
I have no idea what’s going one, it did work perfectly once and the second time I’ve ran it, nothing happened. Now I can’t make it work. I’ll have to investigate this.

@MrL I’ve restarted everything and it’s working great, thank you so much! You’ve saved a lot of my frustration :slight_smile:

My PS version is 23.3.1. It works well. There may be differences between different versions.

That code doesn’t look really efficient and also not correct :thinking: For a nesting of N levels, the while loop will run N times and each time it will create a new layer. IMO, if you were to write it this way, it’d have to be

// *create Layer*
do {
   // *move to front*
} while (!!app.activeDocument.activeLayers[0].parent)

But still, you’d have N iterations.


Here are the missing parts of my solution, it’s mostly getters and setters:

function getDocumentProperty(_property) {
  return photoshop.action.batchPlay([
    {
      _obj: 'get',
      _target: [{ _property},  { _ref: 'document', _enum: 'ordinal', _value: 'targetEnum' }],
    },
  ], { synchronousExecution: true })[0][prop]
}
function __makeLayer(name, below = false) {
  return {
    _obj: 'make',
    _target: [{ _ref: 'layer' }],
    ...(name ? { using: { name } } : {}),
    below
  }
}
function __moveLayerToIndex(_index) {
  return {
    _obj: 'move',
    _target: { _ref: 'layer', _enum: 'ordinal', _value: 'targetEnum' },
    to: { _ref: 'layer', _index },
    adjustment: false,
  }
}
function getLayerCount() {
  return getDocumentProperty('numberOfLayers')
}

bpSync and suspendHistory are just helpers for the usual batchPlay and executeAsModal stuff, I think I even posted my suspendHistory function in another thread here.

2 Likes

@simonhenke You are right. There is a problem with my code. I should not put the created layer into the loop. Now fix it.

async function test() {

    await require("photoshop").core.executeAsModal(CreateLayerToTop, {
        "commandName": "test"
    });

}

async function CreateLayerToTop() {

    let cl = await app.activeDocument.activeLayers[0];

    if (cl.parent != null) {

        await require("photoshop").action.batchPlay([
            Layer_Add(),
        ], {
            synchronousExecution: false
        });

        while (cl.parent != null) {

            await require("photoshop").action.batchPlay([
                Layer_Move_Top(),
            ], {
                synchronousExecution: false
            });

            cl = await app.activeDocument.activeLayers[0];

        }

    } else {
        await require("photoshop").action.batchPlay([
            Layer_Add(),
        ], {
            synchronousExecution: false
        });

        await require("photoshop").action.batchPlay([
            Layer_Move_Top(),
        ], {
            synchronousExecution: false
        });
    }




    function Layer_Add() {

        const json = {
            "_obj": "make",
            "_target": [{
                "_ref": "layer"
            }],
            "_isCommand": true
        }
        return json;

    }

    function Layer_Move_Top() {

        const json = {
            "_obj": "move",
            "_target": [{
                "_ref": "layer",
                "_enum": "ordinal",
                "_value": "targetEnum"
            }],
            "to": {
                "_ref": "layer",
                "_enum": "ordinal",
                "_value": "front"
            },
            "_isCommand": true
        }
        return json;

    }

}
1 Like

It works, Thank you!

Thank you everyone involved, @MrL , @Karmalakas, @Wanokuni, @simonhenke.
You’re creating an mazing community and it’s always a pleasure to learn from you!

1 Like

Sorry for the delay - too busy at work and I forget to reply in the evening. Noticed here all the solutions are way too overcomplicated. All you need to do to create a layer on top of others, is deselect all layers and create a new one.

async function test() {
    await require("photoshop").core.executeAsModal(CreateLayerToTop, {
        "commandName": "test"
    });
}

async function CreateLayerToTop() {
  await require("photoshop").action.batchPlay([
    {
      "_obj": "selectNoLayers",
     "_target": [{"_ref": "layer", "_enum": "ordinal", "_value": "targetEnum"}]
    },
    {
      "_obj": "make",
      "_target": [{"_ref": "layer"}]
    },
  ],
  {
    synchronousExecution: false
  });
}

When no layers are selected, Ps creates new layer on the very top by default. I remembered I do this in one of my plugins.

Previously I completely missed the point of the question, sorry for that too :frowning:

Hi @Karmalakas, no problem :slight_smile: I’ve tested that and unfortunately it doesn’t fully work if you have multiple groups like this:

Screenshot 2022-06-30 at 10.23.27

After running your code, new layer was created under Group 5:

Screenshot 2022-06-30 at 10.23.38

Hmm… That is weird. Does it do the same if you click a button to create a layer in Ps while no layers are selected? Even if so, I think this approach + one step move to front would be a better solution than recursive looping. I wonder what happens if you have several nested groups on the very top and then create a layer while nothing is selected - will it create a layer in the first top-most group or in the deepest one :thinking:

Looks like I might have to patch my plugin too if Ps really behaves like this. I’d say that’s a bug, but I doubt it’s going to be fixed ever

It will still move to highest, outer group:
image

IMO moving to the highest idx + 1 is still the most reliable and efficient way.

I agree, that could work. If you were really serious about performance you’d count the Photoshop operations necessary to do the job, in this case

selectNoLayer
make
move

vs.

make
move

but I doubt that the difference will be noticeable at all, since all those operations are pretty quick.

Or that, yes in this case :slight_smile: I wasn’t aware it creates a layer inside a group

I was curious and did some testing :smiley: if there’s no group on top it is actually faster, since you’re saving the two getters for the layer count and background layer check.

Create Layer → move to highest Idx
~ 115-120 ms

Deselect Layers → Create Layer → Move to front
~ 80-100 ms

However, if there’s a group at the top, it gets slower, around 120-140ms.
Also, it seems like with the latter approach you can see the two steps quickly flicker in the layer panel (when it’s in the top group and then gets moved to the front).

TLDR;
it probably doesn’t matter which of the two you use :grin:

2 Likes

In this case you have to check it there is a layer or a group above the one you’ve created. Otherwise you’ll get an error The command “Move” is not currently available. Before 23.4.1 the error would not show up.

Do you need to care about this error? If it doesn’t move, then it’s already on top

It will be shown as pop-up and will stop your script. Before 23.4.1 nothing would happen and the BP would continue.

Screenshot 2022-06-30 at 11.41.01

Is it a catchable error maybe? Or could you provide to command _options: {dialogOptions: “dontDisplay”} maybe? :thinking:

I just now had a thought, what if you, after creating a layer, no matter where it is, move it not to front, but to first (or last)? Would it work? Or would it still move layer inside a group if there’s one at the top?

        {
            "_obj": "move",
            "_target": [{"_ref": "layer", "_enum": "ordinal"}],
            "to": {
                "_ref": "layer",
                "_enum": "ordinal",
                "_value": "first"
            },
        }

I’m already adding this but the error will still be shown.

With "_value": "first" I’m still getting an error and the layer is not moving at all:

I had “selected layer” selected, I’ve created the new one, then used your script (move with value “first”) and nothing apart from error happened.

Looks like Ps doesn’t consider group layers as the layers above. I really think it’s a bug. Ping @kerrishotts