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() ‘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:
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!