なんとなく分かるgtkrcの書き方


こんにちは、稲垣@Cerevoです。
今回はアセンブリ言語から離れてGTK+に関することを書いてみたいと思います。

gtrkrcの書き方

gtkrcをちょっと書いてみようとして、ググったけどまともな解説がなくて挫折したという方、
けっこういるのではないでしょうか?
実は、書くときに必要な知識はGTK+のドキュメントに書いてあります。
http://library.gnome.org/devel/gtk/stable/gtk-Resource-Files.html

しかし他のドキュメントを参照しないと分からない部分もありますし、
そういう部分に限ってドキュメントのポインタが示されていなかったりするのも事実です。

そこで、gtkrcを書こうとしたけどよく分からなかったという人や
自分のプログラムでどう利用したらいいか分からないという人向けに、
gtkrcの書き方・使い方をまとめてみたいと思います。

スタイル

GTK+では、色やフォントの設定はスタイル (GtkStyle) としてまとめられています。
gtkrcでは次のような記述でスタイルを定義します。

style "hoge" {
   fg  [NORMAL] = "#fff"
   font_name = "M+1P+IPAG 12"
   xthickness = 2
   GtkWidget::focus-line-width = 3
}

fg bg text base bg_aaの部分の5種類の部分について、
NORMAL ACTIVE PRELIGHT SELECTED INSENSITIVEの各状態の色を指定します:

fg[NORMAL] = "#fff"

普段はNORMALの色、カーソルが当たったときはPRELIGHTの色などと使い分けることが想定されているようですが、
実際のところ、これらの色を実際にどこに配置するかはウィジェット次第です。
例えばGtkNotebookはちょっとそれ違わない?って感じの配色をしますね。
極端な話、GtkDrawingやGtkLayoutを使う場合は、5×5=25色のパレットだと思ってしまってもいいかもしれません。
お行儀よくはありませんが。

フォント

font_name = "M+1P+IPAG 12"

などと指定します。fontconfigが面倒を見てくれます。

厚み

xthicknessとythicknessはボタンの厚みやボックスのマージンなんかに使われる、
と思います。ほとんどいじったことはありません。

各クラス固有のスタイルプロパティ

普通のプロパティとは別にスタイルプロパティというものが各クラスに定義されており、
gtkrcで設定することができます。

たとえばGtkWidgetにはfocus-line-widthというスタイルプロパティがあり、

GtkWidget::focus-line-width = 3 

などと指定すると、
フォーカスの当たっているウィジェットに破線で描かれる枠が太くなります。

プログラムからの利用

ウィジェットはスタイルを持っており、公開されていますから、普通にポインタでアクセスできます。
例えばNORMAL状態の前景色のGdkGCは

widget->style->fg_gc[GTK_STATE_NORMAL]

としてアクセスできます。
プログラムの中でGdkGCやPangoを操作するのは面倒ですし 、
デザイン変更のたびにリコンパイルするのも大変ですから、
積極的に利用したいものです。

バインディング

バインディングはキーにアクションシグナルを結びつけます:

binding "fuga" {
  bind "Return" { "move-current" (next) }
  bind "Left" { "cancel" () }
}

解説を探してもEmacsキーバインディングにする方法しか書いてないことがほとんどで、
たとえば「どんな名前のキーがあるのか」ということはよく分からないという
ちょっと厄介な部分です。

バインディングも、スタイルと同様、一式を定義してからウィジエットに結びつけるという形をとります。

キーの名前

使うキーの名前は gdk/gdkkeysyms.h に書かれています。
たとえばGDK_Rightと書かれていたら、gtkrcには”Right”と書きます。
どのキーなのかは、おおむね見れば分かります。それで深く考えたことはありませんが、
どうしても気になるときは、GdkEventKeyをダンプするプログラムでも書いたら分かるんじゃないんでしょうか。

アクションシグナル

アクションシグナルの定義は、スタイルプロパティ同様、各クラス (と親クラス) のドキュメントに書かれています。
引数は、基本的に文字列か数か (上記の”move-current”に対するnextのような) シンボルです。
シンボルに関しては、アクションシグナルの定義をみると型が宣言されているので、それを見ればなんとなく分かります。

たとえばGtkMenuShellの”move-current”シグナルは引数にGtkMenuDirectionTypeを取るのですが、
その型は次のように宣言されています:

typedef enum
{
  GTK_MENU_DIR_PARENT,
  GTK_MENU_DIR_CHILD,
  GTK_MENU_DIR_NEXT,
  GTK_MENU_DIR_PREV
} GtkMenuDirectionType;

この場合、型がGtkMenuDirectionTypeなので、シンボルのGTK_MENU_DIRの部分は型名だと思って切り捨てて、
gtkrcには next と書きます。

例えば「キー”Right”にアクションシグナル”activate”をバインド、引数なし」という設定は
次のように書けます:
bind "Right" { "activate" () }
連続して複数のアクションシグナルを送ることもできますから (;で区切る必要はありません)、
ある程度複雑な動作をキーに割り当てることができると思います。

パスによる指定

定義したスタイルはウィジェットに”装備”させないと意味がありません。
ウィジェットは、ディレクトリと同様に階層構造を成しているので、
パス (path) とよく似たウィジェットパスによって特定することができます
(ウィジェットにはクラスがあるのでCSSのセレクタの方が近い)。
パスの区切り文字は “.” です。
いわゆるshell glob構文が使えると書いてあるのですが、[abc]みたいのは使えないようなので、
ワイルドカードに使えるのは?と*のようです。

GTK+2.10以降では、クラスを指定する部分に<someclass>のように書くと派生クラスにもマッチします。
例えば “<GtkBox>” は GtkHBoxにもGtkVBoxにもマッチします。

widget

例えば

widget "mainwindow.GtkHBox.okbutton" style "hoge"

のように、ウィジェットの名前でパスを指定します。
ウィジェットの名前はgtk_widget_set_nameで設定され、デフォルトではクラス名です。

widget_class

例えば

widget "GtkWindow.GtkHBox.GtkButton" style "hoge"

のように、パスをウィジェットのクラス名によって指定します。

class

例えば

class "GtkMenuShell" binding "fuga"

のように、階層構造を無視してウィジェット単体のクラス名によって指定します。

実際のところ、

widget_class "*GtkMenuShell" binding "fuga"

と書くのとほとんど変わらないと思うのですが……

まとめ

gtkrcを書くときに必要な知識を、分かりにくそうなことを中心になんとなくまとめてみました。
gtkrcを活用すればデザインをプログラムから分離することができ、開発が効率的になるでしょう。
Happy hacking!