Warning, /education/gcompris/src/activities/programmingMaze/ProgrammingMaze.qml is written in an unsupported language. File is not indexed.

0001 /* GCompris - ProgrammingMaze.qml
0002  *
0003  * SPDX-FileCopyrightText: 2015 Siddhesh Suthar <siddhesh.it@gmail.com>
0004  * SPDX-FileCopyrightText: 2018 Aman Kumar Gupta <gupta2140@gmail.com>
0005  *
0006  * Authors:
0007  *   Siddhesh Suthar <siddhesh.it@gmail.com>
0008  *   Aman Kumar Gupta <gupta2140@gmail.com>
0009  *   Timothée Giet <animtim@gcompris.net> (Layout and graphics rework)
0010  *
0011  *   SPDX-License-Identifier: GPL-3.0-or-later
0012  */
0013 import QtQuick 2.12
0014 import GCompris 1.0
0015 import "../../core"
0016 
0017 import "programmingMaze.js" as Activity
0018 
0019 ActivityBase {
0020     id: activity
0021 
0022     onStart: focus = true
0023     onStop: {}
0024 
0025     property int oldWidth: width
0026     onWidthChanged: {
0027         Activity.repositionObjectsOnWidthChanged(width / oldWidth)
0028         oldWidth = width
0029     }
0030 
0031     property int oldHeight: height
0032     onHeightChanged: {
0033         Activity.repositionObjectsOnHeightChanged(height / oldHeight)
0034         oldHeight = height
0035     }
0036 
0037     property bool keyboardNavigationVisible: false
0038 
0039     pageComponent: Image {
0040         id: background
0041         source: "qrc:/gcompris/src/activities/programmingMaze/resource/background-pm.svg"
0042         fillMode: Image.PreserveAspectCrop
0043         sourceSize.width: width
0044         sourceSize.height: height
0045 
0046         signal start
0047         signal stop
0048 
0049         property bool insertIntoMain: true
0050         property alias items: items
0051         property int buttonWidth: background.width / 10
0052         property int buttonHeight: background.height / 15.3
0053 
0054         Component.onCompleted: {
0055             activity.start.connect(start)
0056             activity.stop.connect(stop)
0057         }
0058 
0059         // Add here the QML items you need to access in javascript
0060         QtObject {
0061             id: items
0062             property Item main: activity.main
0063             property alias background: background
0064             property int currentLevel: activity.currentLevel
0065             property alias bonus: bonus
0066             property GCSfx audioEffects: activity.audioEffects
0067             readonly property var levels: activity.datasetLoader.data
0068             property alias mazeModel: mazeModel
0069             property alias instructionModel: instructionModel
0070             property alias mainFunctionModel: mainFunctionModel
0071             property alias mainFunctionCodeArea: mainFunctionCodeArea
0072             property alias procedureModel: procedureModel
0073             property alias procedureCodeArea: procedureCodeArea
0074             property alias instructionArea: instructionArea
0075             property alias player: player
0076             property alias constraintInstruction: constraintInstruction
0077             property alias tutorialImage: tutorialImage
0078             property alias fish: fish
0079             property alias loopCounterSelection: loopCounterSelection
0080             property bool isRunCodeEnabled: true
0081             property bool currentLevelContainsProcedure
0082             property bool currentLevelContainsLoop
0083             property int maxNumberOfInstructionsAllowed
0084             property int numberOfInstructionsAdded
0085             // When the activity is stopped, avoid calling initLevel on activity width/height changed
0086             property bool activityStopped: false
0087         }
0088 
0089         // This function catches the signal emitted after completion of movement of Tux after executing each instruction.
0090         function checkSuccessAndExecuteNextInstruction() {
0091             Activity.checkSuccessAndExecuteNextInstruction()
0092         }
0093 
0094         // This function catches the signal emitted after finding a dead-end in any of the executing instruction.
0095         function deadEnd() {
0096             Activity.deadEnd()
0097         }
0098 
0099         Loader {
0100             id: dataset
0101         }
0102 
0103         onStart: { Activity.start(items) }
0104         onStop: {
0105             items.activityStopped = true
0106             Activity.stop()
0107         }
0108 
0109         property var areaWithKeyboardInput: instructionArea
0110 
0111         onAreaWithKeyboardInputChanged: activeCodeAreaIndicator.changeActiveCodeAreaIndicator(areaWithKeyboardInput)
0112 
0113         // Needed to get keyboard focus on Tutorial
0114         Keys.forwardTo: tutorialSection
0115 
0116         Keys.enabled: items.isRunCodeEnabled
0117         Keys.onPressed: {
0118             activity.keyboardNavigationVisible = true
0119             if(event.key === Qt.Key_Left)
0120                 areaWithKeyboardInput.moveCurrentIndexLeft()
0121             if(event.key === Qt.Key_Right)
0122                 areaWithKeyboardInput.moveCurrentIndexRight()
0123             if(event.key === Qt.Key_Up && items.currentLevelContainsLoop && !background.insertIntoMain)
0124                 increaseButton.increaseClicked()
0125             if(event.key === Qt.Key_Down && items.currentLevelContainsLoop && !background.insertIntoMain)
0126                 decreaseButton.decreaseClicked()
0127             if(event.key === Qt.Key_Space)
0128                 areaWithKeyboardInput.spaceKeyPressed()
0129             if(event.key === Qt.Key_Enter || event.key === Qt.Key_Return)
0130                 runCodeOrResetTux()
0131             if(event.key === Qt.Key_Tab)
0132                 areaWithKeyboardInput.tabKeyPressed()
0133             if(event.key === Qt.Key_Delete && activeCodeAreaIndicator.top !== instructionArea.top) {
0134                 areaWithKeyboardInput.deleteKeyPressed()
0135             }
0136         }
0137 
0138         function runCodeOrResetTux() {
0139             if(!Activity.deadEndPoint)
0140                 runCodeMouseArea.executeCode()
0141             else
0142                 Activity.initLevel()
0143         }
0144 
0145         ListModel {
0146             id: instructionModel
0147         }
0148 
0149         ListModel {
0150             id: mainFunctionModel
0151         }
0152 
0153         //If mode is "procedures", then this model will store instructions exist in the procedure area.
0154         //If mode is "loops", then this model will store Instructions exist in the loop area.
0155         ListModel {
0156             id: procedureModel
0157         }
0158 
0159         Rectangle {
0160             id: constraintInstruction
0161             anchors.left: parent.left
0162             anchors.top: instructionArea.bottom
0163             anchors.topMargin: 5 * ApplicationInfo.ratio
0164             width: parent.width / 2.3
0165             height: parent.height / 8.9
0166             radius: 10
0167             z: 3
0168             color: "#E8E8E8" //paper white
0169             border.width: 3 * ApplicationInfo.ratio
0170             border.color: "#87A6DD"  //light blue
0171 
0172             Behavior on opacity { PropertyAnimation { duration: 200 } }
0173 
0174             function changeConstraintInstructionOpacity() {
0175                 if(opacity)
0176                     constraintInstruction.hide()
0177                 else
0178                     constraintInstruction.show()
0179             }
0180 
0181             function show() {
0182                 if(instructionText.text)
0183                     opacity = 0.8
0184             }
0185             function hide() {
0186                 opacity = 0
0187             }
0188 
0189             GCText {
0190                 id: instructionText
0191                 anchors.fill: parent
0192                 anchors.margins: parent.border.width
0193                 horizontalAlignment: Text.AlignHCenter
0194                 verticalAlignment: Text.AlignVCenter
0195                 fontSizeMode: Text.Fit
0196                 wrapMode: Text.WordWrap
0197 
0198                 readonly property string constraintInstructionText: qsTr("Reach the fish in less than %1 instructions.").arg(items.maxNumberOfInstructionsAllowed + 1)
0199 
0200                 text: constraintInstructionText
0201             }
0202         }
0203 
0204         MouseArea {
0205             anchors.fill: parent
0206             onClicked: constraintInstruction.changeConstraintInstructionOpacity()
0207         }
0208 
0209         Repeater {
0210             id: mazeModel
0211 
0212             anchors.left: parent.left
0213             anchors.top: parent.top
0214 
0215             Image {
0216                 x: modelData.x * width
0217                 y: modelData.y * height
0218                 width: background.width / 10
0219                 height: (background.height - background.height / 10) / 10
0220                 source: Activity.reverseCountUrl + "ice-block.svg"
0221             }
0222         }
0223 
0224         Image {
0225             id: fish
0226             sourceSize.width: background.width / 12
0227             source: Activity.reverseCountUrl + "fish-blue.svg"
0228         }
0229 
0230         Image {
0231             id: player
0232             source: "qrc:/gcompris/src/activities/maze/resource/tux_top_south.svg"
0233             width: background.width / 12
0234             sourceSize.width: width
0235             z: 1
0236             property int duration: 1000
0237             readonly property real playerCenterX: x + width / 2
0238             readonly property real playerCenterY: y + height / 2
0239         }
0240 
0241         Rectangle {
0242             id: activeCodeAreaIndicator
0243             color: "#1dade4"
0244             opacity: 0.5
0245             radius: 8 * ApplicationInfo.ratio
0246             visible: activity.keyboardNavigationVisible
0247             border.width: 2 * ApplicationInfo.ratio
0248             border.color: "white"
0249 
0250             function changeActiveCodeAreaIndicator(activeArea) {
0251                 anchors.top = activeArea.top
0252                 anchors.fill = activeArea
0253             }
0254         }
0255 
0256         InstructionArea {
0257             id: instructionArea
0258         }
0259 
0260         HeaderArea {
0261             id: mainFunctionHeader
0262             headerText: qsTr("Main function")
0263             headerOpacity: background.insertIntoMain ? 1 : 0.5
0264             onClicked: background.insertIntoMain = true
0265             anchors.top: parent.top
0266             anchors.right: parent.right
0267         }
0268 
0269         CodeArea {
0270             id: mainFunctionCodeArea
0271             currentModel: mainFunctionModel
0272             anchors.right: parent.right
0273             anchors.top: mainFunctionHeader.bottom
0274 
0275             onTabKeyPressed: {
0276                 mainFunctionCodeArea.currentIndex = -1
0277                 if(!items.currentLevelContainsProcedure && !items.currentLevelContainsLoop) {
0278                     background.areaWithKeyboardInput = instructionArea
0279                     instructionArea.currentIndex = 0
0280                 }
0281                 else {
0282                     background.areaWithKeyboardInput = procedureCodeArea
0283                     background.insertIntoMain = false
0284                 }
0285             }
0286         }
0287 
0288         HeaderArea {
0289             id: procedureHeader
0290             headerText: items.currentLevelContainsProcedure ? qsTr("Procedure") + " " : qsTr("Loop") + " "
0291             headerIcon: items.currentLevelContainsProcedure ? "call-procedure" : "execute-loop"
0292             headerOpacity: !background.insertIntoMain ? 1 : 0.5
0293             visible: procedureCodeArea.visible
0294             onClicked: background.insertIntoMain = false
0295             anchors.top: mainFunctionCodeArea.bottom
0296             anchors.right: parent.right
0297         }
0298 
0299         Item {
0300             id: loopCounterSelection
0301             visible: items.currentLevelContainsLoop ? true : false
0302             width: procedureHeader.width * 0.75
0303             height: background.buttonHeight * 1.1
0304             anchors.top: procedureHeader.bottom
0305             anchors.horizontalCenter: procedureHeader.horizontalCenter
0306 
0307             signal setLoopNumber()
0308             onSetLoopNumber: {
0309                 Activity.loopsNumber = loopCounterSelection.loopNumber
0310                 Activity.createLoopObjectAndInstructions()
0311             }
0312 
0313             readonly property int minLoopNumber: 1
0314             readonly property int maxLoopNumber: 9
0315 
0316             property int loopNumber: minLoopNumber
0317 
0318             Rectangle {
0319                 id: decreaseButton
0320                 width: parent.width * 0.3
0321                 height: background.buttonHeight
0322                 anchors.left: parent.left
0323                 anchors.verticalCenter: parent.verticalCenter
0324                 border.width: 1.2 * ApplicationInfo.ratio
0325                 border.color: "grey"
0326                 radius: decreaseButton.width * 0.1
0327 
0328                 GCText {
0329                     id: decreaseSign
0330                     anchors.fill: parent
0331                     horizontalAlignment: Text.AlignHCenter
0332                     verticalAlignment: Text.AlignVCenter
0333                     fontSizeMode: Text.Fit
0334                     wrapMode: Text.WordWrap
0335                     color: "#373737"
0336                     text: Activity.LoopEnumValues.MINUS_SIGN
0337                 }
0338 
0339                 function decreaseClicked() {
0340                     if(!decreaseButtonArea.enabled)
0341                         return
0342                     decreaseAnimation.restart()
0343                     if(loopCounterSelection.loopNumber == loopCounterSelection.minLoopNumber) {
0344                         loopCounterSelection.loopNumber = loopCounterSelection.maxLoopNumber
0345                     }
0346                     else {
0347                         loopCounterSelection.loopNumber--
0348                     }
0349                     loopCounterSelection.setLoopNumber()
0350                 }
0351 
0352                 MouseArea {
0353                     id: decreaseButtonArea
0354                     anchors.fill: parent
0355                     enabled: items.isRunCodeEnabled
0356                     onClicked: {
0357                         decreaseButton.decreaseClicked()
0358                     }
0359                 }
0360                 SequentialAnimation {
0361                     id: decreaseAnimation
0362                     PropertyAnimation {
0363                         target: decreaseButton
0364                         property: "scale"
0365                         to: 0.8
0366                         duration: 150
0367                     }
0368 
0369                     PropertyAnimation {
0370                         target: decreaseButton
0371                         property: "scale"
0372                         to: 1
0373                         duration: 150
0374                     }
0375                 }
0376             }
0377 
0378             Rectangle {
0379                 id: loopCounter
0380                 width: parent.width * 0.3
0381                 height: background.buttonHeight
0382                 anchors.horizontalCenter: parent.horizontalCenter
0383                 anchors.verticalCenter: parent.verticalCenter
0384                 border.width: 1.2 * ApplicationInfo.ratio
0385                 border.color: "grey"
0386                 radius: loopCounter.width * 0.1
0387 
0388                 GCText {
0389                     id: loopCounterText
0390                     anchors.fill: parent
0391                     horizontalAlignment: Text.AlignHCenter
0392                     verticalAlignment: Text.AlignVCenter
0393                     fontSizeMode: Text.Fit
0394                     wrapMode: Text.WordWrap
0395                     color: "#373737"
0396                     text: loopCounterSelection.loopNumber
0397                 }
0398             }
0399 
0400             Rectangle {
0401                 id: increaseButton
0402                 width: parent.width * 0.3
0403                 height: background.buttonHeight
0404                 anchors.right: parent.right
0405                 anchors.verticalCenter: parent.verticalCenter
0406                 border.width: 1.2 * ApplicationInfo.ratio
0407                 border.color: "grey"
0408                 radius: increaseButton.width * 0.1
0409 
0410                 GCText {
0411                     id: increaseSign
0412                     anchors.fill: parent
0413                     horizontalAlignment: Text.AlignHCenter
0414                     verticalAlignment: Text.AlignVCenter
0415                     fontSizeMode: Text.Fit
0416                     wrapMode: Text.WordWrap
0417                     color: "#373737"
0418                     text: Activity.LoopEnumValues.PLUS_SIGN
0419                 }
0420 
0421                 function increaseClicked() {
0422                     if(!increaseButtonArea.enabled)
0423                         return
0424                     increaseAnimation.restart()
0425                     if(loopCounterSelection.loopNumber == loopCounterSelection.maxLoopNumber) {
0426                         loopCounterSelection.loopNumber = loopCounterSelection.minLoopNumber
0427                     }
0428                     else {
0429                         loopCounterSelection.loopNumber++
0430                     }
0431                     loopCounterSelection.setLoopNumber()
0432                 }
0433 
0434                 MouseArea {
0435                     id: increaseButtonArea
0436                     anchors.fill: parent
0437                     enabled: items.isRunCodeEnabled
0438                     onClicked: {
0439                         increaseButton.increaseClicked()
0440                     }
0441                 }
0442                 SequentialAnimation {
0443                     id: increaseAnimation
0444                     PropertyAnimation {
0445                         target: increaseButton
0446                         property: "scale"
0447                         to: 0.8
0448                         duration: 150
0449                     }
0450 
0451                     PropertyAnimation {
0452                         target: increaseButton
0453                         property: "scale"
0454                         to: 1
0455                         duration: 150
0456                     }
0457                 }
0458             }
0459         }
0460 
0461         CodeArea {
0462             id: procedureCodeArea
0463             height: items.currentLevelContainsLoop ? background.height * 0.29 - loopCounterSelection.height : background.height * 0.29
0464             currentModel: procedureModel
0465             anchors.right: parent.right
0466             anchors.top: items.currentLevelContainsLoop ? loopCounterSelection.bottom : procedureHeader.bottom
0467             visible: items.currentLevelContainsProcedure || items.currentLevelContainsLoop ? true : false
0468 
0469             property alias procedureIterator: procedureCodeArea.currentIndex
0470 
0471             onTabKeyPressed: {
0472                 procedureCodeArea.currentIndex = -1
0473                 background.areaWithKeyboardInput = instructionArea
0474                 instructionArea.currentIndex = 0
0475                 background.insertIntoMain = true
0476             }
0477         }
0478 
0479         Item {
0480             id: runCodeLayout
0481             height: constraintInstruction.height
0482             anchors.left: constraintInstruction.right
0483             anchors.right: mainFunctionCodeArea.left
0484             anchors.verticalCenter: constraintInstruction.verticalCenter
0485 
0486             Image {
0487                 id: runCode
0488                 height: Math.min(parent.width, parent.height)
0489                 width: height
0490                 sourceSize.width: height
0491                 sourceSize.height: height
0492                 anchors.centerIn: parent
0493                 source:"qrc:/gcompris/src/core/resource/bar_ok.svg"
0494                 fillMode: Image.PreserveAspectFit
0495 
0496                 MouseArea {
0497                     id: runCodeMouseArea
0498                     anchors.fill: parent
0499                     hoverEnabled: ApplicationInfo.isMobile ? false : (!items.isRunCodeEnabled ? false : true)
0500                     enabled: items.isRunCodeEnabled
0501 
0502                     signal executeCode
0503 
0504                     onEntered: runCode.scale = 1.1
0505                     onExecuteCode: {
0506                         if(mainFunctionModel.count)
0507                             startCodeExecution()
0508                     }
0509                     onClicked: executeCode()
0510                     onExited: runCode.scale = 1
0511 
0512                     function startCodeExecution() {
0513                         runCodeClickAnimation.start()
0514                         Activity.resetCodeAreasIndices()
0515 
0516                         if(constraintInstruction.opacity)
0517                             constraintInstruction.hide()
0518 
0519                         Activity.runCode()
0520                     }
0521                 }
0522 
0523                 SequentialAnimation {
0524                     id: runCodeClickAnimation
0525                     NumberAnimation { target: runCode; property: "scale"; to: 0.8; duration: 100 }
0526                     NumberAnimation { target: runCode; property: "scale"; to: 1.0; duration: 100 }
0527                 }
0528             }
0529         }
0530 
0531         Image {
0532             id: tutorialImage
0533             source: "qrc:/gcompris/src/activities/guesscount/resource/backgroundW01.svg"
0534             anchors.fill: parent
0535             z: 5
0536             visible: true
0537 
0538             property bool shownProcedureTutorialInstructions: false
0539             property bool shownLoopTutorialInstructions: false
0540 
0541             Tutorial {
0542                 id:tutorialSection
0543                 tutorialDetails: tutorialImage.selectInstructionTutorial()
0544                 useImage: false
0545                 onSkipPressed: {
0546                     Activity.initLevel()
0547                     tutorialImage.visible = false
0548                     tutorialNumber = 0
0549                 }
0550             }
0551             onVisibleChanged: {
0552                 if(tutorialImage.visible && (tutorialImage.shownProcedureTutorialInstructions || tutorialImage.shownLoopTutorialInstructions))
0553                     tutorialSection.visible = true
0554             }
0555 
0556             function selectInstructionTutorial() {
0557                 if(!tutorialSection.visible) {
0558                     return "";
0559                 }
0560 
0561                 var nextLevelInstructions = items.levels[items.currentLevel].instructions
0562                 if(nextLevelInstructions.indexOf(Activity.EXECUTE_LOOPS) !== -1) {
0563                     return Activity.loopTutorialInstructions;
0564                 }
0565 
0566                 if(nextLevelInstructions.indexOf(Activity.CALL_PROCEDURE) !== -1) {
0567                     return Activity.procedureTutorialInstructions;
0568                 }
0569 
0570                 return Activity.mainTutorialInstructions;
0571             }
0572         }
0573 
0574         DialogHelp {
0575             id: dialogHelp
0576             onClose: home()
0577         }
0578 
0579         DialogChooseLevel {
0580             id: dialogActivityConfig
0581             currentActivity: activity.activityInfo
0582 
0583             onSaveData: {
0584                 levelFolder = dialogActivityConfig.chosenLevels
0585                 currentActivity.currentLevels = dialogActivityConfig.chosenLevels
0586                 ApplicationSettings.setCurrentLevels(currentActivity.name, dialogActivityConfig.chosenLevels)
0587             }
0588             onClose: {
0589                 home()
0590             }
0591             onStartActivity: {
0592                 background.stop()
0593                 background.start()
0594             }
0595         }
0596 
0597         Bar {
0598             id: bar
0599             level: items.currentLevel + 1
0600             content: BarEnumContent { value: tutorialImage.visible ? help | home : help | home | level | reload | activityConfig}
0601             onHelpClicked: {
0602                 displayDialog(dialogHelp)
0603             }
0604             onPreviousLevelClicked: Activity.previousLevel()
0605             onNextLevelClicked: Activity.nextLevel()
0606             onHomeClicked: home()
0607             onReloadClicked: Activity.reloadLevel()
0608             onActivityConfigClicked: {
0609                 displayDialog(dialogActivityConfig)
0610             }
0611         }
0612 
0613         Bonus {
0614             id: bonus
0615             Component.onCompleted: {
0616                 win.connect(Activity.nextLevel)
0617                 loose.connect(Activity.resetTuxPosition)
0618             }
0619         }
0620     }
0621 }