GTK+ Overlay

Gedit を F11 でフルスクリーンにする。
その状態でマウスカーソルを上部に持って行くとフルスクリーンコントローラがスルスルと降りてくる。

これをどうやって実現しているのか今まで謎だった。
Clutter や非推奨の GtkFixed を使えば実は簡単なんだけど。
実際 0.1 や拙作 Clutter 動画プレイヤーのシークバーでやっているし。
Cocoa は GtkFixed みたいな古臭い API という事実は置いておいて。

GTK+ は基本的に Widget を配置すると引き伸ばしされる。
加えて GtkBin のサブクラスには Widget を一つしか配置できない。
つまり Widget 同士を重ねて配置することは不可能。
しかし Gedit 等は実現している、手段を調べてみよう。

実は Widget の引き伸ばしは単なるデフォルト動作だった。
知らなかったよマジで。
valign, halign プロパティのデフォルトが GTK_ALIGN_FILL なのだ。
つまり。

#!/usr/bin/gjs

const System = imports.system;
const GObject = imports.gi.GObject;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;

var FlWindow = GObject.registerClass({
    GTypeName: "FlWindow"
}, class FlWindow extends Gtk.ApplicationWindow {
    _init(app) {
        super._init({application: app});
        //
        let entry = new Gtk.Entry({
            text: "this is a Entry",
            valign: Gtk.Align.END
        });
        this.add(entry);
        //
        this.show_all();
    }
});

var FlApplication = GObject.registerClass({
    GTypeName: "FlApplication"
}, class FlApplication extends Gtk.Application {
    _init(v) {
        GLib.set_prgname("MyProgram");
        super._init({
            application_id: "palepoli.skr.jp",
            flags: Gio.ApplicationFlags.HANDLES_OPEN
        });
    }
    vfunc_startup() {
        super.vfunc_startup();
        new FlWindow(this);
    }
    vfunc_open(files, hint) {
        this.active_window.present();
    }
    vfunc_activate() {
        this.active_window.present();
    }
});

new FlApplication().run([System.programInvocationName].concat(ARGV));

で。

と valign, halign プロパティで Widget を上下左右に貼り付けることができる。
ウインドウをリサイズすると追従することが確認できる。

次は Widget 同士を重ねる方法。
なんてことはない、GtkOverlay というズバリな Widget があった。

#!/usr/bin/gjs

const System = imports.system;
const GObject = imports.gi.GObject;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;

var FlWindow = GObject.registerClass({
    GTypeName: "FlWindow"
}, class FlWindow extends Gtk.ApplicationWindow {
    _init(app) {
        super._init({application: app});
        // Widgets
        this.entry = new Gtk.Entry({
            no_show_all: true,
            text: "this is a Entry",
            valign: Gtk.Align.START
        });
        let view = new Gtk.TextView();
        view.buffer.set_text("1\n2\n3\n4\n5", -1);
        // Overlay
        let ol = new Gtk.Overlay();
        ol.add(view);
        ol.add_overlay(this.entry);
        this.add(ol);
        // Signal
        this.connect("motion-notify-event", (widget, event)=> {
            let [b, x, y] = event.get_coords();
            this.entry.visible = y < 50;
        });
        this.show_all();
    }
});

var FlApplication = GObject.registerClass({
    GTypeName: "FlApplication"
}, class FlApplication extends Gtk.Application {
    _init(v) {
        GLib.set_prgname("MyProgram");
        super._init({
            application_id: "palepoli.skr.jp",
            flags: Gio.ApplicationFlags.HANDLES_OPEN
        });
    }
    vfunc_startup() {
        super.vfunc_startup();
        new FlWindow(this);
    }
    vfunc_open(files, hint) {
        this.active_window.present();
    }
    vfunc_activate() {
        this.active_window.present();
    }
});

new FlApplication().run([System.programInvocationName].concat(ARGV));

結果。

クライアント領域上部にマウスカーソルを持って行くと Widget が重なって出て来る。
こんな感じでフルスクリーン時のみ Widget を重ねて上部に出してやればオケ。

後は Gedit のようにアニメーションで出す方法だけど。
ソースを見ると GtkRevealer を使っているので同じ配置をしてみたんだけど。
GTK_REVEALER_TRANSITION_TYPE_CROSSFADE 以外動かない、何故だ?
おまけに motion-notify-event で得る値がおかしくなる、ワカンネェ!
これについては気が向いたら後日。