Copy link to clipboard
Copied
Hey guys! I have this script that I found here in the community and I've already tested some others. They all close groups containing layers, but if the group is empty, the script stops.
Is there any way for this script to work even on empty groups? My client uses an automation and needs all groups open.
Thanks if anyone can help.
Ther script i am using is this:
Expands even empty groups.
openAllLayerSets(app.activeDocument);
function openAllLayerSets(parent) {
for (var setIndex = 0; setIndex < parent.layerSets.length; setIndex++) {
if (parent.layerSets[setIndex].layers.length) {
app.activeDocument.activeLayer = parent.layerSets[setIndex].layers[0];
openAllLayerSets(parent.layerSets[setIndex]);
} else {
parent.layerSets[setIndex].artLayers.add()
app.activeDocument.activeLayer = paren
...
1. Does not affect the visibility of layers
2. Expands all groups, including empty ones
3. Restores the selection of layers
4. Fast as possible
5. Not compatible with old versions of PS (seemingly less CC 2017) due to the lack of properties 'hasBackgroundLayer' and 'targetLayersIDs' (can be adapted)
#target photoshop
activeDocument.suspendHistory('Expand all layerSets', 'openAllLayerSets()')
function openAllLayerSets() {
var lr = new AM('layer'),
doc = new AM('document'),
...
Copy link to clipboard
Copied
The easy way would be wrapping the affected part in a try-clause.
Copy link to clipboard
Copied
Expands even empty groups.
openAllLayerSets(app.activeDocument);
function openAllLayerSets(parent) {
for (var setIndex = 0; setIndex < parent.layerSets.length; setIndex++) {
if (parent.layerSets[setIndex].layers.length) {
app.activeDocument.activeLayer = parent.layerSets[setIndex].layers[0];
openAllLayerSets(parent.layerSets[setIndex]);
} else {
parent.layerSets[setIndex].artLayers.add()
app.activeDocument.activeLayer = parent.layerSets[setIndex]
openAllLayerSets(parent.layerSets[setIndex]);
parent.layerSets[setIndex].layers[0].remove()
}
};
};
If this is not necessary, then it is better to heed the advice of the @c.pfaffenbichler
Copy link to clipboard
Copied
Worked perfectly, Thank @jazz-y
Copy link to clipboard
Copied
Excellent @jazz-y – it has always bugged me that there is a "collapse all groups" command, but no command to "expand all groups".
I wanted to retain the selected layer from before running the script, which was easy enough.
(Script edited)
// Expand All Layer Groups
expandAllLayerGroups();
function expandAllLayerGroups() {
// If no layer is selected, the top layer will be selected... could this be improved?
// To do: check and restore active layer visibility!
var origLayer = activeDocument.activeLayer;
openAllLayerSets(app.activeDocument);
function openAllLayerSets(parent) {
// by jazz-y
for (var setIndex = 0; setIndex < parent.layerSets.length; setIndex++) {
if (parent.layerSets[setIndex].layers.length) {
app.activeDocument.activeLayer = parent.layerSets[setIndex].layers[0];
openAllLayerSets(parent.layerSets[setIndex]);
} else {
parent.layerSets[setIndex].artLayers.add();
app.activeDocument.activeLayer = parent.layerSets[setIndex];
openAllLayerSets(parent.layerSets[setIndex]);
parent.layerSets[setIndex].layers[0].remove();
}
}
}
activeDocument.activeLayer = origLayer;
}
That being said, if there were no layers selected before running the script, then the top layer will be selected. This is due to the bug in scripting where the top layer is returned when no layer is active/selected. Just wondering if there is a way to improve this further, so if there is no layer selected before running the script, then no layer is selected afterwards. If a layer is selected, then it would still be selected afterwards.
Edit: I didn't even consider the complexity of multiple/discontinuous layers being selected!
Copy link to clipboard
Copied
@Stephen Marsh, this is a recursive function. At the moment, saving the active layer and restoring the selection occurs every time it is called (for all nested groups), which seems redundant. You need to move these commands outside the function.
To save layer selection and restore later, I usually use this code:
var s2t = stringIDToTypeID;
// get selected layes count with layers IDs
(r = new ActionReference()).putProperty(s2t('property'), p = s2t('targetLayersIDs'));
r.putEnumerated(s2t('document'), s2t('ordinal'), s2t('targetEnum'));
var sel = executeActionGet(r).getList(p);
// restore selection
if (sel.count) {
r = new ActionReference();
for (var i = 0; i < sel.count; i++) { r.putIdentifier(s2t('layer'), sel.getReference(i).getIdentifier()) }
(d = new ActionDescriptor()).putReference(s2t('target'), r);
executeAction(s2t('select'), d, DialogModes.NO);
} else {
(r = new ActionReference()).putEnumerated(s2t('layer'), s2t('ordinal'), s2t('targetEnum'));
(d = new ActionDescriptor()).putReference(s2t('target'), r);
executeAction(s2t('selectNoLayers'), d, DialogModes.NO);
}
Copy link to clipboard
Copied
@Stephen Marsh, this is a recursive function. At the moment, saving the active layer and restoring the selection occurs every time it is called (for all nested groups), which seems redundant. You need to move these commands outside the function.
Thank you! You are of course correct. I wanted to keep it all in the function and didn't think about that!
I'll fix up the previous code and look into implementing your layer re-selection code.
Copy link to clipboard
Copied
1. Does not affect the visibility of layers
2. Expands all groups, including empty ones
3. Restores the selection of layers
4. Fast as possible
5. Not compatible with old versions of PS (seemingly less CC 2017) due to the lack of properties 'hasBackgroundLayer' and 'targetLayersIDs' (can be adapted)
#target photoshop
activeDocument.suspendHistory('Expand all layerSets', 'openAllLayerSets()')
function openAllLayerSets() {
var lr = new AM('layer'),
doc = new AM('document'),
currentSelection = doc.getProperty('targetLayersIDs'),
layers = expandGroups(getLayersCollection());
if (layers.length) {
doc.selectLayers(layers)
doc.deleteLayers(layers)
}
if (currentSelection.count) {
var s = [];
for (var i = 0; i < currentSelection.count; i++)
s.push(currentSelection.getReference(i).getIdentifier())
doc.selectLayers(s)
} else doc.selectNoLayers()
function expandGroups(layers, toDelete) {
toDelete = toDelete ? toDelete : [];
for (var a in layers) {
if (layers[a].layerKind == 7) {
if (layers[a].length) {
lr.selectLayers([layers[a][0].id])
expandGroups(layers[a], toDelete)
}
else {
lr.add()
lr.moveLayer(lr.getProperty('itemIndex', layers[a].id) - 2)
toDelete.push(lr.getProperty('layerID'))
}
}
}
return toDelete
}
function getLayersCollection() {
var doc = new AM('document'),
lr = new AM('layer'),
indexFrom = doc.getProperty('hasBackgroundLayer') ? 0 : 1,
indexTo = doc.getProperty('numberOfLayers');
return layersCollection(indexFrom, indexTo)
function layersCollection(from, to, parentItem, group) {
parentItem = parentItem ? parentItem : [];
for (var i = from; i <= to; i++) {
var layerSection = lr.getProperty('layerSection', i, true).value;
if (layerSection == 'layerSectionEnd') {
i = layersCollection(i + 1, to, [], parentItem)
continue;
}
var properties = {};
properties.id = lr.getProperty('layerID', i, true)
properties.visible = lr.getProperty('visible', i, true)
properties.layerKind = lr.getProperty('layerKind', i, true)
if (layerSection == 'layerSectionStart') {
for (o in properties) { parentItem[o] = properties[o] }
group.push(parentItem);
return i;
} else {
parentItem.push(properties)
}
}
return parentItem
}
}
function AM(target, order) {
var s2t = stringIDToTypeID,
t2s = typeIDToStringID;
target = target ? s2t(target) : null;
this.getProperty = function (property, id, idxMode) {
property = s2t(property);
(r = new ActionReference()).putProperty(s2t('property'), property);
id != undefined ? (idxMode ? r.putIndex(target, id) : r.putIdentifier(target, id)) :
r.putEnumerated(target, s2t('ordinal'), order ? s2t(order) : s2t('targetEnum'));
return getDescValue(executeActionGet(r), property)
}
this.selectLayers = function (IDList) {
var r = new ActionReference()
for (var i = 0; i < IDList.length; i++) {
r.putIdentifier(s2t("layer"), IDList[i])
}
(d = new ActionDescriptor()).putReference(s2t("target"), r)
d.putBoolean(s2t("makeVisible"), false)
executeAction(s2t("select"), d, DialogModes.NO)
}
this.add = function () {
(r = new ActionReference()).putClass(target);
(d = new ActionDescriptor()).putReference(s2t("null"), r);
executeAction(s2t("make"), d, DialogModes.NO);
}
this.moveLayer = function (to) {
(r = new ActionReference()).putEnumerated(s2t("layer"), s2t('ordinal'), s2t('targetEnum'));
(d = new ActionDescriptor()).putReference(s2t('null'), r);
(r1 = new ActionReference()).putIndex(s2t('layer'), to);
d.putReference(s2t('to'), r1);
executeAction(s2t('move'), d, DialogModes.NO);
}
this.selectNoLayers = function () {
(r = new ActionReference()).putEnumerated(s2t("layer"), s2t('ordinal'), s2t('targetEnum'));
(d = new ActionDescriptor()).putReference(s2t('target'), r);
executeAction(s2t('selectNoLayers'), d, DialogModes.NO);
}
this.deleteLayers = function (IDList) {
var r = new ActionReference()
for (var i = 0; i < IDList.length; i++) {
r.putIdentifier(s2t("layer"), IDList[i])
}
(d = new ActionDescriptor()).putReference(s2t("target"), r)
executeAction(s2t("delete"), d, DialogModes.NO)
}
function getDescValue(d, p) {
switch (d.getType(p)) {
case DescValueType.OBJECTTYPE: return { type: t2s(d.getObjectType(p)), value: d.getObjectValue(p) };
case DescValueType.LISTTYPE: return d.getList(p);
case DescValueType.REFERENCETYPE: return d.getReference(p);
case DescValueType.BOOLEANTYPE: return d.getBoolean(p);
case DescValueType.STRINGTYPE: return d.getString(p);
case DescValueType.INTEGERTYPE: return d.getInteger(p);
case DescValueType.LARGEINTEGERTYPE: return d.getLargeInteger(p);
case DescValueType.DOUBLETYPE: return d.getDouble(p);
case DescValueType.ALIASTYPE: return d.getPath(p);
case DescValueType.CLASSTYPE: return d.getClass(p);
case DescValueType.UNITDOUBLE: return (d.getUnitDoubleValue(p));
case DescValueType.ENUMERATEDTYPE: return { type: t2s(d.getEnumerationType(p)), value: t2s(d.getEnumerationValue(p)) };
default: break;
};
}
}
}
Copy link to clipboard
Copied
@jazz-y – thank you, just what I wanted to do but couldn't!
Copy link to clipboard
Copied
@jazz-y - I used your layer re-selection code and noticed that layer visibility had changed after the script for loop was completed. How hard is it to retain the selected layers on/off visibility with the layer/s selection?
Copy link to clipboard
Copied
Not difficult. Just add before execute action 'select'
d.putBoolean(s2t("makeVisible"), false);
Copy link to clipboard
Copied
Thank you @jazz-y – I just tested again and can't reproduce...
Copy link to clipboard
Copied
OK, I can get it to mess with visibility. Left before, right after the for loop over top-level layerSets (sorry, they are inside a layerSet which may change things):
It's the 1.1 version script here:
Copy link to clipboard
Copied
Understood what the problem is. I thought that the task was to avoid changing the visibility of the layer during selection, but it turns out that the task is to preserve the visibility of the layer (even if it was changed during the script).
The easiest way is to save the visibility state of all layers and after the script runs, restore it (all, since we know very well that if you change the visibility of the nested elements of the group, the visibility of the parent group will also change):
var currentLayersState = getLayersVisiblity();
// some code here
setLayersVisiblity(currentLayersState)
function getLayersVisiblity() {
var s2t = stringIDToTypeID,
t2s = typeIDToStringID;
(r = new ActionReference()).putProperty(s2t('property'), p = s2t('targetLayersIDs'));
r.putEnumerated(s2t('document'), s2t('ordinal'), s2t('targetEnum'));
var targetLayers = executeActionGet(r).getList(p),
seletion = [],
visiblity = {};
for (var i = 0; i < targetLayers.count; i++) seletion.push(targetLayers.getReference(i).getIdentifier());
(r = new ActionReference()).putProperty(s2t('property'), p = s2t('numberOfLayers'));
r.putEnumerated(s2t('document'), s2t('ordinal'), s2t('targetEnum'));
var len = executeActionGet(r).getInteger(p);
for (var i = 1; i <= len; i++) {
(r = new ActionReference()).putProperty(s2t('property'), p = s2t('layerSection'));
r.putIndex(s2t('layer'), i);
if (t2s(executeActionGet(r).getEnumerationValue(p)) == 'layerSectionEnd') continue;
(r = new ActionReference()).putProperty(s2t('property'), p = s2t('layerID'));
r.putIndex(s2t('layer'), i);
var id = executeActionGet(r).getInteger(p);
(r = new ActionReference()).putProperty(s2t('property'), p = s2t('visible'));
r.putIndex(s2t('layer'), i);
var visible = executeActionGet(r).getBoolean(p);
visiblity[id] = visible;
}
return { selection: seletion, visiblity: visiblity }
}
function setLayersVisiblity(layersStateObject) {
var s2t = stringIDToTypeID;
for (var a in layersStateObject.visiblity) {
makeVisible = layersStateObject.visiblity[a] ? "show" : "hide";
(r = new ActionReference()).putIdentifier(s2t('layer'), a);
(d = new ActionDescriptor()).putReference(s2t('target'), r);
executeAction(s2t(makeVisible), d, DialogModes.NO);
}
if (layersStateObject.selection.length) {
var r = new ActionReference()
for (var i = 0; i < layersStateObject.selection.length; i++)
r.putIdentifier(s2t("layer"), layersStateObject.selection[i]);
(d = new ActionDescriptor()).putReference(s2t("target"), r);
d.putBoolean(s2t("makeVisible"), false);
executeAction(s2t("select"), d, DialogModes.NO);
} else {
(r = new ActionReference()).putEnumerated(s2t("layer"), s2t('ordinal'), s2t('targetEnum'));
(d = new ActionDescriptor()).putReference(s2t('target'), r);
executeAction(s2t('selectNoLayers'), d, DialogModes.NO);
}
}
Copy link to clipboard
Copied
Thank you @jazz-y – this works great!
Copy link to clipboard
Copied
Understood what the problem is. I thought that the task was to avoid changing the visibility of the layer during selection, but it turns out that the task is to preserve the visibility of the layer (even if it was changed during the script).
The easiest way is to save the visibility state of all layers and after the script runs, restore it (all, since we know very well that if you change the visibility of the nested elements of the group, the visibility of the parent group will also change):
By @jazz-y
var uniq_name = "TMP"+Math.random();
var d = new ActionDescriptor();
var r = new ActionReference();
r.putClass(stringIDToTypeID("compsClass"));
d.putReference(stringIDToTypeID("null"), r);
var d1 = new ActionDescriptor();
d1.putBoolean(stringIDToTypeID("useVisibility"), true);
d1.putBoolean(stringIDToTypeID("usePosition"), false);
d1.putBoolean(stringIDToTypeID("useAppearance"), false);
d1.putString(stringIDToTypeID("title"), uniq_name);
d.putObject(stringIDToTypeID("using"), stringIDToTypeID("compsClass"), d1);
executeAction(stringIDToTypeID("make"), d, DialogModes.NO);
// some code here
var d = new ActionDescriptor();
var r = new ActionReference();
r.putName(stringIDToTypeID("compsClass"), uniq_name);
d.putReference(stringIDToTypeID("null"), r);
executeAction(stringIDToTypeID("applyComp"), d, DialogModes.NO);
var d = new ActionDescriptor();
var r = new ActionReference();
r.putName(stringIDToTypeID("compsClass"), uniq_name);
d.putReference(stringIDToTypeID("null"), r);
executeAction(stringIDToTypeID("delete"), d, DialogModes.NO);
Copy link to clipboard
Copied
... аааааааааааааааааааа!!!! 🙂 Гениально!
Я думал layerComps только для хипстеров со смузи, которые всякими UI/UX занимаются 🙂
Copy link to clipboard
Copied
Данный скрипт портит видимость групп (включает её).
: )
Copy link to clipboard
Copied
Мопед не мой, я только гайки подкрутил 🙂
Copy link to clipboard
Copied
Тут чё-тo мне в голову мысля стукнула. Решил озвучить.
Если не добаввлять в каждую папку слой и потом его удалять, а
создать any-слой, и потом
двигать его в папку командой move(target, ElementPlacement.INSIDE),
после его развыделять и выделять опять.
И так со всеми папками. Не нарушает видимость групп.
Если слой не перевыделять, то получим интересный прикол при перемещении в закрытую группу.
Визуально на палитре нет активного(выделенного) слоя, но activeLayer как и положено не layers[0].