JXA」タグアーカイブ

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# とかに慣れているならコッチのほうが理解しやすいかも。

JXA FileList

今回は JXA でディレクトリ内容列挙でも。
まあ Objective-c での手段は検索で山ほど見つかる、しかも日本語で。
Gjs だと英語しか見つからないのに、まあ macOS と比べてもね。

変換するだけとはいえ落とし穴は幾つかある。

#!/usr/bin/osascript

ObjC.import("Cocoa");

nil = $();

let path = $("~/Desktop").stringByExpandingTildeInPath;
let nsFiles = $.NSFileManager.defaultManager.contentsOfDirectoryAtPathError(path, nil);
//$.NSLog("%@", nsFiles);
let jsFiles = nsFiles.js; // NSArray to JS Array
for (let i=0; i<jsFiles.length; i++) {
    console.log(jsFiles[i].js);
}

nil は null ではない。
そういえばデルヒャァがそうだったなぁ、もう完全に忘れたけど。

NSArray のままではループできない。
こいつの変換も *.js メソッドでイケるのか、なるほど。

次は再起のように中身まで辿っていく手段。

#!/usr/bin/osascript

ObjC.import("Cocoa");

let path = $("~/Documents").stringByExpandingTildeInPath;
let nsDirEnum = $.NSFileManager.defaultManager.enumeratorAtPath(path);
//while (let name = nsDirEnum.nextObject) {
while (true) {
    let name = nsDirEnum.nextObject;
    if (name.isNil()) break;
    let fullPath = path.stringByAppendingPathComponent(name);   // Full Path
    console.log(fullPath.js);
}
console.log("done");

if (name == $()) では認識してくれない。
つまり検索でよく見つかるコメントアウト部分は使えない。
isNil() でなんとか JavaScript の boolean 値だと認識するようだ。

Gjs や PyGObject と違って型が明確に分離している。
ってもソレさえ理解できてしまえば日本語情報があるだけ簡単だね。

どうでもいいけど Gedit は日本語入力できないまま使っている。
開き直って日本語を使わなければいいのさ、ガッハッハ!
まあ JXA 専用だね。

JXA and JavaScriptCore stdin

JavaScriptCore は readline が使えるのか。
MacのJavaScript Coreで標準入出力 – Qiita

こういう即時実行を使っている人は何か理由があるのだろうか?
よく見かけるけど無意味だと思うが、まあそれはいいとして。

#!/System/Library/Frameworks/JavaScriptCore.framework/Versions/A/Resources/jsc

print("May I ask your name? ");
let s = this.readline();
print("Hello " + decodeURIComponent(escape(s)));

// ex: ft=js

動くんだね。
戻り値は当然 UTF-8 のままなので変換しないと日本語は化ける。

JXA でも同様かと思ったけどできないや、そもそも print 関数すら無い。
JavaScriptCore とはまったくの別物?イマイチ解っていない。
コッチは Cocoa を使えってことなんでしょうか。

ということで久々に JXA をやってみたので更新。
JXA 版はコッチで。
JavaScript Tips – L’Isola di Niente

JavaScript 文字列と NSString と UTF-8 文字列な NSData が混在。
やっと相互変換を全部やれたけどもう少しなんとかならないものか。
てか情報が少なすぎ、そんな筆者もメインは Fedora てか GNOME だけど。

関係ないけど jjs の -scripting オプション。
シバンに書き込んだら jjs への引渡しでも適用された、そういうもの?