-
scrolling popup menu via Page Up/Down
In the case where a popup menu is too long to fit on the screen, I would like it to scroll in response to the Page Up, Page Down, Home, and End keys. Is there any way to accomplish this? It would also be nice if the menu scrolled in response to the mouse wheel. It seems that by default the only way to scroll a popup menu is via the arrow buttons at the top and bottom of the menu, or via the up and down keys. This is counter-intuitive, particularly on small screens.
I understand that long popup menus are suboptimal, and that the stock behavior may be Microsoft's subtle way of discouraging me from using such menus, but I want it to work better regardless. I would be willing to use a hook function if necessary, however even if I manage to intercept the relevant keys it's not obvious how I can tell the menu to scroll, since it isn't a window.
I have observed this behavior on Windows 7 using Common Controls 6, in both Aero and Classic themes. Is the behavior likely to differ in Windows 8, and/or in later versions of Common Controls?
-
Re: scrolling popup menu via Page Up/Down
"the case where a popup menu is too long to fit on the screen" is just a very bad design!
How could your user choose an item in the list of more than 30-40 ones?confused:
Please, don't struggle against the scrolling. Just change your design.
Perhaps, you could split it into two or more single popup menus? Or implement some additional popups within a main menu?
-
Re: scrolling popup menu via Page Up/Down
Pop up menu behavior is determined by OS. You cannot override it. The only option here is to implement your own one. Which looks like a really bad idea. Say your users work with Windows menus all the time. Now, how could they figure out that your menu supports page keys unlike to standard menus?
I agree with Victor, nested menu hierarchy looks much more attractive.
-
Re: scrolling popup menu via Page Up/Down
Thank you for your response. OK I did say I wanted it to work even though Microsoft and others may think it's a bad idea. If the shortcut keys worked as expected, it wouldn't be so difficult to make a choice in a long menu, especially if the list is properly sorted. Long drop lists are common, but they usually aren't problematic because drop lists support Page Up/Down, Home/End, and the wheel. Anyway for sure I'm not the first to discover this obvious shortcoming of Windows. In Firefox for example, Home/End does work in menus (though not Page Up/Down), presumably because whichever toolkit they use rolled their own menus. Also note that both list boxes and menus support mnemonic selection, i.e. in either case if I press a letter key and there's a menu item or list item that starts with that letter it's automatically scrolled into view. So in fact the two UI elements handle similar problems already. If popup menus supported the standard navigation keys they would become more useful (to me, and no doubt to others) without harming existing users in any way.
Regarding the suggestion to use multiple popups, I have considered this of course! Even assuming the items can logically be broken into groups, there is the problem that I'm expecting the popup menu to show which item is selected, e.g. via a radio button. In a single short menu this isn't a problem. But in a long menu it's suboptimal, again because of stupid behavior: unlike with drop lists, the selected item is not scrolled into view automatically. But with multiple lists, how do I show which list the selected item is in? Suppose I had a submenu for each possible starting letter, A-Z. And suppose the selected item is "Foo" so it's in the F submenu. Does Windows permit me to place a radio button on the F submenu?
FWIW The domain in this case happens to be chord types, e.g. maj7, -7, mM7#5, etc.
-
Re: scrolling popup menu via Page Up/Down
Quote:
Originally Posted by
ckorda
Thank you for your response. OK I did say I wanted it to work even though Microsoft and others may think it's a bad idea. If the shortcut keys worked as expected, it wouldn't be so difficult to make a choice in a long menu, especially if the list is properly sorted. ...
Again it is a very bad design!
Look at the VS IDE: there is a menu Window where a list of currently opened files is displayed.
Although there may be opened some hundred files, only about ten of them you will see in the menu list. To see the full list of files you need to click corresponding menu item (or a toolbar button) and then a dialog with the list of all opened files is displayed. The list has a scrollbar so you will be able very easy (using arrows, mouse, Page Up, Page Down, Home, and End keys) select what you need.
-
Re: scrolling popup menu via Page Up/Down
Quote:
Originally Posted by
ckorda
In the case where a popup menu is too long to fit on the screen, I would like it to scroll in response to the Page Up, Page Down, Home, and End keys. Is there any way to accomplish this? It would also be nice if the menu scrolled in response to the mouse wheel. It seems that by default the only way to scroll a popup menu is via the arrow buttons at the top and bottom of the menu, or via the up and down keys. This is counter-intuitive, particularly on small screens.
I understand that long popup menus are suboptimal, and that the stock behavior may be Microsoft's subtle way of discouraging me from using such menus, but I want it to work better regardless. I would be willing to use a hook function if necessary, however even if I manage to intercept the relevant keys it's not obvious how I can tell the menu to scroll, since it isn't a window.
I have observed this behavior on Windows 7 using Common Controls 6, in both Aero and Classic themes. Is the behavior likely to differ in Windows 8, and/or in later versions of Common Controls?
If you really want to do this (think again as others have indicated), one way would be to subclass the popup menu. WM_INITMENUPOPUP (http://msdn.microsoft.com/en-us/libr...v=vs.85).aspx) is received when the menu is about to become active so you can subclass the menu in the code to handle this message. You can then program whatever behaviour is required for the menu.
-
Re: scrolling popup menu via Page Up/Down
Thanks but I didn't ask whether it was a good idea, I asked how to do it. The substantive problem is this: Obfuscation notwithstanding, a popup menu is a window. It is relatively straightforward to obtain a popup menu's HWND, e.g. via the subclassing method described in this Transparent Menu article. Using this HWND, I am able to move the popup menu anywhere via ::MoveWindow. This at least proves that I have the right HWND and that it accepts messages. But now do I scroll the popup menu? I have tried using ::ScrollWindow, but it merely causes the window to repaint itself. I speculate that this occurs because the menu maintains its scroll state in some non-standard way, instead of using the Windows scrolling API. There may be a way around it, such as an internal message. Unfortunately it is difficult to study the internal behavior of popup menus because they are so ephemeral. I had hoped that someone here could point me in the direction of a source that might provide such information. But maybe not. Must I wait until MS loses another antitrust suit for the details of popup menu scrolling to finally become public?
-
Re: scrolling popup menu via Page Up/Down
The article referenced doesn't describe subclassing menus. It describes super-classing a menu class (not what is required) and other things. You need to subclass the popupmenu in WM_INITMENUPOPUP and then in your code for the new menu wndproc function you can handle the messages as needed to create scroll bars and respond to their messages just as you would if you were adding scroll bars to your own window.
-
Re: scrolling popup menu via Page Up/Down
Quote:
Originally Posted by
ckorda
Regarding the suggestion to use multiple popups, I have considered this of course! Even assuming the items can logically be broken into groups, there is the problem that I'm expecting the popup menu to show which item is selected, e.g. via a radio button. In a single short menu this isn't a problem. But in a long menu it's suboptimal, again because of stupid behavior: unlike with drop lists, the selected item is not scrolled into view automatically. But with multiple lists, how do I show which list the selected item is in? Suppose I had a submenu for each possible starting letter, A-Z. And suppose the selected item is "Foo" so it's in the F submenu. Does Windows permit me to place a radio button on the F submenu?
Yes, you could do that. but it sounds to me like a list box is better suited for your user interface. This would also allow the user to select an item by typing multiple characters, which is much more powerful than mnemonics.
-
Re: scrolling popup menu via Page Up/Down
Typically speaking, and this holds true for ALL the windows common controls.
Most of them can be tweaked (to a degree) to change how they look.
It is either impossible, or very difficult to change the BEHAVIOUR of a common control to make it do something it wasn't designed for.
And even in the case where you can change behaviour, it often will have edge cases where things don't work, or it may totally break down on another version of Windows. It can be particularly problematic if you have external tools that assume a menu works in a certain way (such as the screen narrator, the disabilities features, ...) changing bahaviour could make your software unusable for people needing those.
So as already pointed out, if you don't like HOW it works, use something different, or create your own.
A menu isn't an ideal UI for what you are trying to do, so I would imaging that you'd want to look for an alternative method even if you coud get it to work.
That said, Home/End/PgUp/PgDn seems like a doable feature since you're not trying to "change behaviour" rather, you're trying to do something with keys which are not currently being used. Subclass the menu, handle the keys yourself.
Note that this could totally mess up if in the next version of Windows MS decided to do something with those keys.
-
Re: scrolling popup menu via Page Up/Down
Quote:
Originally Posted by
ckorda
Does Windows permit me to place a radio button on the F submenu?
Yes, I believe it does. But even if you had nested menus with the selected item indicated by a radio button, is that a good user experience? You'll probably need some other indicator to show when an item is selected (other than a user working their way through the menus).
-
Re: scrolling popup menu via Page Up/Down
Regarding subclassing menus, it's not clear what this would mean since CMenu isn't derived from CWnd and therefore doesn't even have a message map. I enclose some test code from a dialog app below. I hook menu creation by installing an alternate window procedure for all menus. This is admittedly crude, but it provides me with the menu's window handle, even for nested popups. There are two distinct problems:
1) I can catch WM_KEYDOWN messages, but I find that no messages are generated for the page up/down or home/end keys. Normally I would get around this via PreTranslateMessage, but I don't know what the equivalent method would be in a window procedure.
2) I have not found any way to tell the menu to scroll itself. I can move the menu anywhere with ::MoveWindow, but ::ScrollWindow has no effect.
Code:
WNDPROC wpOldMenuProc;
LRESULT CALLBACK MenuWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg) {
case WM_CREATE:
printf("!");
break;
case WM_KEYDOWN:
printf("k=%x ", wParam);
// ::MoveWindow(hWnd, 0, 0, 100, 100, 1); // works fine but not helpful
{
CRect r;
::GetClientRect(hWnd, r);
::ScrollWindow(hWnd, 100, 0, 0, r); // doesn't work, -100 doesn't work either
}
return 0;
}
printf("%x ", uMsg);
return CallWindowProc(wpOldMenuProc, hWnd, uMsg, wParam, lParam);
}
In OnInitDialog, install menu window proc:
// TODO: Add extra initialization here
WNDCLASS wc;
GetClassInfo(NULL, _T("#32768"), &wc);
wc.hInstance = AfxGetApp()->m_hInstance;
wpOldMenuProc = wc.lpfnWndProc;
wc.lpfnWndProc = (WNDPROC)MenuWndProc;
SetLastError(0);
RegisterClass(&wc);
// in OnSysCommand or wherever, create and track the popup menu:
CMenu menu;
menu.LoadMenu(IDM_TEST);
CMenu *mp = menu.GetSubMenu(0);
CMenu *pPopup = mp->GetSubMenu(0);
pPopup->DeleteMenu(0, MF_BYPOSITION); // remove placeholder item
for (int i = 0; i < 60; i++) { // add enough items to exceed screen height
CString s;
s.Format("item%d\n", i);
pPopup->AppendMenu(MF_STRING, 1000 + i, s);
}
mp->TrackPopupMenu(0, 100, 100, this);
return;
-
Re: scrolling popup menu via Page Up/Down
The virtual keycodes for pageup/down etc in wparam are described here http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx. If you are not getting messages for pageup/down what does your message loop code look like?
-
Re: scrolling popup menu via Page Up/Down
Quote:
Originally Posted by
ckorda
it provides me with the menu's window handle, even for nested popups
You may be missing a key point about Win32 API design. Generally it's not enough to have a window handle to control the window behavior. Your window must implement some particular control API by defining certain messages it is agree to comply with, maybe along with some data exchange protocol.
Once your window does not implement this, your messages you send to it will be just ignored, i.e. drained down to DefWindowProc with no visible effect. Which means that once menu window does not process page keys, and provides no other API to be controlled with the way you need, you have no option but to accept this humbly. :)
Subclassing menu window is going to be of no value either. What would be your design for intercepting page keys in your subclassed window procedure? You will catch those, but what will you do to make the menu to change item selection? AFAIK, menu does not provide such API.
-
Re: scrolling popup menu via Page Up/Down
"subclassing" in windows terminology does not mean "derive a class from a base class (such as CWnd).
rather, it means "replace the windows' messageloopprocedure with my own". SO it doesn't matter if CMenu is or isn't derived from CWnd. what matters is that what windows is concerned, it's a control with a window and a messageloop and you can "subclass" the actual Windows window. (See http://msdn.microsoft.com/en-us/libr...v=vs.85).aspx)
WIth MFC, this is typically done by rewiring the messageloop into that other object that has a messageloop... a CWnd (or derived). Even if the windows you're replacing fron has nothing to do at all with CWnd.
You can change the active selection in a menu programmatically with SetMenuItemInfo (MFS_HILITE to turn it on, MFS_UNHILITE to turn it off).
-
Re: scrolling popup menu via Page Up/Down
Quote:
subclassing" in windows terminology does not mean "derive a class from a base class (such as CWnd).
Right you are. There is some overloading of the term, hence confusion. But the code snippet I posted is subclassing in the sense you mean, specifically the "old" (pre-common controls 6) subclassing technique, AKA replacing the window procedure as described in the MS Subclassing Controls article.
Quote:
once menu window does not process page keys, and provides no other API to be controlled with the way you need, you have no option but to accept this humbly
I might accept it, but only due to time constraints, and definitely not humbly. Resentfully would be more like it. In my view this hiding of the menu scrolling API, which is obviously extant and useful, is just more MS obstructionism of the same type that was so resoundingly (albeit temporarily) defeated in the 2001 antitrust suit.
-
Re: scrolling popup menu via Page Up/Down
Quote:
If you are not getting messages for pageup/down what does your message loop code look like?
The message loop was shown in post #12 above. The fact is that if I subclass (as in replace the window procedure of) a popup menu window, my window procedure does not receive any messages at all for the Page Up/Down and Home/End keys. It DOES receive messages (either WM_CHAR or WM_KEYDOWN) for most other keys, including the arrow keys. I am unclear on how this can be the case. I was under the impression that when a window is subclassed, the "new" window procedure gets first crack at ALL messages. Could it be that the popup menu's window is somehow disabling the page up/down and home/end keys? I thought of WM_GETDLGCODE but I don't receive this either.
-
Re: scrolling popup menu via Page Up/Down
T
Quote:
he message loop was shown in post #12 above
Post #12 does not show a message loop.
-
Re: scrolling popup menu via Page Up/Down
Quote:
Post #12 does not show a message loop.
Would you care to elaborate on how the code below differs from a message loop? My impression was that a window procedure is the most basic form of message loop, and that CWnd and message maps and so on are just wrappers around this functionality.
Code:
WNDPROC wpOldMenuProc;
LRESULT CALLBACK MenuWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg) {
case WM_CREATE:
printf("!");
break;
case WM_KEYDOWN:
printf("k=%x ", wParam);
// ::MoveWindow(hWnd, 0, 0, 100, 100, 1); // works fine but not helpful
{
CRect r;
::GetClientRect(hWnd, r);
::ScrollWindow(hWnd, 100, 0, 0, r); // doesn't work, -100 doesn't work either
}
return 0;
}
printf("%x ", uMsg);
return CallWindowProc(wpOldMenuProc, hWnd, uMsg, wParam, lParam);
}
-
Re: scrolling popup menu via Page Up/Down
My test app now successfully catches Page Up/Down and Home/End during a popup menu, via a message filter hook (WH_MSGFILTER). This solves the first of the two problems posed in post #12. The second problem (how to tell the menu to scroll) is considerably harder. So far the only method I've found that works at all is to repeatedly send VK_DOWN or VK_UP key down messages to the popup menu's window. This is better than nothing, but not really satisfactory because it confuses the selected item with the scroll position. I investigated secret menu messages, and found quite a few (see below) but none that seem likely to cause scrolling though I haven't tried them all. Any ideas?
Code:
// copy from windows\media\avi\inc.16\windows.inc(1500)
#define MN_SETHMENU 0x01E0
#define MN_GETHMENU 0x01E1
#define MN_SIZEWINDOW 0x01E2
#define MN_OPENHIERARCHY 0x01E3
#define MN_CLOSEHIERARCHY 0x01E4
#define MN_SELECTITEM 0x01E5
#define MN_CANCELMENUS 0x01E6
#define MN_SELECTFIRSTVALIDITEM 0x01E7
//;MN_GETPPOPUPMENU = 01EAh ;Win32
//;MN_FINDMENUWINDOWFROMPOINT = 01EBh ;Win32
//;MN_SHOWPOPUPWINDOW = 01ECh ;Win32
//;MN_BUTTONDOWN = 01EDh ;Win32
//;MN_MOUSEMOVE = 01EEh ;Win32
//;MN_BUTTONUP = 01EFh ;Win32
//;MN_SETTIMERTOOPENHIERARCHY = 01F0 ;Win32
-
Re: scrolling popup menu via Page Up/Down
Out of curiosity, what operating system versions are you testing this on?
-
Re: scrolling popup menu via Page Up/Down
Quote:
Originally Posted by
ckorda
Would you care to elaborate on how the code below differs from a message loop?
For info about a message loop see
http://msdn.microsoft.com/en-us/libr...#creating_loop
http://msdn.microsoft.com/en-us/libr...=vs.85%29.aspx
In WIN32 code the message loop should look something like
Code:
BOOL bRet;
while( (bRet = GetMessage( &msg, hWnd, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
-
Re: scrolling popup menu via Page Up/Down
the code in #12 definately does not do subclassing. Rather, it changes the WindowClass.
That might be ok for your particular case, but it's a rather dangerous approach since it changes the behaviour for ALL menu's, not just the one menu you're interested in.
This could wreak havoc for any sort of tools or other code that executes in your application context (such as for example, the UI of a printer driver...)
Quote:
I might accept it, but only due to time constraints, and definitely not humbly. Resentfully would be more like it. In my view this hiding of the menu scrolling API, which is obviously extant and useful, is just more MS obstructionism of the same type that was so resoundingly (albeit temporarily) defeated in the 2001 antitrust suit.
Actually, you are stubbornly insisting that you want to do it "your way" regardless of user perception.
A menu is not an ideal UI concept if you have hundreds of items in it. That pretty much screams at you to find "a better way".
Maybe by structuring your menu differently to have submenu's (but even that has it's limits in usability), or changing your concept entirely. ANd yes, that might even involve quite a bit more than just flat out replacing the menu with something else, but thinking about what you're trying to do and find a user friendly/intuitive way to achieve the same goal.
Everything about the menu you are using is exposed.
Some parts of the menu behaviour (such as the autoscrolling) can't be targetted directly because there's simply no API for it.
Microsoft products have either stopped using a menu (using ribbon now), or are using a different control that "more or less" looks the same.
Microsoft is in no way required to release code, or even API's of controls they develop specifically for their own products (yes, it's annoying sometimes), but I would also like some controls that are in other 3rd party products. Just because the company that made the OS and the one that made your spreadsheet are one and the same does not matter in that case.
Get your facts straight, the antitrust issues from 2001 are in no way or form even remotely relevant in this discussion. That was all about MS releasing the mediaplayer as part of the OS, and some idiots deciding that was a bad idea, so they forced MS to make a version of windows without media player (WIndows N), that they ended up selling for a bit higher price, so nobody really ever bought it.
The problem with WM_KEYDOWN is probably that the frame the menu is attached to has already processed the PgDn.
-
Re: scrolling popup menu via Page Up/Down
Quote:
Originally Posted by
2kaud
that is a messageloop
but subclassing is not about changing the messageloop itself,
but changing the "callback" function (windowsprocedure) Windows will call when there are messages to be processed (in response of the messageloop)
-
Re: scrolling popup menu via Page Up/Down
Quote:
This could wreak havoc for any sort of tools or other code that executes in your application context
The popup menu places the app in a modal state which is easy to identify. It's no problem to arrange things so that the special handling is only enabled in the desired case. The rest of the time the new window procedure just calls the old window procedure and menu behavior is totally unchanged. I get that you're not a fan of scrolling popup menus, but I'd like to stay focused on how to do it. The message filter Window hook solves the first problem (non-receipt of Page Up/Down and Home/End). The only outstanding difficulty is how to scroll the menu, or rather how to do so more gracefully than by sending it VK_UP/VK_DOWN key messages.
Quote:
the antitrust issues from 2001 are in no way or form even remotely relevant
Not so. The only reason we know many crucial details about Windows, particularly Explorer-related interfaces and protocols, is because the 2001 settlement forced MS to publish them. See e.g. Microsoft To Publish 385 Windows APIs, Protocols To Make Antitrust Case Go Away. It's relevant to this discussion because the settlement was merely a temporary victory, and old habits were quickly reasserted. The menu API is needlessly obfuscated, people have been complaining about it for years, and this is just one of many examples of anti-competitive behavior.
"On November 2, 2001, the DOJ reached an agreement with Microsoft to settle the case. The proposed settlement required Microsoft to share its application programming interfaces with third-party companies and appoint a panel of three people who will have full access to Microsoft's systems, records, and source code for five years in order to ensure compliance." United States v. Microsoft Corp.
-
Re: scrolling popup menu via Page Up/Down
Quote:
Out of curiosity, what operating system versions are you testing this on?
Windows 7, see post #1.
-
Re: scrolling popup menu via Page Up/Down
Quote:
Originally Posted by
ckorda
Windows 7, see post #1.
I saw that but wasn't sure since you mentioned common controls 6 and referenced very old articles.
-
Re: scrolling popup menu via Page Up/Down
Quote:
Originally Posted by
ckorda
The menu API is needlessly obfuscated, people have been complaining about it for years, and this is just one of many examples of anti-competitive behavior.
When you design some functionality, there always public and private parts exist. And it's generally up to you how far public part goes.
I'm in Windows programming since 1996, and frankly, you are the first person I met ever who demands to extend standard menu API. :) Besides, the "old" API inherited from classical Win16 always was kind of rigid. And I completely understand why MS never tried to improve it, as the effort (design, implementation, testing, documenting, training, etc.) is hardly worth the results.
Quote:
"On November 2, 2001, the DOJ reached an agreement with Microsoft to settle the case. The proposed settlement required Microsoft to share its application programming interfaces with third-party companies and appoint a panel of three people who will have full access to Microsoft's systems, records, and source code for five years in order to ensure compliance."
United States v. Microsoft Corp.
Again, I completely understand all the fuzz about exposing IE guts and being able to expel the one from the system, as there's really a place for competition. But competing in pop-up menus... it's something beyond my understandings.