Photoshop Scriptコード

Scripting Listenerで取得したコードやScriptUIコードを載せています。

セレクトレイヤーダイアログ

概要

指定ドキュメントのグループとレイヤーをツリー表示します。
ツリーアイテムにチェックを入れ、OKボタンを押すとチェックされたレイヤーオブジェクトを配列で返します。
検索窓に文字を入力すると、絞り込み表示(部分一致)になります。ただしグループ配下のレイヤーがマッチした場合、先祖グループも表示されます。

実行画面

  • 実行画面
  • テキストボックス入力
    検索窓入力時
  • アイコン使用時
    アイコン使用時

コード

selectLayerDialog.jsx

function main() {
    if (!documents.length) {
        alert('Document not found');
        return;
    }
    var doc = app.activeDocument;
    var result = selectLayerDialog(doc);
    if (!result || !result.length) {
        return;
    }
    alert(result.join('\n'));
}
 
/**
 * レイヤー一覧ダイアログを表示し、チェックのついたレイヤーオブジェクトを
 * 配列で返す
 * @param {Document} doc - 表示するレイヤー一覧の対象ドキュメント
 * @return {?Array.<ArtLayer|LayerSet>} - チェックのついたレイヤー群。
 *     OKボタン以外でダイアログを閉じた場合はnull
 */
function selectLayerDialog(doc) {
    // # dialog
    // ## list item
    var List = {
        folderIconStr:
            '\u0089PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\f\x00\x00' +
            '\x00\n\b\x06\x00\x00\x00\u0080,\u00BF\u00FA\x00\x00\x00' +
            '\x19tEXtSoftware\x00Adobe ImageReadyq\u00C9e<\x00\x00' +
            '\x00YIDATx\u00DAbTRR\u00FA\u00FF\u00F9\u00F3g\x06\x18\u00E0' +
            '\u00E7\u00E7g8\u00B7u\x1A#\x03\x0E\u00C0\u00E2\u00EB\u00EB' +
            '\u00CB`oo\x0F\x17\u00D8\u00B9s\'\x03>\u00C0\u0082\u00AC\x18' +
            '\x04@|e\u009B\u00E8\u00FF\u00B840._\u00BE\u00FC?\x03\t\u0080' +
            '\u00E5\u00F7\u00EF\u00DF\f\u0083L\u00C3\u009F?\x7Fhl\u0083' +
            '\u008A\u008A\nI\x1A\x00\x02\f\x00\u0091E\x1F\u00C2\u00E8{7' +
            '\u00B1\x00\x00\x00\x00IEND\u00AEB`\u0082',
        root: doc,
        layerList: null,
        currentLayerList: null,
 
        /**
         * ドキュメントのレイヤーを再帰でたどり、検索文字列にマッチする
         * レイヤーをダイアログ内ツリーに追加する
         * @param {TreeView} treeObj - ダイアログ内ツリー
         * @param {string} [text=''] - 検索文字列
         */
        setList: function (treeObj, text) {
            var isLayer = function (layerObj) {
                var typename = layerObj.typename;
                return typename == 'ArtLayer' || typename == 'Layer';
            };
 
            /**
             * レイヤーセットを走査し、リストオブジェクトを取得する
             * @param {Array.<Object>|LayerSet|Document} layerObj - 
             *     ドキュメント、レイヤーセット、もしくはそれらと一部
             *     共通するキーを持つオブジェクトを格納した配列
             * @param {ListItem|TreeView} listObj - 
             *     ダイアログ内ツリーのアイテム要素
             * @param {Array.<Object>} layerList -
             *     レイヤーセットと一部共通するキーを持ったオブジェクト
             *     を格納した配列
             * @return {Array.<Object>} -
             *     レイヤーセットと一部共通するキーを持ったオブジェクト
             *     を格納した配列
             */
            var getLayerList = function (layerObj, listObj, layerList) {
                var layers = layerObj.layers;
                var layer;
                for (var i = 0, len = layers.length; i < len; i++) {
                    layer = layers[i];
                    layerList = layerList.concat(setItem(layer, listObj));
                }
                return layerList;
            };
 
            /**
             * ダイアログ内ツリーに、検索文字列にマッチする
             * レイヤー(セット)名を追加し、対象のレイヤーを
             * オブジェクトに格納し配列で返す
             * @param {Array.<Object>|LayerSet} layerObj -
             *     レイヤーセットもしくは一部共通するキーを持つ
             *     オブジェクトを格納した配列
             * @param {ListItem} listObj -
             *     ダイアログ内ツリーのアイテム要素
             * @return {Array.<Object>} -
             *     レイヤーセットと一部共通するキーを持ったオブジェクト
             *     を格納した配列
             */
            var setItem = function (layerObj, listObj) {
                var parent = layerObj.parent;
                var itemList;
                if (parent == root) {
                    itemList = listObj;
                }
                else {
                    itemList = listObj.items[listObj.items.length - 1];
                }
 
                var name = layerObj.name;
                var isMatching = (name.indexOf(searchText) != -1);
                var item;
                if (isLayer(layerObj)) {
                    if (!isMatching) {
                        return [];
                    }
                    item = itemList.add('item', name);
                    item.checked = false;
                    return [{
                        'orig': layerObj.orig || layerObj,
                        'parent': parent,
                        'typename': layerObj.typename,
                        'name': name,
                        'layers': []
                    }];
                }
 
                item = itemList.add('node', layerObj.name);
                item.expanded = true; // show children
                item.checked = false;
                //item.image = folderIconStr; // 520 err (first several times)
                var layerList = getLayerList(layerObj, itemList, []);
                if (isMatching || layerList.length) {
                    return [{
                        'orig': layerObj.orig || layerObj,
                        'parent': parent,
                        'typename': layerObj.typename,
                        'name': name,
                        'layers': layerList
                    }];
                }
                itemList.remove(item); // no match and empty node
                return [];
            };
 
            var folderIconStr = this.folderIconStr;
            var root = this.root;
            var layerObj = this.layerList || root;
            var searchText = (text == null) ? '' : text;
            var layerList = getLayerList(layerObj, treeObj, []);
            var currentLayerList = {'layers': layerList};
            this.layerList = this.layerList || currentLayerList;
            this.currentLayerList = currentLayerList;
        },
 
        /**
         * ダイアログ内ツリーのアイテムを走査し、チェックのついた
         * アイテムに該当するレイヤーを配列で返す
         * @param {TreeView} treeObj - ダイアログ内ツリー
         * @return {Array.<Layer|LayerSet>} - レイヤーオブジェクト
         */
        getCheckedLayer: function (treeObj) {
            var getLayerList = function (layerObj, listObj, layerList) {
                var layers = layerObj.layers;
                var items = listObj.items;
                var layer, item;
                for (var i = 0, len = layers.length; i < len; i++) {
                    layer = layers[i];
                    item = items[i];
                    layerList = layerList.concat(setLayerObj(layer, item));
                }
                return layerList;
            };
            var setLayerObj = function (layerObj, listObj) {
                var layers = listObj.checked ? [layerObj.orig] : [];
                if (listObj.type == 'item') {
                    return layers;
                }
                return getLayerList(layerObj, listObj, layers);
            };
            var layerObj = this.currentLayerList;
            return getLayerList(layerObj, treeObj, []);
        }
    };
 
    // ## dialog
    var resource = 'dialog { \
        text: "select layer", \
        alignChildren: "fill", \
        groupFilter: Group { \
            stSearch: StaticText { \
                text: "Search:", \
            }, \
            etSearch: EditText { \
                active: true, \
                alignment: ["fill", "left"], \
            }, \
        }, \
        lstLayer: TreeView { \
            minimumSize: [150, 200], \
        }, \
        groupBtn: Group { \
            btnOk: Button { \
                text: "OK", \
                properties: { \
                    name: "ok", \
                }, \
            }, \
            btnCancel: Button { \
                text: "Cancel", \
                properties: { \
                    name: "cancel", \
                }, \
            }, \
        }, \
    }';
    var dialog = new Window(resource);
    var list = dialog.lstLayer;
    List.setList(list);
 
    // # event
    // ## EditText event
    dialog.groupFilter.etSearch.addEventListener('changing', function (ev) {
        list.removeAll();
        List.setList(list, this.text);
    });
 
    // ## ListBox event
    /**
     * リストをクリックした際、チェックのtrue/falseを切り替える
     * @param {TreeView} list - ダイアログ内リスト
     */
    var toggleCheck = function (list) {
        var selection = list.selection;
        if (selection == null) {
            return;
        }
        selection.checked = !selection.checked;
    };
    // keydown:
    //   space: expand/collapse a node in a TreeView
    //   enter: click button event (all Control)
    list.addEventListener('keydown', function (ev) {
        var keyName = ev.keyName;
        //if (keyName.toUpperCase() != 'SPACE') {
        if (keyName.toUpperCase() == 'ENTER') {
            return;
        }
        toggleCheck(list);
    });
    list.addEventListener('click', function (ev) {
        toggleCheck(list);
    });
 
    // ## Button event
    var layerList = [];
    // ok
    dialog.groupBtn.btnOk.addEventListener('click', function (ev) {
        layerList = List.getCheckedLayer(list);
        dialog.close(1);
    });
    // cancel
    dialog.groupBtn.btnCancel.addEventListener('click', function (ev) {
        dialog.close();
    });
 
    if (dialog.show() == 1) {
        return layerList;
    }
    return null;
}
 
main();

補足

  • 処理が重いため、レイヤーやレイヤーセット数が多いドキュメントで実行すると、ダイアログ表示まで時間がかかります。
  • アイコン画像を使用した場合、最初の何回かに520エラーが出ることがあるため、コメントアウトしています。
    繰り返し実行することで動作するようになります。

確認バージョン

  • CS6
  • CS2では動作しません

ページトップへ