diff --git a/src/activities/lang/Lang.qml b/src/activities/lang/Lang.qml index e509cbe9c..ea340e570 100644 --- a/src/activities/lang/Lang.qml +++ b/src/activities/lang/Lang.qml @@ -1,563 +1,575 @@ /* GCompris - lang.qml * * Copyright (C) Holger Kaelberer (Qt Quick port of imageid) * * 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.1 import GCompris 1.0 import QtGraphicalEffects 1.0 import "../../core" import "lang.js" as Activity import "quiz.js" as QuizActivity import "spell_it.js" as SpellActivity import "qrc:/gcompris/src/core/core.js" as Core ActivityBase { id: activity onStart: focus = true onStop: {} pageComponent: Image { id: background source: "qrc:/gcompris/src/activities/lang/resource/imageid-bg.svg" fillMode: Image.PreserveAspectCrop sourceSize.width: parent.width property bool horizontalLayout: background.width > background.height property bool keyNavigation: false readonly property string wordsResource: "data2/words/words.rcc" property bool englishFallback: false property bool downloadWordsNeeded: false //system locale by defauls property string locale: "system" signal start signal stop signal voiceError signal voiceDone 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 score: score property alias wordImage: wordImage property alias imageFrame: imageFrame property alias wordTextbg: wordTextbg property alias wordText: wordText property alias categoryTextbg: categoryTextbg property alias categoryText: categoryText property alias previousWordButton: previousWordButton property alias parser: parser property alias repeatItem: repeatItem property alias keyboard: keyboard property alias menuModel: menu_screen.menuModel property alias menu_screen: menu_screen property variant goodWord property int goodWordIndex property alias englishFallbackDialog: englishFallbackDialog property alias miniGameLoader: miniGameLoader property alias locale: background.locale - property int currentProgress: 0 + + function checkWordExistence(wordForCheck) { + if(!activity.audioVoices.fileExists(ApplicationInfo.getAudioFilePath(wordForCheck.voice))) + return false + else + return true + } function playWord() { if(!activity.audioVoices.fileExists(ApplicationInfo.getAudioFilePath(goodWord.voice))) { voiceError(); } else { activity.audioVoices.clearQueue() if (!activity.audioVoices.append(ApplicationInfo.getAudioFilePath(goodWord.voice))) voiceError(); } } onGoodWordChanged: playWord() } function handleResourceRegistered(resource) { if (resource == wordsResource) Activity.start(items); } onStart: { Activity.init(items) items.menu_screen.visible = true repeatItem.visible = false keyNavigation = false activity.audioVoices.error.connect(voiceError) activity.audioVoices.done.connect(voiceDone) // check for words.rcc: if (DownloadManager.isDataRegistered("words")) { // words.rcc is already registered -> start right away Activity.start(items); } else if(DownloadManager.haveLocalResource(wordsResource)) { // words.rcc is there, but not yet registered -> updateResource DownloadManager.resourceRegistered.connect(handleResourceRegistered); DownloadManager.updateResource(wordsResource); } else { // words.rcc has not been downloaded yet -> ask for download downloadWordsNeeded = true } } onStop: { DownloadManager.resourceRegistered.disconnect(handleResourceRegistered); Activity.stop() } JsonParser { id: parser onError: console.error("lang: Error parsing json: " + msg); } Rectangle { id: categoryTextbg x: categoryText.x -4 y: categoryText.y -4 width: imageFrame.width height: categoryText.height +4 color: "#5090ff" border.color: "#000000" border.width: 2 radius: 16 anchors.bottom: imageFrame.top anchors.left: imageFrame.left anchors.bottomMargin: 20 GCText { id: categoryText text: "" fontSize: largeSize font.weight: Font.DemiBold width: parent.width horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter color: "white" wrapMode: Text.WordWrap property string nextCategory function changeCategory(nextCategory_) { nextCategory = nextCategory_ animCategory.start() } SequentialAnimation { id: animCategory PropertyAnimation { target: categoryText property: "opacity" to: 0 duration: 100 } PropertyAction { target: categoryText property: "text" value: "Category: "+ categoryText.nextCategory } PropertyAnimation { target: categoryText property: "opacity" to: 1 duration: 100 } } } } Image { id: imageFrame width: background.width * 0.55 height: background.horizontalLayout ? (background.height - bar.height)* 0.7 : (background.height - bar.height) * 0.4 anchors { horizontalCenter: background.horizontalCenter top: background.top topMargin: (background.height - bar.height) * 0.15 } source: "qrc:/gcompris/src/activities/lang/resource/imageid_frame.svg" sourceSize.width: background.horizontalLayout ? parent.width * 0.9 : parent.height * 1.2 z: 11 Image { id: wordImage sourceSize.width: parent.width * 0.6 anchors { centerIn: parent margins: 0.05 + parent.width } 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 } } } Image { id: previousWordButton source: "qrc:/gcompris/src/core/resource/bar_previous.svg"; sourceSize.width: 30 * 1.2 * ApplicationInfo.ratio anchors { right: parent.left rightMargin: 30 top: parent.top topMargin: parent.height/2 - previousWordButton.height/2 } MouseArea { id: previousWordButtonArea anchors.fill: parent onClicked: Activity.prevSubLevel() } } 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: Activity.nextSubLevel(); } } } Rectangle { id: wordTextbg 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 anchors.left: imageFrame.left anchors.topMargin: 20 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 } } } } MenuScreen{ id: menu_screen } onVoiceDone: repeatItem.visible = true onVoiceError: repeatItem.visible = false 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: { //items.playWord() if(Activity.currentMiniGame ==0) items.playWord() else Activity.loadedItems.playWord(); } } DialogHelp { id: dialogHelp onClose: home() } Bar { id: bar anchors.bottom: keyboard.top - content: BarEnumContent { value: help | home | level | config} + content: BarEnumContent { value: + menu_screen.visible ? help | home | level | config + : help | home | level } onHelpClicked: { displayDialog(dialogHelp) } onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() // onHomeClicked: activity.home() onHomeClicked: { + if(Activity.savedProgress[Activity.currentLevel] < Activity.currentProgress[Activity.currentLevel]) + Activity.savedProgress[Activity.currentLevel] = Activity.currentProgress[Activity.currentLevel] + console.log("clicked on home and saved "+ Activity.savedProgress[Activity.currentLevel]) if(menu_screen.visible == false) { menu_screen.visible = true items.imageFrame.visible = false + items.score.visible = false level = 0 - if (Activity.loadedItems.visible) + if (Activity.loadedItems) Activity.loadedItems.visible = false } else { activity.home() } } onConfigClicked: { dialogActivityConfig.active = true dialogActivityConfig.setDefaultValues() displayDialog(dialogActivityConfig) } } Bonus { id: bonus Component.onCompleted: win.connect(Activity.nextLevel) } Loader { id: englishFallbackDialog sourceComponent: GCDialog { parent: activity.main message: qsTr("We are sorry, we don't have yet a translation for your language.") + " " + qsTr("GCompris is developed by the KDE community, you can translate GCompris by joining a translation team on %2").arg("http://l10n.kde.org/") + "

" + qsTr("We switched to English for this activity but you can select another language in the configuration dialog.") onClose: background.englishFallback = false } anchors.fill: parent focus: true active: background.englishFallback onStatusChanged: if (status == Loader.Ready) item.start() } Loader { id: downloadWordsDialog sourceComponent: GCDialog { parent: activity.main message: qsTr("The images for this activity are not yet installed.") button1Text: qsTr("Download the images") onClose: background.downloadWordsNeeded = false onButton1Hit: { DownloadManager.resourceRegistered.connect(handleResourceRegistered); DownloadManager.downloadResource(wordsResource) var downloadDialog = Core.showDownloadDialog(activity, {}); } } anchors.fill: parent focus: true active: background.downloadWordsNeeded onStatusChanged: if (status == Loader.Ready) item.start() } Score { id: score anchors.bottom: keyboard.top anchors.bottomMargin: 10 * ApplicationInfo.ratio anchors.right: parent.right anchors.rightMargin: 10 * ApplicationInfo.ratio anchors.top: undefined } Loader { id: miniGameLoader width: parent.width height: parent.height anchors.fill: parent asynchronous: false } VirtualKeyboard { id: keyboard anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter width: parent.width property bool visibleFlag: false visible: ApplicationSettings.isVirtualKeyboard && items.keyboard.visibleFlag onKeypress: SpellActivity.processKeyPress(text) onError: console.log("VirtualKeyboard error: " + msg); } DialogActivityConfig { id: dialogActivityConfig currentActivity: activity content: Component { Item { property alias localeBox: localeBox 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 width: 250 * ApplicationInfo.ratio label: qsTr("Select your locale") } } /* TODO handle this: GCDialogCheckBox { id: uppercaseBox width: 250 * ApplicationInfo.ratio text: qsTr("Uppercase only mode") checked: true onCheckedChanged: { print("uppercase changed") } } */ } } } onClose: home() onLoadData: { if(dataToSave && dataToSave["locale"]) { background.locale = dataToSave["locale"]; } } 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('.')) } dataToSave = {"locale": newLocale} background.locale = newLocale; // Restart the activity with new information if(oldLocale !== newLocale) { 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; } } } } } } diff --git a/src/activities/lang/MenuScreen.qml b/src/activities/lang/MenuScreen.qml index b88b25ce9..60be3c22d 100644 --- a/src/activities/lang/MenuScreen.qml +++ b/src/activities/lang/MenuScreen.qml @@ -1,192 +1,196 @@ /* GCompris - MenuScreen.qml * * Copyright (C) Holger Kaelberer (Qt Quick port of imageid) * * 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.1 import GCompris 1.0 import QtGraphicalEffects 1.0 -import QtQuick.Controls 1.3 +import QtQuick.Controls 1.2 import "../../core" import "lang.js" as Activity import "quiz.js" as QuizActivity import "spell_it.js" as SpellActivity import "qrc:/gcompris/src/core/core.js" as Core Image { id: menu_screen anchors.fill: parent fillMode: Image.PreserveAspectCrop source: Activity.baseUrl + "imageid-bg.svg" sourceSize.width: parent.width property alias menuModel: menuModel // Filters property bool horizontal: parent.width > parent.height property var currentActiveGrid: menuGrid property bool keyboardMode: false + Keys.onPressed: { if(event.key === Qt.Key_Space) { currentActiveGrid.currentItem.selectCurrentItem() } } Keys.onReleased: { keyboardMode = true event.accepted = false } Keys.onEnterPressed: currentActiveGrid.currentItem.selectCurrentItem(); Keys.onReturnPressed: currentActiveGrid.currentItem.selectCurrentItem(); Keys.onRightPressed: currentActiveGrid.moveCurrentIndexRight(); Keys.onLeftPressed: currentActiveGrid.moveCurrentIndexLeft(); Keys.onDownPressed: currentActiveGrid.moveCurrentIndexDown(); Keys.onUpPressed: currentActiveGrid.moveCurrentIndexUp(); // Activities property int iconWidth: 190 * ApplicationInfo.ratio property int iconHeight: 190 * ApplicationInfo.ratio property int levelCellWidth: horizontal ? background.width / Math.floor(background.width / iconWidth ): background.width / Math.floor(background.width / iconWidth) property int levelCellHeight: iconHeight * 1.5 ListModel { id: menuModel } GridView { id: menuGrid layer.enabled: true anchors { top: parent.top fill: parent // margins: 4 } width: background.width cellWidth: levelCellWidth cellHeight: levelCellHeight clip: true model: menuModel keyNavigationWraps: true property int spacing: 10 delegate: Item { id: delegateItem width: levelCellWidth - menuGrid.spacing height: levelCellHeight - menuGrid.spacing Rectangle { id: activityBackground width: levelCellWidth - menuGrid.spacing height: levelCellHeight - menuGrid.spacing anchors.horizontalCenter: parent.horizontalCenter color: "white" opacity: 0.5 } Image { + id: containerImage source: "qrc:/gcompris/data/"+ image; anchors.top: activityBackground.top anchors.horizontalCenter: parent.horizontalCenter sourceSize.height: iconHeight anchors.margins: 5 GCText { id: title anchors.top: parent.bottom anchors.horizontalCenter: parent.horizontalCenter horizontalAlignment: Text.AlignHCenter width: activityBackground.width fontSizeMode: Text.Fit minimumPointSize: 7 fontSize: regularSize elide: Text.ElideRight maximumLineCount: 2 wrapMode: Text.WordWrap text: name } // TODO : progress bar ProgressBar { id: progressLang anchors.top: title.bottom anchors.topMargin: ApplicationInfo.ratio * 4 anchors.horizontalCenter: parent.horizontalCenter width: activityBackground.width - maximumValue: wordCount * 5 + maximumValue: wordCount minimumValue: 0 - value: Activity.currentProgress[index] + value: Activity.savedProgress[index] +// value: Activity.getProgressStatus(index) orientation: Qt.Horizontal } } ParticleSystemStarLoader { id: particles anchors.fill: activityBackground } MouseArea { anchors.fill: activityBackground onClicked: selectCurrentItem() } function selectCurrentItem() { particles.burst(50) Activity.initLevel(index) + console.log("maximum Value " +progressLang.maximumValue) } } //delegate close highlight: Rectangle { width: levelCellWidth - menuGrid.spacing height: levelCellHeight - menuGrid.spacing color: "#AA41AAC4" border.width: 3 border.color: "black" visible: menu_screen.keyboardMode Behavior on x { SpringAnimation { spring: 2; damping: 0.2 } } Behavior on y { SpringAnimation { spring: 2; damping: 0.2 } } } } // grid view close Rectangle{ id: menusMask visible: false anchors.fill: menuGrid gradient: Gradient { GradientStop { position: 0.0; color: "#FFFFFFFF" } GradientStop { position: 0.92; color: "#FFFFFFFF" } GradientStop { position: 0.96; color: "#00FFFFFF"} } } layer.effect: OpacityMask { id: activitiesOpacity source: menuGrid maskSource: menusMask anchors.fill: menuGrid } } diff --git a/src/activities/lang/Quiz.qml b/src/activities/lang/Quiz.qml index 8714195c5..7986eab94 100644 --- a/src/activities/lang/Quiz.qml +++ b/src/activities/lang/Quiz.qml @@ -1,270 +1,273 @@ /* GCompris - Quiz.qml * * Copyright (C) 2014 Holger Kaelberer (Qt Quick port of imageid) * * * Authors: * Pascal Georges (pascal.georges1@free.fr) (GTK+ version) * Holger Kaelberer (Qt Quick port of imageid) * Bruno Coudoin (Integration Lang dataset) * Siddhesh suthar (Qt Quick port of lang) * * 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.1 import GCompris 1.0 import QtGraphicalEffects 1.0 import "../../core" import "lang.js" as Activity import "quiz.js" as QuizActivity import "spell_it.js" as SpellActivity import "qrc:/gcompris/src/core/core.js" as Core Item { id: quiz opacity: displayed ? 1 : 0 property bool displayed: index == 0 ? false : true // The opacity of quiz, set to 0 to disable property int index: 1 property alias background: background property alias bonus: bonus property alias wordImage: wordImage property alias imageFrame: imageFrame property alias wordListModel: wordListModel property alias wordListView: wordListView property alias parser: parser property variant goodWord property int goodWordIndex function init(items_, loadedItems_, wordList_, mode_) { loadedItems_.forceActiveFocus() QuizActivity.init(items_, loadedItems_, wordList_, mode_); } function checkWordExistence(wordForCheck) { if(!activity.audioVoices.fileExists(ApplicationInfo.getAudioFilePath(wordForCheck.voice))) return false else return true } function playWord() { if (!activity.audioVoices.append(ApplicationInfo.getAudioFilePath(goodWord.voice))) voiceError(); } onGoodWordChanged: playWord() Image { id: background source: "qrc:/gcompris/src/activities/lang/resource/imageid-bg.svg" fillMode: Image.PreserveAspectCrop sourceSize.width: parent.width height: parent.height property bool horizontalLayout: background.width > background.height property bool keyNavigation: false 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() } JsonParser { id: parser onError: console.error("Lang: Error parsing json: " + msg); } ListModel { id: wordListModel } Grid { id: gridId columns: horizontalLayout ? 2 : 1 spacing: 10 * ApplicationInfo.ratio anchors.fill: parent anchors.margins: 10 * ApplicationInfo.ratio Item { width: background.horizontalLayout ? background.width * 0.40 : background.width - gridId.anchors.margins * 2 height: background.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: background.horizontalLayout ? parent.width * 0.7 : parent.height * 1.2 z: 11 visible: QuizActivity.mode ==3 ? false : true Image { id: wordImage sourceSize.width: parent.width * 0.6 anchors { centerIn: parent margins: 0.05 + parent.width } 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 } } } } } ListView { id: wordListView width: background.horizontalLayout ? background.width * 0.55 : background.width - gridId.anchors.margins * 2 height: background.horizontalLayout ? background.height - bar.height : (background.height - bar.height) * 0.60 spacing: 10 * 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 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: wordListView.width * 0.4 height: wordListView.buttonHeight source: image z: 7 fillMode: Image.PreserveAspectFit anchors.leftMargin: 10* ApplicationInfo.ratio visible: (QuizActivity.mode==1) ? true : false // to hide images after first mini game } AnswerButton { id: wordRectangle width: parent.width * 0.6 height: wordListView.buttonHeight textLabel: word anchors.left: wordImageQuiz.left anchors.leftMargin: 20* ApplicationInfo.ratio anchors.right: parent.right isCorrectAnswer: word === QuizActivity.quizItems.goodWord.translatedTxt onIncorrectlyPressed: { QuizActivity.badWordSelected(goodWordIndex); QuizActivity.nextSubLevelQuiz(); } - onCorrectlyPressed: QuizActivity.nextSubLevelQuiz(); + onCorrectlyPressed: { + ++Activity.currentProgress[Activity.currentLevel] + QuizActivity.nextSubLevelQuiz(); + } } } } } Bonus { id: bonus onWin: Activity.nextMiniGame() } } } diff --git a/src/activities/lang/SpellIt.qml b/src/activities/lang/SpellIt.qml index 814192a34..211607372 100644 --- a/src/activities/lang/SpellIt.qml +++ b/src/activities/lang/SpellIt.qml @@ -1,279 +1,283 @@ /* GCompris - Spell_it.qml * * Copyright (C) 2014 Holger Kaelberer (Qt Quick port of imageid) * * * Authors: * Pascal Georges (pascal.georges1@free.fr) (GTK+ version) * Holger Kaelberer (Qt Quick port of imageid) * Bruno Coudoin (Integration Lang dataset) * Siddhesh suthar (Qt Quick port of lang) * * 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.1 import GCompris 1.0 import QtGraphicalEffects 1.0 import "../../core" import "lang.js" as Activity import "quiz.js" as QuizActivity import "spell_it.js" as SpellActivity import "qrc:/gcompris/src/core/core.js" as Core Item { id: spellIt opacity: displayed ? 1 : 0 property bool displayed: index == 0 ? false : true // The opacity of spell it, set to 0 to disable property int index: 1 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 variant goodWord property int goodWordIndex function init(items_, loadedItems_, wordList_, mode_) { SpellActivity.init(items_, loadedItems_, wordList_, mode_); } function playWord() { if(!activity.audioVoices.fileExists(ApplicationInfo.getAudioFilePath(goodWord.voice))) { console.log("file does not exist so don't display the repeatItem") voiceError(); } else { activity.audioVoices.clearQueue() if (!activity.audioVoices.append(ApplicationInfo.getAudioFilePath(goodWord.voice))) voiceError(); } } onGoodWordChanged: playWord() Image { id: background source: "qrc:/gcompris/src/activities/lang/resource/imageid-bg.svg" fillMode: Image.PreserveAspectCrop sourceSize.width: parent.width 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.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 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 width: background.width * 0.55 height: background.horizontalLayout ? (background.height - Activity.items.bar.height - Activity.items.keyboard.height)* 0.7 : (background.height - Activity.items.bar.height) * 0.4 anchors { horizontalCenter: background.horizontalCenter top: background.top topMargin: (background.height - Activity.items.bar.height - Activity.items.keyboard.height) * 0.15 } source: "qrc:/gcompris/src/activities/lang/resource/imageid_frame.svg" sourceSize.width: background.horizontalLayout ? parent.width * 0.9 : parent.height * 1.2 z: 11 Image { id: wordImage sourceSize.width: parent.width * 0.6 anchors { centerIn: parent margins: 0.05 + parent.width } 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 } } } } 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: true visible: true horizontalAlignment: TextInput.AlignHCenter verticalAlignment: TextInput.AlignVCenter font.pointSize: hintText.fontSize onAccepted: { answer.forceActiveFocus() okMouseArea.clicked(okMouseArea) } } } Image { id: ok source:"qrc:/gcompris/src/core/resource/bar_ok.svg" width: answerbg.width * 1.2 height: answerbg.height fillMode: Image.PreserveAspectFit anchors { top: imageFrame.bottom topMargin: 10* ApplicationInfo.ratio left: imageFrame.right leftMargin: 10* ApplicationInfo.ratio right: parent.right } MouseArea { id: okMouseArea anchors.fill: parent hoverEnabled: true onEntered: ok.scale = 1.1 - onClicked:SpellActivity.checkAnswer(answer.text) + onClicked: { + var check = SpellActivity.checkAnswer(answer.text) + if (check) + ++Activity.currentProgress[Activity.currentLevel] + } onExited: ok.scale = 1 } } // VirtualKeyboard { // id: keyboard // x: 0 // anchors.bottom: parent.bottom // anchors.horizontalCenter: parent.horizontalCenter // width: parent.width // onKeypress: answer.insert(answer.length,text) // onError: console.log("VirtualKeyboard error: " + msg); // } Bonus { id: bonus onWin: Activity.nextMiniGame() } } } diff --git a/src/activities/lang/lang.js b/src/activities/lang/lang.js index 21b508969..6e98f6db8 100644 --- a/src/activities/lang/lang.js +++ b/src/activities/lang/lang.js @@ -1,227 +1,255 @@ /* GCompris - lang.js * * Copyright (C) 2014 Siddhesh suthar * * Authors: * Pascal Georges (pascal.georges1@free.fr) (GTK+ version) * 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 . */.pragma library .import QtQuick 2.0 as Quick .import GCompris 1.0 as GCompris .import "qrc:/gcompris/src/core/core.js" as Core .import "qrc:/gcompris/src/activities/lang/lang_api.js" as Lang .import "qrc:/gcompris/src/activities/lang/quiz.js" as QuizActivity .import "qrc:/gcompris/src/activities/lang/spell_it.js" as SpellActivity var currentLevel = 0; var currentSubLevel = 0; var level = null; var maxLevel; var maxSubLevel; var items; var baseUrl = "qrc:/gcompris/src/activities/lang/resource/"; var dataset = null; var lessons var menus var wordList var savedWordList var subLevelsLeft var currentProgress = [] +var savedProgress = [] +var modifiedWordCount = 0 // miniGames is list of miniGames // first element is Activity name, // second element is mode of miniGame // third element is the qml to load var miniGames = [["QuizActivity", 1,"Quiz.qml"], ["QuizActivity", 2,"Quiz.qml"], ["QuizActivity", 3,"Quiz.qml"], ["SpellActivity", 1,"SpellIt.qml"]]; var currentMiniGame var loadedItems function init(items_) { items = items_ maxLevel = 0 maxSubLevel = 0 currentLevel = 0 currentSubLevel = 0 } function start() { currentLevel = 0; currentSubLevel = 0; var locale = items.locale == "system" ? "$LOCALE" : items.locale dataset = Lang.load(items.parser, baseUrl, "words.json", "content-"+ locale +".json") // If dataset is empty, we try to load from short locale and if not present again, we switch to default one var localeUnderscoreIndex = locale.indexOf('_') if(!dataset) { var localeShort; // We will first look again for locale xx (without _XX if exist) if(localeUnderscoreIndex > 0) { localeShort = locale.substring(0, localeUnderscoreIndex) } else { localeShort = locale; } dataset = Lang.load(items.parser, baseUrl, "words.json", "content-"+localeShort+ ".json") } //If still dataset is empty then fallback to english if(!dataset) { // English fallback items.background.englishFallback = true dataset = Lang.load(items.parser, baseUrl, "words.json", "content-en.json") } else { items.background.englishFallback = false } lessons = Lang.getAllLessons(dataset) maxLevel = lessons.length - menus = Lang.getMenuModel(dataset) + menus = Lang.getMenuModel(dataset, items) items.menuModel.clear() + for (var j =0; j 0)) + currentProgress[j] = 0 + } + for (var k =0; k 0)) + savedProgress[k] = 0 + } + + items.menuModel.clear() items.menuModel.append(menus) - items.menu_screen.focus = true items.imageFrame.visible = false - - for (var k =0; k 0)) - currentProgress[k] = 0 - } + items.score.visible = false items.menu_screen.visible = true items.menu_screen.focus = true items.menu_screen.forceActiveFocus() -// initLevel(); + + // initLevel(); } function stop() { } function initLevel(currentLevel_) { currentLevel = currentLevel_ items.bar.level = currentLevel + 1; var currentLesson = lessons[currentLevel] wordList = Lang.getLessonWords(dataset, currentLesson); Core.shuffle(wordList); maxSubLevel = wordList.length; items.score.numberOfSubLevels = maxSubLevel; items.score.currentSubLevel = 1; + items.score.visible = true items.menu_screen.visible = false items.imageFrame.visible = true items.wordTextbg.visible = true items.wordText.visible = true items.categoryTextbg.visible = true items.categoryText.changeCategory(currentLesson.name); items.miniGameLoader.source = "" items.keyboard.visibleFlag = false currentMiniGame = 0 + currentProgress[currentLevel] = 0 subLevelsLeft = []; for(var i in wordList) { subLevelsLeft.push(i) // This is available in all editors. } initSubLevel() } function initSubLevel() { // initialize sublevel if(items.score.currentSubLevel == 1) items.previousWordButton.visible = false else items.previousWordButton.visible = true items.goodWord = wordList[items.score.currentSubLevel-1] items.wordImage.changeSource("qrc:/gcompris/data/" + items.goodWord.image) items.wordText.changeText(items.goodWord.translatedTxt) } function nextLevel() { + if(savedProgress[currentLevel] < currentProgress[currentLevel]) + savedProgress[currentLevel] = currentProgress[currentLevel] + console.log("clicked on next and saved "+ savedProgress[currentLevel]) + if(maxLevel <= ++currentLevel ) { currentLevel = 0 } initLevel(currentLevel); } function previousLevel() { + if(savedProgress[currentLevel] < currentProgress[currentLevel]) + savedProgress[currentLevel] = currentProgress[currentLevel] + console.log("clicked on prev and saved "+ savedProgress[currentLevel]) + if(--currentLevel < 0) { currentLevel = maxLevel - 1 } initLevel(currentLevel); } function nextSubLevel() { ++items.score.currentSubLevel; + ++currentProgress[currentLevel] + //make a change when progress exceedes previous one 2 0 + if(items.score.currentSubLevel == items.score.numberOfSubLevels+1) { + // saving progress after completion + savedProgress[currentLevel] = currentProgress[currentLevel] //here logic for starting quiz game nextMiniGame() } else { initSubLevel(); } } function prevSubLevel() { --items.score.currentSubLevel + --currentProgress[currentLevel] if( items.score.currentSubLevel <= 0) { //Do nothing } else { initSubLevel() } } //called by a miniGame when it is won function nextMiniGame() { if(currentMiniGame < miniGames.length) { var mode = miniGames[currentMiniGame][1]; var itemToLoad = miniGames[currentMiniGame][2]; // reloading the wordList var currentLesson = lessons[currentLevel] var wordList = Lang.getLessonWords(dataset, currentLesson); Core.shuffle(wordList); maxSubLevel = wordList.length items.miniGameLoader.source = itemToLoad; loadedItems = items.miniGameLoader.item // resetting the wordList length because it could have been spliced by quiz miniGame 3 if(currentMiniGame === 3) { items.score.numberOfSubLevels = wordList.length } // initiate the loaded item mini game loadedItems.init(items, loadedItems, wordList, mode) ++currentMiniGame; } else { nextLevel() } } + +function getProgressStatus(index) { + return savedProgress[index ] +} diff --git a/src/activities/lang/lang_api.js b/src/activities/lang/lang_api.js index fdaa89b88..009499138 100644 --- a/src/activities/lang/lang_api.js +++ b/src/activities/lang/lang_api.js @@ -1,135 +1,144 @@ /* GCompris - lang_api.js * * Copyright (C) 2014 Bruno Coudoin * * Authors: * Bruno Coudoin (bruno.coudoin@gcompris.net) * * 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 . */ .pragma library .import GCompris 1.0 as GCompris .import "qrc:/gcompris/src/core/core.js" as Core var contentText function validateDataset(levels) { return true; } function load(parser, baseUrl, datasetFilename, translationFilename) { var datasetUrl = baseUrl + "/" + datasetFilename; var dataset = parser.parseFromUrl(datasetUrl, validateDataset); if (dataset === null) { console.error("Lang: Invalid dataset, can't continue: " + datasetUrl); return; } dataset['contentText'] = loadContent(parser, GCompris.ApplicationInfo.getLocaleFilePath(baseUrl + "/" + translationFilename)) if(!dataset['contentText']) { return null } return dataset } function loadContent(parser, datasetUrl) { var dataset = parser.parseFromUrl(datasetUrl, validateDataset); if (dataset === null) { console.error("Lang: Invalid dataset, can't continue: " + datasetUrl); return; } return dataset } function getChapter(dataset, chapter) { return dataset[chapter] } // Return a datamodel for the chapter suitable for creating a chapter selector function getChapterModel(dataset) { var chapters = [] for (var c = 0; c < dataset.length; c++) { chapters.push( {'name': dataset[c].name, 'image': dataset[c].content[0].content[0].image, 'index': c }) } return chapters } function getLesson(dataset, chapter, lesson) { return chapter.content[lesson] } function getAllLessons(dataset) { var lessons = [] for (var c in dataset) { for (var l in dataset[c].content) { var lesson = getLesson(dataset, dataset[c], l) lessons.push(lesson) } } return lessons } function getLessonWords(dataset, lesson) { var wordList = lesson.content // Fill up the lesson with the translated text var allWords = [] for (var k in wordList) { var word = wordList[k] word['translatedTxt'] = getTextByAudio(dataset.contentText, word.description) if(word['translatedTxt']) allWords.push(word) } return allWords } function getTextByAudio(contentText, audio) { audio += ".ogg" return contentText[audio] } // Return a datamodel for the lessons for creating menu -function getMenuModel(dataset) { +function getMenuModel(dataset, items) { var menus = [] var levelCount = 0 //used for creating a straight indexing of levels var prevLevelcount = 0 for (var c in dataset) { for (var l in dataset[c].content) { levelCount = parseInt(l) + prevLevelcount var currentLesson = getLesson(dataset, dataset[c], l) + var tempWordList = getLessonWords(dataset, currentLesson); + var modifiedWordCount = tempWordList.length + + for (var m in tempWordList) { + // if voice is disabled then it doesn't affect maximum + if(!items.checkWordExistence (tempWordList[m]) ) { + --modifiedWordCount + } + } + menus.push( { 'name' : currentLesson.name, 'image': currentLesson.content[0].image, 'index': levelCount, - 'wordCount': currentLesson.content.length + 'wordCount': tempWordList.length * 4 + modifiedWordCount }) - } prevLevelcount = levelCount+ 1 } return menus } diff --git a/src/activities/lang/spell_it.js b/src/activities/lang/spell_it.js index 0d0054654..3326b6e21 100644 --- a/src/activities/lang/spell_it.js +++ b/src/activities/lang/spell_it.js @@ -1,195 +1,196 @@ /* GCompris - spell_it.js * * Copyright (C) 2014 Holger Kaelberer (Qt Quick port of imageid) * * * Authors: * Pascal Georges (pascal.georges1@free.fr) (GTK+ version) * Holger Kaelberer (Qt Quick port of imageid) * Bruno Coudoin (Integration Lang dataset) * Siddhesh suthar (Qt Quick port of lang) * * 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 . */ .pragma library .import QtQuick 2.0 as Quick .import GCompris 1.0 as GCompris .import "qrc:/gcompris/src/core/core.js" as Core .import "qrc:/gcompris/src/activities/lang/lang_api.js" as Lang var items; var spellItems; var spellWordList; var subLevelsLeft var currentSubLevel function init(items_, loadedItems_, spellWordList_, mode_) { items = items_ spellItems = loadedItems_ spellWordList = spellWordList_ console.log("new " + spellItems + " loaded") spellItems.answer.forceActiveFocus() initLevel() } function initLevel() { subLevelsLeft = []; for(var i in spellWordList) { subLevelsLeft.push(i) // This is available in all editors. } Core.shuffle(subLevelsLeft) items.score.currentSubLevel = 0 items.imageFrame.visible = false items.wordTextbg.visible = false items.categoryTextbg.visible = false currentSubLevel =0 /* populate VirtualKeyboard for mobile: * 1. for < 10 letters print them all in the same row * 2. for > 10 letters create 3 rows with equal amount of keys per row * if possible, otherwise more keys in the upper rows */ // first generate a map of needed letters var letters = []; for (var i = 0; i < spellWordList.length; i++) { var currentWord = spellWordList[i].translatedTxt; for (var j = 0; j < currentWord.length; j++) { var letter = currentWord.charAt(j); if(letters.indexOf(letter) === -1) letters.push(currentWord.charAt(j)); } } letters.sort(); // generate layout from letter map var layout = []; var row = 0; var offset = 0; while (offset < letters.length-1) { var cols = letters.length <= 10 ? letters.length : (Math.ceil((letters.length-offset) / (3 - row))); layout[row] = new Array(); for (var j = 0; j < cols; j++) layout[row][j] = { label: letters[j+offset] }; offset += j; row++; } layout[row-1][offset] = { label: items.keyboard.backspace }; items.keyboard.layout = layout; items.keyboard.visibleFlag = true; initSubLevel() } function initSubLevel() { if(items.score.currentSubLevel < items.score.numberOfSubLevels) items.score.currentSubLevel = items.score.currentSubLevel + 1; else items.score.visible = false spellItems.goodWord = spellWordList[items.score.currentSubLevel-1] spellItems.wordImage.changeSource("qrc:/gcompris/data/" + spellItems.goodWord.image) spellItems.hintText.changeHint(spellItems.goodWord.translatedTxt[0]) spellItems.hintText.visible = true spellItems.answer.text = "" } function nextSubLevel() { if(items.score.currentSubLevel == items.score.numberOfSubLevels ) { spellItems.displayed = false spellItems.bonus.good("smiley") } else { initSubLevel(); } } function checkAnswer(answer_) { if(spellItems.goodWord.translatedTxt == answer_) { nextSubLevel() + return true } else { badWordSelected(currentSubLevel-1,answer_) } } // Append to the front of queue of words for the sublevel the error function badWordSelected(wordIndex,answer) { if (subLevelsLeft[0] != wordIndex) subLevelsLeft.unshift(wordIndex); provideHint(answer) } //function to construct hint based on the answer entered by user function provideHint(answer_) { var answer = answer_ var hint = "" var firstIncorrectIndex = 0 for (var i=0 ; i< spellItems.goodWord.translatedTxt.length; i++) { var goodChar = spellItems.goodWord.translatedTxt[i] //skipping hint if the suggestion is a space if( goodChar == " ") { hint = hint + " " continue } if( answer[i] == goodChar) { hint = hint + goodChar } else { if(firstIncorrectIndex == 0) { hint = hint + goodChar firstIncorrectIndex = i } else { hint = hint + "." } } } spellItems.hintText.changeHint(hint) spellItems.hintText.visible = true } // to handle virtual key board key press events function processKeyPress(text_){ var answer = spellItems.answer var text = text_ if ( text == items.keyboard.backspace) { backspace(answer) return } answer.insert(answer.length,text) } function backspace(answer) { answer.text = answer.text.slice(0, -1) if(answer.text.length === 0) { answer.text = "" } else { checkAnswer(answer.text) } }