Warning, /education/gcompris/src/activities/note_names/NoteNames.qml is written in an unsupported language. File is not indexed.

0001 /* GCompris - NoteNames.qml
0002  *
0003  * SPDX-FileCopyrightText: 2018 Aman Kumar Gupta <gupta2140@gmail.com>
0004  *
0005  * Authors:
0006  *   Aman Kumar Gupta <gupta2140@gmail.com>
0007  *
0008  *   SPDX-License-Identifier: GPL-3.0-or-later
0009  */
0010 import QtQuick 2.12
0011 import QtQuick.Controls 2.12
0012 import GCompris 1.0
0013 
0014 import "../../core"
0015 import "../piano_composition"
0016 import "note_names.js" as Activity
0017 
0018 ActivityBase {
0019     id: activity
0020     property int speedSetting: 5
0021     property int timerNormalInterval: (13500 / speedSetting)
0022     isMusicalActivity: true
0023 
0024     onStart: focus = true
0025     onStop: {}
0026 
0027     property bool horizontalLayout: width >= height
0028 
0029     pageComponent: Rectangle {
0030         id: background
0031         anchors.fill: parent
0032         color: "#ABCDEF"
0033         signal start
0034         signal stop
0035 
0036         // if audio is disabled, we display a dialog to tell users this activity requires audio anyway
0037         property bool audioDisabled: false
0038 
0039         Component.onCompleted: {
0040             dialogActivityConfig.initialize()
0041             activity.start.connect(start)
0042             activity.stop.connect(stop)
0043         }
0044 
0045         // Needed to get keyboard focus on IntroMessage
0046         Keys.forwardTo: introMessage
0047 
0048         Keys.onPressed: {
0049             var keyNoteBindings = {}
0050             keyNoteBindings[Qt.Key_1] = 'C'
0051             keyNoteBindings[Qt.Key_2] = 'D'
0052             keyNoteBindings[Qt.Key_3] = 'E'
0053             keyNoteBindings[Qt.Key_4] = 'F'
0054             keyNoteBindings[Qt.Key_5] = 'G'
0055             keyNoteBindings[Qt.Key_6] = 'A'
0056             keyNoteBindings[Qt.Key_7] = 'B'
0057 
0058             if(!introMessage.visible && !iAmReady.visible && !messageBox.visible && multipleStaff.musicElementModel.count - 1) {
0059                 if(keyNoteBindings[event.key]) {
0060                     // If the key pressed matches the note, pass the correct answer as parameter.       
0061                     isCorrectKey(keyNoteBindings[event.key])
0062                 }
0063                 else if(event.key === Qt.Key_Left && shiftKeyboardLeft.visible) {
0064                     doubleOctave.currentOctaveNb--
0065                 }
0066                 else if(event.key === Qt.Key_Right && shiftKeyboardRight.visible) {
0067                     doubleOctave.currentOctaveNb++
0068                 }
0069             }
0070         }
0071 
0072         function isCorrectKey(key) {
0073             if(Activity.newNotesSequence[Activity.currentNoteIndex][0] === key)
0074                 Activity.correctAnswer()
0075             else
0076                 items.displayNoteNameTimer.start()
0077         }
0078 
0079         // Add here the QML items you need to access in javascript
0080         QtObject {
0081             id: items
0082             property Item main: activity.main
0083             property alias background: background
0084             property GCSfx audioEffects: activity.audioEffects
0085             property int currentLevel: activity.currentLevel
0086             property alias multipleStaff: multipleStaff
0087             property alias doubleOctave: doubleOctave
0088             property alias bonus: bonus
0089             property alias iAmReady: iAmReady
0090             property alias messageBox: messageBox
0091             property alias addNoteTimer: addNoteTimer
0092             property alias dataset: dataset
0093             property alias progressBar: progressBar
0094             property alias introMessage: introMessage
0095             property bool isTutorialMode: true
0096             property alias displayNoteNameTimer: displayNoteNameTimer
0097         }
0098 
0099         Loader {
0100             id: dataset
0101             asynchronous: false
0102             source: "qrc:/gcompris/src/activities/note_names/resource/dataset_01.qml"
0103         }
0104 
0105         onStart: {
0106             if(!ApplicationSettings.isAudioVoicesEnabled || !ApplicationSettings.isAudioEffectsEnabled) {
0107                     introMessage.index = -1;
0108                     background.audioDisabled = true;
0109             }
0110             Activity.start(items, activity.timerNormalInterval);
0111         }
0112         onStop: { Activity.stop() }
0113 
0114         property string clefType: "Treble"
0115 
0116         DialogChooseLevel {
0117             id: dialogActivityConfig
0118             currentActivity: activity.activityInfo
0119 
0120             onStartActivity: {
0121                 introMessage.visible = false;
0122                 iAmReady.visible = true;
0123             }
0124             onClose: {
0125                 home();
0126                 introMessage.visible = false;
0127                 iAmReady.visible = true;
0128             }
0129             onLoadData: {
0130                 if(activityData && activityData["speedSetting"]) {
0131                     activity.speedSetting = activityData["speedSetting"];
0132                 }
0133             }
0134         }
0135 
0136         Timer {
0137             id: displayNoteNameTimer
0138             interval: 2000
0139             onRunningChanged: {
0140                 if(running) {
0141                     multipleStaff.pauseNoteAnimation()
0142                     addNoteTimer.pause()
0143                     messageBox.visible = true
0144                 }
0145                 else {
0146                     messageBox.visible = false
0147                     if(progressBar.percentage != 100 && Activity.newNotesSequence.length) {
0148                         Activity.wrongAnswer()
0149                         addNoteTimer.resume()
0150                     }
0151                 }
0152             }
0153         }
0154 
0155         Rectangle {
0156             id: messageBox
0157             width: label.width + 20
0158             height: label.height + 20
0159             border.width: 5
0160             border.color: "black"
0161             anchors.centerIn: multipleStaff
0162             radius: 10
0163             z: 11
0164             visible: false
0165 
0166             function getTranslatedNoteName(noteName) {
0167                 for(var i = 0; i < doubleOctave.keyNames.length; i++) {
0168                     if(doubleOctave.keyNames[i][0] == noteName)
0169                         return doubleOctave.keyNames[i][1]
0170                 }
0171                 return ""
0172             }
0173 
0174             onVisibleChanged: {
0175                 if(Activity.targetNotes[0] === undefined)
0176                     text = ""
0177                 else if(items.isTutorialMode)
0178                     text = qsTr("New note: %1").arg(getTranslatedNoteName(Activity.targetNotes[0]))
0179                 else
0180                     text = getTranslatedNoteName(Activity.newNotesSequence[Activity.currentNoteIndex])
0181             }
0182 
0183             property string text
0184 
0185             GCText {
0186                 id: label
0187                 anchors.centerIn: parent
0188                 fontSize: mediumSize
0189                 text: parent.text
0190             }
0191 
0192             MouseArea {
0193                 anchors.fill: parent
0194                 enabled: items.isTutorialMode
0195                 onClicked: {
0196                     items.multipleStaff.pauseNoteAnimation()
0197                     items.multipleStaff.musicElementModel.remove(1)
0198                     Activity.showTutorial()
0199                 }
0200             }
0201         }
0202 
0203         Rectangle {
0204             id: colorLayer
0205             anchors.fill: parent
0206             color: "black"
0207             opacity: 0.3
0208             visible: iAmReady.visible
0209             z: 10
0210             MouseArea {
0211                 anchors.fill: parent
0212             }
0213         }
0214 
0215         ReadyButton {
0216             id: iAmReady
0217             focus: true
0218             z: 10
0219             visible: !introMessage.visible
0220             onVisibleChanged: {
0221                 messageBox.visible = false
0222             }
0223             onClicked: {
0224                 Activity.initLevel()
0225             }
0226         }
0227 
0228         IntroMessage {
0229             id: introMessage
0230             anchors {
0231                 top: parent.top
0232                 topMargin: 10
0233                 right: parent.right
0234                 rightMargin: 5
0235                 left: parent.left
0236                 leftMargin: 5
0237             }
0238             z: 12
0239         }
0240 
0241         AdvancedTimer {
0242             id: addNoteTimer
0243             onTriggered: {
0244                 Activity.noteIndexToDisplay = (Activity.noteIndexToDisplay + 1) % Activity.newNotesSequence.length
0245                 Activity.displayNote(Activity.newNotesSequence[Activity.noteIndexToDisplay])
0246             }
0247         }
0248 
0249         GCProgressBar {
0250             id: progressBar
0251             height: 20 * ApplicationInfo.ratio
0252             width: parent.width / 4
0253 
0254             property int percentage: 0
0255 
0256             value: percentage
0257             to: 100
0258             visible: !items.isTutorialMode
0259             anchors {
0260                 top: parent.top
0261                 topMargin: 10
0262                 right: parent.right
0263                 rightMargin: 10
0264             }
0265             //: The following translation represents percentage.
0266             message: qsTr("%1%").arg(value)
0267         }
0268 
0269         MultipleStaff {
0270             id: multipleStaff
0271             width: horizontalLayout ? parent.width * 0.5 : parent.width * 0.78
0272             height: horizontalLayout ? parent.height * 0.9 : parent.height * 0.7
0273             nbStaves: 1
0274             clef: clefType
0275             notesColor: "red"
0276             softColorOpacity: 0
0277             isFlickable: false
0278             anchors.horizontalCenter: parent.horizontalCenter
0279             anchors.top: parent.top
0280             anchors.topMargin: progressBar.height + 20
0281             flickableTopMargin: multipleStaff.height / 14 + distanceBetweenStaff / 2.7
0282             noteAnimationEnabled: true
0283             noteAnimationDuration: items.isTutorialMode ? 9000 : 45000 / activity.speedSetting
0284             onNoteAnimationFinished: {
0285                 if(!items.isTutorialMode)
0286                     displayNoteNameTimer.start()
0287             }
0288         }
0289 
0290         // We present a pair of two joint piano keyboard octaves.
0291         Item {
0292             id: doubleOctave
0293             width: parent.width * 0.95
0294             height: horizontalLayout ? parent.height * 0.22 : 2 * parent.height * 0.18
0295             anchors.horizontalCenter: parent.horizontalCenter
0296             anchors.bottom: bar.top
0297             anchors.bottomMargin: 30
0298 
0299             readonly property int nbJointKeyboards: 2
0300             readonly property int maxNbOctaves: 3
0301             property int currentOctaveNb: 0
0302             property var coloredKeyLabels: []
0303             property var keyNames: []
0304 
0305             Repeater {
0306                 id: octaveRepeater
0307                 anchors.fill: parent
0308                 model: doubleOctave.nbJointKeyboards
0309                 PianoOctaveKeyboard {
0310                     id: pianoKeyboard
0311                     width: horizontalLayout ? octaveRepeater.width / 2 : octaveRepeater.width
0312                     height: horizontalLayout ? octaveRepeater.height : octaveRepeater.height / 2
0313                     blackLabelsVisible: false
0314                     blackKeysEnabled: blackLabelsVisible
0315                     whiteKeysEnabled: !messageBox.visible && multipleStaff.musicElementModel.count > 1
0316                     onNoteClicked: Activity.checkAnswer(note)
0317                     currentOctaveNb: doubleOctave.currentOctaveNb
0318                     anchors.top: (index === 1) ? octaveRepeater.top : undefined
0319                     anchors.topMargin: horizontalLayout ? 0 : -15
0320                     anchors.bottom: (index === 0) ? octaveRepeater.bottom : undefined
0321                     anchors.right: (index === 1) ? octaveRepeater.right : undefined
0322                     coloredKeyLabels: doubleOctave.coloredKeyLabels
0323                     labelsColor: "red"
0324                     // The octaves sets corresponding to respective clef types are in pairs for the joint piano keyboards at a time when displaying.
0325                     whiteKeyNoteLabelsBass: {
0326                         if(index === 0) {
0327                             return [
0328                                 whiteKeyNoteLabelsArray.slice(0, 4),    // F1 to B1
0329                                 whiteKeyNoteLabelsArray.slice(4, 11),   // C2 to B2
0330                                 whiteKeyNoteLabelsArray.slice(11, 18)   // C3 to B3
0331                             ]
0332                         }
0333                         else {
0334                             return [
0335                                 whiteKeyNoteLabelsArray.slice(4, 11),   // C2 to B2
0336                                 whiteKeyNoteLabelsArray.slice(11, 18),  // C3 to B3
0337                                 whiteKeyNoteLabelsArray.slice(18, 25)   // C4 to B4
0338                             ]
0339                         }
0340                     }
0341                     whiteKeyNoteLabelsTreble: {
0342                         if(index === 0) {
0343                             return [
0344                                 whiteKeyNoteLabelsArray.slice(11, 18),  // C3 to B3
0345                                 whiteKeyNoteLabelsArray.slice(18, 25),  // C4 to B4
0346                                 whiteKeyNoteLabelsArray.slice(25, 32)   // C5 to B5
0347                             ]
0348                         }
0349                         else {
0350                             return [
0351                                 whiteKeyNoteLabelsArray.slice(18, 25),  // C4 to B4
0352                                 whiteKeyNoteLabelsArray.slice(25, 32),  // C5 to B5
0353                                 whiteKeyNoteLabelsArray.slice(32, 34)   // C6 to D6
0354                             ]
0355                         }
0356                     }
0357 
0358                     Component.onCompleted: doubleOctave.keyNames = whiteKeyNoteLabelsArray
0359                 }
0360             }
0361         }
0362 
0363         Image {
0364             id: shiftKeyboardLeft
0365             source: "qrc:/gcompris/src/core/resource/bar_previous.svg"
0366             sourceSize.width: horizontalLayout ? doubleOctave.width / 13 : doubleOctave.width / 6
0367             width: sourceSize.width
0368             height: width
0369             fillMode: Image.PreserveAspectFit
0370             visible: (doubleOctave.currentOctaveNb > 0) && doubleOctave.visible
0371             z: 11
0372             anchors {
0373                 bottom: doubleOctave.top
0374                 left: doubleOctave.left
0375                 leftMargin: -37
0376                 bottomMargin: horizontalLayout ? 10 : 25
0377             }
0378             MouseArea {
0379                 enabled: !messageBox.visible
0380                 anchors.fill: parent
0381                 onClicked: {
0382                     doubleOctave.currentOctaveNb--
0383                 }
0384             }
0385         }
0386 
0387         Image {
0388             id: shiftKeyboardRight
0389             source: "qrc:/gcompris/src/core/resource/bar_next.svg"
0390             sourceSize.width: horizontalLayout ? doubleOctave.width / 13 : doubleOctave.width / 6
0391             width: sourceSize.width
0392             height: width
0393             fillMode: Image.PreserveAspectFit
0394             visible: (doubleOctave.currentOctaveNb < doubleOctave.maxNbOctaves - 1) && doubleOctave.visible
0395             z: 11
0396             anchors {
0397                 bottom: doubleOctave.top
0398                 right: doubleOctave.right
0399                 rightMargin: -37
0400                 bottomMargin: horizontalLayout ? 10 : 25
0401             }
0402             MouseArea {
0403                 enabled: !messageBox.visible
0404                 anchors.fill: parent
0405                 onClicked: {
0406                     doubleOctave.currentOctaveNb++
0407                 }
0408             }
0409         }
0410 
0411         OptionsRow {
0412             id: optionsRow
0413             iconsWidth: 0
0414             visible: false
0415         }
0416 
0417         DialogHelp {
0418             id: dialogHelp
0419             onClose: home()
0420         }
0421 
0422         Bar {
0423             id: bar
0424             level: items.currentLevel + 1
0425             content: BarEnumContent { value: (help | home | level | reload | activityConfig) }
0426             onHelpClicked: {
0427                 displayDialog(dialogHelp)
0428             }
0429             onPreviousLevelClicked: Activity.previousLevel()
0430             onNextLevelClicked: Activity.nextLevel()
0431             onHomeClicked: home()
0432             onActivityConfigClicked: {
0433                 multipleStaff.pauseNoteAnimation()
0434                 addNoteTimer.pause()
0435                 displayDialog(dialogActivityConfig)
0436             }
0437             onReloadClicked: {
0438                 iAmReady.visible = true
0439                 Activity.initLevel()
0440             }
0441         }
0442 
0443         Bonus {
0444             id: bonus
0445             Component.onCompleted: win.connect(Activity.nextLevel)
0446         }
0447 
0448         Loader {
0449             id: audioNeededDialog
0450             sourceComponent: GCDialog {
0451                 parent: activity
0452                 isDestructible: false
0453                 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.")
0454                 button1Text: qsTr("Quit")
0455                 button2Text: qsTr("Continue")
0456                 onButton1Hit: activity.home();
0457                 onClose: {
0458                     background.audioDisabled = false;
0459                     introMessage.index = 0;
0460                 }
0461             }
0462             anchors.fill: parent
0463             focus: true
0464             active: background.audioDisabled
0465             onStatusChanged: if (status == Loader.Ready) item.start()
0466         }
0467     }
0468 }