Programming」カテゴリーアーカイブ

array.forEach (this == array)

JavaScript は本当に奇妙な言語です。

#!/usr/bin/gjs

const Lang = imports.lang;

const TestClass = new Lang.Class({
    Name: 'TestClass',

    _init: function() {

        this.arr = [12, 34, 56];
        this.str = "String";

        for (let i=0; i<this.arr.length; i++) {
            print(this.str);
            print(this.arr[i]);
        }
        this.arr.forEach(function(s) {
            print(this.str);
            print(s);
        });
    }
});
new TestClass();

/* output
String
12
String
34
String
56
undefined
12
undefined
34
undefined
56
*/

ループは forEach が解かり易いので多用していたけどコレにハマった。
Gjs はそもそも class が無い JavaScript にアクロバットな対応をしている。
this は C++ の this では無いと気が付くまで半日近く使ってしまった。

JavaScript の this は呼び出し元という意味。
つまり上記では forEach の this は this.arr である。
this.arr.str なんて宣言していないから undefined ということです。

foreach

こんな深い階層で this を使おうとした筆者が悪いんだけどさ。
Gjs だけではないので注意しようね。

set_accels_for_action

前回書いたように Y901x の ClutterGst 化をしているのですが。
マジで何を今更なことを見つけた。

GtkApplication: GTK+ 3 Reference Manual#gtk-application-add-accelerator

add_accelerator は 3.14 以降は使うなってさ。
おぃおぃ Gedit 標準プラグインが今でも普通に使っているんですけど。

とりあえず Gjs だとこんな感じでいいみたいみたい。
Gio.SimpleAction のほうは今までどおりでいい。
次回の更新で自作の奴も書き換えるか。

const Y901Application = new Lang.Class({
    Name: 'Y901Application',
    Extends: Gtk.Application,

    _init: function() {
        this.parent({});
        //this.add_accelerator("<Control>Q", "win.ctrl_q_action", null);
        this.set_accels_for_action("win.ctrl_q_action", ["<Control>Q"]);
    },
    vfunc_open: function(files, n_file, hint) {
        let uri = files[0].get_uri();
        let w = new Y901Window(this);
        w.set_uri(uri);
    },
    vfunc_activate: function() {
        new Y901Window(this);
    }
});

ついでに Gjs で上記のように open をオーバーライドするには。

let argv = ["y901main.js"];
ARGV.forEach(function(s) {
    argv.push(s);
});
GtkClutter.init(null);
ClutterGst.init(null);
let application = new Y901Application();
application.run(argv);

みたいに配列の先頭に何か突っ込まないと動作しない。
何故こんな仕様にしてしまったんだろう?

他色々と苦しみながらこんな所までやってみた。
DnD で再生、スペースキーでポーズ、シークバーも一応使える。

zep

我ながら以前の面影が何も無いけどどうしよう、名前は変えようか?
シークバーはコラム無しだしポーズボタンも無いし拡大はマウスのみだし。
シークバーも Clutter で作っているので、まだ手探り中なので。

y901x-1.1.x0.tar.gz

ここまでのソースを置いておきます、単なるバックアップですけど。
もし筆者が死んだら誰か続きをやってね。

osascript Filenames

今回は osascript でファイル名関連をば。

OS?X 10.10 Release Notes

を見つけてやっと Objective-C からの変換方法が解ってきた。
物凄く下のほうにあって見つけにくかったよ。
たとえばこんなふうに引数が二つ以上の場合は先頭を大文字にして合体。

/*
contentsOfDirectoryAtPath:error:
to
contentsOfDirectoryAtPathError
*/

fm = $.NSFileManager.defaultManager
files = fm.contentsOfDirectoryAtPathError(pathB, null)

– fileExistsAtPath:isDirectory: – NSFileManager Class Reference

つまり Objective-C の文法を少し理解していないと変換すらできない。
コロンって引数だったのか、解ってしまえば楽勝だけど。

ディレクトリ内容列挙は NSFileManager でできるようだ。
ということでコードを書いてみる。
実は var とセミコロンはいらないけどキモいので書くことにした。

#!/usr/bin/osascript

ObjC.import("Cocoa");

// NSString
var pathA = $.NSString.alloc.initWithString("~/Documents/");
// ~ to $HOME
var pathB = pathA.stringByExpandingTildeInPath;
console.log("Path @ " + pathB.UTF8String);
// to URI
var uri = $.NSURL.fileURLWithPath(pathB);
console.log("URI @ " + uri.absoluteString.UTF8String);

// FileManager
var fm = $.NSFileManager.defaultManager;
// get files
var files = fm.contentsOfDirectoryAtPathError(pathB, null);
for (var i=0; i<files.count; i++) {
    // error files[i]
    var filename = files.objectAtIndex(i).UTF8String;
    // remove dot file
    if (filename[0] != ".") {
        console.log(filename);
    }
}

おかげで気が付いた、NSArray は添字ではアクセスできない。
JavaScript の配列でないからか、当然 forEach も length も使えない。
UTF8String に変換した後の文字列は添字可能、色々戸惑う。
バインドしているのではなく明確に型を分けているということみたい。

そんなこんなで。
Mac では JavaScript だけでやっていこうと思っていたけどコリャ無理だ。
Objective-C の勉強も始めますか、Fedora では使わないと思うけど。

Gjs Desktop Mascot

近頃は小ネタばかりでまともなサンプルコードを書いていないと気が付いた。
このブログらしくないので久々に小物アプリ作ってみます。

もちろん JavaScript(Gjs) で。
基本は大分やったと思うので実際に何か作ってみるとどうかを試したい。
経験で実際にアプリを作ってみないと気が付かないことが多いと知っている。

とりあえず今回はデスクトップマスコットを。
背景が透過されている PNG 画像をデスクトップに表示というありがちなもので。

常にすべての GUI アプリの再背面に表示とかイベントの処理とかのサンプルにも。
それなら応用範囲も広がるだろう、と勝手に考える。
何より重要なのが GtkApplication を必ず使う。
でないと後々で困ることになりそうな謎の雰囲気があるのですよ。
GNOME 公式サンプルからして。

ということで。
背景透過画像は以下からカワイイ苗ちゃんを拾って使う。

【ウチ姫】UR++「小粒姫 優木 苗」ステータス・評価まとめ – お姫さま図鑑|ウチ姫公式攻略Wiki – GAMY(ゲーミー)

んで、こんなコードになりました。

#!/usr/bin/gjs

const Gtk = imports.gi.Gtk;
const Gdk = imports.gi.Gdk;
const Gio = imports.gi.Gio;
const GLib = imports.gi.GLib;
const GdkPixbuf = imports.gi.GdkPixbuf;
const Lang = imports.lang;

const NaeWindow = new Lang.Class({
    Name: 'NaeWindow',
    Extends: Gtk.ApplicationWindow,

    _init: function(app) {
        this.parent({
            application: app
        });
        // GdkColormap to GdkVisual
        let screen = this.get_screen();
        let visual = screen.get_rgba_visual();
        if (visual != null && screen.is_composited()) {
            this.set_visual(visual);
        } else {
            print("no Composited...");
        }
        this.set_app_paintable(true);
        // Transparent background image
        this.pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size("nae_ur++.png", 300, 300);
        // Event Mouse
        //this.set_events(Gdk.EventMask.BUTTON_PRESS_MASK);
        this.connect("button-press-event", Lang.bind(this, function(widget, event) {
            /* NG
            https://people.gnome.org/~gcampagna/docs/Gdk-3.0/Gdk.Event.html
            
            if (event.type === Gdk.EventType.BUTTON_PRESS) {
                this.begin_move_drag(event.button, event.x_root, event.y_root, event.time);
            */
            if (event.get_event_type() === Gdk.EventType.BUTTON_PRESS) {
                let [res, x_root, y_root] = event.get_root_coords();
                this.begin_move_drag(
                    event.get_button()[1],
                    x_root,
                    y_root,
                    event.get_time()
                );
            } else if (event.get_event_type() == Gdk.EventType.DOUBLE_BUTTON_PRESS) {
                this.close();
            }
        }));
        // Event Draw
        this.connect("draw", Lang.bind(this, function(widget, cr) {
            Gdk.cairo_set_source_pixbuf(cr, this.pixbuf, 0, 0);
            cr.paint();
        }));
        // z-order
        this.set_keep_below(true);
        // etc
        this.set_decorated(false);
        this.resize(300, 300);
        this.show_all();
    }
});

const Application = new Lang.Class({
    Name: 'Application',
    Extends: Gtk.Application,

    _init: function() {
        // TaskBar Title
        GLib.set_prgname("Nae Chan !");
        // property
        this.parent({
            application_id: 'apps.test.NaeChan',
            flags: Gio.ApplicationFlags.FLAGS_NONE
        });
    },
    // override activate signal
    vfunc_activate: function() {
        new NaeWindow(this);
    }
});
let application = new Application();
application.run(ARGV);

desktop_naechan

あぁ、苗ちゃんがいつもデスクトップにいる。
カワイイよぉ、剥製にして飾…(嘘です

最近気が付いた、g_set_prgname でアクティビティ横の文字列を決められる。
たしか Ubuntu は違ったような、まあ猿専用ディストリなんてガン無視でいい。

そんなことより。
button-press-event ハンドラの仕様は何なんだよ!

Gdk.Event

UNION だとこういうバインドしか手段が無いのか、それとも別の理由か。
現状 Python, C のようにメソッドは使えず関数で取得しか手が無い。

ぶっちゃけ。
コレで混乱しなかったらこんな素敵なサイトなんて見つけられなかったぞ!
やはり何か具体的にアプリを作ってみないと解らない事って多い。

Gjs resource

Gjs のサンプルコードはほとんど以下が書いてある。

const Lang = imports.lang;

この lang.js って今はドコにあるの?
以前は /usr/share/gjs-1.0 以下にあったのに。

Gjs は gir 以外のモジュールをどこから探しているのだろう。
以下のコードで調べることができるようだ。

#!/usr/bin/gjs

let list = imports.searchPath;
for (let s of list) {
    print(s);
}
/* output @ 3.18
resource:///org/gnome/gjs/modules/
/usr/local/share/gjs-1.0
/usr/share/gjs-1.0
/usr/share/gjs-1.0
*/

こんなディレクトリは 3.18 では存在しない。
どうやら現行版はリソース化されてソレを参照しているようだ。

ちなみにこの resource:///org/gnome/* は Gvfs の URI でして。
たとえば Nautilus にて Ctrl+L して resource:/// と打ち込むと。

resource1

と Nautilus が参照しているリソースにアクセスできます。
ファイルに見えるけど拡張子が無いものはディレクトリ。
Gedit の FileChooserDialog で Ctrl+L しても同じようにできます。

resource2

各アプリが参照しているリソースにしかアクセスできませんので注意。
ということで、Gjs のリソースの中身を探してみる。

gjs/modules at master ? GNOME/gjs ? GitHub

上記がリソースの中身らしい、lang.js もしっかりあります。
以前書いた format.js もこの中にある。
Here Document | PaePoi

色々まとめようと思ったけどバージョンでコロコロ変わりそう。
もしかしたら Gjs も PyGObject 同様にバージョン指定が必要になるかも。
Fedora 24 が出てからもう少し確認しよう。

GNOME 開発センター

もはや GNOME 公式の overview が Gjs なんだよな。
vfunc_* のオーバーライドを使っていないのは何故だ?

GitHub – gcampax/gtk-js-app: A template for a standard Gtk/GNOME JS application

コッチのほうが解りやすい、メモメモ。