Windows + Adobe CC illustrator / CEP 5 でイラレのカスタム UI を作ってみる (2) 中身をざっと見る


Introduction

Adobe illustrator (イラレ) の仕事では線幅やカラーが厳格に指定されている図形を多用する場合があります。(例えば地図作成や誌面デザイン、手芸の手順書 etc.)
illustrator では既定で直線や円などの描画モードがワンボタンで選べるので、同様に所定の図形をワンボタン化できないか調べてまとめました。

この記事は CC 版 illustrator / Windows 環境が対象です。この章では UI 作成の前に、まずはサンプルスクリプトの中身がどう絡み合っているかを紐解いていきます。
※読み飛ばしても OK

エクステンションの中身をざっと把握する

エクステンションのコードがどう動いているのかのイメージを押さえます。(超雑感)
(HTML の感覚が20年くらい前で止まってるのでご容赦を…)

index.html - ユーザーの触るパネル

ユーザー目線ではエクステンションの画面を見て、そこにあるボタンを押下することになります。このインターフェースは中身的には HTML を開いて表示しているわけで、 index.html に記述されています。たとえば、画面真ん中に "Call ExtendScript" ボタンが表示されていますが、これは以下箇所に対応します。

index.html
        <div>             
            <button id="btn_test" class="topcoat-button--large hostFontSize">Call ExtendScript</button>                
        </div>   

こういった対応付けは CefClient の開発者モードが便利です。Elements タブでは今表示されているページの内容 (≒index.html) のソースが見え、各行にマウスオーバーすると対応する箇所が色づいて見えます。「この <button ~~ というのにマウスを重ねたらボタンに色がつくから、この辺のコードはボタンを作ってるんだな~」みたいな感じで見ていけます。

css ファイル - ボタンや文字の色や形などを統一指定

ページに表示したいものは基本的に index.html に直接書けば出てきます。HTML ではボタンやフォーム、文字などをベタ打ちで出すことができるのですが、これをやると幾分古ぼけたインターネットの見た目になります。

変更前
    <div id="content">        
        <div>                
            <button id="btn_test" class="topcoat-button--large hostFontSize">Call ExtendScript</button>                
        </div>   
    </div>
変更後
    <div id="content">        
        <div>                
            <button id="btn_test" class="topcoat-button--large hostFontSize">Call ExtendScript</button>                
        </div>   
        <!-- ボタンタグ追加 -->
        <button type="button">test</button>
    </div>

[test] ボタンができました。でも [Call ExtendScript] ボタンと違って今風じゃないですよね。ほかのインターフェースにマッチしていません。かと言って HTML 内にボタンを作るたびにボタンサイズやら色やらを指定するのはとても煩雑です。

これを解決しているのが css フォルダ内に入っている .css ファイル (Cascading Style Sheets) です。css では文字やボタンなどのサイズ、色、etc. をクラスとして設定しておき、HTML 側では「その要素がどのクラスに準じるか?」だけを教えてあげればスタイルが反映されるのです。もう一度 [Call ExtendScript] の HTML 対応箇所を見てみましょう。
このボタンは topcoat-button--large クラス、hostFontSize クラスが指定されています。(スペースで結んで 2 つのクラスを指定している)

index.html
            <button id="btn_test" class="topcoat-button--large hostFontSize">Call ExtendScript</button>          

それぞれ CSS ファイルを探してみると、topcoat-button--large については topcoat-destop-dark.min.css に記述されています。(が、読みにくいので省略します) VSCode で見るとグレー系の色が指定されているのが見て取れます。

また hostFontSize については styles.css に書いてありますが、以下のコメントを見る限り illustrator が読み込んだ時に改めて指定するようですね。この動きについては、そりゃあイラレの UI はイラレにフォントを合わせるべきでしょうから納得です。

styles.css
/*
Those classes will be edited at runtime with values specified
by the settings of the CC application
*/
.hostFontColor{}
.hostFontFamily{}
.hostFontSize{}

また、ボタンの ID btn_test に特化したスタイルも記述されています。
これは横幅いっぱいにボタンを引き延ばすという意味ですね。

styles.css
#btn_test{
    width: 100%;
}

結局、illustrator の見た目からそれほど外れないインターフェースにするのであれば既存の CSS に手を加える必要はありません。

main.js - メインのスクリプト

index.html の中を見ていくと、[Call ExtendScript] ボタンを押したときに何が発動するのかは一切書いていません。表示以外の処理は以下箇所で読みこむ Javascript、特に main.js に書かれています。(他はライブラリ系なので無視で OK)

index.html
    <script src="js/libs/CSInterface.js"></script>
    <script src="js/libs/jquery-2.0.2.min.js"></script>

    <script src="js/themeManager.js"></script>
    <script src="js/main.js"></script>

main.js を見てみましょう。(一部略)

main.js
(function () {
    'use strict';
    var csInterface = new CSInterface();
    function init() {
        themeManager.init();                
        $("#btn_test").click(function () {
            csInterface.evalScript('sayHello()');
        });
    }
    init();
}());

しばらく javascript に触れていない筆者にとって、この書き方はイマイチ読み方がわからないのでざっと調べます。

まず先頭の (function () 、これは即時関数と呼ばれる書き方のようです。関数を読み込みと同時に実行する方式で、ページの初期化などに使えます。処理自体は関数化しなくても書けるはずですが、関数の形をとることで変数のスコープを限定することができ、予期しない変数名重複などが発生しにくくなります。上の例ですと、csInterface という変数名は main.js の関数内でしか機能しません。
つまり index.html を読み込んで初期化で実行するのにぴったりというわけです。(以下参考)
https://qiita.com/katsukii/items/cfe9fd968ba0db603b1e

続いて init() について。関数内に関数を作る構造になっていますが、これもスコープ管理上有利だからでしょうか。最終的に init() が実行されて初期化されることになります。

この中身を見てみると、ボタンを押したときの処理が記述されています。

main.js
        $("#btn_test").click(function () {
            csInterface.evalScript('sayHello()');
        });

この `$("btn_test") のドル記法みたいなのは JQuery のお作法のようです。
この記述はセレクタと呼ばれ、$("#btn_test") であれば画面内の "btn_test" という ID の要素について、という意味になります。つまり、index.html で btn_test という ID で宣言されているボタンをクリックすると次の処理に入るというわけです。
(以下参考)
https://rfs.jp/sb/javascript/jquery_abc/jquery-selector.html

また、この関数内で csInterface.evalScript() が呼ばれています。実は Adobe は昔から (UI はなくても) スクリプト単体で動かすことはできました。このスクリプトと CEP のエクステンション UI を橋渡しするのがこの evalScript() です。つまりルール上、index.html --> main.js --> JSX (後述) の順に橋渡しをして初めて本題のスクリプトが動かせるのです。

さて、ここで呼ばれている 'sayHello()' はどこにいるのでしょうか?この答は次項の hostscript.jsx にあります。

hostscript.jsx - 本当に動かしたい処理を書くスクリプト

JSX = Adobe の独自仕様の Extended Script File を指します。
紛らわしいのですが、react の JSX とは無関係です。

JSX は短いのでそのまま見てみましょう。

hostscript.jsx
function sayHello(){
    alert("hello from ExtendScript_changed");
}

ようやく往年の JavaScript らしいシンプルな処理にたどり着きました。読んで字のごとく、"hello from ExtendScript_changed" というアラートを出すのがここですね。

manifest.xml

エクステンションにかかわる各ファイルの所在を記述していくもののようです。
例えば以下の箇所ではスクリプトの相対パスを指定しています。jsx ファイルを分けるようなときにはここに追記が必要なのかもしれませんが、1つのエクステンションでそこまで大規模な jsx を作ることもないので一旦カスタムなしで大丈夫です。

manifest.xml
<ScriptPath>./jsx/hostscript.jsx</ScriptPath>

ざっくりした流れ

illustrator がエクステンドを読み込んだ状態における、各ファイルと見た目との対応はこんな感じでしょうか。やはりスクリプティングの理解の上では、index.html <--> main.js --> hostscript.jsx の連携が重要です。

なお、後でポイントになりますが main.js はコンソール出力などエクステンションと直結した処理ができますが、JSX はなぜかできません。JSX は main.js が呼びに行く形でしか触れず、JSX からエクステンション側への処理を書く方法は見つかりませんでした。もともと JSX は Adobe のオブジェクト操作用の JS を書くところなので、無くて当然かもしれません。
(JSX の関数の返り値として main.js に渡すことは可能)

この意味で index <--> main.js と main --> hostscript.jsx は矢印の使い方を分けています。

まとめ

サンプルスクリプトがアラートを出すまでに関わるファイルと流れを書き出してみました。
続いてボタンの作成などを試していきます。