View and screen interactions
What you will learn
In this lesson, you will improve your screen to have 2 buttons and create interaction between screen and views. You will make your screen react to input and create states for your buttons.
This should be the final result :
Navigate between views
Something particular when making a TV application and using a remote control is that you should always have a focus somewhere. User will be able to navigate with constraint directions (even when using a gamepad) and predictable ones. Add a second button to see how to interact between them.
In your CatalogScreen
, add a second button as a child, and place it next to the first one :
export default $AppScreen.declare("CatalogScreen", {
children: /** @lends CatalogScreen.prototype */ {
button1: {class: $ButtonView},
button2: {class: $ButtonView}
},
style: {
button2: {
x: ({parent}) => parent.button1.x + parent.button1.width + 20
}
}
});
Change connectData
method to set different text to each button :
methods: {
connectData: function () {
this.button1.setText("Home");
this.button2.setText("VOD");
}
}
Focus management
Now you have 2 buttons, first one is focused and the other one is not. Focus is automatically given to the first child of a Screen
. And if you want to check, you can use debug tool available in your console. Just enter in Chrome console :
> wtvDebug.currentScreen.focusedChild
Here you have complete access to the instance. To check wich one it is, you can get its id. “Id” is the name you gave in the screen’s children declaration.
> wtvDebug.currentScreen.focusedChild.id
> 'button1'
State management
As focus / unfocused state is mandatory in remote controlled application, it is part of Dana. States will allow you to define a different bunch of style properties when a state is applied to a View
. What is written in style
is considered as the default
state. You are going to define a different style for default
and focused
states.
Edit the ButtonView
as follows :
export default $View.declare("ButtonView", {
style: {
text: {
color: $Theme.COLOR_BLACK
},
rectangle: {
opacity: 0
}
},
states: {
focused: {
"rectangle.opacity": 1,
"text.color": $Theme.COLOR_WHITE
}
}
});
The keyword states
allows you to declare the different set of states where you want to apply different style property. To apply style property to a children, use its id and the property name {childId}.{propertyName}
. Here you have changed the color of the text and the opacity of the rectangle. When focused, a blue rectangle with a white text will be visible, and by default a button will only be composed of a black text.
Caution
focused
state is managed by Dana and you do not need to do anything to add or remove it. You can of course create your one states and manage them by yourself.
Use the browser console to execute javascript code and focus the second button.
wtvDebug.currentScreen.button2.focus()
Second button became focused
and its style is updated. First button lost its focused
state and its style is also updated to roll back to default values.
Transitions
Caution
CSS renderer has no transitions implementation because it targets low performance devices. Edit your profile to template-lightning
for this exercice.
By using the keyword transitions
in a View
, you can define a transition to be applied when a style property change. Using the same kind of notation {childId}.{propertyName}
, you will be able to define a transition duration using the keyword duration
.
export default $View.declare("ButtonView", {
transitions: {
"rectangle.opacity": {duration: 250}
}
});
To see it, you can use focus again :
wtvDebug.currentScreen.button2.focus()
Caution
Transitions are applied when style property value is updated, no matter the way. It is working when using transitions, but also if you update value by yourself. For example, try to change rectangle’s opacity of button1
by entering wtvDebug.currentScreen.button1.rectangle.opacity = 1
Input management
As explained, TV applications are used with remote controller. In CatalogScreen
, create two functions for : right
and left
.
methods
keyword is used to store functions of an instance. Implementation of those two functions are basics :
right
method : if current focus is onbutton1
, focusbutton2
. Otherwise, do nothingleft
method : if current focus is onbutton2
, focusbutton1
. Otherwise, do nothing
export default $AppScreen.declare("CatalogScreen", {
methods: /** @lends CatalogScreen.prototype */ {
right: function () {
if (this.focusedChild.id === "button1") {
this.button2.focus();
}
},
left: function () {
if (this.focusedChild.id === "button2") {
this.button1.focus();
}
}
}
});
To be able to simulate remote behaviour, on browser you can use navigation key (up / down / right / left). Use right and left keys to switch focus between your two buttons.
As those inputs are going to be used in every TV applications, a default implementation has been done in AppScreen
To ease and speed up development . CatalogScreen
inherits from AppScreen
, so you benefits of those implementations. Let’s review this file.
export default $Screen.declare("AppScreen", {
inputs: ["keypress.UP", "keypress.DOWN", "keypress.LEFT", "keypress.RIGHT", "keypress.OK", "keypress.BACK"],
methods: /** @lends AppScreen.prototype */ {
onKeypressLEFT: function (key) {
return this.left(key);
},
onKeypressRIGHT: function (key) {
return this.right(key);
},
/**
* @override
*/
left: function (key) {
return this.focusedChild.hasNavigation ? this.focusedChild.left(key) : false;
},
/**
* @override
*/
right: function (key) {
return this.focusedChild.hasNavigation ? this.focusedChild.right(key) : false;
}
}
});
inputs
keyword is used to list input key events that screen is going to listen to. Key event name is split in two parts :
- key event : 3 values possible
keydown
,keypress
,keyup
- key name : comprehensible name of the key used
Once inputs are registered, you need to declare the relative function called when input is used. Naming should follow the convention on{keyEvent}{keyName}
. First letter of keyEvent
and keyName
should be put in uppercase. As a result you have :
keypress.LEFT
will callonKeypressLEFT
keypress.DOWN
will callonKeypressDOWN
In AppScreen
, onKeypressLEFT
calls left
method. In CatalogScreen
, you have overriden left
method. That’s why, when you press your keyboard’s left key, focus is changing.
Navigating between screens
In the begining of the lesson, you have changed you profile to start on CatalogScreen
. Revert to splash
to be able to use routing between those 2 screens.
In app.config.json
, inside the object default
, edit back firstRoute
:
"firstRoute": {
"id": "splash"
}
Now open SplashScreen
. In beforeShow
method, a timer is started. Once resolved, route to your new screen.
export default $AppScreen.declare("SplashScreen", {
methods: /** @lends SplashScreen.prototype */ {
beforeShow: function (context) {
this.timerManager.setTimeout(() => {
this.route("catalog");
}, this.splashDuration, this.statics.HIDE_SPLASH_SCREEN_TIMER_ID);
}
}
});
Note
Do not forget to relaunch your npm start
. Now, you can see both of your screens displayed one after the other.
Summary
To summarize, during this lesson you have :
- controlled navigation between views inside a screen
- used states and transitions
- routed to a screen from another one
To go further on those concepts, you can browse the following documentation pages :