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 }