JXA NSButton

JXA からの Objective-c バインディングを再勉強。

Building OS X Apps with JavaScript by Tyler Gaw

今回はボタンを押した時のハンドラ指定でも。
target にインスタンス化したデリゲートを指定。
action にハンドラ名を文字列にて指定。
でいいみたい、何だか変な指定方法だけど従うしかない。

ところで現在の JXA は ES6 が使えるので var より let のほうがいい。
ただ全部グローバル変数だとコードが長くなった時にワケワカメになるのよね。
やっぱり class にしたいなぁ。

ObjC.import("Cocoa");
class ButtonWindow extends $.NSWindow {
}
//TypeError: The value of the superclass's
//prototype property is not an object. (-2700)

駄目だ。

ぶっちゃけ Cocoa の API はこう継承するメリットは無さそうだし。
this.window みたいな感じで class にくっつけておけばいいかなと。

それと忘れていたけどアプリにするなら起動パラメーターも得ないと。
まあ少しづつやっていくつもり。

#!/usr/bin/osascript

ObjC.import("Cocoa");

class ButtonWindow {
    constructor(app) {
        ObjC.registerSubclass({
            name: 'WinDelegate',
            protocols: ['NSWindowDelegate'],
            methods: {
                'windowWillClose:': {
                    types: ['void', ['id']],
                    implementation: function(notification) {
                        return app.terminate(0);
                    }
                }
            }
        });
        ObjC.registerSubclass({
            name: 'AppDelegate',
            methods: {
                'onButtonClicked': {
                    types: ['void', ['id']],
                    implementation: (button)=> {
                        // parameter ?
                        button.title = "Click!Click!Click!Click!Click!";
                        // this ?
                        this.window.title = "Click!";
                    }
                }
            }
        });
        let appDelegate = $.AppDelegate.new;
        // NSButton
        this.button = $.NSButton.alloc.initWithFrame($.NSMakeRect(10, 10, 150, 50));
        this.button.title = "Click!";
        this.button.bezelStyle = $.NSRoundedBezelStyle;
        this.button.buttonType = $.NSMomentaryLightButton;
        this.button.target = appDelegate;
        this.button.action = "onButtonClicked";
        // NSWindow
        this.window = $.NSWindow.alloc.initWithContentRectStyleMaskBackingDefer(
            $.NSMakeRect(0, 0, 320, 120),
            $.NSTitledWindowMask
            | $.NSClosableWindowMask
            | $.NSMiniaturizableWindowMask
            | $.NSResizableWindowMask,
            $.NSBackingStoreBuffered,
            false
        );
        this.window.title = $("JXA NSButton");
        this.window.orderFrontRegardless;
        this.window.delegate = $.WinDelegate.new;
        this.window.contentView.addSubview(this.button);
        this.window.makeKeyAndOrderFront(this.window);
    }
}

function run(argv) {
    let app = $.NSApplication.sharedApplication;
    app.setActivationPolicy($.NSApplicationActivationPolicyRegular);
    app.mainMenu = function() {
        function newMenu(title, action, key) {
            return $.NSMenuItem.alloc.initWithTitleActionKeyEquivalent(title, action, key);
        }
        const mainMenu = $.NSMenu.alloc.initWithTitle("Test");
        const itemApp  = $.NSMenuItem.new;
        const menuApp  = $.NSMenu.alloc.initWithTitle("Test2");
        itemApp.submenu  = menuApp;
        mainMenu.addItem(itemApp);
        menuApp.addItem(newMenu('Quit', 'terminate:', 'q') );
        return mainMenu;
    }();
    let window = new ButtonWindow(app);
    app.run;
}

ボタンをクリックするとタイトルバーとボタンの文字が変わります。
よしハンドラ指定方法はコレでいいようだ。

で、やはり絶対位置配置なのでボタン文字列のはみ出しが起こります。
macOS って見た目は洗練されているけど API は古臭いな。

constructor(app) の app ポインタは保持されるようです。
app.terminate(0) をどうしようかと思ったけど試してみるもんです。

class と run を使ったらいかにもアプリのコードって感じになった。
アロー関数で this が保持され一般的な class のように使える。
やっぱり class にしたほうが理解しやすい、this だらけになってもーたけど。