• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
0

Get previous and next layer names through script.

Participant ,
May 30, 2020 May 30, 2020

Copy link to clipboard

Copied

Hi everyone,

 

I am looking for the most efficient way to get both the next and previous layer names in the layer stack. I need it to work a little differently then the usual Photoshop behaviour. Photoshop usually skips invisible layers, and will not get "inside" a folded layerset. I need a function that would get both visible and invisible layers, and would treat all layerset as if it was unfolded.

 

I did manage to make things work using a recursive function. I basically build myself an array with all the layers, then loop through it until I find the current active layer. I can then use "myArray[i-1]" and "myArray[i+1]" to get what I am looking for. It works, but it's clearly not an efficient way to do it. Gets pretty slow with documents with large number of layers...

 

I was hoping someone might be able to help me with some action manager magic... 🙂

Basically, all I have is "app.activeDocument.activeLayer", and I want a function (or functions) that would return the name of the layer directly above and directly under it, be it visible or not, be it part of a layerset or not...


Any thoughts ?


Thanks !

 

J.

TOPICS
Actions and scripting , SDK

Views

2.9K

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct answer

Participant , May 31, 2020 May 31, 2020

Thanks mate.  This was of great help.

 

It was having trouble with both the first and last layers in the stack, though.  Made a few little tweaks and everything seems fine now.  

 

function getSurroundingLayersNames() {
	// get names of next layer above and below;

	// Get layer index
	var ref1 = new ActionReference();
		ref1.putProperty (stringIDToTypeID ("property"), stringIDToTypeID ("itemIndex"));
		ref1.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
	va
...

Votes

Translate

Translate
Adobe
Community Expert ,
May 30, 2020 May 30, 2020

Copy link to clipboard

Copied

A Group has a »layerSectionEnd« that’s invisible in the Layers Panel and a »layerSectionStart«, so the next layer above a Layer below a Group would not be a Layer in the Group but the »layerSectionEnd« … so what do you mean by »invisible« exactly? 

 

If you had posted a more meaningful description that included screenshots or sketches and the intended results one would not have to ask about such a thing. 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
May 30, 2020 May 30, 2020

Copy link to clipboard

Copied

Yeah, I guess "invisible" wasn't the good term.  I simply meant "not visible", or "with the visibility turned off".

 

Consider those 2 figures : 

 

Fig 1Fig 1 Fig 2Fig 2

 

In Fig1, if the Background is selected, and you move to the "next layer" using the usual kb shortcuts, PS will select Layer 2, ignoring the (not visible) Layer 1. 

 

In Fig2, if the Layer 2 is selected, and again, you move to the "next layer", Group 1 gets selected.

 

I am trying to find a way to avoid this behaviour, and always get the layer that sits immediately above or below the active one. 

 

Example : If Layer 2 is the active layer, previousLayer would be Layer 1, and nextLayer would be Layer 3, no matter if Group 1 is unfolded or not.

 

Is that clearer ?

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
May 30, 2020 May 30, 2020

Copy link to clipboard

Copied

For that names seem useless and the index or the identifier would seem to make more sense … 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
May 30, 2020 May 30, 2020

Copy link to clipboard

Copied

The names can't be "useless", it's what I am looking for, what I am trying to get.  I don't really care how I get there, but I need the names.  (and just the names, I don't need the access any of those layers properties afterward)

 

Finding the activeLayer's index was my first idea, but it seems like (still refering to my previous post's figures), that if Layer 2 has index 4, Group 1 is gonna have index 5, which means I can't simply find the activeLayer's index and do +1 and -1 to get the right next and previous layers...

 

 

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
May 30, 2020 May 30, 2020

Copy link to clipboard

Copied

And how do you handle multiple layers of the same name? 

Referencing Layers by name is not prudent. 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
May 30, 2020 May 30, 2020

Copy link to clipboard

Copied

I am not.

 

Again, I am not trying to use those two layer names (previous one and next one), I just need to display those names.  Once I get the names, they get displayed on a panel and that's it. 

 

I've seen your other post.  I'll look into it as soon as I have a moment.  Thanks for the help.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
May 31, 2020 May 31, 2020

Copy link to clipboard

Copied

// get names of next layer above and below;
// 2020, use it at your own risk;
if (app.documents.length > 0) {
var ref1 = new ActionReference();
ref1.putProperty (stringIDToTypeID ("property"), stringIDToTypeID ("itemIndex"));
ref1.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
var layerDesc = executeActionGet(ref1);
var theIndex = layerDesc.getInteger(stringIDToTypeID ("itemIndex"));
// check background;
var ref4 = new ActionReference();
ref4.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
var docDesc = executeActionGet(ref4);
var hasBackground = docDesc.getBoolean(stringIDToTypeID("hasBackgroundLayer"));
var theNumberOfLayers = docDesc.getInteger(stringIDToTypeID("numberOfLayers"));
// determine the indices;
if (hasBackground == false) {
var index1 = theIndex-1;
var index2 = theIndex+1;
} else {
var index1 = theIndex-2;
var index2 = theIndex;
};
// get names;
while (isLayerSectionEnd(index1) == true && index1 < theNumberOfLayers) {index1--};
while (isLayerSectionEnd(index2) == true && index2 < theNumberOfLayers) {index2++};
var theNameBelow = getLayerName (index1);
var theNameAbove = getLayerName (index2);
alert ("\nbelow "+theNameBelow+"\nabove "+theNameAbove);
};
////// get name //////
function getLayerName (theIndex) {
var ref = new ActionReference();
ref.putIndex( charIDToTypeID( "Lyr " ), theIndex);
var layerDesc = executeActionGet(ref);
var theName = layerDesc.getString(stringIDToTypeID ("name"));
return theName
};
////// is layer section end //////
function isLayerSectionEnd (theIndex) {
var ref = new ActionReference();
ref.putIndex( charIDToTypeID( "Lyr " ), theIndex);
var layerDesc = executeActionGet(ref);
try {
var layerSet = typeIDToStringID(layerDesc.getEnumerationValue(stringIDToTypeID("layerSection")));
// if group end;
if (layerSet == "layerSectionEnd") {return true};
} catch (e) {return false};
};

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
May 30, 2020 May 30, 2020

Copy link to clipboard

Copied

You can give this a try: 

 

 

// get names of next layer above and below;
// 2020, use it at your own risk;
if (app.documents.length > 0) {
var ref1 = new ActionReference();
ref1.putProperty (stringIDToTypeID ("property"), stringIDToTypeID ("itemIndex"));
ref1.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
var layerDesc = executeActionGet(ref1);
var theIndex = layerDesc.getInteger(stringIDToTypeID ("itemIndex"));
// check background;
var ref4 = new ActionReference();
ref4.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") ); 
var docDesc = executeActionGet(ref4);
var hasBackground = docDesc.getBoolean(stringIDToTypeID("hasBackgroundLayer"));
var theNumberOfLayers = docDesc.getInteger(stringIDToTypeID("numberOfLayers"));// determine the indices;
if (hasBackground == false) {
var index1 = theIndex-1;
var index2 = theIndex+1;
} else {
var index1 = theIndex-2;
var index2 = theIndex;
theNumberOfLayers++};
// get below name;
if (index1 >= 1) {
while (isLayerSectionEnd(index1) == true && index1 < theNumberOfLayers) {index1--}
var theNameBelow = getLayerName (index1);
} else {var theNameBelow = "nothing"};
// get above name;
if (index2 < theNumberOfLayers) {
while (isLayerSectionEnd(index2) == true && index2 < theNumberOfLayers) {index2++};
var theNameAbove = getLayerName (index2);
} else {var theNameAbove = "nothing"};
alert ("\nbelow "+theNameBelow+"\nabove "+theNameAbove);
};
////// get name //////
function getLayerName (theIndex) {
var ref = new ActionReference();
ref.putIndex( charIDToTypeID( "Lyr " ), theIndex);
var layerDesc = executeActionGet(ref);
var theName = layerDesc.getString(stringIDToTypeID ("name"));
return theName
};
////// is layer section end //////
function isLayerSectionEnd (theIndex) {
var ref = new ActionReference();
ref.putIndex( charIDToTypeID( "Lyr " ), theIndex);
var layerDesc = executeActionGet(ref);
try {
var layerSet = typeIDToStringID(layerDesc.getEnumerationValue(stringIDToTypeID("layerSection")));
// if group end;
if (layerSet == "layerSectionEnd") {return true};
} catch (e) {return false};
};

 

edited

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
May 31, 2020 May 31, 2020

Copy link to clipboard

Copied

Thanks mate.  This was of great help.

 

It was having trouble with both the first and last layers in the stack, though.  Made a few little tweaks and everything seems fine now.  

 

function getSurroundingLayersNames() {
	// get names of next layer above and below;

	// Get layer index
	var ref1 = new ActionReference();
		ref1.putProperty (stringIDToTypeID ("property"), stringIDToTypeID ("itemIndex"));
		ref1.putEnumerated( charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
	var layerDesc = executeActionGet(ref1);
	var theIndex = layerDesc.getInteger(stringIDToTypeID ("itemIndex"));

	// check background;
	var ref4 = new ActionReference();
		ref4.putEnumerated( charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt") );
	var docDesc = executeActionGet(ref4);
	var hasBackground = docDesc.getBoolean(stringIDToTypeID("hasBackgroundLayer"));
	var theNumberOfLayers = docDesc.getInteger(stringIDToTypeID("numberOfLayers"));

	// determine the indices;
	var indexPrev;
	var indexNext;
	if (hasBackground == false) {
		indexPrev = theIndex-1;
		indexNext = theIndex+1;
	}
	else {
		indexPrev = theIndex-2;
		indexNext = theIndex;
	}
	
	while (isLayerSectionEnd(indexPrev) == true && indexPrev > 0) {indexPrev--;}
	while (isLayerSectionEnd(indexNext) == true && indexNext < theNumberOfLayers) {indexNext++;}

	if ((indexPrev < 0 && hasBackground) || (indexPrev == 0 && !hasBackground)) {
		// Bottom layer of the stack
		var theNameBelow = "";}
	else {
		var theNameBelow = getLayerName (indexPrev);}

	if (indexNext > theNumberOfLayers) {
		// Top layer of the stack
		var theNameAbove = "";}
	else {
		var theNameAbove = getLayerName (indexNext);}

	returnArray = new Array(theNameAbove,theNameBelow);
	return returnArray;
}

////// get name //////
function getLayerName (theIndex) {
	try{
		var ref = new ActionReference();
			ref.putIndex( charIDToTypeID( "Lyr " ), theIndex);
		var layerDesc = executeActionGet(ref);
		var theName = layerDesc.getString(stringIDToTypeID ("name"));
		return theName;
	}
	catch (e) {return e;}
}
////// is layer section end //////
function isLayerSectionEnd (theIndex) {
	try {
		var ref = new ActionReference();
			ref.putIndex( charIDToTypeID( "Lyr " ), theIndex);
		var layerDesc = executeActionGet(ref);

			var layerSet = typeIDToStringID(layerDesc.getEnumerationValue(stringIDToTypeID("layerSection")));
			// if group end;
			if (layerSet == "layerSectionEnd") {return true;}
	} catch (e) {return false;}
}

 

Thanks again.

 

J.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
May 31, 2020 May 31, 2020

Copy link to clipboard

Copied

Hi jonathankemp,

 

I tested your code as well as the once shared by c_pfaffenbichler, i see that it crashes if the selected layer is the on the top and bottom of the stack for both. Did you try my code, i have edited it and it seems to be working fine in these cases as well.

 

-Manan

 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jun 01, 2020 Jun 01, 2020

Copy link to clipboard

Copied

Yeah, insufficient testing … 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
May 31, 2020 May 31, 2020

Copy link to clipboard

Copied

Here is my stab at the implementation. It will prompt the name of the content layer before and after the currently selected layer. This is my first ever Photoshop scripting code and that too Action Manager code, so do test it at your own risk. Anything can happen, it might fry your computer 🙂

 

 

var s2t = stringIDToTypeID;
var t2s = typeIDToStringID;
var i = activeDocument.activeLayer.itemIndex

var layerCount = getProperty("document", "numberOfLayers").getInteger(s2t("numberOfLayers"))
var bgLayer = getProperty("document", "hasBackgroundLayer").getBoolean(s2t("hasBackgroundLayer")) ? 0 : 1
!bgLayer && i--
var retval = getName(i + 1, true)
if(retval != undefined)
	alert("Before Layer name is " + retval)

var retval = getName(i-1, false)
if(retval != undefined)
	alert("After Layer name is " + retval)
function getProperty(className, property, index)
{
	var r = new ActionReference(), p = s2t(property)
	r.putProperty(s2t('property'), p)
	if(index != undefined)
		r.putIndex(s2t(className),index)
	else
		r.putEnumerated(s2t(className), s2t('ordinal'), s2t('targetEnum'))
	var a = executeActionGet(r)
	return a
}
function getName(i, inc)
{
	if(i < bgLayer || i > layerCount)
	{
		return
	}
	var prop = "layerSection"
	var retval = t2s(getProperty("layer", prop, i).getEnumerationValue(s2t(prop)))
	if(retval == 'layerSectionEnd' || retval == 'layerSectionStart')
	{
		inc ? i++ : i--
		return getName(i, inc)
	}
	else
	{
		var prop = "name"
		var retval =  getProperty("layer", prop, i).getString(s2t(prop))
		return retval
	}
}

 

 

 

Question for Action Manager veterans, this works fine but i have noticed that the when the background layer is present then getting the name using index 0 or 1 returns the layer above background layer. The nameof background is not returned, is this so or i missed something?

 

-Manan

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
May 31, 2020 May 31, 2020

Copy link to clipboard

Copied

Another question for the experienced folks, is there a way to jump from ActionDescriptor to the JS DOM object and vice versa. For ex i get ActionDescriptor for layer, from that can i get the JS object, i suspect not just want to confirm

 

-Manan

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
May 31, 2020 May 31, 2020

Copy link to clipboard

Copied

Yeah, one needs to correct the index depending on whether a Background Layer exists or not. 

Edit: For an example see the code I posted titled»// get names of next layer above and below;«

 

Does combining AM- and DOM-code give you any trouble? 

Could you give an example in which it does not work as intended or expected? 

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
May 31, 2020 May 31, 2020

Copy link to clipboard

Copied

Thanks for the pointers. I was indeed adjusting the index, the code had a small glitch with regards to a condition check due to which wrong code was executing. I have edited it out, now its working fine.

 

As regards AM and DOM code, i did not run into any issues. Was curious to know if that was even possible, for ex. if we have an ActionDescriptor for a document can we get the JS DOM object for it. For example lets consider a situation where we have to target a layer by its index(this seems to be not possible via DOM), once i get this object via AM can i get the layer DOM object to access its properties that would be available in that domain. I hope this makes sense

 

-Manan

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Community Expert ,
Jun 01, 2020 Jun 01, 2020

Copy link to clipboard

Copied

LATEST

I don’t think there should be any DOM Properties that one cannot determine via AM-code, too. Do you know any? 

And to apply DOM Methods one may have to make the object active (via AM-code).  

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines