読者です 読者をやめる 読者になる 読者になる

メニューバーを使用する

なんか久しぶりすぎるが気にしない。w
今回はメニューの追加について。


メニューの構造は「ファイル」→「新規作成」「開く」など、階層構造になっていることが多い。1つの階層の構造は以下のようなクラスを使った構造で表される。

  GtkMenuShell (メニューバー、サブメニュー)
       GtkMenuItem (メニュー上のアイテム)
       GtkMenuItem
       ....

GtkMenuItem というのは、メニューに表示されるアイテム一つ一つのことで、例えば「ファイル」「新規作成」「開く」など、メニュー上で選択可能なものはすべて GtkMenuItem。

(GtkMenuItem)

GtkMenuShell は、これら GtkMenuItem を入れるためのコンテナ。たとえば画面上部のメニューバーが GtkMenuShell であれば、その上に表示される「ファイル」「編集」などの文字が GtkMenuItem であって、GtkMenuShell は、これらの GtkMenuItem をまとめる役割を行う。

(GtkMenuShell)


また、GtkMenuItem には、サブメニューとして GtkMenuShell を紐付けることができる。これにより、メニューの階層構造を実現できる。

なお、GtkMenuShell というのは抽象クラスであり、実際には以下の2つが使用される。

  • GtkMenuBar : 常時表示されている横長のメニュー
  • GtkMenu : 必要なときにのみ表示されるポップアップメニュー

実際には、最上位のメニューに GtkMenuBar を使用し、それ以下の階層、例えば「ファイル」メニューをクリックしたときに表示されるサブメニューなどに GtkMenu を使用するとよい。


さて実際の例。今回は、メニューにいくつかのアイテムを追加し、それらが選択されたときに、ウィンドウ内の文字列を変化されるサンプルを作ってみた。

(ソースは以下の場所に上げてあります)
http://github.com/egawata/hatena_blog/tree/master/menu

    //  (1)MenuBar 'menubar' を作成する
    menubar = gtk_menu_bar_new();

    //  (1)MenuBar 'menubar' を BOX に追加する
    gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);    

まず一番上の階層であるメニューバーから。メニューバーの生成には gtk_menu_bar_new() を使用する。生成時点では、内容は空。

    //  (2)MenuItem 'lunch' を作成する
    lunch = gtk_menu_item_new_with_label("メニュー");
    …
    
    //  (2)MenuItem 'lunch' を MenuBar 'menubar' に追加する
    gtk_menu_shell_append(GTK_MENU_SHELL(menubar), lunch);

メニューバーに入れるアイテムを作成し、メニューバーに追加する。今回は1つだけ、「メニュー」というアイテムを追加する。

    //  (3)Menu 'lunchmenu' を作成する
    lunchmenu = gtk_menu_new();
    ...
    
    //  (3)Menu 'lunchmenu' を MenuItem 'lunch' のサブメニューとする
    gtk_menu_item_set_submenu(GTK_MENU_ITEM(lunch), lunchmenu);    

先ほど追加したアイテム「メニュー」に、サブメニュー lunchmenu を紐付ける。これにより、メニューバー上の「メニュー」を押すとサブメニューが現れるようになる。ただし現時点では内容は空。

    //  (4a)MenuItem 'menu*' を作成する
    menu1 = gtk_menu_item_new_with_label("カレー");
    menu2 = gtk_menu_item_new_with_label("おにぎり");
    menu3 = gtk_menu_item_new_with_label("俺の塩");

    //  (4b)MenuItem 'menu*' を Menu 'lunchmenu' に追加する
    gtk_menu_shell_append(GTK_MENU_SHELL(lunchmenu), menu1);
    gtk_menu_shell_append(GTK_MENU_SHELL(lunchmenu), menu2);
    gtk_menu_shell_append(GTK_MENU_SHELL(lunchmenu), menu3);

このサブメニューに入れるアイテムを3つ作成し、それをサブメニューに追加していく。

これでメニューが一通り表示されるようにはなったのだが、今の状態では、サブメニュー内の「カレー」などのアイテムを選択しても何も起こらない。メニューが選択されたときに何かをさせたい場合は、コールバック関数を登録する。

    //  (5)MenuItem 'menu*' が選択されたときに呼ばれるコールバック関数を
    //     menu_activated() に設定する。
    //     また、このときの引数として label も渡すようにする
    g_signal_connect(G_OBJECT(menu1), "activate",
                     G_CALLBACK(menu_activated), (gpointer)label);
    g_signal_connect(G_OBJECT(menu2), "activate",
                     G_CALLBACK(menu_activated), (gpointer)label);
    g_signal_connect(G_OBJECT(menu3), "activate",
                     G_CALLBACK(menu_activated), (gpointer)label);

各メニューが選択されたら、関数 menu_activated が呼ばれるようにする。menu_activated() は、ラベル label に書かれた文字列を変更している。

static void menu_activated(GtkMenuItem *menuitem, GtkWidget *label)
{
    const gchar *name = gtk_menu_item_get_label(menuitem);
    char message[100];
    snprintf(message, 100, "%sの注文を承りました。", name);

    gtk_label_set_text(GTK_LABEL(label), message);
}


(実行結果)

  • 初期状態


  • 「カレー」が選択された

実はサブメニューが表示されているときのキャプチャも取りたかったんだけど、サブメニューが表示されてる間はキャプチャできないのねorz