Ubuntu Weekly Recipe

第492回 GNOME Shellの拡張機能を作ってみよう

この記事を読むのに必要な時間:およそ 6 分

extension.jsの中身

拡張機能の本体であるextension.jsの中身を見ていきましょう。基本的に「拡張機能ロード時に呼び出されるinit()関数」⁠拡張機能有効化時のenable()⁠拡張機能無効化時のdisable()が存在し,拡張機能ごとにそれ以外の関数を実装・呼び出していくことになります。

const St = imports.gi.St;
    const Main = imports.ui.main;
    const Tweener = imports.ui.tweener;
    
    let text, button;

ファイルの冒頭は必要な機能のインポートとグローバル変数の設定です。St「Shell Toolkit」と呼ばれるUIエレメントを操作するツールキットです※2)⁠実際はClutterのJavaScriptバインディングなので,Clutterのドキュメントも参考になるでしょう。MainはStで作成したUIエレメントを登録し管理するインスタンスです。Tweenerはアニメーションフレームワークです。UIエレメントを動的に変化させたい場合に使います。

※2
Moblinのツールキットがベースになっています。http://moblin.org/projects/moblin-ui-toolkitにアクセスしようとしたら,Tizenのサイトにリダイレクトされました。

textやbuttonはこのサンプル拡張機能で使用しているブロックスコープの変数です。

先にinit()の中を見てみましょう。前述したとおり,この関数はロード時に一度だけ呼び出されます。拡張機能としては必須の関数です。有効無効に関わらずロード時に呼び出されるため,この関数の中でUIの変更(つまりMainオブジェクトに対する変更)を行ってはいけません。

今回のサンプルではトップパネルに表示するボタン(UIコンテナー)とアイコンを作成し,それらを紐付けた上で,ボタン上でマウスを押した時のイベントハンドラーを登録しています。

function init() {
    /*
     * トップパネルに表示するボタン(UIコンテナー)を作成し、
     * プロパティを設定しています。
     *
     *   style_class: スタイルシートのクラス名
     *   reactive: マウス操作などに応答するかどうか
     *   can_focus: キーボード操作によるフォーカスが合うかどうか
     *   x_fill/y_fill: 子要素がx/y方向それぞれに親要素にあわせて
     *                  サイズ調整されるかどうか
     *   track_hover: マウスカーソルでhoverステートになるかどうか
     *
     * 今回はボタンとして使うため、reactiveをtrueにしています。
     */
    button = new St.Bin({ style_class: 'panel-button',
                          reactive: true,
                          can_focus: true,
                          x_fill: true,
                          y_fill: false,
                          track_hover: true });

    /*
     * アイコンを作成しています。
     *
     * "system-run-symbolic"はシステムに最初から入っているアイコン名です。
     * /usr/share/icons/Adwaita/scalable/actions/system-run-symbolic.svg
     */
    let icon = new St.Icon({ icon_name: 'system-run-symbolic',
                             style_class: 'system-status-icon' });

    /*
     * buttonの子要素として先ほど作成したアイコンを設定しています。
     */
    button.set_child(icon);

    /*
     * button-press-eventで応答するハンドラーとして
     * _showHello()を設定しています。
     *
     * サポートしているシグナルは以下のURLで確認できます。
     * https://developer.gnome.org/clutter/stable/ClutterActor.html#ClutterActor.signals
     */
    button.connect('button-press-event', _showHello);
}

init()以外に必須の関数が,拡張機能を有効化・無効化されたときに呼び出されるenable()disable()です。

function enable() {
    /* ボタンをトップパネルの右側に追加します。
     *
     * トップパネルは右端の_leftBox、_centerBox、_rightBoxの
     * 3つのコンテナーが存在します。
     *   _leftBox: 左端のアクティビティやカレントタスクを表示している領域
     *   _centerBOx: 中央の日時を表示している領域
     *   _rightBox: 右端のインジケーター領域
     * Looking Glassで「Main.panel._rightBox」を実行すれば
     * どれがどこかわかりやすいでしょう。
     *
     * 今回は_rightBoxにbuttonを追加しています。
     * 第二引数で追加順が最初か末尾かを変更できます。
     */
    Main.panel._rightBox.insert_child_at_index(button, 0);
}

function disable() {
    /* 無効化時にトップパネルからボタンを削除します。 */
    Main.panel._rightBox.remove_child(button);
}

ここまででボタンの作成,ボタンを押された時のイベントハンドラーの登録までを行えました。あとはイベントハンドラーを実装するだけです。

今回のハンドラーは,メインディスプレイの中央に一定期間メッセージダイアログを表示するというものです。

function _hideHello() {
    /*
     * Mainオブジェクトから、_showHello()が追加した
     * ラベルオブジェクトを削除します。
     */
    Main.uiGroup.remove_actor(text);
    text = null;
}

function _showHello() {
    /*
     * ディスプレイ中央に表示するラベルオブジェクトを作成します。
     *
     * style_classに指定してるhelloworld-labelクラスは、
     * stylesheet.cssで設定しています。
     * つまりこのCSSファイルを変更するだけで
     * ラベルの見た目を変更できるわけです。
     *
     * textに表示するテキストを設定し、Mainオブジェクトに追加しています。
     */
    if (!text) {
        text = new St.Label({ style_class: 'helloworld-label', text: "Hello, world!" });
        Main.uiGroup.add_actor(text);
    }

    /* ラベルオブジェクトを不透明に設定しています。 */
    text.opacity = 255;

    /* プライマリーモニターのインスタンスを探しています。 */
    let monitor = Main.layoutManager.primaryMonitor;

    /* ラベルの表示位置を、プライマリーモニターの中央に設定しています。 */
    text.set_position(monitor.x + Math.floor(monitor.width / 2 - text.width / 2),
                      monitor.y + Math.floor(monitor.height / 2 - text.height / 2));

    /*
     * Tweenerを用いてラベルオブジェクトのアニメーションを設定しています。
     *
     * 2秒(time)かけて不透明度(opacity)を0(透明)にしています。
     * opacityの変化の方法はeaseOutQuadを指定しています。
     * 参考: http://hosted.zeh.com.br/tweener/docs/en-us/misc/transitions.html
     * 変化が完了したら_hideHello()を呼び出し、
     * ラベルオブジェクトを削除します。
     */
    Tweener.addTween(text,
                     { opacity: 0,
                       time: 2,
                       transition: 'easeOutQuad',
                       onComplete: _hideHello });
}

これがサンプルコードのすべてです。

拡張機能のデバッグ方法

拡張機能の中でもlog()関数を使ってデバッグログを記録できます。これがどこに出力されるかと言うと,GNOME Shell本体のログ出力先です。Ubuntu 17.10の場合,journalctlコマンドを使えば特定のプロセスのログを取得できます。よって次のコマンドを実行するとGNOME Shell本体のログを表示できることでしょう。

$ journalctl /usr/bin/gnome-shell -f

-fオプションを付けることで,ログが追加されるたびに表示が更新されます。過去も遡って見たい場合-fを外してください。

標準設定だとページャーとしてlessを使うのですが,-Sオプションが付いているため長い行は端末の横幅サイズで見えないようになっています。表示する場合はカーソルキーで左右を移動しましょう。

単に右端で折り返したいなら,以下の方法が使えます。

単に出力をlessに渡す(カラーコードなどは設定されなくなる)
$ journalctl /usr/bin/gnome-shell --no-pager | less

lessのオプションを変更する(一部カラーコードが表示されない)
$ SYSTEMD_LESS="FRMXK" journalctl /usr/bin/gnome-shell

別のページャーを使う
$ SYSTEMD_PAGER=foo journalctl /usr/bin/gnome-shell

好みに合わせて選択すると良いでしょう。

Looking Glassを開いて,左上のスポイトをクリックすると,マウスを用いて個々のUIエレメントのオブジェクトを選択できます。あとは選択したオブジェクトをLooking Glass上で評価すれば,より深いデバッグが可能になることでしょう。

著者プロフィール

柴田充也(しばたみつや)

Ubuntu Japanese Team Member株式会社 創夢所属。数年前にLaunchpad上でStellariumの翻訳をしたことがきっかけで,Ubuntuの翻訳にも関わるようになりました。

コメント

コメントの記入