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 }