diff --git a/src/activities/piano_composition/MultipleStaff.qml b/src/activities/piano_composition/MultipleStaff.qml --- a/src/activities/piano_composition/MultipleStaff.qml +++ b/src/activities/piano_composition/MultipleStaff.qml @@ -36,12 +36,14 @@ property int nbMaxNotesPerStaff: 6 property int firstNoteX: width / 5 + // Stores the note number and the staff number in which the replacable note is. + property var noteToReplace: [-1, -1] property bool noteIsColored property bool isMetronomeDisplayed: false property alias flickableStaves: flickableStaves - signal noteClicked(string noteName, string noteType) + signal noteClicked(string noteName, string noteType, int noteIndex, int staffIndex) Flickable { id: flickableStaves @@ -85,11 +87,17 @@ currentStaff++ } - staves.itemAt(currentStaff).addNote(noteName, noteType, highlightWhenPlayed) + staves.itemAt(currentStaff).addNote(noteName, noteType, highlightWhenPlayed, false) if(playAudio) playNoteAudio(noteName, noteType) } + function replaceNote(noteName, noteType) { + if(noteToReplace [0] != -1 && noteToReplace[1] != -1) { + staves.itemAt(noteToReplace[1]).replaceNote(noteName, noteType) + } + } + function playNoteAudio(noteName, noteType) { if(noteType != "Rest") { var audioPitchType @@ -169,6 +177,10 @@ } } + function eraseNote(noteIndex, staffIndex) { + staves.itemAt(staffIndex).eraseNote(noteIndex) + } + function eraseAllNotes() { for(var v = 0 ; v <= currentStaff ; ++ v) staves.itemAt(v).eraseAllNotes() diff --git a/src/activities/piano_composition/Note.qml b/src/activities/piano_composition/Note.qml --- a/src/activities/piano_composition/Note.qml +++ b/src/activities/piano_composition/Note.qml @@ -56,6 +56,15 @@ property var noteDetails + rotation: { + if((noteDetails === undefined) || ((noteDetails.positonOnStaff < 0) && (noteType === "Whole"))) + return 0 + else if(noteDetails.positonOnStaff > 6 && noteType === "Whole") + return 180 + else + return noteDetails.rotation + } + Image { id: blackTypeImage source: blackType !== "" ? "qrc:/gcompris/src/activities/piano_composition/resource/black" + blackType + ".svg" : "" @@ -63,10 +72,10 @@ anchors.right: parent.rotation === 180 ? undefined : noteImage.left anchors.left: parent.rotation === 180 ? noteImage.right : undefined rotation: parent.rotation === 180 ? 180 : 0 - anchors.rightMargin: -width / 2 - anchors.leftMargin: -width + anchors.rightMargin: -12 + anchors.leftMargin: -18 anchors.bottom: noteImage.bottom - anchors.bottomMargin: height / 2 + anchors.bottomMargin: parent.height / 6 fillMode: Image.PreserveAspectFit } @@ -92,8 +101,9 @@ Image { id: noteImage - source: noteType != "Rest" ? "qrc:/gcompris/src/activities/piano_composition/resource/" + noteDetails.imageName + noteType + ".svg" - : "qrc:/gcompris/src/activities/piano_composition/resource/" + noteDetails.imageName + ".svg" + source: (noteDetails === undefined) ? "" + : noteType != "Rest" ? "qrc:/gcompris/src/activities/piano_composition/resource/" + noteDetails.imageName + noteType + ".svg" + : "qrc:/gcompris/src/activities/piano_composition/resource/" + noteDetails.imageName + ".svg" sourceSize.width: 200 width: note.width height: note.height @@ -105,15 +115,17 @@ source: noteImage readonly property int noteColorNumber: { - if(noteType === "Rest") - return 0 - else if(blackType === "") + if(noteDetails === undefined || noteType === "" || noteType === "Rest" || noteName === "") + return -6 + else if((blackType === "") && (whiteNoteName[noteName[0]] != undefined)) return whiteNoteName[noteName[0]] - else + else if((noteName.length > 2) && (blackNoteName[noteName.substring(0,2)] != undefined)) return blackNoteName[noteName.substring(0,2)] + else + return -6 } - color: noteType != "Rest" ? noteColorMap[noteColorNumber] : "black" // make image like it lays under red glass + color: (noteColorNumber > -6) ? noteColorMap[noteColorNumber] : "black" // make image like it lays under red glass visible: noteIsColored } diff --git a/src/activities/piano_composition/Piano_composition.qml b/src/activities/piano_composition/Piano_composition.qml --- a/src/activities/piano_composition/Piano_composition.qml +++ b/src/activities/piano_composition/Piano_composition.qml @@ -108,6 +108,7 @@ property alias melodyList: melodyList property alias file: file property alias piano: piano + property alias staffModesOptions: staffModesOptions } onStart: { Activity.start(items) } @@ -116,6 +117,7 @@ property string currentType: "Whole" property string restType: "Whole" property string clefType: bar.level == 2 ? "bass" : "treble" + property string staffMode: "add" File { id: file @@ -171,7 +173,14 @@ anchors.top: instructionBox.bottom anchors.topMargin: parent.height * 0.1 anchors.rightMargin: parent.width * 0.043 - onNoteClicked: playNoteAudio(noteName, noteType) + onNoteClicked: { + if(background.staffMode === "add") + playNoteAudio(noteName, noteType) + else if(background.staffMode === "replace") + noteToReplace = [noteIndex, staffIndex] + else + multipleStaff.eraseNote(noteIndex, staffIndex) + } } GCButtonScroll { @@ -198,7 +207,12 @@ anchors.topMargin: horizontalLayout ? parent.height * 0.08 : parent.height * 0.025 blackLabelsVisible: [4, 5, 6, 7, 8].indexOf(items.bar.level) == -1 ? false : true useSharpNotation: bar.level == 5 ? false : true - onNoteClicked: multipleStaff.addNote(note, currentType, false, true) + onNoteClicked: { + if(background.staffMode === "add") + multipleStaff.addNote(note, currentType, false, true) + else if(background.staffMode === "replace") + multipleStaff.replaceNote(note, currentType) + } } Image { @@ -246,10 +260,12 @@ anchors.horizontalCenter: parent.horizontalCenter readonly property var noteLengthName: ["Whole", "Half", "Quarter", "Eighth"] + readonly property var staffModes: ["add", "replace", "erase"] SwitchableOptions { id: noteOptions source: "qrc:/gcompris/src/activities/piano_composition/resource/genericNote%1.svg".arg(optionsRow.noteLengthName[currentIndex]) + nbOptions: optionsRow.noteLengthName.length onClicked: currentType = optionsRow.noteLengthName[currentIndex] } @@ -278,6 +294,16 @@ } } + SwitchableOptions { + id:staffModesOptions + nbOptions: optionsRow.staffModes.length + source: "qrc:/gcompris/src/activities/piano_composition/resource/%1.svg".arg(optionsRow.staffModes[currentIndex]) + anchors.top: parent.top + anchors.topMargin: 4 + onClicked: background.staffMode = optionsRow.staffModes[currentIndex] + visible: true + } + Image { id: clearButton source: "qrc:/gcompris/src/activities/piano_composition/resource/edit-clear.svg" @@ -337,6 +363,7 @@ readonly property string restTypeImage: ((optionsRow.noteLengthName[currentIndex] === "Half") ? "Whole" : optionsRow.noteLengthName[currentIndex]).toLowerCase() source: "qrc:/gcompris/src/activities/piano_composition/resource/%1Rest.svg".arg(restTypeImage) + nbOptions: optionsRow.noteLengthName.length onClicked: restType = optionsRow.noteLengthName[currentIndex] rotation: optionsRow.noteLengthName[currentIndex] === "Half" ? 180 : 0 sourceSize.width: 70 @@ -354,7 +381,10 @@ onPressed: parent.scale = 0.8 onReleased: { parent.scale = 1 - multipleStaff.addNote(restType.toLowerCase(), "Rest", false, false) + if(background.staffMode === "add") + multipleStaff.addNote(restType.toLowerCase(), "Rest", false, false) + else + multipleStaff.replaceNote(restType.toLowerCase(), "Rest") } } } diff --git a/src/activities/piano_composition/Staff.qml b/src/activities/piano_composition/Staff.qml --- a/src/activities/piano_composition/Staff.qml +++ b/src/activities/piano_composition/Staff.qml @@ -51,6 +51,7 @@ property int nbMaxNotesPerStaff property bool noteIsColored property alias notesRepeater: notesRepeater + readonly property int staffNb: index Image { id: clefImage @@ -136,15 +137,37 @@ return 812.5 } - function addNote(noteName, noteType, blackType, highlightWhenPlayed) { + function addNote(noteName, noteType, highlightWhenPlayed, isReplacing) { var duration if(noteType === "Rest") duration = calculateTimerDuration(noteName) else duration = calculateTimerDuration(noteType) - notes.append({"noteName_": noteName, "noteType_": noteType, "mDuration": duration, - "highlightWhenPlayed": highlightWhenPlayed}); + if(!isReplacing) + notes.append({"noteName_": noteName, "noteType_": noteType, "mDuration": duration, + "highlightWhenPlayed": highlightWhenPlayed}) + else + notes.set(multipleStaff.noteToReplace[0], { "noteName_": noteName, "noteType_": noteType, "mDuration": duration }) + } + + function replaceNote(newNoteName, newType) { + addNote(newNoteName, newType, false, true) + } + + function eraseNote(noteIndex) { + var noteLength = notes.get(noteIndex).mDuration + var restName + if(noteLength === 2000) + restName = "whole" + else if(noteLength === 1500) + restName = "half" + else if(noteLength === 1000) + restName = "quarter" + else + restName = "eighth" + + notes.set(noteIndex, { "noteName_": restName, "noteType_": "Rest" }) } function playNote(noteId) { @@ -172,20 +195,12 @@ height: staff.height noteDetails: Activity.getNoteDetails(noteName, noteType) - rotation: { - if(noteDetails.positonOnStaff < 0 && noteType === "Whole") - return 0 - else if(noteDetails.positonOnStaff > 6 && noteType === "Whole") - return 180 - else - return noteDetails.rotation - } MouseArea { id: noteMouseArea anchors.fill: parent hoverEnabled: true - onClicked: multipleStaff.noteClicked(noteName, noteType) + onClicked: multipleStaff.noteClicked(noteName, noteType, index, staff.staffNb) } function highlightNote() { @@ -193,6 +208,8 @@ } y: { + if(noteDetails === undefined) + return 0 var shift = -verticalDistanceBetweenLines / 2 var relativePosition = noteDetails.positonOnStaff var imageY = (nbLines - 3) * verticalDistanceBetweenLines diff --git a/src/activities/piano_composition/SwitchableOptions.qml b/src/activities/piano_composition/SwitchableOptions.qml --- a/src/activities/piano_composition/SwitchableOptions.qml +++ b/src/activities/piano_composition/SwitchableOptions.qml @@ -29,6 +29,7 @@ id: switchableOptions property int currentIndex: 0 + property int nbOptions: 1 signal clicked @@ -39,7 +40,7 @@ MouseArea { anchors.fill: parent onClicked: { - parent.currentIndex = (parent.currentIndex + 1) % 4 + parent.currentIndex = (parent.currentIndex + 1) % nbOptions clickAnimation.start() parent.clicked() } diff --git a/src/activities/piano_composition/piano_composition.js b/src/activities/piano_composition/piano_composition.js --- a/src/activities/piano_composition/piano_composition.js +++ b/src/activities/piano_composition/piano_composition.js @@ -90,6 +90,9 @@ items.bar.level = currentLevel + 1 items.piano.currentOctaveNb = items.piano.defaultOctaveNb items.multipleStaff.nbStaves = 2 + items.background.staffMode = "add" + items.multipleStaff.noteToReplace = [-1, -1] + items.staffModesOptions.currentIndex = 0 } function getNoteDetails(noteName, noteType) { @@ -112,7 +115,7 @@ if(numberOfLevel <= ++currentLevel) { currentLevel = 0 } - initLevel(); + initLevel() } function previousLevel() { @@ -120,5 +123,5 @@ if(--currentLevel < 0) { currentLevel = numberOfLevel - 1 } - initLevel(); + initLevel() } diff --git a/src/activities/piano_composition/resource/add.svg b/src/activities/piano_composition/resource/add.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/add.svg @@ -0,0 +1,3052 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/src/activities/piano_composition/resource/erase.svg b/src/activities/piano_composition/resource/erase.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/erase.svg @@ -0,0 +1,59 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/src/activities/piano_composition/resource/replace.svg b/src/activities/piano_composition/resource/replace.svg new file mode 100644 --- /dev/null +++ b/src/activities/piano_composition/resource/replace.svg @@ -0,0 +1,59 @@ + + + + + + image/svg+xml + + + + + + + + +