How to highlighted select a specified layer?

Contributor ,
Aug 15, 2022 Aug 15, 2022

Copy link to clipboard

Copied

How to select a specified layer with a script? For the same effect as clicking with the mouse, this layer must be highlighted.

tempsnip.png

I have tried the following methods, which are not always feasible:

app.activeDocument.activeLayer=app.activeDocument.layers[0]

app.activeDocument.layers[0].hasSelectedArtwork=true

When running the script, if a layer (highlighted) has been selected, they are feasible, but when running the script, if the specified layer had selected but not highlighted ( the mouse focus is not on the layer panel ), they are not feasible (or they have run correctly and selected the specified layer, but the layer is not highlighted).

 

So, the focus of my question is how to highlight select layers, for the same effect as clicking with the mouse. I do this mainly to run the "Duplicate Layer" command in the layer panel menu. If the layer is not highlighted, the "Duplicate Layer" command is not available even though the layer is already selected.

 

Looking forward to your response and many Thx for help and ideas.

TOPICS
Scripting

Views

220

Likes

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 2 Correct answers

Community Expert , Aug 15, 2022 Aug 15, 2022

The highlighting of a layer is a big problem because with ExtendScript there's no way to do so. However, what you can do is cause a condition which brings this about. Some sitations could be, adding a new layer for example. Its' behavior is to be automatically highlighted. Another nuance of this behavior is that if a layer happened to be deleted, the layer below it becomes highlighted. So we could apparently use enough redraws during a script which adds and removes a new layer, to cause the high

...

Likes

Translate

Translate
Community Expert , Aug 16, 2022 Aug 16, 2022

Oh, this just IN! A brand-new technique which uses more actions to highlight any layer.

What it does is put a temporary path item to your desired layer and selects that item, then runs an action which uses "Locate Object" from the layers' panel to bring a highlighted selection to that item. Apparently the behavior of Illustrator is to automatically highlight the parent layer of an object that has recently exited the Isolation Mode!

This actually works really conveniently for this situation, so m

...

Likes

Translate

Translate
Community Expert ,
Aug 15, 2022 Aug 15, 2022

Copy link to clipboard

Copied

The highlighting of a layer is a big problem because with ExtendScript there's no way to do so. However, what you can do is cause a condition which brings this about. Some sitations could be, adding a new layer for example. Its' behavior is to be automatically highlighted. Another nuance of this behavior is that if a layer happened to be deleted, the layer below it becomes highlighted. So we could apparently use enough redraws during a script which adds and removes a new layer, to cause the highlighting to both appear and stay. However there is a little issue with not being able to highlight a layer whose index is not 0 in the case of there not being a highlight on any layer in the first place, unless beforehand one ensures to highlight the layer at the very top and then do so to their intended layer.
This is an experimentation method which highlights my layers according to the code, let me know if it ends up working for you.

  /**
   * 0-based index Highlights a layer by adding and removing a temporary layer.
   * @Param {number} layerIndex
   */
  function highlightLayerAtIndex (layerIndex) {
    var doc = app.activeDocument;
    if (layerIndex >= doc.layers.length) {
      throw new Error("The layer index (" + layerIndex + ") is out of bounds (0-" + (doc.layers.length - 1) + ")");
    }
    var indexLayer = doc.layers[layerIndex];
    var tempLayer = doc.layers.add();
    app.redraw();
    tempLayer.move(indexLayer, ElementPlacement.PLACEBEFORE);
    app.redraw();
    tempLayer.remove();
    app.redraw();
  }

	// Ensure a highlight for any scenario.
  highlightLayerAtIndex(0);
	// Highlights the 4th layer.
  highlightLayerAtIndex(3);

This is de facto really undo-heavy, as in how the app.redraw() causes an undo point each time it is used.

Nevertheless, when used along with an action which duplicates a layer, this will create the duplication per Illustrator rules and appends " copy" after the name. It then also removes the highlighting. -- not a problem for us, as the same highlight method can surely rehighlight it.

Likes

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
Contributor ,
Aug 16, 2022 Aug 16, 2022

Copy link to clipboard

Copied

Thank you @Silly-V  for helping me out on this. This is worked within the scope of doc.layers.

 

I tried to highlight a sub layer (sub layer under layer 0 ) with the following code, but it didn't work! How can it be changed to highlight a sub layer? 

 

function highlightLayerAtIndex (layerIndex) {
    var doc = app.activeDocument;
    if (layerIndex >= doc.layers[0].layers.length) {
      throw new Error("The layer index (" + layerIndex + ") is out of bounds (0-" + (doc.layers[0].layers.length - 1) + ")");
    }
    var indexLayer = doc.layers[0].layers[layerIndex];
    var tempLayer = doc.layers[0].layers.add();
    app.redraw();
    tempLayer.move(indexLayer, ElementPlacement.PLACEBEFORE);
    app.redraw();
    tempLayer.remove();
    app.redraw();
  }

  highlightLayerAtIndex(0);  // This also highlights the first layer in the document(doc.layers[0]), Not doc.layers[0].layers[0]

tempsnip.png

 

Many Thx for help and ideas.

 

Likes

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 ,
Aug 16, 2022 Aug 16, 2022

Copy link to clipboard

Copied

Yea, this is where I think the magic sort of ... stops. The issue is of course that the automatic behavior of Illustrator has stopped accommodating ourthese-here work-arounds.

Well, here's what I think: if the objective is to trigger some Illustrator action which specifically works on layers in a highlighted state at least we can move the things from one layer into a top-level layer, then run the procedure to highlight and do whatever-else such as duplicate, then move the new duplicated layer to the new spot.

Maybe this will still be applicable to you?

 

Likes

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 ,
Aug 16, 2022 Aug 16, 2022

Copy link to clipboard

Copied

Oh, this just IN! A brand-new technique which uses more actions to highlight any layer.

What it does is put a temporary path item to your desired layer and selects that item, then runs an action which uses "Locate Object" from the layers' panel to bring a highlighted selection to that item. Apparently the behavior of Illustrator is to automatically highlight the parent layer of an object that has recently exited the Isolation Mode!

This actually works really conveniently for this situation, so much so that this method and action is probably usable for all-time.

  /**
	 * Highlights any layer by inserting a temporary item into it and using an action which enters and exists the isolation mode.
	 * Afterwards, this item is removed.
   * @Param {Layer} targetLayer
   */
  function highlightLayerInContainer (targetLayer) {
		var someTempPath = targetLayer.pathItems.rectangle(0, 0, 200, 200);
    someTempPath.name = "TEMP [for highlighting]";
		app.activeDocument.selection =  null;
		someTempPath.selected = true;
		app.doScript("target layer", "Test Set 2");
		someTempPath.remove();
  }

	highlightLayerInContainer(app.activeDocument.layers.getByName("Layer 5").layers.getByName("Layer 7"));

	// app.doScript("DupeLayer", "Test Set 2"); // * Do custom processing on the layer when it is highlighted.
/version 3
/name [ 10
	54657374205365742032
]
/isOpen 1
/actionCount 2
/action-1 {
	/name [ 9
		447570654c61796572
	]
	/keyIndex 0
	/colorIndex 0
	/isOpen 1
	/eventCount 1
	/event-1 {
		/useRulersIn1stQuadrant 0
		/internalName (ai_plugin_Layer)
		/localizedName [ 5
			4c61796572
		]
		/isOpen 1
		/isOn 1
		/hasDialog 0
		/parameterCount 2
		/parameter-1 {
			/key 1836411236
			/showInPalette -1
			/type (integer)
			/value 1
		}
		/parameter-2 {
			/key 1851878757
			/showInPalette -1
			/type (ustring)
			/value [ 19
				4475706c69636174652053656c656374696f6e
			]
		}
	}
}
/action-2 {
	/name [ 12
		746172676574206c61796572
	]
	/keyIndex 0
	/colorIndex 0
	/isOpen 1
	/eventCount 3
	/event-1 {
		/useRulersIn1stQuadrant 0
		/internalName (ai_plugin_Layer)
		/localizedName [ 5
			4c61796572
		]
		/isOpen 0
		/isOn 1
		/hasDialog 0
		/parameterCount 2
		/parameter-1 {
			/key 1836411236
			/showInPalette -1
			/type (integer)
			/value 19
		}
		/parameter-2 {
			/key 1851878757
			/showInPalette -1
			/type (ustring)
			/value [ 13
				4c6f63617465204f626a656374
			]
		}
	}
	/event-2 {
		/useRulersIn1stQuadrant 0
		/internalName (ai_plugin_Layer)
		/localizedName [ 5
			4c61796572
		]
		/isOpen 0
		/isOn 1
		/hasDialog 0
		/parameterCount 1
		/parameter-1 {
			/key 1836411236
			/showInPalette -1
			/type (integer)
			/value 24
		}
	}
	/event-3 {
		/useRulersIn1stQuadrant 0
		/internalName (ai_plugin_Layer)
		/localizedName [ 5
			4c61796572
		]
		/isOpen 0
		/isOn 1
		/hasDialog 0
		/parameterCount 1
		/parameter-1 {
			/key 1836411236
			/showInPalette -1
			/type (integer)
			/value 25
		}
	}
}

Likes

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
Contributor ,
Aug 16, 2022 Aug 16, 2022

Copy link to clipboard

Copied

Thanks @Silly-V 

An error occurred when I ran this script, as shown in the following figure:

RaymondZJH_0-1660699099641.png

 

Do you perhaps have an idea what I may be overlooking?

Likes

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 ,
Aug 16, 2022 Aug 16, 2022

Copy link to clipboard

Copied

You did save the action part as a .aia file and loaded it, no?

Make sure to save this part as .aia and load in the actions panel. Then the part of the script which is 

app.doScript("target layer", "Test Set 2");

 will work. But that action text cannot be part of your javascript.

 

Likes

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
Contributor ,
Aug 17, 2022 Aug 17, 2022

Copy link to clipboard

Copied

This is more than I could have asked for, thank you so much!! @Silly-V ,

 

I simply added a loop, it worked like a charm:). You made my day! Thank you for helping me out on this.

for (var i=1;i<199;i++) {
        highlightLayerInContainer(app.activeDocument.layers[0].layers[0]); //target layer
        app.doScript("DupeLayer", "Test Set 2");
        app.activeDocument.layers[0].layers[0].name=i+1  //target layer's name is "1", set dupeLayer name is "2" ......and so on
}

 

If you have a way to keep the names of all copied sub layers consistent with the source layer, or if you have a way to change the names of all copied sub layers to be consistent with the source layer, instead of having the word "copy", it will solve the problem in my following post, many thanks in advance for your time and advice!

 

https://community.adobe.com/t5/illustrator-discussions/how-to-copy-layer-structure-by-number-sequenc... 

Likes

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 ,
Aug 17, 2022 Aug 17, 2022

Copy link to clipboard

Copied

It can be a bit of a mess, but at least we are confined to only Layer objects, and a recursive traversal can yield to you such information as nested layer names and indexes.

You can make a 'mirror' object to put instructions about layers into. This object would be a 'node'. A node can have information about it as well as most importantly - other nodes. And those nodes can have their own information and other nodes and so on. Once you have the snapshot of what the original names are you could recursively traverse this tree and assign new names based on that.
Here is some code which has a simple recursive function and traversal example. This example starts out assuming I have "Layer 4" which contains a nested "Layer 5". This Layer 4 is copied, so it gets named "Layer 4 copy" and the nested layer is "Layer 5 copy". Now we can run the code and edit the names however one wishes.

 

Note: JSDoc is handled badly by these forums, it bakes in some poor soul's user-name when you go @ type or @ param (without the spaces). I wonder if those couple of people get awesome forum emails each time I paste such snippets up!

 

 

	/**
	 * @typedef LayerNodeTree
	 * @property {string} name
	 * @property {LayerNodeTree[]} layers
	 */

	/**
	 * Collects layer information into a node tree.
	 * @Param {Document | Layer} entryContainer - Where to start the node tree.
	 */
  function getNestedLayerInfo (entryContainer) {
		/** @TyPe {LayerNodeTree} */
    var node = {
			name : (entryContainer instanceof Document) ? "DOCUMENT" : entryContainer.name,
			layers : [],
		};
		var thisLayer;
		for (var i = 0; i < entryContainer.layers.length; i++) {
			thisLayer = entryContainer.layers[i];
			node.layers.push(getNestedLayerInfo(thisLayer));
		}
		return node;
  }

	/**
	 * Processes a layer node tree with arbitrary processing.
	 * @Param {LayerNodeTree} nodeTree
	 * @Param {Layer | Document} newContainer
	 */
	function processLayerTree (nodeTree, newContainer) {
		
		// alert(nodeTree.name + " : " + newRootContainer.name);
		
		if (newContainer instanceof Layer && nodeTree.name == newContainer.name.replace(/ copy$/, "")) {
			newContainer.name = newContainer.name.replace(/ copy$/, " legit!");
		}

		var thisLayer;
		for (var i = 0; i < newContainer.layers.length; i++) {
			thisLayer = newContainer.layers[i];
			processLayerTree(nodeTree.layers[i], thisLayer);
		}
	}

	var layerInfo = getNestedLayerInfo(app.activeDocument.layers.getByName("Layer 4"));
	// alert(layerInfo.toSource());

	processLayerTree(layerInfo, app.activeDocument.layers.getByName("Layer 4 copy"));

 

 

Likes

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
Contributor ,
Aug 17, 2022 Aug 17, 2022

Copy link to clipboard

Copied

It seems a little difficult to understand, because my knowledge is limited, but anyway, thanks for your time and advice! 👍👍👍 @Silly-V 

Likes

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 ,
Aug 17, 2022 Aug 17, 2022

Copy link to clipboard

Copied

Keep in mind that what makes the recursive methods work is a [data structure] that contains a property which is an array of.. the [data structure].

So a layer will always have the .layers property, and each object in the array-like object that is layer.layers is also a Layer. Therefore a Layer object contains a collection of Layer objects, each one contains its own and so on. You could write numerous loops an keep track of a crazy number of arrays in custom objects, but it will never truly work when you do not know the amount of nested items and they're dynamic. So you never know what you're gonna get and no matter how many loops are written, the users can always have just one more layer in some layer. Therefore, the answer to this conundrum is a function that can run itself. 

var node = {
    name : (entryContainer instanceof Document) ? "DOCUMENT" : entryContainer.name,
    layers : [],
};

This part creates the node object, as you see it has a property that isn't an array and it's symbolic for any node's random properties that one may create as needed - in this case just the layer's name. But the array one is a special property because it will end up containing some nested node objects.

var thisLayer;
for (var i = 0; i < entryContainer.layers.length; i++) {
    thisLayer = entryContainer.layers[i];
    node.layers.push(getNestedLayerInfo(thisLayer));
}

This part here goes over the Illustrator Layer's .layers collection and uses the same function that we're in right now, to process those nested layers. So our original "var node = ..." object will have a .layers array of our custom node objects. If there were no nested layers in the variable "entryContainer", this same loop still works by iterating 0 times and pushing nothing to the node's .layers array because it doesn't get a chance to run and the recursion stops. However, if there was just one layer, the line node.layers.push(getNestedLayerInfo(thisLayer)) will run, and that one layer actually may have 20 layers in its .layers.

Finally, the node object is returned from the recursive function:

return node;


The logic is actually simple if you think of it in terms of this one function working on just the one layer. An Illustrator layer is passed in, we create an object that features its name and starts an empty array for its layers. Then it goes through any layers and runs itself, but we can stop thinking about what nesting level it's at and just proceed to the return and be out of it mental-wise. The only question is: when this function ran through the layer's array, is it going to process a layer? Yes, of course. And how would it do so? Well, we go to the top and see that a Layer object is our parameter, and yes indeed we are processing a layer.. check. Then we create an object that features its name and starts an emty array for its layers. Does the Layer object that is being worked on have a .layers property that is an array that can be full of other Layer objects only? Check. So is this function going to work on any layer? Yep, it sure will! 

Likes

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
Contributor ,
Aug 19, 2022 Aug 19, 2022

Copy link to clipboard

Copied

LATEST

Thank you for your explanation @Silly-V .  Now I seem to have some ideas. I'll still take some time to study...

Likes

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