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

Clutter Fullscreen HeaderBar

フルスクリーン時にマウスカーソルを上に持って行くとヘッダーがヒョッコリ。
という処理は GTK+ なら GtkOverlay で簡単に実現できる。

GTK+ Overlay | PaePoi

けれど Y901x でやってみたらどうやっても表示してくれない。
GtkOverlay は GtkClutter.Embed の上には被せることができないようだ。

なので GtkClutter.Actor の上にヘッダーを置いて表示させるしかない。
GTK+ の上に Clutter を載せて、その上に GTK+ を置く。
という奇妙な構造だけど他に手段が無い。
下記はそのまま動くようにしたものを抜き出ししてみた。

#!/usr/bin/gjs

const GObject = imports.gi.GObject;
const Gtk = imports.gi.Gtk;
const Clutter = imports.gi.Clutter;
const GtkClutter = imports.gi.GtkClutter;

var ClWindow = GObject.registerClass({
    GTypeName: 'ClWindow'
}, class ClWindow extends Gtk.Window {
    _init() {
        super._init();
        this.is_fullscreen = false;
        // actor
        this.actor = new Clutter.Actor();
        // Fullscreen Header and Actor
        this.upperbar = new Gtk.HeaderBar({
            no_show_all: true,
            valign: Gtk.Align.START
        });
        this.upperActor = new GtkClutter.Actor({
            contents: this.upperbar
        });
        this.upperActor.hide();
        // Restore Button
        let restoreButton = new Gtk.Button({
            image: Gtk.Image.new_from_icon_name('view-restore-symbolic', Gtk.IconSize.MENU),
            visible: true
        });
        restoreButton.connect('clicked', ()=> {
            this.change_fullscreen();
        });
        this.upperbar.pack_end(restoreButton);
        // Embed
        let embed = new GtkClutter.Embed();
        let stage = embed.get_stage();
        stage.add_child(this.actor);
        stage.add_child(this.upperActor);
        stage.connect('motion-event', (actor, event)=> {
            if (this.is_fullscreen) {
                let [, y] = event.get_coords();
                // upperbar
                if (this.is_fullscreen) {
                    if (y <= this.hb_height+10 && !this.upperbar.visible) {
                        this.upperActor.show();
                        this.upperbar.show();
                    } else if (y > this.hb_height+10 && this.upperbar.visible) {
                        this.upperActor.hide();
                        this.upperbar.hide();
                    }
                }
            }
        });
        // box
        let vbox = new Gtk.Box({orientation: Gtk.Orientation.VERTICAL});
        vbox.pack_start(embed, true, true, 0);
        vbox.connect('size-allocate', (vbox, allocation)=> {
            if (this.is_fullscreen) {
                this.upperActor.set_size(allocation.width, this.hb_height);
                this.upperbar.set_size_request(allocation.width, this.hb_height);
            }
        });
        // Double Click Fullscreen
        vbox.connect('button-press-event', (widget, event)=> {
            if (event.get_event_type() == 5) {
                this.change_fullscreen();
            }
            return true;
        });
        // main
        let hb = new Gtk.HeaderBar({show_close_button: true});
        this.set_titlebar(hb);
        this.add(vbox);
        this.connect('delete-event', ()=> {Gtk.main_quit();});
        this.resize(300, 300);
        this.show_all();
        this.hb_height = hb.get_allocated_height();
    }
    change_fullscreen() {
        this.is_fullscreen = this.is_fullscreen === false;
        if (this.is_fullscreen) {
            this.fullscreen();
        } else {
            this.unfullscreen();
            if (this.upperbar.visible) {
                this.upperbar.hide();
                this.upperActor.hide();
            }
        }
    }
});
Gtk.init(null);
GtkClutter.init(null);
let w = new ClWindow();
Gtk.main();

やっぱり長いな。

細かい解説はしないから自力で解析してね。
注意点は GtkWindow の motion-notify-event で処理しないこと。
ドロップしたメニューを選ぼうとマウスを動かすとメニューが消える罠がwwwww

って Comipoli 0.3.5 がそうなっているということに今気が付いた!
Comipoli は全部 GTK+ なんだが、同じ手段は使えない。
さて困った、eog みたいにメニューを出さないという手もあるけど。

GtkPopover

Fedora 30 の準備をそろそろしなければ。

GNOME が 3.32 になって ApplicationMenu が非推奨に。
GTK3 アプリ以外には広がらなかったからしかたがないかもね。
Android のメニューボタンみたいなものだし広まってもよさげだったのにね。
何故か反 Apple みたいな輩がボロクソに言っていたり、根暗ってマジで意味不明。

我がアプリもハンバーガーメニューに変更しなきゃ、面倒だなぁ。
と思ったんだけーが。

#!/usr/bin/env python3

from gi.repository import Gtk

class ComipoliMenuButton(Gtk.Button):
    def __init__(self):
        # MenuIten
        menu_open  = Gtk.ModelButton(active=True, action_name='app.new_file_action', text="_Open", use_markup=True)
        menu_new   = Gtk.ModelButton(active=True, action_name='app.new_window_action', text="_New Window", use_markup=True)
        menu_pref  = Gtk.ModelButton(active=True, action_name='app.preference_action', text="_Preference", use_markup=True)
        menu_kbd   = Gtk.ModelButton(active=True, action_name='app.shortcut_action', text="_Keyboard Shortcut", use_markup=True)
        menu_about = Gtk.ModelButton(active=True, action_name='app.about_action', text="_About", use_markup=True)
        menu_quit  = Gtk.ModelButton(active=True, action_name='app.quit_action', text="_Quit", use_markup=True)
        # Box
        vbox = Gtk.Box(visible=True, margin=10, orientation=Gtk.Orientation.VERTICAL)
        vbox.pack_start(menu_open, False, False, 0)
        vbox.pack_start(Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL), False, False, 0)
        vbox.pack_start(menu_new, False, False, 0)
        vbox.pack_start(menu_pref, False, False, 0)
        vbox.pack_start(menu_kbd, False, False, 0)
        vbox.pack_start(menu_about, False, False, 0)
        vbox.pack_start(Gtk.Separator(orientation=Gtk.Orientation.HORIZONTAL), False, False, 0)
        vbox.pack_start(menu_quit, False, False, 0)
        # Hamburger Image
        image = Gtk.Image(icon_name='open-menu-symbolic')
        # init
        Gtk.Button.__init__(self, can_focus=False, focus_on_click=False, image=image, visible=True)
        # Popover
        self.popover = Gtk.Popover(relative_to=self)
        self.popover.add(vbox)

    def do_clicked(self):
        if self.popover.props.visible:
            self.popover.hide()
        else:
            self.popover.show_all()

を作ってみた。

GtkApplication から Gio.Menu だけを消す。
app.*** と指定していたアクション名を GtkModelButton でそのまま指定。
後は掟どおりにパッキング、relative-to に自分を指定。
GtkPopover のクリック処理もオーバーライドでやってしまえ。

って、コレだけで動いてしまうジャン!
本体に新たなハンドラを書く必要もアクションを新規で作る必要も無かった。
つまり変更は超簡単です、本体に F10 でメニュードロップ追加を忘れずに。

後はフルスクリーン時の F10 キーをどうするかだ、斜めにポップアップするんだが。
それとデザインがなんかイマイチ、Fedora 30 が出るまでに色々考えておく。
てか Y901x はどうしよう、スタンドアロンの動画プレイヤーってもういらなくね?

いやまあ、AppKit では分離があたりまえなので同じようにできないかなって。
AppKit より簡単だった、フレームワークは色々使ってみると経験値が上がるね。
そんなことより、やっぱり RealForce は快適だ!

f-string

PEP 498 — Literal String Interpolation | Python.org

f-string なんてものが Python 3.6 から使えるのか。

#!/usr/bin/env python3

suzuki = 'スズキ'

# Python2 互換
print('%sのバイクはカッコイイ' % suzuki)
# Python3 format
print('{0}はスクーターもカッコイイ'.format(suzuki))
# f-string (3.6 or lator)
print(f'{suzuki}は自動車もカッコイイ')

# raw f-string
lf = '元からある改行はダメだけど\n'
print(fr'{lf}この \n は改行されない')

# docstring
doc = 'ヒアドキュメント'
print(f'''これなら ' も " も普通に使える
まるで{doc}だね''')

# ゼロ詰め
for n in range(3):
    print(f'{n:#03d}')

なるほど。

当然だけど f-string が一番短く書ける、かつ理解しやすい。
ゼロ詰めが可能なので js のテンプレートリテラルより便利。
今後 Python はコレでいこう。

vscode だとヘンテコな色分けをしてくれます。
てか f’ と打ったら閉じクォートを勝手に補完するのをヤメてくれ!
閉じクォート補完設定は OFF にしているのに、別設定なのかよ。
本当に余計なことばかりするクソエディタだな。

私的には $ 記号が欲しかった、sh, php, js 互換になるし。

Graphics

18 日に上げた PyObjC Tips の日付が 2018 年になってた!
よしさっさと更新してごまかしてまえwwwww

PyObjC Tips – L’Isola di Niente

てか、AppKit に関しては comipoli で必要な API しか調べていないんで。
全部調べて作ってだとスゲェ時間が掛かるので細切れで出すことにした。
ゴールデンウイークは 10 連休なのでもちっと進めたい。

あっちにも書いたけど。
Cocoa Dev Central: Cocoa Graphics with Quartz
Cocoa Dev Central: Cocoa Graphics with Quartz II
すごく解りやすいよね。
今後のページ作りの参考にしよう。

cairo だとココとか。
Cairo samples

日本語でこの手を探すとスクショだらけな Xcode の使い方解説ばかりで困る。
Visual Studio もそうだったね、Mac も Windows も本当に何も変わらないなと。
インストールのスキルしか上がらない Node.js よりマシ程度。
日本人はコードを書くのが嫌いなんだろうか?

import objc

今頃気が付いた。
PyObjC の AppKit に objc は含まれていた。

#!/usr/bin/env python3

#import objc
from AppKit import *

いらなかったんや!!!

PyObjC Tips ページを作っていて気がつく、書いていないのに動いたって。
作っているうちに又気がついたことがあったらコッチに書くわ、ネタがないし。
しかしゼロから作るのはどう書くか迷って中々進まない。