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__