Now we've looked at the Playlist and the Manifest file for our sample, let's look at the markup file.
The markup file is not radically different from our previous attempts, but there are a few new things in it. Sorry if the line wrapping is ugly on your screen :-/
<? xml version = " 1.0 " ?>
< root xml:lang = " en " xmlns = " http://www.dvdforum.org/2005/ihd "
xmlns:style = " http://www.dvdforum.org/2005/ihd#style "
xmlns:state = " http://www.dvdforum.org/2005/ihd#state " >
< head >
< styling >
< style select = " //button " style:backgroundColor = " rgba(0,0,255,255) "
style:position = " absolute " style:width = " 200px " style:height = " 100px "
style:opacity = " 0.8 " />
</ styling >
< timing clock = " page " >
< par >
<!-- Same basic highlighting logic as before -->
< cue begin = " //button[state:focused()=true() and style:backgroundColor()!='rgba(255,0,0,255)'] "
end = " //button[state:focused()=false() and style:backgroundColor()='rgba(255,0,0,255)'] " >
< set style:backgroundColor = " rgba(255,0,0,255) " />
</ cue >
<!-- New failsafe cue for when things go bad -->
< cue select = " //button[state:focused()=true()] "
begin = " (count(//button[style:backgroundColor()='rgba(255,0,0,255)'])=0) "
dur = " 1f " >
< set style:backgroundColor = " rgba(255,0,0,255) " />
</ cue >
<!-- New cue for the event -->
< cue begin = " //button[state:actioned()=true()] " dur = " 0.5s " >
< set style:opacity = " 1 " />
< event name = " click " />
</ par >
</ timing >
</ head >
< body >
< div style:position = " absolute " style:x = " 0px " style:y = " 680px "
style:width = " 1920px " style:height = " 400px " >
< button id = " pause " style:x = " 250px " style:y = " 100px "
state:focused = " true " />
< button id = " play " style:x = " 550px " style:y = " 100px "
style:backgroundColor = " white " />
< button id = " chapter1 " style:x = " 100px " style:y = " 300px "
style:backgroundColor = " lime " />
< button id = " chapter2 " style:x = " 400px " style:y = " 300px "
style:backgroundColor = " yellow " />
< button id = " chapter3 " style:x = " 700px " style:y = " 300px "
style:backgroundColor = " fuchsia " />
</ div >
</ body >
</ root >
This markup file basically says we have five buttons, all of which have different colours. They also have different id attributes, which we will use when we go over to the script file in the next part. We've also made the highlight and selection logic a bit smarter to get around the problem I alluded to in the playlist post, and we'll do an animation when buttons are clicked.
Looking at the Elements
Not much is new here... check the previous posts (Part 1, Part 2, and Part 3) if you are lost.
Nothing new in this first cue; it's the same as the one we looked at last time. It attempts to find a focused-but-not-red button and make it red until there is a not-focused-but-red button.
The second cue is new though; here what we're doing is recovering from a failure condition that happens if the user hits the keys on the remote too fast. You see, iHD timing and animation is built on a tick-based model. At every "tick", the state of the markup page is determined and then the XPath queries are run to determine what (if any) animations should start or stop. As long as we're in the tick-based universe, the first cue will work because the system will always toggle between a red-focused button and a non-red-focused button.
But then you throw the user into the mix, and the user isn't bound by the tick-based system of iHD. If they can cause two buttons to be focused in rapid succession, they can break the cue so that it never fires again. In the "normal" scheme of things, focus management would go like this:
·Time t: Assume there are two buttons, A and B. They are both blue, and neither of them has the focus.
·Time t + 1: A gets the focus. Now the system can find a focused-non-red button (A), so the begin clause becomes true and A turns red.
·Time t + 2: A still has the focus. Now the system can't find a focused-non-red button, so the begin clause becomes false (making it eligible to re-start once it has ended). Note though that A is still red because the end clause has not become true yet.
·Time t + arbitrary time: Now the user flips focus to B. The system can now find a non-focused-red button (that would be A) so the end clause becomes true and A reverts back to blue. It can also find a focused-non-blue button (that would be B) but it can't do anything about it just yet because the cue hasn't ended (that's the next tick).
·Time t + arbitrary time + 1: Now the system can re-start the cue because the begin is true. B turns red and the world is happy.
But what happens when the user is too fast and switches focus between frames?
·Time t: Same two buttons, A and B. They are both blue, and neither of them has the focus.
·Time t + 2: B gets the focus. Now the system can still find a focused-non-red button (that would be B), so the begin does not become false. It can, however, find a non-focused-red button (that would be A) so the cue ends and A reverts back to blue.
·Time t + arbitrary time: Now it doesn't matter what you do; the cue will never re-start because the begin will never become false -- you are stuck in focused-non-red button limbo.
One way out of this is to use a second cue to detect that limbo situation -- if there is ever a time when there are no red buttons, something has gone wrong and we should just go ahead and make the focused button red. We end up with the following XPath: (count(//button[style:backgroundColor()='rgba(255,0,0,255)'])=0) which is true if the count of all red buttons is zero. If this is true, then we select the focused element and force it to be red, thus falsifying the focused-non-red button clause and allowing the cue to re-start.
This cue fires when we "action" (click) a button, and lasts for half a second
Nothing too new here; we just set the opacity of the button to 1. This highlights it for a brief period of time, since the default opacity is 80% (0.8) as set in the styling section.
Now here's something new! The event element is used to fire an event into the script engine. In this case, the event is named click and has no parameters. We'll do something with it in the script file, coming up next... Events happen only once per cue (ie, it doesn't fire continuously for the half second duration of the cue) and they theoretically take up no time (assuming your script code can process it within one tick).
It's Almost Over...
Now there's only one file left -- the script file!