タグ別アーカイブ: JXA

jxa doShellScript

Gjs のまとめが全然進まないので久々に macOS で jxa でも。

そういえばテンプレートリテラルって doShellScript で使えるの?
可能なら if 文とか for を利用して複雑なコマンドも使えて便利そうだ。

#!/usr/bin/osascript
 
let app = Application.currentApplication();
app.includeStandardAdditions = true;
let res = app.doShellScript(`if [[ $PWD = $HOME ]]; then
    echo ホームです
else
    echo ホームではありません
fi`);
console.log(res);

使えるジャン。

コレを使えばあの有名な Finder で隠しファイル表示切り替えとかを拡張で一発だな。
いや筆者は普段端末で ls -al を使っているんだけーが。
拡張スクリプトならそっちのが便利そう、ってどんなコマンドだっけ?

Finderで隠しファイルを一時的に表示する(キーボードショートカット) – Qiita

command+shift+. で今はイケるんかい!
拡張を作る必要が無かったよ。
しかしコレは知らない人が多そうだ、覚書ページに追記しとこう。

JavaScript Template literals (Here Document)

さて Fedora 26 での Gjs は何か進展があるのかな?
って下記の更新適用はいつだか知らないんだけど。

gjs – GNOME JavaScript/Spidermonkey bindings

あれ、この Template literals ってなんていうか…
こういうのをヒアドキュメントというんじゃないの?

少なくとも WikiPedia では Python の DocString すらヒアドキュメント扱いだぞぃ。
筆写としては「Python のソレは流石に別物!」と言いたいが、色々な解釈があるなぁと。

テンプレート文字列 – JavaScript | MDN

Gjs の機能ではなく ES6 定義か、それなら Node.js てか V8 でも使えるだろう。
うん、どう見てもどう考えてもどう突っ込まれてもヒアドキュメントだよね。

文法とデータ型 – JavaScript | MDN

なのに上記ではヒアドキュメント未対応ですとハッキリ記述、なんだかなぁ。
しっかり定義してくださいよ Mozilla さん。

#!/usr/bin/gjs

let val = "backquote";

let heredoc = `JavaScript
${val}
Test`;

print(heredoc);

バッククォートです、シングルクォートと間違えないでね。
これは便利、プラス記号で文字列の合体って作る側は「読み難いコード」でしかない。
つか Gedit は普通に色分けするんだ、知らなかった。

Safari も対応ってことで JXA でも使えるようだ。
vscode もしっかり色分け。

ちなみに jjs ではバッククォートの時点でエラーになる。
そりゃ今でも let 宣言にすら対応していないのですし。
現状 JAVA 界隈でもまったく使われていないようだし jjs ってガン無視でいいかも。

JXA NSWindow

今回は JXA でウインドウを作ってみる。

実はウチの PyGObject Tips ページのアクセス数で解るんだけどさ。
ウインドウを作るのページだけブッチギリで多いのよ、初心者ってそんなもんだ。
なんだかなぁ、プログラミングが面白いのはその先からだと思うんですけど。
ということで画像も表示してみる。

検索すると色々な実装があるけど結果は全部同じだね。
それなら簡潔で理解が早い人が多いっぽい実装を選んでみよう。

でも気に入らないのは大半の人がウインドウを閉じると終了するコードだ。
おい macOS だぞ、メニューバーに command+Q は必須だろ?
それと無意味な即時実行多すぎ、多分よく解っていないんだろうけど。

NSApp.servicesMenu in JXA ? GitHub

なんだよ、command+Q メニューはこんなにアッサリ実装できるんかい。
まさかこんなところでバックコーテーションを使うとは。
alloc.init だけなら new メソッドでいいみたい、ふむふむ。

しかし一回しか使わない関数なら無名関数の即時実行でいいのに。
即時実行ってそういう場合に使うと思うんだが。
ということで、こんなサンプルコードになりました。

#!/usr/bin/osascript

ObjC.import("Cocoa");

const imgePath = $("/Users/sasakima-nao/Pictures/[シクシクおよよ]三嶋ゆらら(SSR).jpg");

/**
 * Contents
 */
let image = $.NSImage.alloc.initWithContentsOfFile(imgePath);
if (image.isNil()) {
    console.log("Image Not Found.");
}
let imageView = $.NSImageView.alloc.initWithFrame(
    $.NSMakeRect(0, 0, 320, 400)
);
imageView.setImageScaling($.NSScaleToFit);
imageView.setImage(image);

/**
 * Window
 */
let window = $.NSWindow.alloc.initWithContentRectStyleMaskBackingDefer(
    $.NSMakeRect(0, 0, 320, 400),
    $.NSTitledWindowMask
    | $.NSClosableWindowMask
    | $.NSMiniaturizableWindowMask,
    //| $.NSResizableWindowMask,
    $.NSBackingStoreBuffered,
    false
);
window.title = $("[シクシクおよよ]三嶋ゆらら(SSR)");
window.center;
window.orderFrontRegardless;
window.contentView.addSubview(imageView);

/**
 * Application
 */
let app = $.NSApplication.sharedApplication;
app.setActivationPolicy($.NSApplicationActivationPolicyRegular);
app.mainMenu = function() {
    
    function newMenu(title, action, key) {
       return $.NSMenuItem.alloc.initWithTitleActionKeyEquivalent(title, action, key);
    }
    
    const appName = "Test Window";
    
    const mainMenu = $.NSMenu.new;
    const itemApp  = $.NSMenuItem.new;
    const menuApp  = $.NSMenu.new;
    itemApp.submenu  = menuApp;
    mainMenu.addItem(itemApp);
    menuApp.addItem(newMenu(`Quit ${appName}`, 'terminate:', 'q') );

    return mainMenu;
}();
app.run;

やばい、ゆららちゃんがこんなにカワイイとは!
つい炭酸全部使ってフルマカロン取っ、、、、、いやそれはどうでもよくて。

macOS アプリって Application と Window に何も関連性が無いんだね。
あぁだから macOS では Window を全部閉じてもアプリは終了しないのか。

GTK+, WPF, VCL 等は全部 Application class が Window を管理している。
で、管理する Window が無くなったら Application が終了する、という流れ。
mac は本当に独特なんだなと。

しかしメソッドに括弧が無いのは相変わらず慣れない。
app.run にすら不要って意味ワカンネ!

JavaScript toString

JXA で筆者が知りたいことを全部やっている人がいた!

JXA with Require ? GitHub

文字列のメソッドが…完全に Ruby 屋だwww
と思ったら日本人だった。

tom-u – Qiita

もう JXA についてはアチラをごらんください。
一つだけ注意、数値のフォーマットなんだけど。

#!/usr/bin/osascript

// connect
let nsStr = $("合体").stringByAppendingString($("します"));
console.log(nsStr.js);

// format: NSString
nsStr = $.NSString.stringWithFormat($("%@ %@"), $("フォーマット"), $("します"));
console.log(nsStr.js);

// format: JavaScript Number
let num = 3+5;
//nsStr = $.NSString.stringWithFormat($("num: %d"), num); //=> 2135
//nsStr = $.NSString.stringWithFormat($("num: %@"), $(8.toString())); // Error
nsStr = $.NSString.stringWithFormat($("num: %@"), $(num.toString()));
console.log(nsStr.js);

%d がなんでこうなるネン!
理由はもっと JavaScript に詳しい人が教えてくれるだろう、俺は知らん。

てか直接 [数値.toString()] ってできないんだね。
詳しい人は何を今更なんだろうけど。

V8, SpiderMonkey, Nashorn も当然のように全部駄目だった。
エンジンを作っている所はバラバラでも仕様は統一されているって素晴らしい。

JXA NSString UTF-8

JXA で NSString から直接 UTF-8 でファイルに保存する方法が見つかった。

utf 8 – JXA: Set UTF-8 encoding when writing files – Stack Overflow

$("苗ちゃん").writeToFileAtomicallyEncodingError // OK
$("苗ちゃん").writeToFileAtomicallyEncoding // NG

下ではエラーなので今まで不可能だと思っていたけど。
throws つまり例外付き関数の場合は Error を最後に付けるようだ。

write(toFile:atomically:encoding:) – NSString | Apple Developer Documentation

Objective-C からの変換にこんな罠があったとは。
キーワードの先頭を大文字にしてくっつけるだけでは駄目な場合もある。
覚書ページの書き換えをしなきゃいけないな。

ついでにディレクトリの作成なんかもやってみる。

#!/usr/bin/osascript

ObjC.import("Cocoa");

let nil = $();
let newDir = $("新規ディレクトリ");
let newFile = $("nae.txt");
let nae = $("苗ちゃんカワイイ\nなでなでしたい")

let fm = $.NSFileManager.defaultManager;

// Create Directory
fm.createDirectoryAtPathWithIntermediateDirectoriesAttributesError(newDir, false, nil, nil);

// Exist?
if (fm.fileExistsAtPath(newDir)) {
    console.log("Exists");
}

// Directory?
let ref = Ref();
if (fm.fileExistsAtPathIsDirectory(newDir, ref)) {
    if (ref[0])
        console.log("is Directory");
}

// Change Current Directory
fm.changeCurrentDirectoryPath(newDir);
console.log(fm.currentDirectoryPath.js);

// Ceate Text File
let res = nae.writeToFileAtomicallyEncodingError(newFile, true, $.NSUTF8StringEncoding, nil);
if (res) {
    console.log("Write Success!");
}

Ref については公式に解説があるから解ると思う。
PyGtk をパクった Gjs のようにタプルで戻してくれると楽なんだけーが。
って C# とかに慣れているならコッチのほうが理解しやすいかも。