Getting Started with BrightScript Screen Navigation

A common task for BrightScript developers is implementing the screen navigation that underlies the user interface flow in their Roku channels.  In this article we will explore some basic BrightScript screen navigation techniques.  When you have finished reading and played with the sample channel and dug through the source code, you’ll have a good understanding of BrightScript screen navigation.

The Screen Stack

An important fundamental concept in BrightScript navigation is the screen stack.  The Roku device maintains an internal stack of screens.  Whenever a screen is created, it is added to the top of the stack.  All remote control events are sent to the screen at the top of the screen stack.  These events are sent to your BrightScript code as messages handled by the message loops that you code.

From BrightScript, screens are added to the top of the stack by calling the screen’s Show() function.  This call also displays the screen in your channel.  Pressing the back button on the Roku remote closes the screen and removes it from the screen stack.  Closing a screen can also be done programmatically by calling the screen’s Close() function.  You may want to do this, for example, to dismiss a screen in response to a button press or some other event.

Typically, you create a new screen in your channel with a custom BrightScript function.  This function will create an instance of one of the BrightScript screen components, such as roGridScreen, roListScreen, and so on.  The function will also create a message port and assign it to the screen, which then listens for incoming events directed to it if it is on top of the screen stack.  Additionally, the screen will populate its content and other UI elements in this function.  As an example, here is how the sample channel provided with this article creates the lunch menu screen.  The code that populates the UI has been removed for brevity, but can be explored if you download the full sample channel:

Function CreateLunchMenu() as integer
    screen = CreateObject("roGridScreen")
    port = CreateObject("roMessagePort")
    screen.SetMessagePort(port)
    screen.show()

    while (true)
      msg = wait(0, port)
      if type(msg) = "roGridScreenEvent"
        if (msg.isScreenClosed())
          return -1
        endif
      endif
    end while
End Function

A call to CreateLunchMenu() from anywhere else in the BrightScript code will thus create the lunch menu screen, add it to the top of the screen stack, and display it on the user’s TV.

Implementing a Simple Navigation Menu

The screenshot below shows a landing page typical of many Roku channels. The list screen component is a common way to implement simple menus that drive the navigation through channel content. Each list item can represent a video genre, a music style, a group of settings, or any other content category that you want to group in your channel. In a typical channel, when a list item is selected, the channel navigates to a new screen. In our example, the list screen allows the user to browse the breakfast and lunch menus at a hypothetical diner. Selecting one of the items brings up a detailed list of offerings for that particular meal category.

The key to navigating to a new screen when a list item is selected is to handle the list screen’s isListItemSelected event. The index returned by msg.GetIndex() contains the zero-based index of the selected list item. Your channel can map each list item index to a unique screen and navigate to that screen in response to the isListItemSelected event. There are a couple of ways to accomplish this. The simplest is to just use an if statement approach in the calling screen’s event loop that tests the selected index and calls the right function:

while (true)
    msg = wait(0, port)
    if (type(msg) = "roListScreenEvent")
      if (msg.isListItemSelected())
        index = msg.GetIndex()
        if (index = 0)
          ShowBreakfastMenu()
        else if (index = 1)
          ShowLunchMenu()
          …
        endif
      endif
    endif
end while

Another more complex, but cleaner, way to do this is to create an array of function references.

menuFunctions = [ShowBreakfastMenu, ShowLunchMenu,…]
while (true)
    msg = wait(0, port)
    if (type(msg) = "roListScreenEvent")
      if (msg.isListItemSelected())
        menuFunctions[msg.GetIndex()]() ‘Call function based on index
      endif
    endif
end while

The key to understanding how this code works lies in the BrightScript function reference concept.  A function reference is just the name of a given function.  Function references can be assigned to variables, used as array elements, and even passed to other functions.  The function that corresponds to a function reference can be called by using the function call operator “()”.  Here are some examples of calling a function called doSomething by reference:

Function doSomething() as void
    ...
End Function
doSomethingReference = doSomething
‘Call doSomething by reference
doSomethingReference()
references = [doSomething, …] ‘We can also put function references in an array
references[0]() ‘Call doSomething through the array element function reference

With this in mind, let’s go back to the second approach to implementing the menu in the list view screen.  The array menuFunctions contains the name of each of the channel’s screen creation functions.  These are in the same order as the list items to which they correspond in the associated list screen.  Then, when the isListItemSelected event is handled in the list screen’s event loop, we grab the corresponding function name from the menuFunctions array.  The function is then called by reference using the “()” operator on the correct array element:

menuFunctions[msg.GetIndex()]() ‘Call function based on index

In the sample channel we use the function reference approach.  To see what the menu items actually do, click the Breakfast menu item and you’ll see the detailed breakfast menu rendered in a simple poster screen:

Clicking the Lunch menu item displays the detailed lunch menu, which is implemented using a grid screen:

Deeper Navigation

So far we’ve looked at how to use a list screen to build simple category navigation into a channel.  Moving from screens like the poster or grid screens above to other screens in your channel is just as easy.  Simply handle the isListItemSelected event in the screen’s event loop to detect when an item is selected and respond appropriately.

In the sample channel, you can click on any of the items in the breakfast menu poster screen to display a details screen with more information about the selected item.  A sample of this screen (also called a “springboard” screen) is shown below:

The two buttons at the right of this screen demonstrate another BrightScript navigation technique.  When one of these buttons is selected, the details screen receives an isButtonPressed event.  The message sent with the event contains the index of the selected button.  As with list, poster, and grid screen item indexes, this button index can be used to control what the channel does in response to the button press, including navigating to new screens, opening dialogs, and other actions.

while (true)
    msg = wait(0, port)
    if (type(msg) = "roSpringboardScreenEvent")
      if (msg.isButtonPressed())
        buttonIndex = msg.GetIndex()
        'Open a new screen corresponding to the button index
        …
      endif
  endif
end while

To try and minimize the size of the sample channel download, only the breakfast and lunch list screen items open poster or grid screens. Furthermore, only the breakfast menu poster screen items can be clicked to open details screens. You could easily use these two categories as a guide to complete the channel on your own. Or you can just use the code as is to get started navigating within your own channels. Happy coding!

Click Here To Download The Sample Channel

About Robert Burdick

Roku Developer Support Manager
This entry was posted in Uncategorized. Bookmark the permalink.
  • Garyarbaugh

    But where do you place the code? Is a new .brs file created and accessed through another component? Or will the program simply accept any new .brs file that is submitted?

    • rburdick

      It pretty much depends on how you want to structure your code. If you wanted, for example, to add a dinner menu screen, you could create a new .brs file to host the functions specific to the dinner menu functionality. Or if you preferred, these new functions could go in main.brs. When you create a function in BrightScript, the framework will find your function whereever it is implemented, as long as the function name is unique. In short, the structure of the source code files in your project is up to you. Hope this helps, feel free to comment if you need more help.

      • Garyarbaugh

        Thanks!!! That answers so many questions!

        • rburdick

          Glad to hear I could help. Feel free to get in touch with any other questions.

  • Klimov

    think-article.com

  • Klimov

    daphnenr.com

  • http://www.facebook.com/gary.arbaugh Gary Arbaugh

    Attempting to implement a grid screen with the source videos referenced via an external XML file without success. The basic stack I currently have is “TV” then “Cartoons”.
    When “Cartoons” is clicked on by the viewer, I would like to show a grid screen similar to the detailed lunch menu, then when an item is clicked on, will show a detail screen with the option to launch the video.
    Any help or guidance would be greatly appreciated.
    And the more detail you could provide the better..LOL
    Thanks in advance.

    • Robert Burdick

      Hi Gary. So I can start diagnosing in the right place, can you tell me how far you’ve gotten? For example, if you’ve started with the sample in the post, do you have the basic list -> grid -> detail screen navigation in place? I’m not sure if the problem you are having is in the XML parsing, screen navigation, or launching the video.

      • http://www.facebook.com/gary.arbaugh Gary Arbaugh

        I have made it as far as the basic list function..

        Function CreateTVMenu() as integer
        screen = CreateObject(“roGridScreen”)
        port = CreateObject(“roMessagePort”)
        screen.SetMessagePort(port)
        screen.SetBreadcrumbText(“TV”, “Listings”)
        screen.SetupLists(4)
        screen.SetListNames([“Cartoons”, “Classic TV”, “Game Shows”, “Westerns”])

        screen.SetContentList(0, GetTVMenuOptions_Cartoons())
        screen.SetContentList(1, GetTVMenuOptions_Classic TV())
        screen.SetContentList(2, GetTVMenuOptions_Game Shows())
        screen.SetContentList(3, GetTVMenuOptions_Westerns())
        screen.show()

        while (true)
        msg = wait(0, port)
        if type(msg) = “roGridScreenEvent”
        if (msg.isScreenClosed())
        return -1
        endif
        endif

        end while
        End Function

        from that point on, I can’t seem to grasp the concept of creating the grid screen, nor the proper way to implement (parse) the XML file.
        I know that I have to create a function, and then call that function..
        I have not created an InitContentList because I was trying to do that via the XML, but for the life of me, I can’t seem to get a grasp of the proper way to code it.

        Thanks again in advance.

  • Saumitra Bhave

    Hi: Thanks for the great article. I was just thinking if its possible to stop a screen from closing even if back button is pressed? Something like preventDefault in JavaScript?

    My usecase is that when I am on my channel homepage I dont want app to be closed/quit on back button. Rather show a message saying press Home to quit the app.

  • XoceUnder

    this play channel live