Your BlackBerry 10 app Menu just got serious! (with Sheet, NavigationPane & Tabs)

Cascades

Guest post from Shadab R. – Ed.

bb10-app-menu-1 bb10-app-menu-2

Many applications require the Menu UI component. This is the global menu within the app which is visible when the user swipes down on the top edge of the screen. It is the place to keep global ActionItem objects like Info, Settings or Help. However, it is easy to get confused on how to actually display these resulting pages and which components to use for them; namely, between Sheet & NavigationPane. Hence, we decided to provide you a sample app on how to actually integrate and tie all these components together, especially if you had a TabbedPane with NavigationPane on the Menu.

Key differences between Sheet & NavigationPane

Before we start exploring the different options with these two UI components, we should recap the general Cascades UI guidelines behind them (respective sections are hyperlinked inline).

  • Sheet – This should be used whenever there is a break in the user’s flow. That is, anything that takes the user away from their current action(s) and navigation flow; the Sheet slides in from the bottom. Typically, a sheet is presented for things such as sending emails, taking user credentials and of course, for application menu items such as Info, Help or Settings! This is typically the easiest way to implement action items on the application menu.
  • NavigationPane – This should be used whenever there is any drill down navigation involved. You get the nice sliding transition (from the right) for free, along with the swipe gestures for navigating (peek!). In terms of the application menu, you might want to use NavigationPane instead of Sheet if you want to maintain the stack like navigation for your app. Sometimes, you might also want it if you have additional drill down navigation lists within your settings page (note that you can also have a navigation pane within a sheet). However, if you are in fact pushing navigation page objects through the application menu, especially with a TabbedPane, there are certain edge cases and work around that you would have to watch out for (mentioned at the end).

For the purpose of this blog post and the github sample code provided, we are showing the implementation of both the components. However, only one of these approaches should be chosen for the application menu and it should be a consistent behavior.

Related BlackBerry 10 Tips:

  • If you swipe back by touching the navigation pane’s back arrow, you can pop back all the pages in your navigation stack (as opposed to having to press back multiple times!).
  • The primary contents or pages of the app should not be presented through the application Menu; this area is mainly designed for items which are rarely used but accessible from anywhere in the app.

Show me some code already!

Enabling the menu is the first thing you would have to worry about. If using the navigation pane, right before we are pushing the settings page, we have to disable the application menu (to be enabled back when that page is popped). This is to ensure that there is no way to push the same settings page on top of itself.

    Menu.definition: MenuDefinition {
        id: menu
        actions: [
            ActionItem {
                title: "Info"
                imageSource: "images/ic_info.png"
                onTriggered: {
                    console.log("Info: ActionItem : onTriggered");
                    // For InfoPage, we will use Sheet
                    infoSheet.open();
                }
            }
        ]
        settingsAction: SettingsActionItem {
            onTriggered: {
                console.log("Settings: ActionItem : onTriggered");
                // For Settings, we will use NavigationPane
                var settingsPageObj = settingsPage.createObject();
                Application.menuEnabled = false;
                currentNavigationPane.push(settingsPageObj);
            }
        }
    } // MenuDefinition

And how are these infoSheet and settingsPage objects created? They can be included as attachedObjects. To show some code encapsulation, we used separate QML files here for both. They can be defined inline as well, if the respective code is within them is too trivial (but why cluster the code, right?)

    // Define the Page and Sheet objects that would be used from the Application menu
    attachedObjects: [
        ComponentDefinition {
            id: settingsPage
            source: "SettingsPage.qml"
        },
        Sheet {
            id: infoSheet
            // The following page refers to the InfoPage.qml
            InfoPage {
                id: infoPage
                // Handle the custom signal from InfoPage.qml
                onDone : {
                    infoSheet.close();
                }
            }
        }
    ] // attachedObjects

Note that the onDone signal is actually a custom signal emitted from InfoPage.qml

Page {
    id: infoPage

    // Custom signal for notifying that this page needs to be closed
    signal done ()

    titleBar: TitleBar {
        title: "Info"
        dismissAction: ActionItem {
            title: "Close"
            onTriggered: {
         // Emit the custom signal here to indicate that this page needs to be closed
         // The signal would be handled by the page which invoked it
                infoPage.done();
            }
        }
    }

    Container {
		// The rest of the Page is defined here
    }
} // Page

If you were using a sheet, this would have been enough. But if you pushed a Page using NavigationPane (like we did for this Settings page), you would have to enable the menu back on again (remember, we disabled it before we pushed it on).

TabbedPane {
    // Everything else

    Tab {
        id: tab1Handle
        title: qsTr("Tab 1")
        description: "Tab 1 Description"
        property alias navHandle: nav1

        NavigationPane {
            id: nav1

            onPopTransitionEnded: {
                console.log("Tab1 : NavigationPane - onPopTransitionEnded");
                Application.menuEnabled = true;
            }

            Page {
                // Content goes here . . .
            }
        }
    } // Tab1
    // Everything else . . .
}

bb10-app-menu-3 bb10-app-menu-4

Now, to finally address some of the concerns if you are using NavigationPane from the Menu. It should be noted that Cascades doesn’t allow adding NavigationPane directly to the global TabbedPane or to a Page object (Page can be added to a NavigationPane, but not the other way around).These limitations have been imposed by Cascades intentionally, to enforce certain UI behaviours. But we do need a NavigationPane object to push a Page onto. Hence, in this case, you are left with having to add a NavigationPane object for each and every Tab that we are showing. We also have to keep track of the handle of the currently-active Tab, just so that we know which NavigationPane object we should be pushing the new Page onto.

    property NavigationPane currentNavigationPane: tab1Handle.navHandle

    // Update the NavigationPane handle when the Tab is changed
    onActiveTabChanged: {
        currentNavigationPane = activeTab.navHandle
    }

For your coding pleasure

Phew! Yes, that may have seemed like a long blog post but it is actually much simpler when you look at the complete source files. Feel free to check out the actual code project on github right here. Compile, deploy and get going with the menus of those awesome Cascades apps of yours!

About Shadab Rashid

Application Development Consultant; passionate about user experience, consumer software products, entrepreneurship, music and people! @surashid

Join the conversation

Show comments Hide comments
+ -
blog comments powered by Disqus