diff --git a/src/activities/gletters/Gletters.qml b/src/activities/gletters/Gletters.qml index 2490f79fa..5b94f8362 100644 --- a/src/activities/gletters/Gletters.qml +++ b/src/activities/gletters/Gletters.qml @@ -1,316 +1,317 @@ /* 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 property string activityName: "gletters" /* 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 var levels: activity.datasetLoader.item.data property alias background: background property alias bar: bar property alias bonus: bonus property alias wordlist: wordlist property alias score: score property alias keyboard: keyboard property alias wordDropTimer: wordDropTimer property GCSfx audioEffects: activity.audioEffects property alias locale: background.locale property alias textinput: textinput } onStart: { 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 = ""; } } } //created to retrieve available menu modes for domino configurations Domino { id: invisibleDomino visible: false } DialogActivityConfig { id: dialogActivityConfig currentActivity: activity content: Component { Item { property alias localeBox: localeBox property alias dominoModeBox: dominoModeBox property alias uppercaseBox: uppercaseBox height: column.height property alias availableLangs: langs.languages LanguageList { id: langs } property var availableModes: invisibleDomino.menuModes Column { id: column spacing: 10 width: parent.width Flow { spacing: 5 width: dialogActivityConfig.width GCComboBox { id: localeBox visible: (activity.activityName == "gletters") model: langs.languages background: dialogActivityConfig label: qsTr("Select your locale") } GCComboBox { id: dominoModeBox visible: (activity.activityName == "smallnumbers2") model: availableModes background: dialogActivityConfig label: qsTr("Select Domino mode") } } GCDialogCheckBox { id: uppercaseBox visible: (activity.activityName == "gletters") width: dialogActivityConfig.width text: qsTr("Uppercase only mode") checked: activity.uppercaseOnly } } } } onClose: home() onLoadData: { if (activity.activityName == "gletters") { if(dataToSave && dataToSave["locale"]) { background.locale = dataToSave["locale"]; activity.uppercaseOnly = dataToSave["uppercaseMode"] === "true" ? true : false; } } else if (activity.activityName == "smallnumbers2") { if(dataToSave && dataToSave["mode"]) { activity.dominoMode = dataToSave["mode"]; } } } onSaveData: { var configHasChanged = false if (activity.activityName == "gletters") { 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; if(oldLocale !== newLocale || oldUppercaseMode !== activity.uppercaseOnly) { configHasChanged = true; } } else if (activity.activityName == "smallnumbers2") { var newMode = dialogActivityConfig.configItem.availableModes[dialogActivityConfig.configItem.dominoModeBox.currentIndex].value; if (newMode !== activity.dominoMode) { activity.dominoMode = newMode; dataToSave = {"mode": activity.dominoMode}; configHasChanged = true; } } // Restart the activity with new information if(configHasChanged) { background.stop(); background.start(); } } function setDefaultValues() { if (activity.activityName == "gletters") { 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.configItem.localeBox.currentIndex = i; break; } } } else if (activity.activityName == "smallnumbers2") { for(var i = 0 ; i < dialogActivityConfig.configItem.availableModes.length ; i++) { if(dialogActivityConfig.configItem.availableModes[i].value === activity.dominoMode) { dialogActivityConfig.configItem.dominoModeBox.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/gletters/gletters.js b/src/activities/gletters/gletters.js index 5fe02004d..8ce64ae9b 100644 --- a/src/activities/gletters/gletters.js +++ b/src/activities/gletters/gletters.js @@ -1,418 +1,417 @@ /* GCompris - gletters.js * * Copyright (C) 2014-2016 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 . */ /* ToDo / open issues: * - adjust wordlist filenames once we have an ApplicationInfo.dataPath() or so */ .pragma library .import QtQuick 2.6 as Quick .import GCompris 1.0 as GCompris //for ApplicationInfo .import "qrc:/gcompris/src/core/core.js" as Core var currentLevel = 0; var currentSubLevel = 0; var level = null; var maxLevel = 0; var maxSubLevel = 0; var items; var uppercaseOnly; var mode; //speed calculations, common: var speed = 0; // how fast letters fall var fallSpeed = 0; // how often new letters are dropped var incFallSpeed = 1000; // how much drop rate increases per sublevel var incSpeed = 10; // how much speed increases per sublevel // gletters: var fallRateBase = 40; // default for how fast letters fall (smaller is faster) var fallRateMult = 80; // default for how much falling speed increases per level (smaller is faster) var dropRateBase = 5000; // default for how often new letters are dropped var dropRateMult = 100; // default for how much drop rate increases per level // wordsgame: var wgMaxFallSpeed = 7000; var wgMaxSpeed = 150; var wgMinFallSpeed = 3000; var wgMinSpeed = 50; var wgDefaultFallSpeed = 8000; var wgDefaultSpeed = 170; var wgAddSpeed = 20; var wgAddFallSpeed = 1000; var wgMaxFallingItems; var droppedWords; var currentWord = null; // reference to the word currently typing, null if n/a var wordComponent = null; var successRate // Falling speed depends on it function start(items_, uppercaseOnly_, _mode) { items = items_; uppercaseOnly = uppercaseOnly_; mode = _mode; currentLevel = 0; currentSubLevel = 0; var locale = items.locale == "system" ? "$LOCALE" : items.locale // register the voices for the locale GCompris.DownloadManager.updateResource(GCompris.DownloadManager.getVoicesResourceForLocale(GCompris.ApplicationInfo.getVoicesLocale(items.locale))); - items.wordlist.loadFromFile(GCompris.ApplicationInfo.getLocaleFilePath( - items.ourActivity.dataSetUrl + "default-"+locale+".json")); + items.ourActivity.dataSetUrl + "default-"+locale+".json"), items.levels); // If wordlist is empty, we try to load from short locale and if not present again, we switch to default one var localeUnderscoreIndex = locale.indexOf('_') // probably exist a better way to see if the list is empty if(items.wordlist.maxLevel == 0) { 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; } // If not found, we will use the default file items.wordlist.useDefault = true items.wordlist.loadFromFile(GCompris.ApplicationInfo.getLocaleFilePath( - items.ourActivity.dataSetUrl + "default-"+localeShort+".json")); + items.ourActivity.dataSetUrl + "default-"+localeShort+".json"), items.levels) // We remove the using of default file for next time we enter this function items.wordlist.useDefault = false } maxLevel = items.wordlist.maxLevel; droppedWords = new Array(); initLevel(); } function stop() { deleteWords(); wordComponent = null items.wordDropTimer.stop(); } function initLevel() { items.audioVoices.clearQueue() items.bar.level = currentLevel + 1; wgMaxFallingItems = 3 successRate = 1.0 // initialize level deleteWords(); level = items.wordlist.getLevelWordList(currentLevel + 1); maxSubLevel = items.wordlist.getMaxSubLevel(currentLevel + 1); if (maxSubLevel == 0) { // If "sublevels" length is not set in wordlist, use the words length maxSubLevel = level.words.length } items.score.numberOfSubLevels = maxSubLevel; setSpeed(); /*console.log("Gletters: initializing level " + (currentLevel + 1) + " maxSubLvl=" + maxSubLevel + " wordCount=" + level.words.length + " speed=" + speed + " fallspeed=" + fallSpeed);*/ { /* 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 * 3. if we have both upper- and lowercase letters activate the shift * key*/ // first generate a map of needed letters var letters = new Array(); items.keyboard.shiftKey = false; for (var i = 0; i < level.words.length; i++) { if(mode ==='letter') { // The word is a letter, even if it has several chars (digraph) var letter = level.words[i]; var isUpper = (letter == letter.toLocaleUpperCase()); if (isUpper && letters.indexOf(letter.toLocaleLowerCase()) !== -1) items.keyboard.shiftKey = true; else if (!isUpper && letters.indexOf(letter.toLocaleUpperCase()) !== -1) items.keyboard.shiftKey = true; else if (letters.indexOf(letter) === -1) letters.push(level.words[i]); } else { // We split each word in char to create the keyboard for (var j = 0; j < level.words[i].length; j++) { var letter = level.words[i].charAt(j); var isUpper = (letter == letter.toLocaleUpperCase()); if (isUpper && letters.indexOf(letter.toLocaleLowerCase()) !== -1) items.keyboard.shiftKey = true; else if (!isUpper && letters.indexOf(letter.toLocaleUpperCase()) !== -1) items.keyboard.shiftKey = true; else if (letters.indexOf(letter) === -1) letters.push(level.words[i].charAt(j)); } } } letters = GCompris.ApplicationInfo.localeSort(letters, items.locale); // generate layout from letter map var layout = new Array(); 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++; } items.keyboard.layout = layout; } items.wordlist.initRandomWord(currentLevel + 1) initSubLevel() } function initSubLevel() { currentWord = null; if (currentSubLevel != 0) { // increase speed speed = Math.max(speed - incSpeed, wgMinSpeed); items.wordDropTimer.interval = fallSpeed = Math.max(fallSpeed - incFallSpeed, wgMinFallSpeed); } items.score.currentSubLevel = currentSubLevel + 1; if (currentSubLevel == 0 || droppedWords.length <= 1) // note, last word is still fading out dropWord(); //console.log("Gletters: initializing subLevel " + (currentSubLevel + 1) + " words=" + JSON.stringify(level.words)); } function processKeyPress(text) { var typedText = uppercaseOnly ? text.toLocaleUpperCase() : text; if (currentWord !== null) { // check against a currently typed word if (!currentWord.checkMatch(typedText)) { currentWord = null; audioCrashPlay() } else { playLetter(text) } } else { // no current word, check against all available words var found = false for (var i = 0; i< droppedWords.length; i++) { if (droppedWords[i].checkMatch(typedText)) { // typed correctly currentWord = droppedWords[i]; playLetter(text) found = true break; } } if(!found) { audioCrashPlay() } } if (currentWord !== null && currentWord.isCompleted()) { // win! currentWord.won(); // note: deleteWord() is triggered after fadeout successRate += 0.1 currentWord = null nextSubLevel(); } } function setSpeed() { if (mode === "letter") { speed = (level.speed !== undefined) ? level.speed : (fallRateBase + Math.floor(fallRateMult / (currentLevel + 1))); fallSpeed = (level.fallspeed !== undefined) ? level.fallspeed : Math.floor((dropRateBase + (dropRateMult * (currentLevel + 1)))); } else { // wordsgame speed = (level.speed !== undefined) ? level.speed : wgDefaultSpeed - (currentLevel+1)*wgAddSpeed; fallSpeed = (level.fallspeed !== undefined) ? level.fallspeed : wgDefaultFallSpeed - (currentLevel+1)*wgAddFallSpeed if(speed < wgMinSpeed ) speed = wgMinSpeed; if(speed > wgMaxSpeed ) speed = wgMaxSpeed; if(fallSpeed < wgMinFallSpeed ) fallSpeed = wgMinFallSpeed; if(fallSpeed > wgMaxFallSpeed ) fallSpeed = wgMaxFallSpeed; } items.wordDropTimer.interval = fallSpeed; } function deleteWords() { if (droppedWords === undefined || droppedWords.length < 1) return; for (var i = 0; i< droppedWords.length; i++) droppedWords[i].destroy(); droppedWords.length = 0; } function deleteWord(w) { if (droppedWords === undefined || droppedWords.length < 1) return; if (w == currentWord) currentWord = null; for (var i = 0; i< droppedWords.length; i++) if (droppedWords[i] == w) { droppedWords[i].destroy(); droppedWords.splice(i, 1); break; } } function createWord() { if (wordComponent.status == 1 /* Component.Ready */) { var text = items.wordlist.getRandomWord(); if(!text) { items.wordDropTimer.restart(); return } // if uppercaseOnly case does not matter otherwise it does if (uppercaseOnly) text = text.toLocaleUpperCase(); var word if(items.ourActivity.getImage(text)) { word = wordComponent.createObject( items.background, { "text": text, "image": items.ourActivity.getImage(text), // assume x=width-25px for now, Word auto-adjusts onCompleted(): "x": Math.random() * (items.main.width - 25), "y": -25, }); } else if(items.ourActivity.getDominoValues(text).length) { word = wordComponent.createObject( items.background, { "text": text, "mode": items.ourActivity.getMode(), "dominoValues": items.ourActivity.getDominoValues(text), // assume x=width-25px for now, Word auto-adjusts onCompleted(): "x": Math.random() * (items.main.width - 25), "y": -25, }); } else { word = wordComponent.createObject( items.background, { "text": text, // assume x=width-25px for now, Word auto-adjusts onCompleted(): "x": Math.random() * (items.main.width - 25), "y": -25, "mode": mode, }); } if (word === null) console.log("Gletters: Error creating word object"); else { droppedWords[droppedWords.length] = word; // speed to duration: var duration = (items.main.height / 2) * speed / successRate; /* console.debug("Gletters: dropping new word " + word.text + " duration=" + duration + " (speed=" + speed + ")" + " num=" + droppedWords.length);*/ word.startMoving(duration); } items.wordDropTimer.restart(); } else if (wordComponent.status == 3 /* Component.Error */) { console.log("Gletters: error creating word component: " + wordComponent.errorString()); } } function dropWord() { // Do not create too many falling items if(droppedWords.length > wgMaxFallingItems) { items.wordDropTimer.restart(); return } if (wordComponent !== null) createWord(); else { var text = items.wordlist.getRandomWord(); items.wordlist.appendRandomWord(text) var fallingItem if(items.ourActivity.getImage(text)) fallingItem = "FallingImage.qml" else if(items.ourActivity.getDominoValues(text).length) fallingItem = "FallingDomino.qml" else fallingItem = "FallingWord.qml" wordComponent = Qt.createComponent("qrc:/gcompris/src/activities/gletters/" + fallingItem); if (wordComponent.status == 1 /* Component.Ready */) createWord(); else if (wordComponent.status == 3 /* Component.Error */) { console.log("Gletters: error creating word component: " + wordComponent.errorString()); } else wordComponent.statusChanged.connect(createWord); } } function appendRandomWord(word) { items.wordlist.appendRandomWord(word) } function audioCrashPlay() { if(successRate > 0.5) successRate -= 0.1 items.audioEffects.play("qrc:/gcompris/src/core/resource/sounds/crash.wav") } function nextLevel() { if(maxLevel <= ++currentLevel ) { currentLevel = 0 } currentSubLevel = 0; initLevel(); } function previousLevel() { if(--currentLevel < 0) { currentLevel = maxLevel - 1 } currentSubLevel = 0; initLevel(); } function nextSubLevel() { if( ++currentSubLevel >= maxSubLevel) { currentSubLevel = 0 items.bonus.good("lion") } else items.score.playWinAnimation(); initSubLevel(); } function playLetter(letter) { var locale = GCompris.ApplicationInfo.getVoicesLocale(items.locale) items.audioVoices.append(GCompris.ApplicationInfo.getAudioFilePath("voices-$CA/"+locale+"/alphabet/" + Core.getSoundFilenamForChar(letter))) } function focusTextInput() { if (!GCompris.ApplicationInfo.isMobile && items && items.textinput) items.textinput.forceActiveFocus(); } diff --git a/src/activities/smallnumbers/ActivityInfo.qml b/src/activities/smallnumbers/ActivityInfo.qml index 4f1d2e651..666a43782 100644 --- a/src/activities/smallnumbers/ActivityInfo.qml +++ b/src/activities/smallnumbers/ActivityInfo.qml @@ -1,40 +1,41 @@ /* GCompris - ActivityInfo.qml * * Copyright (C) 2015 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 GCompris 1.0 ActivityInfo { name: "smallnumbers/Smallnumbers.qml" difficulty: 2 icon: "smallnumbers/smallnumbers.svg" author: "Bruno Coudoin <bruno.coudoin@gcompris.net>" demo: false //: Activity title title: qsTr("Numbers With Dice") //: Help title description: qsTr("Count the number of dots on dice before they reach the ground") // intro: "Count the number on your dice and type it on your keyboard before it reaches the ground." //: Help goal goal: qsTr("In a limited time, count the number of dots") //: Help prerequisite prerequisite: qsTr("Counting skills") //: Help manual manual: qsTr("With the keyboard, type the number of dots you see on the falling dice.") credit: "" section: "computer keyboard math numeration" createdInVersion: 0 + levels: "1,2,3" } diff --git a/src/activities/smallnumbers/resource/1/Data.qml b/src/activities/smallnumbers/resource/1/Data.qml new file mode 100644 index 000000000..07c9cf097 --- /dev/null +++ b/src/activities/smallnumbers/resource/1/Data.qml @@ -0,0 +1,53 @@ +/* GCompris - Data.qml + * + * Copyright (C) 2019 Akshay Kumar + * + * Authors: + * Akshay Kumar + * + * 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" + +Dataset { + objective: qsTr("Select number on dice up to 4") + data: [ + { + "level" : "1", + "words" : [ + "1", + "2" + ] + }, + { + "level" : "2", + "words" : [ + "1", + "2", + "3", + ] + }, + { + "level" : "3", + "words" : [ + "1", + "2", + "3", + "4" + ] + } + ] +} diff --git a/src/activities/smallnumbers/resource/2/Data.qml b/src/activities/smallnumbers/resource/2/Data.qml new file mode 100644 index 000000000..6a25ebe3b --- /dev/null +++ b/src/activities/smallnumbers/resource/2/Data.qml @@ -0,0 +1,71 @@ +/* GCompris - Data.qml + * + * Copyright (C) 2019 Akshay Kumar + * + * Authors: + * Akshay Kumar + * + * 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" + +Dataset { + objective: qsTr("Select number on dice up to 7") + data: [ + { + "level" : "1", + "words" : [ + "1", + "2", + "3", + "4" + ] + }, + { + "level" : "2", + "words" : [ + "1", + "2", + "3", + "4", + "5" + ] + }, + { + "level" : "3", + "words" : [ + "1", + "2", + "3", + "4", + "5", + "6" + ] + }, + { + "level" : "4", + "words" : [ + "1", + "2", + "3", + "4", + "5", + "6", + "7" + ] + } + ] +} diff --git a/src/activities/smallnumbers/resource/3/Data.qml b/src/activities/smallnumbers/resource/3/Data.qml new file mode 100644 index 000000000..188f2e5d1 --- /dev/null +++ b/src/activities/smallnumbers/resource/3/Data.qml @@ -0,0 +1,79 @@ +/* GCompris - Data.qml + * + * Copyright (C) 2019 Akshay Kumar + * + * Authors: + * Akshay Kumar + * + * 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" + +Dataset { + objective: qsTr("Select number on dice up to 9") + data: [ + { + "level" : "1", + "words" : [ + "1", + "2", + "3", + "4", + "5", + "6" + ] + }, + { + "level" : "2", + "words" : [ + "1", + "2", + "3", + "4", + "5", + "6", + "7" + ] + }, + { + "level" : "3", + "words" : [ + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8" + ] + }, + { + "level" : "4", + "words" : [ + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9" + ] + } + ] +} diff --git a/src/activities/smallnumbers2/ActivityInfo.qml b/src/activities/smallnumbers2/ActivityInfo.qml index bee3fbc0c..97f8742bc 100644 --- a/src/activities/smallnumbers2/ActivityInfo.qml +++ b/src/activities/smallnumbers2/ActivityInfo.qml @@ -1,40 +1,41 @@ /* GCompris - ActivityInfo.qml * * Copyright (C) 2015 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 GCompris 1.0 ActivityInfo { name: "smallnumbers2/Smallnumbers2.qml" difficulty: 2 icon: "smallnumbers2/smallnumbers2.svg" author: "Bruno Coudoin <bruno.coudoin@gcompris.net>" demo: false //: Activity title title: qsTr("Numbers with dominoes") //: Help title description: qsTr("Count the number of dots on the dominoes before they reach the ground") // intro: "Count the number of the points on the dominoes then type the result on your keyboard." //: Help goal goal: qsTr("In a limited time, count the number of dots") //: Help prerequisite prerequisite: qsTr("Counting skills") //: Help manual manual: qsTr("With the keyboard, type the number of dots you see on the falling dominoes.") credit: "" section: "math numeration" createdInVersion: 0 + levels: "1,2,3" } diff --git a/src/activities/smallnumbers2/resource/1/Data.qml b/src/activities/smallnumbers2/resource/1/Data.qml new file mode 100644 index 000000000..07c9cf097 --- /dev/null +++ b/src/activities/smallnumbers2/resource/1/Data.qml @@ -0,0 +1,53 @@ +/* GCompris - Data.qml + * + * Copyright (C) 2019 Akshay Kumar + * + * Authors: + * Akshay Kumar + * + * 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" + +Dataset { + objective: qsTr("Select number on dice up to 4") + data: [ + { + "level" : "1", + "words" : [ + "1", + "2" + ] + }, + { + "level" : "2", + "words" : [ + "1", + "2", + "3", + ] + }, + { + "level" : "3", + "words" : [ + "1", + "2", + "3", + "4" + ] + } + ] +} diff --git a/src/activities/smallnumbers2/resource/2/Data.qml b/src/activities/smallnumbers2/resource/2/Data.qml new file mode 100644 index 000000000..6a25ebe3b --- /dev/null +++ b/src/activities/smallnumbers2/resource/2/Data.qml @@ -0,0 +1,71 @@ +/* GCompris - Data.qml + * + * Copyright (C) 2019 Akshay Kumar + * + * Authors: + * Akshay Kumar + * + * 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" + +Dataset { + objective: qsTr("Select number on dice up to 7") + data: [ + { + "level" : "1", + "words" : [ + "1", + "2", + "3", + "4" + ] + }, + { + "level" : "2", + "words" : [ + "1", + "2", + "3", + "4", + "5" + ] + }, + { + "level" : "3", + "words" : [ + "1", + "2", + "3", + "4", + "5", + "6" + ] + }, + { + "level" : "4", + "words" : [ + "1", + "2", + "3", + "4", + "5", + "6", + "7" + ] + } + ] +} diff --git a/src/activities/smallnumbers2/resource/3/Data.qml b/src/activities/smallnumbers2/resource/3/Data.qml new file mode 100644 index 000000000..188f2e5d1 --- /dev/null +++ b/src/activities/smallnumbers2/resource/3/Data.qml @@ -0,0 +1,79 @@ +/* GCompris - Data.qml + * + * Copyright (C) 2019 Akshay Kumar + * + * Authors: + * Akshay Kumar + * + * 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" + +Dataset { + objective: qsTr("Select number on dice up to 9") + data: [ + { + "level" : "1", + "words" : [ + "1", + "2", + "3", + "4", + "5", + "6" + ] + }, + { + "level" : "2", + "words" : [ + "1", + "2", + "3", + "4", + "5", + "6", + "7" + ] + }, + { + "level" : "3", + "words" : [ + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8" + ] + }, + { + "level" : "4", + "words" : [ + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9" + ] + } + ] +} diff --git a/src/core/Wordlist.qml b/src/core/Wordlist.qml index 48f6e25ed..188ae5721 100644 --- a/src/core/Wordlist.qml +++ b/src/core/Wordlist.qml @@ -1,226 +1,236 @@ /* GCompris - Wordlist.qml * * Copyright (C) 2014 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 GCompris 1.0 import "core.js" as Core /** * A Wordlist component loads and maintains GCompris wordlists. * @ingroup components * * It loads wordlists from json-files, validates its content and exposes * wordlists and levels to activities. * * It expects and returns the following wordlist format (UTF8 encoded): * * @code * { * "name":"default-gd", * "description":"Gàidhlig", * "locale":"gd", * "levels":[ { "level":1, * "speed":150, <-- optional * "fallspeed":7000, <-- optional * "sublevels":10, <-- optional * "words":["a","jim", "beam", ... ]}, <-- mandatory * { "level":2, ... } * ] * } * @endcode * * @inherit QtQuick.Item * @sa JsonParser */ Item { id: wordlist /** * type:string * Default filename to be used if the language specific wordlist file could * not be loaded. * Default is emtpy. */ property string defaultFilename: "" /** * type:bool * Whether to automatically fallback to the default filename if the * language specific wordlist file could not be loaded. * Default is true. */ property bool useDefault: true /** * type:string * Name of the file to load the language specific wordlist from. * Default is empty. Can also be passed directly in loadFromFile(). * If set in the QML definition, the wordlist is autoloaded onCompleted. */ property string filename: "" /** * type:object * Complete Wordlist content loaded. You probably want to use one of the * convenience accessors like getLevelWordList(). * Default is empty. */ property var wordList: ({}) /// @cond INTERNAL_DOCS property var randomWordList: [] property int maxLevel: 0 /// @endcond /** * Emitted if an error occurs. * @param msg Error message. */ signal error(string msg); /** * Load Wordlist from file @p fname. * * @param type:string fname Filename to load wordlist from. */ - function loadFromFile(fname) { + function loadFromFile(fname, levels) { + + // If levels are provided explicitly as function parameter, use them as wordlist + if(levels !== undefined) { + var levelsObject = {levels: levels} + wordList = levelsObject; + maxLevel = wordList.levels.length; + return wordList; + } + + // Otherwise load wordlist from file filename = fname; var from; maxLevel = 0 wordList = parser.parseFromUrl(filename, validateWordlist); if (wordList == null) { error("Wordlist: Invalid wordlist file " + fname); if (useDefault) { // fallback to default file: wordList = parser.parseFromUrl(defaultFilename, validateWordlist); if (wordList == null) { error("Wordlist: Invalid wordlist file " + defaultFilename); return; } from = "default-file " + defaultFilename; } else { error("Wordlist: do not use default list, no list loaded"); return; } } else { from = "file " + fname; } // at this point we have valid levels maxLevel = wordList.levels.length; console.log("Wordlist: loaded " + maxLevel + " levels from " + from); return wordList; } /** * Get wordlist data for @p level * * @param type:int level Level. * @returns type:object wordlist data. */ function getLevelWordList(level) { if (level > maxLevel) return null; return wordList.levels[level - 1]; } /** * Get number of sub-levels in @p level. * * @param type:int level Level. * @returns type:int Number of sublevels. */ function getMaxSubLevel(level) { if (level > maxLevel || level === 0) { console.log("ERROR: Wordlist.getMaxSubLevel out of range, requested level", level, "out of expected range", 1, "-", maxLevel) return null; } return wordList.levels[level - 1].sublevels !== undefined ? wordList.levels[level - 1].sublevels : 0; } /** * Build a random word list for @p level. * * We don't want to propose several time the same word. First call * initRandomWord(level) to create the initial shuffled list of words. * Then call getRandomWord() to get the words one at a time. * If a word was not found by the child, add it again to the list * with appendRandomWord(word) * * @param type:int level Level. */ function initRandomWord(level) { randomWordList = Core.shuffle(wordList.levels[level - 1].words).slice(0) } /** * Re-add a random word to a shuffled word list. * * @param type:string word Word to append. * @sa initRandomWord */ function appendRandomWord(word) { randomWordList.unshift(word) } /** * Returns the next random word from a shuffled wordlist. * * @sa initRandomWord */ function getRandomWord() { return randomWordList.pop() } /// @cond INTERNAL_DOCS function validateWordlist(doc) { // minimal syntax check: var i; if (undefined === doc.levels) return false; for (i = 0; i < doc.levels.length; i++) { // check mandatory level properties only (speed, fallspeed and sublevels are optional) if (doc.levels[i].words.length < 1) return false; } if (i < 1) return false; return true; } /// @endcond JsonParser { id: parser onError: wordlist.error(msg); } Component.onCompleted: { if (filename != "") loadFromFile(filename); } }