Amazonで商品を見るセール会場へ

Photoshop JSXで非表示を含む1つ下を選ぶ|同じ親・ID指定

現在のアクティブレイヤーと同じ親にある1つ下の要素を、非表示も候補に含めて選ぶPhotoshop JSXです。parent.layersで隣を探し、レイヤーIDを指定して選択を置き換えます。追加選択ではありません。表示状態で候補を除外しないため、非表示の隣も対象になります。

今回確認したのは標準JavaScriptの構文と模擬条件です。Photoshopでの選択・表示・実画面の撮影は未検証です。コードはレイヤーの移動・名前変更・保存を呼びませんが、選択状態は変わります。非表示、ロック、複数選択、アートボードの実際の挙動は自作の文書で確認してください。

目次

同じ親の隣接要素を対象にする理由

DOMのlayersの先頭はレイヤーパネルの上側です。現在の要素を親のlayersからIDで探し、添字を1足した要素を対象にします。文書直下の要素なら親はDocument、グループの子ならそのグループです。グループ自体が選ばれている場合も、その親の一覧で隣を探します。グループの子へ自動で入りません。

対象の条件この例の扱い
同じ親の隣がグループグループ自体のIDを指定。内部の子は自動で複数選択しない。
隣が非表示・ロック候補には含める。選択可否と表示状態は実機で比較する。
同じ親の端・別の親にしか候補がない親の境界で停止し、別グループへ移らない。
背景・同名レイヤーDOMの添字から候補を探し、名前やAMのitemIndex±1では指定しない。
IDまたは選択一覧を読めない名前・AM番号へ自動で置き換えず停止する。

置き換えと追加選択を混ぜない

旧コードは下の対象を選び直した後に、同じ対象をもう一度追加選択しています。現在の対象と下の対象を2つ選ぶ処理にはなっていません。新例はURLの「非表示を含む下の選択」に範囲を合わせ、単一の置き換え要求へ整理しました。元の選択を残して追加する目的には既存の選択へ追加する記事を参照してください。

同じ親の1つ下を選ぶ全体コード

新しい全体コードをUTF-8の.jsxとして保存し、ファイル→スクリプト→参照から実行します。旧コードへ追記せず、別ファイルとして1回実行してください。メニューへの登録は動作を確認した後に行います。

#target photoshop
(function () {
    if (app.documents.length === 0) { alert("文書を開いてください。"); return; }
    function validId(value) {
        return typeof value === "number" && isFinite(value) && value >= 0 && Math.floor(value) === value;
    }
    function selectedIds() {
        var key = stringIDToTypeID("targetLayersIDs");
        var ref = new ActionReference();
        ref.putProperty(charIDToTypeID("Prpr"), key);
        ref.putEnumerated(charIDToTypeID("Dcmn"), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"));
        var result = executeActionGet(ref);
        if (!result.hasKey(key)) { throw new Error("選択IDの一覧を取得できません。"); }
        var list = result.getList(key);
        var ids = [];
        for (var i = 0; i < list.count; i++) {
            var id = list.getReference(i).getIdentifier();
            if (!validId(id)) { throw new Error("選択IDが数値の整数ではありません。"); }
            ids.push(id);
        }
        return ids;
    }
    function contains(ids, id) {
        for (var i = 0; i < ids.length; i++) { if (ids[i] === id) { return true; } }
        return false;
    }
    try {
        var ids = selectedIds();
        if (ids.length !== 1) { alert("レイヤーを1つだけ選択してください。"); return; }
        var current = app.activeDocument.activeLayer;
        if (!validId(current.id) || !contains(ids, current.id)) {
            throw new Error("アクティブレイヤーと選択IDを照合できません。");
        }
        var siblings = current.parent.layers;
        var position = -1;
        for (var i = 0; i < siblings.length; i++) {
            if (siblings[i].id === current.id) {
                if (position !== -1) { throw new Error("同じIDが親の一覧に複数あります。"); }
                position = i;
            }
        }
        if (position < 0) { throw new Error("親の一覧にアクティブレイヤーがありません。"); }
        var targetPosition = position + 1;
        if (targetPosition < 0 || targetPosition >= siblings.length) {
            alert("同じ親の下に隣接要素がありません。選択を変更しません。"); return;
        }
        var targetId = siblings[targetPosition].id;
        if (!validId(targetId)) { throw new Error("隣接要素のIDを取得できません。"); }
        var ref = new ActionReference();
        ref.putIdentifier(charIDToTypeID("Lyr "), targetId);
        var desc = new ActionDescriptor();
        desc.putReference(charIDToTypeID("null"), ref);
        desc.putBoolean(charIDToTypeID("MkVs"), false);
        executeAction(charIDToTypeID("slct"), desc, DialogModes.NO);
    } catch (error) {
        alert("読取または選択で中断: " + error.message + "\n行: " + error.line);
    }
}());

レイヤーIDと添字を混同しない

parent.layersの添字は、この親の中で位置を調べるためだけに使います。選択の指定にはputIdentifierを使い、targetIdを渡します。itemIndex、DOMの添字、layerIDは別の値です。LyrIの一覧へitemIndexをそのまま入れたり、背景の有無に合わせて独自に1を加減したりしません。

IDはこの実行中の文書で取得した値を使います。別文書へ持ち越す恒久的なIDとは扱いません。DOMのidやtargetLayersIDsが現在の版・対象で取得できなければ、選択前に中断します。2020年の公式資料だけで、すべての対象のid取得と現在の版の挙動を確認済みとはしていません。

自作PSDで確認する条件

入力の条件模擬条件で確認した処理・実機で確かめること
同じ親の上からA・B・CでBが基準Cへ置き換え要求
隣が非表示・同名・グループ名前やvisibleで候補を変更せず、そのIDを指定。実選択・表示を比較。
グループの子に隣があるその親だけの一覧で隣を指定。別の親へ飛ばない。
元が複数選択実行前に停止し、1件を勝手に選ばない。
同じ親の端・ID不明・選択不整合選択要求前に停止。
executeActionの例外原文と行番号を表示。失敗時の状態を確認し、別対象で自動再試行しない。

実機では選択ID一覧とレイヤーパネルのハイライトを前後で比較し、表示・数・順序・名前も記録します。置き換え後に対象の1件が選ばれたことを確認してください。MkVs=falseを指定したことと、実際に表示が変わらなかったことは分けて記録します。複数選択の自動復元は含めていません。activeLayerだけを保存しても、元の全選択を復元したことにはなりません。

旧コードと過去の画面(参照用)

旧例はitemIndex−1を対象に置き換えてから同じ対象を追加するため、説明の追加選択とは一致しません。belowLayerIndex<1も背景やグループ境界をまとめて扱う保証ではありません。旧表紙の「表示レイヤー」は新例の候補条件を表していません。

// 1つ下のレイヤーを追加選択
function selectAddBelowLayer() {
    var currentLayerIndex = getActiveLayerIndex(); // 現在のレイヤーのインデックス
    var belowLayerIndex = currentLayerIndex - 1; // 1つ下のレイヤーのインデックス

    // 最下層のレイヤーの場合、処理を中止
    if (belowLayerIndex < 1) {
        alert("これ以上下にレイヤーはありません。");
        return;
    }

    // 1つ下のレイヤーを選択
    selectLayerByIndex(belowLayerIndex, false);

    // 1つ下のレイヤーを追加選択
    addLayerToSelection(belowLayerIndex);
}

// 現在のアクティブなレイヤーのインデックスを取得
function getActiveLayerIndex() {
    var ref = new ActionReference();
    ref.putProperty(charIDToTypeID("Prpr"), charIDToTypeID("ItmI"));
    ref.putEnumerated(charIDToTypeID("Lyr "), charIDToTypeID("Ordn"), charIDToTypeID("Trgt"));
    return executeActionGet(ref).getInteger(charIDToTypeID("ItmI"));
}

// 指定したインデックスのレイヤーを選択(可視状態を変更しない)
function selectLayerByIndex(layerIndex, makeVisible) {
    var desc = new ActionDescriptor();
    var ref = new ActionReference();
    ref.putIndex(charIDToTypeID("Lyr "), layerIndex);
    desc.putReference(charIDToTypeID("null"), ref);
    desc.putBoolean(charIDToTypeID("MkVs"), makeVisible);
    executeAction(charIDToTypeID("slct"), desc, DialogModes.NO);
}

// 指定したインデックスのレイヤーを追加選択
function addLayerToSelection(layerIndex) {
    var desc = new ActionDescriptor();
    var ref = new ActionReference();
    ref.putIndex(charIDToTypeID("Lyr "), layerIndex);
    desc.putReference(charIDToTypeID("null"), ref);
    desc.putEnumerated(
        stringIDToTypeID("selectionModifier"),
        stringIDToTypeID("selectionModifierType"),
        stringIDToTypeID("addToSelection")
    );
    desc.putBoolean(charIDToTypeID("MkVs"), false);
    executeAction(charIDToTypeID("slct"), desc, DialogModes.NO);
}

// スクリプトを実行
selectAddBelowLayer();
旧記事のぼかされたコード編集画面。下部のExecution finishedと結果undefinedだけでは、Photoshopの選択行・表示状態は確認できず、レイヤーパネルは写っていません。
旧記事のぼかされたコード編集画面。下部のExecution finishedと結果undefinedだけでは、Photoshopの選択行・表示状態は確認できず、レイヤーパネルは写っていません。 過去の掲載画像で、撮影日・Photoshopの詳細版は未確認です。

参照資料と確認範囲

Adobe Photoshop Scripting Guide 2020の38〜39ページ(レイヤーの並び順・グループ)、JavaScript Scripting Reference 2020のActionDescriptor、ActionList、ActionReference、Document、Layers、LayerSetを参照しています。2020年版のExtendScript資料で、今回のPhotoshop実機の検証記録ではありません。

Cutterman作者のレイヤー操作コードのtargetLayersIDsとID指定、Adobe CommunityのJJMack氏による選択追加の記録のselectionModifierを比較しました。作者の掲載例は公式の全条件保証ではなく、今回の実機成功や速度測定にも数えていません。Adobeのレイヤー選択手順で説明する行の選択と、サムネールからのピクセル選択は別です。

対象範囲が異なる選択

上への相対選択/下への相対選択はFrwr/Bckwの要求です。同じ親のDOM隣接と同じ結果を保証しません。名前から選ぶ場合は階層を含む名前選択を参照してください。

旧配布と関連する手順

旧配布はGitHubの元コードに残しています。公開フォルダーの01.jsは今回の新しい例へ更新していません。実行する場合は、このページの新しい全体コードを別の.jsxとして保存してください。

次に学ぶ・作業環境を選ぶ

学習を続けたい方や、作業環境を整えたい方は、目的に合うガイドをご覧ください。

Photoshopのおすすめ書籍

動作要件とPCの確認項目

周辺機器の優先度

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次