Flixnet theme, Part 3: Using API data
Finally, the time has come to replace the placeholder elements with actual content. Let's start by using the real collection data. According to the API reference, collections can be accessed and selected through api.collections
, which we can use as the model
of a ListView (or any other View).
Vertical axis
First, find the ListView for the collection axis and change its model
property:
ListView {
id: collectionAxis
// ...
model: api.collections
delegate: collectionAxisDelegate
// ...
}
Previously the model
was set to 10
, and so the modelData
available in the delegates was a number between 0 and 9. With model
set to api.collections
, the modelData
will be a Collection
object.
A Collection
always has a name
, so let's show that:
Component {
id: collectionAxisDelegate
Item {
// ...
Text {
id: label
text: modelData.name
color: "white"
font.pixelSize: vpx(18)
font.family: globalFonts.sans
// ...
}
ListView {
id: gameAxis
// ...
}
}
}
Tip
If the name of the modelData
property you use (in this case name
) don't collide with other properties of the object, it's not required to type out modelData
: you can simply write text: name
.
After a refresh, you should see the names of collections appearing in Pegasus.
Horizontal axis
Now let's show the game titles in the horizontal rectangles. Every Collection
has a games
member that holds list of games associated with the collection. Let's use it in the horizontal axis (collectionAxisDelegate
):
Component {
id: collectionAxisDelegate
Item {
// ...
Text {
id: label
// ...
}
ListView {
id: gameAxis
// ...
model: modelData.games
delegate: gameAxisDelegate
spacing: vpx(10)
// ...
}
}
}
And now the Views scroll properly, with the real data!
Game items
With the previous changes, gameAxisDelegate
is now a visual representation for a Game element. A Game always has a title
, so we can simply set it as the text:
Component {
id: gameAxisDelegate
Rectangle {
width: vpx(240)
height: vpx(135)
color: "green"
Text {
text: modelData.title
}
}
}
Help
The model
of the vertical ListView was a list of Collection
s, so the modelData
received by a delegate of that ListView (one whole horizontal row) is one Collection
object.
The model
of these horizontal ListViews is a list of Game
s, so a delegate of the horizontal ListViews will see a Game
in its modelData
.
And now the game names also show up in Pegasus:
Launching games
Now that the game selection items are in sync with the API, we can actually launch games! To launch a game, we just have to call its launch()
function. Let's make it so when Enter is pressed the current game is launched:
ListView {
id: collectionAxis
// ...
focus: true
Keys.onLeftPressed: currentItem.axis.decrementCurrentIndex()
Keys.onRightPressed: currentItem.axis.incrementCurrentIndex()
Keys.onReturnPressed: ???.launch()
}
Hm, but how do we access that game in the first place? All Views have a currentIndex
property that tells us which item is currently selected, and we can use this with the get()
function available in the models that come from the API.
The current game is the currently selected game of the currently selected collection, which we can write like this:
Keys.onReturnPressed: api.collections.get(currentIndex).games.get(currentItem.currentIndex).launch()
Which, while works, is a tiny bit long to type out. To make things simpler, let's add a currentGame
field to the game axis:
ListView {
id: gameAxis
property var currentGame: modelData.games.get(currentIndex) // or just 'model.get(...)'
// ...
With that, we can reduce the line to just this:
Keys.onLeftPressed: currentItem.axis.decrementCurrentIndex()
Keys.onRightPressed: currentItem.axis.incrementCurrentIndex()
Keys.onReturnPressed: currentItem.axis.currentGame.launch()
Looks better (well, at least to me, your mileage may vary).
Help
Careful not to confuse the onReturnPressed
and onEnterPressed
calls: technically Return
is the key next to the letters, while Enter
is the one on the numeric keypad.
And with this, technically our theme is fully functional!
Gamepads and custom configurations
Now you might wonder, "how can I make it work with a gamepad?" or "what if I changed the keyboard layout?". We want to launch the game when any key or button set for "accepting things" is pressed. For this we can add a general input check using Keys.onPressed
, and test whether this key/button press event is recognized using the keys-related API functions:
Keys.onLeftPressed: currentItem.axis.decrementCurrentIndex()
Keys.onRightPressed: currentItem.axis.incrementCurrentIndex()
Keys.onPressed: {
if (api.keys.isAccept(event))
currentItem.axis.currentGame.launch();
}
With all that, navigation should now work well in our theme. Next step, let's make it pretty!
The code so far
The code so far
import QtQuick 2.0
FocusScope {
ListView {
id: collectionAxis
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.verticalCenter
anchors.bottom: parent.bottom
model: api.collections
delegate: collectionAxisDelegate
snapMode: ListView.SnapOneItem
highlightRangeMode: ListView.StrictlyEnforceRange
clip: true
focus: true
Keys.onLeftPressed: currentItem.axis.decrementCurrentIndex()
Keys.onRightPressed: currentItem.axis.incrementCurrentIndex()
Keys.onPressed: {
if (api.keys.isAccept(event))
currentItem.axis.currentGame.launch();
}
}
Component {
id: collectionAxisDelegate
Item {
width: ListView.view.width
height: vpx(180)
Text {
id: label
text: modelData.name
color: "white"
font.pixelSize: vpx(18)
font.family: globalFonts.sans
height: vpx(45)
verticalAlignment: Text.AlignVCenter
anchors.left: parent.left
anchors.leftMargin: vpx(100)
}
ListView {
id: gameAxis
property var currentGame: modelData.games.get(currentIndex)
anchors.left: parent.left
anchors.right: parent.right
anchors.top: label.bottom
anchors.bottom: parent.bottom
orientation: ListView.Horizontal
model: modelData.gameList.model
currentIndex: modelData.gameList.index
delegate: gameAxisDelegate
spacing: vpx(10)
snapMode: ListView.SnapOneItem
highlightRangeMode: ListView.StrictlyEnforceRange
preferredHighlightBegin: vpx(100)
preferredHighlightEnd: preferredHighlightBegin + vpx(240)
}
}
}
Component {
id: gameAxisDelegate
Rectangle {
width: vpx(240)
height: vpx(135)
color: "green"
Text {
text: modelData.title
}
}
}
}