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
: TheImage
component which contains the background image for the activityboxes
: TheRepeater
component which contains a specific number ofRectangle
components which serves as the individual boxes for the activity.flow
: TheFlow
component, which is responsible for positioning the Rectangles side by sidecontainer
: TheRectangle
component which defines the area under which theflow
should be presentinstruction
: AGCText
component containing the instructions for each levelsscore
: Ascore
component 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
: TheQtObject
containing the QML objectsmode
: The variable to determine the current mode of the activity (numbers
oralphabets
)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!