diff --git a/src/activities/clockgame/Clockgame.qml b/src/activities/clockgame/Clockgame.qml index 5f2a29458..a14558e9a 100644 --- a/src/activities/clockgame/Clockgame.qml +++ b/src/activities/clockgame/Clockgame.qml @@ -1,527 +1,526 @@ /* GCompris - Clockgame.qml * * Copyright (C) 2014 Stephane Mankowski * * Authors: * Bruno Coudoin (GTK+ version) * Stephane Mankowski (Qt Quick port) * * 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 QtGraphicalEffects 1.0 import GCompris 1.0 import "../../core" import "clockgame.js" as Activity ActivityBase { id: activity onStart: focus = true onStop: { } pageComponent: Image { id: background source: "qrc:/gcompris/src/activities/menu/resource/background.svg" sourceSize.width: Math.max(parent.width, parent.height) fillMode: Image.PreserveAspectCrop anchors.fill: parent signal start signal stop Component.onCompleted: { dialogActivityConfig.initialize() activity.start.connect(start) activity.stop.connect(stop) } // Add here the QML items you need to access in javascript QtObject { id: items property alias background: background property alias bar: bar property alias bonus: bonus property int targetH: 12 property int targetM: 0 property int targetS: 0 property int currentH: 1 property int currentM: 25 property int currentS: 43 property int numberOfTry: 3 property int currentTry: 0 property var levels: activity.datasetLoader.data property bool minutesHandVisible property bool secondsHandVisible property bool zonesVisible } onStart: { Activity.start(items) } onStop: { Activity.stop() } Score { id: score anchors { bottom: bar.top bottomMargin: 10 * ApplicationInfo.ratio right: bar.right rightMargin: 10 * ApplicationInfo.ratio left: parent.left leftMargin: 10 * ApplicationInfo.ratio top: undefined } numberOfSubLevels: items.numberOfTry currentSubLevel: items.currentTry + 1 } /* Target text */ Rectangle { id: questionItemBackground color: "#c0ceb339" border.width: 0 radius: 10 z: 10 anchors { horizontalCenter: parent.horizontalCenter top: parent.top margins: 10 } height: questionItem.height + anchors.margins * 2 width: questionItem.width + anchors.margins * 2 Behavior on height { PropertyAnimation { duration: 100 } } GCText { id: questionItem text: qsTr("Set the watch to:") + " " + //~ singular %n hour //~ plural %n hours addNbsp(qsTr("%n hour(s)", "", items.targetH)) + " " + //~ singular %n minute //~ plural %n minutes addNbsp(qsTr("%n minute(s)", "", items.targetM)) + //~ singular %n second //~ plural %n seconds (s.visible ? " " + addNbsp(qsTr("%n second(s)", "", items.targetS)) : "") fontSize: mediumSize textFormat: Text.RichText font.weight: Font.DemiBold color: "white" horizontalAlignment: Text.AlignHCenter wrapMode: Text.WordWrap width: background.width anchors { horizontalCenter: parent.horizontalCenter top: parent.top margins: 10 } // We don't want the wrapping to happen anywhere, set no break space function addNbsp(str) { return str.replace(" ", " "); } } } /* The clock */ Image { id: clock source: activity.resourceUrl + "clock_bg.svg" anchors.centerIn: parent fillMode: Image.PreserveAspectFit sourceSize.height: parent.height property int radius: Math.min(background.width * 1.4, background.height) /* The yellow zones */ Image { id: zones source: activity.resourceUrl + "clock_zones.svg" anchors.centerIn: parent fillMode: Image.PreserveAspectFit sourceSize.height: parent.height * 0.7 visible: items.zonesVisible z: 2 } /* The setter */ Image { id: setter source: activity.resourceUrl + "clock_setter.svg" anchors { verticalCenter: parent.verticalCenter left: parent.right leftMargin: -10 } z: 1 } /* The minutes */ Repeater { model: 60 GCText { text: index + 1 font { pointSize: NaN // need to clear font.pointSize explicitly pixelSize: Math.max( (index + 1) % 5 === 0 ? clock.radius / 40 : clock.radius / 45, 1) bold: items.currentM === ((index + 1) % 60) || (items.currentS === ((index + 1) % 60) && s.visible) underline: items.currentM === ((index + 1) % 60) || (items.currentS === ((index + 1) % 60) && s.visible) } anchors { verticalCenter: clock.verticalCenter horizontalCenter: clock.horizontalCenter verticalCenterOffset: -clock.radius * 0.33 * Math.cos( (index + 1) * 2 * Math.PI / 60) horizontalCenterOffset: clock.radius * 0.33 * Math.sin( (index + 1) * 2 * Math.PI / 60) } z: 4 color: "#d56a3a" visible: items.minutesHandVisible } } /* The seconds */ Repeater { model: 60 Rectangle { color: "#d56a3a" width: clock.radius * 0.02 height: 2 rotation: 90 + (index + 1) * 360 / 60 radius: 1 anchors { verticalCenter: clock.verticalCenter horizontalCenter: clock.horizontalCenter verticalCenterOffset: -clock.radius * 0.3 * Math.cos( (index + 1) * 2 * Math.PI / 60) horizontalCenterOffset: clock.radius * 0.3 * Math.sin( (index + 1) * 2 * Math.PI / 60) } z: 4 visible: items.secondsHandVisible } } /* The hours */ Repeater { model: 12 GCText { text: index + 1 font { pointSize: NaN // need to clear font.pointSize explicitly pixelSize: Math.max(clock.radius / 30, 1) bold: items.currentH === ((index + 1) % 12) underline: items.currentH === ((index + 1) % 12) } anchors { verticalCenter: clock.verticalCenter horizontalCenter: clock.horizontalCenter verticalCenterOffset: -clock.radius * 0.26 * Math.cos( (index + 1) * 2 * Math.PI / 12) horizontalCenterOffset: clock.radius * 0.26 * Math.sin( (index + 1) * 2 * Math.PI / 12) } z: 4 color: "#3a81d5" visible: items.bar.level < 7 } } Repeater { model: 12 Rectangle { color: "#3a81d5" width: clock.radius * 0.03 height: 4 rotation: 90 + (index + 1) * 360 / 12 radius: 1 anchors { verticalCenter: clock.verticalCenter horizontalCenter: clock.horizontalCenter verticalCenterOffset: -clock.radius * 0.3 * Math.cos( (index + 1) * 2 * Math.PI / 12) horizontalCenterOffset: clock.radius * 0.3 * Math.sin( (index + 1) * 2 * Math.PI / 12) } z: 4 visible: items.bar.level < 9 || (items.bar.level === 9 && ((index + 1) % 3) === 0) } } /* Help text */ GCText { id: helper text: Activity.get2CharValue( items.currentH) + ":" + Activity.get2CharValue( items.currentM) + ":" + Activity.get2CharValue( items.currentS) font.pointSize: NaN font.pixelSize: Math.max(clock.radius / 30, 1) anchors { verticalCenter: clock.verticalCenter horizontalCenter: clock.horizontalCenter verticalCenterOffset: clock.radius * 0.2 } z: 4 color: "#2a2a2a" visible: false } /* Arrow H */ Rectangle { id: h property alias angle: roth.angle height: clock.radius * 0.2 width: height / 10 radius: width / 2 color: "#3a81d5" transform: Rotation { id: roth origin.x: h.width / 2 origin.y: 0 angle: (180 + 360 * (items.currentH / 12 + items.currentM / 60 / 12)) % 360 Behavior on angle { RotationAnimation { duration: 100 direction: RotationAnimation.Shortest } } } anchors { verticalCenter: clock.verticalCenter horizontalCenter: clock.horizontalCenter verticalCenterOffset: h.height / 2 } z: 5 } /* Arrow M */ Rectangle { id: m property alias angle: rotm.angle height: clock.radius * 0.28 width: height / 20 radius: width / 2 color: "#d56a3a" visible: items.minutesHandVisible transform: Rotation { id: rotm origin.x: m.width / 2 origin.y: 0 angle: (180 + 360 * (items.currentM / 60 + items.currentS / 60 / 60)) % 360 Behavior on angle { RotationAnimation { duration: 100 direction: RotationAnimation.Shortest } } } anchors { verticalCenter: clock.verticalCenter horizontalCenter: clock.horizontalCenter verticalCenterOffset: m.height / 2 } z: 6 } /* Arrow S */ Rectangle { id: s property alias angle: rots.angle height: clock.radius * 0.32 width: height / 30 radius: width / 2 color: "#2ccf4b" visible: items.secondsHandVisible transform: Rotation { id: rots origin.x: s.width / 2 origin.y: 0 angle: (180 + 360 * items.currentS / 60) % 360 Behavior on angle { RotationAnimation { duration: 100 direction: RotationAnimation.Shortest } } } anchors { verticalCenter: clock.verticalCenter horizontalCenter: clock.horizontalCenter verticalCenterOffset: s.height / 2 } z: 7 } /* Center */ Rectangle { id: center color: "#2a2a2a" height: clock.radius / 25 width: height radius: width / 2 anchors.centerIn: clock z: 8 } /* Manage the move */ MouseArea { anchors.fill: parent acceptedButtons: Qt.LeftButton onPressed: { /* Find the closer Arrow */ var a = (270 + 360 + 180 * Math.atan2( mouseY - (center.y + center.height / 2), mouseX - (center.x + center.width / 2)) / Math.PI) % 360 var agnh = h.angle var angm = m.angle var angs = s.angle var dh = Math.min(Math.abs(a - agnh), Math.abs(a - agnh - 360), Math.abs(a - agnh + 360)) var dm = m.visible ? Math.min(Math.abs(a - angm), Math.abs(a - angm - 360), Math.abs(a - angm + 360)) : 9999 var ds = s.visible ? Math.min( Math.abs(a - angs), Math.abs(a - angs - 360), Math.abs(a - angs + 360)) : 9999 var dmin = Math.min(dh, dm, ds) if (dh === dmin) { Activity.selectedArrow = h } else if (dm === dmin) { Activity.selectedArrow = m } else { Activity.selectedArrow = s } } onMouseXChanged: { /* Move */ if (Activity.selectedArrow !== null) { var a = (270 + 360 + 180 * Math.atan2( mouseY - (center.y + center.height / 2), mouseX - (center.x + center.width / 2)) / Math.PI) % 360 var previousM = items.currentM var previousS = items.currentS if (Activity.selectedArrow === h) { items.currentH = Math.round( 12 * ((a - 180) / 360 - items.currentM / 60 / 12) + 12) % 12 } else if (Activity.selectedArrow === m) { items.currentM = Math.round( 60 * ((a - 180) / 360 - items.currentS / 60 / 60) + 60) % 60 } else { items.currentS = Math.round( 60 * (a - 180) / 360 + 60) % 60 } if (previousS > 45 && items.currentS < 15) items.currentM = (items.currentM + 1 + 60) % 60 if (previousS < 15 && items.currentS > 45) items.currentM = (items.currentM - 1 + 60) % 60 if (previousM > 45 && items.currentM < 15) items.currentH = (items.currentH + 1 + 12) % 12 if (previousM < 15 && items.currentM > 45) items.currentH = (items.currentH - 1 + 12) % 12 } } } } BarButton { id: okButton source: "qrc:/gcompris/src/core/resource/bar_ok.svg" sourceSize.width: 60 * ApplicationInfo.ratio anchors.bottom: bar.top anchors.bottomMargin: parent.width * 0.03 anchors.left: score.right anchors.leftMargin: parent.width * 0.7 ParticleSystemStarLoader { id: okButtonParticles clip: false } MouseArea { id: okButtonMouseArea anchors.fill: parent onClicked: { Activity.checkAnswer() } } } DialogChooseLevel { id: dialogActivityConfig currentActivity: activity.activityInfo onSaveData: { levelFolder = dialogActivityConfig.chosenLevels currentActivity.currentLevels = dialogActivityConfig.chosenLevels ApplicationSettings.setCurrentLevels(currentActivity.name, dialogActivityConfig.chosenLevels) - // restart activity on saving - background.start() } onClose: { home() } onStartActivity: { + background.stop() background.start() } } DialogHelp { id: dialogHelp onClose: home() } Bar { id: bar content: BarEnumContent { value: help | home | level | hint | activityConfig } onHelpClicked: { displayDialog(dialogHelp) } onHintClicked: { helper.visible = !helper.visible } onActivityConfigClicked: { displayDialog(dialogActivityConfig) } onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onHomeClicked: activity.home() } Bonus { id: bonus Component.onCompleted: win.connect(Activity.nextTry) } } } diff --git a/src/activities/enumerate/Enumerate.qml b/src/activities/enumerate/Enumerate.qml index 8893655e7..1e83f4044 100644 --- a/src/activities/enumerate/Enumerate.qml +++ b/src/activities/enumerate/Enumerate.qml @@ -1,282 +1,281 @@ /* GCompris - Enumerate.qml * * Copyright (C) 2014 Thib ROMAIN * * Authors: * Bruno Coudoin (GTK+ version) * Thib ROMAIN (Qt Quick port) * * 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 "." import "../../core" import "enumerate.js" as Activity ActivityBase { id: activity focus: true onStart: {} onStop: {} pageComponent: Image { id: background anchors.fill: parent signal start signal stop fillMode: Image.PreserveAspectCrop source: Activity.url + "background.svg" sourceSize.width: Math.max(parent.width, parent.height) Component.onCompleted: { dialogActivityConfig.initialize() activity.start.connect(start) activity.stop.connect(stop) } onStart: { Activity.start(items); keyboard.populate(); } onStop: { Activity.stop() } //instruction rectangle Rectangle { id: instruction anchors { top: parent.top topMargin: 5 horizontalCenter: parent.horizontalCenter } height: instructionTxt.contentHeight * 1.1 width: Math.max(Math.min(parent.width * 0.8, instructionTxt.text.length * 10), parent.width * 0.3) opacity: 0.8 visible: items.levels radius: 10 border.width: 2 z: instruction.opacity === 0 ? -10 : 10 border.color: "#DDD" color: "#373737" Behavior on opacity { PropertyAnimation { duration: 200 } } //shows/hides the Instruction MouseArea { anchors.fill: parent onClicked: instruction.opacity = instruction.opacity == 0 ? 0.8 : 0 } GCText { id: instructionTxt anchors { top: parent.top topMargin: 5 horizontalCenter: parent.horizontalCenter } opacity: instruction.opacity z: instruction.z fontSize: smallSize color: "white" text: items.instructionText horizontalAlignment: Text.AlignHCenter width: parent.width * 0.8 wrapMode: TextEdit.WordWrap } } Keys.onDownPressed: { if(++answerColumn.currentIndex >= answerColumn.count) answerColumn.currentIndex = 0 Activity.registerAnswerItem(answerColumn.itemAt(answerColumn.currentIndex)) } Keys.onUpPressed: { if(--answerColumn.currentIndex < 0) answerColumn.currentIndex = answerColumn.count - 1 Activity.registerAnswerItem(answerColumn.itemAt(answerColumn.currentIndex)) } QtObject { id: items property alias background: background property alias bar: bar property alias bonus: bonus property alias okButton: okButton property alias answerColumn: answerColumn property alias itemListModel: itemList.model property string instructionText: "" property alias score: score property var levels: activity.datasetLoader.data.length !== 0 ? activity.datasetLoader.data : null } DropArea { id: dropableArea anchors.left: background.left anchors.bottom: background.bottom width: background.width height: background.height } Image { source: Activity.url + 'turtle.svg' anchors.fill: parent fillMode: Image.PreserveAspectFit sourceSize.width: Math.max(parent.width, parent.height) } Column { id: answer anchors { left: parent.left top: parent.top margins: 10 } spacing: 5 Repeater { id: answerColumn property int currentIndex onModelChanged: currentIndex = count - 1 AnswerArea { imgPath: modelData focus: true audioEffects: activity.audioEffects state: "default" } } add: Transition { NumberAnimation { properties: "x,y"; duration: 200 } } } // Reposition the items to find when whidh or height changes onWidthChanged: { for(var i in itemList.model) itemList.itemAt(i).positionMe() } onHeightChanged: { for(var i in itemList.model) itemList.itemAt(i).positionMe() } Repeater { id: itemList ItemToEnumerate { source: modelData main: background } } VirtualKeyboard { id: keyboard anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter function populate() { layout = [ [ { label: "0" }, { label: "1" }, { label: "2" }, { label: "3" }, { label: "4" }, { label: "5" }, { label: "6" }, { label: "7" }, { label: "8" }, { label: "9" } ] ] } onKeypress: Activity.currentAnswerItem.appendText(text) onError: console.log("VirtualKeyboard error: " + msg); } DialogChooseLevel { id: dialogActivityConfig currentActivity: activity.activityInfo onSaveData: { levelFolder = dialogActivityConfig.chosenLevels currentActivity.currentLevels = dialogActivityConfig.chosenLevels ApplicationSettings.setCurrentLevels(currentActivity.name, dialogActivityConfig.chosenLevels) activity.focus = true - background.stop() - background.start() } onLoadData: { if(activityData) { Activity.initLevel() } } onClose: { home() } onStartActivity: { + background.stop() background.start() } } Score { id: score anchors.top: okButton.bottom anchors.bottom: keyboard.top anchors.rightMargin: 10 * ApplicationInfo.ratio onStop: Activity.initSubLevel() } DialogHelp { id: dialogHelp onClose: home() } Bar { id: bar anchors.bottom: keyboard.top content: BarEnumContent { value: help | home | level | activityConfig } onHelpClicked: { displayDialog(dialogHelp) } onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onHomeClicked: activity.home() onActivityConfigClicked: { displayDialog(dialogActivityConfig) } } BarButton { id: okButton anchors { bottom: bar.top right: parent.right rightMargin: 9 * ApplicationInfo.ratio bottomMargin: 9 * ApplicationInfo.ratio } source: "qrc:/gcompris/src/core/resource/bar_ok.svg" sourceSize.width: 80 * ApplicationInfo.ratio onClicked: Activity.checkAnswers(); } Keys.onReturnPressed: okButton.enabled === true ? Activity.checkAnswers() : "" Keys.onEnterPressed: okButton.enabled === true ? Activity.checkAnswers() : "" Bonus { id: bonus Component.onCompleted: win.connect(Activity.nextLevel) } } } diff --git a/src/activities/gletters/Gletters.qml b/src/activities/gletters/Gletters.qml index 3e82c4359..4182dcdbd 100644 --- a/src/activities/gletters/Gletters.qml +++ b/src/activities/gletters/Gletters.qml @@ -1,278 +1,276 @@ /* GCompris - gletters.qml * * Copyright (C) 2014 Holger Kaelberer * * Authors: * Bruno Coudoin (GTK+ version) * Holger Kaelberer (Qt Quick port) * * 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" import "gletters.js" as Activity ActivityBase { id: activity // Overload this in your activity to change it // Put you default-.json files in it property string dataSetUrl: "qrc:/gcompris/src/activities/gletters/resource/" property bool uppercaseOnly: false property int speedSetting: 10 property string activityName: "gletters" property bool useDataset: false /* mode of the activity, "letter" (gletters) or "word" (wordsgame):*/ property string mode: "letter" // Override if you want to replace texts by your image function getImage(key) { return "" } // Override if you want to replace texts by the domino function getDominoValues(key) { return [] } onStart: focus = true onStop: {} // When going on configuration, it steals the focus and re set it to the activity. // We need to set it back to the textinput item in order to have key events. onFocusChanged: { if(focus) { Activity.focusTextInput() } } pageComponent: Image { id: background source: activity.dataSetUrl + "background.svg" fillMode: Image.PreserveAspectCrop sourceSize.height: parent.height signal start signal stop // system locale by default property string locale: "system" Component.onCompleted: { dialogActivityConfig.initialize() activity.start.connect(start) activity.stop.connect(stop) } QtObject { id: items property Item main: activity.main property Item ourActivity: activity property GCAudio audioVoices: activity.audioVoices property var levels: activity.datasetLoader.data.length !== 0 ? activity.datasetLoader.data : null property string instructionText: "" property alias background: background property alias bar: bar property alias bonus: bonus property alias wordlist: wordlist property alias score: score property alias keyboard: keyboard property alias wordDropTimer: wordDropTimer property GCSfx audioEffects: activity.audioEffects property alias locale: background.locale property alias textinput: textinput } onStart: { // for smallnumbers and smallnumbers2, we want to have the application locale, not the system one if(activity.activityName !== "gletters") { var overridenLocale = ApplicationSettings.locale // Remove .UTF-8 if(overridenLocale.indexOf('.') != -1) { overridenLocale = overridenLocale.substring(0, overridenLocale.indexOf('.')) } background.locale = overridenLocale } Activity.start(items, uppercaseOnly, mode, speedSetting); Activity.focusTextInput() } onStop: { Activity.stop() } //instruction rectangle Rectangle { id: instruction anchors { top: parent.top topMargin: 5 horizontalCenter: parent.horizontalCenter } height: instructionTxt.contentHeight * 1.1 width: Math.max(Math.min(parent.width * 0.8, instructionTxt.text.length * 10), parent.width * 0.3) opacity: 0.8 visible: items.levels radius: 10 border.width: 2 z: 10 border.color: "#DDD" color: "#373737" Behavior on opacity { PropertyAnimation { duration: 200 } } //shows/hides the Instruction MouseArea { anchors.fill: parent onClicked: instruction.opacity = instruction.opacity == 0 ? 0.8 : 0 } GCText { id: instructionTxt anchors { top: parent.top topMargin: 5 horizontalCenter: parent.horizontalCenter } opacity: instruction.opacity z: instruction.z fontSize: smallSize color: "white" text: items.instructionText horizontalAlignment: Text.AlignHCenter width: parent.width * 0.8 wrapMode: TextEdit.WordWrap } } TextInput { // Helper element to capture composed key events like french ô which // are not available via Keys.onPressed() on linux. Must be // disabled on mobile! id: textinput anchors.centerIn: background enabled: !ApplicationInfo.isMobile focus: true visible: false onTextChanged: { if (text != "") { Activity.processKeyPress(text); text = ""; } } } DialogChooseLevel { id: dialogActivityConfig currentActivity: activity.activityInfo onClose: { home() } onSaveData: { levelFolder = dialogActivityConfig.chosenLevels currentActivity.currentLevels = dialogActivityConfig.chosenLevels ApplicationSettings.setCurrentLevels(currentActivity.name, dialogActivityConfig.chosenLevels) - // todo this is triggered before the change of locale, so it is not taken in account! - background.stop() - background.start() } onLoadData: { if (activity.activityName === "gletters") { if(activityData && activityData["locale"]) { background.locale = activityData["locale"]; activity.uppercaseOnly = activityData["uppercaseMode"] === "true" ? true : false; } } else if (activity.activityName === "smallnumbers2") { if(activityData && activityData["mode"]) { activity.dominoMode = activityData["mode"]; } } if(activityData && activityData["speedSetting"]) { activity.speedSetting = activityData["speedSetting"]; } } onStartActivity: { + background.stop() background.start() } } DialogHelp { id: dialogHelp onClose: home() } Bar { id: bar anchors.bottom: keyboard.top content: BarEnumContent { value: help | home | level | activityConfig } onHelpClicked: { displayDialog(dialogHelp) } onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onHomeClicked: activity.home() onActivityConfigClicked: { displayDialog(dialogActivityConfig) } } Bonus { id: bonus interval: 2000 Component.onCompleted: win.connect(Activity.nextLevel) } Score { id: score anchors.right: parent.right anchors.rightMargin: 10 * ApplicationInfo.ratio anchors.bottom: bar.top } VirtualKeyboard { id: keyboard anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter width: parent.width onKeypress: Activity.processKeyPress(text) onError: console.log("VirtualKeyboard error: " + msg); } Wordlist { id: wordlist defaultFilename: activity.dataSetUrl + "default-en.json" // To switch between locales: xx_XX stored in configuration and // possibly correct xx if available (ie fr_FR for french but dataset is fr.) useDefault: false filename: "" onError: console.log("Gletters: Wordlist error: " + msg); } Timer { id: wordDropTimer repeat: false onTriggered: Activity.dropWord(); } } } diff --git a/src/activities/guessnumber/Guessnumber.qml b/src/activities/guessnumber/Guessnumber.qml index cc06fba85..a64b836d4 100644 --- a/src/activities/guessnumber/Guessnumber.qml +++ b/src/activities/guessnumber/Guessnumber.qml @@ -1,190 +1,190 @@ /* GCompris - guessnumber.qml * * Copyright (C) 2014 Thib ROMAIN * * Authors: * Clement Coudoin (GTK+ version) * Thib ROMAIN (Qt Quick port) * * 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" import "guessnumber.js" as Activity ActivityBase { id: activity onStart: focus = true onStop: {} property alias currentActivity: activity.activityInfo pageComponent: Image { id: background fillMode: Image.PreserveAspectCrop source: "resource/cave.svg" anchors.fill: parent signal start signal stop onWidthChanged: helico.init() onHeightChanged: helico.init() Component.onCompleted: { dialogActivityConfig.initialize() activity.start.connect(start) activity.stop.connect(stop) } // Add here the QML items you need to access in javascript QtObject { id: items property Item main: activity.main property alias background: background property alias bar: bar property alias bonus: bonus property alias helico: helico property alias textArea: textArea property alias infoText: userInfo property alias answerArea: answerArea property var levels: activity.datasetLoader.data.length !== 0 ? activity.datasetLoader.data : null property int currentMax: 0 property alias numpad: numpad property int maxSize: 120 property int minSize: 80 property int barHeightAddon: ApplicationSettings.isBarHidden ? 1 : 3 property int size: Math.min(background.width / 9, background.height / (8 + barHeightAddon)) } onStart: { Activity.start(items) } onStop: { Activity.stop() } Helico { id: helico fillMode: "PreserveAspectFit" sourceSize.height: items.maxSize * ApplicationInfo.ratio height: (items.size>items.minSize) ? (items.size * * Authors: * (GTK+ version) * Thibaut ROMAIN (Qt Quick port) * * 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" import "magic-hat.js" as Activity import "." ActivityBase { id: activity onStart: focus = true onStop: {} property string mode: "minus" pageComponent: Image { id: background anchors.fill: parent source: Activity.url + "background.svg" sourceSize.width: Math.max(parent.width, parent.height) fillMode: Image.PreserveAspectCrop property int starSize: Math.min(rightLayout.width / 12, background.height / 21) signal start signal stop property var starColors : ["1", "2", "3"] Component.onCompleted: { dialogActivityConfig.initialize() activity.start.connect(start) activity.stop.connect(stop) } onStart: Activity.start(items, mode) onStop: Activity.stop() property bool vert: background.width >= (background.height - okButton.height) // 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 var levels: activity.datasetLoader.data property alias bonus: bonus property alias hat: theHat property alias introductionText: introText property var repeatersList: [repeaterFirstRow, repeaterSecondRow, repeaterAnswerRow] } Item { id: mainlayout anchors.left: background.left width: background.width * 0.4 height: background.height z: 11 Hat { id: theHat starsSize: background.starSize audioEffects: activity.audioEffects } GCText { id: introText anchors { horizontalCenter: parent.horizontalCenter top: parent.top topMargin: 20 * ApplicationInfo.ratio } width: parent.width - 5 * ApplicationInfo.ratio fontSize: regularSize font.bold: true style: Text.Outline styleColor: "black" color: "white" wrapMode: TextEdit.WordWrap horizontalAlignment: TextEdit.AlignHCenter text: qsTr("Click on the hat to begin the game") } GCText { //: The math operation text: mode == "minus" ? qsTr("−") : qsTr("+") anchors.right: mainlayout.right anchors.rightMargin: 10 y: secondRow.y fontSize: 66 color: "white" style: Text.Outline styleColor: "black" } } Grid { id: rightLayout anchors { left: mainlayout.right right: background.vert ? okButton.left : background.right rightMargin: background.vert ? 0 : 10 verticalCenter: background.verticalCenter verticalCenterOffset: background.height/8 } height: background.height columns: 1 Column { id: firstRow height: background.starSize * 4 spacing: 5 z: 10 Repeater { id: repeaterFirstRow model: 3 StarsBar { barGroupIndex: 0 barIndex: index width: rightLayout.width backgroundColor: "grey" starsColor: starColors[index] theHat: items.hat starsSize: background.starSize opacity: 0 } } } Column { id: secondRow height: background.starSize * 4 spacing: 5 z: 9 Repeater { id: repeaterSecondRow model: 3 StarsBar { barGroupIndex: 1 barIndex: index width: rightLayout.width backgroundColor: "grey" starsColor: starColors[index] theHat: items.hat starsSize: background.starSize opacity: 0 } } } Rectangle { width: (background.starSize + 5) * 10 - 5 height: 5 * ApplicationInfo.ratio color: "white" } Rectangle { width: (background.starSize + 5) * 10 - 5 height: 10 * ApplicationInfo.ratio opacity: 0 } Column { id: answerRow height: background.starSize * 4 spacing: 5 Repeater { id: repeaterAnswerRow model: 3 StarsBar { barGroupIndex: 2 barIndex: index width: rightLayout.width backgroundColor: "#53b9c9" starsColor: starColors[index] authorizeClick: false theHat: items.hat starsSize: background.starSize opacity: 0 } } } } DialogChooseLevel { id: dialogActivityConfig currentActivity: activity.activityInfo onSaveData: { levelFolder = dialogActivityConfig.chosenLevels currentActivity.currentLevels = dialogActivityConfig.chosenLevels ApplicationSettings.setCurrentLevels(currentActivity.name, dialogActivityConfig.chosenLevels) - background.start() } onClose: { home() } onStartActivity: { + background.stop() background.start() } } DialogHelp { id: dialogHelp onClose: home() } Bar { id: bar content: BarEnumContent { value: help | home | level | activityConfig } onHelpClicked: { displayDialog(dialogHelp) } onActivityConfigClicked: { displayDialog(dialogActivityConfig) } onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onHomeClicked: activity.home() } BarButton { id: okButton anchors { bottom: bar.top right: parent.right rightMargin: 10 * ApplicationInfo.ratio bottomMargin: 10 * ApplicationInfo.ratio } source: "qrc:/gcompris/src/core/resource/bar_ok.svg" sourceSize.width: 60 * ApplicationInfo.ratio onClicked: Activity.verifyAnswer() } Bonus { id: bonus Component.onCompleted: win.connect(Activity.nextLevel) } } } diff --git a/src/activities/money/MoneyCore.qml b/src/activities/money/MoneyCore.qml index 0d4e57563..64051533f 100644 --- a/src/activities/money/MoneyCore.qml +++ b/src/activities/money/MoneyCore.qml @@ -1,297 +1,296 @@ /* GCompris - MoneyCore.qml * * Copyright (C) 2014 Bruno Coudoin * * Authors: * Bruno Coudoin (GTK+ version) * Bruno Coudoin (Qt Quick port) * * 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" import "money.js" as Activity ActivityBase { id: activity onStart: focus = true onStop: {} property var dataset pageComponent: Image { id: background anchors.fill: parent source: Activity.url + "/background.svg" sourceSize.width: parent.width signal start signal stop Component.onCompleted: { dialogActivityConfig.initialize() activity.start.connect(start) activity.stop.connect(stop) } // 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 answerModel: answerArea.pocketModel property alias pocketModel: pocketArea.pocketModel property alias store: store property alias instructions: instructions property alias tux: tux property var levels: activity.datasetLoader.data property alias tuxMoney: tuxMoney property alias bar: bar property alias bonus: bonus property int itemIndex property int pocketRows property bool verticalOrientation: background.height > background.width - bar.height property var selectedArea property alias pocket: pocketArea.answer property alias answer: answerArea.answer } onStart: { Activity.start(items, dataset) } onStop: { Activity.stop() } Column { id: columnLayout spacing: 10 x: parent.width * 0.05 y: parent.height * 0.05 width: parent.width * 0.9 property int nbColumns: 5 property int nbLines: (items.verticalOrientation) ? items.pocketRows + 1 : items.pocketRows property int itemWidth: Math.min(width / nbColumns - 10 - 10 / nbColumns, parent.height * 0.4 / nbLines - 10 - 10 / nbLines) property int itemHeight: itemWidth * 0.59 // === The Answer Area === MoneyArea { id: answerArea onTransaction: Activity.unpay(index) } // === The Store Area === property int nbStoreColumns: activity.dataset === "BACK_WITHOUT_CENTS" || activity.dataset === "BACK_WITH_CENTS" ? store.model.length + 1 : store.model.length //tempSpace is a workaround to replace instructionsArea.realHeight that is freezing with Qt-5.9.1 property int tempSpace: bar.level === 1 ? 140 + columnLayout.spacing : 0 property int itemStoreWidth: Math.min((columnLayout.width - storeAreaFlow.spacing * nbStoreColumns) / nbStoreColumns, (parent.height - answerArea.height - pocketArea.height - bar.height) * 0.8) - tempSpace property int itemStoreHeight: itemStoreWidth Rectangle { id: storeArea height: columnLayout.itemStoreHeight + 10 width: columnLayout.width color: "#55333333" border.color: "black" border.width: 2 radius: 5 Flow { id: storeAreaFlow anchors.topMargin: 4 anchors.bottomMargin: 4 anchors.leftMargin: 20 anchors.rightMargin: 20 anchors.fill: parent spacing: 40 add: Transition { NumberAnimation { properties: "x" from: parent.width * 0.05 duration: 300 } } Image { id: tux visible: activity.dataset === "BACK_WITHOUT_CENTS" || activity.dataset === "BACK_WITH_CENTS" source: Activity.url + "/tux.svg" sourceSize.height: columnLayout.itemStoreHeight sourceSize.width: columnLayout.itemStoreHeight Repeater { id: tuxMoney Image { source: modelData.img sourceSize.height: columnLayout.itemStoreHeight * 0.3 x: tux.x + index * 50 y: tux.y + tux.height / 2 + index * 20 } } } Repeater { id: store Image { source: modelData.img sourceSize.height: columnLayout.itemStoreHeight sourceSize.width: columnLayout.itemStoreHeight GCText { text: modelData.price height: parent.height width: parent.width fontSizeMode: Text.Fit font.weight: Font.DemiBold style: Text.Outline styleColor: "black" color: "white" anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top anchors.topMargin: index % 2 == 0 ? 0 : parent.height - height } } } } } // == The instructions Area == Rectangle { id: instructionsArea height: instructions.height width: columnLayout.width color: "#55333333" border.color: "black" border.width: 2 radius: 5 anchors.topMargin: 4 anchors.bottomMargin: 4 anchors.leftMargin: 10 anchors.rightMargin: 10 visible: bar.level === 1 property int realHeight: bar.level === 1 ? height + columnLayout.spacing : 0 GCText { id: instructions horizontalAlignment: Text.AlignHCenter width: columnLayout.width height: columnLayout.height / 6 wrapMode: Text.WordWrap fontSizeMode: Text.Fit } } // === The Pocket Area === MoneyArea { id: pocketArea onTransaction: Activity.pay(index) } } Keys.onPressed: { if(event.key === Qt.Key_Tab) { if(items.selectedArea.count !== 0 && items.itemIndex !== -1) items.selectedArea.itemAt(items.itemIndex).selected = false if(items.selectedArea == items.pocket) { items.selectedArea = items.answer } else { items.selectedArea = items.pocket } items.itemIndex = 0 } if(items.selectedArea.count !== 0) { if(items.itemIndex >= 0) items.selectedArea.itemAt(items.itemIndex).selected = false if(event.key === Qt.Key_Right) { if(items.itemIndex != (items.selectedArea.count-1)) items.itemIndex++ else items.itemIndex = 0 } if(event.key === Qt.Key_Left) { if(items.itemIndex > 0) items.itemIndex-- else items.itemIndex = items.selectedArea.count-1 } if([Qt.Key_Space, Qt.Key_Enter, Qt.Key_Return].indexOf(event.key) != -1 && items.itemIndex !== -1 ) { if(items.selectedArea == items.pocket) Activity.pay(items.itemIndex) else Activity.unpay(items.itemIndex) if(items.itemIndex > 0) items.itemIndex-- } } if(items.selectedArea.count !== 0 && items.itemIndex !== -1) items.selectedArea.itemAt(items.itemIndex).selected = true } DialogChooseLevel { id: dialogActivityConfig currentActivity: activity.activityInfo onSaveData: { levelFolder = dialogActivityConfig.chosenLevels currentActivity.currentLevels = dialogActivityConfig.chosenLevels ApplicationSettings.setCurrentLevels(currentActivity.name, dialogActivityConfig.chosenLevels) - background.stop() - background.start() } onClose: { home() } onStartActivity: { + background.stop() background.start() } } DialogHelp { id: dialogHelp onClose: home() } Bar { id: bar content: BarEnumContent { value: help | home | level | activityConfig } onHelpClicked: { displayDialog(dialogHelp) } onActivityConfigClicked: { displayDialog(dialogActivityConfig) } onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onHomeClicked: activity.home() } Bonus { id: bonus Component.onCompleted: win.connect(Activity.nextLevel) } } } diff --git a/src/activities/redraw/Redraw.qml b/src/activities/redraw/Redraw.qml index 5344036ba..e14bbe4f4 100644 --- a/src/activities/redraw/Redraw.qml +++ b/src/activities/redraw/Redraw.qml @@ -1,469 +1,468 @@ /* GCompris - redraw.qml * * Copyright (C) 2014 Bruno Coudoin * * Authors: * Bruno Coudoin (GTK+ version) * Bruno Coudoin (Qt Quick port) * * 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 QtGraphicalEffects 1.0 import "../../core" import "redraw.js" as Activity ActivityBase { id: activity onStart: focus = true onStop: {} property bool symmetry: false pageComponent: Image { id: background anchors.fill: parent source: Activity.url + "background.svg" fillMode: Image.PreserveAspectCrop sourceSize.width: Math.max(parent.width, parent.height) property bool landscape: width >= height signal start signal stop Component.onCompleted: { dialogActivityConfig.initialize() activity.start.connect(start) activity.stop.connect(stop) } // Add here the QML items you need to access in javascript QtObject { id: items property Item main: activity.main property alias background: background property alias bar: bar property alias bonus: bonus property int colorSelector: 0 property alias userModel: userModel property int numberOfColumn property int numberOfColor property int numberOfLine: targetModelData.length / numberOfColumn property alias targetModel: targetModel property var targetModelData property var levels: activity.datasetLoader.data.length !== 0 ? activity.datasetLoader.data : null } onStart: { Activity.start(items) } onStop: { Activity.stop() } Keys.onPressed: { if(event.key >= Qt.Key_0 && event.key < Qt.Key_0 + items.numberOfColor) items.colorSelector = event.key - Qt.Key_0 if(event.key === Qt.Key_Backspace) userModel.clearCurrentItem() } Keys.onEnterPressed: userModel.paintCurrentItem() Keys.onReturnPressed: userModel.paintCurrentItem() Keys.onSpacePressed: userModel.paintCurrentItem() Keys.onDeletePressed: userModel.clearCurrentItem() Keys.onRightPressed: userModel.moveCurrentIndexRight() Keys.onLeftPressed: userModel.moveCurrentIndexLeft() Keys.onDownPressed: userModel.moveCurrentIndexDown() Keys.onUpPressed: userModel.moveCurrentIndexUp() // For creating new content, dump the drawing on the console Keys.onTabPressed: Activity.dump() Row { anchors { top: parent.top right: parent.right left: parent.left bottom: bar.top } anchors.margins: 10 spacing: 20 // The color selector Flickable { id: flickable interactive: true width: 70 * ApplicationInfo.ratio height: background.height boundsBehavior: Flickable.StopAtBounds contentHeight: items.numberOfColor * width bottomMargin: bar.height Column { id: colorSelector Repeater { model: items.numberOfColor Item { width: flickable.width height: width Image { id: img source: Activity.url + Activity.colorShortcut[modelData] + ".svg" sourceSize.width: parent.width z: iAmSelected ? 10 : 1 property bool iAmSelected: modelData == items.colorSelector states: [ State { name: "notclicked" when: !img.iAmSelected && !mouseArea.containsMouse PropertyChanges { target: img scale: 0.8 } }, State { name: "clicked" when: mouseArea.pressed PropertyChanges { target: img scale: 0.7 } }, State { name: "hover" when: mouseArea.containsMouse PropertyChanges { target: img scale: 1.1 } }, State { name: "selected" when: img.iAmSelected PropertyChanges { target: img scale: 1 } } ] SequentialAnimation { id: anim running: img.iAmSelected loops: Animation.Infinite alwaysRunToEnd: true NumberAnimation { target: img property: "rotation" from: 0; to: 10 duration: 200 easing.type: Easing.OutQuad } NumberAnimation { target: img property: "rotation" from: 10; to: -10 duration: 400 easing.type: Easing.InOutQuad } NumberAnimation { target: img property: "rotation" from: -10; to: 0 duration: 200 easing.type: Easing.InQuad } } Behavior on scale { NumberAnimation { duration: 70 } } MouseArea { id: mouseArea anchors.fill: parent hoverEnabled: true onClicked: { activity.audioEffects.play('qrc:/gcompris/src/core/resource/sounds/scroll.wav') items.colorSelector = modelData } } } GCText { id: text1 anchors.fill: parent text: modelData fontSize: regularSize z: modelData == items.colorSelector ? 12 : 2 font.bold: true style: Text.Outline styleColor: "black" color: "white" } DropShadow { anchors.fill: text1 cached: false horizontalOffset: 1 verticalOffset: 1 radius: 8.0 samples: 16 color: "#80000000" source: text1 } } } } } Grid { id: drawAndExampleArea columns: background.landscape ? 2 : 1 width: gridWidth height: parent.height spacing: 10 property int gridWidth: parent.width - colorSelector.width // The drawing area Grid { id: drawingArea width: background.landscape ? parent.gridWidth / 2 - parent.spacing * 2 : parent.gridWidth height: background.landscape ? parent.height : parent.height / 2 columns: items.numberOfColumn Repeater { id: userModel model: items.targetModelData.length property int currentItem: 0 property bool keyNavigation: false function reset() { for(var i=0; i < items.userModel.count; ++i) userModel.itemAt(i).paint(items.colorSelector) currentItem = 0 keyNavigation = false } function clearCurrentItem() { userModel.itemAt(currentItem).paint(0) } function paintCurrentItem() { userModel.itemAt(currentItem).playEffect(items.colorSelector) userModel.itemAt(currentItem).paint(items.colorSelector) } function moveCurrentIndexRight() { keyNavigation = true if(currentItem++ >= items.targetModelData.length - 1) currentItem = 0 } function moveCurrentIndexLeft() { keyNavigation = true if(currentItem-- <= 0) currentItem = items.targetModelData.length - 1 } function moveCurrentIndexUp() { keyNavigation = true currentItem -= items.numberOfColumn if(currentItem < 0) currentItem += items.targetModelData.length } function moveCurrentIndexDown() { keyNavigation = true currentItem += items.numberOfColumn if(currentItem > items.targetModelData.length - 1) currentItem -= items.targetModelData.length } Item { id: userItem width: Math.min(drawingArea.width / items.numberOfColumn, drawingArea.height / items.numberOfLine) height: width property color color: Activity.colors[colorIndex] property int colorIndex function paint(color) { colorIndex = color } function playEffect(color) { if(color === 0) activity.audioEffects.play(Activity.url + 'eraser.wav') else activity.audioEffects.play(Activity.url + 'brush.wav') } Rectangle { id: userRect anchors.fill: parent border.width: userModel.keyNavigation && userModel.currentItem == modelData ? 3 : 1 border.color: 'black' color: parent.color Behavior on color { ColorAnimation { duration: 200 onRunningChanged: { if(!running && Activity.checkModel()) bonus.good("flower") } } } } GCText { id: text2 anchors.fill: parent anchors.margins: 4 text: parent.colorIndex == 0 ? "" : parent.colorIndex fontSize: regularSize font.bold: true style: Text.Outline styleColor: "black" color: "white" } DropShadow { anchors.fill: text2 cached: false horizontalOffset: 1 verticalOffset: 1 radius: 8.0 samples: 16 color: "#80000000" source: text2 } } } } // The painting to reproduce Grid { id: imageArea width: drawingArea.width height: drawingArea.height columns: items.numberOfColumn LayoutMirroring.enabled: activity.symmetry LayoutMirroring.childrenInherit: true Repeater { id: targetModel model: items.targetModelData Item { width: Math.min(imageArea.width / items.numberOfColumn, imageArea.height / items.numberOfLine) height: width property alias color: targetRect.color Rectangle { id: targetRect anchors.fill: parent color: Activity.colors[modelData] border.width: 1 border.color: 'black' } GCText { id: text3 anchors.fill: parent anchors.margins: 4 text: modelData == 0 ? "" : modelData fontSize: regularSize font.bold: true style: Text.Outline styleColor: "black" color: "white" } DropShadow { anchors.fill: text3 cached: false horizontalOffset: 1 verticalOffset: 1 radius: 8.0 samples: 16 color: "#80000000" source: text3 } } } } } } MultiPointTouchArea { x: drawAndExampleArea.x y: drawAndExampleArea.y width: drawAndExampleArea.width height: drawAndExampleArea.height onPressed: checkTouchPoint(touchPoints) onTouchUpdated: checkTouchPoint(touchPoints) function checkTouchPoint(touchPoints) { for(var i in touchPoints) { var touch = touchPoints[i] var block = drawingArea.childAt(touch.x, touch.y) if(block) { block.playEffect(items.colorSelector) block.paint(items.colorSelector) } } } } DialogChooseLevel { id: dialogActivityConfig currentActivity: activity.activityInfo onSaveData: { levelFolder = dialogActivityConfig.chosenLevels currentActivity.currentLevels = dialogActivityConfig.chosenLevels ApplicationSettings.setCurrentLevels(currentActivity.name, dialogActivityConfig.chosenLevels) activity.focus = true - background.stop() - background.start() } onLoadData: { if(activityData) { Activity.initLevel() } } onClose: { home() } onStartActivity: { + background.stop() background.start() } } DialogHelp { id: dialogHelp onClose: home() } Bar { id: bar content: BarEnumContent { value: help | home | level | activityConfig} onHelpClicked: { displayDialog(dialogHelp) } onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onHomeClicked: activity.home() onActivityConfigClicked: { displayDialog(dialogActivityConfig) } } Bonus { id: bonus Component.onCompleted: win.connect(Activity.nextLevel) } } } diff --git a/src/activities/reversecount/Reversecount.qml b/src/activities/reversecount/Reversecount.qml index 73c458e8b..370879d40 100644 --- a/src/activities/reversecount/Reversecount.qml +++ b/src/activities/reversecount/Reversecount.qml @@ -1,252 +1,251 @@ /* GCompris - ReverseCount.qml * * Copyright (C) 2014 Emmanuel Charruau * * Authors: * Bruno Coudoin (GTK+ version) * Emmanuel Charruau (Qt Quick port) * Bruno Coudoin (Major rework) * Timothée Giet (Layout and graphics rework) * * 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" import "reversecount.js" as Activity ActivityBase { id: activity onStart: focus = true onStop: {} pageComponent: Rectangle { id: background anchors.fill: parent color: "#ff1dade4" signal start signal stop Component.onCompleted: { dialogActivityConfig.initialize() activity.start.connect(start) activity.stop.connect(stop) } // Add here the QML items you need to access in javascript QtObject { id: items property Item main: activity.main property GCSfx audioEffects: activity.audioEffects readonly property string resourceUrl: activity.resourceUrl property var levels: activity.datasetLoader.data property alias background: background property alias bar: bar property alias bonus: bonus property alias chooseDiceBar: chooseDiceBar property alias tux: tux property alias fishToReach: fishToReach property int clockPosition: 4 property string mode: "dot" property var heightBase: (background.height - bar.height * 1.5) / 5 property var widthBase: background.width / 5 } onStart: { Activity.start(items) } onStop: { Activity.stop() } Keys.onEnterPressed: Activity.moveTux() Keys.onReturnPressed: Activity.moveTux() onWidthChanged: { sizeChangedTimer.restart() } onHeightChanged: { sizeChangedTimer.restart() } function replaceItems() { if(Activity.fishIndex > 0) { // set x fishToReach.x = Activity.iceBlocksLayout[Activity.fishIndex % Activity.iceBlocksLayout.length][0] * items.widthBase + (items.widthBase - fishToReach.width) / 2 // set y fishToReach.y = Activity.iceBlocksLayout[Activity.fishIndex % Activity.iceBlocksLayout.length][1] * items.heightBase + (items.heightBase - fishToReach.height) / 2 // Move Tux Activity.moveTuxToIceBlock() } } Timer { id: sizeChangedTimer interval: 100 onTriggered: replaceItems() } // === The ice blocks === Repeater { model: Activity.iceBlocksLayout Image { x: modelData[0] * items.widthBase y: modelData[1] * items.heightBase width: items.widthBase height: items.heightBase source: items.resourceUrl + "ice-block.svg" } } Tux { id: tux sourceSize.width: Math.min(items.widthBase, items.heightBase) z: 11 } Image { id: fishToReach source: items.resourceUrl + "fish-blue.svg" sourceSize.width: Math.min(items.widthBase, items.heightBase) z: 10 property int nextX property int nextY function showParticles() { particles.burst(40) } ParticleSystemStarLoader { id: particles clip: false } onOpacityChanged: { if(opacity == 0) { x = nextX y = nextY opacity = 1 } } Behavior on opacity { NumberAnimation { duration: 500 } } } DialogHelp { id: dialogHelp onClose: home() } Bar { id: bar content: BarEnumContent { value: help | home | level | activityConfig } onHelpClicked: { displayDialog(dialogHelp) } onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onHomeClicked: activity.home() onActivityConfigClicked: { displayDialog(dialogActivityConfig) } } Image { id: clock anchors { right: parent.right bottom: parent.bottom margins: 10 } sourceSize.width: 66 * bar.barZoom property int remainingLife: items.clockPosition onRemainingLifeChanged: if(remainingLife >= 0) clockAnim.restart() SequentialAnimation { id: clockAnim alwaysRunToEnd: true ParallelAnimation { NumberAnimation { target: clock; properties: "opacity"; to: 0; duration: 800; easing.type: Easing.OutCubic } NumberAnimation { target: clock; properties: "rotation"; from: 0; to: 180; duration: 800; easing.type: Easing.OutCubic } } PropertyAction { target: clock; property: 'source'; value: items.resourceUrl + "flower" + items.clockPosition + ".svg" } ParallelAnimation { NumberAnimation { target: clock; properties: "opacity"; to: 1; duration: 800; easing.type: Easing.OutCubic } NumberAnimation { target: clock; properties: "rotation"; from: 180; to: 0; duration: 800; easing.type: Easing.OutCubic } } } } DialogChooseLevel { id: dialogActivityConfig currentActivity: activity.activityInfo onClose: { home() } onSaveData: { levelFolder = dialogActivityConfig.chosenLevels currentActivity.currentLevels = dialogActivityConfig.chosenLevels ApplicationSettings.setCurrentLevels(currentActivity.name, dialogActivityConfig.chosenLevels) - // restart activity on saving - background.start() } onLoadData: { if(activityData && activityData["mode"]) { items.mode = activityData["mode"]; } } onStartActivity: { + background.stop() background.start() } } ChooseDiceBar { id: chooseDiceBar mode: items.mode anchors.horizontalCenter: items.background.horizontalCenter y: items.heightBase * 2 audioEffects: activity.audioEffects } Bonus { id: bonus winSound: "qrc:/gcompris/src/activities/ballcatch/resource/tuxok.wav" looseSound: "qrc:/gcompris/src/activities/ballcatch/resource/youcannot.wav" onWin: Activity.nextLevel() onLoose: Activity.initLevel() } } } diff --git a/src/activities/target/Target.qml b/src/activities/target/Target.qml index 65610d57b..898fe6a99 100644 --- a/src/activities/target/Target.qml +++ b/src/activities/target/Target.qml @@ -1,265 +1,265 @@ /* GCompris - target.qml * * Copyright (C) 2014 Bruno coudoin * * Authors: * Bruno Coudoin (GTK+ version) * Bruno Coudoin (Qt Quick port) * * 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" import "target.js" as Activity ActivityBase { id: activity onStart: focus = true onStop: {} pageComponent: Item { id: background anchors.fill: parent signal start signal stop signal targetReached Keys.onPressed: { if(items.currentArrow != items.nbArrow) return if(event.key === Qt.Key_Backspace) { backspace() } appendText(event.text) } Component.onCompleted: { dialogActivityConfig.initialize() activity.start.connect(start) activity.stop.connect(stop) } // Add here the QML items you need to access in javascript QtObject { id: items property Item main: activity.main property alias background: background property alias bar: bar property alias bonus: bonus property alias targetModel: targetItem.model property var levels: activity.datasetLoader.data property alias targetItem: targetItem property alias userEntry: userEntry property int currentArrow property int nbArrow property int currentSubLevel property int numberOfSubLevel property bool arrowFlying onNbArrowChanged: { arrowRepeater.init(nbArrow) } } onStart: { keyboard.populate(); Activity.start(items) } onStop: { Activity.stop() } TargetItem { id: targetItem } onTargetReached: { items.arrowFlying = false if(items.currentArrow == items.nbArrow) { targetItem.stop() targetItem.scoreText += " = " userEntry.text = "?" } } Arrow { id: arrowRepeater } Image { id: cross anchors.centerIn: parent source: Activity.url + "cross.svg" opacity: items.currentArrow != items.nbArrow ? 1 : 0 sourceSize.width: 50 * ApplicationInfo.ratio } MouseArea { id: mouseArea anchors.fill: parent enabled: items.currentArrow != items.nbArrow && !items.arrowFlying onClicked: { activity.audioEffects.play(Activity.url + 'arrow.wav') items.arrowFlying = true if(items.currentArrow != items.nbArrow) { arrowRepeater.itemAt(items.currentArrow).opacity = 1 arrowRepeater.itemAt(items.currentArrow++).scale = 1 } } } GCText { id: scoreItem anchors.horizontalCenter: parent.horizontalCenter width: parent.width text: targetItem.scoreText fontSize: 22 font.bold: true style: Text.Outline styleColor: "black" color: "white" wrapMode: Text.WordWrap horizontalAlignment: Text.AlignHCenter } function backspace() { userEntry.text = userEntry.text.slice(0, -1) if(userEntry.text.length === 0) { userEntry.text = "?" } else { if(targetItem.scoreTotal == userEntry.text) bonus.good("flower") } } function appendText(text) { if(text === keyboard.backspace) { backspace() return } var number = parseInt(text) if(isNaN(number)) return if(userEntry.text === "?") { userEntry.text = "" } if(userEntry.text.length > ('' + targetItem.scoreTotal).length) { return } userEntry.text += text if(targetItem.scoreTotal.toString() === userEntry.text) bonus.good("flower") } GCText { id: userEntry anchors.top: scoreItem.bottom width: parent.width fontSize: 22 font.bold: true style: Text.Outline styleColor: "black" color: "white" wrapMode: Text.WordWrap horizontalAlignment: Text.AlignHCenter } VirtualKeyboard { id: keyboard anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter hide: items.currentArrow == items.nbArrow ? false : true function populate() { layout = [ [ { label: "0" }, { label: "1" }, { label: "2" }, { label: "3" }, { label: "4" }, { label: "5" }, { label: "6" }, { label: "7" }, { label: "8" }, { label: "9" }, { label: keyboard.backspace } ] ] } onKeypress: background.appendText(text) onError: console.log("VirtualKeyboard error: " + msg); } DialogChooseLevel { id: dialogActivityConfig currentActivity: activity.activityInfo onSaveData: { levelFolder = dialogActivityConfig.chosenLevels currentActivity.currentLevels = dialogActivityConfig.chosenLevels ApplicationSettings.setCurrentLevels(currentActivity.name, dialogActivityConfig.chosenLevels) - background.start() } onClose: { home() } onStartActivity: { + background.stop() background.start() } } DialogHelp { id: dialogHelp onClose: home() } Bar { id: bar anchors.bottom: keyboard.top content: BarEnumContent { value: help | home | level | activityConfig } onHelpClicked: { displayDialog(dialogHelp) } onActivityConfigClicked: { displayDialog(dialogActivityConfig) } onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onHomeClicked: activity.home() } Score { id: score anchors.right: parent.right anchors.top: parent.top anchors.bottom: undefined currentSubLevel: items.currentSubLevel + 1 numberOfSubLevels: items.numberOfSubLevel } Bonus { id: bonus Component.onCompleted: win.connect(Activity.nextSubLevel) } } } diff --git a/src/core/DialogChooseLevel.qml b/src/core/DialogChooseLevel.qml index 515563447..e89e155b9 100644 --- a/src/core/DialogChooseLevel.qml +++ b/src/core/DialogChooseLevel.qml @@ -1,404 +1,407 @@ /* GCompris - DialogChooseLevel.qml * * Copyright (C) 2018 Johnny Jazeix * * Authors: * Johnny Jazeix * * 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 /** * todo * @ingroup components * * todo * * @sa ApplicationSettings * @inherit QtQuick.Item */ Rectangle { id: dialogChooseLevel visible: false /* Public interface: */ /** * type:string * The name of the activity in case of per-activity config. * * Will be autogenerated unless set by the caller. */ property string activityName: currentActivity.name.split('/')[0] /// @cond INTERNAL_DOCS property bool isDialog: true /** * type:string * Title of the configuration dialog. */ readonly property string title: currentActivity ? qsTr("%1 settings").arg(currentActivity.title) : "" property var difficultiesModel: [] property QtObject currentActivity property var chosenLevels: [] property var activityData onActivityDataChanged: loadData() /// @endcond /** * By default, we display configuration (this avoids to add code in each * activity to set it by default). */ property bool displayDatasetAtStart: !hasConfig /** * Emitted when the config dialog has been closed. */ signal close /** * Emitted when the config dialog has been started. */ signal start onStart: initialize() signal stop /** * Emitted when the settings are to be saved. * * The actual persisting of the settings in the settings file is done by * DialogActivityConfig. The activity has to take care to update its * internal state. */ signal saveData signal startActivity /** * Emitted when the config settings have been loaded. */ signal loadData property bool hasConfigOrDataset: hasConfig || hasDataset property bool hasConfig: activityConfigFile.exists("qrc:/gcompris/src/activities/"+activityName+"/ActivityConfig.qml") property bool hasDataset: currentActivity && currentActivity.levels.length !== 0 color: "#696da3" border.color: "black" border.width: 1 property bool inMenu: false onVisibleChanged: { if(visible) { configLoader.initializePanel() } } function initialize() { // dataset information chosenLevels = currentActivity.currentLevels difficultiesModel = [] if(currentActivity.levels.length == 0) { print("no levels to load for", activityName) } else { for(var level in currentActivity.levels) { objectiveLoader.dataFiles.push({"level": currentActivity.levels[level], "file": "qrc:/gcompris/src/activities/"+activityName+"/resource/"+currentActivity.levels[level]+"/Data.qml"}) } objectiveLoader.start() } // Defaults to config if in an activity else to dataset if in menu if(displayDatasetAtStart) { datasetVisibleButton.clicked() } else { optionsVisibleButton.clicked() } } Loader { id: objectiveLoader property var dataFiles: [] property var currentFile signal start signal stop onStart: { var file = dataFiles.shift() currentFile = file source = file.file.toString() } onLoaded: { difficultiesModel.push({"level": currentFile.level, "objective": item.objective, "difficulty": item.difficulty, "selectedInConfig": (chosenLevels.indexOf(currentFile.level) != -1)}) if(dataFiles.length != 0) { start() } else { stop() } } onStop: { difficultiesRepeater.model = difficultiesModel } } Column { id: titleColumn spacing: 10 anchors.top: parent.top anchors.topMargin: 10 anchors.horizontalCenter: parent.horizontalCenter width: dialogChooseLevel.width - 30 Rectangle { id: titleRectangle color: "#e6e6e6" radius: 10 * ApplicationInfo.ratio width: parent.width height: title.height * 1.2 border.color: "black" border.width: 0 Row { spacing: 2 padding: 8 Image { id: titleIcon anchors { left: parent.left top: parent.top margins: 4 * ApplicationInfo.ratio } } GCText { id: title text: dialogChooseLevel.title width: titleColumn.width - 10 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter color: "black" fontSize: 20 font.weight: Font.DemiBold wrapMode: Text.WordWrap } } } // Header buttons Row { id: datasetOptionsRow height: dialogChooseLevel.height / 12 width: titleRectangle.width spacing: titleRectangle.width * 0.1 Button { id: datasetVisibleButton text: qsTr("Dataset") enabled: hasDataset height: parent.height width: titleRectangle.width * 0.45 opacity: enabled ? 1 : 0 property bool selected: true style: GCButtonStyle { theme: "settingsButton" selected: datasetVisibleButton.selected } onClicked: { selected = true; } } Button { id: optionsVisibleButton text: qsTr("Options") enabled: hasConfig height: parent.height width: titleRectangle.width * 0.45 opacity: enabled ? 1 : 0 style: GCButtonStyle { theme: "settingsButton" selected: !datasetVisibleButton.selected } onClicked: { datasetVisibleButton.selected = false; } //showOptions() } } // "Dataset"/"Options" content Rectangle { color: "#bdbed0" radius: 10 * ApplicationInfo.ratio width: dialogChooseLevel.width - 30 height: dialogChooseLevel.height - (30 + title.height * 1.2) - saveAndPlayRow.height - datasetOptionsRow.height - 3 * parent.spacing border.color: "white" border.width: 3 * ApplicationInfo.ratio anchors.margins: 100 Flickable { id: flick anchors.margins: 10 * ApplicationInfo.ratio anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom flickableDirection: Flickable.VerticalFlick clip: true contentHeight: contentItem.childrenRect.height + 40 * ApplicationInfo.ratio Loader { id: configLoader visible: !datasetVisibleButton.selected active: optionsVisibleButton.enabled source: active ? "qrc:/gcompris/src/activities/"+activityName+"/ActivityConfig.qml" : "" // Load configuration at start of activity // in the menu, it's done when the visibility property // of the dialog changes onItemChanged: if(!inMenu) { initializePanel(); } function initializePanel() { if(item) { // only connect once the signal to save data if(item.background !== dialogChooseLevel) { item.background = dialogChooseLevel dialogChooseLevel.saveData.connect(save) } getInitialConfiguration() } } function getInitialConfiguration() { activityData = Qt.binding(function() { return item.dataToSave }) if(item) { item.dataToSave = ApplicationSettings.loadActivityConfiguration(activityName) item.setDefaultValues() } } function save() { item.saveValues() ApplicationSettings.saveActivityConfiguration(activityName, item.dataToSave) } } Column { visible: datasetVisibleButton.selected spacing: 10 Repeater { id: difficultiesRepeater delegate: Row { height: objective.height Image { id: difficultyIcon source: "qrc:/gcompris/src/core/resource/difficulty" + modelData.difficulty + ".svg"; sourceSize.height: objective.indicatorImageHeight sourceSize.width: height anchors.verticalCenter: objective.verticalCenter } GCDialogCheckBox { id: objective width: dialogChooseLevel.width - 30 - difficultyIcon.width - 2 * flick.anchors.margins text: modelData.objective // to be fixed by all last used levels checked: modelData.selectedInConfig onClicked: { if(checked) { chosenLevels.push(modelData.level) } else if(chosenLevels.length > 1) { chosenLevels.splice(chosenLevels.indexOf(modelData.level), 1) } else { // At least one must be selected checked = true; } } } } } } } // The scroll buttons GCButtonScroll { anchors.right: parent.right anchors.rightMargin: 5 * ApplicationInfo.ratio anchors.bottom: flick.bottom anchors.bottomMargin: 5 * ApplicationInfo.ratio onUp: flick.flick(0, 1400) onDown: flick.flick(0, -1400) upVisible: flick.visibleArea.yPosition <= 0 ? false : true downVisible: flick.visibleArea.yPosition + flick.visibleArea.heightRatio >= 1 ? false : true } } // Footer buttons Row { id: saveAndPlayRow height: dialogChooseLevel.height / 12 width: titleRectangle.width spacing: titleRectangle.width * 0.05 Button { id: cancelButton text: qsTr("Cancel") height: parent.height width: titleRectangle.width * 0.25 property bool selected: true style: GCButtonStyle { theme: "settingsButton" } onClicked: close(); } Button { id: saveButton text: qsTr("Save") height: parent.height width: titleRectangle.width * 0.25 property bool selected: true style: GCButtonStyle { theme: "settingsButton" } onClicked: { saveData(); + if (inMenu === false) { + startActivity(); + } close(); } } Button { id: saveAndStartButton text: qsTr("Save and start") height: parent.height width: titleRectangle.width * 0.4 visible: inMenu === true style: GCButtonStyle { theme: "settingsButton" } onClicked: { saveData(); startActivity(); } } } } File { id: activityConfigFile } }