Menus

To let users navigate within your apps and access options that you provide, you can add menus to the screens of your app. A menu contains a set of actions that a user might want to do on a specific screen in your app. For example, an app that displays a list of sports scores can include actions to view the details of a particular game, change the league that's displayed, or change the order that the scores are displayed in.

Menus let you hide certain options until they're needed. You probably don't want to show your users every option that's available in your app at the same time, because users might need an option only at a specific time. By using menus to organize the options, you can keep the main UI of your app neat and tidy.

There is a set of icons for BlackBerry 10 that you can use for menus in your apps. To download these icons, visit the UI Guidelines for BlackBerry 10.

Adding an action menu

You may have already seen an example of a menu in the Cascades framework. When you add actions to a screen in your app, the actions are placed automatically in the action menu (as well as on the action bar, if you specify the appropriate property). This menu appears on the right side of the action bar, and users can tap the menu to display all of the actions for the screen. To learn more about actions and the action menu, see Adding actions.


Screens showing a user tapping the menu to display the action menu.

Adding a context menu

A context menu (sometimes known as the cross-cut menu) is displayed when a user touches and holds a UI control in your app. This menu displays actions that are associated with that control. In an app that displays sports scores, you could add a context menu to each game that's displayed and include an action to view the details of that game.

You can add a context menu to any Cascades component that extends the Control class. This includes custom components that you create by extending the CustomControl class (because CustomControl inherits from Control). When the user touches and holds a control that has an associated context menu, the context menu is partially displayed. This partial menu appears from the right side of the screen and shows only the icons that are associated with each action. The user can then swipe the partial menu to the left to display the entire context menu.


Screens showing a partical context menu.


Screens showing an expanded context menu.

Users don't need to expand the partial context menu to select an action. They can simply select one of the icons that's displayed in the partial menu to perform the associated action. When a user touches an action icon in the partial menu, the selected action expands to show its title. When the user releases the touch, the selected action is executed.


Screen showing the title of an action on a partial context menu.

Users can also touch and hold a control to display the context menu, and then drag their finger directly to an action in the partial context menu to select that action. This behavior lets users interact quickly with actions on the context menu; a single touch interaction can be used to both open the context menu for a control and select an action from the menu.

You add actions to the context menu of a control by using an ActionSet. This class represents a group of actions, and contains a list of ActionItem objects. Each ActionItem represents an action in the context menu, and you can respond to the selection of an ActionItem in any way you want. You can also specify a title and subtitle for the ActionSet, and these are displayed at the top of the context menu when the full menu is displayed.


Diagram showing the contents of an ActionSet.

There isn't any inheritance of context menus for controls that are children of other controls. For example, if you add a context menu to a button in your app, and the button's parent container also includes a context menu, only the button's context menu is displayed when the user touches and holds the button.

Creating a context menu in QML

To add a context menu to a control in QML, you use the  contextActions list property. You add the ActionSet that contains your ActionItem objects to this list. When you add your ActionItem objects to the ActionSet, you use the actions list property to hold the actions. Here's how to create a blue Container with a context menu that includes three actions:

import bb.cascades 1.0
 
Page {
    content: Container {   
        Container {
            preferredWidth: 200
            preferredHeight: 200
            background: Color.Blue
             
            contextActions: [
                ActionSet {
                    title: "Action Set"
                    subtitle: "This is an action set."
                     
                    actions: [
                        ActionItem {
                            title: "Action 1"
                        },
                        ActionItem {
                            title: "Action 2"
                        },
                        ActionItem {
                            title: "Action 3"
                        }
                    ]
                } // end of ActionSet   
            ] // end of contextActions list
        } // end of blue Container
    } // end of top-level Container
} // end of Page

The actions list property of ActionSet is a default property, meaning that you don't need to include the actions list property explicitly. You can simply start adding ActionItem objects to the ActionSet directly, allowing you to save some space and indents in your code.

Here's what the ActionSet from the example above looks like without the actions list property. Note that when you use this approach, you no longer need to separate the ActionItem objects with commas.

ActionSet {
    title: "Action Set"
    subtitle: "This is an action set."
     
    ActionItem {
        title: "Action 1"
    }
    ActionItem {
        title: "Action 2"
    }
    ActionItem {
        title: "Action 3"
    }
}

The ActionItem objects that you add to an ActionSet are the same ones that you can add as actions on a screen. So, you can do similar things when a user selects an action. You can change properties of other controls, display new screens, and so on. You can also add images to the actions, and these images are visible when the context menu is partially displayed.

Here's how to create a context menu for an ImageView that contains three actions. Each action includes a custom image and starts a different animation for the control.

import bb.cascades 1.0
 
Page {
    content: Container {
        layout: DockLayout {}
        ImageView {
            // Use layout properties to center the image on the
            // screen
            layoutProperties: DockLayoutProperties {
                horizontalAlignment: HorizontalAlignment.Center
                verticalAlignment: VerticalAlignment.Center
            }
            imageSource: "asset:///images/evil_smiley_small.png"
             
            animations: [
                // A translation animation that moves the image
                // downwards by a small amount
                TranslateTransition {
                    id: translateAnimation
                    toY: 150
                    duration: 1000
                },
                 
                // A rotation animation that spins the image
                // by 180 degrees
                RotateTransition {
                    id: rotateAnimation
                    toAngleZ: 180
                    duration: 1000
                },
                 
                // A scaling animation that increases the size
                // of the image by a factor of 2 in both the
                // x and y directions
                ScaleTransition {
                    id: scaleAnimation
                    toX: 2.0
                    toY: 2.0
                    duration: 1000
                }
            ]
             
            contextActions: [
                ActionSet {
                    title: "Animations"
                    subtitle: "Choose your animation"
                     
                    // This action plays the translation animation
                    ActionItem {
                        title: "Slide"
                        imageSource: "asset:///images/slide_action.png"
                         
                        onTriggered: {
                            translateAnimation.play();
                        }
                    }
                     
                    // This action plays the rotation animation
                    ActionItem {
                        title: "Spin"
                        imageSource: "asset:///images/spin_action.png"
                         
                        onTriggered: {
                            rotateAnimation.play();
                        }
                    }
                     
                    // This action plays the scaling animation
                    ActionItem {
                        title: "Grow"
                        imageSource: "asset:///images/grow_action.png"
                         
                        onTriggered: {
                            scaleAnimation.play();
                        }
                    }
                } // end of ActionSet
            ] // end of contextActions list
        } // end of ImageView
    } // end of Container
} // end of Page

Creating a context menu in C++

In C++, you create ActionItem objects and add them to an ActionSet. Then, you call Control::addActionSet(), which adds the ActionSet to the control as a context menu.

Here's how to create a Container with a context menu. The context menu includes an ActionSet with three actions.

// Create the Container and ActionSet
Container* contextContainer = new Container();
ActionSet* actionSet = ActionSet::create()
        .title("Context menu")
        .subtitle("Select an action.");
 
// Create the ActionItem objects and add them to the ActionSet
ActionItem* action1 = ActionItem::create()
        .title("First action");
ActionItem* action2 = ActionItem::create()
        .title("Second action");
ActionItem* action3 = ActionItem::create()
        .title("Third action");
actionSet->add(action1);
actionSet->add(action2);
actionSet->add(action3);
 
// Add the ActionSet to the Container
contextContainer->addActionSet(actionSet);

Adding an application menu

You might want to include options in your apps that aren't associated with any particular screen or UI control, but that apply to the entire app and can be accessed no matter where a user is within the app. For example, you might want to provide a settings option for users to specify application-wide preferences, or a help option for users to learn how to use your app. To display these types of options, you can use the application menu.


Screen showing an application menu.

The application menu is displayed when a user swipes down from the top of the screen. You should consider using this menu for options that are important but seldom used. This menu supports the same types of actions as the action menu and context menu. Each action has properties such as title and image, and you can respond in any way you'd like when a user taps an action (for example, by displaying a new screen). The application menu is represented by the Menu class, and each action is an ActionItem object.


Screens showing swiping down from the top of the screen to display the application menu.

Here's how to add an application menu that contains three actions, in QML. You use a property called Menu.definition and specify its value using a MenuDefinition object. This object contains the actions that you want to include in the application menu, all of which are included in the  actions list property. In this code sample, each action changes the text in a TextField that's displayed on the screen. Note that if you don't specify your own image for an action, a default image is provided for you.

import bb.cascades 1.0
 
Page {
    // Add the application menu using a MenuDefinition
    Menu.definition: MenuDefinition {
     
        // Specify the actions that should be included in the menu
        actions: [
            ActionItem {
                title: "Action 1"
                imageSource: "images/actionOneIcon.png"
                 
                onTriggered: {
                    textField.text = "Action 1 selected!"
                }
            },
            ActionItem {
                title: "Action 2"
                imageSource: "images/actionTwoIcon.png"
                 
                onTriggered: {
                    textField.text = "Action 2 selected!"
                }
            },
            ActionItem {
                title: "Action 3"
                 
                onTriggered: {
                    textField.text = "Action 3 selected!"
                }
            }
        ] // end of actions list
    } // end of MenuDefinition
     
    Container {
     
        // Add a text field to display which action is selected
        TextField {
            id: textField
            text: "No action selected."
        }
    }
} // end of Page

As a best practice, you should add an application menu to the top-level control in your app. This approach makes sense because the application menu applies to your entire app instead of just a single screen or view. In the code sample above, the menu is added to the top-level Page, but it could just as easily be added to a NavigationPane or TabbedPane.

import bb.cascades 1.0
 
NavigationPane {
    Menu.definition: MenuDefinition {
        actions: [
            ActionItem {
                ...
            },
            ActionItem {
                ...
            }
        ]
    }
     
    Page {
        ....
    }
}

Here's how to add an application menu in C++. You create a Menu object and add ActionItem objects to it. Then, you retrieve an instance of the application by using Application::instance() and call setMenu() to set the menu.

// Create the application menu
Menu *menu = new Menu;
 
// Create the actions and add them to the menu
ActionItem *actionOne = ActionItem::create()
                        .title("Action 1");
ActionItem *actionTwo = ActionItem::create()
                        .title("Action 2");
menu->addAction(actionOne);
menu->addAction(actionTwo);
 
// Set the menu of the application
Application::instance()->setMenu(menu);

You can display a maximum of five actions on the application menu. If you add more than five actions, the extra actions aren't displayed. By default, when you add actions to the application menu, the first action that you add appears on the left side of the menu, and the second action appears on the right side. Any remaining actions appear in the center of the menu.


Screens showing how actions are placed on an application menu.

Using the Help action and Settings action

When you include an application menu in your app, it's considered a best practice for the left-most action on this menu to provide some type of help or other information about your app. It's also typical for the right-most action to provide application settings or options.

Cascades makes it easy to follow this convention by providing special classes for each of these types of actions. You can use a HelpActionItem to provide access to help or other information, and you can use a SettingsActionItem for application-wide settings. Each of these classes has a default image and title that's displayed if you don't specify an image or title for the action, and they both appear automatically in their respective locations on the menu (the Help action on the left and the Settings action on the right).


Screens showing the icons for the Help and Settings actions.

Here's how to add a Help action and Settings action to the application menu, in QML, by using the helpAction and properties of a Menu. If you include both the Help action and Settings action, you can only have a maximum of three additional actions on the menu.

import bb.cascades 1.0
 
Page {
    Menu.definition: MenuDefinition {
        // Add a Help action
        helpAction: HelpActionItem {}
         
        // Add a Settings action
        settingsAction: SettingsActionItem {}
         
        // Add any remaining actions
        actions: [
            ActionItem {
                title: "Action 1"
            }
        ]
    }
}

Here's how to accomplish the same thing in C++.

// Create the application menu
Menu *menu = new Menu;
 
// Create the actions to add to the application menu
HelpActionItem *help = new HelpActionItem;
SettingsActionItem *settings = new SettingsActionItem;
ActionItem *actionOne = ActionItem::create()
                         .title("Action 1");
 
// Create the application menu and add the actions
menu->setHelpAction(help);
menu->setSettingsAction(settings);
menu->addAction(actionOne);
 
// Set the menu of the application
Application::instance()->setMenu(menu);

Adding a custom menu

The predefined menu types that are included in Cascades, such as the action menu and context menu, let you easily create menus, populate them with items, and add the menus to your apps. These menus use the same visual style and behavior as menus in core BlackBerry 10 apps, making it easy to match the style and presentation of your app with these other apps.

However, you might want a bit more control of your menus and how they're presented in your app. You can create a menu with a custom look and feel, and this menu can take advantage of context-sensitive logic that performs actions based on a specific type of data. For example, you might want to display your menu in a radial style and include actions that invoke other apps (such as the Calendar application or BBM). To learn more about invoking other apps, see App integration.

Screen showing a menu with a custom appearance.

To create a custom menu, you can use the  MenuManager class. You specify the data that you want the menu to apply to, and MenuManager creates the menu automatically and populates it with relevant actions for that data. Then, you can retrieve the menu that was created, access its items, set additional properties, and so on. This class is available only in C++.

You can specify the data that you want the menu to apply to by using three primary types of information: the URI, the MIME type, or the raw data itself. Each of these types has its own setter function in MenuManager ( setUri() setMimeType(), and  setData()). For example, if you specify a URI of "file://<file_path>", MenuManager creates a menu with actions that apply to a file on the device's file system. Or, if you specify a MIME type of "image/jpeg", the MenuManager creates a menu with actions that apply to a JPEG image.

When you specify the data that the menu applies to, you need to provide either the URI or the MIME type (or both). The raw data is usually optional, depending on the MIME type that you provide. If you choose to provide the data, it's used to populate a  MenuItemInvokeParams object that contains all of the required information to invoke a target application. You can use this MenuItemInvokeParams to invoke the target application right away, with the data to act on.

In some cases, the data that you provide is used to create the menu. For example, if you specify a MIME type of "application/vnd.blackberry.string.phone" (which represents a phone number), you should include the selected phone number as the raw data. This data is used to determine if the phone number is related to a contact. If so, contact-related actions (such as "View Contact") are added to the menu.

In addition to the primary types, there are other types of information that you can provide to a MenuManager to help it create a suitable menu for your data. For example, you can use the setTargetTypes() function to indicate which types of invocation targets should be considered when building the menu. Invocation targets can be processes such as applications, viewers, or services. To learn more about invocation targets, see App integration.

Here's how to create a simple menu using MenuManager that's designed to apply to MPEG video data. The  populateMenu() function is used to request that the menu be populated with items, and the MenuManager emits the  finished() signal after this population is complete. You can retrieve the created menu by calling  menu().

// Create the menu manager and specify the type of data
// that the menu should apply to
bb::system::MenuManager *manager = new MenuManager;
manager->setMimeType("video/mpeg");
 
// Connect the manager's finished() signal to a slot function.
// Make sure to test the return value to detect any errors.
bool connectResult = QObject::connect(manager, SIGNAL(finished()),
                                      this, SLOT(onFinished()));
// If any Q_ASSERT statement(s) indicate that the slot failed to
// connect to the signal, make sure you know exactly why this has
// happened. This is not normal, and will cause your app to stop
// working!!
Q_ASSERT(connectResult);
 
// Indicate that the variable connectResult isn't used in the rest
// of the app, to prevent a compiler warning.
Q_UNUSED(res);
 
// Request that the menu is populated with relevant items
if (manager->populateMenu() != true) {
    // Handle any errors that occurred while the menu was
    // being populated
}

// Create a slot function to handle the finished() signal
Q_SLOT void onFinished()
{
    // Check for any errors
    if (manager->error() != MenuManagerError::None) {
        // Handle the error
    } else {
        // Retrieve the menu
        bb::system::Menu *theMenu = manager->menu();
    }
}

After you populate a menu and retrieve it using menu(), you receive a  Menu object that contains relevant menu items for the data you specified. Each menu item is represented by a  MenuItem object, and you can retrieve a list of these items by calling  items(). You can also retrieve the title and subtitle of the menu by calling  title() and  subtitle(), respectively.

Each MenuItem object in the menu represents an action to take when that item is selected. Selecting a MenuItem can result in one of the following:

  • An invocation target is invoked using the specified data, MIME type, or URI. You can call invoke().isValid() to determine if a MenuItem represents an invocation target, and you can use the  MenuItemInvokeParams class to determine the appropriate invocation parameters to pass to the target.

  • A submenu is displayed that provides additional menu items to choose from. You can call subMenu().isValid() to determine if a MenuItem contains a submenu.

Some of the predefined menu types in Cascades actually use an underlying MenuManager to populate their items. For example, consider the Music app on a device that's running BlackBerry 10. If you touch and hold a song in that app, a context menu is displayed that includes a Share action. This action is provided by an underlying MenuManager, and when you choose this action, a list of targets for the Share action is displayed.

If you're creating a Cascades app, it's usually a good idea to use the predefined menu types as much as possible. If you use the MenuManager to create a custom menu, you receive a Menu that's populated with relevant MenuItem objects, but it's up to you to determine how to present the menu to users in your app.

Last modified: 2013-12-21

comments powered by Disqus