GCompris- Ascending order
The QML Code
In the beginning, we declare a variable called mode to identify which mode we are currently in. As if now, there are two modes: 1. Numbers and 2. Alphabets. We are going to talk about alphabets shortly after, right now, let’s concentrate on the numbers.
For the numbers activity, we set the mode variable to number"
property string mode: "number"
We then create a QtObject which will store all the QML items we need in the javascript. It looks like the following:
QtObject {
id: items
property Item main: activity.main
property alias background: background
property alias bar: bar
property alias bonus: bonus
property alias boxes: boxes
property alias flow: flow
property alias container: container
property alias instruction: instruction
property real ratio: ApplicationInfo.ratio
property alias score: score
}
background: TheImagecomponent which contains the background image for the activityboxes: TheRepeatercomponent which contains a specific number ofRectanglecomponents which serves as the individual boxes for the activity.flow: TheFlowcomponent, which is responsible for positioning the Rectangles side by sidecontainer: TheRectanglecomponent which defines the area under which theflowshould be presentinstruction: AGCTextcomponent containing the instructions for each levelsscore: Ascorecomponent to display the score for the current level
The instructions
The instructions is a GCText component which displays whether the elements for the current level should be arranged in ascending or descending order. The width of this component should be a little less than the width of it’s parent and it should be anchored along with the horizontalCenter of it’s parent. The text should also get wrapped accordingly, if it doesn’t fit in the current screen.
For creating a background of the text, we create a Rectangle element under it, with the gradient starting from #000 to #666 to #AAA
GCText {
id: instruction
wrapMode: TextEdit.WordWrap
fontSize: tinySize
horizontalAlignment: Text.Center
anchors.horizontalCenter: parent.horizontalCenter
width: parent.width * 0.9
color: 'white'
Rectangle {
z: -1
opacity: 0.8
gradient: Gradient {
GradientStop { position: 0.0; color: "#000" }
GradientStop { position: 0.9; color: "#666" }
GradientStop { position: 1.0; color: "#AAA" }
}
radius: 10
border.color: 'black'
border.width: 1
anchors.centerIn: parent
width: parent.width * 1.1
height: parent.contentHeight
}
}
The items
For the items, we first have an invisible container which will contain the flow and boxes. It is a transparent Rectangle, centered in the center of the screen. The width depends on the number of Rectangles in the level. This is done to make sure that the children of the flow element are always at the center of the screen. The width is given by the formula:
width: Math.min(parent.width, ((boxes.itemAt(0)).width*boxes.model)+(boxes.model-1)*flow.spacing)
As the child of this transparent Rectangle is the Flow, with two variables, onGoingAnimationCount and validMousePress:
onGoingAnimationCount: Counts the number of on-going animations in the current level. No input will be taken as valid if the value is != 0validMousePress: A boolean variable to determine whether we can make a valid input or not (we can’t give input if there are any ongoing tasks)
As the child of the flow, we have a Repeater element which will repeat few instances of a Rectangle type, which will be our blocks which we drag and drop in the activity.
The blocks are a white Rectangle of width and height 65 * ApplicationInfo.ratio with a black border. As a child of this rectangle is a GCText, which is the label (number or alphabet) on the blocks.
For the purpose of drag and drop, we have a MouseArea which has onPressed and onReleased to deal with the drag and drop
onPressed: {
box.beginDragPosition = Qt.point(box.x, box.y)
}
onReleased: {
Activity.placeBlock(box, box.beginDragPosition);
}
The beginDragPosition is a point variable under box and placeBlock() is a function on the javascript, which we will discuss shortly
There are two animation component, which plays whenever there is a change in x and y positions in the blocks
Rectangle {
id: container
color: "transparent"
width: Math.min(parent.width, ((boxes.itemAt(0)).width*boxes.model)+(boxes.model-1)*flow.spacing)
height: parent.height/2
anchors {
horizontalCenter: parent.horizontalCenter
verticalCenter: parent.verticalCenter
}
Flow {
id: flow
spacing: 10
/*
* Count number of active animations in the activity
* at this current time
* (No input will be taken at this time)
*/
property int onGoingAnimationCount: 0
property bool validMousePress
anchors {
fill: parent
}
Repeater {
id: boxes
model: 6
Rectangle {
id: box
color: "white"
z: mouseArea.drag.active || mouseArea.pressed ? 2 : 1
property int boxValue: 0
property point beginDragPosition
width: 65 * ApplicationInfo.ratio
height: 65 * ApplicationInfo.ratio
radius: 10
border{
color: "black"
width: 3 * ApplicationInfo.ratio
}
GCText {
id: numText
anchors.centerIn: parent
text: mode == "alphabets" ? String.fromCharCode(boxValue) : boxValue.toString()
font.pointSize: 20 * ApplicationInfo.ratio
}
MouseArea {
id: mouseArea
anchors.fill: parent
drag.target: parent
enabled: (flow.onGoingAnimationCount == 0 && flow.validMousePress == true) ? true : false
onPressed: {
box.beginDragPosition = Qt.point(box.x, box.y)
}
onReleased: {
Activity.placeBlock(box, box.beginDragPosition);
}
}
Behavior on x {
PropertyAnimation {
id: animationX
duration: 500
easing.type: Easing.InOutBack
onRunningChanged: {
if(animationX.running) {
flow.onGoingAnimationCount++
} else {
flow.onGoingAnimationCount--
}
}
}
}
Behavior on y {
PropertyAnimation {
id: animationY
duration: 500
easing.type: Easing.InOutBack
onRunningChanged: {
if(animationY.running) {
flow.onGoingAnimationCount++
} else {
flow.onGoingAnimationCount--
}
}
}
}
}
}
}
}
For the score for each levels we use the Score component in-built in GCompris.
Score {
id: score
anchors {
right: parent.right
top: instruction.bottom
bottom: undefined
}
}
We now move on to the javascript part, which mainly deals with the mechanism to shift the positions of the blocks on drag and drop.
The javascript code
The variables
var currentLevel = 0
var numberOfLevel = 4
var items
var mode
// num[] will contain the random numbers
var num = []
var originalArrangement = []
var ascendingOrder
var thresholdDistance
var noOfTilesInPreviousLevel
currentLevel: The current levelnumberOfLevel: The number of levelsitems: TheQtObjectcontaining the QML objectsmode: The variable to determine the current mode of the activity (numbersoralphabets)num: An array containing the random numbersoriginalArrangement: The original arrangement provided to the userthresholdDistance: The minimum distance from the drop area to the closest block to allow shifting of the blocksnoOfTilesInPreviousLevel: To store the number of blocks in the previous level, used to reset variables
Let me know what you think of this article on twitter @RudraNilBasu or leave a comment below!