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 }