diff --git a/src/activities/alphabetical_order/AlphabeticalOrder.qml b/src/activities/alphabetical_order/AlphabeticalOrder.qml index 62c695ec1..57eb7b4c8 100644 --- a/src/activities/alphabetical_order/AlphabeticalOrder.qml +++ b/src/activities/alphabetical_order/AlphabeticalOrder.qml @@ -1,391 +1,501 @@ /* GCompris - alphabetical_order.qml * * Copyright (C) 2016 Stefan Toncu * * 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 "../../core" import "alphabetical_order.js" as Activity ActivityBase { id: activity onStart: focus = true onStop: {} pageComponent: Rectangle { id: background anchors.fill: parent - color: "#ABCDEF" + color: "#fffae6" signal start signal stop // system locale by default property string locale: "system" 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 var missingLetters property alias listModel: listModel property alias listModel2: listModel2 property alias repeater: repeater property alias solutionRepeater: solutionRepeater property alias locale: background.locale property alias wordlist: wordlist + property bool gameFinished: false } onStart: { Activity.start(items) } onStop: { Activity.stop() } onFocusChanged: { if(focus) { Activity.focusTextInput() } } ListModel { id: listModel } ListModel { id: listModel2 } function computeWidth(repeater) { if (repeater == null) return 0 var sum = 0 for (var i = 0; i < repeater.count; i++) { sum += repeater.itemAt(i).width } return sum } + Timer { + id: finishAnimation + interval: 100 + repeat: true + + property int index: 0 + property int delay: 3 + + onTriggered: { + if (index < items.repeater.count) { + items.gameFinished = true + items.repeater.itemAt(index).particleLoader.item.burst(40) + index++ + } else if (index < items.repeater.count + delay) { + index++ + } else { + stop() + bonus.good("tux") + } + } + } - Rectangle { + Image { id: board - width: (background.computeWidth(items.repeater) + guessArea.spacing * (items.repeater.count - 0.5)) * 1.2 - height: parent.height * 0.6 - anchors.centerIn: parent - color: "black" + sourceSize.height: parent.height * 0.8 + sourceSize.width: parent.width * 0.8 + width: parent.width * 0.8 + height: parent.height * 0.8 + + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: -50 + + source: "resource/blackboard.svg" Rectangle { id: topRectangle width: background.computeWidth(items.repeater) + guessArea.spacing * (items.repeater.count - 0.5) height: board.height / 2 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: - height / 2 + color: "transparent" Flow { id: guessArea spacing: background.width / (listModel.count * 1.8) anchors.centerIn: parent Repeater { id: repeater anchors.horizontalCenter: parent.horizontalCenter model: listModel GCText { id: letter text: listModel.get(index) ? listModel.get(index).letter : "" fontSize: hugeSize color: "white" property var _x property var _y + property alias particleLoader: particleLoader + property alias failureAnimation: failureAnimation + + // Create a particle only for the strawberry + Loader { + id: particleLoader + anchors.fill: parent + active: true + sourceComponent: particle + } + + Component { + id: particle + ParticleSystemStarLoader { + id: particles + clip: false + } + } + SequentialAnimation { + id: failureAnimation + PropertyAction { target: letter; property: "color"; value: "red" } + NumberAnimation { target: letter; property: "scale"; to: 2; duration: 400 } + NumberAnimation { target: letter; property: "scale"; to: 1; duration: 400 } + NumberAnimation { target: letter; property: "opacity"; to: 0; duration: 100 } + PropertyAction { target: letter; property: "color"; value: "white" } + NumberAnimation { target: letter; property: "opacity"; to: 1; duration: 400 } + } MouseArea { anchors.fill: parent drag.target: parent + enabled: items.gameFinished ? false : true + onPressed: { letter._x = letter.x letter._y = letter.y } onReleased: { /* use mouse's coordinates (x and y) to compare to the "guessArea"'s repeater items */ var modified = mapToItem(background,mouse.x,mouse.y) - print("mouse x: ",modified.x," mouse y: ",modified.y) + //print("mouse x: ",modified.x," mouse y: ",modified.y) //search through the "repeater"'s items to find if THIS item can replace it for (var i=0; i guess.x && modified.x < guess.x + item.width && - modified.y > guess.y - item.height && modified.y < guess.y + item.height * 2) { + modified.y > guess.y && modified.y < guess.y + item.height * 2) { + var textAux = items.listModel.get(i).letter items.listModel.setProperty(i,"letter",letter.text) items.listModel.setProperty(index,"letter",textAux) + + + if (index != i) { + if (Activity.solution[i] == items.repeater.itemAt(i).text) { + if (Activity.solution[index] == letter.text) { + if (!Activity.checkCorectness()) { + items.repeater.itemAt(i).particleLoader.item.burst(40) + letter.particleLoader.item.burst(40) + } + } else { + items.repeater.itemAt(i).particleLoader.item.burst(40) + if (items.repeater.itemAt(index).text != '_') + items.repeater.itemAt(index).failureAnimation.start() + } + } else { + if (Activity.solution[index] == letter.text) { + letter.particleLoader.item.burst(40) + if (items.repeater.itemAt(i).text != '_') + items.repeater.itemAt(i).failureAnimation.start() + } + else { + if (items.repeater.itemAt(i).text != '_') + items.repeater.itemAt(i).failureAnimation.start() + if (items.repeater.itemAt(index).text != '_') + items.repeater.itemAt(index).failureAnimation.start() + } + } + } } } //search through the "solutionRepeater"'s items to find if THIS item can replace it for (i=0; i guess1.x && modified.x < guess1.x + item1.width && - modified.y > guess1.y - item1.height && modified.y < guess1.y + item1.height * 2) { + modified.y > guess1.y && modified.y < guess1.y + item1.height * 2) { var textAux1 = items.listModel2.get(i).letter - items.listModel2.setProperty(i,"letter",letter.text) - items.listModel.setProperty(index,"letter",textAux1) - break + if (textAux1 != '_' && letter.text != '_') { + items.listModel2.setProperty(i,"letter",letter.text) + items.listModel.setProperty(index,"letter",textAux1) + break + } + } } letter.x = letter._x letter.y = letter._y if (Activity.checkCorectness()) { - bonus.good("tux") + finishAnimation.index = 0 + finishAnimation.start() } - } } } } } } Rectangle { + id: bottomRectangle + + //TODO: on 3 or more letters (on bottom row of letter), only 2 are shown; have to handle width better width: background.computeWidth(items.solutionRepeater) + solutionArea.spacing + (items.solutionRepeater.count - 0.5) height: parent.height / 2.1 - anchors.top: topRectangle.bottom + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: height / 2 + color: "transparent" Flow { id: solutionArea width: parent.width height: parent.height spacing: board.width / (listModel2.count + 1) anchors.centerIn: parent Repeater { id: solutionRepeater model: listModel2 GCText { id: missingLetter text: listModel2.get(index) ? listModel2.get(index).letter : "" fontSize: hugeSize color: "white" property var _x property var _y property alias mouseArea: mouseArea + MouseArea { id: mouseArea anchors.fill: parent drag.target: parent + enabled: items.gameFinished ? false : true onPressed: { missingLetter._x = missingLetter.x missingLetter._y = missingLetter.y } onReleased: { /* use mouse's coordinates (x and y) to compare to the "guessArea"'s repeater items */ var modified = mapToItem(background,mouse.x,mouse.y) //search through the "repeater"'s items to find if THIS item should replace it for (var i=0; i guess.x && modified.x < guess.x + item.width && modified.y > guess.y && modified.y < guess.y + item.height) { //print("belongs to: ",i) var textAux = items.listModel.get(i).letter if (textAux == '_') { items.listModel.setProperty(i,"letter",missingLetter.text) items.listModel2.setProperty(index,"letter",'_') items.solutionRepeater.itemAt(index).opacity = 0 parent.enabled = false } else { - items.listModel.setProperty(i,"letter",missingLetter.text) items.listModel2.setProperty(index,"letter",textAux) } + + //if the letter is placed in the correct spot, the particle is activated + if (Activity.solution[i] == items.listModel.get(i).letter) { + if (!Activity.checkCorectness()) + items.repeater.itemAt(i).particleLoader.item.burst(40) + } else + items.repeater.itemAt(i).failureAnimation.start() + break } } missingLetter.x = missingLetter._x missingLetter.y = missingLetter._y if (Activity.checkCorectness()) { - bonus.good("tux") + finishAnimation.index = 0 + finishAnimation.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 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; } } } } 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("Reading: Wordlist error: " + msg); } DialogHelp { id: dialogHelp onClose: home() } Bar { id: bar content: BarEnumContent { value: help | home | level | config } onHelpClicked: { displayDialog(dialogHelp) } onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onHomeClicked: activity.home() onConfigClicked: { dialogActivityConfig.active = true dialogActivityConfig.setDefaultValues() displayDialog(dialogActivityConfig) } } Bonus { id: bonus Component.onCompleted: win.connect(Activity.nextLevel) } } } diff --git a/src/activities/alphabetical_order/alphabetical_order.js b/src/activities/alphabetical_order/alphabetical_order.js index 376844648..b04cb6bd4 100644 --- a/src/activities/alphabetical_order/alphabetical_order.js +++ b/src/activities/alphabetical_order/alphabetical_order.js @@ -1,224 +1,230 @@ /* GCompris - alphabetical_order.js * * Copyright (C) 2016 Stefan Toncu * * 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 //for ApplicationInfo .import "qrc:/gcompris/src/core/core.js" as Core var currentLevel = 0 -var numberOfLevel = 4 +var numberOfLevel = 10 var items var alphabet = [] var solution var dataSetUrl= "qrc:/gcompris/src/activities/gletters/resource/" +var totalLetters = [5,5,5,5,5,5,5,5,5,5] +var guessLetters = [2,2,2,2,2,2,2,2,2,2] function start(items_) { items = items_ currentLevel = 0 //imported from "readingh" activity var locale = items.locale == "system" ? "$LOCALE" : items.locale items.wordlist.loadFromFile(GCompris.ApplicationInfo.getLocaleFilePath( dataSetUrl + "default-"+locale+".json")); // 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( dataSetUrl + "default-"+localeShort+".json")); // We remove the using of default file for next time we enter this function items.wordlist.useDefault = false } //setup the alphabet from file createAlphabet() initLevel() } function stop() { } function initLevel() { items.bar.level = currentLevel + 1 init() } function init() { + items.gameFinished = false + // ['a','b','c','d'] - solution = getSolution(alphabet, 5) + solution = getSolution(alphabet, totalLetters[currentLevel]) // 3 1 5 2 4 - var numbers = getNumbers(5) + var numbers = getNumbers(totalLetters[currentLevel]) // ['a','_','c','_'] var model = solution.slice() // ['b', 'd'] var modelAux = [] - for (var i = 0; i < 2; i++) { + for (var i = 0; i < guessLetters[currentLevel]; i++) { model[numbers[i]] = '_' modelAux[i] = solution[numbers[i]] } //debugging // print(solution) // print("numbers: ", numbers) // print("solution: ",solution) // print("model: ",model) // print("modelAux: ",modelAux) items.listModel.clear() items.listModel2.clear() for (i = 0; i < modelAux.length; i++) { items.listModel2.append({"letter": modelAux[i]}) } for (i = 0; i < model.length; i++) { items.listModel.append({"letter": model[i]}) } + + } //goes through each level (in gletters json files) and appends all letters // (removing digits and uppercase duplicates) in the alphabet function createAlphabet() { // all levels for (var i = 1; i < items.wordlist.wordList.levels.length; i++) { var words = items.wordlist.getLevelWordList(i).words // se for (var j = 0; j < words.length; j++) { if (alphabet.indexOf(words[j].toLowerCase()) < 0 && (words[j] >= '0' && words[j] <= '9') == false ) { alphabet = alphabet.concat(words[j]) } } } alphabet = GCompris.ApplicationInfo.localeSort(alphabet) // print("alphabet: ",alphabet) } function sortNumber(a,b) { return a - b; } function getNumbers(len) { var numbers = [] for (var i = 0; i < len; i++) { numbers[i] = i } numbers = Core.shuffle(numbers) return numbers } function getSolution(alphabet, noOfLetters) { var solution = [] var aux = getNumbers(alphabet.length) //"noOfLetters" numbers sorted of "alphabet.length" numbers shuffeled // print("generate", aux) var generate = [] for (var i = 0; i < noOfLetters; i++) { generate[i] = aux[i] } generate.sort(sortNumber) for (i = 0; i < noOfLetters; i++) { solution[i] = alphabet[generate[i]] } return solution } function getModel(alphabet, noOfLetters) { var model for (var i = 0; i < noOfLetters; i++) { } return model } /* Receives the model (what the player has to complete) and the solution (how the result should look like) and returns the missing letters from the model (the letters from bottom, which the user will place on top) */ function missingLetters(model,solution) { var missingLetters = [] for (var i=0; i false if (nr !== items.listModel.count) return false for (var i = 0; i < nr; i++) if (solution[i]!==items.listModel.get(i).letter) return false // the letters are different at index i -> false return true } function nextLevel() { if(numberOfLevel <= ++currentLevel ) { currentLevel = 0 } initLevel(); } function previousLevel() { if(--currentLevel < 0) { currentLevel = numberOfLevel - 1 } initLevel(); } function focusTextInput() { if (!GCompris.ApplicationInfo.isMobile && items && items.textinput) items.textinput.forceActiveFocus(); } diff --git a/src/activities/alphabetical_order/resource/readme b/src/activities/alphabetical_order/resource/README similarity index 70% rename from src/activities/alphabetical_order/resource/readme rename to src/activities/alphabetical_order/resource/README index 2135a784f..d59b4bc22 100644 --- a/src/activities/alphabetical_order/resource/readme +++ b/src/activities/alphabetical_order/resource/README @@ -1,8 +1,11 @@ blackboard: https://openclipart.org/detail/49381/blackboard-with-stand-and-letters blackboard_front: https://openclipart.org/detail/49369/blackboard-with-letters A: https://openclipart.org/detail/193272/a B: https://openclipart.org/detail/193303/b C: https://openclipart.org/detail/193305/c https://openclipart.org/search/?query=letters&page=19 + +blackboard3; https://openclipart.org/detail/32395/white-board-silhouette +blackboard4; https://openclipart.org/detail/227004/student-chalk-slate diff --git a/src/activities/alphabetical_order/resource/blackboard.svg b/src/activities/alphabetical_order/resource/blackboard.svg new file mode 100644 index 000000000..d3e4f13f3 --- /dev/null +++ b/src/activities/alphabetical_order/resource/blackboard.svg @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + 2010-04-21T03:53:49 + A blackboard, with eraser, chalk and some drawn letters. Drawn in Inkscape. + https://openclipart.org/detail/49369/blackboard-with-letters-by-j_alves + + + J_Alves + + + + + blackboard + chalk + chalkboard + education + school + + + + + + + + + + + diff --git a/src/activities/alphabetical_order/resource/blackboard3.svg b/src/activities/alphabetical_order/resource/blackboard3.svg new file mode 100644 index 000000000..ea31ccc71 --- /dev/null +++ b/src/activities/alphabetical_order/resource/blackboard3.svg @@ -0,0 +1,145 @@ + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + white board silhouette + 2010-03-18T20:55:33 + + https://openclipart.org/detail/32395/white-board-silhouette-by-rg1024 + + + rg1024 + + + + + board + classroom + educational + silhouette + white board + + + + + + + + + + + diff --git a/src/activities/alphabetical_order/resource/blackboard4.svg b/src/activities/alphabetical_order/resource/blackboard4.svg new file mode 100644 index 000000000..de74be384 --- /dev/null +++ b/src/activities/alphabetical_order/resource/blackboard4.svg @@ -0,0 +1,424 @@ + + + + + + Student Chalk Slate + + + + + + + + + + + + + + image/svg+xml + + Student Chalk Slate + + + + Arvin61r58 + + + + + Openclipart.org + + + https://openclipart.org/ + https://openclipart.org/user-detail/Arvin61r58 + "old time" student's chalk slate/board + + + chalk + chalkboard + classroom + school + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +