Copyright (c) 2012-2016 Hyperion Entertainment and contributors.

BOOPSI Popup Menus - Part 2

From AmigaOS Documentation Wiki
Jump to: navigation, search

Author

Simon Archer
Copyright (c) 2011 Simon Archer
Used by Permission

Introduction

In part one we took a look at the new "popup" menu feature of window.class and what the default options are and how to use them. In part two, we shall discuss how the application programmer can add items to the popup menu, and be notified when the user has selected one of these custom options.

Overview

The popup menu available from the window objects title bar is not designed to be a replacement for the applications menus, it is purely to offer the user some options which are applicable to the window object the menu is attached to. Such options could be offering a "Snapshot" facility where the application could save the current window size and position so that it will always open in the same place and be the same size (more on that in part 3). Whatever the options, they should be relevant to the window and its operation. Remember, users are easily confused, so make your custom options simple and plainly obvious!

Adding items

The adding of custom options is done via the "Hook" method, and the hook function should use the standard popupmenu.class way of adding items. The hook function is called like so:

LONG PopupHook( struct Hook *hook, APTR o, UNUSED APTR reserved )

The "hook" parameter is a pointer to the Hook structure used to declare this function, and the "o" parameter is the popupmenu object which you can add items to with PMA_AddItem. You may also use the standard h_Data pointer of the "hook" structure for your own use, such as passing a data pointer so that the hook function may better determine what options to offer.

As with all hook functions, the pointers supplied are guaranteed to be valid when the function is called, but not afterwards. Do NOT cache any of these pointers, as they WILL change with each invocation of the popup menu.

When adding custom items to the popup menu, it is advisable to supply a separator as the first item. This gives the user a visual distinction between the standard items, and the custom ones. For each item you add, you should supply a meaningful PMIA_ID value, as this is returned to you in the window event loop, and it is how your application determines which custom item has been selected by the user. There are no real restrictions on what IDs you can use here, but it is recommended to start the IDs at 1 rather than ZERO. Also, always add your custom items to the bottom of the item list. The name of the game here is "Consistency", and keeping an overall unform nature to these menus will help the users become accustomed to this new functionality.

The hook function should return the amount of items successfully added to the menu. This is used to verify that an empty menu will not be shown.

So, let's have a look at an example hook function that will add a separator and one custom item to the popup menu:

LONG PopupHook( struct Hook *hook, APTR o, UNUSED APTR reserved )
{
  LONG added = 0;
  Object *item;
 
  /* add a separator to separate custom items from default ones */
  if(( item = IIntuition->NewObject( IPopupMenu->POPUPMENU_GetItemClass(), NULL,
    PMIA_WideTitleBar, TRUE,
    TAG_END )))
  {
    if(!( IIntuition->IDoMethod( o, PM_INSERT, item, ~0 )))
    {
      /* we failed to add the item, dispose of it */
      IIntuition->DisposeObject( item );
    }
    else
      added++;
  }
 
  if(( item = IIntuition->NewObject( IPopupMenu->POPUPMENU_GetItemClass(), NULL,
    PMIA_Title, (ULONG) "Snapshot",
    PMIA_ID, (ULONG)ID_SNAPSHOT,
    TAG_END )))
  {
    if(!( IIntuition->IDoMethod( o, PM_INSERT, item, ~0 )))
    {
      /* again, adding the item to the menu failed, so dispose of it here */
      IIntuition->DisposeObject( item );
    }
    else
      added++;
  }
  return added;
}

If the items were added successfully, the popup menu will now contain a separator after the last default option, and a new custom "Snapshot" item which the user can select. All the rules of popupmenu.class apply here, which means we can add as many items as are required. This also includes adding sub-items etc. Please refer to the autodoc for popupmenu.class for further information on what can be done.

Now let's look at how we get the notification of this event, and how to deal with it.

Specifying your hook

Once you have your hook function created and adding the items that you require, window.class needs to know about it. We do this by initialising a struct Hook, and adding our hook function to it. The code for that can be like so:

struct Hook *popuphook;
popuphook = IExec->AllocSysObjectTags( ASOT_HOOK,
  ASOHOOK_Entry, mypopupfunction,
  ASOHOOK_Subentry, NULL,
  ASOHOOK_Data, mydatapointer,
  TAG_END );

This hook now gets passed to the window.class by passing WINDOW_PopupHook, mypopuphook in the tags when the window object is created.

Obviously, do not forget to

IExec->FreeSysObject( ASOT_HOOK, mypopuphook );

Getting custom menu item events

In part one, we showed how to expand the window event loop to receive the new screen jumping event. In a similar vein, we also need to add a new event type so that we may be notified that the user selected one of our custom menu items. We do this by testing for the WMHI_POPUPMENU event. The enhanced event loop will now look like so:

  while( !done )
  {
    IExec->Wait( winsig );
 
    while(( result = IIntuition->IDoMethod( winobj, WM_HANDLEINPUT, code )) != WMHI_LASTMSG )
    {
      switch( result & WMHI_CLASSMASK )
      {
        case WMHI_CLOSEWINDOW:
        done = TRUE;
        break;
 
        case WMHI_ICONIFY:
        break;
 
        case WMHI_GADGETUP:
        break;
 
        case WMHI_JUMPSCREEN:
        break;
 
        case WMHI_POPUPMENU:
        break;
      }
    }
  }

Earlier we mentioned about supplying meaningful menu item IDs, and this is where they play their part. Once your event loop detects the WMHI_POPUPMENU event, we can get the ID of the menu item selected by using the WMHI_GADGETMASK just like we would for gadgets. Taking our example hook function above into account, the event loop code would be something like this to detect the menu item selected:

  case WMHI_POPUPMENU:
  {
    switch( result & WMHI_GADGETMASK )
    {
      case ID_SNAPSHOT:
      /* the code to snapshot the window goes here */
      break;
 
      /* we can add the IDs of extra menu items here */
    }
    break;
  }

Summary

We hope you have found this look at the popupmenu feature of window.class useful. Obviously the example code above is not complete, it is merely to show how the application programmer can go about using this new feature, and the steps required. A fully working example with source code can be downloaded from here, which should give you a good indication of how to adapt your code. You will not be able to compile this code just yet, as a newer SDK will be required than that which is available at the time of writing.

In part 3, we will take a look at some extra functionality that has been added to window.class, and allows saving of window positions. For now, this is purely for reference only, as it will require a newer version of window.class than is currently publicly available.