diff --git a/src/activities/piano_composition/BpmMeter.qml b/src/activities/piano_composition/BpmMeter.qml new file mode 100644 index 000000000..c80167d46 --- /dev/null +++ b/src/activities/piano_composition/BpmMeter.qml @@ -0,0 +1,186 @@ +/* GCompris - BpmMeter.qml +* +* Copyright (C) 2018 Aman Kumar Gupta +* +* Authors: +* Beth Hadley (GTK+ version) +* Johnny Jazeix (Qt Quick port) +* Aman Kumar Gupta (Qt Quick port) +* Timothée Giet (refactoring) +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, see . +*/ +import QtQuick 2.6 +import GCompris 1.0 + +import "../../core" + +Item { + id: bpmMeter + width: optionsRow.iconsWidth * 2 + height: optionsRow.iconsWidth + visible: bpmVisible + Rectangle { + id: bpmBg + color: "yellow" + opacity: 0.1 + border.width: 2 + border.color: "black" + width: optionsRow.iconsWidth + height: optionsRow.iconsWidth + anchors.left: parent.left + radius: 10 + } + GCText { + //: BPM is the abbreviation for Beats Per Minute. + text: qsTr("%1 BPM").arg(multipleStaff.bpmValue) + width: 0.9 * bpmBg.width + height: bpmBg.height + verticalAlignment: Text.AlignVCenter + anchors.centerIn: bpmBg + fontSizeMode: Text.Fit + } + + Image { + id: bpmDown + source: "qrc:/gcompris/src/core/resource/bar_down.svg" + width: iconsWidth + height: iconsWidth * 0.5 + sourceSize.width: width + fillMode: Image.PreserveAspectFit + anchors.bottom: parent.bottom + anchors.left: bpmBg.right + Timer { + id: decreaseBpm + interval: 500 + repeat: true + onTriggered: { + bpmDecreased() + interval = 1 + } + onRunningChanged: { + if(!running) + interval = 500 + } + } + MouseArea { + id: mouseDown + anchors.fill: parent + hoverEnabled: true + onPressed: { + bpmDown.scale = 0.85 + bpmDecreased() + decreaseBpm.start() + } + onReleased: { + decreaseBpm.stop() + bpmDown.scale = 1 + } + } + states: [ + State { + name: "notclicked" + PropertyChanges { + target: bpmDown + scale: 1.0 + } + }, + State { + name: "clicked" + when: mouseDown.pressed + PropertyChanges { + target: bpmDown + scale: 0.9 + } + }, + State { + name: "hover" + when: mouseDown.containsMouse + PropertyChanges { + target: bpmDown + scale: 1.1 + } + } + ] + + Behavior on scale { NumberAnimation { duration: 70 } } + Behavior on opacity { PropertyAnimation { duration: 200 } } + } + + Image { + id: bpmUp + source: "qrc:/gcompris/src/core/resource/bar_up.svg" + width: iconsWidth + height: bpmDown.height + sourceSize.width: width + fillMode: Image.PreserveAspectFit + anchors.top: parent.top + anchors.left: bpmBg.right + Timer { + id: increaseBpm + interval: 500 + repeat: true + onTriggered: { + bpmIncreased() + interval = 1 + } + onRunningChanged: { + if(!running) + interval = 500 + } + } + MouseArea { + id: mouseUp + anchors.fill: parent + hoverEnabled: true + onPressed: { + bpmUp.scale = 0.85 + bpmIncreased() + increaseBpm.start() + } + onReleased: { + increaseBpm.stop() + bpmUp.scale = 1 + } + } + states: [ + State { + name: "notclicked" + PropertyChanges { + target: bpmUp + scale: 1.0 + } + }, + State { + name: "clicked" + when: mouseUp.pressed + PropertyChanges { + target: bpmUp + scale: 0.9 + } + }, + State { + name: "hover" + when: mouseUp.containsMouse + PropertyChanges { + target: bpmUp + scale: 1.1 + } + } + ] + + Behavior on scale { NumberAnimation { duration: 70 } } + Behavior on opacity { PropertyAnimation { duration: 200 } } + } +} diff --git a/src/activities/piano_composition/KeyOption.qml b/src/activities/piano_composition/KeyOption.qml new file mode 100644 index 000000000..aee6347af --- /dev/null +++ b/src/activities/piano_composition/KeyOption.qml @@ -0,0 +1,80 @@ +/* GCompris - KeyOption.qml +* +* Copyright (C) 2018 Aman Kumar Gupta +* +* Authors: +* Beth Hadley (GTK+ version) +* Johnny Jazeix (Qt Quick port) +* Aman Kumar Gupta (Qt Quick port) +* Timothée Giet (refactoring) +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, see . +*/ +import QtQuick 2.6 +import GCompris 1.0 + +import "../../core" + +Item { + id: clefOption + + property alias clefButtonIndex: clefButton.currentIndex + property bool clefButtonVisible: false + signal clefAdded + + width: optionsRow.iconsWidth * 2 + height: optionsRow.iconsWidth + visible: clefButtonVisible + Rectangle { + color: "yellow" + opacity: 0.1 + border.width: 2 + border.color: "black" + anchors.fill: parent + radius: 10 + } + + SwitchableOptions { + id: clefButton + nbOptions: 2 + source: "qrc:/gcompris/src/activities/piano_composition/resource/" + (!currentIndex ? "trebbleClefButton.svg" + : "bassClefButton.svg") + width: optionsRow.iconsWidth * 0.9 + sourceSize.width: width + visible: clefButtonVisible + onClicked: { + //: Treble clef and Bass clef are the notations to indicate the pitch of the sound written on it. + emitOptionMessage(!currentIndex ? qsTr("Treble clef") : qsTr("Bass clef")) + } + anchors.topMargin: 3 + anchors.left: parent.left + anchors.leftMargin: 5 + } + + BarButton { + id: addClefButton + width: clefButton.width + sourceSize.width: width + source: "qrc:/gcompris/src/activities/piano_composition/resource/add.svg" + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + visible: clefButton.visible + onClicked: { + background.clefType = !clefButton.currentIndex ? "Treble" : "Bass" + emitOptionMessage(!clefButton.currentIndex ? qsTr("Added Treble clef") : qsTr("Added Bass clef")) + parent.scale = 1 + clefAdded() + } + } +} diff --git a/src/activities/piano_composition/OptionsRow.qml b/src/activities/piano_composition/OptionsRow.qml index d8d4ac390..c5b049aa3 100644 --- a/src/activities/piano_composition/OptionsRow.qml +++ b/src/activities/piano_composition/OptionsRow.qml @@ -1,347 +1,210 @@ /* GCompris - OptionsRow.qml * * Copyright (C) 2018 Aman Kumar Gupta * * Authors: * Beth Hadley (GTK+ version) * Johnny Jazeix (Qt Quick port) * Aman Kumar Gupta (Qt Quick port) +* Timothée Giet (refactoring) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ import QtQuick 2.6 import GCompris 1.0 import "../../core" -Row { +Grid { id: optionsRow - spacing: iconsWidth * 0.1 + columns: 2 //: Whole note, Half note, Quarter note and Eighth note are the different length notes in the musical notation. readonly property var noteLengthName: [[qsTr("Whole note"), "Whole"], [qsTr("Half note"), "Half"], [qsTr("Quarter note"), "Quarter"], [qsTr("Eighth note"), "Eighth"]] //: Whole rest, Half rest, Quarter rest and Eighth rest are the different length rests (silences) in the musical notation. - readonly property var translatedRestNames: [qsTr("Whole rest"), qsTr("Half rest"), qsTr("Quarter rest"), qsTr("Eighth rest")] readonly property var restAddedMessage: [qsTr("Added whole rest"), qsTr("Added half rest"), qsTr("Added quarter rest"), qsTr("Added eighth rest")] + readonly property var translatedRestNames: [qsTr("Whole rest"), qsTr("Half rest"), qsTr("Quarter rest"), qsTr("Eighth rest")] readonly property var lyricsOrPianoModes: [[qsTr("Piano"), "piano"], [qsTr("Lyrics"), "lyrics"]] property real iconsWidth: score.height * 1.2 property alias noteOptionsIndex: noteOptions.currentIndex property alias lyricsOrPianoModeIndex: lyricsOrPianoModeOption.currentIndex + property alias keyOption: keyOption + property alias bpmMeter: bpmMeter property alias restOptionIndex: restOptions.currentIndex - property alias clefButtonIndex: clefButton.currentIndex - + + property bool restOptionsVisible: false property bool noteOptionsVisible: false property bool playButtonVisible: false - property bool clefButtonVisible: false property bool clearButtonVisible: false property bool undoButtonVisible: false property bool openButtonVisible: false property bool saveButtonVisible: false property bool changeAccidentalStyleButtonVisible: false property bool lyricsOrPianoModeOptionVisible: false - property bool restOptionsVisible: false property bool bpmVisible: false signal undoButtonClicked signal clearButtonClicked signal openButtonClicked signal saveButtonClicked signal playButtonClicked - signal clefAdded signal bpmIncreased signal bpmDecreased signal emitOptionMessage(string message) - SwitchableOptions { - id: noteOptions - source: "qrc:/gcompris/src/activities/piano_composition/resource/genericNote%1.svg".arg(optionsRow.noteLengthName[currentIndex][1]) - nbOptions: optionsRow.noteLengthName.length - currentIndex: 2 - onClicked: { - background.currentType = optionsRow.noteLengthName[currentIndex][1] - emitOptionMessage(optionsRow.noteLengthName[currentIndex][0]) - } - visible: noteOptionsVisible - } - Item { + BpmMeter { id: bpmMeter - width: 4 * optionsRow.iconsWidth - height: optionsRow.iconsWidth + 10 - visible: bpmVisible - Rectangle { - color: "yellow" - opacity: 0.1 - border.width: 2 - border.color: "black" - anchors.fill: parent - radius: 10 - } - - Image { - source: "qrc:/gcompris/src/core/resource/bar_previous.svg" - sourceSize.width: parent.width / 4 - width: sourceSize.width - height: width - fillMode: Image.PreserveAspectFit - anchors.left: parent.left - anchors.leftMargin: -10 - anchors.verticalCenter: parent.verticalCenter - Timer { - id: decreaseBpm - interval: 500 - repeat: true - onTriggered: { - bpmDecreased() - interval = 1 - } - onRunningChanged: { - if(!running) - interval = 500 - } - } - - MouseArea { - anchors.fill: parent - onPressed: { - parent.scale = 0.85 - bpmDecreased() - decreaseBpm.start() - } - onReleased: { - decreaseBpm.stop() - parent.scale = 1 - } - } - } - - GCText { - //: BPM is the abbreviation for Beats Per Minute. - text: qsTr("%1 BPM").arg(multipleStaff.bpmValue) - width: 0.6 * parent.width - height: width - verticalAlignment: Text.AlignVCenter - anchors.centerIn: parent - fontSizeMode: Text.Fit - } - - Image { - source: "qrc:/gcompris/src/core/resource/bar_next.svg" - sourceSize.width: parent.width / 4 - width: sourceSize.width - height: width - fillMode: Image.PreserveAspectFit - anchors.verticalCenter: parent.verticalCenter - anchors.right: parent.right - anchors.rightMargin: -10 - Timer { - id: increaseBpm - interval: 500 - repeat: true - onTriggered: { - bpmIncreased() - interval = 1 - } - onRunningChanged: { - if(!running) - interval = 500 - } - } - - MouseArea { - anchors.fill: parent - onPressed: { - parent.scale = 0.85 - bpmIncreased() - increaseBpm.start() - } - onReleased: { - increaseBpm.stop() - parent.scale = 1 - } - } - } } BarButton { id: playButton source: "qrc:/gcompris/src/activities/piano_composition/resource/play.svg" sourceSize.width: optionsRow.iconsWidth visible: playButtonVisible onClicked: { optionsRow.playButtonClicked() emitOptionMessage(qsTr("Play melody")) multipleStaff.play() } } - Item { - id: clefOption - width: 2.3 * optionsRow.iconsWidth - height: optionsRow.iconsWidth + 10 - visible: clefButtonVisible - Rectangle { - color: "yellow" - opacity: 0.1 - border.width: 2 - border.color: "black" - anchors.fill: parent - radius: 10 - } - - SwitchableOptions { - id: clefButton - nbOptions: 2 - source: "qrc:/gcompris/src/activities/piano_composition/resource/" + (!currentIndex ? "trebbleClefButton.svg" - : "bassClefButton.svg") - sourceSize.width: optionsRow.iconsWidth - visible: clefButtonVisible - onClicked: { - //: Treble clef and Bass clef are the notations to indicate the pitch of the sound written on it. - emitOptionMessage(!currentIndex ? qsTr("Treble clef") : qsTr("Bass clef")) - } - anchors.topMargin: 3 - anchors.left: parent.left - anchors.leftMargin: 5 - } - - BarButton { - id: addClefButton - sourceSize.width: optionsRow.iconsWidth / 1.4 - source: "qrc:/gcompris/src/activities/piano_composition/resource/add.svg" - anchors.left: clefButton.right - anchors.leftMargin: 8 - visible: clefButton.visible - anchors.top: parent.top - anchors.topMargin: 10 - onClicked: { - background.clefType = !clefButton.currentIndex ? "Treble" : "Bass" - emitOptionMessage(!clefButton.currentIndex ? qsTr("Added Treble clef") : qsTr("Added Bass clef")) - parent.scale = 1 - clefAdded() - } - } - } - BarButton { id: clearButton source: "qrc:/gcompris/src/activities/piano_composition/resource/erase.svg" sourceSize.width: optionsRow.iconsWidth visible: clearButtonVisible onClicked: clearButtonClicked() } BarButton { id: undoButton source: "qrc:/gcompris/src/activities/piano_composition/resource/undo.svg" sourceSize.width: optionsRow.iconsWidth visible: undoButtonVisible onClicked: { emitOptionMessage(qsTr("Undo")) undoButtonClicked() } } - BarButton { - id: openButton - source: "qrc:/gcompris/src/activities/piano_composition/resource/open.svg" - sourceSize.width: optionsRow.iconsWidth - visible: openButtonVisible - onClicked: openButtonClicked() + KeyOption { + id: keyOption + } - - BarButton { - id: saveButton - source: "qrc:/gcompris/src/activities/piano_composition/resource/save.svg" - sourceSize.width: optionsRow.iconsWidth - visible: saveButtonVisible - onClicked: saveButtonClicked() - } - - BarButton { - id: changeAccidentalStyleButton - source: changeAccidentalStyleButtonVisible ? (piano.useSharpNotation ? "qrc:/gcompris/src/activities/piano_composition/resource/blacksharp.svg" - : "qrc:/gcompris/src/activities/piano_composition/resource/blackflat.svg") - : "" - sourceSize.width: optionsRow.iconsWidth - visible: changeAccidentalStyleButtonVisible - onClicked: { - piano.useSharpNotation = !piano.useSharpNotation - //: Sharp notes and Flat notes represents the accidental style of the notes in the music. - emitOptionMessage(piano.useSharpNotation ? qsTr("Sharp notes") : qsTr("Flat notes")) - } - } - - SwitchableOptions { - id: lyricsOrPianoModeOption - nbOptions: optionsRow.lyricsOrPianoModes.length - source: "qrc:/gcompris/src/activities/piano_composition/resource/%1-icon.svg".arg(optionsRow.lyricsOrPianoModes[currentIndex][1]) - anchors.top: parent.top - anchors.topMargin: 4 - visible: lyricsOrPianoModeOptionVisible - onClicked: emitOptionMessage(optionsRow.lyricsOrPianoModes[currentIndex][0]) - } - + Item { id: rests - width: 2.3 * optionsRow.iconsWidth - height: optionsRow.iconsWidth + 10 + width: optionsRow.iconsWidth * 2 + height: optionsRow.iconsWidth visible: restOptionsVisible Rectangle { color: "yellow" opacity: 0.1 border.width: 2 border.color: "black" anchors.fill: parent radius: 10 } - + // Since the half rest image is just the rotated image of whole rest image, we check if the current rest type is half, we assign the source as whole rest and rotate it by 180 degrees. SwitchableOptions { id: restOptions - + readonly property string restTypeImage: ((optionsRow.noteLengthName[currentIndex][1] === "Half") ? "Whole" : optionsRow.noteLengthName[currentIndex][1]).toLowerCase() - + source: "qrc:/gcompris/src/activities/piano_composition/resource/%1Rest.svg".arg(restTypeImage) nbOptions: optionsRow.noteLengthName.length onClicked: { background.restType = optionsRow.noteLengthName[currentIndex][1] emitOptionMessage(optionsRow.translatedRestNames[currentIndex]) } + width: optionsRow.iconsWidth * 0.9 + sourceSize.width: width rotation: optionsRow.noteLengthName[currentIndex][1] === "Half" ? 180 : 0 visible: restOptionsVisible anchors.topMargin: -3 anchors.left: parent.left anchors.leftMargin: 5 } - + BarButton { id: addRestButton - sourceSize.width: optionsRow.iconsWidth / 1.4 + width: restOptions.width + sourceSize.width: width source: "qrc:/gcompris/src/activities/piano_composition/resource/add.svg" - anchors.left: restOptions.right - anchors.leftMargin: 8 + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter visible: restOptions.visible - anchors.top: parent.top - anchors.topMargin: 10 onClicked: { emitOptionMessage(optionsRow.restAddedMessage[restOptionIndex]) parent.scale = 1 background.addMusicElementAndPushToStack(restType.toLowerCase(), "Rest") } } } + + BarButton { + id: changeAccidentalStyleButton + source: changeAccidentalStyleButtonVisible ? (piano.useSharpNotation ? "qrc:/gcompris/src/activities/piano_composition/resource/blacksharp.svg" + : "qrc:/gcompris/src/activities/piano_composition/resource/blackflat.svg") + : "" + sourceSize.width: optionsRow.iconsWidth + visible: changeAccidentalStyleButtonVisible + onClicked: { + piano.useSharpNotation = !piano.useSharpNotation + //: Sharp notes and Flat notes represents the accidental style of the notes in the music. + emitOptionMessage(piano.useSharpNotation ? qsTr("Sharp notes") : qsTr("Flat notes")) + } + } + + SwitchableOptions { + id: noteOptions + source: "qrc:/gcompris/src/activities/piano_composition/resource/genericNote%1.svg".arg(optionsRow.noteLengthName[currentIndex][1]) + nbOptions: optionsRow.noteLengthName.length + currentIndex: 2 + onClicked: { + background.currentType = optionsRow.noteLengthName[currentIndex][1] + emitOptionMessage(optionsRow.noteLengthName[currentIndex][0]) + } + visible: noteOptionsVisible + } + + BarButton { + id: openButton + source: "qrc:/gcompris/src/activities/piano_composition/resource/open.svg" + sourceSize.width: optionsRow.iconsWidth + visible: openButtonVisible + onClicked: openButtonClicked() + } + + BarButton { + id: saveButton + source: "qrc:/gcompris/src/activities/piano_composition/resource/save.svg" + sourceSize.width: optionsRow.iconsWidth + visible: saveButtonVisible + onClicked: saveButtonClicked() + } + + SwitchableOptions { + id: lyricsOrPianoModeOption + nbOptions: optionsRow.lyricsOrPianoModes.length + source: "qrc:/gcompris/src/activities/piano_composition/resource/%1-icon.svg".arg(optionsRow.lyricsOrPianoModes[currentIndex][1]) + visible: lyricsOrPianoModeOptionVisible + onClicked: emitOptionMessage(optionsRow.lyricsOrPianoModes[currentIndex][0]) + } + } diff --git a/src/activities/piano_composition/Piano_composition.qml b/src/activities/piano_composition/Piano_composition.qml index a88dc70c5..fd85e0d47 100644 --- a/src/activities/piano_composition/Piano_composition.qml +++ b/src/activities/piano_composition/Piano_composition.qml @@ -1,498 +1,502 @@ /* GCompris - Piano_composition.qml * * Copyright (C) 2016 Johnny Jazeix * Copyright (C) 2018 Aman Kumar Gupta * * Authors: * Beth Hadley (GTK+ version) * Johnny Jazeix (Qt Quick port) * Aman Kumar Gupta (Qt Quick port) - * + * Timothée Giet (refactoring) + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ import QtQuick 2.6 import QtQuick.Controls 1.5 import GCompris 1.0 import "../../core" import "qrc:/gcompris/src/core/core.js" as Core import "piano_composition.js" as Activity import "melodies.js" as Dataset ActivityBase { id: activity onStart: focus = true onStop: {} isMusicalActivity: true property bool horizontalLayout: background.width > background.height pageComponent: Rectangle { id: background anchors.fill: parent color: "#ABCDEF" signal start signal stop Component.onCompleted: { activity.start.connect(start) activity.stop.connect(stop) } Keys.onPressed: { var keyboardBindings = {} keyboardBindings[Qt.Key_1] = 0 keyboardBindings[Qt.Key_2] = 1 keyboardBindings[Qt.Key_3] = 2 keyboardBindings[Qt.Key_4] = 3 keyboardBindings[Qt.Key_5] = 4 keyboardBindings[Qt.Key_6] = 5 keyboardBindings[Qt.Key_7] = 6 keyboardBindings[Qt.Key_F1] = 1 keyboardBindings[Qt.Key_F2] = 2 keyboardBindings[Qt.Key_F3] = 3 keyboardBindings[Qt.Key_F4] = 4 keyboardBindings[Qt.Key_F5] = 5 if(event.key >= Qt.Key_1 && event.key <= Qt.Key_7) { piano.keyRepeater.itemAt(keyboardBindings[event.key]).whiteKey.keyPressed() } else if(event.key >= Qt.Key_F1 && event.key <= Qt.Key_F5) { if(piano.blackKeysEnabled) findBlackKey(keyboardBindings[event.key]) } if(event.key === Qt.Key_Left && shiftKeyboardLeft.visible) { piano.currentOctaveNb-- } if(event.key === Qt.Key_Right && shiftKeyboardRight.visible) { piano.currentOctaveNb++ } if(event.key === Qt.Key_Delete) { optionsRow.clearButtonClicked() } if(event.key === Qt.Key_Backspace) { optionsRow.undoButtonClicked() } if(event.key === Qt.Key_Space) { multipleStaff.play() } } function findBlackKey(keyNumber) { for(var i = 0; keyNumber; i++) { if(piano.keyRepeater.itemAt(i) == undefined) break if(piano.keyRepeater.itemAt(i).blackKey.visible) keyNumber-- if(keyNumber == 0) piano.keyRepeater.itemAt(i).blackKey.keyPressed() } } // Add here the QML items you need to access in javascript QtObject { id: items property Item main: activity.main property alias background: background property GCSfx audioEffects: activity.audioEffects property alias bar: bar property alias bonus: bonus property alias multipleStaff: multipleStaff property string staffLength: "short" property alias melodyList: melodyList property alias file: file property alias piano: piano property alias optionsRow: optionsRow property alias lyricsArea: lyricsArea } onStart: { Activity.start(items) } onStop: { Activity.stop() } property string currentType: "Quarter" property string restType: "Quarter" property string clefType: bar.level == 2 ? "Bass" : "Treble" property bool isLyricsMode: (optionsRow.lyricsOrPianoModeIndex === 1) && optionsRow.lyricsOrPianoModeOptionVisible File { id: file onError: console.error("File error: " + msg) } Item { id: clickedOptionMessage signal show(string message) onShow: { messageText.text = message messageAnimation.stop() messageAnimation.start() } width: horizontalLayout ? parent.width / 12 : parent.width / 6 height: width * 0.4 visible: false anchors.top: optionsRow.bottom anchors.horizontalCenter: optionsRow.horizontalCenter z: 5 Rectangle { id: messageRectangle width: messageText.contentWidth + 5 height: messageText.height + 5 anchors.centerIn: messageText color: "black" opacity: 0.5 border.width: 3 border.color: "black" radius: 15 } GCText { id: messageText anchors.fill: parent anchors.rightMargin: parent.width * 0.02 anchors.leftMargin: parent.width * 0.02 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter fontSizeMode: Text.Fit color: "white" } SequentialAnimation { id: messageAnimation onStarted: clickedOptionMessage.visible = true PauseAnimation { duration: 1000 } NumberAnimation { targets: [messageRectangle, messageText] property: "opacity" to: 0 duration: 200 } onStopped: { clickedOptionMessage.visible = false messageRectangle.opacity = 0.5 messageText.opacity = 1 } } } MelodyList { id: melodyList onClose: { visible = false piano.enabled = true focus = false activity.focus = true } } Rectangle { id: instructionBox radius: 10 - width: background.width * 0.98 - height: background.height / 9 - anchors.horizontalCenter: parent.horizontalCenter + width: background.width * 0.75 + height: background.height * 0.15 + anchors.right: parent.right + anchors.margins: 10 opacity: 0.8 border.width: 6 color: "white" border.color: "#87A6DD" GCText { id: instructionText color: "black" z: 3 anchors.fill: parent anchors.rightMargin: parent.width * 0.02 anchors.leftMargin: parent.width * 0.02 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter fontSizeMode: Text.Fit wrapMode: Text.WordWrap text: Activity.instructions[bar.level - 1].text } } MultipleStaff { id: multipleStaff - width: horizontalLayout ? parent.width * 0.50 : parent.width * 0.8 + width: horizontalLayout ? parent.width * 0.50 : parent.width * 0.6 height: horizontalLayout ? parent.height * 0.58 : parent.height * 0.3 nbStaves: 2 clef: clefType coloredNotes: ['C','D', 'E', 'F', 'G', 'A', 'B'] anchors.right: horizontalLayout ? parent.right: undefined anchors.horizontalCenter: horizontalLayout ? undefined : parent.horizontalCenter anchors.top: instructionBox.bottom anchors.topMargin: parent.height * 0.1 anchors.rightMargin: parent.width * 0.043 noteHoverEnabled: true onNoteClicked: { if(selectedIndex === noteIndex) selectedIndex = -1 else { selectedIndex = noteIndex background.clefType = musicElementModel.get(selectedIndex).soundPitch_ playNoteAudio(musicElementModel.get(selectedIndex).noteName_, musicElementModel.get(selectedIndex).noteType_, background.clefType, musicElementRepeater.itemAt(selectedIndex).duration) } } } GCButtonScroll { id: multipleStaffFlickButton anchors.right: parent.right anchors.rightMargin: 5 * ApplicationInfo.ratio anchors.verticalCenter: multipleStaff.verticalCenter width: horizontalLayout ? parent.width * 0.033 : parent.width * 0.06 height: width * heightRatio onUp: multipleStaff.flickableStaves.flick(0, multipleStaff.height * 1.3) onDown: multipleStaff.flickableStaves.flick(0, -multipleStaff.height * 1.3) upVisible: multipleStaff.flickableStaves.visibleArea.yPosition > 0 downVisible: (multipleStaff.flickableStaves.visibleArea.yPosition + multipleStaff.flickableStaves.visibleArea.heightRatio) < 1 } PianoOctaveKeyboard { id: piano - width: horizontalLayout ? parent.width * 0.34 : parent.width * 0.7 + width: horizontalLayout ? parent.width * 0.34 : parent.width * 0.6 height: horizontalLayout ? parent.height * 0.40 : parent.width * 0.26 anchors.horizontalCenter: horizontalLayout ? undefined : parent.horizontalCenter anchors.left: horizontalLayout ? parent.left : undefined anchors.leftMargin: parent.width * 0.04 anchors.top: horizontalLayout ? multipleStaff.top : multipleStaff.bottom anchors.topMargin: horizontalLayout ? parent.height * 0.08 : parent.height * 0.025 blackLabelsVisible: [3, 4, 5, 6, 7, 8].indexOf(items.bar.level) == -1 ? false : true useSharpNotation: bar.level != 4 blackKeysEnabled: bar.level > 2 visible: !background.isLyricsMode currentOctaveNb: (background.clefType === "Bass") ? 0 : 1 onNoteClicked: { parent.addMusicElementAndPushToStack(note, currentType) } } function addMusicElementAndPushToStack(noteName, noteType, elementType) { if(noteType === "Rest") elementType = "rest" else if(elementType == undefined) elementType = "note" var tempModel = multipleStaff.createNotesBackup() Activity.pushToStack(tempModel) multipleStaff.addMusicElement(elementType, noteName, noteType, false, true, background.clefType) } Image { id: shiftKeyboardLeft source: "qrc:/gcompris/src/core/resource/bar_previous.svg" sourceSize.width: piano.width / 7 width: sourceSize.width height: width fillMode: Image.PreserveAspectFit visible: (piano.currentOctaveNb > 0) && piano.visible anchors { verticalCenter: piano.verticalCenter right: piano.left } MouseArea { anchors.fill: parent onClicked: piano.currentOctaveNb-- } } Image { id: shiftKeyboardRight source: "qrc:/gcompris/src/core/resource/bar_next.svg" sourceSize.width: piano.width / 7 width: sourceSize.width height: width fillMode: Image.PreserveAspectFit visible: (piano.currentOctaveNb < piano.maxNbOctaves - 1) && piano.visible anchors { verticalCenter: piano.verticalCenter left: piano.right } MouseArea { anchors.fill: parent onClicked: piano.currentOctaveNb++ } } LyricsArea { id: lyricsArea + width: PianoOctaveKeyboard.width } GCCreationHandler { id: creationHandler onFileLoaded: { // We need to draw the notes twice since we first need to count the number of staffs needed for the melody (we get that from // the 1st redraw call), then we redraw the 2nd time to actually display the notes perfectly. This is done because for some reason, the // staves model is updated slower than the addition of notes, so the notes aggregates in their default position instead of // their required position, due to unavailability of the updated staff at that instant. So calculating the number of required staffs first seems the only solution for now. multipleStaff.redraw(data) multipleStaff.redraw(data) lyricsArea.resetLyricsArea() } onClose: { optionsRow.lyricsOrPianoModeIndex = 0 } } OptionsRow { id: optionsRow - anchors.top: instructionBox.bottom - anchors.topMargin: 10 - anchors.horizontalCenter: parent.horizontalCenter - iconsWidth: bar.height * 0.5 + columns: horizontalLayout ? 11 : 1 + anchors.top: horizontalLayout ? instructionBox.bottom : background.top + anchors.margins: 10 + anchors.left: background.left + iconsWidth: horizontalLayout ? background.width / 15 : (background.height - bar.height) / 12 noteOptionsVisible: bar.level > 4 playButtonVisible: true - clefButtonVisible: bar.level > 2 + keyOption.clefButtonVisible: bar.level > 2 clearButtonVisible: true undoButtonVisible: true openButtonVisible: bar.level > 6 saveButtonVisible: bar.level > 6 changeAccidentalStyleButtonVisible: bar.level >= 4 lyricsOrPianoModeOptionVisible: bar.level > 6 restOptionsVisible: bar.level > 5 bpmVisible: true onUndoButtonClicked: { Activity.undoChange() } onClearButtonClicked: { if((multipleStaff.musicElementModel.count > 1) && multipleStaff.selectedIndex === -1) { Core.showMessageDialog(main, qsTr("You have not selected any note. Do you want to erase all the notes?"), qsTr("Yes"), function() { Activity.undoStack = [] lyricsArea.resetLyricsArea() multipleStaff.eraseAllNotes() multipleStaff.nbStaves = 2 }, qsTr("No"), null, null ) } else if((multipleStaff.musicElementModel.count > 1) && !multipleStaff.musicElementModel.get(multipleStaff.selectedIndex).isDefaultClef_) { var noteIndex = multipleStaff.selectedIndex var tempModel = multipleStaff.createNotesBackup() Activity.pushToStack(tempModel) multipleStaff.eraseNote(noteIndex) } } onOpenButtonClicked: { dialogActivityConfig.active = true displayDialog(dialogActivityConfig) } onSaveButtonClicked: { var notesToSave = multipleStaff.createNotesBackup() // add bpm at start notesToSave.unshift({"bpm": multipleStaff.bpmValue}); print(notesToSave) creationHandler.saveWindow(notesToSave) } - onClefAdded: { + keyOption.onClefAdded: { var insertingIndex = multipleStaff.selectedIndex === -1 ? multipleStaff.musicElementModel.count : multipleStaff.selectedIndex var tempModel = multipleStaff.createNotesBackup() Activity.pushToStack(tempModel) multipleStaff.addMusicElement("clef", "", "", false, false, background.clefType) if(background.clefType === "Bass") piano.currentOctaveNb = 0 else piano.currentOctaveNb = 1 } onBpmDecreased: { if(multipleStaff.bpmValue - 1 >= 1) multipleStaff.bpmValue-- } onBpmIncreased: { multipleStaff.bpmValue++ } onEmitOptionMessage: clickedOptionMessage.show(message) } DialogActivityConfig { id: dialogActivityConfig content: Component { Column { id: column spacing: 10 width: dialogActivityConfig.width height: dialogActivityConfig.height GCText { text: qsTr("Select the type of melody to load.") fontSizeMode: mediumSize } Button { text: qsTr("Pre-defined melodies") onClicked: { melodyList.melodiesModel.clear() var dataset = Dataset.get() for(var i = 0; i < dataset.length; i++) { melodyList.melodiesModel.append(dataset[i]) } melodyList.visible = true piano.enabled = false melodyList.forceActiveFocus() dialogActivityConfig.close() } width: 150 * ApplicationInfo.ratio height: 60 * ApplicationInfo.ratio style: GCButtonStyle { theme: "dark" } } Button { text: qsTr("Your saved melodies") onClicked: { creationHandler.loadWindow() dialogActivityConfig.close() } width: 150 * ApplicationInfo.ratio height: 60 * ApplicationInfo.ratio style: GCButtonStyle { theme: "dark" } } } } onClose: home() } DialogHelp { id: dialogHelp onClose: home() } Bar { id: bar content: BarEnumContent { value: help | home | level } onHelpClicked: { displayDialog(dialogHelp) } onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onHomeClicked: activity.home() } Bonus { id: bonus Component.onCompleted: win.connect(Activity.nextLevel) } } } diff --git a/src/activities/piano_composition/SwitchableOptions.qml b/src/activities/piano_composition/SwitchableOptions.qml index 8a9dd9ffc..97d147f15 100644 --- a/src/activities/piano_composition/SwitchableOptions.qml +++ b/src/activities/piano_composition/SwitchableOptions.qml @@ -1,63 +1,78 @@ /* GCompris - SwitchableOptions.qml * * Copyright (C) 2018 Aman Kumar Gupta * * Authors: * Beth Hadley (GTK+ version) * Johnny Jazeix (Qt Quick port) * Aman Kumar Gupta (Qt Quick port) +* Timothée Giet (refactoring) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ import QtQuick 2.6 import GCompris 1.0 import "../../core" Image { id: switchableOptions property int currentIndex: 0 property int nbOptions: 1 - + signal clicked sourceSize.width: optionsRow.iconsWidth - anchors.top: parent.top - anchors.topMargin: -6 + MouseArea { + id: mouseArea anchors.fill: parent + hoverEnabled: true onClicked: { parent.currentIndex = (parent.currentIndex + 1) % nbOptions - clickAnimation.start() + //clickAnimation.start() parent.clicked() } } - - SequentialAnimation { - id: clickAnimation - NumberAnimation { - target: switchableOptions - property: "scale" - to: 0.7 - duration: 150 + + states: [ + State { + name: "notclicked" + PropertyChanges { + target: switchableOptions + scale: 1.0 + } + }, + State { + name: "clicked" + when: mouseArea.pressed + PropertyChanges { + target: switchableOptions + scale: 0.9 + } + }, + State { + name: "hover" + when: mouseArea.containsMouse + PropertyChanges { + target: switchableOptions + scale: 1.1 + } } - NumberAnimation { - target: switchableOptions - property: "scale" - to: 1 - duration: 150 - } - } + ] + + Behavior on scale { NumberAnimation { duration: 70 } } + Behavior on opacity { PropertyAnimation { duration: 200 } } } diff --git a/src/core/resource/bar_down.svg b/src/core/resource/bar_down.svg new file mode 100644 index 000000000..215a391d7 --- /dev/null +++ b/src/core/resource/bar_down.svg @@ -0,0 +1,97 @@ + + + + + + + + + + + + image/svg+xml + + + 2015 + + + Timothée Giet + + + + + + + + + + + + + + + + + + + diff --git a/src/core/resource/bar_up.svg b/src/core/resource/bar_up.svg new file mode 100644 index 000000000..bb9ae5e39 --- /dev/null +++ b/src/core/resource/bar_up.svg @@ -0,0 +1,97 @@ + + + + + + + + + + + + image/svg+xml + + + 2015 + + + Timothée Giet + + + + + + + + + + + + + + + + + + +