A popdown or pullup menu
PtWidget → PtBasic → PtContainer → PtGroup → PtMenu
For more information, see the diagram of the widget hierarchy.
None — use PhAB's Menu module.
<photon/PtMenu.h>
Menus display a list of possible selections (called items) and let you choose one of them. The PtMenu class provides popup and pulldown menus that work in either press-drag-release or click-move-click mode.
You can use PhAB's Menu module instead of this widget, which makes it easier to add menus to your project. See “Menu modules” in the Working with Modules chapter of the Photon Programmer's Guide. |
The selections displayed in a menu are usually pushbuttons that activate application functions, but they may also be toggle buttons, cascaded menu buttons that invoke submenus, or any other selectable widget. Although menus usually consist of PtMenuButton widgets, you can use any type of widget as a menu item by making it the child of a PtMenu widget.
If PtMenu has any PtLabel-class children that have the Pt_ARG_ACCEL_KEY resource defined, the menu attaches hotkey callbacks on behalf of those children.
A PtMenu widget blocks any nonmenu hotkeys while it's displayed. |
If you use a nonselectable widget as a menu item, you can make it selectable by setting the Pt_SELECTABLE bit in its Pt_ARG_FLAGS (see PtWidget). PtMenu then sets up callbacks on its children to alter their behavior where necessary, thus ensuring that the children operate as you expect. For example, the children automatically highlight in press-drag-release mode.
If a menu item has a PtMenu widget as a child, and that child has the Pt_MENU_CHILD bit set in its Pt_ARG_MENU_FLAGS resource, the child behaves as a submenu and is realized when necessary.
Most applications have a menu bar, a horizontal bar (usually a PtMenuBar widget) across the top of the application window. The menu bar contains a number of menu buttons; when you click on one, the associated pulldown menu appears.
A menu may also be activated or popped up in response to an event such as a button press inside another type of widget. Known as a popup menu, this type of menu normally appears at the current pointer position.
A menu is displayed when it's activated; it may be in one of these modes:
When a selectable widget (such as a button or toggle) is chosen from the menu:
If the selected item is a cascaded menu button, its menu is activated and it's displayed to the right of or below the selection.
You can use PtMenu to create either a pulldown or a popup menu.
The Pt_ARG_MENU_FLAGS resource controls the menu's behavior and appearance, including:
You can set Pt_ARG_MENU_TITLE to specify a title to be displayed at the top of the menu. You can also set Pt_ARG_MENU_TITLE_FONT to specify the title's font.
You can use Pt_ARG_MENU_TEXT_FONT to specify the font to use for displaying menu items. This resource overrides the normal default font for text items placed in the menu.
As with other containers, items are placed in a menu by creating them as children of the menu itself. The widgets that you place in the menu behave according to their type. You can use any widgets that have the Pt_SELECTABLE flag set in their Pt_ARG_FLAGS. This includes buttons, toggle buttons, and menu buttons.
Menu buttons within the menu provide cascaded submenus. For more information on how to create cascaded submenus, see “Cascaded Menus.”
Widgets that have the Pt_AUTOHIGHLIGHT flag set automatically have the visual cuing provided for you. All the widgets in the Photon widget library that set the Pt_SELECTABLE flag by default set this flag as well. If you explicitly set either of these flags for other widget types, be sure to use them appropriately in combination so they'll behave correctly in a menu.
The menu may also contain other widgets that aren't menu selections. For example, you can add separators and labels to the menu to make a visual distinction between different sections of the menu and to enhance its appearance.
Normally, the menu is wide enough to hold the widest menu item; all the menu items are made this size as well. The Pt_MENU_AUTO bit in the Pt_ARG_MENU_FLAGS resource controls menu sizing.
If you want to explicitly control the sizes of the menu items, you can clear this flag — it's then your responsibility to set the dimensions of each menu item.
A menu may be created either in advance or dynamically in response to the action that activated it.
If you create the menu in advance, you have to create a callback function to position the menu and realize it in response to the action that activates it. When you make a selection, the menu unrealizes itself.
To create the menu dynamically, your callback function must create the menu first, then position it and realize it. You may also want the menu to be re-created from scratch the next time the callback is invoked. In this case, you can create the menu as a transient menu by setting the Pt_MENU_TRANSIENT bit in Pt_ARG_MENU_FLAGS. This menu destroys itself after you select a menu item.
The menu might be destroyed before the selected menu item's callback
is invoked. To make sure that the menu item's callback is called
first, attach the menu item callback as follows:
PtCallback_t callbacks[] = { { menu_item_callback, NULL } }; PtSetArg( &arg[0], Pt_ARG_TEXT_STRING, "Open", 0 ); PtSetArg( &arg[1], Pt_CB_ACTIVATE, callbacks, Pt_LINK_INSERT ); item = PtCreateWidget( PtMenuButton, menu, 2, arg ); |
To create a pulldown menu, you must create the menu as a child of the menu button. You must also attach a callback to the menu button that pulls down the menu in response to a button press.
Attach the callback to the Pt_CB_ARM or Pt_CB_MENU callback list (see PtBasic) so that both press-drag-release and click-stay modes work, then provide the pointer to the menu widget as the client_data for the callback.
Your callback must position the menu beneath the menu button and make it appear. To do this, call PtPositionMenu() with the menu widget as the first parameter, and a NULL pointer as the second parameter. This function determines the correct position for the menu, knowing that its parent is a menu button. After this, you can make the menu appear by calling PtRealizeWidget(). For more information about these functions, see the Photon Library Reference.
For example, to display a pulldown menu:
int postMenu( PtWidget_t *w, void *client_data, PtCallbackInfo_t *info) { if (client_data) { PtWidget_t *menu = (PtWidget_t *)client_data; PtPositionMenu(menu, NULL); PtRealizeWidget(menu); } return (Pt_CONTINUE); }
To create a pulldown menu invoked by a “File” menu button with this routine as the callback function, use the following code:
fileMenu = PtCreateWidget(PtMenu, fileButton, 0, NULL); create_file_items(fileMenu); PtAddCallback(fileButton, Pt_CB_ARM, postMenu, fileMenu);
In this example, the create_file_items() function creates the menu items in the file menu.
Popup menus are frequently used in an application's work area. Often this area is not a container widget. In this case, you don't create the menu as a child of the work area itself; instead, create the menu as a child of the work area's parent.
As with pulldown menus, you have to provide a callback function or event handler to activate your popup menu. This callback has to position the menu and make it appear.
If the widget that you wish to associate with the popup menu doesn't have a Pt_CB_ARM or Pt_CB_MENU callback list, the simplest way to activate the menu is to associate an event handler with button-press events. Provide the menu pointer to the event handler as client_data. The event handler should make sure that the appropriate menu button was actually pressed before it activates the menu.
The event handler should position the menu by calling PtPositionMenu(). In this case, pass the Photon event from the PtCallbackInfo_t structure as the second parameter to the function. This identifies the pointer position where the menu should be placed.
The following function illustrates how you can activate a popup menu:
int postMenu( PtWidget_t *w, void *client_data, PtCallbackInfo_t *info) { PhEvent_t *event = info ? info->event : NULL; PhPointerEvent_t *ptr = event ? (PhPointerEvent_t *) PhGetData(event) : NULL; /* post the popup if the right button was pressed */ if (event && client_data && (event->type & Ph_EV_BUT_PRESS) && (ptr->buttons == 1)) { PtWidget_t *menu = (PtWidget_t *)client_data; PtPositionMenu(menu, event); PtRealizeWidget(menu); } return (Pt_CONTINUE); }
The following code illustrates how you can create a popup menu and activate it using the above function:
PtSetArg(&arg[0], Pt_ARG_POS, &offsets.ul, 0); PtSetArg(&arg[1], Pt_ARG_DIM, &workarea, 0); PtSetArg(&arg[2], Pt_ARG_ANCHOR_FLAGS, Pt_LEFT_ANCHORED_LEFT|Pt_RIGHT_ANCHORED_RIGHT| Pt_TOP_ANCHORED_TOP|Pt_BOTTOM_ANCHORED_BOTTOM, Pt_IS_ANCHORED); PtSetArg(&arg[3], Pt_ARG_ANCHOR_OFFSETS, &offsets, 0); raw = PtCreateWidget(PtRaw, window, 4, arg); popupMenu = PtCreateWidget(PtMenu, window, 0, NULL ); create_file_items(popupMenu); PtAddEventHandler(raw, Ph_EV_BUT_PRESS, popup_menu_cb, popupMenu);
If you place a menu button in a menu and create another menu as its child, you'll get a cascaded submenu. Unlike pulldown menus or popup menus, you don't have to attach any callbacks to activate submenus — they're handled automatically.
You must, however, set the Pt_MENU_CHILD bit in the Pt_ARG_MENU_FLAGS to indicate that the menu is a submenu. If you want the submenu to appear to the right of its parent — the conventional way that submenus cascade — you'll also have to set the Pt_MENU_RIGHT flag in the PtMenuButton widget's Pt_ARG_BUTTON_TYPE resource.
Here's the create_file_items() function from the previous example; notice how it creates a cascaded submenu:
void create_file_items(PtWidget_t *parent) { PtArg_t arg[3]; PtWidget_t *importButton, *importMenu; PtCallback_t quit_callbacks[] = { {quit_cb, NULL} }; PtCallback_t noop[] = { {nop_cb, NULL} }; PtSetArg(&arg[0], Pt_ARG_TEXT_STRING, "Open", 0); PtSetArg(&arg[1], Pt_ARG_TEXT_FONT, Helvetica_14B, 0); PtSetArg(&arg[2], Pt_CB_ACTIVATE, noop, 1); PtCreateWidget(PtButton, parent, 3, arg); PtSetArg(&arg[0], Pt_ARG_TEXT_STRING, "New", 0); PtSetArg(&arg[1], Pt_ARG_TEXT_FONT, Helvetica_14B, 0); PtSetArg(&arg[2], Pt_CB_ACTIVATE, noop, 1); PtCreateWidget(PtButton, parent, 3, arg); PtSetArg(&arg[0], Pt_ARG_TEXT_STRING, "Import", 0); PtSetArg(&arg[1], Pt_ARG_TEXT_FONT, Helvetica_14B, 0); PtSetArg(&arg[2], Pt_ARG_BUTTON_TYPE, Pt_MENU_RIGHT, Pt_MENU_RIGHT); importButton = PtCreateWidget(PtMenuButton, parent, 3, arg); PtSetArg(&arg[0], Pt_ARG_MENU_FLAGS, Pt_MENU_AUTO|Pt_MENU_CHILD, Pt_MENU_AUTO|Pt_MENU_CHILD); importMenu = PtCreateWidget(PtMenu, importButton, 1, arg ); create_import_items(importMenu); PtCreateWidget(PtSeparator, parent, 0, NULL); PtSetArg(&arg[0], Pt_ARG_TEXT_STRING, "Quit", 0); PtSetArg(&arg[1], Pt_ARG_TEXT_FONT, Helvetica_14B, 0); PtSetArg(&arg[2], Pt_CB_ACTIVATE, quit_callbacks, 1); PtCreateWidget(PtButton, parent, 3, arg); }
Here's a complete example that uses all the techniques described above. It includes an application window with a menu bar along the top, and a work area. The menu bar consists of a File menu and a Help menu. The work area is a PtRaw widget that has a popup menu associated with the right pointer button. The popup menu contains the same selections as the file menu.
#include <Pt.h> #include <stdlib.h> /* The name of the font to use in the menus: */ char Helvetica_14B[MAX_FONT_TAG]; int post_menu_cb(PtWidget_t *w, void *client_data, PtCallbackInfo_t *info) { client_data = client_data; info = info; if (client_data) { PtWidget_t *menu = (PtWidget_t *)client_data; PtPositionMenu(menu, NULL); PtRealizeWidget(menu); } return (Pt_CONTINUE); } int popup_menu_cb(PtWidget_t *w, void *client_data, PtCallbackInfo_t *info) { PhEvent_t *event = info ? info->event : NULL; PhPointerEvent_t *ptr = event ? (PhPointerEvent_t *) PhGetData (event) : NULL; w = w; /* post the popup if the right button was pressed */ if (event && client_data && (event->type & Ph_EV_BUT_PRESS) && (ptr->buttons == 1)) { PtWidget_t *menu = (PtWidget_t *)client_data; PtPositionMenu(menu, event); PtRealizeWidget(menu); } return (Pt_CONTINUE); } int nop_cb(PtWidget_t *w, void *client_data, PtCallbackInfo_t *info) { PtArg_t arg[1]; char *text; w = w; client_data = client_data; info = info; PtSetArg(&arg[0], Pt_ARG_TEXT_STRING, &text, 0); PtGetResources(w, 1, arg); if (text) printf("Pushed the %s button\n", text); return (Pt_CONTINUE); } int quit_cb(PtWidget_t *w, void *client_data, PtCallbackInfo_t *info) { w = w; client_data = client_data; info = info; PtExit( EXIT_SUCCESS ); return (Pt_CONTINUE); } void create_import_items(PtWidget_t *parent) { PtArg_t arg[3]; PtCallback_t noop[] = { {nop_cb, NULL} }; PtSetArg(&arg[0], Pt_ARG_TEXT_STRING, "Image", 0); PtSetArg(&arg[1], Pt_ARG_TEXT_FONT, Helvetica_14B, 0); PtSetArg(&arg[2], Pt_CB_ACTIVATE, noop, 1); PtCreateWidget(PtButton, parent, 3, arg); PtSetArg(&arg[0], Pt_ARG_TEXT_STRING, "Bitmap", 0); PtSetArg(&arg[1], Pt_ARG_TEXT_FONT, Helvetica_14B, 0); PtSetArg(&arg[2], Pt_CB_ACTIVATE, noop, 1); PtCreateWidget(PtButton, parent, 3, arg); } void create_file_items(PtWidget_t *parent) { PtArg_t arg[3]; PtWidget_t *importButton, *importMenu; PtCallback_t quit_callbacks[] = { {quit_cb, NULL} }; PtCallback_t noop[] = { {nop_cb, NULL} }; PtSetArg(&arg[0], Pt_ARG_TEXT_STRING, "Open", 0); PtSetArg(&arg[1], Pt_ARG_TEXT_FONT, Helvetica_14B, 0); PtSetArg(&arg[2], Pt_CB_ACTIVATE, noop, 1); PtCreateWidget(PtButton, parent, 3, arg); PtSetArg(&arg[0], Pt_ARG_TEXT_STRING, "New", 0); PtSetArg(&arg[1], Pt_ARG_TEXT_FONT, Helvetica_14B, 0); PtSetArg(&arg[2], Pt_CB_ACTIVATE, noop, 1); PtCreateWidget(PtButton, parent, 3, arg); PtSetArg(&arg[0], Pt_ARG_TEXT_STRING, "Import", 0); PtSetArg(&arg[1], Pt_ARG_TEXT_FONT, Helvetica_14B, 0); PtSetArg(&arg[2], Pt_ARG_BUTTON_TYPE, Pt_MENU_RIGHT, Pt_MENU_RIGHT); importButton = PtCreateWidget(PtMenuButton, parent, 3, arg); PtSetArg(&arg[0], Pt_ARG_MENU_FLAGS, Pt_MENU_AUTO|Pt_MENU_CHILD, Pt_MENU_AUTO|Pt_MENU_CHILD); importMenu = PtCreateWidget(PtMenu, importButton, 1, arg ); create_import_items(importMenu); PtCreateWidget(PtSeparator, parent, 0, NULL); PtSetArg(&arg[0], Pt_ARG_TEXT_STRING, "Quit", 0); PtSetArg(&arg[1], Pt_ARG_TEXT_FONT, Helvetica_14B, 0); PtSetArg(&arg[2], Pt_CB_ACTIVATE, quit_callbacks, 1); PtCreateWidget(PtButton, parent, 3, arg); } void create_help_items(PtWidget_t *parent) { PtArg_t arg[3]; PtCallback_t noop[] = { {nop_cb, NULL} }; PtSetArg(&arg[0], Pt_ARG_TEXT_STRING, "About", 0); PtSetArg(&arg[1], Pt_ARG_TEXT_FONT, Helvetica_14B, 0); PtSetArg(&arg[2], Pt_CB_ACTIVATE, noop, 1); PtCreateWidget(PtMenuButton, parent, 3, arg); } int main(int argc, char *argv[]) { PtAppContext_t app; PhDim_t dim, workarea, *group_size; PhPoint_t pos; PtArg_t arg[5]; PtWidget_t *window, *group, *raw; PtWidget_t *fileButton, *helpButton; PtWidget_t *fileMenu, *helpMenu, *popupMenu; if (PtInit(NULL) == -1) PtExit(EXIT_FAILURE); if ((window = PtCreateWidget(PtWindow, Pt_NO_PARENT, 0, NULL)) == NULL) PtExit(EXIT_FAILURE); /* Generate the name of the font for the menus. */ if(PfGenerateFontName("Helvetica", PF_STYLE_BOLD, 14, Helvetica_14B) == NULL) { perror("Unable to generate font name"); PtExit(EXIT_FAILURE); } PtSetArg(&arg[0], Pt_ARG_ANCHOR_FLAGS, Pt_LEFT_ANCHORED_LEFT|Pt_RIGHT_ANCHORED_RIGHT, Pt_IS_ANCHORED); PtSetArg(&arg[1], Pt_ARG_BEVEL_WIDTH, 2, 0); PtSetArg(&arg[2], Pt_ARG_FLAGS, Pt_HIGHLIGHTED, Pt_HIGHLIGHTED); group = PtCreateWidget(PtGroup, window, 3, arg); PtSetArg(&arg[0], Pt_ARG_TEXT_STRING, "File", 0); PtSetArg(&arg[1], Pt_ARG_TEXT_FONT, Helvetica_14B, 0); fileButton = PtCreateWidget(PtMenuButton, group, 2, arg); PtSetArg(&arg[0], Pt_ARG_TEXT_STRING, "Help", 0); PtSetArg(&arg[1], Pt_ARG_TEXT_FONT, Helvetica_14B, 0); helpButton = PtCreateWidget(PtMenuButton, group, 2, arg); fileMenu = PtCreateWidget(PtMenu, fileButton, 0, NULL); create_file_items(fileMenu); helpMenu = PtCreateWidget(PtMenu, helpButton, 0, NULL); create_help_items(helpMenu); PtAddCallback(fileButton, Pt_CB_ARM, post_menu_cb, fileMenu); PtAddCallback(helpButton, Pt_CB_ARM, post_menu_cb, helpMenu); PtRealizeWidget(window); PtSetArg(&arg[0], Pt_ARG_DIM, &group_size, 0); PtGetResources(group, 1, arg); workarea.w = 300; workarea.h = 200; pos.x = 0; pos.y = group_size->h + 2 * 2; PtSetArg(&arg[0], Pt_ARG_POS, &pos, 0); PtSetArg(&arg[1], Pt_ARG_DIM, &workarea, 0); raw = PtCreateWidget(PtRaw, window, 2, arg); popupMenu = PtCreateWidget(PtMenu, window, 0, NULL ); create_file_items(popupMenu); PtAddEventHandler(raw, Ph_EV_BUT_PRESS, popup_menu_cb, popupMenu); PtRealizeWidget(raw); dim.w = workarea.w; dim.h = workarea.h + group_size->h + 2 * 2; PtSetArg(&arg[0], Pt_ARG_DIM, &dim, 0); PtSetResources(window, 1, arg); PtMainLoop(); return (EXIT_SUCCESS); }
Resource | C type | Pt type | Default |
---|---|---|---|
Pt_ARG_MENU_FLAGS | unsigned long | Flag | Pt_MENU_AUTO | Pt_MENU_TEXT_ALIGN |
Pt_ARG_MENU_INPUT_GROUP | short | Scalar | 0 |
Pt_ARG_MENU_ITEM_FILL_COLOR | PgColor_t | Scalar | 0xCCCCCC |
Pt_ARG_MENU_ITEM_HIGHLIGHT_COLOR | PgColor_t | Scalar | 0xA6B1CE |
Pt_ARG_MENU_SPACING | short | Scalar | Value of Pt_ARG_BEVEL_WIDTH |
Pt_ARG_MENU_TEXT_FONT | char * | String | NULL |
Pt_ARG_MENU_TITLE | char * | String | NULL |
Pt_ARG_MENU_TITLE_FONT | char * | String | "MenuFont09" |
Pt_ARG_SUBMENU_PARENT_HIGHLIGHT_COLOR | PgColor_t | Scalar | 0xB0B0B0 |
C type | Pt type | Default |
---|---|---|
unsigned long | Flag | Pt_MENU_AUTO | Pt_MENU_TEXT_ALIGN |
Flags that control the menu's appearance and behavior. You can set this resource to any combination of the following bits:
C type | Pt type | Default |
---|---|---|
short | Scalar | 0 |
The input group for the menu. When this resource is set to a non-zero value, the menu uses that input group's rectangle to position itself on the screen. A mouse-click outside of the menu in any other input group does not dismiss the menu.
If this resource is 0, and you have more than one input group, the menu picks a random group to position itself. Mouse-clicks from any input group will dismiss the menu. |
C type | Pt type | Default |
---|---|---|
PgColor_t | Scalar | 0xCCCCCC |
Menu items normally inherit their fill color from the menu, which inherits its color from the parent widget. If you set this resource, this color is used as the fill color for all menu items, except the items which have a non-transparent fill color.
C type | Pt type | Default |
---|---|---|
PgColor_t | Scalar | 0xA6B1CE |
The highlight fill color for menu items.
C type | Pt type | Default |
---|---|---|
short | Scalar | Value of Pt_ARG_BEVEL_WIDTH |
The amount of space, in pixels, between each menu item. The default is the value of the Pt_ARG_BEVEL_WIDTH resource defined by PtWidget.
C type | Pt type | Default |
---|---|---|
char * | String | NULL |
The font used for PtLabel-based menu items; see PgSetFont() in the Photon Library Reference. This resource overrides the normal default font for text items placed in the menu.
C type | Pt type | Default |
---|---|---|
char * | String | NULL |
The menu's title. If you don't want a title, set this resource to NULL.
C type | Pt type | Default |
---|---|---|
char * | String | "MenuFont09" |
The font used for displaying the title.
C type | Pt type | Default |
---|---|---|
PgColor_t | Scalar | 0xB0B0B0 |
The highlight fill color for a menu item when an item in its submenu is selected.
If the widget modifies an inherited resource, the “Default override” column indicates the new value. This modification affects any subclasses of the widget.