diff --git a/src/activities/hangman/Hangman.qml b/src/activities/hangman/Hangman.qml index 880a2801b..5371d11f7 100644 --- a/src/activities/hangman/Hangman.qml +++ b/src/activities/hangman/Hangman.qml @@ -1,398 +1,417 @@ /* GCompris - hangman.qml * * Copyright (C) 2015 Rajdeep Kaur * * Authors: * Bruno Coudoin (GTK+ version) * Rajdeep kaur (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 QtGraphicalEffects 1.0 import "../../core" import "hangman.js" as Activity import "qrc:/gcompris/src/core/core.js" as Core 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/hangman/resource/" 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 anchors.fill: parent sourceSize.width: Math.max(parent.width, parent.height) // system locale by default property string locale: "system" property bool englishFallback: false signal start signal stop Component.onCompleted: { dialogActivityConfig.initialize() 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 Item ourActivity: activity property alias bar: bar property alias bonus: bonus property alias keyboard: keyboard property alias hidden: hidden property alias guessedText: guessedText property alias textinput: textinput property alias wordImage: wordImage property alias score: score property alias parser: parser property alias locale: background.locale property alias ok: ok property int remainingLife property double maskThreshold property var goodWord property int goodWordIndex property bool easyMode: false property alias englishFallbackDialog: englishFallbackDialog + property alias winTimer: winTimer + property alias goodIcon: goodIcon function playWord() { var locale = ApplicationInfo.getVoicesLocale(items.locale) if(activity.audioVoices.append( ApplicationInfo.getAudioFilePathForLocale(goodWord.voice, locale))) bonus.interval = 2500 else bonus.interval = 500 } onRemainingLifeChanged: { maskThreshold = 0.15 * remainingLife if(remainingLife == 3) { playWord(); } } } onStart: { focus = true Activity.start(items) Activity.focusTextInput() } onStop: { Activity.stop(); } + Image { + id: goodIcon + source: "qrc:/gcompris/src/core/resource/apply.svg" + anchors.top: score.bottom + anchors.horizontalCenter: score.horizontalCenter + anchors.topMargin: ok.anchors.bottomMargin + sourceSize.height: ok.width + sourceSize.width: ok.width + visible: false + } + GCText { id: hidden fontSize: largeSize color: "#4d4d4d" font.letterSpacing: 0.5 width: parent.width * 0.90 - score.width fontSizeMode: Text.Fit horizontalAlignment: Text.AlignHCenter anchors { right: score.left bottom: bar.top bottomMargin: 10 * ApplicationInfo.ratio } z: 11 } GCText { id: guessedText fontSize: smallSize color: "#FFFFFF" wrapMode: Text.WordWrap horizontalAlignment: Text.AlignHCenter width: parent.width - 2.1 * clock.width anchors { horizontalCenter: parent.horizontalCenter } z: 12 } Rectangle { width: guessedText.width height: guessedText.height radius: 10 border.width: 1 gradient: Gradient { GradientStop { position: 0.0; color: "#000" } GradientStop { position: 0.9; color: "#666" } GradientStop { position: 1.0; color: "#AAA" } } anchors { horizontalCenter: parent.horizontalCenter } z: 11 } 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 enabled: !ApplicationInfo.isMobile visible: false focus: true onTextChanged: { if (text != "") { Activity.processKeyPress(text); text = ""; } } onAccepted: if(items.remainingLife === 0) Activity.nextSubLevel() } Item { id: imageframe width: Math.min(300 * ApplicationInfo.ratio, background.width * 0.8, hidden.y) - guessedText.height height: width anchors.horizontalCenter: parent.horizontalCenter anchors.top: guessedText.bottom y: 5 * ApplicationInfo.ratio z: 10 opacity: items.easyMode ? 1 : 0 Image { id: wordImage smooth: true visible: false anchors.fill: parent property string nextSource function changeSource(nextSource_) { nextSource = nextSource_ animImage.start() } SequentialAnimation { id: animImage PropertyAnimation { target: wordImage property: "opacity" to: 0 duration: 100 } PropertyAction { target: wordImage property: "source" value: wordImage.nextSource } PropertyAnimation { target: wordImage property: "opacity" to: 1 duration: 100 } } } Image { id: threshmask smooth: true visible: false width: 1.3*parent.width height: 1.2*parent.height source: dataSetUrl + "fog.png" } ThresholdMask { id: thresh anchors.fill: wordImage source: wordImage maskSource: threshmask spread: 0.4 // remainingLife between 0 and 6 => threshold between 0 and 0.9 threshold: items.maskThreshold } } DialogChooseLevel { id: dialogActivityConfig currentActivity: activity.activityInfo onClose: { home() } onSaveData: { levelFolder = dialogActivityConfig.chosenLevels currentActivity.currentLevels = dialogActivityConfig.chosenLevels ApplicationSettings.setCurrentLevels(currentActivity.name, dialogActivityConfig.chosenLevels) } onLoadData: { if(activityData && activityData["activityLocale"]) { background.locale = activityData["activityLocale"]; } else { background.locale = Core.resolveLocale(background.locale) } if(activityData && activityData["easyMode"]) { items.easyMode = (activityData["easyMode"] === "true"); } } onStartActivity: { background.stop() background.start() } } DialogHelp { id: dialogHelp onClose: home() } Bar { id: bar anchors.bottom: keyboard.top content: BarEnumContent { value: help | home | level | activityConfig } onHelpClicked: { displayDialog(dialogHelp) } onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onHomeClicked: activity.home() onActivityConfigClicked: { displayDialog(dialogActivityConfig) } } Score { id: score height: 1.2 * internalTextComponent.height width: 1.3 * internalTextComponent.width anchors { bottom: keyboard.enabled ? keyboard.top : parent.bottom bottomMargin: keyboard.enabled ? 1.2 * bar.height : 0.55 * parent.height right: parent.right rightMargin: 0.025 * parent.width } } BarButton { id: ok source: "qrc:/gcompris/src/core/resource/bar_ok.svg"; sourceSize.width: Math.min(score.width, clock.width) visible: false anchors { bottom: score.top horizontalCenter: score.horizontalCenter bottomMargin: 5 * ApplicationInfo.ratio } onClicked: Activity.nextSubLevel() } JsonParser { id: parser onError: console.error("Hangman: Error parsing json: " + msg); } Image { id: clock anchors { left: parent.left top: parent.top margins: 10 } sourceSize.width: 66 * bar.barZoom property int remainingLife: items.remainingLife onRemainingLifeChanged: if(remainingLife >= 0) clockAnim.restart() SequentialAnimation { id: clockAnim alwaysRunToEnd: true ParallelAnimation { NumberAnimation { target: clock; properties: "opacity"; to: 0; duration: 800; easing.type: Easing.OutCubic } NumberAnimation { target: clock; properties: "rotation"; from: 0; to: 180; duration: 800; easing.type: Easing.OutCubic } } PropertyAction { target: clock; property: 'source'; value: "qrc:/gcompris/src/activities/reversecount/resource/" + "flower" + items.remainingLife + ".svg" } ParallelAnimation { NumberAnimation { target: clock; properties: "opacity"; to: 1; duration: 800; easing.type: Easing.OutCubic } NumberAnimation { target: clock; properties: "rotation"; from: 180; to: 0; duration: 800; easing.type: Easing.OutCubic } } } } VirtualKeyboard { id: keyboard anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter width: parent.width onKeypress: Activity.processKeyPress(text); onError: console.log("VirtualKeyboard error: " + msg); } Bonus { id: bonus interval: 2000 onLoose: ok.visible = true onWin: Activity.nextSubLevel() } 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("https://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() } + + Timer { + id: winTimer + interval: 2000 + onTriggered: bonus.good("lion") + } } } diff --git a/src/activities/hangman/hangman.js b/src/activities/hangman/hangman.js index bb5afb4e1..e28425910 100644 --- a/src/activities/hangman/hangman.js +++ b/src/activities/hangman/hangman.js @@ -1,231 +1,234 @@ /* GCompris - hangman.js * * Copyright (C) 2015 Rajdeep Kaur * * Authors: * Bruno Coudoin (GTK+ version) * Rajdeep kaur (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 . */ .pragma library .import QtQuick 2.6 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 currentLevel var currentSubLevel var maxLevel var maxSubLevel var items var currentWord var sp ="_ " var dataset = null var lessons var wordList var subLevelsLeft var alreadyTypedLetters // js strings are immutable, can't replace letter like that... // http://stackoverflow.com/questions/1431094/how-do-i-replace-a-character-at-a-particular-index-in-javascript String.prototype.replaceAt = function(index, character) { return this.substr(0, index) + character + this.substr(index+character.length); } function start(items_) { items = items_ currentLevel = 0; currentSubLevel = 0; items.remainingLife = 6; var locale = GCompris.ApplicationInfo.getVoicesLocale(items.locale) var resourceUrl = "qrc:/gcompris/src/activities/lang/resource/" // register the voices for the locale GCompris.DownloadManager.updateResource( GCompris.DownloadManager.getVoicesResourceForLocale(locale)) var data = Lang.loadDataset(items.parser, resourceUrl, locale); dataset = data["dataset"]; items.background.englishFallback = data["englishFallback"]; lessons = Lang.getAllLessons(dataset) maxLevel = lessons.length initLevel(); } function stop() { } function initLevel() { 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.visible = true; subLevelsLeft = [] for(var i in wordList) subLevelsLeft.push(i) initSubLevel(); //to set the layout...populate var letters = new Array(); for (var i = 0; i < wordList.length; i++) { var word = wordList[i].translatedTxt; for (var j = 0; j < word.length; j++) { var letter = word.charAt(j).toLocaleLowerCase(); if (letters.indexOf(letter) === -1) letters.push(letter); } } letters = GCompris.ApplicationInfo.localeSort(letters, items.locale); // Remove space character if in list var indexOfSpace = letters.indexOf(' ') if(indexOfSpace > -1) letters.splice(indexOfSpace, 1) // 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; } function processKeyPress(text) { if(items.remainingLife === 0 || items.bonus.isPlaying) return text = text.toLocaleLowerCase() // Check if the character has already been typed if(alreadyTypedLetters.indexOf(text) !== -1) { // Character already typed, do nothing return; } // Add the character to the already typed characters alreadyTypedLetters.push(text); // add the typed character in the "Attempted characters" text field createAttemptedText() // Get all the indices of this letter in the word var indices = []; for(var i = 0 ; i < currentWord.length ; i ++) { if (currentWord[i].toLocaleLowerCase() === text) { indices.push(i); } } if(indices.length == 0) { // The letter is not in the word to find // If no more life, we display the good word and show the bonus if(--items.remainingLife == 0) { items.hidden.text = items.goodWord.translatedTxt; items.playWord() items.bonus.bad("lion"); return; } } else { // For all the indices found, we replace the "_" by the letter for(var index = 0 ; index < indices.length ; index ++) { // Characters in the word displayed are separated by spaces, this is why we do 2*index items.hidden.text = items.hidden.text.replaceAt(2*indices[index], currentWord[indices[index]]); } } // If no more '_' in the word to find, we have found all letters, show bonus if(items.hidden.text.indexOf("_") === -1) { items.maskThreshold = 0; - items.playWord() - items.bonus.good("lion"); + items.playWord(); + items.hidden.text = items.goodWord.translatedTxt; + items.goodIcon.visible = true + items.winTimer.start(); } } function nextLevel() { if(maxLevel <= ++currentLevel) { currentLevel = 0 } currentSubLevel = 0; initLevel(); } function previousLevel() { if(--currentLevel < 0) { currentLevel = maxLevel - 1 } currentSubLevel = 0; initLevel(); } function initSubLevel() { // initialize sublevel if(items.score.currentSubLevel < items.score.numberOfSubLevels) items.score.currentSubLevel = currentSubLevel + 1; else items.score.visible = false items.goodWordIndex = subLevelsLeft.pop() items.ok.visible = false + items.goodIcon.visible = false items.goodWord = wordList[items.goodWordIndex] items.wordImage.changeSource(items.goodWord.image); items.remainingLife = 6; alreadyTypedLetters = new Array(); currentWord = items.goodWord.translatedTxt; items.hidden.text = "" createAttemptedText() for(var i = 0; i < currentWord.length ; ++ i) { if(currentWord[i] == " ") { items.hidden.text = items.hidden.text + " " + " " } else { items.hidden.text = items.hidden.text + sp; } } } function createAttemptedText() { alreadyTypedLetters.sort() items.guessedText.text = qsTr("Attempted: %1").arg(alreadyTypedLetters.join(", ")) } function nextSubLevel() { if( ++currentSubLevel >= maxSubLevel) { currentSubLevel = 0; nextLevel(); } else { initSubLevel(); } } function focusTextInput() { if (!GCompris.ApplicationInfo.isMobile && items && items.textinput) items.textinput.forceActiveFocus(); }