GTK+」タグアーカイブ

GtkDrawingArea Custom Drawing

前回の続き、GtkDrawingArea です。
フォトレタッチのようにマウス軌跡の連続線を GTK+ で描きたい。
って探すまでもなく GTK+ 3 Reference Manual に書いてあった。

Custom Drawing: GTK+ 3 Reference Manual

つまり軌跡を残すには draw シグナル引数にある cairo_t の中に描写しない。
サーフェス(面の意味)を別に作ってそちらに描写する、当然追記になる。
そのまま gtk_widget_queue_draw 関数を呼び出し draw シグナルを送る。
ハンドラにて先程のサーフェスを cairo_t にセットする。

解ってしまえばそりゃそうだ、みたいな。

しかし gjs へのバインディング記法がサッパリ解らない。
Gir の cairo は何もできないし、使おうとした人なら解ってくれるよね。
Gdk.cairo*** も GTK+ と同様に書き換えるとエラー。
cr.set_source_rgb みたいなバインドではないようだ、うーん。

installed-tests/js/testCairo.js ? master ? GNOME / gjs ? GitLab

散々遠回りをしてやっとこんなのを捜し出した。
リソースにある cairo を使えばいいってことね。
しかしコッチは GTK+ と違って C 言語とかなり違う表記を強いられる。

// C
// JavaScript

cairo_create (surface);
new Cairo.Context(surface);

cairo_set_source_surface (cr, surface, 0, 0);
cr.setSourceSurface(surface, 0, 0);

cairo_set_source_rgb (cr, 1, 1, 1);
cr.setSource(Cairo.SolidPattern.createRGB(1, 1, 1));

gdk_window_create_similar_surface
widget.window.create_similar_surface
//Gdk.Window.create_similar_surface // Error

CAIRO_CONTENT_COLOR
Cairo.Content.COLOR

てな感じ。

ということで、実際に上手くいったソース。

#!/usr/bin/gjs

// Resource
const System = imports.system;
const Cairo = imports.cairo;
// Gir
const GObject = imports.gi.GObject;
const GLib = imports.gi.GLib;
const Gio = imports.gi.Gio;
const Gtk = imports.gi.Gtk;
const Gdk = imports.gi.Gdk;

var DrawingWindow = GObject.registerClass({
    GTypeName: "DrawingWindow"
}, class DrawingWindow extends Gtk.ApplicationWindow {
    _init(app) {
        super._init({application: app});
        // var
        this.surface = null;
        // HeadreBar
        let hb = new Gtk.HeaderBar({show_close_button: true});
        let clearButton = new Gtk.Button({label: "Clear"});
        clearButton.connect("clicked", ()=> {
            this.clearSurface();
            this.canvas.queue_draw();
        });
        hb.pack_start(clearButton);
        this.set_titlebar(hb);
        // DrawingArea
        this.canvas = new Gtk.DrawingArea();
        this.canvas.set_events(
            Gdk.EventMask.BUTTON_PRESS_MASK |
            Gdk.EventMask.BUTTON_RELEASE_MASK |
            Gdk.EventMask.BUTTON1_MOTION_MASK |
            Gdk.EventMask.STRUCTURE_MASK );
        this.canvas.connect("configure-event", (widget, event)=> {
            this.surface = this.canvas.window.create_similar_surface(
                Cairo.Content.COLOR,
                widget.get_allocated_width(),
                widget.get_allocated_height() );
            this.clearSurface();
            return true;
        });
        this.canvas.connect("button-release-event", (widget, event)=> {
            return false;
        });
        this.canvas.connect("button-press-event", (widget, event)=> {
            if (event.get_button()[1] === 1) {
                let [res, x, y] = event.get_coords();
                hb.set_title(`${x}/${y}`);
                this.drawBrush(x, y);
            }
            return false;
        });
        this.canvas.connect("motion-notify-event", (widget, event)=> {
            let [res, x, y] = event.get_coords();
            hb.set_title(`${x}/${y}`);
            this.drawBrush(x, y);
            return false;
        });
        this.canvas.connect("draw", (widget, cr)=> {
            cr.setSourceSurface(this.surface, 0, 0);
            cr.paint();
            return false;
        });
        this.add(this.canvas);
        // this
        this.resize(400, 400);
        this.show_all();
    }
    drawBrush(x, y) {
        let cr = new Cairo.Context(this.surface);
        cr.arc(x, y, 10.0, 0.0, 2*Math.PI);
        cr.fill();
        //this.canvas.queue_draw();
        this.canvas.queue_draw_area(x-10, y-10, 20, 20);
    }
    clearSurface() {
        let cr = new Cairo.Context(this.surface);
        cr.setSource(Cairo.SolidPattern.createRGB(1, 1, 1));
        cr.paint();
    }
});

var DrawingApplication = GObject.registerClass({
    GTypeName: "DrawingApplication"
}, class DrawingApplication extends Gtk.Application {
    _init(v) {
        GLib.set_prgname("Program");
        super._init({
            application_id: "org.sasakima.drawwin",
            flags: Gio.ApplicationFlags.HANDLES_OPEN
        });
    }
    vfunc_startup() {
        super.vfunc_startup();
        new DrawingWindow(this);
    }
    vfunc_open(files, hint) {
        this.active_window.present();
    }
    vfunc_activate() {
        this.active_window.present();
    }
});

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

で。

となります。
ImageSurface に画像を指定して文字入れ、なんてのもこの応用で可能。
フォトレタッチとはとても言えないけど手段は解ったということで。

GtkDrawingArea GdkEvent

あけましておめでとうございます。
昨日出した comipoli がバグだらけで今年も正月からプログラミング。

まだおかしい、button-press-event がタイトルバーに反応する。
多分 GtkHeaderBar 部分もクライアントエリアとして認識されているかと。
GNOME は GtkHeaderBar を推奨だけど使うとこんな弊害が色々出るんだよなぁ。

GtkDrawingArea で GdkEvent を取ればいいのだが、手段が解らなかった。
普段あまりネタが無いのに元旦にネタができるという。

日本語で見つかったサイトはココだけだった、GTK2 だけど。
GtkDrawingAreaに図形を表示する
2019 年にもなって手打ち HTML かつスマホ用タグ無しサイトってどうなんだ。。。。。
いやそれはどうでもよくて。

c – In GTK+3, how do I get a drawingarea to respond to mouse events? – Stack Overflow

やはり GTK3 も gtk_widget_add_events と can-focus プロパティか。
とにかくやってみよう。

Events: GDK 3 Reference Manual

GdkEventMask を調べたら GDK_BUTTON1_MOTION_MASK なんて便利そうなものが。
マウス左ボタンを押しっ放しで動かした場合のみ飛んでくるシグナルのようだ。
自分でフラグを用意しなくてもいいのね、早速。

#!/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;
const Gdk = imports.gi.Gdk;

var DrawingWindow = GObject.registerClass({
    GTypeName: "DrawingWindow"
}, class DrawingWindow extends Gtk.ApplicationWindow {
    _init(app) {
        super._init({application: app});
        // var
        this.xy = [false, 0, 0];
        // HeadreBar
        let hb = new Gtk.HeaderBar({show_close_button: true});
        this.set_titlebar(hb);
        // DrawingArea
        let da = new Gtk.DrawingArea();//{can_focus: true});
        //da.set_events(
        da.add_events(
            Gdk.EventMask.BUTTON_PRESS_MASK |
            Gdk.EventMask.BUTTON_RELEASE_MASK |
            Gdk.EventMask.BUTTON1_MOTION_MASK |
            Gdk.EventMask.STRUCTURE_MASK );
        da.connect("button-release-event", (widget, event)=> {
            return false;
        });
        da.connect("button-press-event", (widget, event)=> {
            if (event.get_button()[1] === 1) {
                this.xy = event.get_coords();
                hb.set_title(`${this.xy[1]}/${this.xy[2]}`);
                widget.queue_draw();
            }
            return false;
        });
        da.connect("motion-notify-event", (widget, event)=> {
            this.xy = event.get_coords();
            hb.set_title(`${this.xy[1]}/${this.xy[2]}`);
            widget.queue_draw();
            return false;
        });
        da.connect("draw", (widget, cr)=> {
            cr.arc(this.xy[1], this.xy[2], 10.0, 0.0, 2*Math.PI);
            cr.fill();
        });
        this.add(da);
        // this
        this.resize(400, 400);
        this.show_all();
    }
});

var DrawingApplication = GObject.registerClass({
    GTypeName: "DrawingApplication"
}, class DrawingApplication extends Gtk.Application {
    _init(v) {
        GLib.set_prgname("Program");
        super._init({
            application_id: "org.sasakima.drawwin",
            flags: Gio.ApplicationFlags.HANDLES_OPEN
        });
    }
    vfunc_startup() {
        super.vfunc_startup();
        new DrawingWindow(this);
    }
    vfunc_open(files, hint) {
        this.active_window.present();
    }
    vfunc_activate() {
        this.active_window.present();
    }
});

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

あれ?

cairo のクリアをしていないので追加描写だと思ったら書き換えされる。
逆にフォトレタッチみたく追加で描くにはどうすればいいんだ?
あぁヤルことが増えてしまった、それは後日ということで。
とにかくマウス左ボタンに追従する円のできあがり。

マウスだけなら can-focus プロパティはセットする必要が無いみたい。
GDK_KEY_PRESS_MASK 等を使うなら当然必須になると思うけど。

add_events, set_events のどちらでも動いた。
set は初期化、add は既にあるイベントに追加という振り分けみたい。
GtkDrawingArea は元々何もないので同じということね。

今年もこんな感じでいきます。

enable_emoji_completion

Fedora 29 の GTK+ は 3.24 のままだ。
開発者、システム管理者向けの新規事項

だけど上記に書いてある(英語)とおりチョッピリ追加機能がある。
GtkEntry に enable-emoji-completion property が追加されたようだ。
早速試してみよう。

#!/usr/bin/gjs

imports.gi.versions.Gtk = "3.0";
const Gtk = imports.gi.Gtk;
Gtk.init(null);

let w = new Gtk.Window();
w.connect("delete-event", ()=>Gtk.main_quit());
let e = new Gtk.Entry();
e.enable_emoji_completion = true;
w.add(e);
w.show_all();
Gtk.main()

たとえば sushi の絵文字が使いたいな、ってなった時。
コロンの後に打ち込んでいくと

と出て来るので上下矢印キーかマウスで選択して流し込む。
最後まで打ち込んでコロンを打つでは無視されてしまうので注意。

ぶっちゃけコレ GtkTextView でもできないと意味がないような。。。。。
Gtk4 ではどうなるのかな。

ところで今回からこの手の投稿は GTK+ 等のライブラリ名タグに変更します。
gir はどんな言語でも同じだものね。
今迄 Python とか言語名で作ったページの変更をするかどうかは未定。