In this lesson we will add a screen to the application to demonstrate the fundamentals of SceneGraph.
Screens require 2 files, an XML file for the UI and a BrightScript (brs) file for the application logic. The convention is to use the same name for both, for example myscreen.xml
and myscreen.brs
. While it is legal to put the BrightScript code in the XML file, it is an awful thing to do. It is difficult to maintain and just ugly.
OK, let's go:
-
Create a directory called
components
in your project -
Add 2 files:
home_scene.xml
andhome_scene.brs
-
Open
home_scene.xml
and add this code:<?xml version="1.0" encoding="utf-8" ?> <component name="home_scene" extends="Scene"> <script type="text/brightscript" uri="pkg:/components/home_scene.brs" /> </component>
Note the
<script>
tag. This is how you bind the UI to the logic. Also, this component extendsScene
, a core component in the Roku API. SceneGraph applications are intended to have one "Scene", and the visual screens are drawn as children of this component.
Read more here: https://sdkdocs.roku.com/display/sdkdoc/Scene -
Open
home_scene.brs
and add this code:function init() ? "[home_scene] init" end function
-
Now run the application. This will appear in the debugger:
------ Running dev 'Roku Lesson 2' main ------ [home_scene] init
Every SceneGraph component must define an
init()
function. This is called by the firmware whenever an instance of the component is created. -
When you run the application, the splash screen appears, but the application immediately closes. We need to add logic to keep the app running until it is closed with the remote.
The home_scene must be referenced by the application, so the main file needs updates. Navigate tosource/main.brs
and update it:sub main() screen = createObject("roSGScreen") scene = screen.createScene("home_scene") screen.Show() end sub
screen
is a core component of a SceneGraph application, and there should only ever be one. The UI defined in the application will be attached to this screen.
scene = screen.createScene("home_scene")
creates an instance of the component defined incomponents/home_scene.xml
.
screen.Show()
triggers the rendering of the components.
Continue to edit the file:sub main() screen = createObject("roSGScreen") scene = screen.createScene("home_scene") screen.Show() port = createObject("roMessagePort") screen.setMessagePort(m.port) ' this loop is necessary to keep the application open ' otherwise the channel will exit when it reaches the end of main() while(true) ' nothing happens in here, yet ' the HOME and BACK buttons on the remote will nuke the app end while end sub
m.port
is an mechanism to read events from an object. When coupled with a loop, the application can capture information as the clock runs.
Thewhile
loop is, uh, strange. Fundamentally, this loop keeps the main() function open during the execution of the application. Keep in mind that the HOME button press on the remote will nuke your application without any warning or message.
Read more here:
https://sdkdocs.roku.com/display/sdkdoc/Event+Loops, https://sdkdocs.roku.com/display/sdkdoc/ifMessagePort -
Run the application again. Now it stays open with a blank gray screen (this is the instance of home_scene) until you click the BACK button or HOME button.
-
Now add some visual components. In
components/home_scene.xml
, add the children node within the component definition.<?xml version="1.0" encoding="utf-8" ?> <component name="home_scene" extends="Scene"> <script type="text/brightscript" uri="pkg:/components/home_scene.brs" /> <children> <Rectangle translation="[950, 530]" width="20" height="20" id="center_square" color="0xFF0000"/> <Label translation="[1720, 980]" id="label_hello" font="font:LargeBoldSystemFont" color="0x000000" text="Hello."/> </children> </component>
Notice the
translation
attribute. This defines the [x,y] coordinates for placement, where [0,0] is the top left of the screen. It is important to keep these values consistent for a single target resolution (for example, 1080p) throughout your application. Later on you will see how these values are interpreted by the device. -
Open
components/home_scene.brs
again and add this code:function init() ? "[home_scene] init" m.top.backgroundURI = "" m.top.backgroundColor = "0xFFFFFFFF" m.center_square = m.top.findNode("center_square") m.center_square.setFocus(true) end function
Notice how the reference to the XML components is done.
m.top
refers to the component that is running this code. The background stuff sets the screen color to white. For some odd reason, in order forbackgroundColor
to work, thebackgroundURI
must be set to an empty string. In order to manipulate the objects in the component, you must define references using thefindNode()
API.
It is not entirely necessary to includem.center_square.setFocus(true)
, but it is good to get in the habit of making the remote control focus explicit. SceneGraph depends on the focus being properly set for the remote control to work right. This becomes more important, and easier to mess up, as your application grows in complexity.
As form
, it's used for scope, similar to "this" in other languages. Refer to the docs: https://sdkdocs.roku.com/display/sdkdoc/Component+Architecture#ComponentArchitecture-Scope -
To understand the coordinate system and how the Roku handles screen resolution, we need to return to the
manifest
file. Update the splash screen with a new entry for the splash_screen,splash_screen_fhd
:## Channel Details title=Roku Lesson 2 major_version=1 minor_version=0 build_version=0 ## Channel Icons #### Image size for mm_icon_focus_hd: 290x218px mm_icon_focus_hd=pkg:/images/channel_logo_hd.png ### Splash Screen + Loading Screen Artwork #### Image sizes are splash_screen_fhd: 1920x1080px | splash_screen_hd: 1280x720px splash_screen_fhd=pkg:/images/splash_fhd.png splash_screen_hd=pkg:/images/splash_hd.png
This image file is a 1920x1080 image used for high resolution UI displays. If the Roku supports a 1080p UI, then this image is used for the splash screen. It is important to make a distinction between the UI resolution and the video playback resolution. Until recently, most Roku devices used 1280x720/720p (aka HD) UIs, yet could playback video at a full 1080p. So the application interface, with the menus, information screens, etc was all 1280x720 no matter what the attached display could handle. Only when video playback began would the full 1920x1080/1080p (aka "Full HD", or FHD) resolution be used. The newer models, however, support 1080p UI resolution and 1080p and 4K video playback.
In order for the UI to render correctly on all devices, use theui_resolutions
manifest option the indicate what resolution the app was designed for. If there is no entry in the manifest, the default value issd,hd
. To indicate that the application is laid out using a 1920x1080 screen, the entry would be
ui_resolutions=fhd
For
fhd
, all thetranslation
attributes defined in the SceneGraph components would be based on a 1080p resolution, and any other resolution will scale automatically. If the value washd,fhd
, the Roku would assume the application logic would handle all scaling, and would not automatically adjust the layout between the different resolutions. In practice this would mean all of the components would have to read the device information, determine the available resolution, and adjust all the translations. Read more here: https://sdkdocs.roku.com/display/sdkdoc/Specifying+Display+Resolution -
To demonstrate, look at the code in
components/home_scene.xml
.<Rectangle translation="[950, 530]" width="20" height="20" id="center_square" color="0xFF0000"/>
The coordinates place a 20x20 square dead center on a 1080p screen. Here's what the application looks like on a 720p device (Roku Stick 3500X) without the ui_resolutions manifest entry:
Now add the
ui_resolutions=fhd
to the manifest:## Channel Details title=Roku Lesson 2 major_version=1 minor_version=0 build_version=0 ## Channel Icons #### Image size for mm_icon_focus_hd: 290x218px mm_icon_focus_hd=pkg:/images/channel_logo_hd.png ### Splash Screen + Loading Screen Artwork #### Image sizes are splash_screen_fhd: 1920x1080px | splash_screen_hd: 1280x720px splash_screen_fhd=pkg:/images/splash_fhd.png splash_screen_hd=pkg:/images/splash_hd.png #tell the Roku we designed our app for 1080p ui_resolutions=fhd
This is the end of Lesson 2.
Next Up:
Lesson 3: Extended Components, Observers, Tasks, Network Requests