Perl/Tk provides several ways to pop up menus. Thus far, all menus have been posted automatically by pressing a menubutton, but we can do it ourselves by binding keystrokes or button clicks to callbacks.
The lowest-level mechanism is the Menu post method, which posts a menu at a specific screen coordinate. The Post method works like post, but additionally activates a specific menu item. In either case, we are responsible for determining where the menu is displayed.
Let's start with the code that produced Figure 12-5. It creates one menu, then displays it several ways. The toolbar across the top of the window contains a left-justified Menubutton and a right-justified labeled Optionmenu. The menu contains nine menu items, four separators, and five command menus at indexes 0, 2, 4, 6, and 8. Of course, the first way to post the menu is to press the File menubutton.
my $toolbar = $mw->Frame->pack(qw/-fill x -expand 1/); my $file = $toolbar->Menubutton( -text => 'File', -relief => 'raised', ); $file->pack(qw/-side left/);; my $menu = $file->Menu(-tearoff => 0, -menuitems => [ [qw/command ~New/], '', [qw/command ~Open/], '', [qw/command ~Save/], '', [qw/command ~Close/], '', [qw/command ~Quit/, -command => \&exit], ]); $file->configure(-menu => $menu); my $menu_index = 0; my $cursor = $toolbar->LabOptionmenu( -label => 'menu_index', -labelPack => [qw/-side left/], -variable => \$menu_index, -options => [(0, 2, 4, 6, 8)], ); $cursor->pack(qw/-side right/);
Here are two other ways to post the menu. Typing the character "p" invokes the post method and posts the menu so that its northwest corner is over the cursor. Typing the character "P" invokes the Post method and posts the menu so menu item $menu_index is centered over the cursor.
my $t = $mw->Text->pack; $t->insert('end', <<"EOT"); <p> invokes \$menu->post(cursor_x, cursor_y) <P> invokes \$menu->Post(cursor_x, cursor_y, menu_index) EOT $mw->bind('<p>' => [sub { my($w, $x, $y) = @_; $menu->post($x, $y); }, Ev('X'), Ev('Y')]); $mw->bind('<P>' => [sub { my($w, $x, $y) = @_; $menu->Post($x, $y, $menu_index); }, Ev('X'), Ev('Y')]);
You've probably had occasion to use a Dialog (or DialogBox) widget. These widgets are derived from a Toplevel and spend most of their time in a withdrawn state. It's also common to use Toplevels as containers for custom-built popup windows.[26] When it's time to display these dialogs, we call the special Perl/Tk window manager Popup method. Popup is essentially a wrapper around a call to Post, with three special purpose options that specify placement information in high-level terms rather than numerical coordinates. It's Popup's responsibility to take our human specifications and turn them into actual screen coordinates suitable for Post.
[26] If you want a dialog window without window manager decorations, create the Toplevel and then call overrideredirect(1).
What has this got to do with Menus? As Figure 12-6 indicates, the isa program from Chapter 14, "Creating Custom Widgets in Pure Perl/Tk" shows us that a Menu widget is a subclass of Tk::Wm, the window manager class. This means that Menus can invoke Popup too. Let's define some terms, then examine the three special options.
We can direct a popup menu (or, in general, any Toplevel) to appear in two general locations: either over another window—for example, the root window (screen) or a particular widget—or over the cursor. This is called the popover location. Once we've made this decision, we can further refine the exact placement of the popup relative to the popover location by specifying the intersection of two anchor points. The popanchor point is associated with the popup menu and the overanchor point is associated with the popover location (whether it be a window or the cursor). The point where the two anchor points coincide is the popup locus. Anchor points are string values and can be c (for center) or any of the eight cardinal compass points: n, ne, e, se, s, sw, w, or nw. See Figure 12-7.
This spatial information is embodied in the following three options (which are applicable for any widget derived from Tk::Wm, including Menus, Toplevels, and dialog widgets like Dialog and DialogBox):
This program, pop3, shows various ways to pop up a Dialog widget; the same principles apply to menus:
my(@popup_opts) = (-popover => undef, qw/-overanchor sw -popanchor sw/); my $d1 = $mw->Dialog( @popup_opts, -text => "Original options:\n" . join(' ', say(@popup_opts)) . "This Dialog should be in the screen's lower-left " . "corner. When you dismiss this Dialog another will " . "popup in the southeast corner.", ); $d1->Show; @popup_opts = qw/-overanchor se -popanchor se/; $d1->configure( @popup_opts, -text => "Changed options:\n" . join(' ', say(@popup_opts)) . "1 second after you dismiss this Dialog another " . "will popup, without window manager decorations ". "(overrideredirect on), with its southeast corner " . "over the cursor.", ); $d1->Show; $mw->after(1000); @popup_opts = qw/-popover cursor -popanchor se/; $d1->configure( @popup_opts, -text => "Changed options:\n" . join(' ', say(@popup_opts)) . "1 second after you dismiss this Dialog another " . "will popup, with window manager decorations once ". "again (overrideredirect off), with its northwest " . "corner over the cursor.", ); $d1->overrideredirect(1); $d1->Show; @popup_opts = qw/-popanchor nw/; $d1->configure( @popup_opts, -wraplength => '3i', -text => "Changed options:\n" . join(' ', say(@popup_opts)) . "End of demonstration.", ); $d1->overrideredirect(0); $mw->after(1000); $d1->Show; sub say { map {defined($_) ? $_ : 'undef'} (@_, "\n\n"); }
Copyright © 2002 O'Reilly & Associates. All rights reserved.