(2017.10.27 追加記事;
gtk2,gtk3 でメニューを表示する、今回は B. 手作業で作っていく方法を述べます。
前回と同様、メニューバーを作っていきます。

→・手作業でのメニュー作成Ruby/GTK2 チュートリアル
→・Menu BarsRuby/GTK2 Tutorial
→・GTK Programming in Ruby (2) - Adding Menubar - Socrateos
→・GTK Programming in Ruby (3) - Adding Menubar, using Stock items - Socrateos
→・Menus & toolbars in Ruby GTK - Ruby GTK tutorial


B.手作業で作っていく方法
メニューバー関連のウィジェットは3つあります。
・メニューバー(Gtk::MenuBar
・メニュー(Gtk::Menu
・メニューアイテム(Gtk::MenuItem

このうち、メニューとメニューアイテムの区別と使い分けが混乱しやすくて、理解するのが大変でした。
簡単に言うと、メニューアイテムがいわゆる個々のメニュー項目そのもので、実際に表示されるのもメニューアイテムです。一方、メニューはGTK2,GTK3でいうコンテナ(Gtk::Fixed,Gtk::HBox,Gtk::VBox,Gtk::Box などの類)で、複数のウィジェット(ここではメニューアイテム)をまとめるウィジェットです。メニューアイテムが一つならコンテナであるメニューは必要ないのですが、メニューアイテムは大体複数あったり、サブメニューがあったりするので、メニューを使って1つにまとめる必要があります。ちなみにメニューバーもコンテナなので、複数の子ウィジェット(メニューやメニューアイテム)を登録できます。

1.メニューバー(Gtk::MenuBar
Gtk::MenuBar.new
メニューバーを作成します。
最終的にはメニューバーをコンテナ(Gtk::Fixed,Gtk::HBox,Gtk::VBox,Gtk::Box など)に登録して、そのコンテナをウィンドウに登録(Gtk::Window#add)することで表示させます


2.メニュー(Gtk::Menu
Gtk::Menu.new
メニューアイテムをまとめるコンテナを作成します

Gtk::MenuShell#append(child)
メニューアイテムを登録します
 ・child;登録する子メニューアイテム(Gtk::MenuItem)


3.メニューアイテム
 a)Gtk::MenuItem(一般的なメニューアイテム)
 b)Gtk::ImageMenuItem(アイコンイメージ付きのメニューアイテム)
 c)Gtk::CheckMenuItem(チェックを付ける/外すメニューアイテム)
 d)Gtk::RadioMenuItem(複数の項目から一つ選択するラジオメニューアイテム)

Gtk::MenuItem.new(label = nil, use_underline = true)
メニューアイテムを作成します
 ・label;表示させたいメニュー名
 ・use_underline:true;アンダーライン付きのメニュー名にする(実際は動作しない?)


Gtk::ImageMenuItem.new(label = nil, use_underline = true)
Gtk::ImageMenuItem.new(stock_id, accel_group = nil)
イメージ付きメニューアイテムを作成します
 ・stock_id;Gtk::Stockに登録されているアイコンイメージを指定します
      (GTK3では実際はイメージの無いこともあります)
  Gtk::Stock::FILE,Gtk::Stock::NEW,Gtk::Stock::OPEN,Gtk::Stock::QUITなど


Gtk::CheckMenuItem.new(label = nil, use_underline = true)
チェックメニューアイテムを作成します

Gtk::CheckMenuItem#set_active(active)
チェックを入れた状態にします


Gtk::RadioMenuItem.new(label = nil, use_underline = true) ;gtk3ではエラー
Gtk::RadioMenuItem.new(group, label = nil, use_underline = true)
ラジオメニューアイテムを作成します
 ・group;ラジオメニューアイテムで同じグループにするものを指定します
     (Gtk::RadioMenuItem または Gtk::RadioMenuItemの配列)
     ※GTK3 では group は省略できません(nil を入れる)

Gtk::RadioMenuItem#set_group(group)
ラジオメニューアイテムを既存のグループに登録します
 ・group;ラジオメニューアイテムで同じグループにするものを指定します
     (Gtk::RadioMenuItem または Gtk::RadioMenuItemの配列)


(メニューアイテム共通)
Gtk::MenuItem#set_label()
表示させたいメニュー名を設定します

Gtk::MenuItem#set_submenu(submenu)
メニューアイテムをサブメニュー(子メニュー)として登録します
自分が子メニューになります(※勘違いしやすいので注意!)
 ・submenu;登録する親メニューコンテナ(Gtk::Menu)


4.メニューアイテムの動作
GLib::Instantiatable#signal_connect(detailed_signal) { |widget, event| ... } 
メニューアイテムが選択された時の処理(アクション)を設定します
 ・detailed_signal:文字列で 'activate' と指定
 ・{ |widget, event| 処理 }
  widget;シグナルを発行したウィジット(ここではメニューアイテム)
  event;Gdk::EventExpose が入ります(使い道がない?) 
  処理;処理を直接書くか、Proc を入れます



5.キーボードショートカット(Gtk::AccelGroup
Gtk::AccelGroup.new
キーボードショートカットを使うための AccelGroup を作成します

(メニューアイテム共通)
Gtk::Widget#add_accelerator(accel_signal, accel_group, accel_key, accel_mods, accel_flags)
 ・accel_signal;文字列で 'activate' と指定
 ・accel_group;登録する Gtk::AccelGroup を指定
 ・accel_key;キーを指定(Gdk::Keyval
  (gtk2:Gdk::Keyval::GDK_Q,gtk3:Gdk::Keyval::KEY_Q など) 
 ・accel_mods: 同時押しする修飾キー(Gdk::Window::GdkModifierType
   (gtk2:CONTROL_MASK,gtk2/gtk3::control_mask など)
 ・accel_flags: flag ?(Gtk::GtkAccelFlags
  (gtk2:Gtk::ACCEL_VISIBLE,gtk2/gtk3::visible など)


(license; public domain)
・gtk_menu_4_gtk2.rb
require 'gtk2'

window = Gtk::Window.new
window.set_size_request(400, 300)


# "ファイル"メニューを作成
file_item = Gtk::MenuItem.new("ファイル")
# "ファイル"メニューはサブメニューを持つので、メニューコンテナを作成
file_menu_container = Gtk::Menu.new
file_item.set_submenu(file_menu_container)

# 開く"、"終了"メニューを作成(アイコンを使うImageMenuItemで作成)
open_item = Gtk::ImageMenuItem.new(Gtk::Stock::OPEN)
open_item.set_label("開く(O)")
quit_item = Gtk::ImageMenuItem.new(Gtk::Stock::QUIT)
quit_item.set_label("終了(Q)")

# "開く"、"終了"メニューを"ファイル"メニューコンテナに登録(サブメニューにする)
file_menu_container.append(open_item)
file_menu_container.append(quit_item)


# "オプション"メニューを作成
option_item = Gtk::MenuItem.new("オプション")
# "オプション"メニューはサブメニューを持つので、メニューコンテナを作成
option_menu_container = Gtk::Menu.new
option_item.set_submenu(option_menu_container)

# "イタリック"メニューを作成(トグルメニューで作成)
italic_item = Gtk::CheckMenuItem.new("イタリック(I)")
italic_item.set_active(false)  # "イタリック"の初期状態はfalse
# "イタリック"メニューを"オプション"メニューコンテナに登録(サブメニューにする)
option_menu_container.append(italic_item)


# "カラー"メニューを作成
color_item = Gtk::MenuItem.new("カラー")
# "カラー"メニューはサブメニューを持つので、メニューコンテナを作成
color_menu_container = Gtk::Menu.new
color_item.set_submenu(color_menu_container)
# "カラー"メニューを"オプション"メニューコンテナに登録(サブメニューにする)
option_menu_container.append(color_item)


# ラジオメニューで"レッド","グリーン","ブルー"メニューを作成、同じグループにする
# Gtk::RadioMenuItem.new(グループ, ラベル)
red_item = Gtk::RadioMenuItem.new("レッド")   # gtk3では(nil, "レッド") とnilが必要
green_item = Gtk::RadioMenuItem.new(red_item, "グリーン")
blue_item = Gtk::RadioMenuItem.new(red_item, "ブルー")
green_item.set_active(true)  # "カラー"のラジオメニューの初期選択は"グリーン"

# "レッド","グリーン","ブルー"メニューを"カラー"メニューコンテナに登録(サブメニューにする)
color_menu_container.append(red_item)
color_menu_container.append(green_item)
color_menu_container.append(blue_item)


# メニューバーを作成
menubar = Gtk::MenuBar.new
menubar.set_size_request(400, -1)
# メニューバーに"ファイル","オプション"メニューを登録
menubar.append(file_item)
menubar.append(option_item)



# キーボードショートカットを作成
shortcuts = Gtk::AccelGroup.new

# 各メニューのキーボードショートカットを作成、メニュー選択時の処理(アクション)を登録
open_item.add_accelerator('activate', shortcuts, Gdk::Keyval::GDK_O, :control_mask, :visible)
open_item.signal_connect('activate') do |widget, event|
  puts "'#{widget.label}' is called."
end

quit_item.add_accelerator('activate', shortcuts, Gdk::Keyval::GDK_Q, :control_mask, :visible)
quit_item.signal_connect('activate') { Gtk.main_quit }

italic_item.add_accelerator('activate', shortcuts, Gdk::Keyval::GDK_I, :control_mask, :visible)
italic_item.signal_connect('activate') do |widget, event|
  puts "Italic: #{widget.active?}"
end

red_item.add_accelerator('activate', shortcuts, Gdk::Keyval::GDK_R, :control_mask, :visible)
red_item.signal_connect('activate') do |widget, event|
  puts "Red: #{widget.active?}"
end

green_item.add_accelerator('activate', shortcuts, Gdk::Keyval::GDK_G, :control_mask, :visible)
green_item.signal_connect('activate') do |widget, event|
  puts "Green: #{widget.active?}"
end

blue_item.add_accelerator('activate', shortcuts, Gdk::Keyval::GDK_B, :control_mask, :visible)
blue_item.signal_connect('activate') do |widget, event|
  puts "Blue: #{widget.active?}"
end

# キーボードショートカットを登録
window.add_accel_group(shortcuts)



text_area = Gtk::TextView.new
text_area.set_size_request(150, 200)
text_area.buffer.text = 'Hello World!'

fixed = Gtk::Fixed.new
fixed.put(menubar, 0, 0)
fixed.put(text_area, 20, 60)
window.add(fixed)

window.show_all
window.signal_connect("destroy") { Gtk.main_quit }
Gtk.main

gtk_menu_4_gtk2

gtk_menu_4_gtk2_tem


・gtk_menu_4_gtk3.rb
require 'gtk3'

window = Gtk::Window.new
window.set_size_request(400, 300)


# "ファイル"メニューを作成
file_item = Gtk::MenuItem.new(:label => "ファイル")
# "ファイル"メニューはサブメニューを持つので、メニューコンテナを作成
file_menu_container = Gtk::Menu.new
file_item.set_submenu(file_menu_container)

# 開く"、"終了"メニューを作成(アイコンを使うImageMenuItemで作成)
open_item = Gtk::ImageMenuItem.new(:label => "開く(O)", :stock_id => Gtk::Stock::OPEN)
quit_item = Gtk::ImageMenuItem.new(:label => "終了(Q)", :stock_id => Gtk::Stock::QUIT)

# "開く"、"終了"メニューを"ファイル"メニューコンテナに登録(サブメニューにする)
file_menu_container.append(open_item)
file_menu_container.append(quit_item)


# "オプション"メニューを作成
option_item = Gtk::MenuItem.new(:label => "オプション")
# "オプション"メニューはサブメニューを持つので、メニューコンテナを作成
option_menu_container = Gtk::Menu.new
option_item.set_submenu(option_menu_container)

# "イタリック"メニューを作成(トグルメニューで作成)
italic_item = Gtk::CheckMenuItem.new(:label => "イタリック(I)")
italic_item.set_active(false)  # "イタリック"の初期状態はfalse
# "イタリック"メニューを"オプション"メニューコンテナに登録(サブメニューにする)
option_menu_container.append(italic_item)


# "カラー"メニューを作成
color_item = Gtk::MenuItem.new(:label => "カラー")   #gtk3
# "カラー"メニューはサブメニューを持つので、メニューコンテナを作成
color_menu_container = Gtk::Menu.new
color_item.set_submenu(color_menu_container)
# "カラー"メニューを"オプション"メニューコンテナに登録(サブメニューにする)
option_menu_container.append(color_item)


# ラジオメニューで"レッド","グリーン","ブルー"メニューを作成、同じグループにする
# Gtk::RadioMenuItem.new(グループ, ラベル)
red_item = Gtk::RadioMenuItem.new(nil, "レッド")    # gtk2では nilは省略可能
green_item = Gtk::RadioMenuItem.new(red_item, "グリーン")
blue_item = Gtk::RadioMenuItem.new(red_item, "ブルー")
green_item.set_active(true)  # "カラー"のラジオメニューの初期選択は"グリーン"

# "レッド","グリーン","ブルー"メニューを"カラー"メニューコンテナに登録(サブメニューにする)
color_menu_container.append(red_item)
color_menu_container.append(green_item)
color_menu_container.append(blue_item)


# メニューバーを作成
menubar = Gtk::MenuBar.new
menubar.set_size_request(400, -1)
# メニューバーに"ファイル","オプション"メニューを登録
menubar.append(file_item)
menubar.append(option_item)



# キーボードショートカットを作成
shortcuts = Gtk::AccelGroup.new

# 各メニューのキーボードショートカットを作成、メニュー選択時の処理(アクション)を登録
open_item.add_accelerator('activate', shortcuts, Gdk::Keyval::KEY_O, :control_mask, :visible)
open_item.signal_connect('activate') do |widget, event|
  puts "'#{widget.label}' is called."
end

quit_item.add_accelerator('activate', shortcuts, Gdk::Keyval::KEY_Q, :control_mask, :visible)
quit_item.signal_connect('activate') { Gtk.main_quit }

italic_item.add_accelerator('activate', shortcuts, Gdk::Keyval::KEY_I, :control_mask, :visible)
italic_item.signal_connect('activate') do |widget, event|
  puts "Italic: #{widget.active?}"
end

red_item.add_accelerator('activate', shortcuts, Gdk::Keyval::KEY_R, :control_mask, :visible)
red_item.signal_connect('activate') do |widget, event|
  puts "Red: #{widget.active?}"
end

green_item.add_accelerator('activate', shortcuts, Gdk::Keyval::KEY_G, :control_mask, :visible)
green_item.signal_connect('activate') do |widget, event|
  puts "Green: #{widget.active?}"
end

blue_item.add_accelerator('activate', shortcuts, Gdk::Keyval::KEY_B, :control_mask, :visible)
blue_item.signal_connect('activate') do |widget, event|
  puts "Blue: #{widget.active?}"
end

# キーボードショートカットを登録
window.add_accel_group(shortcuts)



text_area = Gtk::TextView.new
text_area.set_size_request(150, 200)
text_area.buffer.text = 'Hello World!'

fixed = Gtk::Fixed.new
fixed.put(menubar, 0, 0)
fixed.put(text_area, 20, 60)
window.add(fixed)

window.show_all
window.signal_connect("destroy") { Gtk.main_quit }
Gtk.main

gtk_menu_4_gtk3


gtk_menu_4_gtk3_tem