Warning, /education/gcompris/src/activities/melody/Melody.qml is written in an unsupported language. File is not indexed.
0001 /* GCompris - melody.qml
0002 *
0003 * SPDX-FileCopyrightText: 2015 Bruno Coudoin <bruno.coudoin@gcompris.net>
0004 *
0005 * Authors:
0006 * Jose JORGE <jjorge@free.fr> (GTK+ version)
0007 * Bruno Coudoin <bruno.coudoin@gcompris.net> (Qt Quick port)
0008 *
0009 * SPDX-License-Identifier: GPL-3.0-or-later
0010 */
0011 import QtQuick 2.12
0012 import GCompris 1.0
0013
0014 import "../../core"
0015 import "qrc:/gcompris/src/core/core.js" as Core
0016
0017 ActivityBase {
0018 id: activity
0019
0020 onStart: focus = true
0021 onStop: {}
0022 isMusicalActivity: true
0023
0024 pageComponent: Rectangle {
0025 id: background
0026 anchors.fill: parent
0027 color: "#ABCDEF"
0028
0029 // if audio is disabled, we display a dialog to tell users this activity requires audio anyway
0030 property bool audioDisabled: false
0031
0032 signal start
0033 signal stop
0034
0035 Component.onCompleted: {
0036 activity.start.connect(start)
0037 activity.stop.connect(stop)
0038 }
0039
0040 // Add here the QML items you need to access in javascript
0041 QtObject {
0042 id: items
0043 property string url: "qrc:/gcompris/src/activities/melody/resource/"
0044 property var question
0045 property var questionToPlay
0046 property var answer
0047 property alias questionInterval: questionPlayer.interval
0048 readonly property int numberOfLevel: 10
0049 property int currentLevel: 0
0050 property bool running: false
0051 property bool buttonsBlocked: false
0052 }
0053
0054 onStart: {
0055 items.currentLevel = Core.getInitialLevel(items.numberOfLevel);
0056 score.numberOfSubLevels = 5;
0057 score.currentSubLevel = 0;
0058 if(!ApplicationSettings.isAudioVoicesEnabled || !ApplicationSettings.isAudioEffectsEnabled) {
0059 background.audioDisabled = true;
0060 } else {
0061 initLevel();
0062 items.running = true;
0063 introDelay.start();
0064 }
0065 }
0066
0067 onStop: {
0068 introDelay.stop()
0069 knock.stop()
0070 questionPlayer.stop()
0071 items.running = false
0072 }
0073
0074 Item {
0075 id: layoutArea
0076 width: parent.width
0077 height: parent.height - bar.height * 1.5 - score.height * 1.3
0078 anchors.top: score.bottom
0079 anchors.left: parent.left
0080 }
0081
0082 Image {
0083 id: xylofon
0084 anchors {
0085 fill: layoutArea
0086 margins: 10 * ApplicationInfo.ratio
0087 }
0088 source: items.url + 'xylofon.svg'
0089 sourceSize.width: width
0090 sourceSize.height: height
0091 fillMode: Image.PreserveAspectFit
0092 }
0093
0094 Repeater {
0095 id: parts
0096 model: 4
0097 Image {
0098 id: part
0099 source: items.url + 'xylofon_part' + (index + 1) + '.svg'
0100 rotation: - 80
0101 anchors.horizontalCenter: xylofon.horizontalCenter
0102 anchors.horizontalCenterOffset: (- xylofon.paintedWidth) * 0.3 + xylofon.paintedWidth * index * 0.22
0103 anchors.verticalCenter: xylofon.verticalCenter
0104 anchors.verticalCenterOffset: - xylofon.paintedHeight * 0.1
0105 sourceSize.width: xylofon.paintedWidth * 0.5
0106 fillMode: Image.PreserveAspectFit
0107
0108 property alias anim: anim
0109
0110 SequentialAnimation {
0111 id: anim
0112 NumberAnimation {
0113 target: part
0114 property: "scale"
0115 from: 1; to: 0.95
0116 duration: 150
0117 easing.type: Easing.InOutQuad
0118 }
0119 NumberAnimation {
0120 target: part
0121 property: "scale"
0122 from: 0.95; to: 1
0123 duration: 150
0124 easing.type: Easing.OutElastic
0125 }
0126 }
0127
0128 MouseArea {
0129 anchors.fill: parent
0130 enabled: !questionPlayer.running && !knock.running && !introDelay.running
0131 && !anim.running && !items.buttonsBlocked
0132 onClicked: {
0133 anim.start()
0134 background.playNote(index)
0135 items.answer.push(index)
0136 if(items.answer.length >= items.question.length) {
0137 items.buttonsBlocked = true;
0138 feedbackTimer.restart();
0139 }
0140 }
0141 }
0142 }
0143 }
0144
0145 function playNote(index) {
0146 activity.audioEffects.play(ApplicationInfo.getAudioFilePath(items.url +
0147 'xylofon_son' + (index + 1) + ".wav"))
0148 }
0149 Timer {
0150 id: introDelay
0151 interval: 1000
0152 repeat: false
0153 onTriggered: {
0154 if(activity.audioVoices.playbackState == 1) {
0155 introDelay.restart()
0156 }
0157 else {
0158 parent.repeat()
0159 }
0160 }
0161 }
0162
0163
0164 Timer {
0165 id: knock
0166 interval: 1000
0167 repeat: false
0168 onTriggered: {
0169 questionPlayer.start()
0170 }
0171 }
0172
0173 Timer {
0174 id: questionPlayer
0175 onTriggered: {
0176 var partIndex = items.questionToPlay.shift()
0177 if(partIndex !== undefined) {
0178 parts.itemAt(partIndex).anim.start()
0179 background.playNote(partIndex)
0180 start()
0181 }
0182 }
0183 }
0184
0185 Timer {
0186 id: feedbackTimer
0187 interval: 500
0188 onTriggered: {
0189 background.checkAnswer()
0190 }
0191 }
0192
0193 ErrorRectangle {
0194 id: errorRectangle
0195 anchors.fill: layoutArea
0196 imageSize: 60 * ApplicationInfo.ratio
0197 function releaseControls() {
0198 introDelay.restart();
0199 items.buttonsBlocked = false;
0200 }
0201 }
0202
0203 DialogHelp {
0204 id: dialogHelp
0205 onClose: home()
0206 }
0207
0208 Bar {
0209 id: bar
0210 level: items.currentLevel + 1
0211 content: BarEnumContent { value: help | home | level | repeat }
0212 onHelpClicked: {
0213 displayDialog(dialogHelp)
0214 }
0215 onPreviousLevelClicked: {
0216 score.stopWinAnimation();
0217 score.currentSubLevel = 0;
0218 items.currentLevel = Core.getPreviousLevel(items.currentLevel, items.numberOfLevel);
0219 initLevel();
0220 parent.repeat();
0221 }
0222 onNextLevelClicked: {
0223 parent.nextLevel();
0224 parent.repeat();
0225 }
0226 onHomeClicked: activity.home()
0227 onRepeatClicked: parent.repeat()
0228 }
0229
0230 Bonus {
0231 id: bonus
0232 onWin: {
0233 parent.nextLevel();
0234 introDelay.restart();
0235 }
0236 }
0237
0238 Score {
0239 id: score
0240 anchors.bottom: undefined
0241 anchors.right: parent.right
0242 anchors.rightMargin: 10 * ApplicationInfo.ratio
0243 anchors.top: parent.top
0244 onStop: {
0245 parent.nextSubLevel();
0246 introDelay.restart();
0247 }
0248 }
0249
0250 function initLevel() {
0251 errorRectangle.resetState();
0252 items.question = []
0253 questionPlayer.stop()
0254
0255 var numberOfParts = 4
0256 if(items.currentLevel < 2)
0257 numberOfParts = 2
0258 else if(items.currentLevel < 4)
0259 numberOfParts = 3
0260
0261 for(var i = 0; i < items.currentLevel + 3; ++i) {
0262 items.question.push(Math.floor(Math.random() * numberOfParts))
0263 }
0264 items.questionInterval = 1200 - Math.min(500, 100 * (items.currentLevel + 1))
0265 items.answer = []
0266 items.buttonsBlocked = false
0267 }
0268
0269 function nextSubLevel() {
0270 if(score.currentSubLevel < score.numberOfSubLevels) {
0271 initLevel()
0272 return
0273 }
0274 bonus.good("note")
0275 }
0276
0277 function nextLevel() {
0278 score.stopWinAnimation();
0279 score.currentSubLevel = 0;
0280 items.currentLevel = Core.getNextLevel(items.currentLevel, items.numberOfLevel);
0281 initLevel();
0282 }
0283
0284 function repeat() {
0285 if(items.running == true) {
0286 introDelay.stop()
0287 activity.audioVoices.stop()
0288 questionPlayer.stop()
0289 activity.audioEffects.play(ApplicationInfo.getAudioFilePath(items.url + 'knock.wav'))
0290 items.questionToPlay = items.question.slice()
0291 items.answer = []
0292 knock.start()
0293 }
0294 }
0295
0296 function checkAnswer() {
0297 if(items.answer.join() == items.question.join()) {
0298 score.currentSubLevel += 1
0299 score.playWinAnimation()
0300 activity.audioEffects.play("qrc:/gcompris/src/core/resource/sounds/completetask.wav")
0301 } else {
0302 activity.audioEffects.play("qrc:/gcompris/src/core/resource/sounds/crash.wav")
0303 errorRectangle.startAnimation()
0304 }
0305 }
0306
0307 Loader {
0308 id: audioNeededDialog
0309 sourceComponent: GCDialog {
0310 parent: activity
0311 isDestructible: false
0312 message: qsTr("This activity requires sound, so it will play some sounds even if the audio voices or effects are disabled in the main configuration.")
0313 button1Text: qsTr("Quit")
0314 button2Text: qsTr("Continue")
0315 onButton1Hit: activity.home();
0316 onClose: {
0317 background.audioDisabled = false;
0318 initLevel();
0319 items.running = true;
0320 introDelay.start();
0321 }
0322 }
0323 anchors.fill: parent
0324 focus: true
0325 active: background.audioDisabled
0326 onStatusChanged: if (status == Loader.Ready) item.start()
0327 }
0328 }
0329 }