AfterEffects23.1からiconbuttonが丸くなってしまったときに使う黒魔術

AE23.1からScriptUIでiconbuttonを使うとフチが丸くなってしまうようになった...

仕方ないのでonDrawで四角に戻すしかない。
onDraw関数に自分の関数を指してあげると描画をカスタマイズすることが出来る(PySideでいうdelegateみたいなもの)
マウスやキーボードの状態を引数で得ることが出来るので、適当な名前の変数を用意しておいて状態によって描画を変更するようにしてみた。


```js:square_iconbutton_delegate.jsx
(function() {
    function hexcolor2float(hexcolor) {
        var int_color = parseInt(hexcolor.replace(/^#/, ''), 16);
        var r = (int_color >> 16 & 255) / 255;
        var g = (int_color >> 8 & 255) / 255;
        var b = (int_color & 255) / 255;
        return [r, g, b];
    }

    var square_iconbutton_delegate = function(state) {
        var gp = this.graphics;
        gp.drawOSControl();

        var width = this.bounds.width;
        var height = this.bounds.height;
        gp.rectPath(0, 0, width, height);

        /*
            ボタンの塗り色
            app.themeColor(3)は環境設定>アピアランスの明るさの近似みたい(ただし公式なメソッドではない)
        */
        var default_brush_color = app.themeColor(3);
        // var default_brush_color = hexcolor2float('#232323');
        var clicked_brush_color = hexcolor2float('#636363');
        var hoverd_brush_color = hexcolor2float('#8A8A8A');
        var hasFocus_brush_color = hexcolor2float('#2D8CEB');

        var brush_color = default_brush_color;
        brush_color = (state.mouseOver) ? hoverd_brush_color : brush_color;
        brush_color = (state.hasFocus) ? hasFocus_brush_color : brush_color;
        brush_color = (state.mouseOver && state.leftButtonPressed) ? clicked_brush_color : brush_color;

        var brush = gp.newBrush(gp.BrushType.SOLID_COLOR, brush_color);
        gp.fillPath(brush);

        /*
            デフォルトのiconbuttonはクリックされた状態等ではボタンの枠線を
            描画していないみたいだったので、描画するかどうかを先に判定する
        */
        var show_stroke = true;
        show_stroke = (state.mouseOver) ? false : show_stroke;
        show_stroke = (state.hasFocus) ? false : show_stroke;
        show_stroke = (state.mouseOver && state.leftButtonPressed) ? false : show_stroke;

        if (show_stroke) {
            var default_pen_color = hexcolor2float('#8A8A8A');
            var pen_color = default_pen_color;
            var pen_border = 2;

            var pen = gp.newPen(gp.PenType.SOLID_COLOR, pen_color, pen_border);
            gp.newPath();
            gp.moveTo(0, 0);
            gp.lineTo(width, 0);
            gp.lineTo(width, height);
            gp.lineTo(0, height);
            gp.lineTo(0, 0);
            gp.strokePath(pen);
        }

        // align image to center
        var top = (width - this.image.size[0]) / 2;
        var left = (height - this.image.size[1]) / 2;

        gp.drawImage(this.image, top, left);
    }

    var win = new Window('palette', app.version, undefined, {
        resizeable: true
    });

    win.preferredSize = [150, 100];
    win.alignChildren = ['fill', 'fill'];

    var group1 = win.add('panel', undefined, 'custom iconbutton');
    var btn1 = group1.add('iconbutton', undefined, 'SystemWarningIcon');
    btn1.onDraw = square_iconbutton_delegate;

    var group2 = win.add('panel', undefined, 'default iconbutton');
    var btn2 = group2.add('iconbutton', undefined, 'SystemWarningIcon');

    win.layout.layout();

    win.onResize = function() {
        win.layout.resize();
    }

    win.center();
    win.show()
})();
```

いい感じになった。2022で確認してもデフォルトと近い見た目と挙動になっている
![result.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149913/ac57cc34-4c62-bf92-0e9c-cb0d3c1b9dc9.png)

:::note alert
AE2019はonDrawが他のバージョンと違う動作をするので注意。
CS6もちょっとグラデーションとか装飾が入っているので、今回の方法はAE2023の23.1以降の使用に限定したほうがよさそう。

:::

ちなみにVSCodeのExtendScript Debugger(v2.0.3)だとlaunch.jsonにあるconfigurationsの項目をcompoundsって項目でまとめて実行できるので便利。

```json:launch.json
{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "extendscript-debug",
            "request": "launch",
            "hostAppSpecifier": "aftereffects-23.0",
            "script": "${workspaceFolder}/square_iconbutton_delegate.jsx",
            "name": "test_2023"
        },
        {
            "type": "extendscript-debug",
            "request": "launch",
            "hostAppSpecifier": "aftereffects-22.0",
            "script": "${workspaceFolder}/square_iconbutton_delegate.jsx",
            "name": "test_2022"
        },
    ],
    "compounds": [
        {
            "name": "test_all",
            "configurations": [
                "test_2023",
                "test_2022",
            ]
        }
    ]
}
```

がんばると結構カスタマイズできるonDrawですが、凝った描画をさせると重くなるので計算を軽くしたりしなくちゃいけなくなることもあるのでうまく使ってみてくださいー。
Adobe的にはScriptUIじゃなくてCEP使えってことなのかもしれないけど。

### 追記

既存のScriptUIを修正しようとしたらめちゃくちゃiconbuttonがあって直すの大変になった場合、ウィンドウオブジェクトから全走査して当てていくのが楽かも
```js
function fix_iconbuttons(o) {
    function _fix_iconbuttons(o) {
        for (var i = 0; i < o.children.length; ++i) {
            var child = o.children[i];
            if (child instanceof IconButton) {
                child.onDraw = square_iconbutton_delegate;
            }
            _fix_iconbuttons(child);
        }
    }
    _fix_iconbuttons(o);
}

if (23.0 < Number(app.version.match(/^\d+\.\d+/))) {
    fix_iconbuttons(win);
}
```