Warning, /education/gcompris/src/activities/balancebox/editor/BalanceboxEditor.qml is written in an unsupported language. File is not indexed.
0001 /* GCompris - BalanceboxEditor.qml 0002 * 0003 * SPDX-FileCopyrightText: 2015 Holger Kaelberer <holger.k@elberer.de> 0004 * 0005 * Authors: 0006 * Holger Kaelberer <holger.k@elberer.de> 0007 * 0008 * SPDX-License-Identifier: GPL-3.0-or-later 0009 */ 0010 import QtQuick 2.12 0011 import QtGraphicalEffects 1.0 0012 import GCompris 1.0 0013 import QtQuick.Controls 2.12 0014 0015 import "../../../core" 0016 import ".." 0017 import "balanceboxeditor.js" as Activity 0018 0019 0020 Item { 0021 id: editor 0022 0023 property string filename: "" 0024 onFilenameChanged: Activity.initEditor(props) 0025 0026 property ActivityBase currentActivity 0027 property var testBox 0028 0029 property bool isTesting: false 0030 0031 // props needed for stackView integration: 0032 signal close 0033 signal start 0034 signal stop 0035 property bool isDialog: true 0036 property bool alwaysStart: true // enforce start signal for configDialog-to-editor-transition 0037 0038 function handleBackEvent() 0039 { 0040 if (!isTesting) { 0041 if (Activity.levelChanged) 0042 Activity.warnUnsavedChanges(function() {stop(); home();}, 0043 function() {}); 0044 else { 0045 stop(); 0046 home(); 0047 } 0048 return true; 0049 } else 0050 return false; 0051 0052 } 0053 0054 Keys.onEscapePressed: event.accepted = handleBackEvent(); 0055 0056 Keys.onReleased: { 0057 if (event.key === Qt.Key_Back) { 0058 event.accepted = handleBackEvent(); 0059 } else 0060 event.accepted = false; 0061 } 0062 0063 onStart: { 0064 focus = true; 0065 if (!isTesting) 0066 Activity.initEditor(props); 0067 else 0068 stopTesting(); 0069 } 0070 0071 onStop: testBox.focus = true; 0072 0073 QtObject { 0074 id: props 0075 property int columns: 10 0076 property int rows: 10 0077 property int currentTool 0078 property alias editor: editor 0079 property alias mapModel: mapModel 0080 property alias mapWrapper: mapWrapper 0081 property int cellSize: mapWrapper.length / Math.min(mapWrapper.rows, mapWrapper.columns) 0082 property int wallSize: cellSize / 5 0083 property int ballSize: cellSize - 2*wallSize 0084 property alias toolBox: toolBox 0085 property string contactValue: "1" 0086 property int lastOrderNum: 0 0087 property alias file: file 0088 property alias parser: parser 0089 property alias bar: bar 0090 property int lastGoalIndex: -1 0091 property int lastBallIndex: -1 0092 property alias editorWorker: editorWorker 0093 } 0094 0095 function startTesting() { 0096 editor.isTesting = true; 0097 testBox.mode = "test"; 0098 testBox.testLevel = Activity.modelToLevel(); 0099 testBox.needRestart = true; 0100 back(testBox); 0101 testBox.start(); 0102 } 0103 0104 function stopTesting() { 0105 editor.isTesting = false; 0106 testBox.mode = "play"; 0107 testBox.testLevel = null; 0108 testBox.needRestart = true; 0109 } 0110 0111 Rectangle { 0112 id: background 0113 anchors.fill: parent 0114 0115 File { 0116 id: file 0117 0118 onError: console.error("File error: " + msg); 0119 } 0120 0121 JsonParser { 0122 id: parser 0123 onError: console.error("Balanceboxeditor: Error parsing JSON: " + msg); 0124 } 0125 0126 Column { 0127 id: toolBox2 0128 anchors.top: mapWrapper.top 0129 anchors.left: mapWrapper.right 0130 anchors.leftMargin: 10 * ApplicationInfo.ratio 0131 anchors.topMargin: 20 * ApplicationInfo.ratio 0132 spacing: 5 * ApplicationInfo.ratio 0133 width: (background.width - mapWrapper.width - props.wallSize - 20 * ApplicationInfo.ratio) / 2 0134 height: parent.height 0135 // anchors.topMargin: 20 0136 0137 GCButton { 0138 id: loadButton 0139 width: parent.width 0140 height: props.cellSize 0141 text: qsTr("Load") 0142 onClicked: creationHandler.loadWindow() 0143 } 0144 GCButton { 0145 id: saveButton 0146 width: parent.width 0147 height: props.cellSize 0148 text: qsTr("Save") 0149 onClicked: creationHandler.saveWindow(Activity.saveModel()) 0150 } 0151 GCButton { 0152 id: testButton 0153 width: parent.width 0154 height: props.cellSize 0155 text: qsTr("Test") 0156 onClicked: editor.startTesting(); 0157 } 0158 } 0159 0160 Column { 0161 id: toolBox 0162 anchors.top: mapWrapper.top 0163 anchors.topMargin: 20 * ApplicationInfo.ratio 0164 anchors.left: parent.left 0165 anchors.leftMargin: 10 0166 width: (mapWrapper.x - 20) 0167 spacing: 5 * ApplicationInfo.ratio 0168 0169 Component.onCompleted: clearTool.selected = true; 0170 0171 function setCurrentTool(item) 0172 { 0173 props.currentTool = item.type; 0174 if (clearTool !== item) clearTool.selected = false; 0175 if (hWallTool !== item) hWallTool.selected = false; 0176 if (vWallTool !== item) vWallTool.selected = false; 0177 if (holeTool !== item) holeTool.selected = false; 0178 if (ballTool !== item) ballTool.selected = false; 0179 if (contactTool !== item) contactTool.selected = false; 0180 if (goalTool !== item) goalTool.selected = false; 0181 } 0182 0183 EditorTool { 0184 id: clearTool 0185 type: Activity.TOOL_CLEAR 0186 anchors.horizontalCenter: parent.horizontalCenter 0187 width: parent.width 0188 height: props.cellSize - 2 0189 0190 onSelectedChanged: { 0191 if (selected) { 0192 toolBox.setCurrentTool(clearTool); 0193 } 0194 } 0195 0196 Image { 0197 id: clear 0198 0199 source: "qrc:/gcompris/src/core/resource/cancel.svg" 0200 width: props.cellSize - 4 0201 height: props.cellSize - 4 0202 0203 anchors.centerIn: parent 0204 anchors.margins: 3 0205 } 0206 } 0207 0208 EditorTool { 0209 id: hWallTool 0210 type: Activity.TOOL_H_WALL 0211 anchors.horizontalCenter: parent.horizontalCenter 0212 width: parent.width 0213 height: props.cellSize 0214 0215 onSelectedChanged: { 0216 if (selected) { 0217 toolBox.setCurrentTool(hWallTool); 0218 } 0219 } 0220 0221 Wall { 0222 id: hWall 0223 0224 width: props.cellSize 0225 height: props.wallSize 0226 0227 anchors.centerIn: parent 0228 anchors.margins: 3 0229 } 0230 } 0231 0232 EditorTool { 0233 id: vWallTool 0234 anchors.horizontalCenter: parent.horizontalCenter 0235 width: parent.width 0236 height: props.cellSize 0237 type: Activity.TOOL_V_WALL 0238 0239 onSelectedChanged: { 0240 if (selected) { 0241 toolBox.setCurrentTool(vWallTool); 0242 } 0243 } 0244 0245 Wall { 0246 id: vWall 0247 width: props.wallSize 0248 height: props.cellSize - 4 0249 anchors.centerIn: parent 0250 anchors.margins: 3 0251 } 0252 } 0253 0254 EditorTool { 0255 id: holeTool 0256 anchors.horizontalCenter: parent.horizontalCenter 0257 width: parent.width 0258 height: props.cellSize 0259 type: Activity.TOOL_HOLE 0260 onSelectedChanged: { 0261 if (selected) { 0262 toolBox.setCurrentTool(holeTool); 0263 } 0264 } 0265 0266 BalanceItem { 0267 id: hole 0268 width: props.cellSize - props.wallSize / 2 0269 height: props.cellSize - props.wallSize / 2 0270 anchors.centerIn: parent 0271 anchors.margins: props.wallSize / 2 0272 visible: true 0273 imageSource: Activity.baseUrl + "/hole.svg" 0274 } 0275 } 0276 0277 EditorTool { 0278 id: ballTool 0279 anchors.horizontalCenter: parent.horizontalCenter 0280 width: parent.width 0281 height: props.cellSize 0282 type: Activity.TOOL_BALL 0283 onSelectedChanged: { 0284 if (selected) { 0285 toolBox.setCurrentTool(ballTool); 0286 } 0287 } 0288 0289 BalanceItem { 0290 id: ball 0291 width: props.cellSize - props.wallSize / 2 0292 height: parent.height - props.wallSize / 2 0293 anchors.centerIn: parent 0294 anchors.margins: props.wallSize / 2 0295 visible: true 0296 imageSource: Activity.baseUrl + "/ball.svg" 0297 } 0298 } 0299 0300 EditorTool { 0301 id: goalTool 0302 anchors.horizontalCenter: parent.horizontalCenter 0303 width: parent.width 0304 height: props.cellSize 0305 type: Activity.TOOL_GOAL 0306 onSelectedChanged: { 0307 if (selected) { 0308 toolBox.setCurrentTool(goalTool); 0309 } 0310 } 0311 0312 BalanceItem { 0313 id: goal 0314 width: props.cellSize - props.wallSize 0315 height: props.cellSize - props.wallSize 0316 anchors.centerIn: parent 0317 anchors.margins: props.wallSize / 2 0318 z: 1 0319 imageSource: Activity.baseUrl + "/door.svg" 0320 imageScale: 1.1 0321 } 0322 } 0323 0324 EditorTool { 0325 id: contactTool 0326 anchors.horizontalCenter: parent.horizontalCenter 0327 width: parent.width 0328 height: props.cellSize 0329 type: Activity.TOOL_CONTACT 0330 onSelectedChanged: { 0331 if (selected) { 0332 toolBox.setCurrentTool(contactTool); 0333 } 0334 } 0335 0336 Row { 0337 id: contactToolRow 0338 spacing: 5 0339 width: contact.width + contactTextInput.width + spacing 0340 anchors.centerIn: parent 0341 0342 BalanceContact { 0343 id: contact 0344 width: props.cellSize - props.wallSize 0345 height: props.cellSize - props.wallSize 0346 anchors.margins: props.wallSize / 2 0347 pressed: false 0348 orderNum: 99 0349 text: props.contactValue 0350 z: 1 0351 } 0352 SpinBox { 0353 id: contactTextInput 0354 width: contact.width * 2 0355 height: contact.height 0356 value: props.contactValue 0357 to: 99 0358 from: 1 0359 font.family: GCSingletonFontLoader.fontLoader.name 0360 font.pixelSize: height / 2 0361 onValueChanged: if (value != props.contactValue) props.contactValue = value; 0362 } 0363 } 0364 } 0365 } 0366 0367 WorkerScript { 0368 id: editorWorker 0369 0370 source: "editor_worker.js" 0371 onMessage: { 0372 // worker finished, update all changed values (except the model): 0373 props.contactValue = messageObject.maxContactValue; 0374 props.lastBallIndex = messageObject.lastBallIndex; 0375 props.lastGoalIndex = messageObject.lastGoalIndex; 0376 props.lastOrderNum = messageObject.lastOrderNum; 0377 Activity.targetList = messageObject.targetList; 0378 testBox.loading.stop(); 0379 } 0380 } 0381 0382 ListModel { 0383 id: mapModel 0384 } 0385 0386 Rectangle { 0387 id: mapWrapper 0388 0389 property double margin: 20 0390 property int columns: props.columns 0391 property int rows: props.rows 0392 property double length: Math.min(background.height - 0393 2*mapWrapper.margin, background.width - 2*mapWrapper.margin); 0394 0395 color: "#E3DEDB" 0396 0397 width: length 0398 height: length 0399 0400 anchors.horizontalCenter: parent.horizontalCenter 0401 anchors.verticalCenter: parent.verticalCenter 0402 0403 Grid { 0404 id: mapGrid 0405 columns: mapWrapper.columns 0406 rows: mapWrapper.rows 0407 anchors.fill: parent 0408 width: parent.width 0409 height: parent.height 0410 spacing: 0 0411 0412 Repeater { 0413 id: mapGridRepeater 0414 model: mapModel//mapGrid.columns * mapGrid.rows 0415 0416 delegate: Item { // cell wrapper 0417 id: cell 0418 width: props.cellSize 0419 height: props.cellSize 0420 0421 property bool highlighted: false 0422 0423 Loader { 0424 id: northWallLoader 0425 active: value & Activity.NORTH 0426 width: props.cellSize + props.wallSize 0427 height: props.wallSize 0428 anchors.top: parent.top 0429 anchors.left: parent.left 0430 anchors.topMargin: -props.wallSize / 2 0431 anchors.leftMargin: -props.wallSize / 2 0432 sourceComponent: Wall { 0433 id: northWall 0434 shadow: false 0435 anchors.centerIn: parent 0436 z: 1 0437 } 0438 } 0439 0440 Loader { 0441 id: eastWallLoader 0442 active: value & Activity.EAST || (cell.highlighted && props.currentTool === Activity.TOOL_V_WALL) 0443 width: props.wallSize 0444 height: props.cellSize + props.wallSize 0445 anchors.bottom: parent.bottom 0446 anchors.right: parent.right 0447 anchors.bottomMargin: -props.wallSize / 2 0448 anchors.rightMargin: -props.wallSize / 2 0449 sourceComponent: Wall { 0450 id: eastWall 0451 anchors.centerIn: parent 0452 shadow: false 0453 z: 1 0454 } 0455 } 0456 0457 Loader { 0458 id: southWallLoader 0459 active: value & Activity.SOUTH || (cell.highlighted && props.currentTool === Activity.SOUTH) 0460 width: props.cellSize + props.wallSize 0461 height: props.wallSize 0462 anchors.bottom: parent.bottom 0463 anchors.left: parent.left 0464 anchors.bottomMargin: -props.wallSize / 2 0465 anchors.leftMargin: -props.wallSize / 2 0466 sourceComponent: Wall { 0467 id: southWall 0468 anchors.centerIn: parent 0469 shadow: false 0470 z: 1 0471 } 0472 } 0473 0474 Loader { 0475 id: westWallLoader 0476 active: value & Activity.WEST 0477 width: props.wallSize 0478 height: props.cellSize + props.wallSize 0479 anchors.bottom: parent.bottom 0480 anchors.left: parent.left 0481 anchors.bottomMargin: -props.wallSize / 2 0482 anchors.leftMargin: -props.wallSize / 2 0483 sourceComponent: Wall { 0484 id: westWall 0485 anchors.centerIn: parent 0486 shadow: false 0487 z: 1 0488 } 0489 } 0490 0491 Loader { 0492 id: doorLoader 0493 active: value & Activity.GOAL || (cell.highlighted && props.currentTool === Activity.TOOL_GOAL) 0494 anchors.centerIn: parent 0495 width: props.cellSize - props.wallSize 0496 height: props.cellSize - props.wallSize 0497 sourceComponent: BalanceItem { 0498 id: goal 0499 anchors.centerIn: parent 0500 z: 1 0501 imageSource: Activity.baseUrl + "/door.svg" 0502 imageScale: 1.1 0503 } 0504 } 0505 0506 Loader { 0507 id: holeLoader 0508 active: value & Activity.HOLE || (cell.highlighted && props.currentTool === Activity.TOOL_HOLE) 0509 anchors.centerIn: parent 0510 sourceComponent: BalanceItem { 0511 id: hole 0512 width: props.ballSize 0513 height:props.ballSize 0514 anchors.centerIn: parent 0515 z: 1 0516 imageSource: Activity.baseUrl + "/hole.svg" 0517 } 0518 } 0519 0520 Loader { 0521 id: ballLoader 0522 active: value & Activity.START || (cell.highlighted && props.currentTool === Activity.TOOL_BALL) 0523 anchors.centerIn: parent 0524 sourceComponent: BalanceItem { 0525 id: ball 0526 width: props.ballSize 0527 height:props.ballSize 0528 anchors.centerIn: parent 0529 visible: true 0530 imageSource: Activity.baseUrl + "/ball.svg" 0531 z: 1 0532 } 0533 } 0534 0535 Loader { 0536 id: contactLoader 0537 active: (value & Activity.CONTACT) || (cell.highlighted && props.currentTool === Activity.TOOL_CONTACT) 0538 width: props.cellSize - props.wallSize 0539 height: props.cellSize - props.wallSize 0540 anchors.centerIn: parent 0541 sourceComponent: BalanceContact { 0542 id: contact 0543 anchors.centerIn: parent 0544 visible: true 0545 pressed: false 0546 text: contactValue 0547 z: 1 0548 } 0549 } 0550 0551 Rectangle { // bounding rect 0552 id: cellRect 0553 0554 width: props.cellSize 0555 height: props.cellSize 0556 color: "transparent" 0557 border.width: 1 0558 border.color: cell.highlighted ? "yellow": "lightgray" 0559 z: 10 0560 0561 MouseArea { 0562 id: cellMouse 0563 anchors.fill: parent 0564 0565 hoverEnabled: ApplicationInfo.isMobile ? false : true 0566 onEntered: cell.highlighted = true 0567 onExited: cell.highlighted = false 0568 onClicked: { 0569 editor.focus = true; 0570 Activity.modifyMap(props, row, col); 0571 } 0572 } 0573 } 0574 0575 } 0576 } 0577 } 0578 0579 // right: 0580 Wall { 0581 id: rightWall 0582 0583 width: props.wallSize 0584 height: parent.height + props.wallSize 0585 0586 anchors.left: mapWrapper.right 0587 anchors.leftMargin: - props.wallSize/2 0588 anchors.top: parent.top 0589 anchors.topMargin: -props.wallSize/2 0590 0591 shadow: false 0592 } 0593 // bottom: 0594 Wall { 0595 id: bottomWall 0596 0597 width: parent.width + props.wallSize 0598 height: props.wallSize 0599 0600 anchors.left: mapWrapper.left 0601 anchors.leftMargin: - props.wallSize/2 0602 anchors.top: parent.bottom 0603 anchors.topMargin: -props.wallSize/2 0604 0605 shadow: false 0606 } 0607 // top: 0608 Wall { 0609 id: topWall 0610 0611 width: parent.width + props.wallSize 0612 height: props.wallSize 0613 0614 anchors.left: mapWrapper.left 0615 anchors.leftMargin: - props.wallSize/2 0616 anchors.top: parent.top 0617 anchors.topMargin: -props.wallSize/2 0618 shadow: false 0619 } 0620 // left: 0621 Wall { 0622 id: leftWall 0623 0624 width: props.wallSize 0625 height: parent.height + props.wallSize 0626 0627 anchors.left: mapWrapper.left 0628 anchors.leftMargin: - props.wallSize/2 0629 anchors.top: parent.top 0630 anchors.topMargin: -props.wallSize/2 0631 shadow: false 0632 } 0633 } 0634 } 0635 0636 Bar { 0637 id: bar 0638 content: BarEnumContent { value: home | level } // FIXME: add dedicated editor help? 0639 onPreviousLevelClicked: { 0640 if (Activity.currentLevel > 0) { 0641 if (Activity.levelChanged) 0642 Activity.warnUnsavedChanges(Activity.previousLevel, 0643 function() {}); 0644 else 0645 Activity.previousLevel(); 0646 } 0647 } 0648 onNextLevelClicked: { 0649 if (Activity.levelChanged) 0650 Activity.warnUnsavedChanges(function() { 0651 Activity.levelChanged = false; // mark unchanged early to make check in nextLevel() work 0652 Activity.nextLevel(); 0653 }, function() {}); 0654 else 0655 Activity.nextLevel(); 0656 } 0657 onHomeClicked: { 0658 if (Activity.levelChanged) 0659 Activity.warnUnsavedChanges(activity.home, 0660 function() {}); 0661 else { 0662 activity.home() 0663 } 0664 } 0665 } 0666 }