JXA NSMenu

今回は JXA でメニューバーとキーボードショートカット。
GNOME は取っ払ってしまったけど macOS ではまだ必須だもんな。

JXAでメニューバーアプリ – Qiita

またデリゲートの登録か。。。。。
action に普通の関数を入れてみたりしたけど動かなかった。
従うしかないな。

それと前回 JavaScript 文字列を NSString に変換していなかった。
アルファベットしか使わないならそれでもいいんだけどね。

ということで前回のコードに書き足しと修正を行なって。
ついでに osacompile でコンパイル実行してみる。

#!/usr/bin/osascript

// osacompile -l JavaScript -o MenuTest.app jxa_menu.js

ObjC.import("Cocoa");

class ButtonWindow {
    constructor(app) {
        ObjC.registerSubclass({
            name: "WinDelegate",
            protocols: ["NSWindowDelegate"],
            methods: {
                "windowWillClose:": {
                    types: ["void", ["id"]],
                    implementation: (notification)=> {
                        app.terminate(0);
                    }
                }
            }
        });
        ObjC.registerSubclass({
            name: "AppDelegate",
            methods: {
                "onButtonClicked:": {
                    types: ["void", ["id"]],
                    implementation: (button)=> {
                        this.window.title = $("Click!");
                    }
                }
            }
        });
        let appDelegate = $.AppDelegate.new;
        // NSButton
        this.button = $.NSButton.alloc.initWithFrame($.NSMakeRect(50, 5, 200, 30));
        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, 300, 50),
            $.NSTitledWindowMask
            | $.NSClosableWindowMask
            | $.NSMiniaturizableWindowMask
            | $.NSResizableWindowMask,
            $.NSBackingStoreBuffered,
            false
        );
        this.window.title = $("Title");
        this.window.orderFrontRegardless;
        this.window.delegate = $.WinDelegate.new;
        this.window.contentView.addSubview(this.button);
        this.window.makeKeyAndOrderFront(this.window);
    }
    changeTitle() {
        this.window.title = $("Menu !");
    }
}

function run(argv) {
    const app = $.NSApplication.sharedApplication;
    const window = new ButtonWindow(app);
    app.setActivationPolicy($.NSApplicationActivationPolicyRegular);
    ObjC.registerSubclass({
        name: "MenuAction",
        methods: {
            "changeTitle:": {
                types: ["void", ["id"]],
                implementation: (sender)=> {
                    window.changeTitle();
                }
            }
        }
    });
    app.mainMenu = function() {
        function nm(title, action, key, target) {
            let item = $.NSMenuItem.new;
            if (target) item.target = target;
            item.title = $(title);
            item.action = action;
            item.keyEquivalent = $(key);
            return item;
        }
        // main
        const mainMenu = $.NSMenu.new;
        // Menubar
        const itemApp  = $.NSMenuItem.new;
        const itemFile = $.NSMenuItem.new;
        mainMenu.addItem(itemApp);
        mainMenu.addItem(itemFile);
        // Drop Down Menu
        const menuApp  = $.NSMenu.alloc.initWithTitle($("Comipoli"));
        const menuFile  = $.NSMenu.alloc.initWithTitle($("File"));
        itemApp.submenu  = menuApp;
        itemFile.submenu = menuFile;
        // Action
        menuApp.addItem(nm("Quit", "terminate:", "q", null));
        //  
        let ac = $.MenuAction.new;
        menuFile.addItem(nm("Change", "changeTitle:", "w", ac));
        //
        return mainMenu;
    }();
    app.run;
}

メニューやキーボードショートカットが動作しているのが解る。

コンパイルするとアプリ名になってくれるようだ。
js のまま実行するとアプリ名が osascript になる。
osacompile はシバンを付けたままでも実行できるようです。

何故かスクリプトエディタ.app からはエラーでコンパイルできなかった。
こんなショボいエディタを使っている人はいないだろうからどうでもいいけど。

とりあえず JXA でアプリを作る雛形はこんなもんでいいかな。