diff --git a/src/activities/lang/Lang.qml b/src/activities/lang/Lang.qml index 42493c2b6..cc92bde62 100644 --- a/src/activities/lang/Lang.qml +++ b/src/activities/lang/Lang.qml @@ -1,285 +1,299 @@ /* 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.1 import GCompris 1.0 import QtGraphicalEffects 1.0 import "../../core" import "lang.js" as Activity 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 readonly property string wordsResource: "data2/words/words.rcc" property bool englishFallback: false property bool downloadWordsNeeded: false 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 GCAudio audioVoices: activity.audioVoices property alias background: background property alias bar: bar property alias imageReview: imageReview property alias parser: parser property alias menuModel: menuScreen.menuModel property var wordList property alias menuScreen: menuScreen property alias englishFallbackDialog: englishFallbackDialog property string locale: 'system' property alias dialogActivityConfig: dialogActivityConfig + property variant categoriesTranslations: activity.categoriesTranslations } function handleResourceRegistered(resource) { if (resource == wordsResource) Activity.start(); } onStart: { Activity.init(items) dialogActivityConfig.getInitialConfiguration() 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(); } else if(DownloadManager.haveLocalResource(wordsResource)) { // words.rcc is there -> register old file first if (DownloadManager.registerResource(wordsResource)) Activity.start(items); else // could not register the old data -> react to a possible update DownloadManager.resourceRegistered.connect(handleResourceRegistered); // then try to update in the background DownloadManager.updateResource(wordsResource); } else { // words.rcc has not been downloaded yet -> ask for download downloadWordsNeeded = true } } onStop: { DownloadManager.resourceRegistered.disconnect(handleResourceRegistered); dialogActivityConfig.saveDatainConfiguration() Activity.stop() } JsonParser { id: parser onError: console.error("lang: Error parsing json: " + msg); } MenuScreen { id: menuScreen } ImageReview { id: imageReview } DialogHelp { id: dialogHelp onClose: home() } Bar { id: bar anchors.bottom: keyboardArea.top content: BarEnumContent { value: menuScreen.started ? help | home | config : help | home } onHelpClicked: { displayDialog(dialogHelp) } onHomeClicked: { // if we don't have the images, we leave the activity on home() if(DownloadManager.haveLocalResource(wordsResource) && !items.menuScreen.started && !items.imageReview.started) // We're in a mini game, start imageReview items.imageReview.start() else if(items.imageReview.started) // Leave imageReview Activity.launchMenuScreen() else home() } onConfigClicked: { dialogActivityConfig.active = true dialogActivityConfig.setDefaultValues() displayDialog(dialogActivityConfig) } } // This is a stop to hold the virtual keyboard from a mini game Row { id: keyboardArea anchors.bottom: parent.bottom width: parent.width } 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() } 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: dialogActivityConfig.width label: qsTr("Select your locale") } } } } } onLoadData: { if(!dataToSave) return if(dataToSave['locale']) { items.locale = dataToSave["locale"]; } } onSaveData: { // Save the lessons status on the current locale var oldLocale = items.locale dataToSave[ApplicationInfo.getVoicesLocale(items.locale)] = Activity.lessonsToSavedProperties(dataToSave) if(!dialogActivityConfig.loader.item) return 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 items.locale = newLocale; // Restart the activity with new information if(oldLocale !== newLocale) { Activity.stop() Activity.start(); } } function setDefaultValues() { var localeUtf8 = items.locale; if(items.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; } } } onClose: home() } } + property variant categoriesTranslations: {"other": qsTr("other"), + "action": qsTr("action"), "adjective": qsTr("adjective"), + "color": qsTr("color"), "number": qsTr("number"), + "people": qsTr("people"), "bodyparts": qsTr("bodyparts"), + "clothes": qsTr("clothes"), "emotion": qsTr("emotion"), + "job": qsTr("job"), "sport": qsTr("sport"), + "nature": qsTr("nature"), "animal": qsTr("animal"), + "fruit": qsTr("fruit"), "plant": qsTr("plant"), + "vegetables": qsTr("vegetables"), "object": qsTr("object"), + "construction": qsTr("construction"), + "furniture": qsTr("furniture"), "houseware": qsTr("houseware"), + "tool": qsTr("tool"), "food": qsTr("food")} + } diff --git a/src/activities/lang/MenuScreen.qml b/src/activities/lang/MenuScreen.qml index b8c1d36c9..f5b5ab706 100644 --- a/src/activities/lang/MenuScreen.qml +++ b/src/activities/lang/MenuScreen.qml @@ -1,242 +1,242 @@ /* GCompris - MenuScreen.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.1 import GCompris 1.0 import QtGraphicalEffects 1.0 import QtQuick.Controls 1.2 import "../../core" import "lang.js" as Activity Image { id: menuScreen anchors.fill: parent fillMode: Image.PreserveAspectCrop source: Activity.baseUrl + "imageid-bg.svg" sourceSize.width: parent.width opacity: 0 property alias menuModel: menuModel property bool keyboardMode: false property bool started: opacity == 1 Behavior on opacity { PropertyAnimation { duration: 200 } } function start() { focus = true forceActiveFocus() menuGrid.currentIndex = 0 opacity = 1 } function stop() { focus = false opacity = 0 } Keys.onEscapePressed: { home() } Keys.onPressed: { if(event.key === Qt.Key_Space) { menuGrid.currentItem.selectCurrentItem() event.accepted = true } if(event.key === Qt.Key_Enter) { menuGrid.currentItem.selectCurrentItem() event.accepted = true } if(event.key === Qt.Key_Return) { menuGrid.currentItem.selectCurrentItem() event.accepted = true } if(event.key === Qt.Key_Left) { menuGrid.moveCurrentIndexLeft() event.accepted = true } if(event.key === Qt.Key_Right) { menuGrid.moveCurrentIndexRight() event.accepted = true } if(event.key === Qt.Key_Up) { menuGrid.moveCurrentIndexUp() event.accepted = true } if(event.key === Qt.Key_Down) { menuGrid.moveCurrentIndexDown() event.accepted = true } } Keys.onReleased: { keyboardMode = true event.accepted = false } // Activities property int iconWidth: 180 * ApplicationInfo.ratio property int iconHeight: 180 * ApplicationInfo.ratio property int levelCellWidth: background.width / Math.floor(background.width / iconWidth ) property int levelCellHeight: iconHeight * 1.4 ListModel { id: menuModel } GridView { id: menuGrid layer.enabled: true anchors { fill: parent bottomMargin: bar.height } 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 property string sectionName: name 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 width: iconWidth 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 + text: Activity.items.categoriesTranslations[name] } ProgressBar { id: progressLang anchors.top: title.bottom anchors.topMargin: ApplicationInfo.ratio * 4 anchors.horizontalCenter: parent.horizontalCenter width: activityBackground.width height: 14 * ApplicationInfo.ratio maximumValue: wordCount minimumValue: 0 value: progress orientation: Qt.Horizontal } } ParticleSystemStarLoader { id: particles anchors.fill: activityBackground } MouseArea { anchors.fill: activityBackground enabled: menuScreen.opacity == 1 onClicked: selectCurrentItem() } function selectCurrentItem() { particles.burst(50) Activity.initLevel(index) } Image { source: "qrc:/gcompris/src/activities/menu/resource/" + ( favorite ? "all.svg" : "all_disabled.svg" ); anchors { top: parent.top right: parent.right rightMargin: 4 * ApplicationInfo.ratio } sourceSize.width: iconWidth * 0.25 visible: ApplicationSettings.sectionVisible MouseArea { anchors.fill: parent onClicked: { menuModel.get(index)['favorite'] = !menuModel.get(index)['favorite'] } } } } //delegate close highlight: Rectangle { width: levelCellWidth - menuGrid.spacing height: levelCellHeight - menuGrid.spacing color: "#AA41AAC4" border.width: 3 border.color: "black" visible: menuScreen.keyboardMode Behavior on x { SpringAnimation { spring: 2; damping: 0.2 } } Behavior on y { SpringAnimation { spring: 2; damping: 0.2 } } } 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 } } // grid view close } diff --git a/src/activities/lang/lang.js b/src/activities/lang/lang.js index d92c573b6..4b5132ed1 100644 --- a/src/activities/lang/lang.js +++ b/src/activities/lang/lang.js @@ -1,192 +1,193 @@ /* GCompris - lang.js * * 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 . */.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 lessonModelIndex = 0; var currentSubLevel = 0; var items; var baseUrl = "qrc:/gcompris/src/activities/lang/resource/"; var dataset var lessons var maxWordInLesson = 12 function init(items_) { items = items_ lessonModelIndex = 0 currentSubLevel = 0 } function start() { lessonModelIndex = 0; currentSubLevel = 0; items.imageReview.stop() var locale = GCompris.ApplicationInfo.getVoicesLocale(items.locale) // register the voices for the locale GCompris.DownloadManager.updateResource(GCompris.DownloadManager.getVoicesResourceForLocale(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 } // We have to keep it because we can't access content from the model lessons = Lang.getAllLessons(dataset) addPropertiesToLessons(lessons) items.menuModel.clear() items.menuModel.append(lessons) savedPropertiesToLessons(items.dialogActivityConfig.dataToSave) sortByFavorites(); items.menuScreen.start() } // Insert our specific properties in the lessons function addPropertiesToLessons(lessons) { for (var i in lessons) { // Ceil the wordCount to a maxWordInLesson count lessons[i]['wordCount'] = Math.ceil(Lang.getLessonWords(dataset, lessons[i]).length / maxWordInLesson) * maxWordInLesson lessons[i]['image'] = lessons[i].content[0].image lessons[i]['progress'] = 0 lessons[i]['favorite'] = false // We need to keep a back reference from the model to the lessons array lessons[i]['lessonIndex'] = i } } // Return a new json that contains all the properties we have to save function lessonsToSavedProperties() { var locale = GCompris.ApplicationInfo.getVoicesLocale(items.locale) var props = {} for(var i = 0; i < items.menuModel.count; i++) { var lesson = items.menuModel.get(i) props[lesson.name] = { 'favorite': lesson['favorite'], 'progress': lesson['progress'] } } return props } // Update the lessons based on a previous saving function savedPropertiesToLessons(dataToSave) { var locale = GCompris.ApplicationInfo.getVoicesLocale(items.locale) var props = dataToSave[locale] for(var i = 0; i < items.menuModel.count; i++) { var lesson = items.menuModel.get(i) if(props && props[lesson.name]) { lesson['favorite'] = props[lesson.name].favorite lesson['progress'] = props[lesson.name].progress } else { lesson['favorite'] = false lesson['progress'] = 0 } } } function stop() { } function initLevel(lessonModelIndex_) { lessonModelIndex = lessonModelIndex_ var lessonIndex = items.menuModel.get(lessonModelIndex).lessonIndex var flatWordList = Lang.getLessonWords(dataset, lessons[lessonIndex]); // We have to split the works in chunks of maxWordInLesson items.wordList = [] var i = 0 while(flatWordList.length > 0) { items.wordList[i++] = Core.shuffle(flatWordList.splice(0, maxWordInLesson)); } // If needed complete the last set to have maxWordInLesson items in it // We pick extra items from the head of the list if(items.wordList[i-1].length != maxWordInLesson) { var flatWordList = Lang.getLessonWords(dataset, lessons[lessonIndex]); var lastLength = items.wordList[i-1].length items.wordList[i-1] = items.wordList[i-1].concat(flatWordList.splice(0, maxWordInLesson - lastLength)) } - items.imageReview.category = lessons[lessonIndex].name + items.imageReview.category = items.categoriesTranslations[lessons[lessonIndex].name] //lessons[lessonIndex].name + // Calc the sublevel to start with var subLevel = Math.floor(items.menuModel.get(lessonModelIndex)['progress'] / maxWordInLesson) if(subLevel >= items.wordList.length) // Level done, start again at level 0 subLevel = 0 items.menuScreen.stop() items.imageReview.initLevel(subLevel) } function launchMenuScreen() { items.imageReview.stop() items.menuScreen.start() } function sortByFavorites() { for(var i = 0; i < items.menuModel.count; i++) { if(items.menuModel.get(i)['favorite']) items.menuModel.move(i, 0, 1); } } function markProgress() { // We count progress as a number of image learnt from the lesson start items.menuModel.get(lessonModelIndex)['progress'] += maxWordInLesson } function playWord(word) { var locale = GCompris.ApplicationInfo.getVoicesLocale(items.locale) return items.audioVoices.append( GCompris.ApplicationInfo.getAudioFilePathForLocale(word, locale)) }