AEScriptで、現在フォーカスしているタイムラインのコンポを表すには
app.project.activeItem
と書きますが、これにはちょっとクセがありまして、もしもプロジェクトパネルをフォーカスしてしまっている場合には、タイムラインコンポではなく、プロジェクトパネル内の1選択アイテムのことを表すことになってしまいます…。
これでは、例えばUI付きスクリプトを作ってボタンを押したときに、作った人としてはタイムラインコンポ内のレイヤーを処理するはずが、たまたまユーザーがプロジェクトパネルを選択していたら全然別のコンポ内のレイヤーを処理してしまう可能性があるんですねえ…。
コードで強制的にタイムラインコンポを選択させるには、ビューワーをアクティブにするとかのやり方がありますが、ボタンを押すたびにビューワーがピッと色変わりするので、あんまり見た目がよくないんですね。
なので、強制的にタイムラインコンポを選択させるんではなく「今、タイムラインコンポを選んでませんよ」と判断できるような関数を作りました。以下コードです。
//アクティブアイテム(パネル内ではなく、タイムラインorビューワーorフローチャート)が
//あればそれを返し、無ければNullを返す。
function mGetActiveItemNotOnPanel(){
var mAi = app.project.activeItem;
//アクティブアイテムが無ければリターンする。
//パネルであれば、選択数ゼロor複数。
//もしくはタイムラインorビューワーorフローチャートが選択されていない。
if( mAi === null ){ return null; }
//アクティブアイテムがコンポでなければ(パネルのコンポ以外のアイテムが選ばれているので)リターンする。
if( mAi instanceof CompItem !== true ){ return null; }
//メイン処理。
//アクティブアイテムがパネルで唯一選択されているコンポなのか、
//タイムラインetcが選択されているのかをチェックする。
//もしもパネル内の選択が外れているのにNullにならないということは、
//タイムラインetcがアクティブなのでそれを返す。
if( mAi.selected === false ){ return mAi; }
//そうではなく、パネル内でセレクトされている場合、
//アクティブアイテムのパネル内での選択を外してみて、もう一度定義する。
mAi.selected = false;
mNewAi = app.project.activeItem;
//新アクティブアイテムがNullならば、アクティブだったのはパネル内アイテムなのでリターンする。
//(パネル内選択は元に戻しておく)。
if( mNewAi === null ){
mAi.selected = true;
return null;
}
//アクティブアイテムの選択を外して再定義してもNullにならなければ
//タイムラインetcが選ばれているので新アクティブアイテムをリターンする。
//(パネル内選択は元に戻しておく)。
mAi.selected = true;
return mNewAi;
}
コメント付きだと長く感じるんですが、以下、コメント無しコードです。
function mGetActiveItemNotOnPanel(){
var mAi = app.project.activeItem;
if( mAi === null ){ return null; }
if( mAi instanceof CompItem !== true ){ return null; }
if( mAi.selected === false ){ return mAi; }
mAi.selected = false;
mNewAi = app.project.activeItem;
if( mNewAi === null ){
mAi.selected = true;
return null;
}
mAi.selected = true;
return mNewAi;
}
「タイムラインコンポが選択されていなければnullを、選択されているならばそのコンポを戻してくれる」関数です。これを使えば、アラートや情報パネルで確実にユーザーにタイムラインコンポを選ばせることができますね。
判断したいことは『フォーカスがパネルなのかタイムラインなのか』です。それを色々な条件でふるいにかけているわけですね。それではコードを詳しくみていきましょう。
if( mAi === null ){ return null; }
アクティブアイテムがnullになるのは『フォーカスがパネルで、パネル内の選択状態がゼロもしくは複数の場合』のみです。フォーカスがタイムラインだった場合はアクティブアイテムがnullになりえないですからね。
つまり、アクティブアイテムがnullならばフォーカスがパネルなのは確定なのでnullをリターンします。
そして、この一行を通過した、つまりアクティブアイテムがnullでないならば、ありえる状態は以下の2つに限定されます。
この時点でありえる状態 |
---|
フォーカスがパネルで、選択アイテムが何か1つ(選択アイテムがコンポかどうかは問わない) |
フォーカスがタイムラインで、パネル内の選択は問わない(複数でも1つでもゼロ個でもありうる) |
では次です。
if( mAi instanceof CompItem !== true ){ return null; }
アクティブアイテムがコンポ以外の種類のアイテムであれば、フォーカスがタイムラインであるはずはないのでnullをリターンします。
そして、この一行を通過したならば状態はさらに限定されます。
この時点でありえる状態 |
---|
フォーカスがパネルで、選択アイテムが1つのコンポ |
フォーカスがタイムラインで、パネル内の選択は問わない(複数でも1つでもゼロ個でもありうる) |
では次です。
if( mAi.selected === false ){ return mAi; }
ここからがこのスクリプトのミソですね!
まず、ちょっと前のif文で、この時点でアクティブアイテムがnullでないことは確定しています。
にもかかわらずselectedがfalseというのが成り立つのは、タイムラインがフォーカスされているとき以外にはありえないので、activeItemをリターンします。
(もしもパネルがフォーカスされていたならば、アクティブアイテムは『“選択されている”アイテム1つ』という意味なのでselectedがfalseになるわけがない、ということです)
そして、この一行を通過した、つまりselectedがtrueならば状態はさらに限定されます。
この時点でありえる状態 |
---|
フォーカスがパネルで、選択アイテムが1つのコンポ |
フォーカスがタイムラインで、パネル内の選択は複数or1つで、タイムラインと同コンポが選択されている |
ラストです。
mAi.selected = false;
mNewAi = app.project.activeItem;
if( mNewAi === null ){
mAi.selected = true;
return null;
}
mAi.selected = true;
return mNewAi;
selectedをいったんわざと外し、アクティブアイテムを再定義して、その新アクティブアイテムがnullかどうかを判断しています。
もしもフォーカスがパネルで選択アイテムが1つのコンポの場合、そのコンポのselectedを外すと新アクティブアイテムはnullとなるので、その場合はnullをリターンします。
もしもフォーカスがタイムラインで、パネル内でタイムラインと同コンポが選択されている場合、(他アイテムが選択されていようがいなかろうが)、パネル内でのselectedを外してアクティブアイテムを再定義してもnullではないままです。
なのでその場合は新アクティブアイテムをリターンします。
(最後の行の部分です。つどつどリターンで処理を終わらすという書き方をしているので、if&elseで条件分岐していないですが、if&elseと特に意味はかわりません)。
両方とも、いったんselectedを外しているので、それぞれのリターン前に選択状態に戻しています。
以上です!
自分用のスクリプトならば特に必要ないですが、配布用ともなると一応やっといたほうがいいかなあ、という処理ですね。
ぜひともご活用下さい!
■追記
読みやすさ優先で、フォーカスが『パネルかタイムラインか』の2択にしてありますが、厳密にいえば『パネルか、タイムラインorビューワーorフローチャートか』の2択です。タイムラインとビューワー、フローチャートは連動しているので、どれかが選ばれていればタイムラインコンポに対しての処理をしてくれます。問題になるのは「タイムラインのつもりがパネルの別アイテムを選んでいた」場合なので、ビューワー、フローチャートがタイムラインと連動する点は、今回のスクリプトでは特に問題ないと言えるでしょう。