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

0001 /* GCompris - CodeArea.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 
0016 import "programmingMaze.js" as Activity
0017 
0018 GridView {
0019     id: codeArea
0020     z: 1
0021     width: background.width * 0.4
0022     height: background.height * 0.29
0023     cellWidth: background.buttonWidth
0024     cellHeight: background.buttonHeight
0025 
0026     interactive: false
0027     model: currentModel
0028 
0029     highlight: Rectangle {
0030         width: buttonWidth
0031         height: buttonHeight
0032         color: "#00ffffff"
0033         border.width: 3.5 * ApplicationInfo.ratio
0034         border.color: "#e77935"
0035         z: 11
0036         radius: width / 18
0037     }
0038     highlightFollowsCurrentItem: true
0039     keyNavigationWraps: true
0040     focus: true
0041 
0042     property ListModel currentModel
0043     property int draggedItemIndex: -1
0044     property int possibleDropIndex: -1
0045     property int possibleDropRemoveIndex: -1
0046     property int xCoordinateInPossibleDrop: -1
0047     property int yCoordinateInPossibleDrop: -1
0048 
0049     /**
0050      * Stores the index of the item which is clicked to edit.
0051      *
0052      * 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.
0053      *
0054      * 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.
0055      */
0056     property int initialEditItemIndex: -1
0057 
0058     // Tells if any instruction is selected for editing.
0059     property bool isEditingInstruction: false
0060 
0061     signal spaceKeyPressed
0062     signal tabKeyPressed
0063     signal deleteKeyPressed
0064 
0065     /**
0066      * There can be three possibilities here:
0067      *
0068      * 1. We want to insert an instruction at the currentIndex position.
0069      * 2. We want to select an instruction to edit, or deselect it.
0070      * 3. We want to append an instruction.
0071      */
0072     onSpaceKeyPressed: {
0073         if(currentIndex != -1) {
0074             if(instructionArea.instructionToInsert && (items.numberOfInstructionsAdded < items.maxNumberOfInstructionsAllowed)) {
0075                 var isInstructionInserted = appendInstruction()
0076                 if(isInstructionInserted)
0077                     currentModel.move(currentModel.count - 1, currentIndex, 1)
0078             }
0079             else {
0080                 if((initialEditItemIndex == currentIndex) || (initialEditItemIndex == -1 && currentIndex != -1)) {
0081                     codeArea.isEditingInstruction = !codeArea.isEditingInstruction
0082                 }
0083                 if(!codeArea.isEditingInstruction)
0084                     codeArea.initialEditItemIndex = -1
0085                 else
0086                     initialEditItemIndex = currentIndex
0087 
0088                 var calculatedX = (initialEditItemIndex % 4) * codeArea.cellWidth
0089                 var calculatedY = Math.floor(initialEditItemIndex / 4) * codeArea.cellHeight
0090                 editInstructionIndicator.x = calculatedX + 1.5 * ApplicationInfo.ratio
0091                 editInstructionIndicator.y = calculatedY + 1.5 * ApplicationInfo.ratio
0092             }
0093         }
0094 
0095         else if((items.numberOfInstructionsAdded < items.maxNumberOfInstructionsAllowed) && instructionArea.instructionToInsert)
0096             appendInstruction()
0097     }
0098 
0099     onDeleteKeyPressed: {
0100         if(currentIndex != -1) {
0101             currentModel.remove(currentIndex)
0102             items.numberOfInstructionsAdded--
0103         }
0104         resetEditingValues()
0105     }
0106 
0107     function appendInstruction() {
0108         if(background.insertIntoMain || (instructionArea.instructionToInsert != "call-procedure") || (instructionArea.instructionToInsert != "execute-loop")) {
0109             currentModel.append({ "name": instructionArea.instructionToInsert })
0110             items.numberOfInstructionsAdded++
0111             instructionArea.instructionToInsert = ""
0112             return true
0113         }
0114         return false
0115     }
0116 
0117     function resetEditingValues() {
0118         initialEditItemIndex = -1
0119         isEditingInstruction = false
0120     }
0121 
0122     Item {
0123         id: dropPositionIndicator
0124         visible: false
0125         height: background.buttonHeight
0126         width: 3 * ApplicationInfo.ratio
0127 
0128         Rectangle {
0129             visible: parent.visible
0130             anchors.centerIn: parent
0131             width: parent.width
0132             height: parent.height - 3 * ApplicationInfo.ratio
0133             color: "#e77935"
0134         }
0135 
0136         states: [
0137             State {
0138                 name: "shown"
0139                 when: codeArea.possibleDropIndex != -1
0140                 PropertyChanges {
0141                     target: dropPositionIndicator
0142                     visible: true
0143                     x: Math.floor(codeArea.xCoordinateInPossibleDrop / codeArea.cellWidth) *
0144                        codeArea.cellWidth - 1.5 * ApplicationInfo.ratio
0145                     y: Math.floor(codeArea.yCoordinateInPossibleDrop / codeArea.cellHeight) *
0146                        codeArea.cellHeight + 1.5 * ApplicationInfo.ratio
0147                 }
0148             }
0149         ]
0150     }
0151 
0152     Rectangle {
0153         id: editInstructionIndicator
0154         visible: codeArea.isEditingInstruction && codeArea.count != 0
0155         width: background.buttonWidth - 3 * ApplicationInfo.ratio
0156         height: background.buttonHeight - 3 * ApplicationInfo.ratio
0157         color: "red"
0158         border.color: "red"
0159         border.width: 1.5 * ApplicationInfo.ratio
0160         opacity: 0.2
0161         radius: width / 18
0162     }
0163 
0164     MouseArea {
0165         id: codeAreaMouse
0166         anchors.fill: parent
0167         enabled: items.isRunCodeEnabled
0168         onPressed: {
0169             codeArea.currentIndex = -1
0170             codeArea.draggedItemIndex = codeArea.indexAt(mouseX,mouseY)
0171             if(codeArea.draggedItemIndex === -1) {
0172                 constraintInstruction.changeConstraintInstructionOpacity()
0173                 codeArea.isEditingInstruction = false
0174             }
0175             else if(!codeArea.isEditingInstruction)
0176                 codeArea.initialEditItemIndex = codeArea.draggedItemIndex
0177         }
0178 
0179         onPositionChanged: {
0180             var newPos = codeArea.indexAt(mouseX, mouseY)
0181             var calculatedX = Math.floor(mouseX / codeArea.cellWidth) * codeArea.cellWidth
0182             var previousIndexPosition = codeArea.indexAt(calculatedX - 1, mouseY)
0183 
0184             // If the user want to move an item to the end, then the new position will be after the last instruction.
0185             if(newPos == -1 && previousIndexPosition != -1)
0186                 newPos = previousIndexPosition + 1
0187 
0188             codeArea.isEditingInstruction = false
0189             codeArea.xCoordinateInPossibleDrop = mouseX
0190             codeArea.yCoordinateInPossibleDrop = mouseY
0191             codeArea.possibleDropIndex = newPos
0192         }
0193 
0194         onReleased: {
0195             if(codeArea.draggedItemIndex != -1) {
0196                 var draggedIndex = codeArea.draggedItemIndex
0197                 var dropIndex = codeArea.indexAt(mouseX,mouseY)
0198                 var calculatedX = Math.floor(mouseX / codeArea.cellWidth) * codeArea.cellWidth
0199                 var calculatedY = Math.floor(mouseY / codeArea.cellHeight) * codeArea.cellHeight
0200                 codeArea.draggedItemIndex = -1
0201 
0202                 if(dropIndex == -1) {
0203                     var previousIndexPosition = codeArea.indexAt(calculatedX - 1, mouseY)
0204                     if(previousIndexPosition == -1) {
0205                         currentModel.remove(draggedIndex)
0206                         items.numberOfInstructionsAdded--
0207                     }
0208                     else {
0209                         currentModel.append(currentModel.get(draggedIndex))
0210                         currentModel.remove(draggedIndex)
0211                     }
0212                     codeArea.initialEditItemIndex = -1
0213                 }
0214                 else if(draggedIndex != dropIndex) {
0215                     if(dropIndex <= draggedIndex) {
0216                         // Moving instruction from right to left
0217                         currentModel.move(draggedIndex, dropIndex, 1)
0218                     }
0219                     else {
0220                         // Moving instruction from left to right
0221                         currentModel.move(draggedIndex, dropIndex - 1, 1)
0222                     }
0223                     codeArea.initialEditItemIndex = -1
0224                 }
0225                 else {
0226                     /**
0227                      * If the index of the initially selected instruction (if any) is same as the currently selected instruction,
0228                      * deselect it for editing, else make the current instruction as the initially editable item and move the edit indicator to that position.
0229                      */
0230                     if(codeArea.initialEditItemIndex == dropIndex) {
0231                         codeArea.isEditingInstruction = !codeArea.isEditingInstruction
0232                         if(!codeArea.isEditingInstruction)
0233                             codeArea.initialEditItemIndex = -1
0234                     }
0235                     else
0236                         codeArea.initialEditItemIndex = draggedIndex
0237 
0238                     editInstructionIndicator.x = calculatedX + 1.5 * ApplicationInfo.ratio
0239                     editInstructionIndicator.y = calculatedY + 1.5 * ApplicationInfo.ratio
0240                 }
0241                 codeArea.possibleDropIndex = -1
0242             }
0243         }
0244     }
0245 
0246     delegate: Item {
0247         id: itemParent
0248         width: background.buttonWidth
0249         height: background.buttonHeight
0250 
0251         Item {
0252             id: item
0253             width: background.buttonWidth
0254             height: background.buttonHeight
0255             state: "inactive"
0256             opacity: 1
0257 
0258             Behavior on width { NumberAnimation { duration: 300; easing.type: Easing.InOutQuad } }
0259             Behavior on height { NumberAnimation { duration: 300; easing.type: Easing.InOutQuad } }
0260             Behavior on opacity {NumberAnimation { duration: 300; easing.type: Easing.InOutQuad } }
0261 
0262             states: [
0263                 State {
0264                     name: "inDrag"
0265                     when: index == codeArea.draggedItemIndex
0266 
0267                     PropertyChanges { target: item; parent: codeArea }
0268                     PropertyChanges { target: item; width: background.buttonWidth * 0.80 }
0269                     PropertyChanges { target: item; height: background.buttonHeight * 0.80 }
0270                     PropertyChanges { target: item; anchors.centerIn: undefined }
0271                     PropertyChanges { target: item; x: codeAreaMouse.mouseX - item.width / 2 }
0272                     PropertyChanges { target: item; y: codeAreaMouse.mouseY - item.height / 2 }
0273                 },
0274                 State {
0275                     name: "greyedOut"
0276                     when: (codeArea.draggedItemIndex != -1) && (codeArea.draggedItemIndex != index)
0277 
0278                     PropertyChanges { target: item; opacity: 0.7 }
0279                 },
0280                 State {
0281                     name: "inactive"
0282                     when: (codeArea.draggedItemIndex == -1) || (codeArea.draggedItemIndex == index)
0283 
0284                     PropertyChanges { target: item; opacity: 1.0 }
0285                 }
0286             ]
0287 
0288             transitions: [
0289                 Transition {
0290                     from: "inDrag"
0291                     to: "*"
0292                     PropertyAnimation {
0293                         target: item
0294                         properties: "scale, opacity"
0295                         from: 0.7
0296                         to: 1.0
0297                         duration: 200
0298                     }
0299                 }
0300             ]
0301 
0302             Rectangle {
0303                 width: parent.width - 3 * ApplicationInfo.ratio
0304                 height: parent.height - 3 * ApplicationInfo.ratio
0305                 border.width: 1.2 * ApplicationInfo.ratio
0306                 border.color: "#2a2a2a"
0307                 anchors.centerIn: parent
0308                 radius: width / 18
0309 
0310                 Image {
0311                     id: codeAreaIcon
0312                     source: Activity.url + name + ".svg"
0313                     width: Math.round(parent.width / 1.2)
0314                     height: Math.round(parent.height / 1.2)
0315                     sourceSize.width: height
0316                     sourceSize.height: height
0317                     anchors.centerIn: parent
0318                     fillMode: Image.PreserveAspectFit
0319                     mipmap: true
0320                 }
0321             }
0322         }
0323     }
0324 }