Path: utzoo!attcan!uunet!husc6!uwvax!speedy!solomon From: solomon@speedy.cs.wisc.edu (Marvin Solomon) Newsgroups: comp.windows.x Subject: How do I create popup menus with Xt? Summary: popup shell widgets found somewhat wanting Keywords: menus popup toolkit x11 widgets Message-ID: <6005@spool.cs.wisc.edu> Date: 18 Jul 88 18:27:28 GMT Sender: news@spool.cs.wisc.edu Reply-To: solomon@speedy.cs.wisc.edu (Marvin Solomon) Organization: U of Wisconsin CS Dept Lines: 111 References: A little while back somebody asked how to create popup menus and got a humorous reply that the X Consortium decided that menus are a bad idea. Well I decided I really wanted to provide popup menus with my application (a troff previewer), so I spend a week wrestling with the X Toolkit trying to figure out how to do it. I've had some limited success, but what I did was by no means straightforward, so I thought I'd share my experiences with this newsgroup in order that 1. Others can benfit from my experience 2. Somebody can tell me how I really SHOULD have done it 3. Perhaps the next version of the intrinsics will may things a bit easier. First, I looked at all the examples of applications I could find to see how others did it, and discovered that although the Toolkit intrinsics have all kinds of stuff that seems to be designed for the purpose, ABSOLUTELY NOBODY USES IT! Among other places, I looked in xterm, twm, and awm (which uses a modified version of the rtl menu package), xman, and xmh. The stuff in contrib/menus seems to use some of it, but that whole directory seems not to have been updated to V11R2. I can't get close to getting it to compile. All the working applications use raw Xlib functions to draw and manage their menus (and they each do it differently). All that stuff in Chapter 7 (Popup Widgets) seems to be virtually unused. Moreoever, I've come to the conclusion that at least some of it is unusable. But more on that later. For concreteness sake, suppose I want a Macintosh-like interface: There's a menu bar with a bunch of buttons. A mouse press in one of these buttons pops up a menu. Dragging the cursor through the menu without releasing the mouse button highlights menu items. Releasing the button while one of the menu items is highlighted invokes a corresponding action and makes the popup menu go away. Dragging the cursor outside the popup menu without releasing the button makes the menu go away without causing any action. For the menu, bar I create a Box widget with some Command widgets inside it. At initialization time, I create the menu itself as a popup shell containing a Box containing a set of Buttons. (By the way, the documentation gives no advice on what widget_class to use for the popup-shell. I found that I had to make it overrideShellWidgetClass to prevent the window manager from getting into the act when the window pops up). The first problem is how to get the menu to pop up. According to the manual, there are three ways to get the menu to pop up: By calling XtPopup() explicitly (from inside a callback), by using XtCallbackExclusive() (or one of its friends) as a callback procedure, or by using MenuPopup() in a translation. The first two approaches create the popup only when the Command widget does its callbacks--by default, when the mouse button is released. I tried using XtOverrideTranslations to override the default ": set()\n : notify() unset()" (NB: this is from the code; the manual has it wrong) with the actions ": set() notify()\n : unset()" but somehow, this doesn't work quite right (more on this later). The third approach worked better. I use ": set() MenuPopup(menu)\n" where "menu" is the name of the popup shell. The second problem is how to get the menu to pop up in a reasonable place-- i.e., near the cursor. If the shell widget is not of class overrideShellWidgetClass, the window manager kindly askes me to size and locate the menu, which is clearly ridiculous. Actions set up using the translation manager are passed an event structure, which contains the right information, but unfortunatly the buildin action for MenuPopup() throws this information away. What I settled on was to add an XtNpopupCallback procedure to set the XtNx and XtNy attributes of the popup shell. I pass a pointer to the Command widget that caused the popup as the 'closure' of the callback. It queries the (x,y) position of that widget and makes the menu pop up slightly offset from there. On reflection, I might have been better off to define my own popup action and ignore the "handy" MenuPopup(). The third problem is to make the popup menu behave properly. That was relatively easy. I overrode its transations with ": highlight() set()" The fourth and hardest problem was to make the menu go away. Once again, there are three methods documented, none of which seems to work quite right: call XtPopdown() from a callback, use XtCallbackPopdown() as a callback, or use MenuPopdown() as an action. The latter would seem to be the best, but I couldn't figure out how to use it. As a matter of fact, I've convinced myself that MenuPopdown() is totally unusable. The only reasonable action to bind it to would be . MenuPopdown() complains if the target widget is not a shell widget. Since MenuPopdown() has no arguments, its target widget is always the widget to which the translation is bound. But if I set a translation for in the popup shell, the Command buttons in the window never see the button up events, so I can't select anything with them. I can't modify them to activate on button down, since the butten is already down, and activating them on EnterWidow is obviously wrong too. The solution I finally came up with is to add an XtCallbackPopdown() action to EACH AND EVERY button in the menu, passing an XtPopdownID as the closure. One final problem is how to make the popup menu go way if the user doesn't choose any of its options. I still haven't got that to work, but I have one more trick I might try: Define a translation for LeaveWindow on the Box widget containing the menu. Right now, if I pop up the menu with a button press and then release the button somewhere other than one of the menu items, the menu stays popped up. If I go back and click in one of the menu items, the corresponding action is taken and the menu goes away. Curiously enough, there doesn't seem to be any grab active: I can still activate other buttons in the menu bar. All this despite that fact that the builtin action for MenuPopup() sets "spring-loaded" and (so far as I can make out) does an exclusive grab. The same thing happend if I use XtCallbackExclusive() instead of MenuPopup() to raise the menu. Only in this case, the menu does not "feel" the mouse until I release the button for the first time. I intended to include the whole test program at the end of this message, but as it's already too long, I'll send the test program under separate cover. Marvin Solomon Computer Sciences Department University of Wisconsin, Madison WI solomon@cs.wisc.edu or seismo!uwvax!solomon