diff --git a/src/activities/algebra_by/Algebra.qml b/src/activities/algebra_by/Algebra.qml index 996df118b..16d27d6d5 100644 --- a/src/activities/algebra_by/Algebra.qml +++ b/src/activities/algebra_by/Algebra.qml @@ -1,175 +1,175 @@ /* GCompris - Algebra.qml * * Copyright (C) 2014 Aruna Sankaranarayanan * * 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 "algebra.js" as Activity ActivityBase { id: activity property alias operand: operand onStart: { focus = true; } pageComponent: Image { id: background source: "qrc:/gcompris/src/activities/algebra_by/resource/background.svg" fillMode: Image.PreserveAspectCrop sourceSize.width: Math.max(parent.width, parent.height) signal start signal stop Component.onCompleted: { activity.start.connect(start) activity.stop.connect(stop) } Item { id: coreItems property alias background: background property alias bar: bar property alias bonus: bonus property alias score: score property alias balloon: balloon property alias timer:timer - property GCAudio audioEffects: activity.audioEffects + property GCSfx audioEffects: activity.audioEffects } onStart: Activity.start(coreItems, otherItems, operand) onStop: Activity.stop() DialogHelp { id: dialogHelpLeftRight onClose: home() } Timer { id: timer interval: 1000 onTriggered: Activity.run() } Bar { id: bar content: BarEnumContent { value: help | home | level } onHelpClicked: { displayDialog(dialogHelpLeftRight) } onPreviousLevelClicked: { Activity.previousLevel() } onNextLevelClicked: { Activity.nextLevel() } onHomeClicked: home() } Balloon { id: balloon onTimeout: bonus.bad("smiley") } Bonus { id: bonus Component.onCompleted: { loose.connect(Activity.run) win.connect(Activity.nextLevel) } } Score { id: score x: parent.width * 0.25 y: parent.height * 0.65 anchors.right: undefined anchors.bottom: undefined currentSubLevel: 0 numberOfSubLevels: 10 } } Item { id: otherItems property alias iAmReady: iAmReady property alias firstOp: firstOp property alias secondOp: secondOp property alias numpad: numpad property int result } NumPad { id: numpad onAnswerChanged: Activity.questionsLeft() maxDigit: ('' + otherItems.result).length + 1 } ReadyButton { id: iAmReady onClicked: Activity.run() } Flow { id:textFlow x: 200 * ApplicationInfo.ratio y: 80 width: parent.width / 2 height: 100 anchors.margins: 4 spacing: 10 AlgebraText { id: firstOp visible: !iAmReady.visible } AlgebraText { id: operand visible: firstOp.visible } AlgebraText { id: secondOp visible: !iAmReady.visible } AlgebraText { id: equals visible: firstOp.visible text: "=" } AlgebraText { id: result visible: !iAmReady.visible text: numpad.answer } } Keys.onPressed: { numpad.updateAnswer(event.key, true); } Keys.onReleased: { numpad.updateAnswer(event.key, false); } } diff --git a/src/activities/algorithm/Algorithm.qml b/src/activities/algorithm/Algorithm.qml index d06ef8f15..8ccf77b0a 100644 --- a/src/activities/algorithm/Algorithm.qml +++ b/src/activities/algorithm/Algorithm.qml @@ -1,247 +1,247 @@ /* GCompris - algorithm.qml * * Copyright (C) 2014 Bharath M S * * Authors: * Christof Petig and Ingo Konrad (GTK+ version) * Bharath M S (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 "algorithm.js" as Activity ActivityBase { id: activity onStart: focus = true onStop: {} pageComponent: Image { id: background anchors.fill: parent source: Activity.url + "desert_scene.svg" sourceSize.width: parent.width signal start signal stop Component.onCompleted: { 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 questionTray: questionTray property alias answerTray: answerTray property alias choiceTray: choiceTray property alias question: question property alias answer: answer property alias choice: choice - property GCAudio audioEffects: activity.audioEffects + property GCSfx audioEffects: activity.audioEffects property alias background: background property alias bar: bar property alias bonus: bonus property int nbSubLevel: 3 property int currentSubLevel: 0 } onStart: { Activity.start(items) } onStop: { Activity.stop() } Column { id: column spacing: 10 x: parent.width * 0.1 y: parent.height * 0.1 width: parent.width * 0.8 property int itemWidth: width / Activity.images.length - 10 property int itemHeight: itemWidth Rectangle { id: questionTray height: column.itemHeight + 10 width: parent.width + 10 color: "#55333333" border.color: "black" border.width: 0 radius: 5 Row { anchors.topMargin: 4 anchors.bottomMargin: 4 anchors.leftMargin: 10 anchors.rightMargin: 10 anchors.fill: parent spacing: 10 Repeater { id: question Image { source: Activity.url + modelData + '.svg' sourceSize.height: questionTray.height width: column.itemWidth height: column.itemHeight } } } } Rectangle { id: answerTray height: column.itemHeight + 10 width: parent.width + 10 color: "#55333333" border.color: "black" border.width: 0 radius: 5 Row { anchors.topMargin: 4 anchors.bottomMargin: 4 anchors.leftMargin: 10 anchors.rightMargin: 10 anchors.fill: parent spacing: 10 Repeater { id: answer Image { source: "qrc:/gcompris/src/activities/algorithm/resource/" + modelData + '.svg' sourceSize.height: answerTray.height width: column.itemWidth height: column.itemHeight } } } } // A spacer Item { height: column.itemHeight / 2 width: parent.width } Rectangle { id: choiceTray height: column.itemHeight + 10 width: parent.width + 10 color: "#55333333" border.color: "black" border.width: 0 radius: 5 Row { anchors.topMargin: 4 anchors.bottomMargin: 4 anchors.leftMargin: 10 anchors.rightMargin: 10 anchors.fill: parent spacing: 10 Repeater { id: choice model: Activity.images Image { id: img source: Activity.url + modelData + '.svg' sourceSize.height: parent.height width: column.itemWidth height: column.itemHeight state: "notclicked" signal clicked MouseArea { id: mouseArea hoverEnabled: true anchors.fill: parent onClicked: { if(Activity.clickHandler(modelData)) { particle.burst(20) } } } states: [ State { name: "notclicked" PropertyChanges { target: img scale: 1.0 } }, State { name: "clicked" when: mouseArea.pressed PropertyChanges { target: img scale: 0.9 } }, State { name: "hover" when: mouseArea.containsMouse PropertyChanges { target: img scale: 1.1 } } ] Behavior on scale { NumberAnimation { duration: 70 } } ParticleSystemStarLoader { id: particle clip: false } } } } } } 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.nextSubLevel) } Score { anchors { bottom: parent.bottom bottomMargin: 10 * ApplicationInfo.ratio right: parent.right rightMargin: 10 * ApplicationInfo.ratio top: undefined left: undefined } numberOfSubLevels: items.nbSubLevel currentSubLevel: items.currentSubLevel + 1 } } } diff --git a/src/activities/balancebox/Balancebox.qml b/src/activities/balancebox/Balancebox.qml index 4ced57a5e..a9d32bc0f 100644 --- a/src/activities/balancebox/Balancebox.qml +++ b/src/activities/balancebox/Balancebox.qml @@ -1,572 +1,572 @@ /* GCompris - balancebox.qml * * Copyright (C) 2014-2016 Holger Kaelberer * * Authors: * Holger Kaelberer * * 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.Window 2.1 import QtSensors 5.0 import QtGraphicalEffects 1.0 import GCompris 1.0 import Box2D 2.0 import QtQuick.Controls 1.5 import "../../core" import "editor/" import "balancebox.js" as Activity import "qrc:/gcompris/src/core/core.js" as Core ActivityBase { id: activity property string mode: "play" // "play" or "test" property string levelSet: "builtin" // "builtin" or "user" property var testLevel property bool inForeground: false // to avoid unneeded reconfigurations property bool alwaysStart: true // enforce start signal for editor-to-testing- and returning from config-transition property bool needRestart: true onWidthChanged:if (inForeground && pageView.currentItem === activity) Activity.reconfigureScene(); onHeightChanged: if (inForeground && pageView.currentItem === activity) Activity.reconfigureScene(); onStart: { inForeground = true; focus = true; } onStop: inForeground = false; Keys.onPressed: Activity.processKeyPress(event.key) Keys.onReleased: Activity.processKeyRelease(event.key) pageComponent: Image { id: background source: Activity.baseUrl + "/maze_bg.svg" sourceSize.width: parent.width anchors.fill: parent signal start signal stop function startEditor() { editorLoader.active = true; if (activity.mode == "test") displayDialogs([dialogActivityConfig, editorLoader.item]); else displayDialog(editorLoader.item); } function handleBackEvent() { if (activity.mode == "test") { startEditor(); return true; } else return false; } Keys.onEscapePressed: event.accepted = handleBackEvent(); Keys.onReleased: { if (event.key === Qt.Key_Back) event.accepted = handleBackEvent(); } Component.onCompleted: { dialogActivityConfig.getInitialConfiguration() activity.start.connect(start) activity.stop.connect(stop) items.dpi = Math.round(Screen.pixelDensity*25.4); } onStart: if (activity.needRestart) { Activity.start(items); activity.needRestart = false; } else Activity.initLevel(); onStop: { Activity.stop(); activity.needRestart = true; } QtObject { id: items property string mode: activity.mode property string levelSet: activity.levelSet property var testLevel: activity.testLevel property Item main: activity.main property alias background: background property alias bar: bar property alias bonus: bonus property alias tilt: tilt property alias timer: timer property alias ball: ball property int ballSize: cellSize - 2*wallSize property alias mapWrapper: mapWrapper property int cellSize: mapWrapper.length / Math.min(mapWrapper.rows, mapWrapper.columns) property int wallSize: cellSize / 5 property var world: physicsWorld property alias keyboardTimer: keyboardTimer property var ballType: Fixture.Category1 property var wallType: Fixture.Category2 property var holeType: Fixture.Category3 property var goalType: Fixture.Category4 property var buttonType: Fixture.Category5 property alias parser: parser property double dpi - property GCAudio audioEffects: activity.audioEffects + property GCSfx audioEffects: activity.audioEffects property Loading loading: activity.loading } Loader { id: editorLoader active: false sourceComponent: BalanceboxEditor { id: editor visible: true testBox: activity onClose: activity.home() } } JsonParser { id: parser onError: console.error("Balancebox: Error parsing JSON: " + msg); } Rectangle { id: mapWrapper property double margin: 20 property int columns: 0 property int rows: 0 property double length: Math.min(background.height - 2*mapWrapper.margin, background.width - 2*mapWrapper.margin); color: "#E3DEDB" width: length height: length anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter transform: [ Rotation { origin.x: mapWrapper.width / 2 origin.y: mapWrapper.height / 2 axis { x: 1; y: 0; z: 0 } angle: ApplicationInfo.isMobile ? 0 : -items.tilt.xRotation }, Rotation { origin.x: mapWrapper.width / 2 origin.y: mapWrapper.height / 2 axis { x: 0; y: 1; z: 0 } angle: ApplicationInfo.isMobile ? 0 : items.tilt.yRotation } ] // right: Wall { id: rightWall width: items.wallSize height: parent.height + items.wallSize anchors.left: mapWrapper.right anchors.leftMargin: - items.wallSize/2 anchors.top: parent.top anchors.topMargin: -items.wallSize/2 shadow: false shadowHorizontalOffset: Math.min(items.tilt.yRotation, items.wallSize) shadowVerticalOffset: Math.min(items.tilt.xRotation, items.wallSize) } // bottom: Wall { id: bottomWall width: parent.width + items.wallSize height: items.wallSize anchors.left: mapWrapper.left anchors.leftMargin: - items.wallSize/2 anchors.top: parent.bottom anchors.topMargin: -items.wallSize/2 shadow: false shadowHorizontalOffset: Math.min(items.tilt.yRotation, items.wallSize) shadowVerticalOffset: Math.min(items.tilt.xRotation, items.wallSize) } // top: Wall { id: topWall width: parent.width + items.wallSize height: items.wallSize anchors.left: mapWrapper.left anchors.leftMargin: - items.wallSize/2 anchors.top: parent.top anchors.topMargin: -items.wallSize/2 shadow: false shadowHorizontalOffset: Math.min(items.tilt.yRotation, items.wallSize) shadowVerticalOffset: Math.min(items.tilt.xRotation, items.wallSize) } // left: Wall { id: leftWall width: items.wallSize height: parent.height + items.wallSize anchors.left: mapWrapper.left anchors.leftMargin: - items.wallSize/2 anchors.top: parent.top anchors.topMargin: -items.wallSize/2 shadow: false shadowHorizontalOffset: Math.min(items.tilt.yRotation, items.wallSize) shadowVerticalOffset: Math.min(items.tilt.xRotation, items.wallSize) } BalanceItem { id: ball world: physicsWorld imageSource: Activity.baseUrl + "/ball.svg" visible: false scale: 1.0 width: items.ballSize height: items.ballSize z: 3 // above other BalanceItems categories: items.ballType collidesWith: items.wallType | items.holeType | items.goalType | items.buttonType density: 1 friction: Activity.friction linearDamping: Activity.friction restitution: Activity.restitution bodyType: Body.Dynamic shadow: true shadowHorizontalOffset: (items.tilt.yRotation > 0) ? Math.min(items.tilt.yRotation, items.wallSize) : Math.max(items.tilt.yRotation, -items.wallSize) shadowVerticalOffset: (items.tilt.xRotation > 0) ? Math.min(items.tilt.xRotation, items.wallSize) : Math.max(items.tilt.xRotation, -items.wallSize) Behavior on scale { NumberAnimation { id: fallAnimation duration: 1000 } } onBeginContact: { if (other.categories !== items.wallType) Activity.addBallContact(other); else { // sound-effect on each contact with a wall might be too annoying: //items.audioEffects.stop(); //items.audioEffects.play("qrc:/gcompris/src/core/resource/sounds/brick.wav"); } } onEndContact: { if (other.categories !== items.wallType) Activity.removeBallContact(other); } } World { id: physicsWorld gravity: Qt.point(0, 0) // we calculate acceleration ourselves pixelsPerMeter: Activity.box2dPpm // default: 32 timeStep: Activity.step/1000 // default: 1/60 } DebugDraw { id: debugDraw world: physicsWorld visible: Activity.debugDraw z: 100 } } Timer { id: timer interval: Activity.step; running: false; repeat: true onTriggered: Activity.moveBall() } Item { id: tilt property double xRotation: 0 property double yRotation: 0 property bool swapAxes: false property bool invertX: false property bool invertY: false onXRotationChanged: { if (xRotation > 90) xRotation = 90; else if (xRotation < -90) xRotation = -90; } onYRotationChanged: { if (yRotation > 90) yRotation = 90; else if (yRotation < -90) yRotation = -90; } TiltSensor { id: tiltSensor active: ApplicationInfo.isMobile ? true : false onReadingChanged: { if (!tilt.swapAxes) { tilt.xRotation = tilt.invertX ? -reading.xRotation : reading.xRotation; tilt.yRotation = tilt.invertY ? -reading.yRotation : reading.yRotation; } else { tilt.xRotation = tilt.invertX ? -reading.yRotation : reading.yRotation; tilt.yRotation = tilt.invertY ? -reading.xRotation : reading.xRotation; } tiltText.text = "X/Y Rotation: " + tiltSensor.reading.xRotation + "/" + tiltSensor.reading.yRotation } } } Item { id: textWrapper anchors.left: parent.left anchors.top: parent.top width: parent.width height: parent.height / 3 visible: Activity.debugDraw Text { id: tiltText anchors.left: parent.left anchors.top: parent.top text: "X/Y Rotation: " + tilt.xRotation + "/" + tilt.yRotation font.pointSize: 12 } Text { id: posText anchors.left: parent.left anchors.top: tiltText.bottom text: "X/Y = " + ball.x + "/" + ball.y font.pointSize: 12 } } MultiPointTouchArea { anchors.fill: parent touchPoints: [ TouchPoint { id: point1 } ] property real startX property real startY property int offset: 30 function reset() { startX = point1.x startY = point1.y } onPressed: { reset() } onUpdated: { var moveX = point1.x - startX var moveY = point1.y - startY // Find the direction with the most move if(Math.abs(moveX) * ApplicationInfo.ratio > offset && Math.abs(moveX) > Math.abs(moveY)) { if(moveX > offset * ApplicationInfo.ratio) { Activity.processKeyPress(Qt.Key_Right) reset() } else if(moveX < -offset * ApplicationInfo.ratio) { Activity.processKeyPress(Qt.Key_Left) reset() } } else if(Math.abs(moveY) * ApplicationInfo.ratio > offset && Math.abs(moveX) < Math.abs(moveY)) { if(moveY > offset * ApplicationInfo.ratio) { Activity.processKeyPress(Qt.Key_Down) reset() } else if(moveY < -offset * ApplicationInfo.ratio) { Activity.processKeyPress(Qt.Key_Up) reset() } } } onReleased: { Activity.keyboardIsTilting = false } } DialogHelp { id: dialogHelp onClose: home() } Bar { id: bar content: BarEnumContent { value: activity.mode == "play" ? (help | home | level | config ) : ( help | home ) } onHelpClicked: { // stop everything or the ball keeps moving while we're away: items.timer.stop(); displayDialog(dialogHelp); } onPreviousLevelClicked: if (!Activity.finishRunning) Activity.previousLevel() onNextLevelClicked: if (!Activity.finishRunning) Activity.nextLevel() onHomeClicked: { if (activity.mode == "test") background.startEditor(); else activity.home() } onConfigClicked: { items.timer.stop(); dialogActivityConfig.active = true // Set default values dialogActivityConfig.setDefaultValues(); displayDialog(dialogActivityConfig) } } Bonus { id: bonus looseSound: "qrc:/gcompris/src/core/resource/sounds/crash.wav" Component.onCompleted: { win.connect(Activity.nextLevel); loose.connect(Activity.initLevel); } } Timer { id: keyboardTimer interval: Activity.keyboardTimeStep; running: false repeat: false onTriggered: Activity.keyboardHandler() } DialogActivityConfig { id: dialogActivityConfig currentActivity: activity content: Component { Item { property alias levelsBox: levelsBox property var availableLevels: [ { "text": qsTr("Built-in"), "value": "builtin" }, { "text": qsTr("User"), "value": "user" }, ] Flow { id: flow spacing: 5 width: dialogActivityConfig.width GCComboBox { id: levelsBox model: availableLevels background: dialogActivityConfig label: qsTr("Select your level set") } Button { id: editorButton style: GCButtonStyle {} height: levelsBox.height text: qsTr("Start Editor") visible: levelsBox.currentIndex == 1 onClicked: background.startEditor() } } } } onClose: home(); onLoadData: { if(dataToSave && dataToSave["levels"]) { activity.levelSet = dataToSave["levels"]; } } onSaveData: { var newLevels = dialogActivityConfig.configItem .availableLevels[dialogActivityConfig.configItem.levelsBox.currentIndex].value; if (newLevels !== activity.levelSet) { activity.levelSet = newLevels; dataToSave = {"levels": activity.levelSet}; activity.needRestart = true; } } dataValidationFunc: function() { var newLevels = dialogActivityConfig.configItem .availableLevels[dialogActivityConfig.configItem.levelsBox.currentIndex].value; if (newLevels === "user" && !parser.jsonFile.exists(Activity.userFile)) { Core.showMessageDialog(dialogActivityConfig, qsTr("You selected the user-defined level set, but you have not yet defined any user levels!
" + "Either create your user levels by starting the level editor or choose the 'built-in' level set."), qsTr("Ok"), null, "", null, null); return false; } return true; } function setDefaultValues() { for(var i = 0 ; i < dialogActivityConfig.configItem.availableLevels.length; i ++) { if(dialogActivityConfig.configItem.availableLevels[i].value === activity.levelSet) { dialogActivityConfig.configItem.levelsBox.currentIndex = i; break; } } } } } } diff --git a/src/activities/checkers/Checkers.qml b/src/activities/checkers/Checkers.qml index 39ade3f0d..08433aac3 100644 --- a/src/activities/checkers/Checkers.qml +++ b/src/activities/checkers/Checkers.qml @@ -1,467 +1,466 @@ /* GCompris - checkers.qml * * Copyright (C) 2017 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 QtQuick.Controls.Styles 1.4 import GCompris 1.0 import "../../core" import "." import "checkers.js" as Activity ActivityBase { id: activity property bool acceptClick: true property bool twoPlayers: false // difficultyByLevel means that at level 1 computer is bad better at last level property bool difficultyByLevel: true onStart: focus = true onStop: {} pageComponent: Image { id: background anchors.fill: parent source: Activity.url + 'background-wood.svg' signal start signal stop Component.onCompleted: { 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 GCAudio audioEffects: activity.audioEffects + property GCSfx audioEffects: activity.audioEffects property alias background: background property alias bar: bar property alias bonus: bonus property int barHeightAddon: ApplicationSettings.isBarHidden ? 1 : 3 property bool isPortrait: (background.height > background.width) property int cellSize: items.isPortrait ? Math.min(background.width / (items.numberOfCases + 2), (background.height - controls.height) / (items.numberOfCases + barHeightAddon)) : Math.min(background.width / (items.numberOfCases + 2), background.height / (items.numberOfCases + barHeightAddon)) property var fen: activity.fen property bool twoPlayer: activity.twoPlayers property bool difficultyByLevel: activity.difficultyByLevel property var positions property var pieces: pieces property var squares: squares property var history property var redo_stack property alias redoTimer: redoTimer property int from property bool blackTurn property bool gameOver property var movesToDo: [] property string message property alias trigComputerMove: trigComputerMove property int numberOfCases: 10 Behavior on cellSize { PropertyAnimation { easing.type: Easing.InOutQuad; duration: 1000 } } } onStart: { Activity.start(items) } onStop: { Activity.stop() } Grid { anchors { top: parent.top topMargin: items.isPortrait ? 0 : items.cellSize / 2 leftMargin: 10 * ApplicationInfo.ratio rightMargin: 10 * ApplicationInfo.ratio } columns: (items.isPortrait==true) ? 1 : 3 rows: (items.isPortrait==true) ? 2 : 1 width: (items.isPortrait==true) ? undefined : background.width anchors.horizontalCenter: parent.horizontalCenter spacing: 10 horizontalItemAlignment: Grid.AlignHCenter verticalItemAlignment: Grid.AlignVCenter Column { id: controls anchors { leftMargin: 10 rightMargin: 10 } width: items.isPortrait ? parent.width : Math.max(undo.width * 1.2, Math.min( (background.width * 0.9 - undo.width - chessboard.width), (background.width - chessboard.width) / 2)) GCText { color: "white" anchors.horizontalCenter: parent.horizontalCenter width: parent.width fontSize: smallSize text: items.message horizontalAlignment: Text.AlignHCenter wrapMode: TextEdit.WordWrap } Grid { spacing: 10 columns: items.isPortrait ? 3 : 1 anchors.horizontalCenter: parent.horizontalCenter horizontalItemAlignment: Grid.AlignHCenter verticalItemAlignment: Grid.AlignVCenter Button { id: undo height: 30 * ApplicationInfo.ratio text: qsTr("Undo"); style: GCButtonStyle { theme: "light" } onClicked: Activity.undo() enabled: (items.history && items.history.length > 0) ? true : false opacity: enabled ? 1 : 0 Behavior on opacity { PropertyAnimation { easing.type: Easing.InQuad duration: 200 } } } Button { id: redo height: 30 * ApplicationInfo.ratio text: qsTr("Redo"); style: GCButtonStyle { theme: "light" } onClicked: { Activity.redo() } enabled: items.redo_stack.length > 0 && acceptClick ? 1 : 0 opacity: enabled Behavior on opacity { PropertyAnimation { easing.type: Easing.InQuad duration: 200 } } } Button { height: 30 * ApplicationInfo.ratio text: qsTr("Swap"); style: GCButtonStyle { theme: "light" } enabled: items.twoPlayer opacity: enabled onClicked: chessboard.swap() } } } Rectangle { id:boardBg width: items.cellSize * (items.numberOfCases + 0.2) height: items.cellSize * (items.numberOfCases + 0.2) z: 09 color: "#2E1B0C" // The chessboard GridView { id: chessboard cellWidth: items.cellSize cellHeight: items.cellSize width: items.cellSize * items.numberOfCases height: items.cellSize * items.numberOfCases interactive: false keyNavigationWraps: true model: items.numberOfCases*items.numberOfCases layoutDirection: Qt.RightToLeft delegate: square rotation: 180 z: 10 anchors.centerIn: boardBg Component { id: square Image { source: index % 2 + (Math.floor(index / items.numberOfCases) % 2) == 1 ? Activity.url + 'checkers-white.svg' : Activity.url + 'checkers-black.svg'; width: items.cellSize height: items.cellSize } } Behavior on rotation { PropertyAnimation { easing.type: Easing.InOutQuad; duration: 1400 } } function swap() { items.audioEffects.play('qrc:/gcompris/src/core/resource/sounds/flip.wav') if(chessboard.rotation == 180) chessboard.rotation = 0 else chessboard.rotation = 180 } } } } Repeater { id: squares model: items.positions delegate: square parent: chessboard DropArea { id: square x: items.cellSize * (9 - pos % items.numberOfCases) + spacing / 2 y: items.cellSize * Math.floor(pos / items.numberOfCases) + spacing / 2 width: items.cellSize - spacing height: items.cellSize - spacing z: 1 keys: acceptMove ? ['acceptMe'] : ['sorryNo'] property bool acceptMove: false property bool jumpable: false property int pos: modelData.pos property int spacing: 6 * ApplicationInfo.ratio Rectangle { id: possibleMove anchors.fill: parent color: parent.containsDrag ? '#803ACAFF' : 'transparent' border.width: parent.acceptMove || parent.jumpable ? 5 : 0 border.color: parent.acceptMove ? '#FF808080' : '#C0808080' radius: parent.acceptMove ? width*0.5 : 0 z: 1 } } function getSquareAt(pos) { for(var i=0; i < squares.count; i++) { if(squares.itemAt(i).pos === pos) return squares.itemAt(i) } return undefined } } Repeater { id: pieces model: items.positions delegate: piece parent: chessboard Piece { id: piece sourceSize.width: items.cellSize width: items.cellSize - spacing height: items.cellSize - spacing source: img ? Activity.url + img + '.svg' : '' img: modelData.img x: items.cellSize * (items.numberOfCases - 1 - pos % items.numberOfCases) + spacing / 2 y: items.cellSize * Math.floor(pos / items.numberOfCases) + spacing / 2 z: 1 pos: modelData.pos newPos: modelData.pos rotation: - chessboard.rotation property int spacing: 6 * ApplicationInfo.ratio Drag.active: dragArea.drag.active Drag.hotSpot.x: width / 2 Drag.hotSpot.y: height / 2 MouseArea { id: dragArea anchors.fill: parent enabled: !items.gameOver drag.target: parent onPressed: { piece.Drag.keys = ['acceptMe'] parent.z = 100 if(parent.isWhite == 1 && !items.blackTurn || parent.isWhite == 0 && items.blackTurn) { items.from = parent.newPos Activity.showPossibleMoves(items.from) } else if(items.from != -1 && squares.getSquareAt(parent.newPos)['acceptMove']) { Activity.moveTo(items.from, parent.newPos) } } onReleased: { // If no target or move not possible, we reset the position if(!piece.Drag.target || (items.from != -1 && !Activity.moveTo(items.from, piece.Drag.target.pos))) { var pos = parent.pos // Force recalc of the old x,y position parent.pos = -1 if(pieces.getPieceAt(pos)) pieces.getPieceAt(pos).move(pos) } } } } function moveTo(from, to, moves) { items.movesToDo.push({"from": from, "to": to, "move": moves}); if(items.movesToDo.length == 1) { moveInternal(); } } function moveInternal() { var moveToDo = items.movesToDo[0] var from = moveToDo.from; var to = moveToDo.to; var moves = moveToDo.move; - items.audioEffects.play('qrc:/gcompris/src/core/resource/sounds/scroll.wav') var fromPiece = getPieceAt(from) var toPiece = getPieceAt(to) if(moves.jumps.length != 0) items.audioEffects.play('qrc:/gcompris/src/core/resource/sounds/smudge.wav') else items.audioEffects.play('qrc:/gcompris/src/core/resource/sounds/scroll.wav') toPiece.hide(from) movingPiece = fromPiece if(moves.jumps.length != 0) { listJumps = moves.jumps } else { // create the move if needed listJumps = [Activity.viewPosToEngine(from), Activity.viewPosToEngine(to)] } } function promotion(to) { var toPiece = getPieceAt(to) toPiece.promotion() } function getPieceAt(pos) { for(var i=0; i < pieces.count; i++) { if(pieces.itemAt(i).newPos == pos) return pieces.itemAt(i) } return undefined } } property var movingPiece: undefined property var listJumps property bool restartNeeded: false onListJumpsChanged: { if(listJumps.length >= 2) { if(!animationTimer.isRunning) animationTimer.restart(); else restartNeeded = true; } } SequentialAnimation { id: animationTimer onRunningChanged: { if(!running && restartNeeded) start() } ScriptAction { script: { restartNeeded = false var to = Activity.engineToViewPos(listJumps[1]) movingPiece.move(to) } } PauseAnimation { duration: 200 } ScriptAction { script: { listJumps.shift() // only shifting does not trigger the onChanged var tmp = listJumps listJumps = tmp // only change player once all the jumps have been done if(listJumps.length == 1) { items.movesToDo.shift() if(items.movesToDo.length > 0) { pieces.moveInternal() } else { Activity.refresh() movingPiece = undefined } } } } } Timer { id: trigComputerMove repeat: false interval: 400 onTriggered: Activity.randomMove() } // Use to redo the computer move after the user move Timer { id: redoTimer repeat: false interval: 400 onTriggered: { acceptClick = true; Activity.moveByEngine(move) } property var move function moveByEngine(engineMove) { move = engineMove redoTimer.start() } } DialogHelp { id: dialogHelp onClose: home() } Bar { id: bar content: BarEnumContent { value: help | home | (items.twoPlayer ? 0 : level) | (items.twoPlayer && !items.gameOver ? 0 : reload) } onHelpClicked: { displayDialog(dialogHelp) } onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onHomeClicked: activity.home() onReloadClicked: { trigComputerMove.stop() Activity.initLevel() } } Bonus { id: bonus } } } diff --git a/src/activities/chess/Chess.qml b/src/activities/chess/Chess.qml index 66accc922..7309d888f 100644 --- a/src/activities/chess/Chess.qml +++ b/src/activities/chess/Chess.qml @@ -1,462 +1,461 @@ /* GCompris - chess.qml * * Copyright (C) 2015 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 QtQuick.Controls 1.5 import QtQuick.Controls.Styles 1.4 import GCompris 1.0 import "../../core" import "." import "chess.js" as Activity ActivityBase { id: activity property bool acceptClick: true property bool twoPlayers: false property int coordsOpacity: 1 // difficultyByLevel means that at level 1 computer is bad better at last level property bool difficultyByLevel: true property var fen: [ ["initial state", "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 1 1"], ["initial state", "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 1 1"], ["initial state", "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 1 1"], ["initial state", "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 1 1"], ["initial state", "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 1 1"] ] onStart: focus = true onStop: {} pageComponent: Image { id: background anchors.fill: parent source: Activity.url + 'background-wood.svg' signal start signal stop Component.onCompleted: { 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 GCAudio audioEffects: activity.audioEffects + property GCSfx audioEffects: activity.audioEffects property alias background: background property alias bar: bar property alias bonus: bonus property int barHeightAddon: ApplicationSettings.isBarHidden ? 1 : 3 property bool isPortrait: (background.height > background.width) property int cellSize: items.isPortrait ? Math.min((background.width - numbers.childrenRect.width) / (8 + 2), (background.height - controls.height - letters.childrenRect.height) / (8 + barHeightAddon)) : Math.min((background.width - numbers.childrenRect.width) / (8 + 2), (background.height - letters.childrenRect.height) / (8.5 + barHeightAddon)) property var fen: activity.fen property bool twoPlayer: activity.twoPlayers property bool difficultyByLevel: activity.difficultyByLevel property var positions property var pieces: pieces property var squares: squares property var history property var redo_stack property alias redoTimer: redoTimer property int from property bool blackTurn property bool gameOver property string message property alias trigComputerMove: trigComputerMove Behavior on cellSize { PropertyAnimation { easing.type: Easing.InOutQuad; duration: 1000 } } } onStart: { Activity.start(items) } onStop: { Activity.stop() } Grid { anchors { top: parent.top topMargin: items.isPortrait ? 0 : items.cellSize leftMargin: 10 * ApplicationInfo.ratio rightMargin: 10 * ApplicationInfo.ratio } columns: (items.isPortrait==true)?1:3 rows: (items.isPortrait==true)?2:1 width: (items.isPortrait==true)?undefined:background.width anchors.horizontalCenter: parent.horizontalCenter spacing: 10 horizontalItemAlignment: Grid.AlignHCenter verticalItemAlignment: Grid.AlignVCenter Column { id: controls anchors { leftMargin: 10 rightMargin: 10 } z: 20 width: items.isPortrait ? parent.width : Math.max(undo.width * 1.2, Math.min( (background.width * 0.9 - undo.width - chessboard.width), (background.width - chessboard.width) / 2)) GCText { color: "white" anchors.horizontalCenter: parent.horizontalCenter width: parent.width fontSize: smallSize text: items.message horizontalAlignment: Text.AlignHCenter wrapMode: TextEdit.WordWrap } Grid { spacing: 10 columns: items.isPortrait ? 3 : 1 anchors.horizontalCenter: parent.horizontalCenter horizontalItemAlignment: Grid.AlignHCenter verticalItemAlignment: Grid.AlignVCenter Button { id: undo height: 30 * ApplicationInfo.ratio text: qsTr("Undo"); style: GCButtonStyle { theme: "light" } onClicked: Activity.undo() enabled: items.history.length > 0 ? 1 : 0 opacity: enabled Behavior on opacity { PropertyAnimation { easing.type: Easing.InQuad duration: 200 } } } Button { id: redo height: 30 * ApplicationInfo.ratio text: qsTr("Redo"); style: GCButtonStyle { theme: "light" } onClicked: { if (!twoPlayers) { acceptClick = false; Activity.redo() } else { Activity.redo() } } enabled: items.redo_stack.length > 0 && acceptClick ? 1 : 0 opacity: enabled Behavior on opacity { PropertyAnimation { easing.type: Easing.InQuad duration: 200 } } } Button { height: 30 * ApplicationInfo.ratio text: qsTr("Swap"); style: GCButtonStyle { theme: "light" } enabled: items.twoPlayer opacity: enabled onClicked: chessboard.swap() } } } Rectangle { id:boardBg width: items.cellSize * 8.2 height: boardBg.width z: 08 color: "#452501" // The chessboard GridView { id: chessboard cellWidth: items.cellSize cellHeight: items.cellSize width: items.cellSize * 8 height: chessboard.width interactive: false keyNavigationWraps: true model: 64 layoutDirection: Qt.RightToLeft delegate: square rotation: 180 z: 10 anchors.centerIn: boardBg Component { id: square Image { source: index % 2 + (Math.floor(index / 8) % 2) == 1 ? Activity.url + 'chess-white.svg' : Activity.url + 'chess-black.svg'; width: items.cellSize height: items.cellSize } } Behavior on rotation { PropertyAnimation { easing.type: Easing.InOutQuad; duration: 1400 } } function swap() { items.audioEffects.play('qrc:/gcompris/src/core/resource/sounds/flip.wav') coordsOpacity = 0 timerSwap.start() if(chessboard.rotation == 180) chessboard.rotation = 0 else chessboard.rotation = 180 } } Timer { id: timerSwap interval: 1500 running: false repeat: false onTriggered: coordsOpacity = 1 } Grid { id: letters anchors.left: chessboard.left anchors.top: chessboard.bottom opacity: coordsOpacity Behavior on opacity { PropertyAnimation { easing.type: Easing.InOutQuad; duration: 500} } Repeater { id: lettersA model: chessboard.rotation == 0 ? ["F", "G", "F", "E", "D", "C", "B", "A"] : ["A", "B", "C", "D", "E", "F", "G", "H"] GCText { x: items.cellSize * (index % 8) + (items.cellSize/2-width/2) y: items.cellSize * Math.floor(index / 8) text: modelData color: "#CBAE7B" } } } Grid { id: numbers anchors.left: chessboard.right anchors.top: chessboard.top opacity: coordsOpacity Behavior on opacity { PropertyAnimation { easing.type: Easing.InOutQuad; duration: 500} } Repeater { model: chessboard.rotation == 0 ? ["1", "2", "3", "4", "5", "6", "7", "8"] : ["8", "7", "6", "5", "4", "3", "2", "1"] GCText { x: items.cellSize * Math.floor(index / 8) + width y: items.cellSize * (index % 8) + (items.cellSize/2-height/2) text: modelData color: "#CBAE7B" } } } Rectangle { id: boardBorder width: items.cellSize * 10 height: boardBorder.width anchors.centerIn: boardBg z: -1 color: "#542D0F" border.color: "#3A1F0A" border.width: items.cellSize * 0.1 } } } Repeater { id: squares model: items.positions delegate: square parent: chessboard DropArea { id: square x: items.cellSize * (7 - pos % 8) + spacing / 2 y: items.cellSize * Math.floor(pos / 8) + spacing / 2 width: items.cellSize - spacing height: square.width z: 1 keys: acceptMove ? ['acceptMe'] : ['sorryNo'] property bool acceptMove : false property int pos: modelData.pos property int spacing: 6 * ApplicationInfo.ratio Rectangle { id: possibleMove anchors.fill: parent color: parent.containsDrag ? '#803ACAFF' : 'transparent' border.width: parent.acceptMove ? 5 : 0 border.color: '#FF808080' z: 1 } } function getSquareAt(pos) { for(var i=0; i < squares.count; i++) { if(squares.itemAt(i).pos === pos) return squares.itemAt(i) } return(undefined) } } Repeater { id: pieces model: items.positions delegate: piece parent: chessboard Piece { id: piece sourceSize.width: items.cellSize width: items.cellSize - spacing height: piece.width source: img ? Activity.url + img + '.svg' : '' img: modelData.img x: items.cellSize * (7 - pos % 8) + spacing / 2 y: items.cellSize * Math.floor(pos / 8) + spacing / 2 z: 1 pos: modelData.pos newPos: modelData.pos rotation: - chessboard.rotation property int spacing: 6 * ApplicationInfo.ratio Drag.active: dragArea.drag.active Drag.hotSpot.x: width / 2 Drag.hotSpot.y: height / 2 MouseArea { id: dragArea anchors.fill: parent enabled: !items.gameOver drag.target: parent onPressed: { piece.Drag.keys = ['acceptMe'] parent.z = 100 if(parent.isWhite == 1 && !items.blackTurn || parent.isWhite == 0 && items.blackTurn) { items.from = parent.newPos Activity.showPossibleMoves(items.from) } else if(items.from != -1 && squares.getSquareAt(parent.newPos)['acceptMove']) { Activity.moveTo(items.from, parent.newPos) } } onReleased: { // If no target or move not possible, we reset the position if(!piece.Drag.target || (items.from != -1 && !Activity.moveTo(items.from, piece.Drag.target.pos))) { var pos = parent.pos // Force recalc of the old x,y position parent.pos = -1 pieces.getPieceAt(pos).move(pos) } } } } function moveTo(from, to) { - items.audioEffects.play('qrc:/gcompris/src/core/resource/sounds/scroll.wav') var fromPiece = getPieceAt(from) var toPiece = getPieceAt(to) if(toPiece.img != '') items.audioEffects.play('qrc:/gcompris/src/core/resource/sounds/smudge.wav') else items.audioEffects.play('qrc:/gcompris/src/core/resource/sounds/scroll.wav') toPiece.hide(from) fromPiece.move(to) } function promotion(to) { var toPiece = getPieceAt(to) toPiece.promotion() } function getPieceAt(pos) { for(var i=0; i < pieces.count; i++) { if(pieces.itemAt(i).newPos == pos) return pieces.itemAt(i) } return(undefined) } } Timer { id: trigComputerMove repeat: false interval: 400 onTriggered: Activity.randomMove() } // Use to redo the computer move after the user move Timer { id: redoTimer repeat: false interval: 400 onTriggered: { acceptClick = true; Activity.moveByEngine(move) } property var move function moveByEngine(engineMove) { move = engineMove redoTimer.start() } } DialogHelp { id: dialogHelp onClose: home() } Bar { id: bar content: BarEnumContent { value: help | home | (items.twoPlayer ? 0 : level) | (items.twoPlayer && !items.gameOver ? 0 : reload) } onHelpClicked: { displayDialog(dialogHelp) } onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onHomeClicked: activity.home() onReloadClicked: { trigComputerMove.stop() Activity.initLevel() } } Bonus { id: bonus } } } diff --git a/src/activities/enumerate/AnswerArea.qml b/src/activities/enumerate/AnswerArea.qml index f91a2e5fd..10cce88e6 100644 --- a/src/activities/enumerate/AnswerArea.qml +++ b/src/activities/enumerate/AnswerArea.qml @@ -1,153 +1,153 @@ /* GCompris - AnswerArea.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 "enumerate.js" as Activity import "../../core" Rectangle { id: answerBackground width: 140 * ApplicationInfo.ratio height: 70 * ApplicationInfo.ratio color: activeFocus ? "#ff07fff2" : "#cccccccc" radius: 10 border { width: activeFocus ? 3 : 1 color: "black" } property string imgPath // The backspace code coming from the virtual keyboard property string backspaceCode // True when the value is entered correctly property bool valid: false - property GCAudio audioEffects + property GCSfx audioEffects Component.onCompleted: Activity.registerAnswerItem(answerBackground) onValidChanged: valid ? audioEffects.play("qrc:/gcompris/src/core/resource/sounds/win.wav") : null // A top gradient Rectangle { anchors.fill: parent anchors.margins: activeFocus ? 3 : 1 radius: 10 gradient: Gradient { GradientStop { position: 0.0; color: valid ? "#CC00FF00" : "#CCFFFFFF" } GradientStop { position: 0.5; color: valid ? "#8000FF00" : "#80FFFFFF" } GradientStop { position: 1.0; color: valid ? "#8000F000" : "#00000000" } } } // The OK feedback Image { source: "qrc:/gcompris/src/core/resource/apply.svg"; fillMode: Image.PreserveAspectFit anchors { right: parent.left verticalCenter: parent.verticalCenter } sourceSize.height: parent.height * 0.8 anchors.margins: 10 opacity: valid ? 1.0 : 0.0 Behavior on opacity { NumberAnimation { duration: 200 } } } MouseArea { id: mouseArea anchors.fill: parent onClicked: Activity.registerAnswerItem(answerBackground) } Image { id: img anchors { left: parent.left leftMargin: 10 verticalCenter: parent.verticalCenter } height: parent.height * 0.75 width: height source: imgPath fillMode: Image.PreserveAspectFit } Keys.onPressed: { if(event.key === Qt.Key_Backspace) { backspace() } appendText(event.text) } function backspace() { userEntry.text = userEntry.text.slice(0, -1) if(userEntry.text.length === 0) { userEntry.text = "?" valid = Activity.setUserAnswer(imgPath, -1) return } else { valid = Activity.setUserAnswer(imgPath, parseInt(userEntry.text)) return } } function appendText(text) { if(text === answerBackground.backspaceCode) { backspace() return } var number = parseInt(text) if(isNaN(number)) return if(userEntry.text === "?") { userEntry.text = "" } if(userEntry.text.length >= 2) { valid = false return } userEntry.text += text valid = Activity.setUserAnswer(imgPath, parseInt(userEntry.text)) } GCText { id: userEntry anchors { left: img.right verticalCenter: img.verticalCenter leftMargin: 10 } text: "?" color: "black" fontSize: 28 style: Text.Outline styleColor: "white" } } diff --git a/src/activities/explore_farm_animals/ExploreLevels.qml b/src/activities/explore_farm_animals/ExploreLevels.qml index 33f8521ef..6226f8006 100644 --- a/src/activities/explore_farm_animals/ExploreLevels.qml +++ b/src/activities/explore_farm_animals/ExploreLevels.qml @@ -1,305 +1,305 @@ /* GCompris - ExploreLevels.qml * * Copyright (C) 2015 Ayush Agrawal * * Authors: * Beth Hadley (GTK+ version) * Ayush Agrawal (Qt Quick port) * Djalil MESLI (Qt Quick port) * Johnny Jazeix (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 QtQuick.Controls 1.5 import "../../core" import "explore-level.js" as Activity ActivityBase { id: activity property int numberOfLevels property string url property bool hasAudioQuestions onStart: focus = true onStop: {} pageComponent: Item { id: background /* In order to accept any screen ratio the play area is always a 1000x1000 * square and is centered in a big background image that is 2000x2000 */ Image { id: bg source: dataset.item.backgroundImage sourceSize.width: 2000 * ApplicationInfo.ratio sourceSize.height: 2000 * ApplicationInfo.ratio width: 2000 * background.playRatio height: width anchors.centerIn: parent } property bool horizontalLayout: background.width > background.height property int playX: (activity.width - playWidth) / 2 property int playY: (activity.height - playHeight) / 2 property int playWidth: horizontalLayout ? activity.height : activity.width property int playHeight: playWidth property double playRatio: playWidth / 1000 focus: true signal start signal stop Component.onCompleted: { activity.start.connect(start) activity.stop.connect(stop) } // Add here the QML items you need to access in javascript QtObject { id: items property GCAudio audioVoices: activity.audioVoices - property GCAudio audioEffects: activity.audioEffects + property GCSfx audioEffects: activity.audioEffects property Item main: activity.main property alias background: background property alias bar: bar property alias bonus: bonus property alias score: score property alias progressbar: progressbar property alias ok: ok property alias dataModel: dataModel property alias dataset: dataset property alias instruction: instruction property alias instructionText: instructionText property alias descriptionPanel: descriptionPanel property alias nextQuestion: nextQuestion property bool hasAudioQuestions: activity.hasAudioQuestions property string currentAudio property var questionOrder property var currentQuestion: items.dataset ? items.dataset.item.tab[items.questionOrder[progressbar.value]] : "" } Timer { id: nextQuestion repeat: false interval: 2000 onTriggered: { Activity.repeat(); } } Loader{ id: dataset asynchronous: false onStatusChanged: { if (status == Loader.Ready) { // create table of size N filled with numbers from 0 to N items.questionOrder = Array.apply(null, {length: items.dataModel.count}).map(Number.call, Number) } } } onStart: { Activity.start(items, url, numberOfLevels) } onStop: { Activity.stop() } Keys.onEscapePressed: { descriptionPanel.visible ? descriptionPanel.closeDescriptionPanel() : home() } Repeater { id: dataModel model: dataset && dataset.item && dataset.item.tab ? dataset.item.tab.length : 0 AnimalLevels { questionId: index source: dataset.item.tab[index].image x: background.playX + background.playWidth * dataset.item.tab[index].x - width / 2 y: background.playY + background.playHeight * dataset.item.tab[index].y - height / 2 width: background.playWidth * dataset.item.tab[index].width height: background.playHeight * dataset.item.tab[index].height title: dataset.item.tab[index].title description: dataset.item.tab[index].text imageSource: dataset.item.tab[index].image2 question: dataset.item.tab[index].text2 audio: dataset.item.tab[index].audio !== undefined ? dataset.item.tab[index].audio : "" Component.onCompleted: { displayDescription.connect(displayDescriptionItem) } } } function displayDescriptionItem(animal) { descriptionPanel.title = animal.title descriptionPanel.description = animal.description descriptionPanel.imageSource = animal.imageSource descriptionPanel.visible = true descriptionPanel.showDescriptionPanel() } AnimalDescriptionLevels { id: descriptionPanel width: parent.width height: parent.height z: instruction.z + 1 } Column { id: progress visible: items.score.currentSubLevel != 1 anchors.bottom: bar.top anchors.right: parent.right anchors.margins: 10 * ApplicationInfo.ratio ProgressBar { id: progressbar height: progressbarText.height width: bar.width property string message onValueChanged: message = value + "/" + maximumValue onMaximumValueChanged: message = value + "/" + maximumValue GCText { id: progressbarText anchors.centerIn: parent fontSize: mediumSize font.bold: true color: "black" text: progressbar.message } } } Image { id: ok visible: progressbar.value === progressbar.maximumValue source:"qrc:/gcompris/src/core/resource/bar_ok.svg" sourceSize.width: questionText.height * 2 fillMode: Image.PreserveAspectFit anchors.right: progress.left anchors.bottom: bar.top anchors.margins: 10 * ApplicationInfo.ratio MouseArea { anchors.fill: parent onClicked: Activity.nextLevel() } } Row { id: row spacing: 10 * ApplicationInfo.ratio anchors.fill: parent anchors.margins: 10 * ApplicationInfo.ratio layoutDirection: leftCol.width === 0 ? Qt.RightToLeft : Qt.LeftToRight Column { id: leftCol spacing: 10 * ApplicationInfo.ratio Rectangle { id: instruction width: row.width - rightCol.width - 10 * ApplicationInfo.ratio height: instructionText.height color: "#CCCCCCCC" radius: 10 border.width: 3 border.color: "black" GCText { id: instructionText horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter anchors.centerIn: parent.Center color: "black" width: parent.width wrapMode: Text.Wrap text: (dataset.item && items.score.currentSubLevel - 1 != items.score.numberOfSubLevels && items.score.currentSubLevel != 0) ? dataset.item.instructions[items.score.currentSubLevel - 1].text : "" } MouseArea { anchors.fill: parent onClicked: instruction.visible = false enabled: instruction.visible } } Rectangle { id: question width: row.width - rightCol.width - 10 * ApplicationInfo.ratio height: questionText.height color: '#CCCCCCCC' radius: 10 border.width: 3 border.color: "black" visible: items.score.currentSubLevel == 3 || (items.score.currentSubLevel == 2 && !items.hasAudioQuestions) GCText { id: questionText horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter anchors.centerIn: parent.Center color: "black" width: parent.width wrapMode: Text.Wrap text: items.currentQuestion ? items.currentQuestion.text2 : "" } } } Column { id: rightCol spacing: 10 * ApplicationInfo.ratio Score { id: score anchors { bottom: undefined right: undefined } } BarButton { id: repeatItem source: "qrc:/gcompris/src/core/resource/bar_repeat.svg"; sourceSize.width: 60 * ApplicationInfo.ratio anchors.right: parent.right visible: items.score.currentSubLevel == 2 && activity.hasAudioQuestions //&& ApplicationSettings.isAudioVoicesEnabled onClicked: Activity.repeat(); } } } DialogHelp { id: dialogHelp onClose: home() } Bar { id: bar content: BarEnumContent { value: help | home | level | reload } onHelpClicked: { displayDialog(dialogHelp) } onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onHomeClicked: activity.home() onReloadClicked: Activity.start(items, url, numberOfLevels) } Bonus { id: bonus } } } diff --git a/src/activities/fifteen/Fifteen.qml b/src/activities/fifteen/Fifteen.qml index 3f03af008..f4b83d472 100644 --- a/src/activities/fifteen/Fifteen.qml +++ b/src/activities/fifteen/Fifteen.qml @@ -1,187 +1,187 @@ /* GCompris - fifteen.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 QtGraphicalEffects 1.0 import "../../core" import "fifteen.js" as Activity ActivityBase { id: activity onStart: focus = true onStop: {} pageComponent: Image { id: background anchors.fill: parent source: Activity.url + "background.svg" sourceSize.width: Math.max(parent.width, parent.height) fillMode: Image.PreserveAspectCrop signal start signal stop Keys.onPressed: Activity.processPressedKey(event) Component.onCompleted: { 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 GCAudio audioEffects: activity.audioEffects + property GCSfx audioEffects: activity.audioEffects property alias background: background property alias bar: bar property alias bonus: bonus property alias model: fifteenModel property string scene: bar.level < 5 ? Activity.url + "Fishing_Boat_Scene.svg" : Activity.url + "Coastal_Path.svg" } onStart: { Activity.start(items) } onStop: { Activity.stop() } Image { source: Activity.url + "blueframe.svg" sourceSize.width: Math.min(background.width * 0.9, background.height * 0.9) anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter } Grid { id: puzzleArea anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter columns: 4 spacing: 0 property alias trans: trans ListModel { id: fifteenModel } move: Transition { id: trans NumberAnimation { properties: "x, y" easing.type: Easing.InOutQuad } } Repeater { id: repeater model: fifteenModel delegate: Item { width: Math.min(background.width * 0.2, background.height * 0.2) height: width clip: true property int val: value Image { id: image source: value ? items.scene : "" sourceSize.width: parent.width * 4 fillMode: Image.Pad transform: Translate { x: - image.width / 4 * ((value - 1) % 4) y: - image.width / 4 * Math.floor((value - 1) / 4) } } GCText { id: text anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter text: value && bar.level % 2 == 1 ? value : "" fontSize: mediumSize color: "#ffe9f0fb" style: Text.Outline styleColor: "#ff1c4788" } DropShadow { anchors.fill: text cached: false horizontalOffset: 3 verticalOffset: 3 radius: 1 samples: 16 color: "#ff1c4788" source: text } } } } MultiPointTouchArea { x: puzzleArea.x y: puzzleArea.y width: puzzleArea.width height: puzzleArea.height onPressed: checkTouchPoint(touchPoints) function checkTouchPoint(touchPoints) { for(var i in touchPoints) { var touch = touchPoints[i] var block = puzzleArea.childAt(touch.x, touch.y) if(!puzzleArea.trans.running && block) { Activity.onClick(block.val) if(Activity.checkAnswer()) bonus.good('flower') else activity.audioEffects.play("qrc:/gcompris/src/core/resource/sounds/flip.wav") } } } } 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/followline/Followline.qml b/src/activities/followline/Followline.qml index f51349334..c4b44a9b8 100644 --- a/src/activities/followline/Followline.qml +++ b/src/activities/followline/Followline.qml @@ -1,173 +1,173 @@ /* GCompris - FollowLine.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 "followline.js" as Activity ActivityBase { id: activity onStart: focus = true onStop: {} pageComponent: Image { id: background source: Activity.url + "background.svg" fillMode: Image.PreserveAspectCrop sourceSize.width: Math.max(parent.width, parent.height) signal start signal stop Component.onCompleted: { 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 GCAudio audioEffects: activity.audioEffects + property GCSfx audioEffects: activity.audioEffects property alias fireman: fireman property alias fire: fire property alias bar: bar property alias bonus: bonus property int currentLock: 0 property int lastLock: 0 } onHeightChanged: Activity.initLevel() onStart: { Activity.start(items) } onStop: { Activity.stop() } Image { id: fireman source: Activity.url + "fireman.svg" sourceSize.width: 182 * ApplicationInfo.ratio anchors { left: parent.left verticalCenter: parent.verticalCenter verticalCenterOffset: - height / 10 } } Image { id: fire source: Activity.url + "fire.svg" sourceSize.width: 90 * ApplicationInfo.ratio anchors { right: parent.right bottom: bar.top } Image { id: fireflame source: Activity.url + "fire_flame.svg" sourceSize.width: 90 * ApplicationInfo.ratio anchors { fill: parent } Behavior on opacity { NumberAnimation { duration: 2000 } } onOpacityChanged: if(opacity == 0) Activity.nextLevel() } } Image { id: water source: Activity.url + "water_spot.svg" sourceSize.width: 148 * ApplicationInfo.ratio z: 200 opacity: 0 anchors { right: parent.right bottom: fire.top bottomMargin: - fire.height / 2 } Behavior on opacity { NumberAnimation { duration: 500 } } } DialogHelp { id: dialogHelp onClose: home() } function win() { fireflame.opacity = 0 water.opacity = 1 } MultiPointTouchArea { anchors.fill: parent maximumTouchPoints: 1 z: 1000 onTouchUpdated: { for(var i in touchPoints) { var touch = touchPoints[i] var part = background.childAt(touch.x, touch.y) if(part) { if(items.currentLock == part.index) { items.currentLock++ if(items.currentLock == items.lastLock) { background.win() activity.audioEffects.play("qrc:/gcompris/src/core/resource/sounds/water.wav") } else { activity.audioEffects.play("qrc:/gcompris/src/core/resource/sounds/darken.wav") } } } } } } Bar { id: bar content: BarEnumContent { value: help | home | level } onHelpClicked: displayDialog(dialogHelp) onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onHomeClicked: activity.home() onLevelChanged: { fireflame.opacity = 1 water.opacity = 0 } } Bonus { id: bonus Component.onCompleted: win.connect(Activity.nextLevel) } MouseArea { anchors.fill: parent enabled: !ApplicationInfo.isMobile hoverEnabled: true onPositionChanged: items.currentLock > 0 && fireflame.opacity == 1 ? items.currentLock-- : false } } } diff --git a/src/activities/followline/LinePart.qml b/src/activities/followline/LinePart.qml index f1760abf1..d57014a0b 100644 --- a/src/activities/followline/LinePart.qml +++ b/src/activities/followline/LinePart.qml @@ -1,83 +1,83 @@ /* GCompris - LinePart.qml * * Copyright (C) 2014 Bruno Coudoin * * Authors: * Bruno Coudoin * * 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 "../../core" Item { id: part property QtObject items property int index - property GCAudio audioEffects + property GCSfx audioEffects Rectangle { anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter anchors.verticalCenterOffset: - parent.height / 2 width: parent.width height: parent.height * 0.2 radius: height / 8 color: "#30354e" z: 10 } Rectangle { anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenterOffset: - parent.width * 0.1 anchors.verticalCenterOffset: 0 width: parent.width * 1.2 height: parent.height * 0.9 radius: height / 4 z: 5 color: index < part.items.currentLock ? "#3ca7e0" : index === part.items.currentLock ? "#dd3128" : "#7A7F8E" MouseArea { anchors.fill: parent hoverEnabled: true onEntered: { if(part.items.currentLock == part.index) { part.items.currentLock++ if(part.items.currentLock >= part.items.lastLock) { audioEffects.play("qrc:/gcompris/src/core/resource/sounds/water.wav") items.background.win() } else { audioEffects.play("qrc:/gcompris/src/core/resource/sounds/darken.wav") } } } } } Rectangle { anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter anchors.verticalCenterOffset: parent.height / 2 width: parent.width height: parent.height * 0.2 radius: height / 8 color: "#30354e" z: 10 } } diff --git a/src/activities/football/Football.qml b/src/activities/football/Football.qml index fd72307db..436ea5b12 100644 --- a/src/activities/football/Football.qml +++ b/src/activities/football/Football.qml @@ -1,176 +1,176 @@ /* GCompris - football.qml * * Copyright (C) 2014 Bruno Coudoin * * Authors: * Bruno Coudoin (GTK+ version) * Bruno Coudoin (Qt Quick port) * Bharath M S (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 "../../core" import "football.js" as Activity import GCompris 1.0 ActivityBase { id: activity onStart: focus = true onStop: {} pageComponent: Rectangle { id: background anchors.fill: parent signal start signal stop Component.onCompleted: { 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 field: field property alias border: border property alias ball: ball property alias line: line property alias tux: tux property alias moveTux: moveTux property alias moveUp: moveUp property alias moveDown: moveDown property alias bar: bar property alias bonus: bonus property alias timer: timer - property GCAudio audioEffects: activity.audioEffects + property GCSfx audioEffects: activity.audioEffects } onStart: { Activity.start(items) } onStop: { Activity.stop() } /* To modify when screen size changes */ onHeightChanged: { moveUp.to = 0 moveDown.to = background.height * 0.75 - tux.height moveTux.restart() } Image { id: field source: Activity.url + "background.svg" anchors.fill: parent Rectangle { id: border height: parent.height * 0.75 width: parent.width * 0.856 x: parent.width * 0.075 y: parent.height * 0.125 color: "transparent" Rectangle { id: line opacity: 0.0 color: "#ee4b4b" transformOrigin: Item.TopLeft } Image { id: ball source: Activity.url + "ball.svg" sourceSize.height: 50 * ApplicationInfo.ratio property real change z: 10 MultiPointTouchArea { anchors.fill: parent touchPoints: [ TouchPoint { id: point1 }] onReleased: { line.opacity = 0 Activity.startMotion(point1.x - ball.width / 2, point1.y - ball.height / 2) activity.audioEffects.play("qrc:/gcompris/src/core/resource/sounds/brick.wav") } onPressed: line.opacity = 1 onTouchUpdated: { var point = ball.mapToItem(border, point1.x, point1.y) Activity.drawLine(point.x, point.y, ball.x + ball.width/2, ball.y + ball.height/2) } } } Image { id: tux source: Activity.url+"tux_top.svg" sourceSize.height: 80 * ApplicationInfo.ratio x: border.width - tux.width y: border.height / 2 SequentialAnimation on y { id: moveTux loops: Animation.Infinite running: true PropertyAnimation { id: moveUp duration: 800 easing.type: Easing.InOutQuad } PropertyAnimation { id: moveDown duration: 800 easing.type: Easing.InOutQuad } } } } Rectangle { width: parent.width * 0.1 height: parent.height * 0.75 color: "blue" anchors.top: border.top anchors.left: border.right z:10 opacity: 0.3 } } Timer { id: timer interval: 16; running: false; repeat: true onTriggered: Activity.ballMotion() } 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/gletters/Gletters.qml b/src/activities/gletters/Gletters.qml index a04713339..226aa9bcf 100644 --- a/src/activities/gletters/Gletters.qml +++ b/src/activities/gletters/Gletters.qml @@ -1,274 +1,274 @@ /* 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/" /* no need to display the configuration button for smallnumbers */ property bool configurationButtonVisible: true property bool uppercaseOnly: 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.getInitialConfiguration() 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 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 GCAudio audioEffects: activity.audioEffects + property GCSfx audioEffects: activity.audioEffects property alias locale: background.locale property alias textinput: textinput } onStart: { Activity.start(items, uppercaseOnly, mode); Activity.focusTextInput() } onStop: { Activity.stop() } 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 = ""; } } } DialogActivityConfig { id: dialogActivityConfig currentActivity: activity content: Component { Item { property alias localeBox: localeBox property alias uppercaseBox: uppercaseBox height: column.height property alias availableLangs: langs.languages LanguageList { id: langs } Column { id: column spacing: 10 width: parent.width Flow { spacing: 5 width: dialogActivityConfig.width GCComboBox { id: localeBox model: langs.languages background: dialogActivityConfig label: qsTr("Select your locale") } } GCDialogCheckBox { id: uppercaseBox width: dialogActivityConfig.width text: qsTr("Uppercase only mode") checked: activity.uppercaseOnly } } } } onClose: home() onLoadData: { if(dataToSave && dataToSave["locale"]) { background.locale = dataToSave["locale"]; activity.uppercaseOnly = dataToSave["uppercaseMode"] === "true" ? true : false; } } onSaveData: { var oldLocale = background.locale; var newLocale = dialogActivityConfig.configItem.availableLangs[dialogActivityConfig.loader.item.localeBox.currentIndex].locale; // Remove .UTF-8 if(newLocale.indexOf('.') != -1) { newLocale = newLocale.substring(0, newLocale.indexOf('.')) } var oldUppercaseMode = activity.uppercaseOnly activity.uppercaseOnly = dialogActivityConfig.configItem.uppercaseBox.checked dataToSave = {"locale": newLocale, "uppercaseMode": ""+activity.uppercaseOnly} background.locale = newLocale; // Restart the activity with new information if(oldLocale !== newLocale || oldUppercaseMode !== activity.uppercaseOnly) { background.stop(); background.start(); } } function setDefaultValues() { var localeUtf8 = background.locale; if(background.locale != "system") { localeUtf8 += ".UTF-8"; } for(var i = 0 ; i < dialogActivityConfig.configItem.availableLangs.length ; i ++) { if(dialogActivityConfig.configItem.availableLangs[i].locale === localeUtf8) { dialogActivityConfig.loader.item.localeBox.currentIndex = i; break; } } } } DialogHelp { id: dialogHelp onClose: home() } Bar { id: bar anchors.bottom: keyboard.top content: BarEnumContent { value: configurationButtonVisible ? (help | home | level | config) : (help | home | level)} onHelpClicked: { displayDialog(dialogHelp) } onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onHomeClicked: activity.home() onConfigClicked: { dialogActivityConfig.active = true dialogActivityConfig.setDefaultValues() displayDialog(dialogActivityConfig) } } Bonus { id: bonus interval: 2000 Component.onCompleted: win.connect(Activity.nextLevel) } Score { id: score anchors.top: undefined anchors.topMargin: 10 * ApplicationInfo.ratio anchors.right: parent.right anchors.rightMargin: 10 * ApplicationInfo.ratio anchors.bottom: keyboard.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/gnumch-equality/Creature.qml b/src/activities/gnumch-equality/Creature.qml index f87f534b6..3fe963e2d 100644 --- a/src/activities/gnumch-equality/Creature.qml +++ b/src/activities/gnumch-equality/Creature.qml @@ -1,169 +1,169 @@ /* GCompris - Creature.qml * * Copyright (C) 2014 Manuel Tondeur * * Authors: * Joe Neeman (spuzzzzzzz@gmail.com) (GTK+ version) * Manuel Tondeur (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" Item { id: creature property int index property string monsterType property bool movable property bool movingOn: false property bool eating: false property int frames property int frameW property real widthRatio - property GCAudio audioEffects + property GCSfx audioEffects function moveTo(direction) { if (!movable) return true if (!hasReachLimit(direction)) { movementOn(direction) return true } else { return false } } function init() { index = 0 x = 0 y = 0 } function hasReachLimit(direction) { switch (direction) { case 0: if ((index + 1) % 6 > 0) return false break case 1: if ((index % 6) > 0) return false break case 2: if (index < 30) return false break case 3: if (index > 5) return false break } return true } function movementOn(direction) { // Compute if the direction is vertical (1) or not (0) var vertical = Math.floor(direction / 2) var sign = Math.pow(-1, (direction)) index += sign * (1 + 5 * vertical) var restIndex = index % 6 y = ((index - restIndex) / 6) * grid.cellHeight x = restIndex * grid.cellWidth } function updatePosition() { var restIndex = index % 6 y = ((index - restIndex) / 6) * grid.cellHeight x = restIndex * grid.cellWidth } index: 0 z: 0 movable: true width: grid.cellWidth height: grid.cellHeight onEatingChanged: { if (eating == true) { creatureImage.restart() creatureImage.resume() creature.audioEffects.play("qrc:/gcompris/src/activities/gnumch-equality/resource/eat.wav") } } AnimatedSprite { id: creatureImage property int turn: 0 anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter width: parent.width / parent.height < widthRatio ? parent.width * 0.85 : parent.height * 0.85 * widthRatio height: width * (1/widthRatio) source: "qrc:/gcompris/src/activities/gnumch-equality/resource/" + monsterType + ".png" frameCount: frames frameWidth: frameW frameDuration: 50 currentFrame: 0 running: false onCurrentFrameChanged: { if (currentFrame == frames - 1) { turn++ } } onTurnChanged: { if (turn == 2) { eating = false turn = 0 currentFrame = 0 pause() } } } Behavior on x { NumberAnimation { id: xAnim duration: 300 onRunningChanged: { movingOn = !movingOn } } } Behavior on y { NumberAnimation { id: yAnim duration: 300 onRunningChanged: { movingOn = !movingOn } } } Behavior on opacity { NumberAnimation { duration: 500 } } } diff --git a/src/activities/guesscount/Guesscount.qml b/src/activities/guesscount/Guesscount.qml index 0788f665a..136e68840 100644 --- a/src/activities/guesscount/Guesscount.qml +++ b/src/activities/guesscount/Guesscount.qml @@ -1,420 +1,420 @@ /* GCompris - guesscount.qml * * Copyright (C) 2016 RAHUL YADAV * * Authors: * Pascal Georges (GTK+ version) * RAHUL YADAV (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 "../../core" import "guesscount.js" as Activity ActivityBase { id: activity onStart: focus = true onStop: {} property bool needRestart: true pageComponent: Image { id: background anchors.fill: parent source: Activity.baseUrl + "/backgroundW01.svg" signal start signal stop Component.onCompleted: { dialogActivityConfig.getInitialConfiguration() activity.start.connect(start) activity.stop.connect(stop) } MouseArea { anchors.fill: parent onClicked: { if(warningDialog.visible) warningDialog.visible = false } } // 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 sublevel: 0 property alias operandRow: operandRow property var data property int result: data[sublevel-1][1] property alias timer: timer property alias warningDialog: warningDialog - property GCAudio audioEffects: activity.audioEffects + property GCSfx audioEffects: activity.audioEffects property bool solved property bool levelchanged: false property var levelArr property string mode property int currentlevel } onStart: if (activity.needRestart) { Activity.start(items); activity.needRestart = false; } else Activity.initLevel(); onStop: { Activity.stop() } JsonParser { id: parser onError: console.error("Guesscount: Error parsing JSON: " + msg); } Loader { id: admin active: false sourceComponent: Column { spacing: 10 width: parent.width height: parent.height Repeater { id:levels model: Activity.numberOfLevel Admin { id:level level: modelData+1 width: parent.width height: parent.height data: items.data } } } } Rectangle { id: top width: parent.width height: parent.height/10 anchors { top: parent.top topMargin: 20 } color: "transparent" Rectangle { id: questionNo width: parent.width*0.2 height: parent.height radius: 20 color: "steelblue" anchors { right: guessLabel.left rightMargin: 20 } GCText { color: "#E8E8E8" anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter fontSize: mediumSize text: qsTr("%1/%2").arg(items.sublevel).arg(items.data.length) } } Rectangle { id: guessLabel width: parent.width*0.4 height: parent.height radius: 20 color: "orange" anchors { horizontalCenter: parent.horizontalCenter } Rectangle { id: insideFill width: parent.width - anchors.margins height: parent.height - anchors.margins anchors.verticalCenter: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter anchors.margins: parent.height/4 radius: 10 color: "#E8E8E8" } GCText { id: guess anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter fontSize: smallSize text: qsTr("Guesscount: %1").arg(items.result) } } } Column { id: col spacing: 10 anchors.top: top.bottom anchors.topMargin: 20 anchors.left: parent.left anchors.leftMargin: 5 width: parent.width height: parent.height-top.height-background.height/5 OperatorRow { id: operatorRow width: parent.width height: parent.height/7 mode: items.mode operators: items.levelArr level: items.currentlevel } OperandRow { id: operandRow width: parent.width height: parent.height/7 } Repeater { id: repeat model: operatorRow.repeater.model.length delegate: OperationRow { id: operationRow width: col.width height: col.height/7 property alias operationRow: operationRow noOfRows: operatorRow.repeater.model.length rowNo: modelData guesscount: items.result prevText: modelData ? repeat.itemAt(modelData-1).text : '' prevComplete: modelData ? repeat.itemAt(modelData-1).complete : false reparent: items.solved || items.levelchanged } } } DialogHelp { id: dialogHelp onClose: home() } DialogActivityConfig { id: dialogActivityConfig currentActivity: activity content: Component { Item { property alias modeBox: modeBox property var availableModes: [ { "text": qsTr("Admin"), "value": "admin" }, { "text": qsTr("BuiltIn"), "value": "builtin" } ] Rectangle { id: flow width: dialogActivityConfig.width height: background.height GCComboBox { id: modeBox anchors { top: parent.top topMargin: 5 } model: availableModes background: dialogActivityConfig label: qsTr("Select your mode") } Row { id: labels spacing: 20 anchors { top: modeBox.bottom topMargin: 5 } visible: modeBox.currentIndex == 0 Repeater { model: 2 Row { spacing: 10 Rectangle { id: label width: background.width*0.3 height: background.height/15 radius: 20.0; color: modelData ? "green" : "red" GCText { anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter fontSize: smallSize text: modelData ? qsTr("Selected") : qsTr("Not Selected") } } } } } Rectangle { width: parent.width color: "transparent" height: parent.height/1.25-labels.height-modeBox.height anchors { top: labels.bottom topMargin: 5 } ListView { anchors.fill: parent visible: modeBox.currentIndex == 0 spacing: 5 model: Activity.numberOfLevel clip: true delegate: Admin { id: level level: modelData levelOperators: items.levelArr width: background.width height: background.height/10 } } } } } } onClose: { if(Activity.configDone(items.levelArr)){ Activity.initLevel() home() } } onLoadData: { if(dataToSave && dataToSave["mode"] ) { items.mode = dataToSave["mode"] if(dataToSave["levelArr"] == undefined) dataToSave["levelArr"] = Activity.defaultOperators if(dataToSave["levelArr"].length != Activity.numberOfLevel) items.levelArr = Activity.defaultOperators else items.levelArr = dataToSave["levelArr"] } else{ items.mode='builtin' items.levelArr = Activity.defaultOperators } } onSaveData: { items.mode = dialogActivityConfig.configItem.availableModes[dialogActivityConfig.configItem.modeBox.currentIndex].value dataToSave = {"mode": items.mode, "levelArr":items.levelArr} activity.needRestart = true } function setDefaultValues() { for(var i = 0 ; i < dialogActivityConfig.configItem.availableModes.length ; i ++) { if(dialogActivityConfig.configItem.availableModes[i].value === items.mode) { dialogActivityConfig.configItem.modeBox.currentIndex = i; break; } } } } Bar { id: bar content: BarEnumContent { value: help | home | level | config} onConfigClicked: { dialogActivityConfig.active = true dialogActivityConfig.setDefaultValues(); displayDialog(dialogActivityConfig) } onHelpClicked: { displayDialog(dialogHelp) } onPreviousLevelClicked: { items.levelchanged = true Activity.previousLevel() } onNextLevelClicked: { items.levelchanged = true Activity.nextLevel() } onHomeClicked: activity.home() } Bonus { id: bonus Component.onCompleted: win.connect(Activity.nextSublevel) } Timer { id: timer interval: 1500 repeat: false onTriggered: { items.solved = true } } Rectangle { id: warningDialog width: parent.width*0.49 height: parent.height/6 visible: false color: "steelblue" gradient: Gradient { GradientStop { position: 0.0; color: "#000" } GradientStop { position: 0.9; color: "#666" } GradientStop { position: 1.0; color: "#AAA" } } radius: 30 property alias dialogText: dialogText anchors.centerIn: parent GCText { id: dialogText anchors.centerIn: parent anchors { centerIn: warningDialog } opacity: warningDialog.opacity z: warningDialog.z fontSize: background.vert ? regularSize : smallSize color: "white" style: Text.Outline styleColor: "black" horizontalAlignment: Text.AlignHCenter width: parent.width wrapMode: TextEdit.WordWrap } states: [ State { when: warningDialog.visible PropertyChanges { target: top opacity: 0.5 } PropertyChanges { target: col opacity: 0.5 } }, State { when: !warningDialog.visible PropertyChanges { target: top opacity: 1 } PropertyChanges { target: col opacity: 1 } } ] } } } diff --git a/src/activities/hexagon/HexagonItem.qml b/src/activities/hexagon/HexagonItem.qml index 25b545819..a0cdd5904 100644 --- a/src/activities/hexagon/HexagonItem.qml +++ b/src/activities/hexagon/HexagonItem.qml @@ -1,122 +1,122 @@ /* GCompris - Hexagon.qml * * Copyright (C) 2014 Bruno Coudoin * * Authors: * Christof Petig and Ingo Konrad (GTK+ version) * Bruno Coudoin (Qt Quick port) * Timothée Giet (add mode without OpenGL) * * 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 "hexagon.js" as Activity import "../../core" import GCompris 1.0 Item { id: hexagon - property GCAudio audioEffects + property GCSfx audioEffects property ParticleSystemStar particles property alias color: softCanvas.color property bool hasStrawberry: false property double ix property double iy property int nbx property int nby // Warning testing parent here, just to avoid an error at deletion time property double r: parent ? Math.min(parent.width / nbx / 2, (parent.height - 10) / nby / 2) : 0 property double offsetX: parent ? (parent.width % (width * nbx)) / 2 : 0 property double offsetY: parent ? (parent.height % (height * nby)) / 2 : 0 x: (iy % 2 ? width * ix + width / 2 : width * ix) + offsetX y: height * iy - (Math.sin((Math.PI * 2) / 12) * r * 2 * iy) / 2 + offsetY width: Math.cos((Math.PI * 2) / 12) * r * 2 height: r * 2 Image { id: strawberry anchors.fill: parent opacity: 0 onSourceChanged: opacity = 1 Behavior on opacity { PropertyAnimation { duration: 2000; easing.type: Easing.OutQuad } } } Image { id: border anchors.fill: parent source: Activity.url + "hexagon_border.svg" Behavior on opacity { PropertyAnimation { duration: 500 } } } Image { id: canvas anchors.fill: parent source: Activity.url + "hexagon.svg" visible: false } Rectangle { id:softCanvas width: parent.width * 0.8 height: width radius: width * 0.5 anchors.centerIn: parent opacity: strawberry.opacity == 0 ? 0.65 : 0 visible: ApplicationInfo.useOpenGL ? false : true } ColorOverlay { id: colorOverlay anchors.fill: canvas source: canvas onOpacityChanged: if(opacity == 0) Activity.strawberryFound() color: softCanvas.color opacity: 0.65 Behavior on opacity { PropertyAnimation { duration: 500 } } } // Create a particle only for the strawberry Loader { id: particleLoader anchors.fill: parent active: hasStrawberry sourceComponent: particle } Component { id: particle ParticleSystemStarLoader { id: particles clip: false } } property bool isTouched: false function touched() { if(hasStrawberry && !isTouched) { colorOverlay.opacity = 0 border.opacity = 0 isTouched = true strawberry.source = Activity.url + "strawberry.svg" audioEffects.play("qrc:/gcompris/src/core/resource/sounds/win.wav") Activity.strawberryFound() particleLoader.item.burst(40) } else { hexagon.color = Activity.getColor(Activity.getDistance(hexagon.ix, hexagon.iy)) } } } diff --git a/src/activities/intro_gravity/IntroGravity.qml b/src/activities/intro_gravity/IntroGravity.qml index 20be5d08c..09d7c6735 100644 --- a/src/activities/intro_gravity/IntroGravity.qml +++ b/src/activities/intro_gravity/IntroGravity.qml @@ -1,238 +1,238 @@ /* GCompris - intro_gravity.qml * * Copyright (C) 2015 Siddhesh suthar * * Authors: * Bruno Coudoin and Matilda Bernard (GTK+ version) * Siddhesh suthar (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 "intro_gravity.js" as Activity ActivityBase { id: activity onStart: focus = true onStop: {} property int oldWidth: width onWidthChanged: { // Reposition planets and asteroids, same for height Activity.repositionObjectsOnWidthChanged(width / oldWidth) oldWidth = width } property int oldHeight: height onHeightChanged: { // Reposition planets and asteroids, same for height Activity.repositionObjectsOnHeightChanged(height / oldHeight) oldHeight = height } pageComponent: Image { id: background anchors.fill: parent source: Activity.url+"background.svg" sourceSize.width: Math.max(parent.width, parent.height) fillMode: Image.PreserveAspectCrop signal start signal stop Component.onCompleted: { 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 planetLeft: planetLeft property alias planetRight: planetRight property alias scaleLeft: planetLeft.value property alias scaleRight: planetRight.value property alias spaceship: spaceship property alias shuttle: shuttle property alias shuttleMotion: shuttleMotion property alias timer: timer property alias arrow: arrow property alias asteroidCreation: asteroidCreation - property GCAudio audioEffects: activity.audioEffects + property GCSfx audioEffects: activity.audioEffects property double distLeft: Math.abs(spaceshipX / ApplicationInfo.ratio) property double distRight: Math.abs((background.width - spaceshipX) / ApplicationInfo.ratio) property double forceLeft: (Math.pow(scaleLeft, 2) / Math.pow(distLeft, 2)) * Math.pow(10, 6) property double forceRight: (Math.pow(scaleRight, 2) / Math.pow(distRight, 2)) * Math.pow(10, 6) // the center value for the spaceship property double spaceshipX property double spaceshipY: parent.height / 2 } onStart: Activity.start(items,message) onStop: Activity.stop() DialogHelp { id: dialogHelp onClose: home() } Bar { id: bar content: BarEnumContent { value: help | home | level | reload } onHelpClicked: { displayDialog(dialogHelp) } onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onHomeClicked: activity.home() onReloadClicked: Activity.initLevel() } Bonus { id: bonus Component.onCompleted: win.connect(Activity.nextLevel) } IntroMessage { id: message onIntroDone: { items.timer.start() items.asteroidCreation.start() items.shuttleMotion.restart() } intro: [ qsTr("Gravity is universal and Newton's law of universal gravitation extends gravity" +" beyond earth. This force of gravitational attraction is directly dependent" +" upon the masses of both objects and inversely proportional to" +" the square of the distance that separates their centers."), qsTr("Since the gravitational force is directly proportional to the mass of both interacting " +"objects, more massive objects will attract each other with a greater gravitational " +"force. So as the mass of either object increases, the force of gravitational " +"attraction between them also increases"), qsTr("But this force is inversely proportional to the square of the separation distance " +"between the two interacting objects, more separation distance will " +"result in weaker gravitational forces."), qsTr("Your goal is to let Tux's spaceship move by changing the mass " +"of its surrounding planets. Don't get too close to the planets " +"or you will crash on them. " +"The arrow indicates the direction of the force on your ship."), qsTr("Avoid the asteroid and join the space " +"shuttle to win.") ] z: 20 anchors { top: parent.top topMargin: 10 right: parent.right rightMargin: 5 left: parent.left leftMargin: 5 } } Planet { id: planetLeft x: 0 width: parent.width * 0.5 height: parent.height planetSource: Activity.url + "saturn.svg" planetWidth: 240 * ApplicationInfo.ratio isLeft: true } Planet { id: planetRight x: parent.width / 2 width: parent.width * 0.5 height: parent.height planetSource: Activity.url + "neptune.svg" planetWidth: 184 * ApplicationInfo.ratio isLeft: false } Image { id: spaceship source: Activity.url + "tux_spaceship.svg" sourceSize.width: 120 * ApplicationInfo.ratio x: items.spaceshipX - width / 2 y: items.spaceshipY - height / 2 } // line to show force magnitude and direction Image { id: arrow visible: !message.displayed && width > 6 && timer.running x: items.forceLeft < items.forceRight ? items.spaceshipX : items.spaceshipX - width y: spaceship.y - 80 z: 11 sourceSize.width: 120 * 10 * ApplicationInfo.ratio width: Math.min(background.width / 4, Math.abs(items.forceLeft - items.forceRight) * 6) height: 40 * ApplicationInfo.ratio source: Activity.url +"arrow.svg" rotation: items.forceLeft > items.forceRight ? 0 : 180 Behavior on rotation { NumberAnimation{ duration: 100 } } Behavior on width { NumberAnimation{ duration: 100 } } } Image { id: shuttle source: Activity.url + "space_shuttle.svg" sourceSize.width: 80 * ApplicationInfo.ratio z: 10 NumberAnimation { id: shuttleMotion target: shuttle property: "y" to: 0 - shuttle.height duration: 40000 / (Activity.currentLevel+1) } } Timer { id: timer interval: 16 running: false repeat: true onTriggered: { Activity.moveSpaceship() Activity.handleCollisionWithAsteroid() } } Timer { id: asteroidCreation running: false repeat: true interval: 10200 - (bar.level * 200) onTriggered: Activity.createAsteroid() } } } diff --git a/src/activities/leftright/Leftright.qml b/src/activities/leftright/Leftright.qml index 9e0837c3a..a8c735547 100644 --- a/src/activities/leftright/Leftright.qml +++ b/src/activities/leftright/Leftright.qml @@ -1,190 +1,190 @@ /* GCompris - Leftright.qml * * Copyright (C) 2014 Bruno Coudoin * * Authors: * Pascal Georges (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 "leftright.js" as Activity ActivityBase { id: activity onStart: focus = true; Keys.onLeftPressed: Activity.leftClickPressed() Keys.onRightPressed: Activity.rightClickPressed() pageComponent: Image { id: background source: "qrc:/gcompris/src/activities/leftright/resource/back.svg" sourceSize.width: Math.max(parent.width, parent.height) focus: true signal start signal stop fillMode: Image.PreserveAspectCrop QtObject { id: items property alias bar: bar property alias bonus: bonus - property GCAudio audioEffects: activity.audioEffects + property GCSfx audioEffects: activity.audioEffects property alias imageAnimOff: imageAnimOff property alias leftButton: leftButton property alias rightButton: rightButton property alias score: score } Component.onCompleted: { activity.start.connect(start) activity.stop.connect(stop) } onStart: { Activity.start(items) } onStop: { Activity.stop() } Item { id: topBorder height: background.height * 0.08 } Image { id: blackBoard anchors.horizontalCenter: parent.horizontalCenter anchors.top: topBorder.bottom fillMode: Image.PreserveAspectFit sourceSize.width: Math.min(background.width, (background.height - leftButton.height - bar.height) * 1.3) source: "qrc:/gcompris/src/activities/leftright/resource/blackboard.svg" Image { id: handImage anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter fillMode: Image.PreserveAspectFit scale: blackBoard.height / 300 * 0.8 opacity: 0 } Image { id: lightImage source: "qrc:/gcompris/src/activities/leftright/resource/light.svg" sourceSize.width: parent.width sourceSize.height: parent.height anchors.horizontalCenter: parent.horizontalCenter anchors.top: parent.top anchors.topMargin: 40 opacity: 0 } ParallelAnimation { id: imageAnimOff onRunningChanged: { if (!imageAnimOff.running) { handImage.source = Activity.getCurrentHandImage() handImage.rotation = Activity.getCurrentHandRotation() imageAnimOn.start() } } NumberAnimation { target: handImage property: "opacity" from: 1; to: 0 duration: 300 easing.type: Easing.InOutQuad } NumberAnimation { target: lightImage property: "opacity" from: 0.2; to: 0 duration: 300 easing.type: Easing.InOutQuad } } ParallelAnimation { id: imageAnimOn NumberAnimation { target: handImage property: "opacity" from: 0; to: 1.0 duration: 300 easing.type: Easing.InOutQuad } NumberAnimation { target: lightImage property: "opacity" from: 0; to: 0.2 duration: 300 easing.type: Easing.InOutQuad } } AnswerButton { id: leftButton width: blackBoard.width * 0.45 height: background.height * 0.15 anchors.left: blackBoard.left anchors.top: blackBoard.bottom anchors.margins: 10 textLabel: qsTr("Left hand") audioEffects: activity.audioEffects onCorrectlyPressed: Activity.leftClick(); } AnswerButton { id: rightButton width: blackBoard.width * 0.45 height: background.height * 0.15 anchors.right: blackBoard.right anchors.top: blackBoard.bottom anchors.margins: 10 audioEffects: activity.audioEffects textLabel: qsTr("Right hand") onCorrectlyPressed: Activity.rightClick(); } } DialogHelp { id: dialogHelpLeftRight onClose: home() } Bar { id: bar content: BarEnumContent { value: help | home | level } onHelpClicked: { displayDialog(dialogHelpLeftRight) } onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onHomeClicked: home() } Bonus { id: bonus } Score { id: score anchors.top: background.top anchors.bottom: undefined } } } diff --git a/src/activities/magic-hat-minus/Hat.qml b/src/activities/magic-hat-minus/Hat.qml index 2eff8b09e..95e677592 100644 --- a/src/activities/magic-hat-minus/Hat.qml +++ b/src/activities/magic-hat-minus/Hat.qml @@ -1,121 +1,121 @@ /* GCompris - Hat.qml * * Copyright (C) 2014 Thibaut ROMAIN * * Authors: * Thibaut ROMAIN * * 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 "../../core" import "magic-hat.js" as Activity Item { id: hatItem width: parent.width height: parent.height property alias state: hatImg.state property alias target: offStar property int starsSize - property GCAudio audioEffects + property GCSfx audioEffects function getTarget() { return offStar } Image { id: hatImg source: Activity.url + "hat.svg" sourceSize.width: hatItem.width / 3 fillMode: Image.PreserveAspectFit anchors.centerIn: parent state: "NormalPosition" transform: Rotation { id: rotate origin.x: 0 origin.y: hatImg.height axis.x: 0 axis.y: 0 axis.z: 1 Behavior on angle { animation: rotAnim } } states: [ State { name: "NormalPosition" PropertyChanges { target: rotate angle: 0 } }, State { name: "Rotated" PropertyChanges { target: rotate angle: -45 } }, State { name: "GuessNumber" PropertyChanges{ target: hatImg source: Activity.url + "hat-point.svg" } PropertyChanges { target: rotate angle: 0 } } ] } RotationAnimation { id: rotAnim direction: hatImg.state == "Rotated" ? RotationAnimation.Counterclockwise : RotationAnimation.Clockwise duration: 500 onRunningChanged: if(!rotAnim.running && hatImg.state == "Rotated") { Activity.moveStarsUnderHat() } } MouseArea { id: hatMouseArea anchors.fill:hatImg onClicked: { if(hatImg.state == "NormalPosition") hatImg.state = "Rotated" } } // The target for the moving stars Item { id: offStar height: hatItem.starsSize width: hatItem.starsSize y: hatImg.y + hatImg.paintedHeight - height anchors { horizontalCenter: parent.horizontalCenter } z: hatImg.z - 1 } } diff --git a/src/activities/magic-hat-minus/MagicHat.qml b/src/activities/magic-hat-minus/MagicHat.qml index 079c3b7cf..1805a7ef7 100644 --- a/src/activities/magic-hat-minus/MagicHat.qml +++ b/src/activities/magic-hat-minus/MagicHat.qml @@ -1,237 +1,237 @@ /* GCompris - MagicHat.qml * * Copyright (C) 2014 Thibaut ROMAIN * * 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 / 16) signal start signal stop property var starColors : ["1", "2", "3"] Component.onCompleted: { activity.start.connect(start) activity.stop.connect(stop) } onStart: Activity.start(items, mode) onStop: Activity.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 GCAudio audioEffects: activity.audioEffects + property GCSfx audioEffects: activity.audioEffects property alias bar: bar 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.right rightMargin: 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 } } } } 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() } 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/maze/Maze.qml b/src/activities/maze/Maze.qml index 33b7e16cc..1886d4ba7 100644 --- a/src/activities/maze/Maze.qml +++ b/src/activities/maze/Maze.qml @@ -1,349 +1,349 @@ /* GCompris - maze.qml * * Copyright (C) 2014 Stephane Mankowski * * Authors: * Bastiaan Verhoef (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 GCompris 1.0 import QtMultimedia 5.0 import "../../core" import "maze.js" as Activity ActivityBase { id: activity property bool relativeMode: false property bool invisibleMode: false onStart: focus = true onStop: { } Keys.onPressed: Activity.processPressedKey(event) pageComponent: Image { id: background source: Activity.url + "maze_bg.svg" sourceSize.width: parent.width anchors.fill: parent signal start signal stop Component.onCompleted: { 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 mazeRows: maze.rows property alias mazeColumns: maze.columns property alias mazeRepeater: mazeRepeater.model - property GCAudio audioEffects: activity.audioEffects + property GCSfx audioEffects: activity.audioEffects property alias message: message property int playerx: 0 property int playery: 0 property int playerr: 270 property int doory: 0 property int cellSize: Math.min((parent.height - 200) / mazeRows, (parent.width - 40) / mazeColumns) property int wallSize: Math.max(2, cellSize / 10) property bool wallVisible: true property bool fastMode: false } onStart: { Activity.start(items, relativeMode, invisibleMode) } onStop: { Activity.stop() } Rectangle { color: "#E3DEDB" anchors.fill: maze } Grid { id: maze anchors.top: parent.top anchors.topMargin: (parent.height - height) / 2 anchors.horizontalCenter: parent.horizontalCenter columns: 0 rows: 0 spacing: 0 Repeater { id: mazeRepeater Item { width: items.cellSize height: items.cellSize Rectangle { id: north width: items.cellSize + items.wallSize height: items.wallSize radius: height / 2 anchors.top: parent.top anchors.left: parent.left anchors.topMargin: -items.wallSize / 2 anchors.leftMargin: -items.wallSize / 2 color: "#B38B56" visible: modelData & 1 ? items.wallVisible : false z: 1 } Rectangle { id: south width: items.cellSize + items.wallSize height: items.wallSize radius: height / 2 anchors.bottom: parent.bottom anchors.left: parent.left anchors.bottomMargin: -items.wallSize / 2 anchors.leftMargin: -items.wallSize / 2 color: "#B38B56" visible: modelData & 4 ? items.wallVisible : false z: 1 } Rectangle { id: east width: items.wallSize height: items.cellSize + items.wallSize radius: width / 2 anchors.bottom: parent.bottom anchors.right: parent.right anchors.bottomMargin: -items.wallSize / 2 anchors.rightMargin: -items.wallSize / 2 color: "#B38B56" visible: modelData & 8 ? items.wallVisible : false z: 1 } Rectangle { id: west width: items.wallSize height: items.cellSize + items.wallSize radius: width / 2 anchors.bottom: parent.bottom anchors.left: parent.left anchors.bottomMargin: -items.wallSize / 2 anchors.leftMargin: -items.wallSize / 2 color: "#B38B56" visible: modelData & 2 ? items.wallVisible : false z: 1 } } } } MultiPointTouchArea { anchors.fill: parent touchPoints: [ TouchPoint { id: point1 } ] property real startX property real startY // Workaround to avoid having 2 times the onReleased event property bool started onPressed: { startX = point1.x startY = point1.y started = true } onReleased: { if(!started) return false var moveX = point1.x - startX var moveY = point1.y - startY // Find the direction with the most move if(Math.abs(moveX) * ApplicationInfo.ratio > 10 && Math.abs(moveX) > Math.abs(moveY)) { if(moveX > 10 * ApplicationInfo.ratio) Activity.clickRight() else if(moveX < -10 * ApplicationInfo.ratio) Activity.clickLeft() } else if(Math.abs(moveY) * ApplicationInfo.ratio > 10 && Math.abs(moveX) < Math.abs(moveY)) { if(moveY > 10 * ApplicationInfo.ratio) Activity.clickDown() else if(moveY < -10 * ApplicationInfo.ratio) Activity.clickUp() } else { // No move, just a tap or mouse click if(point1.x > player.x + player.width) Activity.clickRight() else if(point1.x < player.x) Activity.clickLeft() else if(point1.y < player.y) Activity.clickUp() else if(point1.y > player.y + player.height) Activity.clickDown() } started = false } } // Show an hint to show that can move by swiping anywhere Image { anchors { right: parent.right bottom: parent.bottom margins: 12 } source: "qrc:/gcompris/src/core/resource/arrows_move.svg" sourceSize.width: 140 opacity: bar.level == 1 && ApplicationInfo.isMobile ? 1 : 0 } Image { id: player source: Activity.url + "tux_top_south.svg" sourceSize.width: items.cellSize * 0.8 x: maze.x + items.cellSize * 0.05 + items.wallSize / 2 + items.playerx * items.cellSize y: maze.y + items.cellSize * 0.20 + items.wallSize / 2 + items.playery * items.cellSize z: 2 rotation: items.playerr Timer { id: timeAutoMove interval: 10 running: false repeat: false onTriggered: Activity.autoMove() } Behavior on x { SequentialAnimation { NumberAnimation { duration: 100 } ScriptAction { script: timeAutoMove.running = true } } } Behavior on y { SequentialAnimation { NumberAnimation { duration: 100 } ScriptAction { script: timeAutoMove.running = true } } } Behavior on rotation { PropertyAnimation { duration: 100 } } Image { id: shoes source: Activity.url + "tux_shoes_top_south.svg" sourceSize.width: items.cellSize * 0.8 anchors.centerIn: parent visible: items.fastMode } } Image { id: door source: Activity.url + "door.svg" width: items.cellSize * 0.6 height: items.cellSize * 0.8 y: maze.y + items.cellSize * 0.05 + items.wallSize / 2 + items.doory * items.cellSize z: 1 anchors.right: maze.right anchors.rightMargin: items.cellSize * 0.15 + items.wallSize / 2 } BarButton { id: fastmode source: Activity.url + "fast-mode-button.svg" sourceSize.width: 66 * bar.barZoom visible: !message.visible x: 10 * ApplicationInfo.ratio y: 10 * ApplicationInfo.ratio onClicked: items.fastMode = !items.fastMode } BarButton { id: switchMaze source: Activity.url + "maze-2d-bubble.svg" anchors { right: parent.right top: parent.top margins: 10 } sourceSize.width: 66 * bar.barZoom visible: invisibleMode onClicked: { items.wallVisible = !items.wallVisible message.visible = items.wallVisible } } GCText { id: message anchors { left: parent.left right: switchMaze.left top: parent.top margins: 10 } width: background.width - x - 20 fontSize: regularSize horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter visible: false wrapMode: Text.Wrap text: qsTr("Look at your position, then switch back to invisible mode to continue your moves") } 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/memory/CardItem.qml b/src/activities/memory/CardItem.qml index 2c268a82a..cf733097d 100644 --- a/src/activities/memory/CardItem.qml +++ b/src/activities/memory/CardItem.qml @@ -1,152 +1,152 @@ /* gcompris - CardItem.qml * * Copyright (C) 2014 JB BUTET * * Authors: * Bruno Coudoin (GTK+ version) * JB BUTET (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 "memory.js" as Activity Flipable { id: card property var pairData property bool isBack: true property bool isShown: false property bool isFound: false property bool tuxTurn property GCAudio audioVoices - property GCAudio audioEffects + property GCSfx audioEffects onIsFoundChanged: { opacity = 0 timer.start() } Timer { id: timer interval: 100 running: false repeat: false onTriggered: particles.burst(50) } ParticleSystemStarLoader { id: particles clip: false } Timer { id: animationTimer interval: 1500 running: false repeat: false onTriggered: selectionReady() } back: Image { source: card.pairData.emptyCard sourceSize.width: parent.width fillMode: Image.PreserveAspectFit anchors.centerIn: parent anchors.fill: parent Image { id: contentImage source: card.pairData.image width: parent.paintedWidth * 0.9 height: parent.paintedHeight * 0.9 sourceSize.width: contentImage.width sourceSize.height: contentImage.height anchors.centerIn: parent fillMode: Image.PreserveAspectFit } GCText { anchors.centerIn: parent fontSize: largeSize horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter color: "black" font.bold: true style: Text.Outline styleColor: "white" text: card.pairData.text } } // Warning front and back property are reversed. Could not find // a way to display back at start time without this trick front: Image { fillMode: Image.PreserveAspectFit source: card.pairData.back sourceSize.width: parent.width anchors.centerIn: parent anchors.fill: parent } transform: Rotation { id: rotation origin.x: card.width / 2 origin.y: card.height / 2 axis.x: 0; axis.y: 1; axis.z: 0 angle: 0 } transitions: Transition { NumberAnimation { target: rotation; property: "angle"; duration: 750 } } MouseArea { anchors.fill: parent enabled: card.isBack && !card.isFound && !card.tuxTurn && items.selectionCount < 2 onClicked: selected() } function selected() { card.isBack = false card.isShown = true items.selectionCount++ animationTimer.start() audioEffects.play(Activity.url + "card_flip.wav") } function selectionReady() { var pairs = Activity.addPlayQueue(card) var win = Activity.reverseCardsIfNeeded() if(tuxTurn && win || tuxTurn && !pairs) Activity.tuxPlay() if (card.pairData.sound) { audioVoices.play(card.pairData.sound) } } Behavior on opacity { NumberAnimation { duration: 1000 } } states : [ State { name: "front" PropertyChanges { target: rotation; angle: 180 } when: !card.isBack } ] } diff --git a/src/activities/memory/MemoryCommon.qml b/src/activities/memory/MemoryCommon.qml index 858b3d1d7..0852e2d4a 100644 --- a/src/activities/memory/MemoryCommon.qml +++ b/src/activities/memory/MemoryCommon.qml @@ -1,195 +1,195 @@ /* GCompris - MemoryCommon.qml * * Copyright (C) 2014 JB BUTET * * Authors: * Bruno Coudoin (GTK+ version) * JB BUTET (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 "memory.js" as Activity ActivityBase { id: activity focus: true property string backgroundImg property var dataset property bool withTux: false property string additionnalPath onStart: focus = true onStop: {} pageComponent: Image { id: background source: activity.backgroundImg sourceSize.width: Math.max(parent.width, parent.height) fillMode: Image.PreserveAspectCrop focus: true signal start signal stop property alias items: items Component.onCompleted: { activity.start.connect(start) activity.stop.connect(stop) } QtObject { id: items property alias bar: bar property alias bonus: bonus - property GCAudio audioEffects: activity.audioEffects + property GCSfx audioEffects: activity.audioEffects property bool withTux: activity.withTux property bool tuxTurn: false property var playQueue property int selectionCount property int tuxScore: tuxScore.text property int playerScore: playerScore.text property var dataset: activity.dataset property alias containerModel: containerModel property alias cardRepeater: cardRepeater property alias grid: grid property int columns property int rows } onStart: Activity.start(items) onStop: Activity.stop() ListModel { id: containerModel } Grid { id: grid spacing: 5 * ApplicationInfo.ratio columns: items.columns rows: items.rows anchors { left: background.left right: background.rigth top: background.top margins: 10 * ApplicationInfo.ratio } Repeater { id: cardRepeater model: containerModel delegate: CardItem { pairData: pairData_ tuxTurn: background.items.tuxTurn width: (background.width - (grid.columns + 1) * grid.spacing) / grid.columns height: (background.height - (grid.rows + 1) * grid.spacing) / (grid.rows + 0.5) audioVoices: activity.audioVoices audioEffects: activity.audioEffects } } add: Transition { PathAnimation { path: Path { PathCurve { x: background.width / 3} PathCurve { y: background.height / 3} PathCurve {} } easing.type: Easing.InOutQuad duration: 2000 } } } DialogHelp { id: dialogHelp onClose: home() } Bar { id: bar content: BarEnumContent { value: help | home | level } onHelpClicked: { displayDialog(dialogHelp) } onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onHomeClicked: home() } Image { id: player source: 'qrc:/gcompris/src/activities/memory/resource/children.svg' anchors { bottom: bar.bottom right: parent.right rightMargin: 2 * ApplicationInfo.ratio } width: height * 0.83 height: bar.height * 1.2 GCText { id: playerScore anchors.centerIn: parent anchors.verticalCenterOffset: parent.height / 6 color: "black" font.bold: true fontSize: largeSize style: Text.Outline styleColor: "white" text: items.playerScore } } Image { id: tux visible: activity.withTux source: 'qrc:/gcompris/src/activities/memory/resource/tux-teacher.svg' anchors { bottom: bar.bottom right: player.left rightMargin: 2 * ApplicationInfo.ratio } width: height * 0.83 height: bar.height * 1.2 GCText { id: tuxScore anchors.centerIn: parent anchors.verticalCenterOffset: parent.height / 6 color: "black" font.bold: true fontSize: largeSize style: Text.Outline styleColor: "white" text: items.tuxScore } } Bonus { id: bonus interval: 2000 Component.onCompleted: win.connect(Activity.nextLevel) } } } diff --git a/src/activities/money/MoneyCore.qml b/src/activities/money/MoneyCore.qml index 00fa20b43..48db1ac6a 100644 --- a/src/activities/money/MoneyCore.qml +++ b/src/activities/money/MoneyCore.qml @@ -1,310 +1,310 @@ /* 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 "../../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: { 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 GCAudio audioEffects: activity.audioEffects + property GCSfx audioEffects: activity.audioEffects property alias answerModel: answerModel property alias pocketModel: pocketModel property alias store: store property alias instructions: instructions property alias tux: tux property alias tuxMoney: tuxMoney property alias bar: bar property alias bonus: bonus } onStart: { Activity.start(items, dataset) } onStop: { Activity.stop() } Column { id: column spacing: 10 x: parent.width * 0.05 y: parent.height * 0.05 width: parent.width * 0.9 property int nbColumns: 5 property int nbLines: 2 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 === Rectangle { id: answerArea height: (column.itemHeight + 10) * column.nbLines width: column.width color: "#55333333" border.color: "black" border.width: 2 radius: 5 Flow { anchors.topMargin: 4 anchors.bottomMargin: 4 anchors.leftMargin: 10 anchors.rightMargin: 10 anchors.fill: parent spacing: 10 add: Transition { NumberAnimation { properties: "x" from: parent.width * 0.05 easing.type: Easing.InOutQuad } } move: Transition { NumberAnimation { properties: "x,y" easing.type: Easing.InOutQuad } } Repeater { id: answer model: ListModel { id: answerModel } Image { source: Activity.url + img sourceSize.height: column.itemHeight height: column.itemHeight MultiPointTouchArea { anchors.fill: parent onReleased: 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 + column.spacing : 0 property int itemStoreWidth: Math.min((column.width - storeAreaFlow.spacing * nbStoreColumns) / nbStoreColumns, (parent.height - answerArea.height - pocketArea.height - bar.height) * 0.8) - tempSpace property int itemStoreHeight: itemStoreWidth Rectangle { id: storeArea height: (column.itemStoreHeight + 10) width: column.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: column.itemStoreHeight sourceSize.width: column.itemStoreHeight Repeater { id: tuxMoney Image { source: Activity.url + modelData.img sourceSize.height: column.itemStoreHeight * 0.4 x: tux.x + index * 20 y: tux.y + tux.height / 2 + index * 20 } } } Repeater { id: store Image { source: Activity.url + modelData.img sourceSize.height: column.itemStoreHeight sourceSize.width: column.itemStoreHeight GCText { text: modelData.price fontSize: 16 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: column.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 + column.spacing : 0 GCText { id: instructions horizontalAlignment: Text.AlignHCenter width: column.width height: column.height / 6 wrapMode: Text.WordWrap fontSizeMode: Text.Fit } } // === The Pocket Area === Rectangle { id: pocketArea height: (column.itemHeight + 10) * column.nbLines width: column.width color: "#661111AA" border.color: "black" border.width: 2 radius: 5 Flow { anchors.topMargin: 4 anchors.bottomMargin: 4 anchors.leftMargin: 10 anchors.rightMargin: 10 anchors.fill: parent spacing: 10 add: Transition { NumberAnimation { properties: "x" from: parent.width * 0.05 easing.type: Easing.InOutQuad } } move: Transition { NumberAnimation { properties: "x,y" easing.type: Easing.InOutQuad } } Repeater { id: pocket model: ListModel { id: pocketModel } Image { source: Activity.url + img sourceSize.height: column.itemHeight height: column.itemHeight MultiPointTouchArea { anchors.fill: parent onReleased: Activity.pay(index) } } } } } } 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/mosaic/Mosaic.qml b/src/activities/mosaic/Mosaic.qml index 5d5dd15ac..7cbe86ced 100644 --- a/src/activities/mosaic/Mosaic.qml +++ b/src/activities/mosaic/Mosaic.qml @@ -1,293 +1,293 @@ /* GCompris - mosaic.qml * * Copyright (C) 2014 Bruno Coudoin * * Authors: * Clement 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 "../../core" import "mosaic.js" as Activity ActivityBase { id: activity onStart: focus = true onStop: {} pageComponent: Image { id: background source: Activity.url + "background.svg" sourceSize.width: Math.max(parent.width, parent.height) fillMode: Image.PreserveAspectCrop anchors.fill: parent signal start signal stop Component.onCompleted: { 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 GCAudio audioEffects: activity.audioEffects + property GCSfx audioEffects: activity.audioEffects property alias question: question property alias answer: answer property alias selector: selector property alias nbItems: column.nbItems property alias background: background property alias bar: bar property alias bonus: bonus property string selectedItem } onStart: { Activity.start(items) } onStop: { Activity.stop() } Column { id: column spacing: 10 x: parent.width * 0.05 y: parent.height * 0.05 width: parent.width * 0.9 property int nbItems: 24 property bool horizontal: background.width > background.height property int nbColumns: Activity.questionLayout[nbItems][0] property int nbLines: Activity.questionLayout[nbItems][1] property int itemWidth: horizontal ? Math.min(width / 2 / nbColumns - 10 - 10 / nbColumns / 2, parent.height / 2 / nbLines - 10 - 10 / nbLines / 2) : Math.min(width / nbColumns - 10 - 10 / nbColumns / 2, parent.height * 0.25 / nbLines - 10 - 10 / nbLines / 2) property int itemHeight: itemWidth property int nbSelectorColumns: horizontal ? Activity.selectorLayout[nbItems][0] : Activity.selectorLayout[nbItems][0] / 2 property int nbSelectorLines: horizontal ? Activity.selectorLayout[nbItems][1] : Activity.selectorLayout[nbItems][1] * 2 Grid { id: row spacing: 10 columns: column.horizontal ? 2 : 1 // === The Question Area === Rectangle { height: (column.itemHeight + 10) * column.nbLines width: column.horizontal ? column.width / 2 : column.width + 10 color: "#55333333" border.color: "black" border.width: 2 radius: 5 Grid { columns: column.nbColumns anchors.topMargin: 4 anchors.bottomMargin: 4 anchors.leftMargin: 10 anchors.rightMargin: 10 anchors.fill: parent spacing: 10 Repeater { id: question Image { source: Activity.url + modelData sourceSize.height: column.itemHeight width: column.itemWidth height: column.itemHeight } } } } // === The Answer Area === Rectangle { height: (column.itemHeight + 10) * column.nbLines width: column.horizontal ? column.width / 2 : column.width + 10 color: "#55333333" border.color: "black" border.width: 2 radius: 5 Grid { columns: column.nbColumns anchors.topMargin: 4 anchors.bottomMargin: 4 anchors.leftMargin: 10 anchors.rightMargin: 10 anchors.fill: parent spacing: 10 Repeater { id: answer Image { id: imageAnswerId source: Activity.url + modelData sourceSize.height: column.itemHeight width: column.itemWidth height: column.itemHeight MouseArea { anchors.fill: parent onClicked: { Activity.answerSelected(index) } } } } } } } // === The Selector === Rectangle { height: (column.itemWidth + 10) * column.nbSelectorLines width: column.width + 10 color: "#661111AA" border.color: "black" border.width: 2 radius: 5 Grid { columns: column.nbSelectorColumns anchors.topMargin: 4 anchors.bottomMargin: 4 anchors.leftMargin: 10 anchors.rightMargin: 10 anchors.fill: parent spacing: 10 + 20 / 12.0 Repeater { id: selector Image { id: imageId source: Activity.url + modelData sourceSize.height: column.itemHeight width: column.itemWidth height: column.itemHeight z: iAmSelected ? 10 : 1 property bool iAmSelected: items.selectedItem === modelData property string basename: modelData states: [ State { name: "notclicked" when: !imageId.iAmSelected && !mouseArea.containsMouse PropertyChanges { target: imageId scale: 0.8 } }, State { name: "clicked" when: mouseArea.pressed PropertyChanges { target: imageId scale: 0.7 } }, State { name: "hover" when: mouseArea.containsMouse PropertyChanges { target: imageId scale: 1.1 } }, State { name: "selected" when: imageId.iAmSelected PropertyChanges { target: imageId scale: 1 } } ] SequentialAnimation { id: anim running: imageId.iAmSelected loops: Animation.Infinite alwaysRunToEnd: true NumberAnimation { target: imageId property: "rotation" from: 0; to: 10 duration: 200 easing.type: Easing.OutQuad } NumberAnimation { target: imageId property: "rotation" from: 10; to: -10 duration: 400 easing.type: Easing.InOutQuad } NumberAnimation { target: imageId property: "rotation" from: -10; to: 0 duration: 200 easing.type: Easing.InQuad } } Behavior on scale { NumberAnimation { duration: 70 } } MouseArea { id: mouseArea anchors.fill: imageId hoverEnabled: true onClicked: { items.audioEffects.play("qrc:/gcompris/src/core/resource/sounds/scroll.wav") items.selectedItem = modelData } } } } } } } 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/number_sequence/NumberSequence.qml b/src/activities/number_sequence/NumberSequence.qml index 387981e2f..8b586964d 100644 --- a/src/activities/number_sequence/NumberSequence.qml +++ b/src/activities/number_sequence/NumberSequence.qml @@ -1,264 +1,264 @@ /* GCompris - NumberSequence.qml * * Copyright (C) 2014 Emmanuel Charruau * * Authors: * Olivier Ponchaut (GTK+ version) * Emmanuel Charruau (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 "." import "number_sequence.js" as Activity import "number_sequence_dataset.js" as Dataset ActivityBase { id: activity property string mode: "number_sequence" property var dataset: Dataset property real pointImageOpacity: 1.0 property string url: "qrc:/gcompris/src/activities/number_sequence/resource/" onStart: focus = true onStop: {} pageComponent: Image { id: background anchors.fill: parent source: "qrc:/gcompris/src/activities/checkers/resource/background-wood.svg" sourceSize.width: parent.witdh sourceSize.height: parent.height signal start signal stop Component.onCompleted: { 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 GCAudio audioEffects: activity.audioEffects + property GCSfx audioEffects: activity.audioEffects property GCAudio audioVoices: activity.audioVoices property alias pointImageRepeater: pointImageRepeater property alias segmentsRepeater: segmentsRepeater property alias imageBack: imageBack property alias imageBack2: imageBack2 property int pointIndexToClick } onStart: { Activity.start(items,mode,dataset,url) } onStop: { Activity.stop() } Image { id: imageBack anchors.top: parent.top anchors.horizontalCenter: background.horizontalCenter width: Math.min((background.height - bar.height * 1.5), background.width) height: imageBack.width sourceSize.width: imageBack.width sourceSize.height: imageBack.height } Image { id: imageBack2 anchors.centerIn: imageBack width: imageBack.width height: imageBack.height sourceSize.width: imageBack2.width sourceSize.height: imageBack2.height } Repeater { id: segmentsRepeater Rectangle { id: line opacity: 0 color: "#373737" transformOrigin: Item.TopLeft x: imageBack.x + modelData[0] * imageBack.width / 520 y: modelData[1] * imageBack.height / 520 property double x2: imageBack.x + modelData[2] * imageBack.width / 520 property double y2: modelData[3] * imageBack.height / 520 width: Math.sqrt(Math.pow(x - x2, 2) + Math.pow(y- y2, 2)) height: 3 * ApplicationInfo.ratio rotation: (Math.atan((y2 - y)/(x2-x)) * 180 / Math.PI) + (((y2-y) < 0 && (x2-x) < 0) * 180) + (((y2-y) >= 0 && (x2-x) < 0) * 180) } } Item { id: playArea anchors.fill: parent Repeater { id: pointImageRepeater Image { id: pointImage source: Activity.url + (highlight ? (pointImageOpacity ? "bluepoint.svg" : "bluepointHighlight.svg") : markedAsPointInternal ? "blackpoint.svg" : "greenpoint.svg") sourceSize.height: bar.height / 4 //to change the size of dots x: imageBack.x + modelData[0] * imageBack.width / 520 - sourceSize.height/2 y: modelData[1] * imageBack.height / 520 - sourceSize.height/2 z: items.pointIndexToClick == index ? 1000 : index // only hide last point for clickanddraw and number_sequence // as the last point is also the first point visible: (mode=="clickanddraw" || mode=="number_sequence") && index == pointImageRepeater.count - 1 && items.pointIndexToClick == 0 ? false : true function drawSegment() { Activity.drawSegment(index) } property bool highlight: false // draw a point instead of hiding the clicked node if it is // the first point of the current line to draw // (helpful to know where the line starts) property bool markedAsPointInternal: markedAsPoint && items.pointIndexToClick == index+1 property bool markedAsPoint: false scale: markedAsPointInternal ? 0.2 : 1 opacity: index >= items.pointIndexToClick || markedAsPointInternal ? 1 : 0 GCText { id: pointNumberText opacity: pointImageOpacity text: index + 1 anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter fontSize: 11 font.weight: Font.DemiBold style: Text.Outline styleColor: "black" color: "white" } ParallelAnimation { id: anim running: pointImageOpacity == 0 && items.pointIndexToClick == index loops: Animation.Infinite SequentialAnimation { NumberAnimation { target: pointImage property: "rotation" from: -150; to: 150 duration: 3000 easing.type: Easing.InOutQuad } NumberAnimation { target: pointImage property: "rotation" from: 150; to: -150 duration: 3000 easing.type: Easing.InOutQuad } } SequentialAnimation { NumberAnimation { target: pointImage property: "scale" from: 1; to: 1.5 duration: 1500 easing.type: Easing.InOutQuad } NumberAnimation { target: pointImage property: "scale" from: 1.5; to: 1 duration: 1500 easing.type: Easing.InOutQuad } NumberAnimation { target: pointImage property: "scale" from: 1; to: 1.5 duration: 1500 easing.type: Easing.InOutQuad } NumberAnimation { target: pointImage property: "scale" from: 1.5; to: 1 duration: 1500 easing.type: Easing.InOutQuad } } } } } } MultiPointTouchArea { anchors.fill: parent minimumTouchPoints: 1 maximumTouchPoints: 1 z: 100 function checkPoints(touchPoints) { for(var i in touchPoints) { var touch = touchPoints[i] for(var p = 0; p < pointImageRepeater.count; p++) { var part = pointImageRepeater.itemAt(p) // Could not make it work with the item.contains() api if(touch.x > part.x && touch.x < part.x + part.width && touch.y > part.y && touch.y < part.y + part.height) { part.drawSegment() } } } } onPressed: { checkPoints(touchPoints) items.audioEffects.play('qrc:/gcompris/src/activities/drawnletters/resource/buttonclick.wav') } onTouchUpdated: { checkPoints(touchPoints) } } 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/photo_hunter/Observe.qml b/src/activities/photo_hunter/Observe.qml index 9e62132fd..265e9446f 100644 --- a/src/activities/photo_hunter/Observe.qml +++ b/src/activities/photo_hunter/Observe.qml @@ -1,192 +1,192 @@ /* GCompris - Observe.qml * * Copyright (C) 2016 Stefan Toncu * * Authors: * (GTK+ version) * Stefan Toncu (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 "photo_hunter.js" as Activity Image { id: card sourceSize.width: background.startedHelp ? background.width : background.vert ? background.width : (background.width - 30) / 2 sourceSize.height: background.startedHelp ? background.height - background.barHeight * 1.5 - frame.problemTextHeight - slider.height : background.vert ? (background.height - background.barHeight - 40 - frame.problemTextHeight) / 2 : background.height - background.barHeight - 30 - frame.problemTextHeight - property GCAudio audioEffects: activity.audioEffects + property GCSfx audioEffects: activity.audioEffects property alias repeater: repeater property alias circleRepeater: circleRepeater property int good: 0 property bool show: false Behavior on anchors.horizontalCenterOffset { enabled: !background.vert NumberAnimation { duration: 1000 easing.type: Easing.InOutQuad } } Behavior on anchors.verticalCenterOffset { enabled: background.vert NumberAnimation { duration: 1000 easing.type: Easing.InOutQuad } } Image { id: wrong source: Activity.url + "wrong.svg" width: 70 height: 70 opacity: 0 } NumberAnimation { id: wrongAnim target: wrong property: "opacity" from: 0 to: 1 duration: 400 } NumberAnimation { id: wrongAnim2 target: wrong property: "opacity" from: 1 to: 0 duration: 400 } MouseArea { id: big anchors.fill: parent enabled: !background.startedHelp onClicked: { audioEffects.play('qrc:/gcompris/src/core/resource/sounds/brick.wav') wrongAnim.start() wrong.x = mouseX - wrong.width/2 wrong.y = mouseY - wrong.height/2 wrongAnim2.start() } } Repeater { id: repeater model: items.model Image { id: photo property alias particleLoader: particleLoader property alias differenceAnimation: differenceAnimation property double widthScale: Activity.dataset[Activity.currentLevel].coordinates[index].w property double heightScale: Activity.dataset[Activity.currentLevel].coordinates[index].h sourceSize.width: Activity.dataset[Activity.currentLevel].coordinates[index].r * 200 sourceSize.height: Activity.dataset[Activity.currentLevel].coordinates[index].r * 200 width: card.width / 10 * widthScale height: card.height / 10 * heightScale source: Activity.url + "photo" + (Activity.currentLevel + 1) + "_" + (index + 1) + ".svg" opacity: card.show ? 1 : 0 x: modelData[0] * card.width / 1200 y: modelData[1] * card.height / 1700 NumberAnimation { id: differenceAnimation target: photo property: "opacity" from: 0 to: 1 duration: 500 } // Create a particle only for the strawberry Loader { id: particleLoader anchors.fill: parent active: true sourceComponent: particle } Component { id: particle ParticleSystemStarLoader { id: particles clip: false } } MouseArea { id: mouseArea anchors.centerIn: parent width: parent.width * 3 height: parent.height * 3 enabled: !background.startedHelp onClicked: { Activity.photoClicked(card,index) audioEffects.play('qrc:/gcompris/src/core/resource/sounds/bleep.wav') } } } } Repeater { id: circleRepeater model: card.repeater.model Rectangle { id: circle color: "transparent" radius: width * 0.5 border.color: card.show ? "blue" : "red" border.width: 6 opacity: 0 x: itemAt.x - width / 6 y: itemAt.y - height / 6 width: itemAt.width * 1.5 height: itemAt.height * 1.5 property alias scaleAnim: scaleAnim property var itemAt: card.repeater.itemAt(index) ? card.repeater.itemAt(index) : card NumberAnimation { id: scaleAnim target: circle property: "scale" from: 0 to: circle.scale duration: 700 } } } } diff --git a/src/activities/planegame/Planegame.qml b/src/activities/planegame/Planegame.qml index e22f6b16e..0f2e4179e 100644 --- a/src/activities/planegame/Planegame.qml +++ b/src/activities/planegame/Planegame.qml @@ -1,147 +1,147 @@ /* gcompris - Planegame.qml Copyright (C) 2014 Johnny Jazeix 2003, 2014: Bruno Coudoin: initial version 2014: Johnny Jazeix: Qt 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 "planegame.js" as Activity ActivityBase { id: activity focus: true onStart: { focus = true; } onStop: { } Keys.onPressed: Activity.processPressedKey(event) Keys.onReleased: Activity.processReleasedKey(event) property var dataset property int oldWidth: width onWidthChanged: { // Reposition helico and clouds, same for height Activity.repositionObjectsOnWidthChanged(width / oldWidth) oldWidth = width } property int oldHeight: height onHeightChanged: { // Reposition helico and clouds, same for height Activity.repositionObjectsOnHeightChanged(height / oldHeight) oldHeight = height } pageComponent: Image { id: background anchors.fill: parent signal start signal stop source: Activity.url + "../algorithm/resource/desert_scene.svg" sourceSize.width: parent.width Component.onCompleted: { activity.start.connect(start) activity.stop.connect(stop) } QtObject { id: items property alias background: background property alias bar: bar property alias bonus: bonus property alias score: score property alias plane: plane property GCAudio audioVoices: activity.audioVoices - property GCAudio audioEffects: activity.audioEffects + property GCSfx audioEffects: activity.audioEffects property alias movePlaneTimer: movePlaneTimer property alias cloudCreation: cloudCreation } onStart: Activity.start(items, dataset) onStop: Activity.stop(); MultiPointTouchArea { anchors.fill: parent touchPoints: [ TouchPoint { id: point1 } ] onReleased: { plane.x = point1.x - plane.width / 2 plane.y = point1.y - plane.height / 2 } } 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) } Score { id: score visible: false fontSize: 24 } property int movePlaneTimerCounter: 0 Timer { id: movePlaneTimer running: false repeat: true onTriggered: { plane.state = "play" interval = 50 if(movePlaneTimerCounter++ % 3 == 0) { /* Do not call this too often or plane commands are too hard */ Activity.handleCollisionsWithCloud(); } Activity.computeVelocity(); Activity.planeMove(); } } Timer { id: cloudCreation running: false repeat: true interval: 10200 - (bar.level * 200) onTriggered: Activity.createCloud() } Plane { id: plane background: background } } } diff --git a/src/activities/renewable_energy/RenewableEnergy.qml b/src/activities/renewable_energy/RenewableEnergy.qml index 903a4484a..42130a321 100644 --- a/src/activities/renewable_energy/RenewableEnergy.qml +++ b/src/activities/renewable_energy/RenewableEnergy.qml @@ -1,574 +1,574 @@ /* GCompris - renewable_energy.qml * * Copyright (C) 2015 Sagar Chand Agarwal * * Authors: * Bruno Coudoin (GTK+ version) * Sagar Chand Agarwal (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" ActivityBase { id: activity onStart: focus = true onStop: {} property string url: "qrc:/gcompris/src/activities/renewable_energy/resource/" property var barAtStart property int oldWidth: width onWidthChanged: { oldWidth: width } property int oldHeight: height onHeightChanged: { oldHeight: height } pageComponent: Item { id: background anchors.fill: parent signal start signal stop Component.onCompleted: { 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 GCAudio audioEffects: activity.audioEffects + property GCSfx audioEffects: activity.audioEffects property int currentLevel property int numberOfLevel: 3 property bool sunIsUp property color consumeColor: '#ffff8100' property color produceColor: '#ffffec00' property bool hasWon: false } onStart: { barAtStart = ApplicationSettings.isBarHidden; ApplicationSettings.isBarHidden = true; } onStop: { ApplicationSettings.isBarHidden = barAtStart; initLevel() } function initLevel() { residentSmallSwitch.on = false residentBigSwitch.on = false tuxSwitch.on = false stepDown.started = false hydro.item.stop() if(wind.item) wind.item.stop() if(solar.item) solar.item.stop() } function nextLevel() { if(items.numberOfLevel <= ++items.currentLevel) { // Stay on the last level items.currentLevel = items.numberOfLevel - 1 } } function previousLevel() { if(--items.currentLevel < 0) { items.currentLevel = items.numberOfLevel - 1 } } function checkForNextLevel() { switch(items.currentLevel) { case 0: if(tuxSwitch.on) win() break case 1: if(tuxSwitch.on && residentSmallSwitch.on) win() break case 2: if(!items.hasWon && tuxSwitch.on && residentSmallSwitch.on && residentBigSwitch.on) { items.hasWon = true win() } break } } Loader { id: hydro anchors.fill: parent source: "Hydro.qml" } Loader { id: wind anchors.fill: parent source: items.currentLevel > 0 ? "Wind.qml" : "" } Loader { id: solar anchors.fill: parent source: items.currentLevel > 1 ? "Solar.qml" : "" } IntroMessage { id: message opacity: items.currentLevel == 0 ? 1 : start() z: 100 anchors { top: parent.top topMargin: 10 right: parent.right rightMargin: 5 left: parent.left leftMargin: 5 } onIntroDone: { hydro.item.start() message.opacity = 0 } intro: [ qsTr("Tux has come back from fishing on his boat. " + "Bring the electrical system back up so he can have light in his home."), qsTr("Click on different active elements : sun, cloud, dam, solar array, " + "wind farm and transformers, in order to reactivate the entire electrical system."), qsTr("When the system is back up and Tux is in his home, push the light button for him. " + "To win you must switch on all the consumers while all the producers are up."), qsTr("Learn about an electrical system based on renewable energy. Enjoy.") ] Behavior on opacity { PropertyAnimation { duration: 200 } } } Rectangle { id: check opacity: 0 width: 400 * ApplicationInfo.ratio height: 200 * ApplicationInfo.ratio anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter z: 100 border.width: 2 radius: 5 color: "#d0f0f0" property bool shown: false GCText { id: warning anchors.fill: parent horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter text: qsTr( "It is not possible to consume more electricity " + "than what is produced. This is a key limitation in the " + "distribution of electricity, with minor exceptions, " + "electrical energy cannot be stored, and therefore it " + "must be generated as it is needed. A sophisticated " + "system of control is therefore required to ensure electric " + "generation very closely matches the demand. If supply and demand " + "are not in balance, generation plants and transmission equipment " + "can shut down which, in the worst cases, can lead to a major " + "regional blackout.") fontSizeMode: Text.Fit minimumPointSize: 10 wrapMode: Text.WordWrap fontSize: smallSize } Behavior on opacity { NumberAnimation { duration: 200 } } MouseArea { anchors.fill: parent enabled: check.opacity > 0 onClicked: check.opacity = 0 } } Image { id: stepDown source: activity.url + (started ? "transformer.svg" : "transformer_off.svg") sourceSize.width: parent.width * 0.06 height: parent.height * 0.09 anchors { top: parent.top left: parent.left topMargin: parent.height * 0.41 leftMargin: parent.width * 0.72 } MouseArea { anchors.centerIn: parent // Size the area for a touch screen width: 70 * ApplicationInfo.ratio height: width onClicked: { onClicked: parent.started = !parent.started } } property bool started: false property int powerIn: started ? (hydro.item.power + (wind.item ? wind.item.power : 0) + (solar.item ? solar.item.power : 0)) : 0 property int powerOut: started ? (tux.powerConsumed + residentSmallLights.powerConsumed + residentBigLights.powerConsumed) : 0 onPowerInChanged: checkPower() // Check powerOut does not exceed powerIn. Cut some consumers in case. function checkPower() { if(powerOut > powerIn && residentBigSwitch.on) { residentBigSwitch.on = false if(!check.shown && powerIn) check.opacity = 1 } if(powerOut > powerIn && residentSmallSwitch.on) { residentSmallSwitch.on = false if(!check.shown && powerIn) check.opacity = 1 } if(powerOut > powerIn && tuxSwitch.on) { tuxSwitch.on = false if(!check.shown && powerIn) check.opacity = 1 } } } Image { source: activity.url + "right.svg" sourceSize.width: stepDown.width / 2 sourceSize.height: stepDown.height / 2 anchors { right: stepDown.left bottom: stepDown.bottom bottomMargin: parent.height * 0.03 } Rectangle { id: produceMeter width: pow.width * 1.1 height: pow.height * 1.1 border.color: "black" radius: 5 color: items.produceColor anchors { top: parent.top right: parent.left } GCText { id: pow anchors.centerIn: parent fontSize: smallSize * 0.5 text: stepDown.powerIn.toString() + "W" } } } Image { source: activity.url + "down.svg" sourceSize.width: stepDown.width / 2 sourceSize.height: stepDown.height / 2 anchors { left: stepDown.left top: stepDown.top topMargin: stepDown.height * 0.8 leftMargin: parent.width * 0.05 } Rectangle { id: consumeMeter width: stepdown_info.width * 1.1 height: stepdown_info.height * 1.1 border.color: "black" radius: 5 color: items.consumeColor anchors { top: parent.top topMargin: parent.height * 0.1 left: parent.right } GCText { id: stepdown_info anchors.centerIn: parent fontSize: smallSize * 0.5 text: stepDown.powerOut.toString() + "W" } } } Image { id: stepDownWire source: activity.url + "hydroelectric/stepdown.svg" sourceSize.width: parent.width anchors.fill: parent visible: power > 0 property int power: stepDown.powerIn } Image { id: residentSmallSwitch visible: items.currentLevel > 0 source: activity.url + (on ? "on.svg" : "off.svg") sourceSize.height: parent.height * 0.03 sourceSize.width: parent.height * 0.03 anchors { left: parent.left top: parent.top leftMargin: parent.width * 0.55 topMargin: parent.height * 0.65 } property bool on: false MouseArea { id: small_area visible: parent.visible anchors.centerIn: parent // Size the area for a touch screen width: 70 * ApplicationInfo.ratio height: width onClicked: { if(stepDown.powerIn - stepDown.powerOut >= residentSmallLights.power) parent.on = !parent.on else parent.on = false checkForNextLevel() } } } Image { id: tuxHouseOn source: activity.url + "tux_house_on.svg" sourceSize.width: parent.width sourceSize.height: parent.height anchors.fill: parent visible: tuxSwitch.on } Image { id: residentSmallLights source: activity.url + "resident_smallon.svg" sourceSize.width: parent.width sourceSize.height: parent.height anchors.fill: parent visible: items.currentLevel > 0 && powerConsumed property int power: 1000 property int powerConsumed: on ? power : 0 property bool on: residentSmallSwitch.on } Rectangle { id: smallConsumeRect width: small_consume.width * 1.1 height: small_consume.height * 1.1 border.color: "black" radius: 5 color: items.consumeColor anchors { top: residentSmallSwitch.bottom left:residentSmallSwitch.left } GCText { id: small_consume anchors.centerIn: parent text: residentSmallLights.powerConsumed.toString() + "W" fontSize: smallSize * 0.5 } visible: items.currentLevel > 0 } Image { id: residentBigSwitch visible: items.currentLevel > 1 source: activity.url + (on ? "on.svg" : "off.svg") sourceSize.height: parent.height * 0.03 sourceSize.width: parent.height * 0.03 anchors { left: parent.left top: parent.top leftMargin: parent.width * 0.60 topMargin: parent.height * 0.65 } property bool on: false MouseArea { id: big_area visible: parent.visible anchors.centerIn: parent // Size the area for a touch screen width: 70 * ApplicationInfo.ratio height: width onClicked: { if(stepDown.powerIn - stepDown.powerOut >= residentBigLights.power) parent.on = !parent.on else parent.on = false checkForNextLevel() } } } Image { id: residentBigLights source: activity.url + "resident_bigon.svg" sourceSize.width: parent.width sourceSize.height: parent.height anchors.fill: parent visible: items.currentLevel > 0 && powerConsumed property int power: 2000 property int powerConsumed: on ? power : 0 property bool on: residentBigSwitch.on } Rectangle { id: bigConsumeRect width: bigConsume.width * 1.1 height: bigConsume.height * 1.1 border.color: "black" radius : 5 color: items.consumeColor anchors { top: residentBigSwitch.bottom left: residentBigSwitch.left } GCText { id: bigConsume anchors.centerIn: parent text: residentBigLights.powerConsumed.toString() + "W" fontSize: smallSize * 0.5 } visible: items.currentLevel > 1 } // Tux is visible when tuxboat animation stops // It's light can be activated after stepdown is on Image { id: tux source: activity.url + (on ? "lightson.svg" : "lightsoff.svg") sourceSize.height: parent.height * 0.2 sourceSize.width: parent.width * 0.15 anchors { bottom: parent.bottom right: parent.right bottomMargin: parent.height * 0.3 rightMargin: parent.width * 0.02 } visible: false property int power: 100 property int powerConsumed: on ? power : 0 property bool on: tuxSwitch.on Image { id: tuxSwitch source: activity.url + (on ? "on.svg" : "off.svg") sourceSize.height: parent.height*0.20 sourceSize.width: parent.height*0.20 property bool on: false anchors { right: tux.right top: tux.top rightMargin: tux.width * 0.20 topMargin: tux.height * 0.30 } MouseArea { id: off_area anchors.centerIn: parent // Size the area for a touch screen width: 70 * ApplicationInfo.ratio height: width onClicked: { if(stepDown.powerIn - stepDown.powerOut >= tux.power) parent.on = !parent.on else parent.on = false checkForNextLevel() } } } Rectangle { id: tuxMeter width: tuxConsume.width * 1.1 height: tuxConsume.height * 1.1 border.color: "black" radius : 5 color: items.consumeColor anchors { bottom: tuxSwitch.top left: tuxSwitch.left } GCText { id: tuxConsume anchors.centerIn: parent fontSize: smallSize * 0.5 text: tux.powerConsumed.toString() + "W" } visible: tux.visible } } function win() { items.bonus.good("flower") } DialogHelp { id: dialogHelp onClose: home() } Bar { id: bar content: BarEnumContent { value: help | home | level | reload } onHelpClicked: displayDialog(dialogHelp) onPreviousLevelClicked: previousLevel() onNextLevelClicked: nextLevel() onHomeClicked: activity.home() onReloadClicked: initLevel() level: items.currentLevel + 1 } Bonus { id: bonus Component.onCompleted: win.connect(nextLevel) } } } diff --git a/src/activities/reversecount/ChooseDiceBar.qml b/src/activities/reversecount/ChooseDiceBar.qml index 66e8ea38e..52fa82786 100644 --- a/src/activities/reversecount/ChooseDiceBar.qml +++ b/src/activities/reversecount/ChooseDiceBar.qml @@ -1,65 +1,65 @@ /* GCompris - ChooseDiceBar.qml * * Copyright (C) 2014 Emmanuel Charruau * * Authors: * Bruno Coudoin (GTK+ version) * Emmanuel Charruau (Qt Quick port) * Bruno Coudoin (Major 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 Item { id: chooseDiceBar z: 1000 property alias value1: domino.value1 property alias value2: domino.value2 property alias valueMax: domino.valueMax - property GCAudio audioEffects + property GCSfx audioEffects Row { id: barRow spacing: 8 BarButton { id: ok source: "qrc:/gcompris/src/core/resource/bar_ok.svg"; sourceSize.width: 75 * ApplicationInfo.ratio visible: true anchors { right: undefined rightMargin: undefined top: undefined topMargin: undefined } onClicked: Activity.moveTux() } Domino { id: domino height: ok.height width: height * 2 isClickable: true audioEffects: activity.audioEffects } } } diff --git a/src/activities/reversecount/Reversecount.qml b/src/activities/reversecount/Reversecount.qml index e6cf29fc6..57259ecbd 100644 --- a/src/activities/reversecount/Reversecount.qml +++ b/src/activities/reversecount/Reversecount.qml @@ -1,232 +1,232 @@ /* GCompris - ReverseCount.qml * * Copyright (C) 2014 Emmanuel Charruau * * Authors: * Bruno Coudoin (GTK+ version) * Emmanuel Charruau (Qt Quick port) * Bruno Coudoin (Major 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: "#ff00a4b0" signal start signal stop Component.onCompleted: { 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 GCAudio audioEffects: activity.audioEffects + property GCSfx audioEffects: activity.audioEffects property alias background: background property alias backgroundImg: backgroundImg property alias bar: bar property alias bonus: bonus property alias chooseDiceBar: chooseDiceBar property alias tux: tux property alias fishToReach: fishToReach property int clockPosition: 4 } onStart: { Activity.start(items) } onStop: { Activity.stop() } Keys.onEnterPressed: Activity.moveTux() Keys.onReturnPressed: Activity.moveTux() onWidthChanged: { if(Activity.fishIndex > 0) { // set x fishToReach.x = Activity.iceBlocksLayout[Activity.fishIndex % Activity.iceBlocksLayout.length][0] * background.width / 5 + (background.width / 5 - tux.width) / 2 // set y fishToReach.y = Activity.iceBlocksLayout[Activity.fishIndex % Activity.iceBlocksLayout.length][1] * (background.height - background.height/5) / 5 + (background.height / 5 - tux.height) / 2 // Move Tux Activity.moveTuxToIceBlock() } } onHeightChanged: { if(Activity.fishIndex > 0) { // set x fishToReach.x = Activity.iceBlocksLayout[Activity.fishIndex % Activity.iceBlocksLayout.length][0] * background.width / 5 + (background.width / 5 - tux.width) / 2 // set y fishToReach.y = Activity.iceBlocksLayout[Activity.fishIndex % Activity.iceBlocksLayout.length][1] * (background.height - background.height/5) / 5 + (background.height / 5 - tux.height) / 2 // Move Tux Activity.moveTuxToIceBlock() } } Image { id: backgroundImg source: Activity.url + Activity.backgrounds[0] sourceSize.height: parent.height * 0.5 anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter } // === The ice blocks === Repeater { model: Activity.iceBlocksLayout Image { x: modelData[0] * background.width / 5 y: modelData[1] * (background.height- background.height/5) / 5 width: background.width / 5 height: background.height / 5 source: Activity.url + "iceblock.svg" } } Tux { id: tux sourceSize.width: Math.min(background.width / 6, background.height / 6) z: 11 } Image { id: fishToReach sourceSize.width: Math.min(background.width / 6, background.height / 6) z: 10 property string nextSource property int nextX property int nextY function showParticles() { particles.burst(40) } ParticleSystemStarLoader { id: particles clip: false } onOpacityChanged: { if(opacity == 0) { source = ""; source = nextSource; } } onSourceChanged: { if(source != "") { 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 } onHelpClicked: { displayDialog(dialogHelp) } onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onHomeClicked: activity.home() } 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: "qrc:/gcompris/src/activities/reversecount/resource/" + "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 } } } } ChooseDiceBar { id: chooseDiceBar x: background.width / 5 + 20 y: (background.height - background.height/5) * 3 / 5 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/scalesboard/MasseArea.qml b/src/activities/scalesboard/MasseArea.qml index 221bbff9f..57f5f50db 100644 --- a/src/activities/scalesboard/MasseArea.qml +++ b/src/activities/scalesboard/MasseArea.qml @@ -1,285 +1,285 @@ /* GCompris - MasseArea.qml * * Copyright (C) 2014 Bruno Coudoin * * Authors: * 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 QtGraphicalEffects 1.0 import "../../core" import "scalesboard.js" as Activity Rectangle { id: masseArea height: itemHeight color: dropArea.containsDrag ? "#33333333" : "#00000000" border.width: 2 border.color: dropArea.containsDrag ? "#33666666" : "#00000000" property bool dropEnabled: true property bool dropEnabledForThisLevel: true property int nbColumns property int itemWidth: (width - masseFlow.spacing * nbColumns) / nbColumns property int itemHeight: itemWidth * 1.2 property Item masseAreaCenter property Item masseAreaLeft property Item masseAreaRight property alias masseModel: masseModel property alias dropArea: dropArea property int weight: 0 - property GCAudio audioEffects + property GCSfx audioEffects function init() { weight = 0 masseModel.clear() } function removeWeight(value) { weight -= value } function removeMasse(masseArea, index, weight) { masseArea.removeWeight(weight) masseArea.masseModel.remove(index) } /* weight is the absolute weight * text is the text being displayed on the masseAreaCenter */ function addMasse(img, weight, text, index, dragEnabled) { masseModel.append( { img: img, weight: weight, text: text, masseIndex: index, opacity: 1.0, dragEnabled: dragEnabled } ) masseArea.weight += weight } function setAllZonesDropEnabled(enabled) { masseAreaCenter.dropEnabled = enabled masseAreaLeft.dropEnabled = enabled masseAreaRight.dropEnabled = enabled } function showMasseInMasseArea(index) { masseAreaCenter.masseModel.get(index).opacity = 1.0 } function hideMasseInMasseArea(index) { masseAreaCenter.masseModel.get(index).opacity = 0.0 } ListModel { id: masseModel function contains(masseIndex) { for(var i = 0; i < masseModel.count; i++) { if(masseModel.get(i).masseIndex == masseIndex) { return masseModel.get(i).opacity == 1 } } return false; } } DropArea { id: dropArea anchors { left: parent.left right: parent.right verticalCenter: parent.verticalCenter } height: parent.height * 2 enabled: dropEnabledForThisLevel && dropEnabled } Flow { id: masseFlow anchors.topMargin: 4 anchors.bottomMargin: 4 anchors.fill: parent spacing: 10 flow: Flow.TopToBottom add: Transition { NumberAnimation { properties: "x" from: parent.width * 0.05 easing.type: Easing.InOutQuad } } move: Transition { NumberAnimation { properties: "x,y" easing.type: Easing.InOutQuad } } Repeater { id: answer model: masseModel Image { source: Activity.url + img sourceSize.height: masseArea.itemHeight height: masseArea.itemHeight opacity: model.opacity property string img: model.img property int masseIndex: model.masseIndex property int modelIndex: index property int weight: model.weight property string text: model.text property int masseOriginX property int masseOriginY property int originX property int originY property Item currentMasseArea: masseArea Drag.active: dragArea.drag.active Drag.hotSpot.x: width / 2 Drag.hotSpot.y: height / 2 function initDrag() { originX = x originY = y if(currentMasseArea == masseArea) { masseOriginX = x masseOriginY = y } z = 111 } function replace() { x = originX y = originY } function replaceInMasse() { x = masseOriginX y = masseOriginY } onOpacityChanged: opacity == 1.0 ? currentMasseArea = masseAreaCenter : null MouseArea { id: dragArea anchors.fill: parent drag.target: parent enabled: model.dragEnabled onPressed: { if(masseModel.contains(parent.masseIndex)) { parent.initDrag() } else { setAllZonesDropEnabled(false) } } function dropOnPlate(masseArea) { parent.Drag.cancel() if(parent.currentMasseArea == masseAreaCenter) { masseArea.hideMasseInMasseArea(parent.masseIndex) parent.replaceInMasse() } masseArea.addMasse(parent.img, parent.weight, parent.text, parent.masseIndex, /* dragEnabled */ true) if(parent.currentMasseArea != masseAreaCenter) { removeMasse(parent.currentMasseArea, parent.modelIndex, parent.weight) } parent.currentMasseArea = masseArea } onReleased: { setAllZonesDropEnabled(true) if(masseArea.audioEffects) masseArea.audioEffects.play(Activity.url + 'metal_hit.wav') if(masseAreaLeft.dropArea.containsDrag && parent.currentMasseArea != masseAreaLeft) { dropOnPlate(masseAreaLeft) } else if (masseAreaRight.dropArea.containsDrag && parent.currentMasseArea != masseAreaRight) { dropOnPlate(masseAreaRight) } else if (masseAreaCenter.dropArea.containsDrag && parent.dropArea != masseAreaCenter) { parent.Drag.cancel() masseAreaCenter.showMasseInMasseArea(parent.masseIndex) parent.replaceInMasse() if(parent.currentMasseArea != masseAreaCenter) { removeMasse(parent.currentMasseArea, parent.modelIndex, parent.weight) } } else { parent.Drag.cancel() parent.replace() } } } GCText { id: text anchors.fill: parent text: model.text.replace(" ", "\n") color: "white" fontSizeMode: Text.Fit minimumPointSize: 10 fontSize: largeSize font.bold : true style: Text.Outline styleColor: "black" horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } DropShadow { anchors.fill: text cached: false horizontalOffset: 3 verticalOffset: 3 radius: 8.0 samples: 16 color: "#80000000" source: text } } } } Behavior on y { NumberAnimation { duration: 500 easing.type: Easing.InOutQuad } } } diff --git a/src/activities/sudoku/Sudoku.qml b/src/activities/sudoku/Sudoku.qml index ee5626e9e..3611eb463 100644 --- a/src/activities/sudoku/Sudoku.qml +++ b/src/activities/sudoku/Sudoku.qml @@ -1,210 +1,210 @@ /* gcompris - Sudoku.qml Copyright (C) 2014 Johnny Jazeix 2003, 2014: Bruno Coudoin: initial version 2014: Johnny Jazeix: Qt 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 "sudoku.js" as Activity import "." ActivityBase { id: activity focus: true onStart: {focus=true} onStop: {} pageComponent: Image { id: background anchors.fill: parent source: Activity.url + "background.svg" sourceSize.width: Math.max(parent.width, parent.height) fillMode: Image.PreserveAspectCrop signal start signal stop Component.onCompleted: { activity.start.connect(start) activity.stop.connect(stop) focus = true } property int nbRegions QtObject { id: items property alias background: background property alias bar: bar property alias bonus: bonus property alias score: score - property GCAudio audioEffects: activity.audioEffects + property GCSfx audioEffects: activity.audioEffects property alias availablePiecesModel: availablePieces property alias columns: sudoColumn.columns property alias rows: sudoColumn.rows property alias sudokuModel: sudokuModel } onStart: Activity.start(items) onStop: { Activity.stop() } DialogHelp { id: dialogHelp onClose: home() } Bar { id: bar content: BarEnumContent { value: help | home | level | reload } onHelpClicked: { displayDialog(dialogHelp) } onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onReloadClicked: Activity.initLevel() onHomeClicked: activity.home() } Bonus { id: bonus z: 1002 Component.onCompleted: win.connect(Activity.incrementLevel) } Score { id: score z: 1003 anchors.bottom: background.bottom anchors.right: background.right } Keys.onPressed: { Activity.onKeyPressed(event); } SudokuListWidget { id: availablePieces audioEffects: activity.audioEffects } ListModel { id: sudokuModel } Grid { z: 100 anchors { left: availablePieces.right top: parent.top topMargin: 2 * ApplicationInfo.ratio } id: sudoColumn width: Math.min(background.width - availablePieces.width - availablePieces.anchors.leftMargin, background.height - 2 * bar.height) height: width Repeater { id: repeater model: sudokuModel delegate: blueSquare Component { id: blueSquare SudokuCase { x: (index%sudoColumn.columns)*width y: Math.floor(index/sudoColumn.columns)*height width: parent != null ? parent.width / sudoColumn.columns : 1 height: parent != null ? parent.height/ sudoColumn.columns : 1 gridIndex: index isInitial: initial text: textValue state: mState } } } } MouseArea { id: dynamic anchors.fill: sudoColumn hoverEnabled: true property int previousHoveredCase: -1 onPositionChanged: { var x = Math.floor(sudoColumn.rows * mouseX / (sudoColumn.width+1)); var y = Math.floor(sudoColumn.columns * mouseY / (sudoColumn.height+1)); var id = x + y * sudoColumn.rows; // Only color if we can modify the case if(sudokuModel.get(id).mState == "default") sudokuModel.get(id).mState = "hovered"; // Restore previous case if different from the new one if(previousHoveredCase != id) { if(previousHoveredCase != -1 && sudokuModel.get(previousHoveredCase).mState == "hovered") sudokuModel.get(previousHoveredCase).mState = "default" previousHoveredCase = id } } onExited: { if(previousHoveredCase != -1 && sudokuModel.get(previousHoveredCase).mState == "hovered") sudokuModel.get(previousHoveredCase).mState = "default" previousHoveredCase = -1 } onClicked: { var x = Math.floor(sudoColumn.rows * mouseX / sudoColumn.width); var y = Math.floor(sudoColumn.columns * mouseY / sudoColumn.height); Activity.clickOn(x, y) } } Grid { z: 1001 id: regionGrid columns: rows rows: nbRegions visible: nbRegions > 1 anchors.fill: sudoColumn Repeater { id: regionRepeater model: nbRegions*nbRegions Rectangle { z: 1001 color: "transparent" border.color: "orange" border.width: 4 x: index / nbRegions * sudoColumn.width y: (index % nbRegions) * sudoColumn.width width: nbRegions * sudoColumn.width / sudoColumn.columns height: nbRegions * sudoColumn.height/ sudoColumn.columns } } } } } diff --git a/src/activities/sudoku/SudokuListWidget.qml b/src/activities/sudoku/SudokuListWidget.qml index b4adf178f..6ebe747e2 100644 --- a/src/activities/sudoku/SudokuListWidget.qml +++ b/src/activities/sudoku/SudokuListWidget.qml @@ -1,155 +1,155 @@ /* gcompris - SudokuListWidget.qml Copyright (C) 2014 Johnny Jazeix 2003, 2014: Bruno Coudoin: initial version 2014: Johnny Jazeix: Qt 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 "sudoku.js" as Activity import "../../core" Item { id: listWidget width: view.width height: view.height anchors { left: parent.left leftMargin: 2 * ApplicationInfo.ratio top: parent.top topMargin: 2 * ApplicationInfo.ratio } property alias model: mymodel; property alias view: view; - property GCAudio audioEffects + property GCSfx audioEffects ListModel { id: mymodel } ListView { id: view width: background.width / 5 height: background.height - 2 * bar.height interactive: false spacing: 10 model: mymodel delegate: contactsDelegate property int iconSize: Math.min((height - mymodel.count * spacing) / mymodel.count, view.width * 0.95) Component { id: contactsDelegate Item { width: view.iconSize height: view.iconSize Image { id: icon anchors.centerIn: parent sourceSize.height: view.iconSize source: model.imgName === undefined ? "" : Activity.url + model.imgName + extension z: iAmSelected ? 10 : 1 property bool iAmSelected: view.currentIndex == index states: [ State { name: "notclicked" when: !icon.iAmSelected && !mouseArea.containsMouse PropertyChanges { target: icon scale: 0.8 } }, State { name: "clicked" when: mouseArea.pressed PropertyChanges { target: icon scale: 0.7 } }, State { name: "hover" when: mouseArea.containsMouse PropertyChanges { target: icon scale: 1.1 } }, State { name: "selected" when: icon.iAmSelected PropertyChanges { target: icon scale: 1 } } ] SequentialAnimation { id: anim running: icon.iAmSelected loops: Animation.Infinite alwaysRunToEnd: true NumberAnimation { target: icon property: "rotation" from: 0; to: 10 duration: 200 easing.type: Easing.OutQuad } NumberAnimation { target: icon property: "rotation" from: 10; to: -10 duration: 400 easing.type: Easing.InOutQuad } NumberAnimation { target: icon property: "rotation" from: -10; to: 0 duration: 200 easing.type: Easing.InQuad } } Behavior on scale { NumberAnimation { duration: 70 } } MouseArea { id: mouseArea anchors.fill: icon hoverEnabled: true onClicked: { listWidget.audioEffects.play('qrc:/gcompris/src/core/resource/sounds/scroll.wav') view.currentIndex = index } } } } } } } diff --git a/src/activities/traffic/Car.qml b/src/activities/traffic/Car.qml index ac554b1e9..91b91a89d 100644 --- a/src/activities/traffic/Car.qml +++ b/src/activities/traffic/Car.qml @@ -1,184 +1,184 @@ /* GCompris - Car.qml * * Copyright (C) 2014 Holger Kaelberer * * Authors: * Bruno Coudoin (GTK+ Mostly full rewrite) * 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 QtGraphicalEffects 1.0 import "../../core" import "traffic.js" as Activity Item { id: car property int xPos property int yPos property int size property bool goal: false property bool isHorizontal: false property alias color: carRect.color property alias source: carImage.source property real blockSize: ((parent.width-3) / 6) property var xBounds: undefined property var yBounds: undefined property string mode property bool isMoving: false Component.onCompleted: { mode = parent.mode connection.target = parent } // Connect the jamGrid.mode to car.mode to automatically change the wrapped object Connections { id: connection onModeChanged: { car.mode = parent.mode; } } x: (mode == "COLOR" || car.isHorizontal) ? (xPos * blockSize) : ((xPos+1) * blockSize) y: (mode == "COLOR" || car.isHorizontal) ? (yPos * blockSize) : ((yPos) * blockSize) // track effective coordinates (needed for transformed image): property real effX: car.xPos * car.blockSize property real effY: car.yPos * car.blockSize property real effWidth: (mode == "COLOR" || car.isHorizontal) ? car.width : car.height property real effHeight: (mode == "COLOR" || car.isHorizontal) ? car.height : car.width - property GCAudio audioEffects + property GCSfx audioEffects width: (mode == "IMAGE" || isHorizontal) ? (size * blockSize) : blockSize height: (mode == "IMAGE" || isHorizontal) ? blockSize : (size * blockSize) Item { id: carWrapper anchors.fill: parent width: parent.width height: parent.height Rectangle { id: carRect visible: (mode == "COLOR") z: 11 anchors.fill: parent width: parent.width height: parent.height border.width: 1 border.color: "white" MultiPointTouchArea { id: rectTouch anchors.fill: parent touchPoints: [ TouchPoint { id: point1 } ] property real startX; property real startY; onPressed: { if (!Activity.isMoving) { Activity.isMoving = true; car.isMoving = true; car.audioEffects.play(Activity.baseUrl + "car.wav") rectTouch.startX = point1.x; rectTouch.startY = point1.y; } } onUpdated: { if (car.isMoving && !Activity.haveWon) { var deltaX = point1.x - startX; var deltaY = point1.y - startY; Activity.updateCarPosition(car, car.x + deltaX, car.y + deltaY); } } onReleased: { if (car.isMoving) { car.isMoving = false; Activity.isMoving = false; if (!Activity.haveWon) Activity.snapCarToGrid(car); } } } } Image { id: carImage visible: (mode == "IMAGE") fillMode: Image.PreserveAspectFit anchors.fill: parent sourceSize.width: parent.width sourceSize.height: parent.height rotation: car.isHorizontal ? 0 : 90 transformOrigin: Item.TopLeft MultiPointTouchArea { id: imageTouch anchors.fill: parent touchPoints: [ TouchPoint { id: imagePoint } ] property real startX; property real startY; onPressed: { if (!Activity.isMoving) { Activity.isMoving = true; car.isMoving = true; car.audioEffects.play(Activity.baseUrl + "car.wav") imageTouch.startX = imagePoint.x; imageTouch.startY = imagePoint.y; } } onUpdated: { if (car.isMoving && !Activity.haveWon) { var deltaX = imagePoint.x - startX; var deltaY = imagePoint.y - startY; if (!car.isHorizontal) { var w = deltaX; deltaX = deltaY; deltaY = w; } Activity.updateCarPosition(car, car.effX + deltaX, car.effY + deltaY); } } onReleased: { if (car.isMoving) { Activity.isMoving = false; car.isMoving = false; if (!Activity.haveWon) Activity.snapCarToGrid(car); } } } } } // note: the following leads to delayed dragging, therefore deactivated: //Behavior on x { PropertyAnimation { duration: 50 } } //Behavior on y { PropertyAnimation { duration: 50 } } } diff --git a/src/activities/traffic/Traffic.qml b/src/activities/traffic/Traffic.qml index 12b86b099..bd38a8f29 100644 --- a/src/activities/traffic/Traffic.qml +++ b/src/activities/traffic/Traffic.qml @@ -1,192 +1,192 @@ /* GCompris - Traffic.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 "traffic.js" as Activity ActivityBase { id: activity onStart: focus = true onStop: {} pageComponent: Image { id: background source: "qrc:/gcompris/src/activities/traffic/resource/traffic_bg.svg" sourceSize.width: Math.max(parent.width, parent.height) fillMode: Image.PreserveAspectCrop signal start signal stop property string mode: "IMAGE" // allow to choose between "COLOR" and "IMAGE" // mode, candidate for a config dialog Component.onCompleted: { dialogActivityConfig.getInitialConfiguration() activity.start.connect(start) activity.stop.connect(stop) } QtObject { id: items property Item main: activity.main - property GCAudio audioEffects: activity.audioEffects + property GCSfx audioEffects: activity.audioEffects property alias background: background property alias bar: bar property alias bonus: bonus property alias score: score property alias jamBox: jamBox property alias jamGrid: jamGrid } onStart: { Activity.start(items, mode) } onStop: { Activity.stop() } Image { id: jamBox source: "qrc:/gcompris/src/activities/traffic/resource/traffic_box.svg" anchors.centerIn: parent sourceSize.width: Math.min(background.width * 0.85, background.height * 0.85) fillMode: Image.PreserveAspectFit property double scaleFactor: background.width / background.sourceSize.width Grid { id: jamGrid anchors.centerIn: parent width: parent.width - 86 * jamBox.scaleFactor * ApplicationInfo.ratio height: parent.height - 86 * jamBox.scaleFactor * ApplicationInfo.ratio columns: 6 rows: 6 spacing: 0 // Add an alias to mode so it can be used on Car items property alias mode: background.mode Repeater { id: gridRepeater model: jamGrid.columns * jamGrid.rows delegate: Rectangle { id: gridDelegate height: jamGrid.height/ jamGrid.rows width: height border.width: 1 border.color: "white" color: "#444444" } } } } DialogHelp { id: dialogHelp onClose: home() } DialogActivityConfig { id: dialogActivityConfig currentActivity: activity content: Component { Item { property alias modeBox: modeBox property var availableModes: [ { "text": qsTr("Colors"), "value": "COLOR" }, { "text": qsTr("Images"), "value": "IMAGE" } ] Flow { id: flow spacing: 5 width: dialogActivityConfig.width GCComboBox { id: modeBox model: availableModes background: dialogActivityConfig label: qsTr("Select your mode") } } } } onClose: home() onLoadData: { if(dataToSave && dataToSave["mode"]) { mode = dataToSave["mode"]; Activity.mode = dataToSave["mode"]; } } onSaveData: { mode = dialogActivityConfig.configItem.availableModes[dialogActivityConfig.configItem.modeBox.currentIndex].value; dataToSave = {"mode": mode} Activity.mode = mode; } function setDefaultValues() { for(var i = 0 ; i < dialogActivityConfig.configItem.availableModes.length ; i ++) { if(dialogActivityConfig.configItem.availableModes[i].value === mode) { dialogActivityConfig.configItem.modeBox.currentIndex = i; break; } } } } Bar { id: bar content: BarEnumContent { value: help | home | level | reload | config } onHelpClicked: { displayDialog(dialogHelp) } onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onHomeClicked: activity.home() onReloadClicked: Activity.initLevel() onConfigClicked: { dialogActivityConfig.active = true // Set default values dialogActivityConfig.setDefaultValues(); displayDialog(dialogActivityConfig) } } Bonus { id: bonus Component.onCompleted: win.connect(Activity.nextSubLevel) } Score { id: score anchors.top: parent.top anchors.topMargin: 10 * ApplicationInfo.ratio anchors.right: parent.right anchors.rightMargin: 10 * ApplicationInfo.ratio anchors.bottom: undefined } } } diff --git a/src/activities/watercycle/Watercycle.qml b/src/activities/watercycle/Watercycle.qml index a0bd14ae8..b662c5c60 100644 --- a/src/activities/watercycle/Watercycle.qml +++ b/src/activities/watercycle/Watercycle.qml @@ -1,802 +1,802 @@ /* GCompris - watercycle.qml * * Copyright (C) 2015 Sagar Chand Agarwal * * Authors: * Bruno Coudoin (GTK+ version) * Sagar Chand Agarwal (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 "." ActivityBase { id: activity onStart: focus = true onStop: {} property string url: "qrc:/gcompris/src/activities/watercycle/resource/" property var barAtStart pageComponent: Item { id: background anchors.fill: parent signal start signal stop Component.onCompleted: { activity.start.connect(start) activity.stop.connect(stop) } onStart: { barAtStart = ApplicationSettings.isBarHidden; ApplicationSettings.isBarHidden = true; shower.hide() river.level = 0 } onStop: { ApplicationSettings.isBarHidden = barAtStart; } QtObject { id: items property var dataset: { "none": "", "start": qsTr("Sun is the main component of water cycle. Click on the sun to start the water cycle."), "sun": qsTr("As the sun rises, the water of the sea starts heating and evaporates."), "cloud": qsTr(" Water vapor condenses to form cloud and when clouds become heavy, it rains. Click on the cloud."), "rain": qsTr("Rain causes rivers to swell up and this water is transported to us via motor pumps through water-tower." + " Click on the motor pump to supply water to residents."), "tower": qsTr("See the tower filled with water. Activate the sewage treatment station by clicking on it."), "shower": qsTr("Great, click on the shower, as Tux arrives home."), "done": qsTr("Fantastic, you have completed water cycle. You can continue playing.") } property bool cycleDone: false - property GCAudio audioEffects: activity.audioEffects + property GCSfx audioEffects: activity.audioEffects } IntroMessage { id: message anchors { top: parent.top topMargin: 10 right: parent.right rightMargin: 5 left: parent.left leftMargin: 5 } z: 100 onIntroDone: { anim.running = true info.visible = true sun_area.enabled = true } intro: [ qsTr("The water cycle (also known as the hydrologic cycle) is the journey water takes" +" as it circulates from the land to the sky and back again." +" The sun's heat provides energy to evaporate water from water bodies like oceans."), qsTr("Plants also lose water to the air through transpiration. The water vapor eventually, " +"cools forming tiny droplets in clouds. When the clouds meet cool air over land, " +"precipitation is triggered and fall down as rain.") , qsTr("Some of the water is trapped between rock or clay layers, called groundwater. " +"But most of the water flows as runoff, eventually returning to the seas via rivers."), qsTr("Your goal is to complete water cycle before Tux reaches home. " +"Click on the different components which make up the water cycle. " +"First click on sun, then cloud, water pumping station near the river, " +"sewage treatment, and at last regulate the switch to provide water to Tux's shower.") ] } Image { id: sky anchors.top: parent.top sourceSize.width: parent.width source: activity.url + "sky.svg" height: (background.height - landscape.paintedHeight) / 2 + landscape.paintedHeight * 0.3 z: 1 } Image { id: sea anchors { left: parent.left bottom: parent.bottom } sourceSize.width: parent.width source: activity.url + "sea.svg" height: (background.height - landscape.paintedHeight) / 2 + landscape.paintedHeight * 0.7 z:3 } Image { id: landscape anchors.fill: parent sourceSize.width: parent.width source: activity.url + "landscape.svg" z: 6 } Image { id: tuxboat opacity: 1 source: activity.url + "boat.svg" sourceSize.width: parent.width*0.15 sourceSize.height: parent.height*0.15 anchors{ bottom: parent.bottom bottomMargin: 15 } x:0 z:30 Behavior on opacity { PropertyAnimation { easing.type: Easing.InOutQuad; duration: 200 } } NumberAnimation on x { id: anim running: false to: parent.width - tuxboat.width duration: 15000 easing.type: Easing.InOutSine onRunningChanged: { if(!anim.running) { items.audioEffects.play('qrc:/gcompris/src/activities/watercycle/resource/harbor2.wav') tuxboat.opacity = 0 boatparked.opacity = 1 shower.stop() if(!sun.hasRun) info.setText('start') } else { items.audioEffects.play('qrc:/gcompris/src/activities/watercycle/resource/harbor1.wav') } } } } Image { id: boatparked source: activity.url + "boat_parked.svg" sourceSize.width: parent.width*0.15 sourceSize.height: parent.height*0.15 opacity: 0 anchors { right: parent.right bottom: parent.bottom bottomMargin: 20 } z: 29 Behavior on opacity { PropertyAnimation { easing.type: Easing.InOutQuad; duration: 200 } } } Image { id: sun source: activity.url + "sun.svg" sourceSize.width: parent.width * 0.05 anchors { left: parent.left top: parent.top leftMargin: parent.width*0.05 topMargin: parent.height * 0.28 } z: 2 property bool hasRun: false MouseArea { id: sun_area anchors.fill: sun onClicked: { if(cloud.opacity == 0) sun.up() } } Behavior on anchors.topMargin { PropertyAnimation { easing.type: Easing.InOutQuad; duration: 5000 } } function up() { items.audioEffects.play('qrc:/gcompris/src/core/resource/sounds/bleep.wav') info.setText('sun') sun.hasRun = true sun.anchors.topMargin = parent.height * 0.05 vapor.up() } function down() { sun.anchors.topMargin = parent.height * 0.28 } } Image { id: vapor opacity: 0 state: "vapor" source: activity.url + "vapor.svg" sourceSize.width: parent.width*0.05 anchors { left: sun.left } y: background.height * 0.28 z: 10 SequentialAnimation { id: vaporAnim loops: 2 NumberAnimation { target: vapor property: "opacity" duration: 200 from: 0 to: 1 } NumberAnimation { target: vapor property: "y" duration: 5000 from: background.height * 0.28 to: background.height * 0.1 } NumberAnimation { target: vapor property: "opacity" duration: 200 from: 1 to: 0 } NumberAnimation { target: vapor property: "y" duration: 0 to: background.height * 0.28 } onRunningChanged: { if(!running) info.setText('cloud') } } function up() { vaporAnim.start() cloud.up() } function down() { } } Image { id: cloud opacity: 0 source: activity.url + "cloud.svg" sourceSize.width: parent.width * 0.20 fillMode: Image.PreserveAspectFit width: 0 anchors { top: parent.top topMargin: parent.height * 0.05 } x: parent.width * 0.05 z: 11 MouseArea { id: cloud_area anchors.fill: cloud onClicked: { sun.down() rain.up() } } ParallelAnimation { id: cloudanimOn running: false PropertyAnimation { target: cloud property: 'opacity' easing.type: Easing.InOutQuad duration: 5000 from: 0 to: 1 } PropertyAnimation { target: cloud property: 'width' easing.type: Easing.InOutQuad duration: 15000 from: 0 to: cloud.sourceSize.width } PropertyAnimation { target: cloud property: 'x' easing.type: Easing.InOutQuad duration: 15000 from: background.width * 0.05 to: background.width * 0.4 } } SequentialAnimation { id: cloudanimOff running: false PropertyAnimation { target: cloud property: 'opacity' easing.type: Easing.InOutQuad duration: 3000 from: 1 to: 0 } PropertyAnimation { target: cloud property: 'width' easing.type: Easing.InOutQuad duration: 0 to: 0 } PropertyAnimation { target: cloud property: 'x' easing.type: Easing.InOutQuad duration: 0 to: background.width * 0.05 } } function up() { cloudanimOn.start() } function down() { opacity = 0 width = 0 x = parent.width * 0.05 } } Image { id: rain source: activity.url + "rain.svg" sourceSize.height: cloud.height * 2 opacity: 0 anchors { top: cloud.bottom } x: cloud.x z: 10 Behavior on opacity { PropertyAnimation { easing.type: Easing.InOutQuad; duration: 300 } } SequentialAnimation{ id: rainAnim running: false loops: 10 NumberAnimation { target: rain property: "scale" duration: 500 to: 0.95 } NumberAnimation { target: rain property: "scale" duration: 500 to: 1 } onRunningChanged: { if(!running) { rain.down() cloud.down() } } } function up() { items.audioEffects.play('qrc:/gcompris/src/core/resource/sounds/water.wav') info.setText('rain') opacity = 1 rainAnim.start() } function down() { opacity = 0 } } Image { id: river source: activity.url + "river.svg" sourceSize.width: parent.width * 0.415 sourceSize.height: parent.height * 0.74 width: parent.width * 0.415 height: parent.height * 0.74 opacity: level > 0 ? 1 : 0 anchors { top: parent.top left: parent.left topMargin: parent.height*0.1775 leftMargin: parent.width*0.293 } z: 10 Behavior on opacity { PropertyAnimation { easing.type: Easing.InOutQuad; duration: 5000 } } property double level: 0 } Image { id: reservoir1 source: activity.url + "reservoir1.svg" sourceSize.width: parent.width*0.06 width: parent.width*0.06 height: parent.height*0.15 anchors { top: parent.top left: parent.left topMargin: parent.height*0.2925 leftMargin: parent.width*0.3225 } opacity: river.level > 0.2 ? 1 : 0 z: 10 Behavior on opacity { PropertyAnimation { easing.type: Easing.InOutQuad; duration: 5000 } } } Image { id: reservoir2 source: activity.url + "reservoir2.svg" sourceSize.width: parent.width*0.12 width: parent.width*0.12 height: parent.height*0.155 anchors { top: parent.top left: parent.left topMargin: parent.height*0.2925 leftMargin: parent.width*0.285 } opacity: river.level > 0.5 ? 1 : 0 z: 10 Behavior on opacity { PropertyAnimation { easing.type: Easing.InOutQuad; duration: 5000 } } } Image { id: reservoir3 source: activity.url + "reservoir3.svg" sourceSize.width: parent.width*0.2 width: parent.width*0.2 height: parent.height*0.17 anchors { top: parent.top left: parent.left topMargin: parent.height*0.29 leftMargin: parent.width*0.25 } opacity: river.level > 0.8 ? 1 : 0 z: 10 Behavior on opacity { PropertyAnimation { easing.type: Easing.InOutQuad; duration: 5000 } } } Image { id: waterplant source: activity.url + "motor.svg" sourceSize.width: parent.width*0.07 sourceSize.height: parent.height*0.08 anchors { top: parent.top left:parent.left topMargin: parent.height*0.38 leftMargin: parent.width*0.4 } z: 20 property bool running: false MouseArea { id: motor_area enabled: river.level > 0.2 anchors.fill: parent onClicked: { items.audioEffects.play('qrc:/gcompris/src/activities/watercycle/resource/bubble.wav') info.setText('tower') waterplant.running = true } } } Image { id: fillpipe anchors.fill: parent sourceSize.width: parent.width width: parent.width source: activity.url + "fillwater.svg" opacity: waterplant.running ? 1 : 0.1 z: 9 Behavior on opacity { PropertyAnimation { easing.type: Easing.InOutQuad; duration: 300 } } } Image { id: sewageplant source: activity.url + "waste.svg" sourceSize.height: parent.height * 0.15 anchors { top: parent.top left: parent.left topMargin: parent.height*0.74 leftMargin: parent.width*0.66 } z: 11 property bool running: false MouseArea { id: waste_area enabled: river.opacity == 1 anchors.fill: parent onClicked: { items.audioEffects.play('qrc:/gcompris/src/activities/watercycle/resource/bubble.wav') info.setText('shower') sewageplant.running = true } } } Image { id: wastepipe anchors.fill: parent sourceSize.width: parent.width width: parent.width source: activity.url + "wastewater.svg" opacity: sewageplant.running ? 1 : 0.1 z: 10 Behavior on opacity { PropertyAnimation { easing.type: Easing.InOutQuad; duration: 300 } } } Image { id: tower source: activity.url + "watertower.svg" sourceSize.width: parent.width*0.18 sourceSize.height: parent.height*0.15 anchors { top: parent.top right: parent.right topMargin: parent.height*0.225 rightMargin: parent.width*0.175 } z: 10 property double level: 0 Image { id: towerfill scale: tower.level source: activity.url + "watertowerfill.svg" sourceSize.width: tower.width*0.4 anchors { top: tower.top left:tower.left topMargin: tower.height*0.13 leftMargin: tower.width*0.3 } Behavior on scale { PropertyAnimation { duration: timer.interval } } } } Image { id: shower source: activity.url + "shower.svg" sourceSize.height: parent.height*0.2 sourceSize.width: parent.width*0.15 anchors { bottom: parent.bottom right: parent.right bottomMargin: parent.height* 0.32 rightMargin: parent.width*0.012 } z: 10 visible: false property bool on: false MouseArea { id: shower_area anchors.fill: parent onClicked: { if(!shower.on && river.opacity == 1 && wastepipe.opacity > 0.8 && fillpipe.opacity > 0.8 && tower.level > 0.5) shower.start() else shower.stop() } } function start() { shower.on = true shower.visible = true showerhot.visible = true tuxbath.visible = true showercold.visible = false tuxoff.visible = false if(!items.cycleDone) { info.setText('done') bonus.good('smiley') items.cycleDone = true } items.audioEffects.play('qrc:/gcompris/src/activities/watercycle/resource/apert.wav') } function stop() { shower.on = false shower.visible = true showerhot.visible = false tuxbath.visible = false showercold.visible = true tuxoff.visible = true } function hide() { shower.visible = false shower.on = false tuxoff.visible = false showercold.visible = false showerhot.visible = false tuxbath.visible = false } } Image { id: tuxoff source:activity.url + "tuxoff.svg" sourceSize.width: shower.height * 0.4 anchors { horizontalCenter: shower.horizontalCenter verticalCenter: shower.verticalCenter verticalCenterOffset: shower.height*0.1 horizontalCenterOffset: -shower.width*0.05 } z: 10 visible: false } Image { id: tuxbath source: activity.url + "tuxbath.svg" sourceSize.width: shower.height * 0.5 anchors { horizontalCenter: shower.horizontalCenter verticalCenter: shower.verticalCenter verticalCenterOffset: shower.height*0.1 horizontalCenterOffset: -shower.width*0.05 } z: 10 visible: false } Image { id: showerhot source: activity.url + "showerhot.svg" sourceSize.width: shower.width * 0.1 anchors { right: shower.right top: shower.top rightMargin: shower.width*0.15 topMargin: shower.height*0.25 } z: 10 visible: false } Image { id: showercold source: activity.url + "showercold.svg" sourceSize.width: shower.width * 0.1 anchors { right: shower.right top: shower.top rightMargin: shower.width*0.15 topMargin: shower.height*0.25 } z: 10 visible: false } // Manage stuff that changes periodically Timer { id: timer interval: 100 running: true repeat: true onTriggered: { if(rain.opacity > 0.9 && river.level < 1) { river.level += 0.01 } if(river.level > 0 && fillpipe.opacity > 0.9 && tower.level < 1 && !shower.on) { river.level -= 0.02 tower.level += 0.05 } if(tower.level > 0 && shower.on) { tower.level -= 0.02 } if(tower.level <= 0 && boatparked.opacity) { shower.stop() } } } GCText { id: info visible: true fontSize: smallSize font.weight: Font.DemiBold horizontalAlignment: Text.AlignHCenter anchors { top: parent.top topMargin: 10 *ApplicationInfo.ratio right: parent.right rightMargin: 5 * ApplicationInfo.ratio left: parent.left leftMargin: parent.width * 0.50 } width: parent.width wrapMode: Text.WordWrap z: 100 onTextChanged: textanim.start() property string newKey SequentialAnimation { id: textanim NumberAnimation { target: info property: "opacity" duration: 200 from: 1 to: 0 } PropertyAction { target: info property: 'text' value: items.dataset[info.newKey] } NumberAnimation { target: info property: "opacity" duration: 200 from: 0 to: 1 } } function setText(key) { if(newKey != key) { newKey = key textanim.start() } } } Rectangle { id: infoBg z: 99 anchors.fill: info color: '#8ebfc7' radius: width * 0.01 opacity: info.text ? 0.7 : 0 Behavior on opacity { PropertyAnimation { easing.type: Easing.InOutQuad; duration: 200 } } } DialogHelp { id: dialogHelp onClose: home() } Bar { id: bar content: BarEnumContent { value: help | home } onHelpClicked: { displayDialog(dialogHelp) } onHomeClicked: activity.home() } Bonus { id:bonus } } } diff --git a/src/core/ActivityBase.qml b/src/core/ActivityBase.qml index 4e38ad42e..b0e0663c7 100644 --- a/src/core/ActivityBase.qml +++ b/src/core/ActivityBase.qml @@ -1,227 +1,227 @@ /* GCompris - ActivityBase.qml * * Copyright (C) 2014 Bruno Coudoin * * Authors: * Bruno Coudoin * * 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 "qrc:/gcompris/src/core/core.js" as Core /** * The base QML component for activities in GCompris. * @ingroup components * * Each activity should be derived from this component. It is responsible for * * * basic common key handling, * * unified audio handling, * * screen switching dynamics (from/to Menu/DialogHelp/etc.) * * The following common keys are handled so far: * * * @c Ctrl+q: Exit the application. * * @c Ctrl+b: Toggle the bar. * * @c Ctrl+f: Toggle fullscreen. * * @c Ctrl+m: Toggle audio effects. * * @c Ctrl+w: Exit the current activity and return to the menu. * * @c Ctrl+p: Make a screenshot. * * @c Back: Return to the home screen (corresponds to the 'Back' button on * Android). * * Cf. Template.qml for a sample skeleton activity. * * Cf. * [the wiki](http://gcompris.net/wiki/Qt_Quick_development_process#Adding_a_new_activity) * for further information about creating a new activity. * * @inherit QtQuick.Item */ Item { id: page /** * type:Item * Parent object. */ property Item main: parent; /** * type:Component * The top-level component containing the visible viewport of an activity. * * Put all you want to present the user into this container. Mostly * implemented using a Rectangle or Image component, itself * containing further graphical elements. You are pretty free of doing * whatever you want inside this component. * * Also common elements as Bar, Score, DialogHelp, etc. should be placed * inside this element. */ property Component pageComponent /** * type:QtObject * Reference to the menu activity. * * Populated automatically during activity-loading. */ property QtObject menu /** * type:QtObject * Reference to the ActivityInfo object of the activity. * * Populated automatically during activity-loading. */ property QtObject activityInfo /** * type:GCAudio * The global audio item for voices. * * Because of problems synchronizing multiple Audio objects between * global/menu/main and individual activities, activities should refrain * from implementing additional Audio elements. * * Instead append to this global object to play your voices after the * intro music. * @sa GCAudio audioEffects */ property GCAudio audioVoices /** * type:GCAudio * The global audio item for audio effects. * * Append to it to play your effects. * @sa GCAudio audioEffects */ - property GCAudio audioEffects + property GCSfx audioEffects /** * type:Loading * The global loading object. * * Start it to signal heavy computation in case of GUI freezes. * @sa Loading */ property Loading loading /** * Emitted when the user wants to return to the Home/Menu screen. */ signal home /** * Emitted when the user wants to return several views back in the * page stack. */ signal back(Item to) /** * Emitted every time the activity has been started. * * Initialize your activity upon this signal. */ signal start /** * Emitted when the activity is about to stop * * Shutdown whatever you need to upon this signal. */ signal stop /** * Emitted when dialog @p dialog should be shown * * Emit this signal when you want to show another dialog, e.g. on * Bar.onHelpClicked * * @param dialog Dialog to show. */ signal displayDialog(Item dialog) /** * Emitted when multiple @p dialogs should be pushed on the page-stack * * Emit this signal when you want to stack >1 views. The last one will be * shown the intermediated ones will be kept on the page stack for later * pop() calls. * * @param dialogs Array of dialogs to push; */ signal displayDialogs(var dialogs) onBack: menu ? menu.back(to) : "" onHome: menu ? menu.home() : "" onDisplayDialog: menu ? menu.displayDialog(dialog) : "" onDisplayDialogs: menu ? menu.displayDialogs(dialogs) : "" Keys.forwardTo: activity.children Keys.onEscapePressed: home(); Keys.onPressed: { if (event.modifiers === Qt.ControlModifier && event.key === Qt.Key_Q) { // Ctrl+Q exit the application Core.quit(main); } else if (event.modifiers === Qt.ControlModifier && event.key === Qt.Key_B) { // Ctrl+B toggle the bar ApplicationSettings.isBarHidden = !ApplicationSettings.isBarHidden; } else if (event.modifiers === Qt.ControlModifier && event.key === Qt.Key_F) { // Ctrl+F toggle fullscreen ApplicationSettings.isFullscreen = !ApplicationSettings.isFullscreen } else if (event.modifiers === Qt.ControlModifier && event.key === Qt.Key_M) { // Ctrl+M toggle sound // We mute / unmute both channels in sync ApplicationSettings.isAudioVoicesEnabled = !ApplicationSettings.isAudioVoicesEnabled ApplicationSettings.isAudioEffectsEnabled = ApplicationSettings.isAudioVoicesEnabled } else if (event.modifiers === Qt.ControlModifier && event.key === Qt.Key_W) { // Ctrl+W exit the current activity home() } else if (event.modifiers === Qt.ControlModifier && event.key === Qt.Key_P) { // Ctrl+P Screenshot ApplicationInfo.screenshot("/tmp/" + activityInfo.name.split('/')[0] + ".png") } } Keys.onReleased: { if (event.key === Qt.Key_Back) { event.accepted = true home() } } Loader { id: activity sourceComponent: pageComponent anchors.fill: parent } Loader { id: demoPageLoader source: ApplicationSettings.activationMode == 1 ? "BuyMeOverlayInapp.qml" : "BuyMeOverlay.qml" anchors.fill: parent active: !activityInfo.demo && ApplicationSettings.isDemoMode } } diff --git a/src/core/AnswerButton.qml b/src/core/AnswerButton.qml index 616ffb1a5..c9f09a520 100644 --- a/src/core/AnswerButton.qml +++ b/src/core/AnswerButton.qml @@ -1,264 +1,264 @@ /* Copyed in GCompris from Touch'n'learn Touch'n'learn - Fun and easy mobile lessons for kids Copyright (C) 2010, 2011 by Alessandro Portale http://touchandlearn.sourceforge.net This file is part of Touch'n'learn Touch'n'learn 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. Touch'n'learn 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 Touch'n'learn; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ import QtQuick 2.6 import GCompris 1.0 /** * A QML component to display an answer button. * * AnswerButton consists of a text (@ref textLabel) * and animations on pressed. * Mostly used to present more than one option to select from * consisting of both good and bad answers. * * @inherit QtQuick.Item */ Item { id: button /** * type:string * Text to display on the button. * * @sa label.text */ property string textLabel /** * type:boolean * * Set to true when this element contains good answer. */ property bool isCorrectAnswer: false /** * type:color * * Color of the container in normal state. */ property color normalStateColor: "#fff" /** * type:color * * Color of the container on good answer selection. */ property color correctStateColor: "#09f" /** * type:color * * Color of the container on bad answer selection. */ property color wrongStateColor: "#f66" /** * type:bool * * Set to true when to avoid misclicks or when correct/wrong answer * animation are running. */ property bool blockClicks: false /** * type:int * * Amplitude of the shake animation on wrong answer selection. */ property int wrongAnswerShakeAmplitudeCalc: width * 0.2 /** * type:int * * Minimum amplitude of the shake animation on wrong answer selection. */ property int wrongAnswerShakeAmplitudeMin: 45 /** * type:int * * Amplitude of the shake animation on wrong answer. * Selects min. from wrongAnswerShakeAmplitudeMin && wrongAnswerShakeAmplitudeCalc. */ property int wrongAnswerShakeAmplitude: wrongAnswerShakeAmplitudeCalc < wrongAnswerShakeAmplitudeMin ? wrongAnswerShakeAmplitudeMin : wrongAnswerShakeAmplitudeCalc // If you want the sound effects just pass the audioEffects - property GCAudio audioEffects + property GCSfx audioEffects /** * Emitted when button is pressed as a good answer. * * Triggers correctAnswerAnimation. */ signal correctlyPressed /** * Emitted when button is pressed as a bad answer. * * Triggers wrongAnswerAnimation. */ signal incorrectlyPressed /** * Emitted when answer button is clicked. */ signal pressed onPressed: { if (!blockClicks) { if (isCorrectAnswer) { if(audioEffects) audioEffects.play("qrc:/gcompris/src/core/resource/sounds/win.wav") correctAnswerAnimation.start(); } else { if(audioEffects) audioEffects.play("qrc:/gcompris/src/core/resource/sounds/crash.wav") wrongAnswerAnimation.start(); } } } Rectangle { id: rect anchors.fill: parent color: normalStateColor opacity: 0.5 } ParticleSystemStarLoader { id: particles } Image { source: "qrc:/gcompris/src/core/resource/button.svg" sourceSize { height: parent.height; width: parent.width } width: sourceSize.width height: sourceSize.height smooth: false } GCText { id: label anchors.verticalCenter: parent.verticalCenter // We need to manually horizonally center the text, because in wrongAnswerAnimation, // the x of the text is changed, which would not work if we use an anchor layout. property int horizontallyCenteredX: (button.width - contentWidth) >> 1; width: button.width x: horizontallyCenteredX; fontSizeMode: Text.Fit font.bold: true text: textLabel color: "#373737" } MouseArea { id: mouseArea anchors.fill: parent onPressed: button.pressed() } SequentialAnimation { id: correctAnswerAnimation ScriptAction { script: { if (typeof(feedback) === "object") feedback.playCorrectSound(); blockClicks = true; if (typeof(particles) === "object") particles.burst(40); } } PropertyAction { target: rect property: "color" value: correctStateColor } PropertyAnimation { target: rect property: "color" to: normalStateColor duration: 700 } PauseAnimation { duration: 300 // Wait for particles to finish } ScriptAction { script: { blockClicks = false; correctlyPressed(); } } } SequentialAnimation { id: wrongAnswerAnimation ParallelAnimation { SequentialAnimation { PropertyAction { target: rect property: "color" value: wrongStateColor } ScriptAction { script: { if (typeof(feedback) === "object") feedback.playIncorrectSound(); } } PropertyAnimation { target: rect property: "color" to: normalStateColor duration: 600 } } SequentialAnimation { PropertyAnimation { target: label property: "x" to: label.horizontallyCenteredX - wrongAnswerShakeAmplitude easing.type: Easing.InCubic duration: 120 } PropertyAnimation { target: label property: "x" to: label.horizontallyCenteredX + wrongAnswerShakeAmplitude easing.type: Easing.InOutCubic duration: 220 } PropertyAnimation { target: label property: "x" to: label.horizontallyCenteredX easing { type: Easing.OutBack; overshoot: 3 } duration: 180 } } } PropertyAnimation { target: rect property: "color" to: normalStateColor duration: 450 } ScriptAction { script: { incorrectlyPressed(); } } } } diff --git a/src/core/Bonus.qml b/src/core/Bonus.qml index 0bb27a9a7..81a87d0b6 100644 --- a/src/core/Bonus.qml +++ b/src/core/Bonus.qml @@ -1,203 +1,204 @@ /* GCompris - Bonus.qml * * Copyright (C) 2014 Bruno Coudoin * * Authors: * Bruno Coudoin * * 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 // Requires the global property in the scope: // property GCAudio audioEffects, audioVoices /** * A QML component providing user feedback upon winning/loosing. * @ingroup components * * Usually triggered by an activity when a user has won/lost a level via the * @ref good / @ref bad methods. Bonus then provides visual and auditive * feedback to the user and emits the @ref win / @ref loose signals when * finished. * * Maintains a list of possible audio voice resources to be playebd back * upon winning/loosing events, and selects randomly from them when triggered. * * @inherit QtQuick.Image */ Image { id: bonus /** * type:string * Url of the audio resource to be used as winning sound. */ property string winSound: url + "sounds/bonus.wav" /** * type:string * Url of the audio resource to be used as loosing sound. */ property string looseSound /** * type:int * Interval in milliseconds after which the bonus will be played (default is 500ms) */ property alias interval: timer.interval /** * Emitted when the bonus starts */ signal start /** * Emitted when the bonus stops */ signal stop /** * Emitted when the win event is over. * * After the animation has finished. */ signal win /** * Emitted when the loose event is over. * * After the animation has finished. */ signal loose /// @cond INTERNAL_DOCS property string url: "qrc:/gcompris/src/core/resource/" property bool isWin: false property var winVoices: [ "voices-$CA/$LOCALE/misc/congratulation.$CA", "voices-$CA/$LOCALE/misc/great.$CA", "voices-$CA/$LOCALE/misc/good.$CA", "voices-$CA/$LOCALE/misc/awesome.$CA", "voices-$CA/$LOCALE/misc/fantastic.$CA", "voices-$CA/$LOCALE/misc/waytogo.$CA", "voices-$CA/$LOCALE/misc/super.$CA", "voices-$CA/$LOCALE/misc/perfect.$CA" ] property var looseVoices: [ "voices-$CA/$LOCALE/misc/check_answer.$CA" ] /// @endcond /** * type:bool * True between the moment we have the win/lose signal emitted and the * bonus image is no more displayed */ property bool isPlaying: animation.running || timer.running visible: true opacity: 0 anchors.fill: parent anchors.margins: 50 * ApplicationInfo.ratio fillMode: Image.PreserveAspectFit z: 1000 sourceSize.width: parent.width * 0.5 /** * Triggers win feedback. * * Tries to play back a voice resource for winning, if not found fall back * to the winSound. * * @param name Type of win image to show. * Possible values are "flower", "gnu", "lion", "note", "smiley", "tux" */ function good(name) { source = url + "bonus/" + name + "_good.svg" isWin = true timer.start() } /** * Triggers loose feedback. * * Tries to play back a voice resource for loosing, if not found fall back * to the looseSound. * * @param name Type of win image to show. * Possible values are "flower", "gnu", "lion", "note", "smiley", "tux" */ function bad(name) { source = url + "bonus/" + name + "_bad.svg" isWin = false; timer.start() } /** * Private: Triggers win feedback after the timer completion */ function _good() { if(!audioVoices.play( ApplicationInfo.getAudioFilePath( winVoices[Math.floor(Math.random()*winVoices.length)]))) if(winSound) audioEffects.play(winSound) + start() animation.start() } /** * Private: Triggers loose feedback after the timer completion. */ function _bad(name) { - if(!audioEffects.play( + if(!audioVoices.play( ApplicationInfo.getAudioFilePath( looseVoices[Math.floor(Math.random()*looseVoices.length)]))) if(looseSound) audioEffects.play(looseSound) start() animation.start() } SequentialAnimation { id: animation NumberAnimation { target: bonus property: "opacity" from: 0; to: 1.0 duration: 1000 easing.type: Easing.InOutQuad } NumberAnimation { target: bonus property: "opacity" from: 1.0; to: 0 duration: 500 easing.type: Easing.InOutQuad } onStopped: { bonus.stop() isWin ? win() : loose() } } // It is useful to launch the bonus after a delay to let the children // appreciate the completed level Timer { id: timer interval: 500 onTriggered: isWin ? bonus._good() : bonus._bad() } } diff --git a/src/core/Domino.qml b/src/core/Domino.qml index fc914af53..cde34e4a7 100644 --- a/src/core/Domino.qml +++ b/src/core/Domino.qml @@ -1,144 +1,144 @@ /* GCompris - Domino.qml * * Copyright (C) 2014 Bruno Coudoin * * Authors: * Bruno Coudoin * * 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 /** * A QML component to display a domino. * * Domino consists of Flipable sides(front and back) * and uses DominoNumber to display numbers. * It is divided into two regions containing DominoNumbers. * * @inherit QtQuick.Flipable */ Flipable { id: flipable /** * type:int * Integer displayed in first region. */ property alias value1: number1.value /** * type:int * Integer displayed in second region. */ property alias value2: number2.value /** * type:int * Highest integer to display. */ property int valueMax: 9 // Domino style property color color: "white" property color borderColor: "black" property int borderWidth: 2 property int radius: width * 0.05 property color backColor: "white" property color pointColor: "black" // Set to true when to display on both sides. property bool flipEnabled: false property bool flipped: true // Set to false to prevent user inputs. property bool isClickable: true - property GCAudio audioEffects + property GCSfx audioEffects front: Rectangle { anchors.fill: parent smooth: true; color: flipable.color border.color: "black" border.width: flipable.borderWidth radius: flipable.radius DominoNumber { id: number1 width: parent.width / 2 height: parent.height color: flipable.pointColor borderColor: "black" borderWidth: 0 radius: parent.height * 0.25 valueMax: flipable.valueMax onValueChanged: if(flipEnabled) flipable.flipped = !flipable.flipped isClickable: flipable.isClickable audioEffects: flipable.audioEffects } // Separation Rectangle { x: parent.width / 2 anchors.verticalCenter: parent.verticalCenter width: 2 height: parent.height * 0.7 color: flipable.borderColor } DominoNumber { id: number2 x: parent.width / 2 width: parent.width / 2 height: parent.height color: flipable.pointColor borderColor: "black" borderWidth: 0 radius: parent.height * 0.25 valueMax: flipable.valueMax onValueChanged: if(flipEnabled) flipable.flipped = !flipable.flipped isClickable: flipable.isClickable audioEffects: flipable.audioEffects } } back: Rectangle { anchors.fill: parent smooth: true; color: flipable.backColor border.width: flipable.borderWidth radius: flipable.radius } transform: Rotation { id: rotation origin.x: flipable.width/2 origin.y: flipable.height/2 axis.x: 0; axis.y: 1; axis.z: 0 // set axis.y to 1 to rotate around y-axis angle: 0 // the default angle } states: State { name: "back" PropertyChanges { target: rotation; angle: 180 } when: flipable.flipped onCompleted: flipable.flipped = false } transitions: Transition { NumberAnimation { target: rotation; property: "angle"; duration: 250 } } } diff --git a/src/core/DominoNumber.qml b/src/core/DominoNumber.qml index 39d2ebe5d..c14f1f526 100644 --- a/src/core/DominoNumber.qml +++ b/src/core/DominoNumber.qml @@ -1,205 +1,205 @@ /* GCompris - DominoNumber.qml * * Copyright (C) 2014 Bruno Coudoin * * Authors: * Bruno Coudoin * * 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 /** * A QML component to display integers(0-9) on Domino. * Numbers are displayed in the form of number of circles. * * @inherit QtQuick.Item */ Item { id: item /** * type:int * Integer to display on the domino */ property int value /** * type:int * Highest number visible on domino. */ property int valueMax /** * type:color * color of the dots to display an integer. */ property color color /** * type:color * Border color of the dots to display an integer. */ property color borderColor /** * type:int * Border width of the dots to display an integer. */ property int borderWidth /** * type:int * Radius of the dots to display an integer. */ property int radius /** * type:boolean * Set false to disable mouse/touch inputs on domino. */ property bool isClickable: true // Default value /** * type:GCAudio * To play sound and audio effects. */ - property GCAudio audioEffects + property GCSfx audioEffects function isVisible(index) { var value = item.value var visible = false switch(index) { case 0: if(value >= 2) visible = true break case 1: if(value >= 6) visible = true break case 2: if(value >= 4) visible = true break case 3: if(value >= 8) visible = true break case 4: if(value == 1 || value == 3 || value == 5 || value == 7 || value == 9) visible = true break case 5: if(value >= 8) visible = true break case 6: if(value >= 4) visible = true break case 7: if(value >= 6) visible = true break case 8: if(value >= 2) visible = true break } return visible } Grid { columns: 3 spacing: 3 anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter horizontalItemAlignment: Grid.AlignHCenter verticalItemAlignment: Grid.AlignVCenter Repeater { model: 9 Rectangle { width: radius height: radius border.width: item.borderWidth color: item.color border.color: item.borderColor radius: item.radius opacity: isVisible(index) Behavior on opacity { PropertyAnimation { duration: 200 } } } } } // Increase the displayed integer value by one. function up() { audioEffects.play('qrc:/gcompris/src/core/resource/sounds/scroll.wav') if(item.value == item.valueMax) item.value = 0 else item.value++ } // Decrease the displayed integer by one. function down() { audioEffects.play('qrc:/gcompris/src/core/resource/sounds/scroll.wav') if(item.value == 0) item.value = item.valueMax else item.value-- } MouseArea { enabled: !ApplicationInfo.isMobile && item.isClickable anchors.fill: parent acceptedButtons: Qt.LeftButton | Qt.RightButton onClicked: { if (mouse.button == Qt.LeftButton) up() else down() } } /** * type:boolean * To check on touch devices to increase or decrease the integer value. */ property bool goUp Timer { id: timer interval: 500 repeat: true onTriggered: goUp ? up() : down() } MultiPointTouchArea { enabled: ApplicationInfo.isMobile && item.isClickable anchors.fill: parent maximumTouchPoints: 1 onPressed: { goUp = true up() timer.start() } onTouchUpdated: { if(touchPoints.length) { var touch = touchPoints[0] if(touch.y < parent.y + parent.height) goUp = true else goUp = false } } onReleased: timer.stop() } } diff --git a/src/core/GCSfx.qml b/src/core/GCSfx.qml new file mode 100644 index 000000000..d5db6a347 --- /dev/null +++ b/src/core/GCSfx.qml @@ -0,0 +1,211 @@ +/* GCompris - GCSoundEffect.qml + * + * Copyright (C) 2018 Timothée Giet + * + * Authors: + * Johnny Jazeix (GCAudio base, 2014) + * Timothée Giet + * + * 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 QtMultimedia 5.0 +import GCompris 1.0 + +/** + * A QML component for sfx playback. + * @ingroup components + * + * Wrapper component around QtQuick's SoundEffect element, handling all sfx + * playback in GCompris uniformly. + * + * It maintains a queue of sfx-sources (@ref files) that are played back + * sequentially, to which the user can enqueue files that should be scheduled + * for playback. + * + * To make sure an sfx voice will be localized, replace the locale part + * of the file by '$LOCALE'. + * + * To makes sure that all sfx sources are normalized with respect to + * ApplicationInfo.CompressedSoundEffect replace the 'ogg' part of the file by + * '$CA'. + * + * @inherit QtQuick.Item + */ +Item { + id: gcsfx + + /** + * type:bool + * Whether sfx should be muted. + */ + property bool muted + + /** + * type:url + * URL to the sfx source to be played back. + */ + property alias source: sfx.source + + /** + * type:string + * Status of the fx player. + */ + property alias status: sfx.status + + /** + * type:var + * Current playback state. + * + * Possible values taken from SoundEffect.status + */ + property var playbackState: (sfx.error == SoundEffect.NoError) ? + sfx.playbackState : SoundEffect.StoppedState; + + /** + * type:list + * Playback queue. + */ + property var files: [] + + /** + * Emitted in case of error. + */ + signal error + + /** + * Emitted when playback of all scheduled sfx sources has finished. + */ + signal done + + /** + * When mute is changed we set the volume to 0 to mute a potential playing + * sound. + */ + onMutedChanged: muted ? sfx.volume = 0 : sfx.volume = 1 + + /** + * Plays back the sfx resource @p file. + * + * @param type:string file [optional] URL to an sfx source. + * @returns @c true if playback has been started, @c false if file does not + * exist or sfx is muted + */ + function play(file) { + if(!fileId.exists(file) || muted) + return false + + if(file) { + // Setting the source to "" on Linux fix a case where the sound is no more played if you play twice the same sound in a row + source = "" + source = file + } + if(!muted) { + sfx.play() + } + return true + } + + /** + * Stops sfx playback. + */ + function stop() { + if(sfx.playbackState != SoundEffect.StoppedState) + sfx.stop() + } + + /** + * Schedules a @p file for sfx playback. + * + * If there is no playback currently running, the new source will be + * played back immediately. Otherwise it is appended to the file queue of + * sources. + * + * @param type:string file File to the sfx file to be played back. + * @returns @c true upon success, or @c false if @p file does not exist or + * sfx is muted + */ + function append(file) { + if(!fileId.exists(file) || muted) + return false + + if(sfx.playbackState !== SoundEffect.PlayingState + || sfx.status === SoundEffect.EndOfMedia + || sfx.status === SoundEffect.NoMedia + || sfx.status === SoundEffect.InvalidMedia) { + // Setting the source to "" on Linux fix a case where the sound is no more played + source = "" + source = file + files.push(file) + silenceTimer.start() + } else { + files.push(file) + } + return true + } + + /** + * Adds a pause of the given duration in ms before playing of the next file. + * + * @param type:int duration_ms Pause in milliseconds. + */ + function silence(duration_ms) { + silenceTimer.interval = duration_ms + } + + /** + * Flushes the list of scheduled files. + * @sa files + */ + function clearQueue() { + while(files.length > 0) { + files.pop(); + } + } + + /// @cond INTERNAL_DOCS + + function _playNextFile() { + var nextFile = files.shift() + if(!nextFile || 0 === nextFile.length) { + sfx.source = "" + gcsfxdone() + } else { + sfx.source = "" + sfx.source = nextFile + if(!muted) + sfx.play() + } + } + + SoundEffect { + id: sfx + } + + Timer { + id: silenceTimer + repeat: false + onTriggered: { + interval = 0 + _playNextFile() + } + } + + File { + id: fileId + } + + /// @endcond + +} diff --git a/src/core/main.qml b/src/core/main.qml index 23f2fd2ca..e998bbb02 100644 --- a/src/core/main.qml +++ b/src/core/main.qml @@ -1,348 +1,348 @@ /* GCompris - main.qml * * Copyright (C) 2014 Bruno Coudoin * * Authors: * Bruno Coudoin * * 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 QtQuick.Window 2.1 import QtQml 2.2 import GCompris 1.0 import "qrc:/gcompris/src/core/core.js" as Core /** * GCompris' main QML file defining the top level window. * @ingroup infrastructure * * Handles application start (Component.onCompleted) and shutdown (onClosing) * on the QML layer. * * Contains the central GCAudio objects audio effects and audio voices. * * Contains the top level StackView presenting and animating GCompris' * full screen views. * * @sa BarButton, BarEnumContent * @inherit QtQuick.Window */ Window { id: main // Start in window mode at full screen size width: ApplicationSettings.previousWidth height: ApplicationSettings.previousHeight minimumWidth: 400 * ApplicationInfo.ratio minimumHeight: 400 * ApplicationInfo.ratio title: "GCompris" /// @cond INTERNAL_DOCS property var applicationState: Qt.application.state onApplicationStateChanged: { if (ApplicationInfo.isMobile && applicationState != Qt.ApplicationActive) { audioVoices.stop(); audioEffects.stop(); } } onClosing: Core.quit(main) GCAudio { id: audioVoices muted: !ApplicationSettings.isAudioVoicesEnabled Timer { id: delayedWelcomeTimer interval: 10000 /* Make sure, that playing welcome.ogg if delayed * because of not yet registered voices, will only * happen max 10sec after startup */ repeat: false onTriggered: { DownloadManager.voicesRegistered.disconnect(playWelcome); } function playWelcome() { audioVoices.append(ApplicationInfo.getAudioFilePath("voices-$CA/$LOCALE/misc/welcome.$CA")); } } Component.onCompleted: { if(ApplicationSettings.isAudioEffectsEnabled) append(ApplicationInfo.getAudioFilePath("qrc:/gcompris/src/core/resource/intro.$CA")) if (DownloadManager.areVoicesRegistered()) delayedWelcomeTimer.playWelcome(); else { DownloadManager.voicesRegistered.connect( delayedWelcomeTimer.playWelcome); delayedWelcomeTimer.start(); } } } - GCAudio { + GCSfx { id: audioEffects muted: !ApplicationSettings.isAudioEffectsEnabled } function playIntroVoice(name) { name = name.split("/")[0] audioVoices.append(ApplicationInfo.getAudioFilePath("voices-$CA/$LOCALE/intro/" + name + ".$CA")) } function checkWordset() { var wordset = ApplicationSettings.wordset if(wordset == '') // Maybe the wordset has been bundled or copied manually // we have to register it if we find it. wordset = 'data2/words/words.rcc' // check for words.rcc: if (DownloadManager.isDataRegistered("words")) { // words.rcc is already registered -> nothing to do } else if(DownloadManager.haveLocalResource(wordset)) { // words.rcc is there -> register old file first // then try to update in the background if(DownloadManager.updateResource(wordset)) { ApplicationSettings.wordset = wordset } } else if(ApplicationSettings.wordset) { // Only if wordset specified // words.rcc has not been downloaded yet -> ask for download Core.showMessageDialog( main, qsTr("The images for several activities are not yet installed. " + "Do you want to download them now?"), qsTr("Yes"), function() { if (DownloadManager.downloadResource(wordset)) var downloadDialog = Core.showDownloadDialog(pageView.currentItem, {}); }, qsTr("No"), null, function() { pageView.currentItem.focus = true } ); } } ChangeLog { id: changelog } Component.onCompleted: { console.log("enter main.qml (run #" + ApplicationSettings.exeCount + ", ratio=" + ApplicationInfo.ratio + ", fontRatio=" + ApplicationInfo.fontRatio + ", dpi=" + Math.round(Screen.pixelDensity*25.4) + ", sharedWritablePath=" + ApplicationInfo.getSharedWritablePath() + ")"); if (ApplicationSettings.exeCount === 1 && !ApplicationSettings.isKioskMode && ApplicationInfo.isDownloadAllowed) { // first run var dialog; dialog = Core.showMessageDialog( main, qsTr("Welcome to GCompris!") + '\n' + qsTr("You are running GCompris for the first time.") + '\n' + qsTr("You should verify that your application settings especially your language is set correctly, and that all language specific sound files are installed. You can do this in the Preferences Dialog.") + "\n" + qsTr("Have Fun!") + "\n" + qsTr("Your current language is %1 (%2).") .arg(Qt.locale(ApplicationInfo.getVoicesLocale(ApplicationSettings.locale)).nativeLanguageName) .arg(ApplicationInfo.getVoicesLocale(ApplicationSettings.locale)) + "\n" + qsTr("Do you want to download the corresponding sound files now?"), qsTr("Yes"), function() { if (DownloadManager.downloadResource( DownloadManager.getVoicesResourceForLocale(ApplicationSettings.locale))) var downloadDialog = Core.showDownloadDialog(pageView.currentItem, {}); }, qsTr("No"), null, function() { pageView.currentItem.focus = true checkWordset() } ); } else { // Register voices-resources for current locale, updates/downloads only if // not prohibited by the settings if (!DownloadManager.areVoicesRegistered()) { DownloadManager.updateResource( DownloadManager.getVoicesResourceForLocale(ApplicationSettings.locale)); } checkWordset() if(changelog.isNewerVersion(ApplicationSettings.lastGCVersionRan, ApplicationInfo.GCVersionCode)) { // display log between ApplicationSettings.lastGCVersionRan and ApplicationInfo.GCVersionCode var dialog; dialog = Core.showMessageDialog( main, qsTr("GCompris has been updated! Here are the new changes:
") + changelog.getLogBetween(ApplicationSettings.lastGCVersionRan, ApplicationInfo.GCVersionCode), "", null, "", null, function() { pageView.currentItem.focus = true } ); // Store new version ApplicationSettings.lastGCVersionRan = ApplicationInfo.GCVersionCode; } } } Loading { id: loading } StackView { id: pageView anchors.fill: parent initialItem: { "item": "qrc:/gcompris/src/activities/" + ActivityInfoTree.rootMenu.name, "properties": { 'audioVoices': audioVoices, 'audioEffects': audioEffects, 'loading': loading } } focus: true delegate: StackViewDelegate { id: root function getTransition(properties) { audioVoices.clearQueue() audioVoices.stop() if(!properties.exitItem.isDialog && // if coming from menu and !properties.enterItem.isDialog) // going into an activity then playIntroVoice(properties.enterItem.activityInfo.name); // play intro if (!properties.exitItem.isDialog || // if coming from menu or properties.enterItem.alwaysStart) // start signal enforced (for special case like transition from config-dialog to editor) properties.enterItem.start(); if(properties.name === "pushTransition") { if(properties.enterItem.isDialog) { return pushVTransition } else { return pushHTransition } } else { if(properties.exitItem.isDialog) { return popVTransition } else { return popHTransition } } } function transitionFinished(properties) { properties.exitItem.opacity = 1 if(!properties.enterItem.isDialog) { properties.exitItem.stop() } } property Component pushHTransition: StackViewTransition { PropertyAnimation { target: enterItem property: "x" from: target.width to: 0 duration: 500 easing.type: Easing.OutSine } PropertyAnimation { target: exitItem property: "x" from: 0 to: -target.width duration: 500 easing.type: Easing.OutSine } } property Component popHTransition: StackViewTransition { PropertyAnimation { target: enterItem property: "x" from: -target.width to: 0 duration: 500 easing.type: Easing.OutSine } PropertyAnimation { target: exitItem property: "x" from: 0 to: target.width duration: 500 easing.type: Easing.OutSine } } property Component pushVTransition: StackViewTransition { PropertyAnimation { target: enterItem property: "y" from: -target.height to: 0 duration: 500 easing.type: Easing.OutSine } PropertyAnimation { target: exitItem property: "y" from: 0 to: target.height duration: 500 easing.type: Easing.OutSine } } property Component popVTransition: StackViewTransition { PropertyAnimation { target: enterItem property: "y" from: target.height to: 0 duration: 500 easing.type: Easing.OutSine } PropertyAnimation { target: exitItem property: "y" from: 0 to: -target.height duration: 500 easing.type: Easing.OutSine } } property Component replaceTransition: pushHTransition } } /// @endcond }