Tips_gui   >   Menus   >   Context Menus

Context Menus

Context menus are great for adding optional commands and features to your application. They are especially appreciated by power users who are more familiar with your application.

Rather than cluttering up the window with too many buttons, use context menus. (Right-click on this field in the StudioTips Browser to see its context menu.)

If a window object supports context menus it will have a $contextmenu property. If you enter a valid menu class name in the $contextmenu property, the menu class will be opened if the user right-clicks on the window object.

Creating and maintaining a menu class for each context menu is a lot of work. A solution is to create all your context menus on-the-fly. This section explains how you can build a context menu on-the-fly.

A user interface problem with context menus is how do you communicate to the user that a context menu is available to them for certain window objects? See the Context Menu Cursor topic for a technique to hint the user when a context menu exists.

Context Menu On-The-Fly

When the user right-clicks on a window object which has a $contextmenu a $event message is sent to the window object, pEventCode is set to evOpenContextMenu, and pContextMenu is reference to the menu instance which is about to appear to the user as a context menu.

By creating a menu class with no menu lines and adding a couple of public methods to the menu class we are able to very easily create context menus on-the-fly.

Because the menu lines are added to the menu instance we never have to make change to the menu class. A single menu class can be use for all of the context menus in an application.

If you have StudioTips a copy of the context menu and a demo is included in the StudioTips library. You are free to copy the menu class to your own applications.

The following topics explain the methods and code involved in creating context menus on-the-fly.

Context Menu Class

You will need to create a menu class with a few basic methods. For this discussion the menu class is named mContextMenu.

There are no menu lines and the menu title is left empty. We just need to add the following class methods to mContextMenu.

The following class methods are added to mContextMenu.

  1. $addMenuLine(pName,pText,pbElipsis) - This method is called from the window object method which the user right-clicks on. An $addMenuLine message is sent for each line you want to add to the context menu. pName is assigned to the $name property of the menu line. pText is assigned to the $text property and is visible to the user. pbElipsis appends ... to the visible text. The method returns a reference to the menu line. The sender can further manipulate the menu line using the menu line reference.
  2. $attachEventsObserver(prObserver,pCallBackMethodName) - This method is called from the window object method which the user right-clicks on. It tells the menu instance where to send the call back message and the public method name of the call back message after the user selects a line in the context menu. The $name property of the the menu line is included as the first parameter of the call back message.
  3. $control - This method is notified when the user selects a line in the context menu. The $control method sends the observer the call back method message and includes the $name of the menu line selected by the user as the first parameter in the call back message, and a reference to the selected menu as a second parameter.

Sample code for each of the context menu class methods is as follows:

  1. $addMenuLine(pName,pText,pbElipsis)

    ; $addMenuLine(pName,pText,pbElipsis)

    ; Append ... to the pText if pbElipsis is set to true. Default is false.
    Calculate Text as con(pText,pick(pbElipsis=1,'','...'))

    ; Add a menu line object to the menu instance.
    ; $add(cText,bEnabled,bChecked)
    Do $cinst.$objs.$add(Text,kTrue,kFalse) Returns rMenuLine

    ; Assign the name to the menu item.
    Do rMenuLine.$name.$assign(pName)

    ; Return a reference to the added menu item.
    Quit method rMenuLine

  2. $attachEventsObserver(prObserver,pCallBackMethodName)

    ; $attachEventsObserver(pfrObserver,pCallBackMethod)

    ; Store a reference to the the observer. Could be a window object or a window class instance.
    Set reference irObserver to pfrObserver.$ref

    ; Store the call back method name.
    Calculate iCallBackMethod as pCallBackMethod

    Quit method kTrue

  3. $control

    ; $control

    ; The $control method is automatically called if the user selects a menu line.
    ; Send a call back message to the observer. Include the name of the selected menu line name as the first parameter.
    Do irObserver.[iCallBackMethod]($cobj().$name)

    Quit method kTrue

Context Menu Window Object

The window object (field) which you want to add a context menu to must have the $contextmenu property set to the name of the context menu you want to instantiate. e.g. mContextMenu. If the menu class is in another library you would need to prefix the menu class name with the library name.

When the user right-clicks on the window object Omnis Studio sends a $event message to the window object. You need to trap On evOpenContextMenu in the $event method of the window object, attach the window object (or window class) as an observer, and then add menu line(s) to the context menu instance. Sample code is as follows:

; $event method of a window object. Automatically called when the user right-clicks on the field.

On evOpenContextMenu

; Add this field as an observer to be notified if the user selects a context menu line.
; Note: Sending $cinst as the observer would result in the call back message being sent to the window class method.
Calculate CallBackMethod as '$eventContextMenuLineSelected'
Do pContextMenu.$attachEventsObserver($cobj,CallBackMethod)


; Add menu lines to the context menu.
Do pContextMenu.$addMenuLine('pingMe','Send Test Ping Message')
Do pContextMenu.$addMenuLine() ;; Empty line.
Do pContextMenu.$addMenuLine('helloWorld',"Hello World",kTrue)

You also need to add the specified call back method to the window object. Sample code is as follows:

; $eventContextMenuLineSelected(pName)

; This method is called by the $control method of the context menu if the user selected a line in the context menu.

Switch low(pName)
   Case 'pingMe'
      OK message [sys(85)] (Icon) {Ping message received.}
   Case 'helloworld'
      OK message [sys(85)] (Icon) {Hello World!}
   Default
      OK message [sys(85)] (Icon) {Error. Switch/case not set up for [pName]}
End Switch

Quit method kTrue

That's all there is to it! I use this technique in all of my applications and love it. Just one menu class for an infinite number of context menu. All context menu lines are added on-the-fly. You can include If/Else logic if in your $event method to include/exclude menu lines based on the state of the window or the current user.

Press the Run Demo button in the StudioTips Browser to try out a context menu on-the-fly demo.

Sequence of Events

The summary of context menu on-the-fly events as they occur are as follows:

  1. The user right-clicks on the field.
  2. Omnis Studio opens an instance of mContextMenu. The instance is not yet visible to the user.
  3. Omnis Studio sends a $event message is sent to the window object (field).
  4. The $event method sends an $addEventsObserver($cfield,'$eventContextMenuLineSelected') message to the invisible instance of mContextMenu.
  5. mContextMenu sets the window object as the observer to receive a call message if the user selects a line in the context menu instance.
  6. The $event method sends a series of $addMenuLine(pName,pText,pbElipsis) messages to invisible instance of mContextMenu.
  7. mContextMenu adds thbe specified menu lines.
  8. Omnis Studio makes the context menu instance visible to the user.
  9. The user selected the menu line Hello World....
  10. Omnis Studio sends an $event message to the selected menu line.
  11. Since the menu line was added using notation it doesn't have an $event method Omnis Studio automatically passes the message to the $control method of the context menu instance.
  12. The $control method notify the events observer by sending an $eventContextMenuLineSelected($cobj().$name) message to the window object.
  13. The window object (field) receives the $eventContextMenuLineSelected message and based on pName takes the appropriate action.

Context Menu Cursors

One of the pitfalls of context menus is that the user might not realize a context menu exists.

How do you communicate the presence of a context menu to the user? You could add an audio message that says: Hey, this object has a context menu which you can use!, but that might get annoying to the user. Rephrase - that would be annoying.

You could add a tooltip to the field which says: Right-click for more options". The would still be annoying to the user, just less annoying.

The solution which I use it so change the cursor to a context menu cursor icon when the mouse passes over any field which has a context menu.

This is accomplished by setting the $cursor property of the field whenever you set to $contextmenu of the field.

I use one type of context menu cursor for text entry fields and another for non-text entry fields.

Omnis Studio doesn't include any context menu cursor icons so I created then in the icons editor. They are included with StudioTips. One is an I-bar style cursor for text entry fields, and the other is an arrow style cursor for non-text entry fields. The context menu of the cursor icon is blue so the user can notice it is different than the standard black MacOS context menu cursor.

Press the Run Demo button in the StudioTips Browser window to see what the context menu cursors look like.

The icons are stored in the #ICONS class of the tipsDocs library which is included with StudioTips. The cursors are located on the Custom Cursors page. For information on copying icons from one library to another refer to the Omnis Studio documentation.