Glenn Linderman wrote:
> So I have a menu that has dynamic entries, that can grow quite long.
> There seem to be no particular limits on how many entries a menu can
> have, but when it exceeds the size of the screen, you get these
> annoying up and down arrows at the top and bottom.  They can be used
> to scroll the contents of the menu.  That is not as annoying as not
> being able to access the menu entries at all, of course, but the
>scrolling behavior is relatively slow.

I've not seen it before, and here on my win98 box it looks horrible, as well as being slow.

> I'm aware (I've used this feature for fixed size menus) that I can
> add columns to a menu explicitly by doing stuff like:
>
> $m_config->{'sUpdate'}->Change( -menubarbreak => 1 );

Right.

> but I was wondering if there is a way to get Windows to figure out
> how many entries will fit vertically, and add the breaks
>automatically?

Not that I'm aware of.

> If not, then I am wondering how to do the size calculations... I can
> get the screen size, I can get the position of the mouse when the menu
> is invoked, but I'm not sure how to get the vertical or horizontal
> size of a menu item... Windows seems to calculate those automatically
> when displaying them, so I'm sure there is a way, somewhere... but I
> haven't found it.  I haven't even found documentation for the
> existence of the scrolled menus...

I was expecting there to be a GetSystemMetrics() call to get the vertical size of a menu, but it appears there is not. I found a very likely looking API call - GetMenuItemRect (see code at end) - but having played with it I can't make it give me the correct dimensions until after the menu has been displayed at least once, and you really want the dimensions *before* the menu is displayed :-( It also sees to give the location (in window co-ordinates), as if the menu was to be drawn in it's 'normal' position, although this wouldn't be a problem for you, as you only want the height, not the absolute position.

Note, also, that although I haven't checked I'm pretty sure that separators have a smaller height than regular menu items, so you would need to take this into account.

> The Windows Start / Programs menu is an example of where Windows
> uses one type or the other (scrolled, or multi-column).

From what I've read the start menu isn't a real menu at all, and when it's in it's scrolling mode it's actually a custom drawn pager control.

> Here's a related question that might not be related at all... is
> there any way to use a real vertical scrollbar, instead of the up/down
> arrows, when displaying a large menu list?  Some way to constrain the
> overall height, but add a scrollbar to scroll the items, kind of like
> the way a list box does it?

I'm pretty sure there's not.

> [OK, one solution would be to dynamically choose whether to use a menu
> or a list box based on the size of the list, I suppose... but that
> would require quite different code for the two cases, especially if
> one has to deal with submenus....[I don't have a reference where
> Windows uses a scrollbar for menus, which makes me doubt the existence
> of this type of solution.]]

You could always uses a Listbox - You'd only need one set of code.

As an alternative, you could look at the code in my CoolBar module (http://www.robmay.me.uk/win32gui), simulate your menu bar using a toolbar, and have a real listbox that you position and size as a result of a click on one of the buttons.

In general the UI experience of having to pick one item from a very long, flat, list needs re-thinking - but without knowing more about what you are trying to get the use to do, it's hard to know if the general rules apply here.

Regards,
Rob.

#!perl -w
use strict;
use warnings;

use Win32::GUI qw(CW_USEDEFAULT WM_ENTERMENULOOP);
use Win32::API();
Win32::API->Import('user32', 'GetMenuItemRect', 'LLLP','L') or die;

my @array = map { +">Item$_" => "Item$_" } (1 .. 60);

my $menu = Win32::GUI::Menu->new(
    "File" => "File",
    @array,
);

my $mw = Win32::GUI::Window->new(
    -left => CW_USEDEFAULT,
    -size => [400,300],
    -menu => $menu,
);
$mw->Hook(WM_ENTERMENULOOP, \&show);

$mw->AddButton(
    -text => 'Show Size',
    -pos  => [10,10],
    -onClick => \&show,
);

$mw->Show();
Win32::GUI::Dialog();
$mw->Hide();
exit(0);

sub show {

    my $rect = pack("LLLL", 0,0,0,0);

    my $result = GetMenuItemRect(
        $mw->{-handle},
        $menu->{File}->{-handle},
        11,
        $rect,
    );
    if (! $result) {
        print "Result: $result ($^E)\n";
    }
    else {
        print "Result: $result\n";

        my ($l, $t, $r, $b) = unpack("LLLL", $rect);
        print "$l, $t, $r, $b\n";
    }

    return 1;
}
__END__

Reply via email to