Drill down

You can use a  NavigationPane to structure multiple screens in a hierarchy and let users navigate between them. A NavigationPane stores screens in a stack. Only the screen on the top of the stack is displayed, and the other screens are hidden. Each screen in a NavigationPane is a  Page object, and has content and actions associated with it.

To add a new screen to the top of the stack, you call  NavigationPane::push(). This function emits the  topChanged() signal to let you know that the screen on the top of the NavigationPanestack changed; you can respond to this signal if you want to. The new screen doesn't appear immediately; instead, it animates by sliding into position. The  pushTransitionEnded() signal is emitted to let you know that this animation is finished.

To remove the screen that's on top of the stack, you call  NavigationPane::pop(). This function pops the top screen off of the stack, which hides it from view. The screen still exists, and you can push it on to the stack later in your app to display it again. Similar to push(), this function emits the topChanged() signal, and the screen that's popped is animated by sliding off the screen. The popTransitionEnded() signal is emitted when the animation is finished.


Diagram showing the push and pop of screens

By default, each screen that you add to a NavigationPane includes a back button. This button is located on the left side of the action bar and pops the current screen off of the stack. The button has a predefined image and text, but you can customize it by using the NavigationPaneProperties class. To learn how, see Specifying pane properties.


Screen showing the back button

If you don't want to display back buttons on screens that you add to the NavigationPane, you can use the backButtonsVisible property in QML or the setBackButtonsVisible() function in C++ to show or hide the buttons. Be careful when you hide the default back buttons, though; to let users go back to the previous screen, you need to provide and implement your own action that accomplishes this.

Using NavigationPane in QML

There are two ways that you can add screens to a  NavigationPane in QML. You can define each screen as a custom QML component and then add them to the NavigationPane by using a ComponentDefinition. Or, you can add the screens to the attached objects list and push them on to the NavigationPane stack when you need them.

Either of these approaches can be an efficient way to store your screens, so you should choose the one that makes the most sense for your app. For example, if your screens are simple and don't require many advanced layouts or formats, you might choose to create them directly in the attached objects list. This way, you can keep all of your QML code in one file. Alternatively, if your screens are more complex, you might want to create a separate QML file for each screen and keep all of the advanced implementation details in that file.

Defining screens as attached objects

You can use the attachedObjects list of a NavigationPane and add Page objects directly to this list. When you want to display these screens, you call  push() and specify the id of the screen that you want to display. To learn more about the attachedObjects list and its other uses, see QML and C++ integration.

When you use attachedObjects to store your screens, you can have only one instance of each screen on the NavigationPane stack at one time. For example, if you push a Page on to the stack to display it, you can't push the same Page on to the stack again until you pop the first instance off of the stack.

Here's how to create a NavigationPane with one initial Page and two Page objects in the attachedObjects list. The initial Page contains two  Button controls that display each Page object from attachedObjects when they're tapped. Each Page that's pushed on to a NavigationPane stack also includes a back button that's added automatically.

import bb.cascades 1.0
 
NavigationPane {
    id: navigationPane
     
    // The initial page
    Page {
        content: Container {
            Button {
                text: "Display first Page"
                 
                onClicked: {
                    navigationPane.push(firstPage);
                }
            }
             
            Button {
                text: "Display second Page"
                 
                onClicked: {
                    navigationPane.push(secondPage);
                }
            }
        } // end of Container
    } // end of Page
     
    attachedObjects: [
        Page {
            id: firstPage
            content: Container {
                Label {
                    text: "First attachedObjects Page"
                }
            }
        },
        Page {
            id: secondPage
            content: Container {
                Label {
                    text: "Second attachedObjects Page"
                }
            }
        } // end of Page
    ] // end of attachedObjects list
} // end of NavigationPane

Defining screens as custom QML components

If you want to define additional screens in separate QML files and then create them dynamically in your app, you can use a ComponentDefinition object. You can define each screen as a custom component in its own QML file, and specify the name of the QML file as part of the ComponentDefinition. Then, when you want to display the screen, you create an object that represents the screen by calling createObject() and push the object on to the NavigationPanestack. To learn more about custom components, see Custom QML components.

To use a ComponentDefinition object, you need to include it in an attachedObjects list. Here's how to create a NavigationPane with an initial  Page. The Page includes a Push action that creates a new screen when it's selected. The new screen uses a custom component that's defined in a file called myPage.qml. The new screen is then pushed on to the NavigationPane stack and displayed. Each new screen that's pushed includes a Push action to create another new screen, as well as a Pop action that removes the screen from the stack and displays the previous screen again. The backButtonsVisible property is used to hide the default back button on each screen. To learn more about actions, see Adding actions.

// main.qml
 
import bb.cascades 1.0
 
NavigationPane {
    id: navigationPane
    backButtonsVisible: false
     
    // Create the initial screen
    Page {
        content: Container {
            Label {
                text: "Initial page"
            }
        }
         
        actions: [
            // Create the "Push" action
            ActionItem {
                title: "Push"
                ActionBar.placement: ActionBarPlacement.OnBar
                 
                // When this action is selected, create an object
                // that's based on the ComponentDefinition below,
                // and then push it on to the stack to display it
                onTriggered: {
                    var newPage = pageDefinition.createObject();
                    navigationPane.push(newPage);
                }
            }
        ]
         
        attachedObjects: [
            // Create the ComponentDefinition that represents the
            // custom component in myPage.qml
            ComponentDefinition {
                id: pageDefinition
                source: "myPage.qml"
            }
        ]
    } // end of Page
} // end of NavigationPane

// myPage.qml
 
import bb.cascades 1.0
 
Page {
    content: Container {
        Label {
            text: "Dynamically pushed page"
        }
    }
      
    actions: [
        // Create the "Push" action
        ActionItem {  
            title: "Push"
            ActionBar.placement: ActionBarPlacement.OnBar
              
            // When this action is selected, create an object
            // that's based on the ComponentDefinition in
            // main.qml, and then push it on to the stack to
            // display it
            onTriggered: {
                var newPage = pageDefinition.createObject();
                navigationPane.push(newPage);
            }
        },
         
        // Create the "Pop" action
        ActionItem {  
            title: "Pop"
            ActionBar.placement: ActionBarPlacement.OnBar
            
            // When this action is selected, pop the current
            // Page off of the stack
            onTriggered: { 
                navigationPane.pop()
            }
        }
    ] // end of actions list
} // end of Page

When you pop a screen off of the NavigationPane stack, either by using the default back button or by calling pop(), the Page object that represents the screen still exists and memory is still allocated for it. When your app is finished with a screen and doesn't need it anymore, it's a good idea to delete the Page. An easy way to handle this is by responding to the  popTransitionEnded() signal, which is emitted by a NavigationPane when a Page is popped off of the stack. The QML signal handler onPopTransitionEnded() includes the Page that was popped as a parameter, and you can call destroy() to free the memory that was allocated for the Page.

NavigationPane {
    Page {
        ...
    }
     
    onPopTransitionEnded: {
        page.destroy();
    }
}

Remember that you should delete a Page only when you know you won't use it in your app again. In the code sample above, deleting the popped Page won't cause too much trouble. The createObject() function is called each time a new Page is pushed on to the NavigationPane stack, and this function creates a new Page object to push each time. For this reason, a new Page is always available even if the previous Page was deleted when it was popped off of the stack.

However, in some cases, the Page that you delete won't be available to push on to the NavigationPane stack again, and you won't be able to use the Page again in your app. For example, if you define a Page in the attachedObjects list (as illustrated in the previous section, "Defining screens as attached objects") and subsequently delete the Page when it's popped, the Page is deleted entirely and isn't available to push on to the stack later in your app.

Using NavigationPane in C++

Unlike in QML, in C++ you don't need to use the attachedObjects list to handle your  Page objects or ComponentDefinition objects. You can simply create your  NavigationPane object and Page objects, and then use  push() and  pop() to arrange the Page objects on the NavigationPane stack.

Here's how to create a NavigationPane with a single  Button that displays a new Page when it's tapped. Both the NavigationPane (mRoot) and the new Page (mNewPage) are declared in a header file so they're accessible to the handlePushButtonClicked() slot. Notice that the popTransitionEnded() signal is connected to its own signal handler so that the Page objects can be deleted when they're popped off of the NavigationPane stack.

// header file
 
...
public slots:
    void handlePushButtonClicked();
    void handlePopTransitionEnded(bb::cascades::Page* page);
     
private:
    NavigationPane* mRoot;
    Page* mNewPage;
     
...

// source file
 
...
 
App::App() {
    // Create the UI elements
    mRoot = new NavigationPane;
    Page* initialPage = new Page;
    Container* rootContainer = new Container;
    Button* pushButton = Button::create().text("Tap for a new page");
 
    // Connect the button's clicked() signal to a slot function.
    // 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
    bool res = QObject::connect(pushButton, SIGNAL(clicked()), this,
                                SLOT(handlePushButtonClicked()));
    
    // This is only available in Debug builds
    Q_ASSERT(res);
     
    // Connect the navigation pane's popTransitionEnded() signal
    // to a slot function so that the new page can be deleted
    res = QObject::connect(mRoot, 
                           SIGNAL(popTransitionEnded(
                               bb::cascades::Page*)),
                           this,
                           SLOT(handlePopTransitionEnded(
                               bb::cascades::Page*)));
    
    // This is only available in Debug builds
    Q_ASSERT(res);
    
    // Since the variable is not used in the app, this is added
    // to avoid a compiler warning
    Q_UNUSED(res);
     
    // Add the UI elements and display the initial screen
    rootContainer->add(pushButton);
    initialPage->setContent(rootContainer);
    mRoot->push(initialPage);
 
    // Create the second screen
    mNewPage = new Page;
    mNewPage->setContent(Label::create("This is a new page."));
 
    setScene(mRoot);
}
 
// A slot function for the button's clicked() signal
void App::handlePushButtonClicked()
{
    mRoot->push(mNewPage);
}
 
// A slot function for the popTransitionEnded() signal
void App::handlePopTransitionEnded(bb::cascades::Page* page)
{
    delete page;
}

Specifying pane properties

You can use NavigationPaneProperties to specify properties for each screen that you add to a NavigationPane. In particular, you can define the appearance and behavior of the back button that's added automatically to each screen that you push on to the NavigationPane stack. You can change what happens when users tap the back button, and you can also change the text and image that appear on the button.


Screen showing a customized back button

When you customize the back button using NavigationPaneProperties, the default functionality that pops the current screen off of the stack isn't provided. You need to provide your own implementation either by implementing the onTriggered() signal handler (in QML) or connecting the triggered() signal to a slot function (in C++).

Using NavigationPaneProperties for a NavigationPane is very similar to using child layout properties for a layout. For example, when you use a  StackLayout for a container in your app, you must specify the layout properties of each child control in that container using StackLayoutProperties. Similarly, when you use a NavigationPane, you must specify the pane properties of each  Page in the NavigationPane using NavigationPaneProperties.

To specify NavigationPaneProperties for a Page, you use the paneProperties property. To change the behavior of the back button, you use the backButton property and provide the new action that you want. In QML, here's how to create a NavigationPane with two Page objects, one Page as the initial screen of the NavigationPane and the other Page defined in the attachedObjects list. The second Page includes a custom back button, which includes a different title and image. Importantly, this custom back button also implements the onTriggered() signal handler to pop the screen off of the NavigationPane stack.

import bb.cascades 1.0
 
NavigationPane {
    id: navPane
     
    Page {
        content: Container {
            Label {
                text: "This is the first Page."
            }
             
            Button {
                text: "Tap to display the second Page"
                 
                onClicked: {
                    navPane.push(secondPage);
                }
            }
        } // end of Container
    } // end of Page
     
    attachedObjects: [
        Page {
            id: secondPage
            content: Container {
                Label {
                    text: "This is the second Page."
                }
            }
             
            paneProperties: NavigationPaneProperties {
                backButton: ActionItem {
                    title: "Pop"
                    imageSource:
                        "asset:///images/customBackButtonImage.png"
                     
                    onTriggered: {
                        navPane.pop();
                    }
                }
            } // end of NavigationPaneProperties
        } // end of Page
    ] // end of attachedObjects
} // end of NavigationPane

To do the same thing in C++, you can use the Page::setPaneProperties() function and provide a NavigationPaneProperties object that includes your custom back button. To create the back button, you can use an ActionItem whose  triggered() signal is connected to the pop() function of the NavigationPane.

// Create the custom back button. The navigationPane variable
// represents the NavigationPane object that contains your Page
// objects.
ActionItem *customBackButton = ActionItem::create()
                                .title("Pop")
                                .imageSource(
                                  "images/customBackButtonImage.png")
                                .onTriggered(
                                  navigationPane, SLOT(pop()));
 
// Create the pane properties and specify the custom back button
NavigationPaneProperties *npp = new NavigationPaneProperties;
npp->setBackButton(customBackButton);
 
// Add the pane properties to a page. The myPage variable
// represents the Page object that you want to add the
// back button to.
myPage->setPaneProperties(npp);

Peeking

Peeking is a feature that allows users to look (or peek) at the screen underneath the screen that's currently displayed. You can use this feature to provide an alternative to tapping the back button in your app. A user can swipe to the right on a screen, or touch and drag the screen to the right, to display a screen that's beneath the current screen in a NavigationPane. Depending on when the user releases their finger, either the top screen stays displayed at the top of the NavigationPane stack or it is popped and the screen underneath is displayed.


Screen showing the peek feature when a user swipes from the left-edge of the screen toward the center.

In QML, you use the peekEnabled property of a NavigationPane to specify whether peeking is enabled. By default, peeking is enabled (peekEnabled is true).

import bb.cascades 1.0
 
NavigationPane {
    peekEnabled: true
     
    Page {
        ...
    }
}

In C++, you use the setPeekEnabled() function:

NavigationPane *navigationPane = new NavigationPane;
navigationPane->setPeekEnabled(false);
 
...

It's important to note that users can always peek at an underlying screen in a NavigationPane by touching and dragging the back button on a screen (if a back button is displayed on the screen). The peekEnabled property controls only the ability to peek by dragging or swiping the content area of a screen.

Peeking by using the back button works differently than peeking using the content area. While peeking by using the content area displays the previous screen in the NavigationPane stack, peeking using the back button displays the first screen on the stack (that is, the screen that's on the bottom of the stack). If a user peeks using the back button and the current screen is popped off of the stack, then all intermediate screens between the current screen and the bottom screen are also popped.

If the NavigationPane is inside a  TabbedPane and a user peeks using the back button, then part of the tab menu is also displayed along with the bottom screen.

Navigating to a specific screen in a NavigationPane

NavigationPane stores  Page objects in a stack, which typically only lets you access elements in order. For example, if you push four objects (numbered 1 through 4) on to the stack in order, a normal stack structure wouldn't let you access object 2 directly. First, you need to pop object 4 off of the stack, and then pop object 3, to access object 2. However, when you arrange screens on a NavigationPane stack in your app, it might be useful to navigate directly to a specific screen in the stack. You might create a Settings screen that includes several levels of options, and it would be helpful to provide the ability to return to the main screen at any time.

Fortunately, a NavigationPane provides this type of direct navigation. You can use the navigateTo() function to jump directly to the specified Page in the NavigationPane stack. This function pops the current Page off of the stack, and it pops any intermediate Page objects that are between the current Page and the target Page in the stack. The function returns the popped Page objects in a list, which makes it easy to delete the objects or perform any other clean-up operations.

Here's how to navigate directly to a Page in QML. This code sample pushes four Page objects on to the NavigationPane stack and provides an action on the fourth Page that navigates directly to the first Page.

import bb.cascades 1.0
 
NavigationPane {
    id: navPane
     
    Page {
        id: firstPage
        content: Container {
            Label {
                text: "This is page 1."
            }
             
            onCreationCompleted: {
                // When the container has been created, push three
                // Page objects onto the NavigationPane stack
                navPane.push(secondPage);
                navPane.push(thirdPage);
                navPane.push(fourthPage);
            }
        } // end of Container
    } // end of Page
     
    attachedObjects: [
        Page {
            id: secondPage
        },
        Page {
            id: thirdPage
        },
        Page {
            id: fourthPage
            content: Container {
                Label {
                    text: "This is an added page."
                }
            }
             
            actions: [
                ActionItem {
                    ActionBar.placement: ActionBarPlacement.OnBar
                    title: "To page 1"
                     
                    // When the action is selected, navigate directly
                    // to the first Page
                    onTriggered: {
                        navPane.navigateTo(firstPage);
                    }
                }
            ]
        } // end of Page
    ] // end of attachedObjects
} // end of NavigationPane

To do the same thing in C++, you simply call navigateTo() whenever you need to jump back to a previous page in the NavigationPane.

// Create the navigation pane and pages
NavigationPane *navPane = new NavigationPane;
Page *firstPage = new Page;
Page *secondPage = new Page;
Page *thirdPage = new Page;
Page *fourthPage = new Page;
 
// Add the pages to the navigation pane
navPane->push(firstPage);
navPane->push(secondPage);
navPane->push(thirdPage);
navPane->push(fourthPage);
 
...
 
// Return to the first page in the navigation pane
navPane->navigateTo(firstPage);

Last modified: 2013-12-21



Got questions about leaving a comment? Get answers from our Disqus FAQ.

comments powered by Disqus