diff --git a/src/activities/lang/ImageReview.qml b/src/activities/lang/ImageReview.qml index 923711658..4272b4780 100644 --- a/src/activities/lang/ImageReview.qml +++ b/src/activities/lang/ImageReview.qml @@ -1,421 +1,416 @@ /* GCompris - lang.qml * * Copyright (C) Siddhesh suthar (Qt Quick port) * * Authors: * Pascal Georges (pascal.georges1@free.fr) (GTK+ version) * Holger Kaelberer (Qt Quick port of imageid) * Siddhesh suthar (Qt Quick port) * Bruno Coudoin (Integration Lang dataset) * * 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 "lang.js" as Activity import "qrc:/gcompris/src/core/core.js" as Core Item { id: imageReview anchors.fill: parent property alias category: categoryText.text property int wordListIndex // This is the current sub list of words property var word: rootItem.opacity == 1 ? items.wordList[wordListIndex][score.currentSubLevel - 1] : undefined // miniGames is list of miniGames // first element is Activity name, // second element is mode of miniGame // third element is the qml to load property var miniGames: [ ["QuizActivity", 1, "Quiz.qml"], ["QuizActivity", 2, "Quiz.qml"], ["QuizActivity", 3, "Quiz.qml"], ["SpellActivity", 1, "SpellIt.qml"] ] property var currentMiniGame property var loadedItems property bool started: rootItem.opacity == 1 // Start at last wordListIndex function start() { initLevel(wordListIndex) } // Start the image review at wordList sublesson function initLevel(wordListIndex_) { wordListIndex = wordListIndex_ score.currentSubLevel = 1 score.numberOfSubLevels = items.wordList[wordListIndex].length focus = true forceActiveFocus() miniGameLoader.source = "" currentMiniGame = -1 rootItem.opacity = 1 } function stop() { focus = false rootItem.opacity = 0 wordImage.changeSource('') wordText.changeText('') repeatItem.visible = false } onWordChanged: { if(word) { if (Activity.playWord(word.voice)) { word['hasVoice'] = true repeatItem.visible = true } else { word['hasVoice'] = false repeatItem.visible = false } wordImage.changeSource(word.image) wordText.changeText(word.translatedTxt) } } // Cheat codes to access mini games directly Keys.onPressed: { if((event.modifiers & Qt.ControlModifier) && (event.key === Qt.Key_1)) { initLevel(wordListIndex) event.accepted = true } if((event.modifiers & Qt.ControlModifier) && (event.key === Qt.Key_2)) { startMiniGame(0) event.accepted = true } if((event.modifiers & Qt.ControlModifier) && (event.key === Qt.Key_3)) { startMiniGame(1) event.accepted = true } if((event.modifiers & Qt.ControlModifier) && (event.key === Qt.Key_4)) { startMiniGame(2) event.accepted = true } if((event.modifiers & Qt.ControlModifier) && (event.key === Qt.Key_5)) { startMiniGame(3) event.accepted = true } } Keys.onEscapePressed: { Activity.launchMenuScreen() } Keys.onLeftPressed: { if( score.currentSubLevel > 1 ) { imageReview.prevWord() event.accepted = true } } Keys.onRightPressed: { imageReview.nextWord() event.accepted = true } Keys.onSpacePressed: { imageReview.nextWord() event.accepted = true } Keys.onEnterPressed: { imageReview.nextWord() event.accepted = true } Keys.onReturnPressed: { imageReview.nextWord() event.accepted = true } Keys.onReleased: { if (event.key === Qt.Key_Back) { event.accepted = true Activity.launchMenuScreen() } } Item { id: rootItem anchors.fill: parent opacity: 0 Behavior on opacity { PropertyAnimation { duration: 200 } } Rectangle { id: categoryTextbg parent: rootItem x: categoryText.x - 4 y: categoryText.y - 4 width: imageFrame.width + 8 height: categoryText.height + 8 color: "#5090ff" border.color: "#000000" border.width: 2 radius: 16 anchors { horizontalCenter: parent.horizontalCenter top: rootItem.top topMargin: 4 * ApplicationInfo.ratio } GCText { id: categoryText fontSize: mediumSize font.weight: Font.DemiBold width: parent.width - 8 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter color: "white" wrapMode: Text.WordWrap } } Image { id: imageFrame parent: rootItem source: "qrc:/gcompris/src/activities/lang/resource/imageid_frame.svg" sourceSize.width: Math.min((parent.width - previousWordButton.width * 2) * 0.8, (parent.height - categoryTextbg.height - wordTextbg.height - bar.height) * 1.1) anchors { horizontalCenter: parent.horizontalCenter top: categoryTextbg.bottom margins: 10 * ApplicationInfo.ratio } z: 11 Image { id: wordImage // Images are not svg width: Math.min(parent.width, parent.height) * 0.9 height: width anchors.centerIn: parent property string nextSource function changeSource(nextSource_) { nextSource = nextSource_ animImage.start() } SequentialAnimation { id: animImage PropertyAnimation { target: wordImage property: "opacity" to: 0 duration: 100 } PropertyAction { target: wordImage property: "source" value: wordImage.nextSource } PropertyAnimation { target: wordImage property: "opacity" to: 1 duration: 100 } } MouseArea { anchors.fill: parent enabled: rootItem.opacity == 1 onClicked: Activity.playWord(word.voice) } } Image { id: previousWordButton source: "qrc:/gcompris/src/core/resource/bar_previous.svg"; sourceSize.width: 30 * 1.2 * ApplicationInfo.ratio visible: score.currentSubLevel > 1 ? true : false anchors { right: parent.left rightMargin: 30 top: parent.top topMargin: parent.height/2 - previousWordButton.height/2 } MouseArea { id: previousWordButtonArea anchors.fill: parent onClicked: imageReview.prevWord() } } Image { id: nextWordButton source: "qrc:/gcompris/src/core/resource/bar_next.svg"; sourceSize.width: 30 * 1.2 * ApplicationInfo.ratio anchors { left: parent.right leftMargin: 30 top: parent.top topMargin: parent.height/2 - previousWordButton.height/2 } MouseArea { id: nextWordButtonArea anchors.fill: parent onClicked: imageReview.nextWord(); } } } Rectangle { id: wordTextbg parent: rootItem x: wordText.x - 4 y: wordText.y - 4 width: imageFrame.width height: wordText.height + 4 color: "#5090ff" border.color: "#000000" border.width: 2 radius: 16 anchors { top: imageFrame.bottom left: imageFrame.left margins: 10 * ApplicationInfo.ratio } GCText { id: wordText text: "" fontSize: largeSize font.weight: Font.DemiBold width: parent.width horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter color: "white" wrapMode: Text.WordWrap property string nextWord function changeText(nextWord_) { nextWord = nextWord_ animWord.start() } SequentialAnimation { id: animWord PropertyAnimation { target: wordText property: "opacity" to: 0 duration: 100 } PropertyAction { target: wordText property: "text" value: wordText.nextWord } PropertyAnimation { target: wordText property: "opacity" to: 1 duration: 100 } } } } BarButton { id: repeatItem parent: rootItem source: "qrc:/gcompris/src/core/resource/bar_repeat.svg"; - sourceSize.width: 80 * ApplicationInfo.ratio + sourceSize.width: Math.min(imageFrame.x, 100 * ApplicationInfo.ratio) - 2 * anchors.margins z: 12 anchors { top: parent.top left: parent.left margins: 10 * ApplicationInfo.ratio } onClicked: Activity.playWord(imageReview.word.voice) Behavior on opacity { PropertyAnimation { duration: 200 } } } Score { id: score parent: rootItem - anchors.bottom: undefined - anchors.bottomMargin: 10 * ApplicationInfo.ratio - anchors.right: parent.right - anchors.rightMargin: 10 * ApplicationInfo.ratio - anchors.top: parent.top } } Loader { id: miniGameLoader width: parent.width height: parent.height anchors.fill: parent asynchronous: false } function nextPressed() { if(currentMiniGame === 0) { nextSubLevel() } } function nextWord() { ++score.currentSubLevel; if(score.currentSubLevel == score.numberOfSubLevels + 1) { nextMiniGame() } } function prevWord() { --score.currentSubLevel } function startMiniGame(miniGameIndex) { currentMiniGame = miniGameIndex var mode = miniGames[miniGameIndex][1]; var itemToLoad = miniGames[miniGameIndex][2]; - // Starting a minigame we don't wan't pending voices to play + // Starting a minigame we don't want pending voices to play Activity.clearVoiceQueue() // preparing the wordList var wordList = Core.shuffle(items.wordList[wordListIndex]).slice() miniGameLoader.source = itemToLoad; var loadedItems = miniGameLoader.item rootItem.opacity = 0 focus = false // Initiate the loaded item mini game // Some Mini Games may not start because they miss voices // In this case we try the next one if(!loadedItems.init(loadedItems, wordList, mode)) nextMiniGame() } //called by a miniGame when it is won function nextMiniGame() { if(currentMiniGame < miniGames.length - 1) { startMiniGame(++currentMiniGame) } else { Activity.markProgress() if(wordListIndex < items.wordList.length - 1) { initLevel(wordListIndex + 1) } else { Activity.launchMenuScreen() } } } } diff --git a/src/activities/lang/Quiz.qml b/src/activities/lang/Quiz.qml index dd343ac24..8370d015d 100644 --- a/src/activities/lang/Quiz.qml +++ b/src/activities/lang/Quiz.qml @@ -1,293 +1,289 @@ /* GCompris - Quiz.qml * * Copyright (C) Siddhesh suthar (Qt Quick port) * * Authors: * Pascal Georges (pascal.georges1@free.fr) (GTK+ version) * Holger Kaelberer (Qt Quick port of imageid) * Siddhesh suthar (Qt Quick port) * Bruno Coudoin (Integration Lang dataset) * * 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 "lang.js" as Activity import "quiz.js" as QuizActivity Item { id: quiz opacity: 0 property alias background: background property alias bonus: bonus property alias score: score property alias wordImage: wordImage property alias imageFrame: imageFrame property alias wordListModel: wordListModel property alias wordListView: wordListView property alias parser: parser property var goodWord property bool horizontalLayout: background.width >= background.height property bool buttonsBlocked: false function init(loadedItems_, wordList_, mode_) { opacity = 1 loadedItems_.forceActiveFocus() return QuizActivity.init(loadedItems_, wordList_, mode_) } onGoodWordChanged: Activity.playWord(goodWord.voice) Behavior on opacity { PropertyAnimation { duration: 200 } } Image { id: background source: "qrc:/gcompris/src/activities/lang/resource/imageid-bg.svg" fillMode: Image.PreserveAspectCrop sourceSize.width: Math.max(parent.width, parent.height) height: parent.height anchors.fill: parent property bool keyNavigation: false Keys.enabled: !quiz.buttonsBlocked Keys.onEscapePressed: { imageReview.start() } Keys.onRightPressed: { keyNavigation = true wordListView.incrementCurrentIndex() } Keys.onLeftPressed: { keyNavigation = true wordListView.decrementCurrentIndex() } Keys.onDownPressed: { keyNavigation = true wordListView.incrementCurrentIndex() } Keys.onUpPressed: { keyNavigation = true wordListView.decrementCurrentIndex() } Keys.onSpacePressed: { keyNavigation = true wordListView.currentItem.children[1].pressed() } Keys.onEnterPressed: { keyNavigation = true wordListView.currentItem.children[1].pressed() } Keys.onReturnPressed: { keyNavigation = true wordListView.currentItem.children[1].pressed() } Keys.onReleased: { if (event.key === Qt.Key_Back) { event.accepted = true imageReview.start() } } JsonParser { id: parser onError: console.error("Lang: Error parsing json: " + msg); } ListModel { id: wordListModel } Grid { id: gridId columns: quiz.horizontalLayout ? 2 : 1 - spacing: 10 * ApplicationInfo.ratio + spacing: 0.5 * ApplicationInfo.ratio anchors.fill: parent anchors.margins: 10 * ApplicationInfo.ratio Item { width: quiz.horizontalLayout ? background.width * 0.40 : background.width - gridId.anchors.margins * 2 height: quiz.horizontalLayout ? background.height - bar.height : (background.height - bar.height) * 0.4 Image { id: imageFrame anchors { horizontalCenter: parent.horizontalCenter verticalCenter: parent.verticalCenter } source: "qrc:/gcompris/src/activities/lang/resource/imageid_frame.svg" sourceSize.width: quiz.horizontalLayout ? parent.width * 0.7 : quiz.width - repeatItem.width - score.width - 50 * ApplicationInfo.ratio z: 11 visible: QuizActivity.mode !== 3 Image { id: wordImage // Images are not svg width: Math.min(parent.width, parent.height) * 0.9 height: width anchors.centerIn: parent property string nextSource function changeSource(nextSource_) { nextSource = nextSource_ animImage.start() } SequentialAnimation { id: animImage PropertyAnimation { target: wordImage property: "opacity" to: 0 duration: 100 } PropertyAction { target: wordImage property: "source" value: wordImage.nextSource } PropertyAnimation { target: wordImage property: "opacity" to: 1 duration: 100 } } MouseArea { anchors.fill: parent onClicked: Activity.playWord(goodWord.voice) } } } } ListView { id: wordListView width: quiz.horizontalLayout ? background.width * 0.55 : background.width - gridId.anchors.margins * 2 height: quiz.horizontalLayout ? background.height - bar.height : (background.height - bar.height) * 0.60 - spacing: 10 * ApplicationInfo.ratio + spacing: 2 * ApplicationInfo.ratio orientation: Qt.Vertical verticalLayoutDirection: ListView.TopToBottom interactive: false model: wordListModel highlight: Rectangle { width: wordListView.width height: wordListView.buttonHeight color: "lightsteelblue" radius: 5 visible: background.keyNavigation - y: wordListView.currentItem.y + y: wordListView.currentItem ? wordListView.currentItem.y : 0 Behavior on y { SpringAnimation { spring: 3 damping: 0.2 } } } highlightFollowsCurrentItem: false focus: true keyNavigationWraps: true property int buttonHeight: height / wordListModel.count * 0.9 delegate: Item { id: wordListViewDelegate width: wordListView.width height: wordListView.buttonHeight anchors.right: parent.right anchors.left: parent.left Image { id: wordImageQuiz width: height height: wordListView.buttonHeight + mipmap: true source: image z: 7 fillMode: Image.PreserveAspectFit anchors.leftMargin: 5 * ApplicationInfo.ratio visible: (QuizActivity.mode == 1) ? true : false // hide images after first mini game } AnswerButton { id: wordRectangle width: parent.width * 0.6 height: wordListView.buttonHeight textLabel: translatedTxt anchors.left: wordImageQuiz.left anchors.right: parent.right blockAllButtonClicks: quiz.buttonsBlocked onPressed: quiz.buttonsBlocked = true isCorrectAnswer: translatedTxt === quiz.goodWord.translatedTxt onIncorrectlyPressed: { // push the error to have it asked again QuizActivity.remainingWords.unshift(quiz.goodWord); QuizActivity.nextSubLevelQuiz(); } onCorrectlyPressed: { QuizActivity.nextSubLevelQuiz(); } } } } } BarButton { id: repeatItem source: "qrc:/gcompris/src/core/resource/bar_repeat.svg"; sourceSize.width: 80 * ApplicationInfo.ratio z: 12 anchors { top: parent.top left: parent.left margins: 10 * ApplicationInfo.ratio } onClicked: Activity.playWord(goodWord.voice) Behavior on opacity { PropertyAnimation { duration: 200 } } } Score { id: score parent: quiz - anchors.bottom: undefined - anchors.bottomMargin: 10 * ApplicationInfo.ratio - anchors.right: parent.right - anchors.rightMargin: 10 * ApplicationInfo.ratio - anchors.top: parent.top } Bonus { id: bonus onWin: imageReview.nextMiniGame() } } } diff --git a/src/activities/lang/SpellIt.qml b/src/activities/lang/SpellIt.qml index 16bc06b35..2ab8f2917 100644 --- a/src/activities/lang/SpellIt.qml +++ b/src/activities/lang/SpellIt.qml @@ -1,302 +1,297 @@ /* GCompris - SpellIt.qml * * Copyright (C) Siddhesh suthar (Qt Quick port) * * Authors: * Pascal Georges (pascal.georges1@free.fr) (GTK+ version) * Holger Kaelberer (Qt Quick port of imageid) * Siddhesh suthar (Qt Quick port) * Bruno Coudoin (Integration Lang dataset) * * 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 "lang.js" as Activity import "spell_it.js" as SpellActivity Item { id: spellIt opacity: 0 property alias background: background property alias wordImage: wordImage property alias imageFrame: imageFrame property alias hintTextbg: hintTextbg property alias hintText: hintText property alias parser: parser property alias answerbg: answerbg property alias answer: answer property alias ok: ok property alias okMouseArea: okMouseArea property alias bonus: bonus property alias keyboard: keyboard property alias score: score property var goodWord property int goodWordIndex property int maximumLengthAnswer function init(loadedItems_, wordList_, mode_) { opacity = 1 return SpellActivity.init(loadedItems_, wordList_, mode_); } onGoodWordChanged: Activity.playWord(goodWord.voice) Behavior on opacity { PropertyAnimation { duration: 200 } } Keys.onEscapePressed: { imageReview.start() } Image { id: background source: "qrc:/gcompris/src/activities/lang/resource/imageid-bg.svg" fillMode: Image.PreserveAspectCrop sourceSize.width: Math.max(parent.width, parent.height) height: parent.height property bool horizontalLayout: background.width >= background.height JsonParser { id: parser onError: console.error("Lang: Error parsing json: " + msg); } Rectangle { id: hintTextbg x: hintText.x -4 y: hintText.y -4 width: imageFrame.width height: hintText.height +4 color: "#5090ff" border.color: "#000000" border.width: 2 radius: 16 anchors.top: parent.top anchors.bottom: imageFrame.top anchors.left: imageFrame.left anchors.bottomMargin: 5 GCText { id: hintText text: "" fontSize: largeSize font.weight: Font.DemiBold width: parent.width horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter color: "white" wrapMode: Text.WordWrap property string nextHint function changeHint(nextHint_) { nextHint = nextHint_ animHint.start() } SequentialAnimation { id: animHint PropertyAnimation { target: hintText property: "opacity" to: 0 duration: 100 } PropertyAction { target: hintText property: "text" value: ""+ hintText.nextHint } PropertyAnimation { target: hintText property: "opacity" to: 1 duration: 100 } } } } Image { id: imageFrame source: "qrc:/gcompris/src/activities/lang/resource/imageid_frame.svg" sourceSize.width: background.horizontalLayout ? parent.width * 0.9 : parent.height * 1.2 width: background.width * 0.55 height: (background.height - hintTextbg.height - answerbg.height - keyboard.height - bar.height) * 0.8 anchors { horizontalCenter: background.horizontalCenter top: background.top topMargin: (background.height) * 0.15 } z: 11 Image { id: wordImage // Images are not svg width: Math.min(parent.width, parent.height) * 0.9 height: width anchors.centerIn: parent property string nextSource function changeSource(nextSource_) { nextSource = nextSource_ animImage.start() } SequentialAnimation { id: animImage PropertyAnimation { target: wordImage property: "opacity" to: 0 duration: 100 } PropertyAction { target: wordImage property: "source" value: wordImage.nextSource } PropertyAnimation { target: wordImage property: "opacity" to: 1 duration: 100 } } MouseArea { anchors.fill: parent onClicked: { Activity.playWord(goodWord.voice) } } } } Rectangle { id: answerbg x: answer.x -4 y: answer.y -4 width: imageFrame.width height: answer.height +4 color: "#5090ff" border.color: "#000000" border.width: 2 radius: 16 anchors { top: imageFrame.bottom left: imageFrame.left topMargin: 20* ApplicationInfo.ratio } TextInput { id: answer width: hintTextbg.width height: hintTextbg.height color: "white" cursorVisible: true focus: false activeFocusOnPress: !ApplicationInfo.isMobile visible: true horizontalAlignment: TextInput.AlignHCenter verticalAlignment: TextInput.AlignVCenter font.pointSize: hintText.pointSize font.weight: Font.DemiBold font.family: GCSingletonFontLoader.fontLoader.name font.capitalization: ApplicationSettings.fontCapitalization font.letterSpacing: ApplicationSettings.fontLetterSpacing maximumLength: maximumLengthAnswer onAccepted: { okMouseArea.clicked(okMouseArea) } } } Image { id: ok source:"qrc:/gcompris/src/core/resource/bar_ok.svg" sourceSize.width: 70 * ApplicationInfo.ratio fillMode: Image.PreserveAspectFit anchors { top: imageFrame.bottom - topMargin: 10* ApplicationInfo.ratio - left: imageFrame.right - leftMargin: 10* ApplicationInfo.ratio - right: parent.right + topMargin: 10 + right: imageFrame.left + rightMargin: parent.width * 0.06 + } MouseArea { id: okMouseArea anchors.fill: parent hoverEnabled: true onEntered: ok.scale = 1.1 onClicked: { SpellActivity.checkAnswer(answer.text) } onExited: ok.scale = 1 } } Bonus { id: bonus onWin: imageReview.nextMiniGame() } } BarButton { id: repeatItem source: "qrc:/gcompris/src/core/resource/bar_repeat.svg"; sourceSize.width: 80 * ApplicationInfo.ratio z: 12 anchors { top: parent.top left: parent.left margins: 10 * ApplicationInfo.ratio } onClicked: Activity.playWord(goodWord.voice) Behavior on opacity { PropertyAnimation { duration: 200 } } } Score { id: score - anchors.bottom: undefined - anchors.bottomMargin: 10 * ApplicationInfo.ratio - anchors.right: parent.right - anchors.rightMargin: 10 * ApplicationInfo.ratio - anchors.top: parent.top } VirtualKeyboard { id: keyboard parent: keyboardArea anchors.bottom: undefined anchors.horizontalCenter: undefined width: parent.width visible: ApplicationSettings.isVirtualKeyboard onKeypress: SpellActivity.processKeyPress(text) onError: console.log("VirtualKeyboard error: " + msg); } }