diff --git a/src/activities/programmingMaze/CodeArea.qml b/src/activities/programmingMaze/CodeArea.qml index c16ab2434..0214ff71c 100644 --- a/src/activities/programmingMaze/CodeArea.qml +++ b/src/activities/programmingMaze/CodeArea.qml @@ -1,343 +1,335 @@ /* GCompris - CodeArea.qml * * Copyright (C) 2015 Siddhesh Suthar * Copyright (C) 2018 Aman Kumar Gupta * * Authors: * Siddhesh Suthar * Aman Kumar Gupta + * Timothée Giet (Layout and graphics rework) * * 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 "programmingMaze.js" as Activity GridView { id: codeArea z: 1 width: background.width * 0.4 height: background.height * 0.29 cellWidth: background.buttonWidth cellHeight: background.buttonHeight interactive: false model: currentModel - clip: true highlight: Rectangle { width: buttonWidth height: buttonHeight - color: "lightsteelblue" + color: "#00ffffff" border.width: 3.5 * ApplicationInfo.ratio - border.color: "purple" - opacity: 0.5 + border.color: "#e77935" z: 11 radius: width / 18 } highlightFollowsCurrentItem: true - highlightMoveDuration: 500 keyNavigationWraps: true focus: true property ListModel currentModel property int draggedItemIndex: -1 property int possibleDropIndex: -1 property int possibleDropRemoveIndex: -1 property int xCoordinateInPossibleDrop: -1 property int yCoordinateInPossibleDrop: -1 /** * Stores the index of the item which is clicked to edit. * * If the index of the item on 2nd click is same as initialEditItemIndex , then the indicator will become invisible, as it means that initially wanted to edit that instruction, but now we want to deselect it. * * If the index of the item on 2nd click is different from initialEditItemIndex, the edit indicator moves to the new item as we now want to edit that one. */ property int initialEditItemIndex: -1 // Tells if any instruction is selected for editing. property bool isEditingInstruction: false signal spaceKeyPressed signal tabKeyPressed signal deleteKeyPressed /** * There can be three possibilities here: * * 1. We want to insert an instruction at the currentIndex position. * 2. We want to select an instruction to edit, or deselect it. * 3. We want to append an instruction. */ onSpaceKeyPressed: { if(currentIndex != -1) { if(instructionArea.instructionToInsert && (items.numberOfInstructionsAdded < items.maxNumberOfInstructionsAllowed)) { var isInstructionInserted = appendInstruction() if(isInstructionInserted) currentModel.move(currentModel.count - 1, currentIndex, 1) } else { if((initialEditItemIndex == currentIndex) || (initialEditItemIndex == -1 && currentIndex != -1)) { codeArea.isEditingInstruction = !codeArea.isEditingInstruction } - if(!codeArea.isEditingInstruction) codeArea.initialEditItemIndex = -1 else initialEditItemIndex = currentIndex var calculatedX = (initialEditItemIndex % 4) * codeArea.cellWidth var calculatedY = Math.floor(initialEditItemIndex / 4) * codeArea.cellHeight editInstructionIndicator.x = calculatedX + 1.5 * ApplicationInfo.ratio editInstructionIndicator.y = calculatedY + 1.5 * ApplicationInfo.ratio } } + else if((items.numberOfInstructionsAdded < items.maxNumberOfInstructionsAllowed) && instructionArea.instructionToInsert) appendInstruction() } onDeleteKeyPressed: { if(currentIndex != -1) { currentModel.remove(currentIndex) items.numberOfInstructionsAdded-- } resetEditingValues() } function appendInstruction() { if(background.insertIntoMain || (instructionArea.instructionToInsert != "call-procedure")) { currentModel.append({ "name": instructionArea.instructionToInsert }) items.numberOfInstructionsAdded++ instructionArea.instructionToInsert = "" return true } return false } function resetEditingValues() { initialEditItemIndex = -1 isEditingInstruction = false } Item { id: dropPositionIndicator visible: false height: background.buttonHeight width: 3 * ApplicationInfo.ratio Rectangle { visible: parent.visible anchors.centerIn: parent width: parent.width height: parent.height - 3 * ApplicationInfo.ratio - color: "blue" - radius: width + color: "#e77935" } states: [ State { name: "shown" when: codeArea.possibleDropIndex != -1 PropertyChanges { target: dropPositionIndicator visible: true x: Math.floor(codeArea.xCoordinateInPossibleDrop / codeArea.cellWidth) * codeArea.cellWidth - 1.5 * ApplicationInfo.ratio y: Math.floor(codeArea.yCoordinateInPossibleDrop / codeArea.cellHeight) * codeArea.cellHeight + 1.5 * ApplicationInfo.ratio } } ] } Rectangle { id: editInstructionIndicator visible: codeArea.isEditingInstruction && codeArea.count != 0 width: background.buttonWidth - 3 * ApplicationInfo.ratio height: background.buttonHeight - 3 * ApplicationInfo.ratio color: "red" - border.color: "black" + border.color: "red" border.width: 1.5 * ApplicationInfo.ratio opacity: 0.2 radius: width / 18 } MouseArea { id: codeAreaMouse anchors.fill: parent enabled: items.isTuxMouseAreaEnabled || items.isRunCodeEnabled onPressed: { + codeArea.currentIndex = -1 codeArea.draggedItemIndex = codeArea.indexAt(mouseX,mouseY) if(codeArea.draggedItemIndex === -1) { constraintInstruction.changeConstraintInstructionOpacity() codeArea.isEditingInstruction = false } else if(!codeArea.isEditingInstruction) codeArea.initialEditItemIndex = codeArea.draggedItemIndex } onPositionChanged: { var newPos = codeArea.indexAt(mouseX, mouseY) var calculatedX = Math.floor(mouseX / codeArea.cellWidth) * codeArea.cellWidth var previousIndexPosition = codeArea.indexAt(calculatedX - 1, mouseY) // If the user want to move an item to the end, then the new position will be after the last instruction. if(newPos == -1 && previousIndexPosition != -1) newPos = previousIndexPosition + 1 codeArea.isEditingInstruction = false codeArea.xCoordinateInPossibleDrop = mouseX codeArea.yCoordinateInPossibleDrop = mouseY codeArea.possibleDropIndex = newPos } onReleased: { if(codeArea.draggedItemIndex != -1) { var draggedIndex = codeArea.draggedItemIndex var dropIndex = codeArea.indexAt(mouseX,mouseY) var calculatedX = Math.floor(mouseX / codeArea.cellWidth) * codeArea.cellWidth var calculatedY = Math.floor(mouseY / codeArea.cellHeight) * codeArea.cellHeight codeArea.draggedItemIndex = -1 if(dropIndex == -1) { var previousIndexPosition = codeArea.indexAt(calculatedX - 1, mouseY) if(previousIndexPosition == -1) { currentModel.remove(draggedIndex) items.numberOfInstructionsAdded-- } else { currentModel.append(currentModel.get(draggedIndex)) currentModel.remove(draggedIndex) } codeArea.initialEditItemIndex = -1 } else if(draggedIndex != dropIndex) { if(dropIndex <= draggedIndex) { // Moving instruction from right to left currentModel.move(draggedIndex, dropIndex, 1) } else { // Moving instruction from left to right currentModel.move(draggedIndex, dropIndex - 1, 1) } codeArea.initialEditItemIndex = -1 } else { /** * If the index of the initially selected instruction (if any) is same as the currently selected instruction, * deselect it for editing, else make the current instruction as the initially editable item and move the edit indicator to that position. */ if(codeArea.initialEditItemIndex == dropIndex) { codeArea.isEditingInstruction = !codeArea.isEditingInstruction if(!codeArea.isEditingInstruction) codeArea.initialEditItemIndex = -1 } else codeArea.initialEditItemIndex = draggedIndex editInstructionIndicator.x = calculatedX + 1.5 * ApplicationInfo.ratio editInstructionIndicator.y = calculatedY + 1.5 * ApplicationInfo.ratio } codeArea.possibleDropIndex = -1 } } } delegate: Item { id: itemParent width: background.buttonWidth height: background.buttonHeight - Rectangle { - id: circlePlaceholder - width: 30 * ApplicationInfo.ratio - height: width - radius: width - anchors.centerIn: parent - color: "#cecece" - opacity: 0 - } - Item { id: item width: background.buttonWidth height: background.buttonHeight state: "inactive" opacity: 1 Behavior on width { NumberAnimation { duration: 300; easing.type: Easing.InOutQuad } } Behavior on height { NumberAnimation { duration: 300; easing.type: Easing.InOutQuad } } Behavior on opacity {NumberAnimation { duration: 300; easing.type: Easing.InOutQuad } } states: [ State { name: "inDrag" when: index == codeArea.draggedItemIndex - PropertyChanges { target: circlePlaceholder; opacity: 1 } PropertyChanges { target: item; parent: codeArea } PropertyChanges { target: item; width: background.buttonWidth * 0.80 } PropertyChanges { target: item; height: background.buttonHeight * 0.80 } PropertyChanges { target: item; anchors.centerIn: undefined } PropertyChanges { target: item; x: codeAreaMouse.mouseX - item.width / 2 } PropertyChanges { target: item; y: codeAreaMouse.mouseY - item.height / 2 } }, State { name: "greyedOut" when: (codeArea.draggedItemIndex != -1) && (codeArea.draggedItemIndex != index) PropertyChanges { target: item; opacity: 0.7 } }, State { name: "inactive" when: (codeArea.draggedItemIndex == -1) || (codeArea.draggedItemIndex == index) PropertyChanges { target: item; opacity: 1.0 } } ] transitions: [ Transition { from: "inDrag" to: "*" PropertyAnimation { target: item properties: "scale, opacity" from: 0.7 to: 1.0 duration: 200 } } ] Rectangle { width: parent.width - 3 * ApplicationInfo.ratio height: parent.height - 3 * ApplicationInfo.ratio border.width: 1.2 * ApplicationInfo.ratio - border.color: "black" + border.color: "#2a2a2a" anchors.centerIn: parent radius: width / 18 Image { id: codeAreaIcon source: Activity.url + name + ".svg" - sourceSize { width: parent.width / 1.2; height: parent.height / 1.2 } + width: Math.round(parent.width / 1.2) + height: Math.round(parent.height / 1.2) + sourceSize.width: height + sourceSize.height: height anchors.centerIn: parent + fillMode: Image.PreserveAspectFit + mipmap: true } } } } } diff --git a/src/activities/programmingMaze/HeaderArea.qml b/src/activities/programmingMaze/HeaderArea.qml index f74151c01..dcfd9ec32 100644 --- a/src/activities/programmingMaze/HeaderArea.qml +++ b/src/activities/programmingMaze/HeaderArea.qml @@ -1,63 +1,58 @@ /* GCompris - HeaderArea.qml * * Copyright (C) 2018 Aman Kumar Gupta * * Authors: * Aman Kumar Gupta + * Timothée Giet (Layout and graphics rework) * * 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" Rectangle { id: header width: background.width * 0.4 height: background.height / 10 + radius: 8 * ApplicationInfo.ratio border.width: 2 * ApplicationInfo.ratio - border.color: "black" - color: "transparent" + border.color: "#a6d8ea" + color: "#ffffff" + opacity: headerOpacity property real headerOpacity property string headerText signal clicked - Image { - width: parent.width - 2 * parent.border.width - height: parent.height - 2 * parent.border.width - anchors.centerIn: parent - source: "qrc:/gcompris/src/activities/guesscount/resource/backgroundW02.svg" - opacity: header.headerOpacity - - GCText { - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - width: parent.width - height: parent.height - fontSizeMode: Font.DemiBold - minimumPointSize: 7 - fontSize: mediumSize - wrapMode: Text.WordWrap - color: "white" - text: header.headerText - } + GCText { + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + width: parent.width + height: parent.height + fontSizeMode: Font.DemiBold + minimumPointSize: 7 + fontSize: mediumSize + wrapMode: Text.WordWrap + color: "#2e2f2f" + text: header.headerText } MouseArea { anchors.fill: parent onClicked: header.clicked() } } diff --git a/src/activities/programmingMaze/InstructionArea.qml b/src/activities/programmingMaze/InstructionArea.qml index e132727bb..ee3203373 100644 --- a/src/activities/programmingMaze/InstructionArea.qml +++ b/src/activities/programmingMaze/InstructionArea.qml @@ -1,182 +1,184 @@ /* GCompris - InstructionArea.qml * * Copyright (C) 2018 Aman Kumar Gupta * * Authors: * Aman Kumar Gupta + * Timothée Giet (Layout and graphics rework) * * 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 "programmingMaze.js" as Activity GridView { id: instructionArea width: parent.width * 0.5 height: parent.height * 0.17 cellWidth: background.buttonWidth cellHeight: background.buttonHeight anchors.left: parent.left anchors.top: mazeModel.bottom anchors.topMargin: background.height * 0.4 interactive: false model: instructionModel header: HeaderArea { width: instructionArea.width height: background.height / 11 headerOpacity: 1 headerText: qsTr("Choose the instructions") } property string instructionToInsert signal spaceKeyPressed signal tabKeyPressed onSpaceKeyPressed: { if(instructionArea.currentIndex != -1) instructionArea.currentItem.selectCurrentInstruction() } onTabKeyPressed: { instructionArea.currentIndex = -1 background.areaWithKeyboardInput = mainFunctionCodeArea } highlight: Rectangle { width: buttonWidth - 3 * ApplicationInfo.ratio height: buttonHeight * 1.18 - 3 * ApplicationInfo.ratio - color: "lightsteelblue" - border.width: 3.5 * ApplicationInfo.ratio - border.color: "purple" + color: "#00ffffff" + border.width: 3.5 * ApplicationInfo.ratio //activity.keyboardNavigationVisible ? 3.5 * ApplicationInfo.ratio : 0 + border.color: "#e77935" z: 2 radius: width / 18 - opacity: 0.6 - visible: activity.keyboardNavigationVisible } highlightFollowsCurrentItem: true keyNavigationWraps: true delegate: Item { id: instructionItem width: background.buttonWidth height: background.buttonHeight * 1.18 Rectangle { id: imageHolder width: parent.width - 3 * ApplicationInfo.ratio height: parent.height - 3 * ApplicationInfo.ratio border.width: 1.2 * ApplicationInfo.ratio - border.color: "black" + border.color: "#2a2a2a" anchors.centerIn: parent radius: width / 18 + color: instructionArea.instructionToInsert == name ? "#f3bc9a" : "#ffffff" Image { id: icon source: Activity.url + name + ".svg" - sourceSize { - width: parent.width / 1.2 - height: parent.height / 1.2 - } + width: Math.round(parent.width / 1.2) + height: Math.round(parent.height / 1.2) + sourceSize.width: height + sourceSize.height: height anchors.centerIn: parent + fillMode: Image.PreserveAspectFit + mipmap: true } } MouseArea { id: mouseArea anchors.fill: parent enabled: (items.isTuxMouseAreaEnabled || items.isRunCodeEnabled) && ((items.numberOfInstructionsAdded < items.maxNumberOfInstructionsAllowed) || procedureCodeArea.isEditingInstruction || mainFunctionCodeArea.isEditingInstruction) onPressed: instructionItem.checkModelAndInsert() } function selectCurrentInstruction() { if(!mainFunctionCodeArea.isEditingInstruction && !procedureCodeArea.isEditingInstruction) { instructionArea.instructionToInsert = name playClickedAnimation() } else { if(mainFunctionCodeArea.isEditingInstruction) insertIntoModel(mainFunctionModel, mainFunctionCodeArea) if(procedureCodeArea.isEditingInstruction && (name != Activity.CALL_PROCEDURE)) insertIntoModel(procedureModel, procedureCodeArea) } } function checkModelAndInsert() { if(items.constraintInstruction.opacity) items.constraintInstruction.hide() if(!background.insertIntoMain && (name != Activity.CALL_PROCEDURE)) insertIntoModel(procedureModel, procedureCodeArea) else if(background.insertIntoMain) insertIntoModel(mainFunctionModel, mainFunctionCodeArea) } /** * If we are adding an instruction, append it to the model if number of instructions added is less than the maximum number of instructions allowed. * If editing, replace it with the selected instruction in the code area. */ function insertIntoModel(model, area) { if(!area.isEditingInstruction) { if(items.numberOfInstructionsAdded >= items.maxNumberOfInstructionsAllowed) constraintInstruction.changeConstraintInstructionOpacity() else { playClickedAnimation() model.append({ "name": name }) items.numberOfInstructionsAdded++ } } else { playClickedAnimation() model.set(area.initialEditItemIndex, {"name": name}, 1) area.resetEditingValues() } } /** * If two successive clicks on the same icon are made very fast, stop the ongoing animation and set the scale back to 1. * Then start the animation for next click. * This gives proper feedback of multiple clicks. */ function playClickedAnimation() { clickedAnimation.stop() icon.scale = 1 clickedAnimation.start() } SequentialAnimation { id: clickedAnimation PropertyAnimation { target: imageHolder property: "scale" to: 0.8 duration: 150 } PropertyAnimation { target: imageHolder property: "scale" to: 1 duration: 150 } } } } diff --git a/src/activities/programmingMaze/ProgrammingMaze.qml b/src/activities/programmingMaze/ProgrammingMaze.qml index 4fc70d0b4..4e5c873d0 100644 --- a/src/activities/programmingMaze/ProgrammingMaze.qml +++ b/src/activities/programmingMaze/ProgrammingMaze.qml @@ -1,410 +1,424 @@ /* GCompris - ProgrammingMaze.qml * * Copyright (C) 2015 Siddhesh Suthar * Copyright (C) 2018 Aman Kumar Gupta * * Authors: * Siddhesh Suthar * Aman Kumar Gupta + * Timothée Giet (Layout and graphics rework) * * 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 "programmingMaze.js" as Activity ActivityBase { id: activity onStart: focus = true onStop: {} property int oldWidth: width onWidthChanged: { Activity.repositionObjectsOnWidthChanged(width / oldWidth) oldWidth = width } property int oldHeight: height onHeightChanged: { Activity.repositionObjectsOnHeightChanged(height / oldHeight) oldHeight = height } property bool keyboardNavigationVisible: false property string mode: "basic" property string datasetUrl: "qrc:/gcompris/src/activities/programmingMaze/Dataset.qml" pageComponent: Image { id: background - source: "qrc:/gcompris/src/activities/programmingMaze/resource/background.svg" + source: "qrc:/gcompris/src/activities/programmingMaze/resource/background-pm.svg" fillMode: Image.PreserveAspectCrop sourceSize.width: parent.width signal start signal stop property bool insertIntoMain: true property alias items: items property int buttonWidth: background.width / 10 property int buttonHeight: background.height / 15.3 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 GCSfx audioEffects: activity.audioEffects property alias mazeModel: mazeModel property alias instructionModel: instructionModel property alias mainFunctionModel: mainFunctionModel property alias mainFunctionCodeArea: mainFunctionCodeArea property alias procedureModel: procedureModel property alias procedureCodeArea: procedureCodeArea property alias instructionArea: instructionArea property alias player: player property alias constraintInstruction: constraintInstruction property alias tutorialImage: tutorialImage property alias fish: fish property alias dataset: dataset property bool isRunCodeEnabled: true property bool isTuxMouseAreaEnabled: false property bool currentLevelContainsProcedure property int maxNumberOfInstructionsAllowed property int numberOfInstructionsAdded } // This function catches the signal emitted after completion of movement of Tux after executing each instruction. function checkSuccessAndExecuteNextInstruction() { Activity.checkSuccessAndExecuteNextInstruction() } // This function catches the signal emitted after finding a dead-end in any of the executing instruction. function deadEnd() { Activity.deadEnd() } Loader { id: dataset } onStart: { Activity.start(items, mode, datasetUrl) } onStop: { Activity.stop() } property var areaWithKeyboardInput: instructionArea onAreaWithKeyboardInputChanged: activeCodeAreaIndicator.changeActiveCodeAreaIndicator(areaWithKeyboardInput) Keys.enabled: items.isTuxMouseAreaEnabled || items.isRunCodeEnabled Keys.onPressed: { activity.keyboardNavigationVisible = true if(event.key === Qt.Key_Left) areaWithKeyboardInput.moveCurrentIndexLeft() if(event.key === Qt.Key_Right) areaWithKeyboardInput.moveCurrentIndexRight() if(event.key === Qt.Key_Up) areaWithKeyboardInput.moveCurrentIndexUp() if(event.key === Qt.Key_Down) areaWithKeyboardInput.moveCurrentIndexDown() if(event.key === Qt.Key_Space) areaWithKeyboardInput.spaceKeyPressed() if(event.key === Qt.Key_Enter || event.key === Qt.Key_Return) runCodeOrResetTux() if(event.key === Qt.Key_Tab) areaWithKeyboardInput.tabKeyPressed() if(event.key === Qt.Key_Delete && activeCodeAreaIndicator.top != instructionArea.top) { areaWithKeyboardInput.deleteKeyPressed() } } function runCodeOrResetTux() { if(!Activity.deadEndPoint) runCodeMouseArea.executeCode() else Activity.initLevel() } ListModel { id: instructionModel } ListModel { id: mainFunctionModel } ListModel { id: procedureModel } Rectangle { id: constraintInstruction anchors.left: parent.left - anchors.bottom: runCode.top + anchors.top: instructionArea.bottom + anchors.topMargin: 5 * ApplicationInfo.ratio width: parent.width / 2.3 height: parent.height / 8.9 radius: 10 z: 3 color: "#E8E8E8" //paper white border.width: 3 * ApplicationInfo.ratio border.color: "#87A6DD" //light blue Behavior on opacity { PropertyAnimation { duration: 200 } } function changeConstraintInstructionOpacity() { if(opacity) constraintInstruction.hide() else constraintInstruction.show() } function show() { if(instructionText.text) opacity = 0.8 } function hide() { opacity = 0 } GCText { id: instructionText anchors.fill: parent anchors.margins: parent.border.width horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter fontSizeMode: Text.Fit wrapMode: Text.WordWrap readonly property string resetTuxInstructionText: qsTr("Click on Tux or press Enter key to reset it or RELOAD button to reload the level.") readonly property string constraintInstructionText: qsTr("Reach the fish in less than %1 instructions.").arg(items.maxNumberOfInstructionsAllowed + 1) text: items.isTuxMouseAreaEnabled ? resetTuxInstructionText : constraintInstructionText } } MouseArea { anchors.fill: parent onClicked: constraintInstruction.changeConstraintInstructionOpacity() } Repeater { id: mazeModel anchors.left: parent.left anchors.top: parent.top Image { x: modelData.x * width y: modelData.y * height width: background.width / 10 height: (background.height - background.height / 10) / 10 source: Activity.reverseCountUrl + "ice-block.svg" } } Image { id: fish sourceSize.width: background.width / 12 source: Activity.reverseCountUrl + "fish-blue.svg" } Image { id: player source: "qrc:/gcompris/src/activities/maze/resource/tux_top_south.svg" - sourceSize.width: background.width / 12 + width: background.width / 12 + sourceSize.width: width z: 1 property int duration: 1000 readonly property real playerCenterX: x + width / 2 readonly property real playerCenterY: y + height / 2 MouseArea { id: tuxMouseArea anchors.fill: parent enabled: items.isTuxMouseAreaEnabled onClicked: { Activity.initLevel() } } } Rectangle { id: activeCodeAreaIndicator + color: "#1dade4" opacity: 0.5 + radius: 8 * ApplicationInfo.ratio visible: activity.keyboardNavigationVisible + border.width: 2 * ApplicationInfo.ratio + border.color: "white" function changeActiveCodeAreaIndicator(activeArea) { anchors.top = activeArea.top anchors.fill = activeArea } } InstructionArea { id: instructionArea } HeaderArea { id: mainFunctionHeader headerText: qsTr("Main function") headerOpacity: background.insertIntoMain ? 1 : 0.5 onClicked: background.insertIntoMain = true anchors.top: parent.top anchors.right: parent.right } CodeArea { id: mainFunctionCodeArea currentModel: mainFunctionModel anchors.right: parent.right anchors.top: mainFunctionHeader.bottom onTabKeyPressed: { mainFunctionCodeArea.currentIndex = -1 if(!items.currentLevelContainsProcedure) { background.areaWithKeyboardInput = instructionArea instructionArea.currentIndex = 0 } else { background.areaWithKeyboardInput = procedureCodeArea background.insertIntoMain = false } } } HeaderArea { id: procedureHeader headerText: qsTr("Procedure") headerOpacity: !background.insertIntoMain ? 1 : 0.5 visible: procedureCodeArea.visible onClicked: background.insertIntoMain = false anchors.top: mainFunctionCodeArea.bottom anchors.right: parent.right } CodeArea { id: procedureCodeArea currentModel: procedureModel anchors.right: parent.right anchors.top: procedureHeader.bottom visible: items.currentLevelContainsProcedure property alias procedureIterator: procedureCodeArea.currentIndex onTabKeyPressed: { procedureCodeArea.currentIndex = -1 background.areaWithKeyboardInput = instructionArea instructionArea.currentIndex = 0 background.insertIntoMain = true } } - Image { - id: runCode - width: background.width / 10 - height: background.height / 10 - anchors.right: instructionArea.right - anchors.bottom: bar.top - anchors.margins: 10 * ApplicationInfo.ratio - - source:"qrc:/gcompris/src/core/resource/bar_ok.svg" - fillMode: Image.PreserveAspectFit - - MouseArea { - id: runCodeMouseArea - anchors.fill: parent - hoverEnabled: ApplicationInfo.isMobile ? false : (!items.isRunCodeEnabled ? false : true) - enabled: items.isRunCodeEnabled - - signal executeCode + Item { + id: runCodeLayout + height: constraintInstruction.height + anchors.left: constraintInstruction.right + anchors.right: mainFunctionCodeArea.left + anchors.verticalCenter: constraintInstruction.verticalCenter - onEntered: runCode.scale = 1.1 - onExecuteCode: { - if(mainFunctionModel.count) - startCodeExecution() + Image { + id: runCode + height: Math.min(parent.width, parent.height) + width: height + sourceSize.width: height + sourceSize.height: height + anchors.centerIn: parent + source:"qrc:/gcompris/src/core/resource/bar_ok.svg" + fillMode: Image.PreserveAspectFit + + MouseArea { + id: runCodeMouseArea + anchors.fill: parent + hoverEnabled: ApplicationInfo.isMobile ? false : (!items.isRunCodeEnabled ? false : true) + enabled: items.isRunCodeEnabled + + signal executeCode + + onEntered: runCode.scale = 1.1 + onExecuteCode: { + if(mainFunctionModel.count) + startCodeExecution() + } + onClicked: executeCode() + onExited: runCode.scale = 1 + + function startCodeExecution() { + runCodeClickAnimation.start() + Activity.resetCodeAreasIndices() + + if(constraintInstruction.opacity) + constraintInstruction.hide() + + Activity.runCode() + } } - onClicked: executeCode() - onExited: runCode.scale = 1 - - function startCodeExecution() { - runCodeClickAnimation.start() - Activity.resetCodeAreasIndices() - if(constraintInstruction.opacity) - constraintInstruction.hide() - - Activity.runCode() + SequentialAnimation { + id: runCodeClickAnimation + NumberAnimation { target: runCode; property: "scale"; to: 0.8; duration: 100 } + NumberAnimation { target: runCode; property: "scale"; to: 1.0; duration: 100 } } } - - SequentialAnimation { - id: runCodeClickAnimation - NumberAnimation { target: runCode; property: "scale"; to: 0.8; duration: 100 } - NumberAnimation { target: runCode; property: "scale"; to: 1.0; duration: 100 } - } } Image { id: tutorialImage source: "qrc:/gcompris/src/activities/guesscount/resource/backgroundW01.svg" anchors.fill: parent z: 5 visible: true property bool shownProcedureTutorialInstructions: false Tutorial { id:tutorialSection tutorialDetails: bar.level <= 2 ? Activity.mainTutorialInstructions : Activity.procedureTutorialInstructions onSkipPressed: { Activity.initLevel() tutorialImage.visible = false tutorialNumber = 0 } } onVisibleChanged: { if(tutorialImage.visible && tutorialImage.shownProcedureTutorialInstructions) tutorialSection.visible = true } } DialogHelp { id: dialogHelp onClose: home() } Bar { id: bar content: BarEnumContent { value: tutorialImage.visible ? help | home : help | home | level | reload } onHelpClicked: { displayDialog(dialogHelp) } onPreviousLevelClicked: Activity.previousLevel() onNextLevelClicked: Activity.nextLevel() onHomeClicked: activity.home() onReloadClicked: Activity.reloadLevel() } Bonus { id: bonus Component.onCompleted: win.connect(Activity.nextLevel) } } } diff --git a/src/activities/programmingMaze/programmingMaze.js b/src/activities/programmingMaze/programmingMaze.js index c355b9ab7..aa774f72c 100644 --- a/src/activities/programmingMaze/programmingMaze.js +++ b/src/activities/programmingMaze/programmingMaze.js @@ -1,358 +1,352 @@ /* GCompris - programmingMaze.js * * Copyright (C) 2015 Siddhesh Suthar * Copyright (C) 2018 Aman Kumar Gupta * * Authors: * Siddhesh Suthar * Aman Kumar Gupta + * Timothée Giet (Layout and graphics rework) * * 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 //for ApplicationInfo // possible instructions var MOVE_FORWARD = "move-forward" var TURN_LEFT = "turn-left" var TURN_RIGHT = "turn-right" var CALL_PROCEDURE = "call-procedure" var mazeBlocks // Length of 1 step along x-axis var stepX // Length of 1 step along y-axis var stepY /** * Lookup tables of instruction objects for main and procedure areas which will be stored here on creation and can be * accessed when required to execute. */ var mainInstructionObjects = [] var procedureInstructionObjects = [] // New rotation of Tux on turning. var changedRotation // Indicates if there is a dead-end var deadEndPoint = false // Stores the index of mainInstructionObjects[] which is going to be processed var codeIterator = 0 /** * Stores if the reset is done only when Tux is clicked. * * If resetTux is true, initLevel() is called and the instruction areas are not cleared. * * Else, it means that initLevel() is called to reset the entire level and the instruction areas are cleared as well. */ var resetTux = false -// Duration of movement of highlight in the execution area. -var moveAnimDuration - //Stores the currrent instruction which is going to be processed var currentInstruction var url = "qrc:/gcompris/src/activities/programmingMaze/resource/" var reverseCountUrl = "qrc:/gcompris/src/activities/reversecount/resource/" var currentLevel = 0 var numberOfLevel var items var NORTH = 0 var WEST = 90 var SOUTH = 180 var EAST = 270 /** * Stores the qml file components of all the instructions used in the activity. * * To add a new instruction, add its component here and add the instruction name in "instructionList" inside createInstructionObjects() along with the other instructions. */ var instructionComponents = { "move-forward": Qt.createComponent(url + "instructions/MoveForward.qml"), "turn-left": Qt.createComponent(url + "instructions/TurnLeftOrRight.qml"), "turn-right": Qt.createComponent(url + "instructions/TurnLeftOrRight.qml"), "call-procedure": Qt.createComponent(url + "instructions/Procedure.qml") } var mainTutorialInstructions = [ { "instruction": qsTr("Instruction Area:" + "There are 3 instructions which you have to use to code and make Tux reach the fish:" + "
  • 1. Move forward: Moves Tux one step forward in the direction it is facing.
  • " + "
  • 2. Turn left: Turns Tux in the left direction from where it is facing.
  • " + "
  • 3. Turn right: Turns Tux in the right direction from where it is facing.
  • "), "instructionImage": "qrc:/gcompris/src/activities/programmingMaze/resource/tutorial1.png" }, { "instruction": qsTr("Main Function:" + "
  • -The execution of code starts here on running.
  • " + "
  • -Click on any instruction in the instruction area to add them to the Main Function
  • " + "
  • -The instructions will execute in order until there's none left, dead-end or Tux reaches the fish.
  • "), "instructionImage": "qrc:/gcompris/src/activities/programmingMaze/resource/tutorial2.png" }, ] var procedureTutorialInstructions = [ { "instruction": qsTr("Procedure:" + "
  • -Procedure is a reusable set of instructions which can be used in a code by calling it where needed.
  • " + "
  • -To switch between the Procedure area and Main Function area to add your code, click on the label Procedure or Main Function.
  • "), "instructionImage": "qrc:/gcompris/src/activities/programmingMaze/resource/tutorial3.png" }, ] // Mode of the activity: basic or loop var activityMode function start(items_, mode_, datasetUrl_) { items = items_ items.dataset.source = datasetUrl_ activityMode = mode_ currentLevel = 0 mazeBlocks = items.dataset.item.levels numberOfLevel = mazeBlocks.length resetTux = false initLevel() } function stop() { destroyInstructionObjects() } /** * This function creates and populate instruction objects for main as well as procedure area. * * These are stored in the lookup table, provided in the parameter as "instructionObjects". * The instructions are then connected to the slots of their code area (main or procedure), provided as "instructionCodeArea" in the parameter. * * The instructions can now be obtained from the look-up tables and executed when called. * * This saves the process of re-creating all the instruction objets, connecting them to their parent's slot and destroying * them everytime for each instruction call which will be very redundant and quite memory consuming on devices with * less RAM, weak processing power and slow performance specially for "loops" mode. * * Hence these look-up table objects will be created and destroyed only once in each level (depending on the need) and can be accessed when needed. */ function createInstructionObjects(instructionObjects, instructionCodeArea) { var instructionList = [MOVE_FORWARD, TURN_LEFT, TURN_RIGHT] for(var i = 0; i < instructionList.length; i++) createInstruction(instructionObjects, instructionList[i], instructionCodeArea) } function createInstruction(instructionObjects, instructionName, instructionCodeArea) { if(instructionName == TURN_LEFT || instructionName == TURN_RIGHT) instructionObjects[instructionName] = instructionComponents[instructionName].createObject(instructionCodeArea, { "turnDirection": instructionName }) else instructionObjects[instructionName] = instructionComponents[instructionName].createObject(instructionCodeArea) instructionObjects[instructionName].foundDeadEnd.connect(instructionCodeArea.deadEnd) instructionObjects[instructionName].executionComplete.connect(instructionCodeArea.checkSuccessAndExecuteNextInstruction) } // Destroy instruction objects from the look-up tables function destroyInstructionObjects() { var instructionList = Object.keys(mainInstructionObjects) for(var i = 0; i < instructionList.length; i++) mainInstructionObjects[instructionList[i]].destroy() instructionList = Object.keys(procedureInstructionObjects) for(var i = 0; i < instructionList.length; i++) procedureInstructionObjects[instructionList[i]].destroy() mainInstructionObjects = [] procedureInstructionObjects = [] } function initLevel() { if(!items || !items.bar) return items.bar.level = currentLevel + 1 destroyInstructionObjects() var levelInstructions = mazeBlocks[currentLevel].instructions if(levelInstructions.indexOf(CALL_PROCEDURE) != -1) items.currentLevelContainsProcedure = true else items.currentLevelContainsProcedure = false // Create, populate and connect signals of instructions for main function code area and store them in mainInstructionObjects. createInstructionObjects(mainInstructionObjects, items.background) if(items.currentLevelContainsProcedure) { if(!items.tutorialImage.shownProcedureTutorialInstructions) { items.tutorialImage.shownProcedureTutorialInstructions = true items.tutorialImage.visible = true } // Create procedure object in the main look-up table ,if the level has procedure/loop, to execute it for procedure/loop calls from the main code area. createInstruction(mainInstructionObjects, CALL_PROCEDURE, items.background) // Create, populate and connect signals of instructions for procedure code area if the level has procedure/loop. createInstructionObjects(procedureInstructionObjects, mainInstructionObjects[CALL_PROCEDURE]) } // Stores the co-ordinates of the tile blocks in the current level var currentLevelBlocksCoordinates = mazeBlocks[currentLevel].map items.mazeModel.model = currentLevelBlocksCoordinates if(!resetTux) { items.mainFunctionModel.clear() items.procedureModel.clear() items.numberOfInstructionsAdded = 0 } stepX = items.mazeModel.itemAt(0).width stepY = items.mazeModel.itemAt(0).height items.instructionModel.clear() for (var i = 0; i < levelInstructions.length; i++) items.instructionModel.append({"name":levelInstructions[i]}) // Center Tux in its first case items.player.x = currentLevelBlocksCoordinates[0].x * stepX + (stepX - items.player.width) / 2 items.player.y = currentLevelBlocksCoordinates[0].y * stepY + (stepY - items.player.height) / 2 items.player.rotation = EAST // Center fish at it's co-ordinate items.fish.x = mazeBlocks[currentLevel].fish.x * stepX + (stepX - items.fish.width) / 2 items.fish.y = mazeBlocks[currentLevel].fish.y * stepY + (stepY - items.fish.height) / 2 changedRotation = EAST deadEndPoint = false - moveAnimDuration = 1000 - items.background.insertIntoMain = true - items.mainFunctionCodeArea.highlightMoveDuration = moveAnimDuration / 2 - items.procedureCodeArea.highlightMoveDuration = moveAnimDuration / 2 items.isTuxMouseAreaEnabled = false items.isRunCodeEnabled = true items.maxNumberOfInstructionsAllowed = mazeBlocks[currentLevel].maxNumberOfInstructions items.constraintInstruction.show() items.mainFunctionCodeArea.resetEditingValues() items.procedureCodeArea.resetEditingValues() items.background.areaWithKeyboardInput = items.instructionArea resetCodeAreasIndices() resetTux = false codeIterator = 0 } function resetCodeAreasIndices() { items.instructionArea.currentIndex = -1 items.mainFunctionCodeArea.currentIndex = -1 items.procedureCodeArea.currentIndex = -1 items.instructionArea.instructionToInsert = '' } function getPlayerRotation() { return ((changedRotation % 360) + 360) % 360 } function runCode() { items.mainFunctionCodeArea.resetEditingValues() items.procedureCodeArea.resetEditingValues() var instructionName // Append all the procedure instructions to the procedure area object. for(var j = 0; j < items.procedureModel.count; j++) { instructionName = items.procedureModel.get(j).name mainInstructionObjects[CALL_PROCEDURE].procedureCode.append({ "name" : instructionName }) } items.isRunCodeEnabled = false if(items.mainFunctionModel.count > 0) executeNextInstruction() else deadEnd() } function executeNextInstruction() { if((codeIterator < items.mainFunctionModel.count) && !deadEndPoint) { items.mainFunctionCodeArea.currentIndex += 1 var instructionToExecute = items.mainFunctionModel.get(codeIterator).name mainInstructionObjects[instructionToExecute].checkAndExecuteMovement() } } function deadEnd() { deadEndPoint = true resetTux = true items.isTuxMouseAreaEnabled = true items.constraintInstruction.show() items.audioEffects.play("qrc:/gcompris/src/core/resource/sounds/brick.wav") items.bonus.bad("tux") } function checkSuccessAndExecuteNextInstruction() { var fishX = mazeBlocks[currentLevel].fish.x var fishY = mazeBlocks[currentLevel].fish.y var tuxX = Math.floor(items.player.playerCenterX / stepX) var tuxY = Math.floor(items.player.playerCenterY / stepY) if(tuxX === fishX && tuxY === fishY) { codeIterator = 0 items.bonus.good("tux") } else if(codeIterator === (items.mainFunctionModel.count - 1)) { deadEnd() } else { codeIterator++ executeNextInstruction() } } function nextLevel() { resetTux = false if(numberOfLevel <= ++currentLevel) { currentLevel = 0 } initLevel(); } function previousLevel() { resetTux = false if(--currentLevel < 0) { currentLevel = numberOfLevel - 1 } initLevel(); } function repositionObjectsOnWidthChanged(factor) { resetTux = true if(items) initLevel() } function repositionObjectsOnHeightChanged(factor) { resetTux = true if(items) initLevel() } function reloadLevel() { resetTux = false initLevel() } diff --git a/src/activities/programmingMaze/programmingMaze.svg b/src/activities/programmingMaze/programmingMaze.svg index b3dd8b1d1..008421d88 100644 --- a/src/activities/programmingMaze/programmingMaze.svg +++ b/src/activities/programmingMaze/programmingMaze.svg @@ -1,146 +1,435 @@ + sodipodi:docname="programmingMaze.svg"> + id="defs11541"> + + + + + + + + inkscape:window-maximized="1" + inkscape:snap-global="false" /> image/svg+xml + + 2019 + + + Timothée Giet + + + + + + + + + + + - - - - - - - - - - - - - - + transform="matrix(0.17306643,0,0,0.17306643,-36.673612,-30.052924)" + id="layer1-6" + inkscape:label="Calque 1"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/programmingMaze/resource/README b/src/activities/programmingMaze/resource/README index 9deafcebc..5498472c9 100644 --- a/src/activities/programmingMaze/resource/README +++ b/src/activities/programmingMaze/resource/README @@ -1,5 +1,11 @@ -1. turn-left.svg: https://upload.wikimedia.org/wikipedia/commons/5/50/Simpleicons_Interface_undo-circular-arrow.svg By SimpleIcon http://www.simpleicon.com/ (http://www.flaticon.com/packs/simpleicon-interface) [CC BY 3.0 (http://creativecommons.org/licenses/by/3.0)] - -2. turn-right.svg: https://upload.wikimedia.org/wikipedia/commons/b/b5/Simpleicons_Interface_redo-circular-arrow.svg By SimpleIcon http://www.simpleicon.com/ (http://www.flaticon.com/packs/simpleicon-interface) [CC BY 3.0 (http://creativecommons.org/licenses/by/3.0)] - -3. move-forward.svg: https://www.flaticon.com/free-icon/arrow-pointing-to-up_66757 By Freepik \ No newline at end of file +Copyright: Timothée Giet, 2019 +License: CC-BY-SA 4.0 +Files: +background-pm.svg +call-procedure.svg +move-forward.svg +turn-left.svg +turn-right.svg +tutorial1.png +tutorial2.png +tutorial3.png diff --git a/src/activities/programmingMaze/resource/background-pm.svg b/src/activities/programmingMaze/resource/background-pm.svg new file mode 100644 index 000000000..238897ff3 --- /dev/null +++ b/src/activities/programmingMaze/resource/background-pm.svg @@ -0,0 +1,131 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + 2019 + + + Timothée Giet + + + + + + + + + + + + + + + + + + + + diff --git a/src/activities/programmingMaze/resource/background.svg b/src/activities/programmingMaze/resource/background.svg deleted file mode 100644 index 4f86b568e..000000000 --- a/src/activities/programmingMaze/resource/background.svg +++ /dev/null @@ -1,822 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - diff --git a/src/activities/programmingMaze/resource/call-procedure.svg b/src/activities/programmingMaze/resource/call-procedure.svg index 0ad22c6af..e9254a31c 100644 --- a/src/activities/programmingMaze/resource/call-procedure.svg +++ b/src/activities/programmingMaze/resource/call-procedure.svg @@ -1,60 +1,121 @@ + + + + + + + id="metadata1472"> image/svg+xml - + + 2019 + + + Timothée Giet + + + + + + + + + + + - - - + + + + + + + + + diff --git a/src/activities/programmingMaze/resource/instructions/Instruction.qml b/src/activities/programmingMaze/resource/instructions/Instruction.qml index 16063a07e..a26e83d3f 100644 --- a/src/activities/programmingMaze/resource/instructions/Instruction.qml +++ b/src/activities/programmingMaze/resource/instructions/Instruction.qml @@ -1,51 +1,51 @@ /* GCompris - Instruction.qml * * Copyright (C) 2018 Aman Kumar Gupta * * Author: * Aman Kumar Gupta * * 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 "../../programmingMaze.js" as Activity Item { id: instruction //stores the movement animation duration of Tux when an instruction is executed. property real movementAnimationDuration /** * This signal is emitted after the execution of current instruction is complete. * * The signal will be caught by ProgrammingMaze.qml/Procedure.qml depending on with whom the connection is made, * and it will check if Tux has reached the fish(level is complete) or will execute the next instruction. */ signal executionComplete /** * This signal is emitted if Tux cannot move according to the current executed instruction. * * It will be caught by deadEnd() in its parent file. */ signal foundDeadEnd - function setCodeAreaHighlightMoveDuration() { - Activity.items.mainFunctionCodeArea.highlightMoveDuration = movementAnimationDuration - Activity.items.procedureCodeArea.highlightMoveDuration = movementAnimationDuration - } +// function setCodeAreaHighlightMoveDuration() { +// Activity.items.mainFunctionCodeArea.highlightMoveDuration = movementAnimationDuration +// Activity.items.procedureCodeArea.highlightMoveDuration = movementAnimationDuration +// } } diff --git a/src/activities/programmingMaze/resource/instructions/MoveForward.qml b/src/activities/programmingMaze/resource/instructions/MoveForward.qml index 2a214e42f..55356bb93 100644 --- a/src/activities/programmingMaze/resource/instructions/MoveForward.qml +++ b/src/activities/programmingMaze/resource/instructions/MoveForward.qml @@ -1,104 +1,102 @@ /* GCompris - MoveForward.qml * * Copyright (C) 2018 Aman Kumar Gupta * * Author: * Aman Kumar Gupta * * 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 "../../programmingMaze.js" as Activity Instruction { id: moveForward movementAnimationDuration: 2000 property double playerXCoordinate: 0 property double playerYCoordinate: 0 //If there has been an x-axis movement, x co-ordinate will be animated without any effect on y-axis movement and same vice-versa. ParallelAnimation { id: movementAnimation SmoothedAnimation { target: Activity.items.player property: 'x' to: playerXCoordinate duration: moveForward.movementAnimationDuration reversingMode: SmoothedAnimation.Immediate } SmoothedAnimation { target: Activity.items.player property: 'y' to: playerYCoordinate duration: moveForward.movementAnimationDuration reversingMode: SmoothedAnimation.Immediate } onStopped: executionComplete() } function nextPositionExists(playerCenterX, playerCenterY) { var playerNextPositionX = Math.floor(playerCenterX / Activity.stepX) var playerNextPositionY = Math.floor(playerCenterY / Activity.stepY) var currentLevelCoordinates = Activity.mazeBlocks[Activity.currentLevel].map for(var i = 0; i < currentLevelCoordinates.length; i++) { if(currentLevelCoordinates[i].x == playerNextPositionX && currentLevelCoordinates[i].y == playerNextPositionY) return true } return false } //Function to check if the current movement is possible or not and then process the instruction accordingly function checkAndExecuteMovement() { var currentRotation = Activity.getPlayerRotation() var playerCenterX = Activity.items.player.playerCenterX var playerCenterY = Activity.items.player.playerCenterY var nextTileExists = false moveForward.playerXCoordinate = Activity.items.player.x moveForward.playerYCoordinate = Activity.items.player.y if(currentRotation === Activity.EAST) { playerCenterX += Activity.stepX moveForward.playerXCoordinate += Activity.stepX } else if(currentRotation === Activity.WEST) { playerCenterX -= Activity.stepX moveForward.playerXCoordinate -= Activity.stepX } else if(currentRotation === Activity.SOUTH) { playerCenterY -= Activity.stepY moveForward.playerYCoordinate -= Activity.stepY } else if(currentRotation === Activity.NORTH) { playerCenterY += Activity.stepY moveForward.playerYCoordinate += Activity.stepY } nextTileExists = nextPositionExists(playerCenterX, playerCenterY) - setCodeAreaHighlightMoveDuration() - if(nextTileExists) { movementAnimation.start() } else foundDeadEnd() } } diff --git a/src/activities/programmingMaze/resource/instructions/TurnLeftOrRight.qml b/src/activities/programmingMaze/resource/instructions/TurnLeftOrRight.qml index bb7fad59c..80f7e15e9 100644 --- a/src/activities/programmingMaze/resource/instructions/TurnLeftOrRight.qml +++ b/src/activities/programmingMaze/resource/instructions/TurnLeftOrRight.qml @@ -1,55 +1,53 @@ /* GCompris - TurnLeftOrRight.qml * * Copyright (C) 2018 Aman Kumar Gupta * * Author: * Aman Kumar Gupta * * 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 "../../programmingMaze.js" as Activity Instruction { id: turnLeftOrRight movementAnimationDuration: 1000 property real finalRotation: 0 property string turnDirection RotationAnimation { id: movementAnimation target: Activity.items.player to: finalRotation duration: turnLeftOrRight.movementAnimationDuration direction: RotationAnimation.Shortest onStopped: executionComplete() } function checkAndExecuteMovement() { var currentRotation = Activity.getPlayerRotation() if(turnLeftOrRight.turnDirection === "turn-left") Activity.changedRotation = (currentRotation - 90) % 360 else Activity.changedRotation = (currentRotation + 90) % 360 - setCodeAreaHighlightMoveDuration() - turnLeftOrRight.finalRotation = Activity.changedRotation movementAnimation.start() } } diff --git a/src/activities/programmingMaze/resource/move-forward.svg b/src/activities/programmingMaze/resource/move-forward.svg index a06c0bc07..4dc3dbbf9 100644 --- a/src/activities/programmingMaze/resource/move-forward.svg +++ b/src/activities/programmingMaze/resource/move-forward.svg @@ -1,43 +1,92 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + image/svg+xml + + + 2019 + + + Timothée Giet + + + + + + + + + + + + + + + + + + diff --git a/src/activities/programmingMaze/resource/turn-left.svg b/src/activities/programmingMaze/resource/turn-left.svg index ea81fc8d0..dc50abb30 100644 --- a/src/activities/programmingMaze/resource/turn-left.svg +++ b/src/activities/programmingMaze/resource/turn-left.svg @@ -1 +1,91 @@ - \ No newline at end of file + + + + + + + + + + + + image/svg+xml + + + 2019 + + + Timothée Giet + + + + + + + + + + + + + + + + + + diff --git a/src/activities/programmingMaze/resource/turn-right.svg b/src/activities/programmingMaze/resource/turn-right.svg index aaae8db9c..edf9e6726 100644 --- a/src/activities/programmingMaze/resource/turn-right.svg +++ b/src/activities/programmingMaze/resource/turn-right.svg @@ -1 +1,91 @@ - \ No newline at end of file + + + + + + + + + + + + image/svg+xml + + + 2019 + + + Timothée Giet + + + + + + + + + + + + + + + + + + diff --git a/src/activities/programmingMaze/resource/tutorial1.png b/src/activities/programmingMaze/resource/tutorial1.png index 76635de1b..0d095b21e 100644 Binary files a/src/activities/programmingMaze/resource/tutorial1.png and b/src/activities/programmingMaze/resource/tutorial1.png differ diff --git a/src/activities/programmingMaze/resource/tutorial2.png b/src/activities/programmingMaze/resource/tutorial2.png index f9dbaf827..144249dc7 100644 Binary files a/src/activities/programmingMaze/resource/tutorial2.png and b/src/activities/programmingMaze/resource/tutorial2.png differ diff --git a/src/activities/programmingMaze/resource/tutorial3.png b/src/activities/programmingMaze/resource/tutorial3.png index 94880472b..1f6a5cc36 100644 Binary files a/src/activities/programmingMaze/resource/tutorial3.png and b/src/activities/programmingMaze/resource/tutorial3.png differ