Clutter」タグアーカイブ

Clutter Animation

ClutterActor はアニメーション機能を内蔵している。

clutter_actor_save_easing_state # pause
clutter_actor_set_easing_duration # time
# move, resize, opacity, etc…
clutter_actor_restore_easing_state # start

たったコレだけで様々な変更がアニメーションになって動く。
ということでサンプルコード、クリック毎に Actor が入れ替わります。

#!/usr/bin/gjs

const Gtk = imports.gi.Gtk;
const GtkClutter = imports.gi.GtkClutter;
const Clutter = imports.gi.Clutter;
const Lang = imports.lang;

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

    _init: function(app) {
        this.parent({
            application: app
        });
        this.red = new Clutter.Actor({
            background_color: Clutter.Color.new(255, 0, 0, 255),
            x: 25,
            y: 25,
            width: 50,
            height: 50
        });
        this.blue = new Clutter.Actor({
            background_color: Clutter.Color.new(0, 0, 255, 255),
            x: 75,
            y: 75,
            width: 50,
            height: 50
        });
        // action
        let click = new Clutter.ClickAction();
        click.connect("clicked", Lang.bind(this, function() {
            // pause
            this.red.save_easing_state();
            this.blue.save_easing_state();
            // animation time (default 250)
            this.red.set_easing_duration(2500);
            // move
            let [x, y] = this.red.get_position();
            let [x2, y2] = this.blue.get_position();
            this.red.set_position(x2, y2);
            this.blue.set_position(x, y);
            // play
            this.red.restore_easing_state();
            this.blue.restore_easing_state();
        }));
        // Embed
        let embed = new GtkClutter.Embed();
        let stage = embed.get_stage();
        stage.add_child(this.red);
        stage.add_child(this.blue);
        stage.add_action(click);
        this.add(embed);
        this.show_all();
    }
});

// init
GtkClutter.init(null);

let app = new Gtk.Application();
app.connect("activate", function() {
    new AnimateWin(app);
});
app.run(null);

コレだけだと使いどころがあんまりなさそうだよね。
しかし、ClutterImage の resize が超滑らかになるメリットがあった!
ということで comipoli に早速採用、永遠に実験用アプリ…

ClutterBoxLayout

ClutterStage が現在 Wayland で使えないのはあきらめて。
当面は GtkClutter を使ったサンプルコードを書いていこう。

今回は ClutterActor のレイアウトマネージャ。

GTK+ と全然違い property の layout-manager で指定するようだ。
デフォルトは ClutterFixedLayout になっている。
書くまでもなく絶対値配置なので子 Actor は重なって表示される。

GtkBox のように並べて配置するには ClutterBoxLayout を指定。
clutter_actor_add_child するだけで普通に並んでいく。

#!/usr/bin/gjs

const Clutter = imports.gi.Clutter;
const GtkClutter = imports.gi.GtkClutter;
const Gtk = imports.gi.Gtk;
const Lang = imports.lang;

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

    _init: function() {
        this.parent({
            title: "add_actor"
        });
        // BoxLayout
        let layout = new Clutter.BoxLayout({
            orientation: Clutter.Orientation.VERTICAL,
            spacing: 1
        });
        // Actor
        this.actor = new Clutter.Actor({
            layout_manager: layout
        });
        // add
        let girls = ["椎名心実です", "あかね!", "くおえうえーーーうえうぅぅぅ"];
        for (let i=0; i<girls.length; i++) {
            let item = new Clutter.Text({
                x_align: Clutter.ActorAlign.START,
                x_expand: true,
                text: girls[i]
            });
            this.actor.add_child(item);
        }
        // Embed
        let embed = new GtkClutter.Embed();
        let stage = embed.get_stage();
        stage.add_child(this.actor);
        this.add(embed);
        // this
        this.connect("hide", Gtk.main_quit);
        this.show_all();
    }
});

GtkClutter.init(null);
new ListTest();
Gtk.main();

GListModel を使ってバインドもできる。
頻繁に入れ替えを行う場合はこちらのほうが便利かもしれない。

#!/usr/bin/gjs

const Clutter = imports.gi.Clutter;
const GtkClutter = imports.gi.GtkClutter;
const Gtk = imports.gi.Gtk;
const Gio = imports.gi.Gio;
const Lang = imports.lang;

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

    _init: function() {
        this.parent({
            title: "GListStore"
        });
        // GListStore  
        this.model = new Gio.ListStore({
            item_type: Clutter.Text
        });
        // layout
        let layout = new Clutter.BoxLayout({
            orientation: Clutter.Orientation.VERTICAL,
            //pack_start: true,
            spacing: 1
        });
        // Actor
        this.actor = new Clutter.Actor({
            background_color: Clutter.Color.from_string("#aaa")[1],
            layout_manager: layout
        });
        this.actor.bind_model(this.model, Lang.bind(this, function(item) {
            return item;
        }));
        // append
        let girls = ["椎名心実です", "あかね!", "くおえうえーーーうえうぅぅぅ"];
        for (let i=0; i<girls.length; i++) {
            let item = new Clutter.Text({
                x_align: Clutter.ActorAlign.START,
                x_expand: true,
                text: girls[i]
            });
            this.model.append(item);
        }
        // remove
        this.model.remove(1);
        // Embed
        let embed = new GtkClutter.Embed();
        let stage = embed.get_stage();
        stage.add_child(this.actor);
        this.add(embed);
        // this
        this.connect("hide", Gtk.main_quit);
        this.resize(this.actor.width, this.actor.height);
        this.show_all();
    }
});

GtkClutter.init(null);
new ListTest();
Gtk.main();

GTK+ 同様に子 Actor のサイズによって親のサイズが拡大されるようだ。
ただし GTK+ 部品にまでは適用されないのでそこらは自力で。

ClutterStage on Wayland

GNOME 3.22 の Wayland 環境では ClutterStage で Window が作れない!

#!/usr/bin/env gjs

const Clutter = imports.gi.Clutter;

Clutter.init(null);
let stage = new Clutter.Stage({
    title: "TestWindow",
    background_color: Clutter.Color.from_string("#663322")[1]
});
stage.connect("hide", Clutter.main_quit);
stage.set_size(300, 200);
stage.show();
Clutter.main();

Window にならないヤン!

てか起動してもアクティブにならないし最初は見えないしetc…
アプリケーションメニューは普通に使えるし例外も吐かないんだけど。
Wayland はどういう認識をしているんだろう?

GNOME on Xorg でログインすれば普通に Window になる。
とはいえ通常なら GtkWindow に乗せて使うから致命的な問題ではないんだが。
サンプルコードを書くのに困るくらいだな。

ついでに GNOME on Xorg だと Gedit で Ctrl+F9 等のキーが使えるんだね。
これが Wayland だと動作しないのが筆者は微妙に不便。

ClutterActor on the GtkWidget

今頃気が付いた、sushi は cbr/cbz を表示できる!

あ、sushi は nautilus で Space キーにてプレビューする拡張。
macOS のプレビューと同じものね、Fedora には最初から入っている。

でも巨大ファイルだと最初の表示が遅いなぁ。
CPU 全コアを使って展開しているけど全部読み込むまで表示されないっぽい。
マルチスレッドのほうがいいというわけではないのね。
Evince と同じなので多分 gir で Evince を使って…

そうか、gir で Evince を使えばいいのか!
読み込みの工夫で初回表示を早くできれば我がアプリに使えるかも。
sushi は Gjs だ、早速コードを見てみよう。

gedit /usr/share/sushi/js/viewers/evince.js

gtkclutter

うぅ、思ったより解析に時間が掛りそうだ。
いやまてよ、この下のほうのコードはまさか。
おまけで ClutterActor 上に GTK+ 部品を乗せる方法が解ったぞ。

#class ComipoliGoButton(Clutter.Actor):
class ComipoliGoButton(GtkClutter.Actor):
    def __init__(self, right):
        '''Clutter.Actor.__init__(self)
        if right:
            pixbuf = GdkPixbuf.Pixbuf.new_from_xpm_data(ICON_RIGHT)
        else:
            pixbuf = GdkPixbuf.Pixbuf.new_from_xpm_data(ICON_LEFT)
        image = Clutter.Image()
        image.set_data(
            pixbuf.get_pixels(),
            Cogl.PixelFormat.RGBA_8888,
            pixbuf.get_width(),
            pixbuf.get_height(),
            pixbuf.get_rowstride()
        )
        self.set_content(image)'''
        GtkClutter.Actor.__init__(self)
        if right:
            self.icon = Gtk.Button.new_from_icon_name("go-next-symbolic", Gtk.IconSize.MENU)
        else:
            self.icon = Gtk.Button.new_from_icon_name("go-previous-symbolic", Gtk.IconSize.MENU)
        #self.set_content(self.icon)
        self.props.contents = self.icon
        self.icon.show()
        #self.connect("enter-event", self.on_enter_event)
        #self.connect("leave-event", self.on_leave_event)
        #self.props.opacity = 0x00
        self.set_reactive(True)

うんうん。

gtkclutter2

ClutterActor 継承をヤメにして GtkClutterActor にする。
set_content だと例外になるけど property なら何故かセット可能。
上に GtkWidget があるせいで enter-event が動作しないので変える。
コレだけで普通に GtkWidget と相互の上下関係が作れる、やったね。

よし久々に comipoli の更新でもするか。
でも今日は「名駅周辺でポケボール集め」という大切な大切な用事が…
我が町でポケ GO をやっているとすぐ無くなっちゃうのは痛い。

Next Button

前々回の更新で Comipoli にマウスのスワイプで次ページ機能を付けた。
二週間使ってみたけど駄目だこりゃ、どうしても使い辛い。

スワイプはタッチパネルだから直感的に使えるとよく解った。
マウスはやはり移動とクリック以外をさせてはいけない。
eog の次画像表示がボタンになったのは多分散々議論した結果なのだろう。

それとフルスクリーン化はやはりダブルクリックですよね。
スワイプ操作とバッティングするので外したけどあったほうが良さげ。
なんたって操作性を eog 同様にしたいんだからそうするべき。

ということで Comipoli にもストック画像でボタンを追加。
具体的には go-next-symbolic 等を。

GTK3 Demo – L’Isola di Niente

ってコレはどうやって ClutterImage に取り込みするのだ?
GtkButton なら簡単なんだけど、手段が解らない。

しかたがないので自作画像で。
XPM は透過属性があるので RGBA_8888 を指定。
#2E2E2E でダークテーマの色とほぼ一致するみたい。

#!/usr/bin/env python3

from gi.repository import GdkPixbuf, Clutter, Cogl

ICON_RIGHT = [
"32 32 3 1",
" 	c None",
".	c #2E2E2E",
"+	c #FFFFFF",
".........+......................",
".........++.....................",
".........+++....................",
".........++++...................",
".........+++++..................",
".........++++++.................",
".........+++++++................",
".........++++++++...............",
".........+++++++++..............",
".........++++++++++.............",
".........+++++++++++............",
".........++++++++++++...........",
".........+++++++++++++..........",
".........++++++++++++++.........",
".........+++++++++++++++........",
".........+++++++++++++++........",
".........+++++++++++++++........",
".........+++++++++++++++........",
".........++++++++++++++.........",
".........+++++++++++++..........",
".........++++++++++++...........",
".........+++++++++++............",
".........++++++++++.............",
".........+++++++++..............",
".........++++++++...............",
".........+++++++................",
".........++++++.................",
".........+++++..................",
".........++++...................",
".........+++....................",
".........++.....................",
".........+......................"]

ICON_LEFT = [
"32 32 3 1",
" 	c None",
".	c #2E2E2E",
"+	c #FFFFFF",
"......................+.........",
".....................++.........",
"....................+++.........",
"...................++++.........",
"..................+++++.........",
".................++++++.........",
"................+++++++.........",
"...............++++++++.........",
"..............+++++++++.........",
".............++++++++++.........",
"............+++++++++++.........",
"...........++++++++++++.........",
"..........+++++++++++++.........",
".........++++++++++++++.........",
"........+++++++++++++++.........",
"........+++++++++++++++.........",
"........+++++++++++++++.........",
"........+++++++++++++++.........",
".........++++++++++++++.........",
"..........+++++++++++++.........",
"...........++++++++++++.........",
"............+++++++++++.........",
".............++++++++++.........",
"..............+++++++++.........",
"...............++++++++.........",
"................+++++++.........",
".................++++++.........",
"..................+++++.........",
"...................++++.........",
"....................+++.........",
".....................++.........",
"......................+........."]

class ComipoliGoButton(Clutter.Actor):
    def __init__(self, right):
        Clutter.Actor.__init__(self)
        if right:
            pixbuf = GdkPixbuf.Pixbuf.new_from_xpm_data(ICON_RIGHT)
        else:
            pixbuf = GdkPixbuf.Pixbuf.new_from_xpm_data(ICON_LEFT)
        image = Clutter.Image()
        image.set_data(
            pixbuf.get_pixels(),
            Cogl.PixelFormat.RGBA_8888,
            pixbuf.get_width(),
            pixbuf.get_height(),
            pixbuf.get_rowstride()
        )
        self.set_content(image)
        self.connect("enter-event", self.on_enter_event)
        self.connect("leave-event", self.on_leave_event)
        self.props.opacity = 0x00
        self.set_reactive(True)

    def on_enter_event(self, actor, event):
        self.props.opacity = 0xff

    def on_leave_event(self, actor, event):
        self.props.opacity = 0x00

で。
eog の表示はウザいのでマウスカーソルが乗った時だけ表示させる。
ボタンを大きくしたいので縦に引伸し、これはウインドウ側に実装。

Linux アプリケーション – L’Isola di Niente

で上記スクリーンショットのようになりました。
いや、バージョンが上がったら画像はまた変更するけど。
ストックの取り込みが解ったら変更するかもしれない。

ウインドウの隅にカーソルを移動するだけだから直感的に使えると思う。
そういえばマウスジェスチャってすっかり廃れたよね。
マウスボタンを押しながら移動ってやっぱり全然直感的じゃないのよ。

キモヲタならロッカージェスチャとかホイールのほうがいいとか言うかも。
それを言うのは Windows しか使えない人だけですから。
特に右クリックはコンテキストメニュー以外には使ってはいけない。
というのが現在の Mac と GNOME が共通する思想です。