Book HomeMastering Perl/TkSearch this book

12.5. Popup Menus

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.

12.5.1. The post and Post Methods

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.

Figure 12-5

Figure 12-5. Post posts a menu and activates a menu item (here, index 4)

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')]);

12.5.2. The Popup Method

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.

Figure 12-6

Figure 12-6. Popup is a method of Tk::Wm

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.

Figure 12-7

Figure 12-7. Two anchor positions define the popup locus

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):

-popover
Specifies the popover location. It may be the string cursor, a widget reference, or undef to specify the root window (screen).

-overanchor
Specifies where the popup should anchor relative to the popover location. For instance, if east is specified, the popup appears over the right side of the popup location; if it's north, the popup is positioned above the popover location; and if it's northeast, the popup is positioned at the upper-right corner of the popover location.

-popanchor
Specifies the anchor point of the popup. If east, the right side of the popup is the anchor; if north, the top of the popup is the anchor; and if southwest, the lower-left corner of the popup is the anchor.

12.5.2.1. Popup examples

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");
}


Library Navigation Links

Copyright © 2002 O'Reilly & Associates. All rights reserved.