ruby-****@sourc*****
ruby-****@sourc*****
2012年 10月 31日 (水) 08:29:59 JST
------------------------- REMOTE_ADDR = 74.14.158.59 REMOTE_HOST = URL = http://ruby-gnome2.sourceforge.jp/hiki.cgi?tut-gtk2-mnstbs-mnui ------------------------- @@ -266,7 +266,7 @@ The first menu item on our context menu's sub-menu is also a sub-menu with the name 'sub_submenu1'. The next item this sub-sub menu is the separator (see: 9.1.1.1 ((<Menu Item Separators|tut-gtk2-mnstbs-popup#For Separators in menus use MenuItem with no label>))). Indeed, we need no callback here either, so just like for the the menu object stored in 'sub_submenu1' variable, the value for its hash entry is((*nil.*))As the comment in the third line above suggests, we will, next, need to make sure that the 'sub_submenu1' variable exists. This determines our next step #3, which will have to be located in the source code before this step (#2). - -3-{{br}} Because the in the variable 'sub_submenu1' is also stored a menu, i.e. the submenu with two final (leaf) menu items, called 't1-test1' and 't1-test2', which will both potentially need different callback procs, step 3 first has to build the appropriate hash array and use it as the argument in the call to our 'mk_submenu' helper method. Finally, after this sub-sub menu is created, we also need to give it the((*name,*))so when it is used in the 'mk_submenu' method as the menu object, we will be able to assign this name to the menu item, which wil represent the sub-menu. Let's see the code that needs to be written in this step #3: + -3-{{br}} Because the in the variable 'sub_submenu1' is also stored a menu, i.e. the submenu with two final (leaf) menu items, called 't1-test1' and 't1-test2', which will both potentially need different callback procs, step 3 first has to build the appropriate hash array and use it as the argument in the call to our 'mk_submenu' helper method. Finally, after this sub-sub menu is created, we also need to give it the((*name,*))so when it is used in the 'mk_submenu' method as the menu object, we will be able to assign this name to the menu item, which will represent the sub-menu. Let's see the code that needs to be written in this step #3: # (3. menu building step) submenu1_menuitems_n_calbacks_hash = { @@ -275,6 +275,129 @@ } sub_submenu1 = mk_submenu(submenu1_menuitems_n_calbacks_hash, true) sub_submenu1.name = "sub-sub menu1" + + We have now defined the entire menu hierarchy, for our two levels deep menu system. It you needed to create a deeper menu hierarchy, though, you would have to repeat the above steps #2 and #3 for all other sub-menu items in the top level menu, and then proceed recursively into each sub-menu on every level. This can become a daunting task if there are many sub-menus. + + Nevertheless, we have not completed our task yet. We still need to create the callbacks for all leaf menu items. Once you have created all the necessary hash arrays with all menu items, you proceed to step #3.a. to make a list of all leaf menu items and their corresponding callback procedure names: + + -3.a.-{{br}} Though it is not strictly necessary to make this purely 'bookkeeping' step, it is a good idea to gather, neatly organized in an easy to survey list all the menu items and their callbacks, to verify your design, and check for possible errors or overlooked spelling mistakes: + + Menu items: Callbacks: + ---------------- ------------------ + t1-test1 ............... t1_test1_lambda + t1-test2 ............... t1_test2_lambda + sub-sub1:item-1 ........ ss1_item_1_lambda + sub-sub1:item-2 ........ ss1_item_2_lambda + + Once we are sure our menu design is correct, we can proceed to step #4 - creating the callback procedures. Since these procedures will are needed in our previous steps, we are required to place them high enough in our program listing, to be visible at the time they are used. To be on the safe side we will place the code creating these procs just before our step #1 above. Following is the code segment presenting the entire step #4 in our program example: + + # (4. menu building step) + # These lambdas, or procs can indeed be different for ech menu item + t1_test1_lambda = lambda {|p, w| puts "item: #{p} selected; w=#{w.class}" } + t1_test2_lambda = lambda {|p, w| puts "item: #{p} selected; w=#{w.class}" } + ss1_item_1_lambda = lambda {|p, w| puts "item: #{p} selected; w=#{w.class}" } + ss1_item_2_lambda = lambda {|p, w| puts "item: #{p} selected; w=#{w.class}" } + + We conclude our menu design with the final step #5. The location of this code in our program listing should be at the end of the entire menu building process. In this code segment we assign all the sub-menus found in the top level menu. Again, in our case, the top menu has only two items, the first which is the leaf or final menu item called 'Test1', stored in variable 'mitem1', and the second menu item called 'Test2', stored in variable 'mitem2' and which actually represents the first sub-menu in the top level context menu. Following is the code from step #5: + + # (5. menu building step) + mitem2.submenu = mk_submenu(top_menus_1st_submenu_hash, true) + menu.show_all + +Everything else in the final program listing (menuitems-n-submenus-2.rb) you have already seen on numerous occasions, and you should not have absolutely no problems understanding it. Let's look at the completed program: + +{{br}} +((*menuitems-n-submenus-2.rb*)) + + #!/usr/bin/env ruby + require 'gtk2' + + def mk_submenu(menuitems_hash, tearoff) + submenu = Gtk::Menu.new + if tearoff + menuitem = Gtk::TearoffMenuItem.new + submenu.append(menuitem) + menuitem.show + end + menuitems_hash.each do |mitem, callback| + if mitem.is_a? String + menuitem = Gtk::RadioMenuItem.new(mitem) + submenu.append(menuitem) + menuitem.signal_connect('activate') { |w| callback[mitem, w] } + elsif mitem.is_a? Gtk::MenuItem # we have a separator + separator = Gtk::MenuItem.new + submenu.append(separator) + elsif mitem.is_a? Gtk::Menu + menuitem = Gtk::RadioMenuItem.new(mitem.name) + submenu.append(menuitem) + menuitem.submenu = mitem # mitem is a menu + else + puts "an unexpected submenu type: #{mitem.class}" + end + menuitem.show + end + submenu.show + return submenu + end + + (4. menu building step) + # These lambdas, or procs can indeed be different for ech menu item + t1_test1_lambda = lambda {|p, w| puts "item: #{p} selected; w=#{w.class}" } + t1_test2_lambda = lambda {|p, w| puts "item: #{p} selected; w=#{w.class}" } + ss1_item_1_lambda = lambda {|p, w| puts "item: #{p} selected; w=#{w.class}" } + ss1_item_2_lambda = lambda {|p, w| puts "item: #{p} selected; w=#{w.class}" } + + # (1. menu building step) + menu = Gtk::Menu.new + menutearoff = Gtk::TearoffMenuItem.new + menu.append(menutearoff) + menu.append(mitem1 = Gtk::MenuItem.new("Test1")) + menu.append(mitem2 = Gtk::MenuItem.new("Test2")) # 'Test2' is the name for the 1st level sub-menu + mitem1.signal_connect('activate') { |w| puts "Wigget=#{w.class} - Test1" } + + # (3. menu building step) + submenu1_menuitems_n_calbacks_hash = { + "sub-sub1:item-1" => ss1_item_1_lambda, + "sub-sub1:item-2" => ss1_item_2_lambda, + } + + sub_submenu1 = mk_submenu(submenu1_menuitems_n_calbacks_hash, true) + sub_submenu1.name = "sub-sub menu1" + + # (2. menu building step) + top_menus_1st_submenu_hash = { + sub_submenu1 => nil, # demands that 'sub_submenu1' is created by now => {{ step 3 }}. + Gtk::MenuItem.new => nil, + "t1-test1" => t1_test1_lambda, + "t1-test2" => t1_test2_lambda, + } + + # (5. menu building step) + mitem2.submenu = mk_submenu(top_menus_1st_submenu_hash, true) + menu.show_all + + window = Gtk::Window.new("Context Menu w/submenus (2)") + + # Make window sensitive to Right-mouse-click, to open the pop-up menu. + window.add_events(Gdk::Event::BUTTON_PRESS_MASK) + window.signal_connect("button_press_event") do |widget, event| + menu.popup(nil, nil, event.button, event.time) if (event.button == 3) + end + + # Make window sensitive to <Shift+F10> accelerator keys. These + # accelerator keys generate the 'popup-menu' signal for window, + # which opens the popup-menu. + window.signal_connect("popup_menu") do |w| + menu.popup(nil, nil, 0, Gdk::Event::CURRENT_TIME) + end + + window.set_default_size(320, 100).show_all + window.signal_connect('destroy') { Gtk.main_quit } + window.add(Gtk::Label.new("Hello World\n" + + "You may 'right-click' me\n\n" + + "or use <Shift+F10>")) + window.show_all + Gtk.main