Skip to main content
Known Participant
July 27, 2024
解決済み

スクリプトパネルの中にスクロールを作る方法について

  • July 27, 2024
  • 返信数 1.
  • 1438 ビュー

以下のスクリプトは、ChatGPTを使って作ったAfterEffectsのスクリプトです。

(私はコーディングができず、中身がなんとなくわかるくらいの知識です。)

 

デフォルトで10行のテキスト入力欄を表示させており、「+」や「-」ボタンを押すことでこのテキスト入力欄の行を増やしたり減らしたりすることができます。

 

ただ、入力欄を20個などつくるとパネルを伸ばさないと下の方の入力欄が見えなくなってかなり不便なので、一番上のタイトルややボタンがある1行目は固定して、例えば、テキスト入力欄だけ一つのコンテナにまとめて、ここのフィールドはスクロールでみれるようにしたいのですが、ChatGPTだとそれがなかなかうまくできません(※)。

(※)具体的には「Scrollbar」というコードを追加している?ようなのですが、表示が消えたり横向きのスクロールだけ出てきたり変になってうまくできないです。

 

<質問>

このテキスト入力部分だけをスクロールで表示できるようにするにはどうしたら良いでしょうか。

できれば、見える範囲はデフォルトで横200と縦100と決めておいて、あとあと見える範囲のデフォルト値を変更できるようにはしておきたいです。

 

<スクリプト>

{
function createUI(thisObj) {
var myPanel = (thisObj instanceof Panel) ? thisObj : new Window("palette", "Frame and Cell Manager", undefined, {resizeable: true});

var res =
"group { \
orientation: 'column', alignment:['fill', 'fill'], \
header: Group { \
alignment: ['fill', 'top'], \
frameText: StaticText { text:'テスト1', alignment:['left', 'top'] }, \
cellTextGroup: Group { \
orientation: 'row', alignment:['fill', 'top'], \
cellText: StaticText { text:'テスト2', alignment:['left', 'top'] }, \
addButton: Button { text:'+', preferredSize:[30, 20] }, \
removeButton: Button { text:'-', preferredSize:[30, 20] }, \
} \
}, \
list: Group { \
orientation: 'column', alignment:['fill', 'fill'], \
}, \
}";

myPanel.grp = myPanel.add(res);

function addRow() {
var group = myPanel.grp.list.add("group", undefined, {name: "row"});
group.orientation = "row";
group.alignment = ['fill', 'top'];
group.add("edittext", undefined, "", {name: "frame"}).alignment = ['fill', 'top'];
group.add("edittext", undefined, "", {name: "cell"}).alignment = ['fill', 'top'];
}

function removeRow() {
var group = myPanel.grp.list;
if (group.children.length > 0) {
group.remove(group.children[group.children.length - 1]);
}
}

for (var i = 0; i < 10; i++) {
addRow();
}

myPanel.grp.header.cellTextGroup.addButton.onClick = function() {
addRow();
myPanel.layout.layout(true);
}

myPanel.grp.header.cellTextGroup.removeButton.onClick = function() {
removeRow();
myPanel.layout.layout(true);
}

myPanel.layout.layout(true);
myPanel.grp.minimumSize = myPanel.grp.size;

return myPanel;
}

var myScriptPal = createUI(this);
if (myScriptPal instanceof Window) {
myScriptPal.center();
myScriptPal.show();
}
}

このトピックへの返信は締め切られました。
解決に役立った回答 stunning_Sunflower16B7
この場合の面倒なところは先に60個の行のグループを作成してしまうと
myPanel.layout.layout()
した際にウィンドウが60個の行のグループ分の大きさになってしまうので
1行等の少ない数、ないしは空の状態で
myPanel.layout.layout()を実行し、
表示が完了した後に任意の数の行を追加してから、行のグループに対して
(例えば行のグループがmyPanel.grp.list.scrollarea.itemareaとするならば)
myPanel.grp.list.scrollarea.itemarea.layout.layout(true)
のようにしてあげなければなりません
そうすると行のグループのsizeが決まりますので親のグループ
(この場合scrollarea)のサイズと高さの差を取って、スクロールバーのmaxvalueを
設定するといいと思います。

さらに表示後に行を任意の数設定する必要があるのでウィンドウが表示されたイベントを
取らないといけません。スクリプトを別ウィンドウで出したいならmyPanel.onShowの
イベントハンドラ内で設定する必要がありますが、ドッキングパネルにしたい場合は
onShowのイベントが実行されないので別の個所で設定する必要があります。

スクロールバーが表示されないのは最初のres = '...'でうまく設定できてないのかも
最初から60行とするならば常に表示するということですので、まずは行のない状態でも
表示されるかどうかからやってみるのがよいかと思います。

AutomaticLayoutは便利なのですが、上記のように特別な場合、非常に難しいコードを
書かなくてはなりません。個人的な感想としてはもうchatGPTが学習していない領域なのかと。

もし面倒でないのであればAutomaticLayoutを使用せずに、座標を直接指定する方法もあります。
 

 

var myPanel = (thisObj instanceof Panel) ?
thisObj :
    new Window("palette",
    "Frame and Cell Manager",
    [100,100, 400,400], // 直接座標を指定
    );

 

 

この場合、座標さえ間違えなければ正しく表示されるのですが、AutomaticLayoutが使えなくなるはずなので全てのパーツの座標を指定しなくてはなりません(一部をAutomaticLayoutにして、他を座標指定するのも原則できなかったはず)
一応親のパーツからの相対位置なので、グループをうまく使用するとわかりやすいと思いますが
自分はもうこの形式では書きたくない感じです...
座標が再計算されないので、パーツを好きな位置、好きな大きさに置けるのですが、ウィンドウをリサイズしたい場合全部のパーツの座標を自分で計算しないといけないので、ものすごい負担になるかと。

 

起動の重さに関しては、addRowで逐一layout.layout(true)して、スクロール量を計算して...
とするよりは60個追加してからlayout.layout(true)する等、繰り返しの負荷を抑えるのが
妥当だと思いますが、スクロール自体が重いのは仕方ない感じですね...
ちなみにCS6でやってみたら爆速で実行されていました。

onShowのイベントハンドラをとらなくてもよさそうでしたのと、他にも同様のことをしたい方もいるかもしれませんのでコードを置いておきます。基本的には前述のとおりAutomaticLayoutのタイミングとどのグループにAutomaticLayoutするかが肝になります。

コードはドッキングパネルにしても単独にしても、表示されますし、リサイズも追従してくれます。

 

(function(isPanel) {
    var res = "panel { \
    orientation: 'column', \
    alignment:['fill', 'fill'], \
    preferredSize : [300,300], \
    \
    header: Group { \
        alignment: ['fill', 'top'], \
    }, \
    \
    list: Panel { \
        orientation: 'row', \
        alignment:['fill', 'fill'], \
        margins: 0, \
        scrollarea: Group { \
            orientation: 'column', \
            alignment:['fill', 'fill'], \
            itemarea: Group{ \
                orientation: 'column', \
                alignment:['fill', 'fill'], \
            }, \
        }, \
        scroll: Scrollbar { \
            alignment: ['right', 'fill'], \
            preferredSize:[15, -1], \
        }, \
    }, \
}";


    function addRow() {
        var group = itemarea.add("group", undefined, {
            name: "row",
        });

        group.orientation = "row";
        group.alignment = ['fill', 'top'];

        var text1 = group.add("edittext", undefined, "", {
            name: "frame"
        })
        text1.alignment = ['fill', 'top'];
        text1.preferredSize = [100, -1]

        var text2 = group.add("edittext", undefined, "", {
            name: "cell"
        })
        text2.alignment = ['fill', 'top'];
        text2.preferredSize = [100, -1]

        text1.text = itemarea.children.length;
        return group
    }

    function calcScrollDistance() {
        var scrollarea = itemarea.parent
        var value = itemarea.size[1] - scrollarea.size[1]
        value = (value < 0) ? 0 : value;
        scrollbar.maxvalue = value
    }

    function calcItemsSize() {
        if (itemarea.children.length === 0) return;
        var item = itemarea.children[0];
        if (item.size === undefined) return;
        var itemarea_height = itemarea.children.length * (item.size[1] + item.spacing) +
            item.parent.margins.top + item.parent.margins.bottom;
        itemarea.size.height = itemarea_height
    }

    var myPanel = (isPanel instanceof Panel) ?
        isPanel :
        new Window("palette",
            "Frame and Cell Manager",
            undefined, {
                resizeable: true
            });

    myPanel.group = myPanel.add(res);

    var itemarea = myPanel.group.list.scrollarea.itemarea;
    var scrollarea = myPanel.group.list.scrollarea;
    var scrollbar = myPanel.group.list.scroll;

    scrollbar.onChange = scrollbar.onChanging = function() {
        itemarea.location = [itemarea.location[0],
            myPanel.group.list.spacing + itemarea.margins.top - this.value
        ]
    }

    myPanel.layout.layout();

    for (var i = 0; i < 60; i++) {
        addRow()
    }

    myPanel.onResize = function() {
        myPanel.layout.resize()
        calcItemsSize()
        calcScrollDistance()
    }

    if (myPanel instanceof Window) {
        myPanel.center();
        myPanel.show();
        itemarea.layout.layout(true)
        calcItemsSize()
        calcScrollDistance()
    } else {
        itemarea.layout.layout(true)
        calcItemsSize()
        calcScrollDistance()
    }

}(this));

返信数 1

Participating Frequently
July 27, 2024
スクリプトで独自のスクロールバーを追加するのは結構難易度が高いと思います。
 
スクロールバーを追加して、スクロールバーの値が変化していくのに応じて
スクロールしたいグループの位置をずらしていくのがよいのかなと思います

おすすめは表示するグループとスクロールバーを並べ、表示するグループの中に
配置するグループを作成し、表示するグループの高さと配置するグループの高さの
差分をスクロールバーのmaxvalueとする等です

さかもとさんのサイトに良い例があります

ただし、今回のように列を増減したい場合はchatGPTの提案では問題があります。
chatGPTは行を追加した際に
myPanel.layout.layout(true);
としていますが、これではすべての要素を再配置してしまっていて、行の数に応じて
ウィンドウ全体の大きさも変わってきてしまいます

この部分が肝心のAutomaticLayoutというものになるのですが、これの使い方について
よく見かける文として
win.layout.layout()
win.layout.layout(true)
などとなっていることが多いのですが、ウィンドウ全体を表示する前にこの文を
実行するのは問題ないのですが、今回のような特殊な場合は全ての局面でこれを使用することは
できません。

この場合、edittextを配置したいグループのみ、layout.layout(true)を実行することで、
そのグループのみ、サイズが変更されます
例としては
myPanel.grp.list.scrollarea.itemarea.layout.layout(true);
のような書き方が出来ますのでこの後にmyPanel.grp.list.scrollarea.itemareaの高さとmyPanel.grp.list.scrollarea
の高さの差分を取り、スクロール量を更新すればよいと思います

自前のスクロールに関しては特に列数が多くなると結構描画にもたつきがあると思います...

個人的にはこの後、特定の列を削除したい、列を移動したい、選択しているedittextの列番号を取得したい等
かなりの追加要素を書いていかなくてはなりませんので、自前のスクロールをせずに
テキストの一覧はlistboxで、listboxを選択するとedittextにテキストを表示するようにすると
コード量が少なくなるというかlistboxの例は沢山あると思うのでchatGPTも答えられるようになりそうな感じがするのでそちらも検討してみるのもよいかもです。デザインが変わってしまいますが...
 
hoshi999作成者
Known Participant
July 28, 2024

さっそくありがとうございます!仰っていただいた通りスクロールバーはかなり挙動が重くなってしまうのですが、ご提案いただいたリストボックスは動作が早いものの、やはり意図している見た目が崩れてしまった(リストボックスの中にテキスト欄を入れられなかった)ので、スクロールバー型で進めようと思っています。

表示範囲を固定しないと色々と見た目が崩れてしまったので、縦の表示範囲を固定することでなんとかやりたいことはできそうになってきました!色々と本当にありがとうございます。大変参考になりました。