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

0001 /* GCompris - tangram.qml
0002  *
0003  * SPDX-FileCopyrightText: 2015 Bruno Coudoin <bruno.coudoin@gcompris.net>
0004  *
0005  * Authors:
0006  *   Yves Combe /  Philippe Banwarth (GTK+ version)
0007  *   Bruno Coudoin <bruno.coudoin@gcompris.net> (Qt Quick port)
0008  *   Timothée Giet <animtim@gmail.com> (layout improvements and cleaning)
0009  *
0010  *   SPDX-License-Identifier: GPL-3.0-or-later
0011  */
0012 import QtQuick 2.12
0013 import GCompris 1.0
0014 
0015 import "../../core"
0016 import "tangram.js" as Activity
0017 import "dataset.js" as Dataset
0018 import "."
0019 
0020 ActivityBase {
0021     id: activity
0022 
0023     onStart: focus = true
0024     onStop: {}
0025 
0026     property var dataset: Dataset
0027     property string resourceUrl: "qrc:/gcompris/src/activities/tangram/resource/"
0028 
0029     Keys.onPressed: Activity.processPressedKey(event)
0030 
0031     pageComponent: Image {
0032         id: background
0033         source: activity.resourceUrl + "tangram/background.svg"
0034         anchors.fill: parent
0035 
0036         property bool horizontalLayout: background.width >= background.height - bar.height * 1.2
0037         property int playX: playArea.x
0038         property int playY: playArea.y
0039         property int playWidth: horizontalLayout ? background.height - bar.height * 1.2 : background.width
0040         property int playHeight: playWidth
0041         property double playRatio: playWidth / 1000
0042 
0043         signal start
0044         signal stop
0045 
0046         /* In order to accept any screen ratio the play area is always a 1000x1000 square * playRatio
0047          * and is centered in the background with a vertical offset of -bar.height * 0.6
0048          */
0049 
0050         Rectangle {
0051             id: playArea
0052             width: background.playWidth
0053             height: background.playHeight
0054             anchors.verticalCenter: parent.verticalCenter
0055             anchors.verticalCenterOffset: -bar.height * 0.6
0056             anchors.horizontalCenter: parent.horizontalCenter
0057             border.width: 2
0058             border.color: "black"
0059             color: "transparent"
0060             visible: items.editionMode /* debug to see the play area */
0061         }
0062 
0063         Component.onCompleted: {
0064             activity.start.connect(start)
0065             activity.stop.connect(stop)
0066         }
0067 
0068         // Add here the QML items you need to access in javascript
0069         QtObject {
0070             id: items
0071             property Item main: activity.main
0072             property alias background: background
0073             property int currentLevel: activity.currentLevel
0074             property alias bonus: bonus
0075             property alias modelListModel: modelList.model
0076             property alias userList: userList
0077             property alias userListModel: userList.model
0078             property Item selectedItem
0079             property var currentTans: dataset.dataset[items.currentLevel]
0080             property int numberOfLevel: dataset.dataset.length
0081             property bool editionMode: false
0082         }
0083 
0084         onStart: {
0085             Activity.start(items)
0086         }
0087         onStop: {
0088             checkWinTimer.stop()
0089             Activity.stop()
0090         }
0091 
0092         Image {
0093             id: bgData
0094             source: items.currentTans.bg ? activity.resourceUrl + items.currentTans.bg : ''
0095             sourceSize.width: width
0096             sourceSize.height: width
0097             width: playArea.width
0098             height: width
0099             anchors.centerIn: playArea
0100         }
0101 
0102         RotateMouseArea {}
0103 
0104         DropArea {
0105             id: dropableArea
0106             anchors.left: background.left
0107             anchors.bottom: background.bottom
0108             width: background.width
0109             height: background.height
0110         }
0111 
0112         Repeater {
0113             id: modelList
0114             model: items.currentTans.pieces
0115             Item {
0116                 anchors.fill: background
0117                 Image {
0118                     id: tansModel
0119                     x: background.playX + background.playWidth * modelData.x - width / 2
0120                     y: background.playY + background.playHeight * modelData.y - height / 2
0121                     source: activity.resourceUrl + "m-" + modelData.img
0122                     sourceSize.width: modelData.width * background.playWidth
0123                     sourceSize.height: modelData.height * background.playWidth
0124                     z: index
0125                     rotation: modelData.rotation
0126                     mirror: modelData.flipping ? true : false
0127                     visible: true
0128                 }
0129             }
0130         }
0131 
0132         Repeater {
0133             id: userList
0134             model: items.currentTans.pieces
0135             Item {
0136                 id: tansItem
0137                 x: background.playX + background.playWidth * xRatio - tans.width / 2
0138                 y: background.playY + background.playHeight * yRatio - tans.height / 2
0139                 width: tans.width
0140                 height: tans.height
0141 
0142                 z: 100 + index
0143                 property real xRatio: !items.editionMode ? modelData.initX : modelData.x
0144                 property real yRatio: !items.editionMode ? modelData.initY : modelData.y
0145                 property bool selected: false
0146                 property int animDuration: 48
0147                 property bool flippable: modelData.flippable
0148                 property bool rotable: modelData.moduloRotation != 0
0149 
0150                 property alias tans: tans
0151                 rotation: !items.editionMode ? modelData.initRotation : modelData.rotation
0152                 property alias mirror: tans.mirror
0153                 function restoreZindex() {
0154                     z = 100 + index
0155                 }
0156 
0157                 onSelectedChanged: {
0158                     if(!selected)
0159                         restoreZindex()
0160                 }
0161 
0162                 function positionToTans() {
0163                     return [
0164                     (x + width / 2 - background.playX) / background.playWidth,
0165                     (y + height / 2 - background.playY) / background.playHeight
0166                     ]
0167                 }
0168 
0169                 // After a drag the [x, y] positions are addressed directly breaking our
0170                 // binding. Call me to reset the binding.
0171                 function restoreBindings() {
0172                     x = Qt.binding(function() { return background.playX + background.playWidth * xRatio - width / 2})
0173                     y = Qt.binding(function() { return background.playY + background.playHeight * yRatio - height / 2 })
0174                 }
0175 
0176                 Image {
0177                     id: tans
0178                     mirror: !items.editionMode ? modelData.initFlipping : modelData.flipping
0179                     source: activity.resourceUrl + modelData.img
0180                     sourceSize.width: modelData.width * background.playWidth
0181                     sourceSize.height: modelData.height * background.playWidth
0182                 }
0183                 // Manage to return a base rotation as it was provided in the model
0184                 function rotationToTans() {
0185                     // moduloRotation == 0 to disable rotation, assume 360 in this case
0186                     var mod = modelData.moduloRotation ? modelData.moduloRotation : 360
0187                     if(modelData.flipable || modelData.flipping || !mirror)
0188                          return rotation >= 0 ? rotation % mod : (360 + rotation) % mod
0189                     else
0190                         // It flipping but model is not flipping sensitive we have to rotate accordingly
0191                         return rotation >= 0 ? (rotation - (mod - 90)) % mod : (360 + rotation - (mod - 90)) % mod
0192                 }
0193 
0194                 // Return all the positions as we got it from a tans definition
0195                 function asTans() {
0196                     return {
0197                         'img': modelData.img,
0198                         'flipping': mirror,
0199                         'x': positionToTans()[0],
0200                         'y': positionToTans()[1],
0201                         'rotation': rotationToTans()
0202                     }
0203                 }
0204 
0205                 function flipMe() {
0206                     if(flippable)
0207                         mirror = !mirror
0208                     background.checkWin()
0209                 }
0210 
0211                 Drag.active: dragArea.drag.active
0212                 Drag.hotSpot.x : width / 2
0213                 Drag.hotSpot.y : height / 2
0214 
0215                 MouseArea {
0216                     id: dragArea
0217                     anchors.fill: parent
0218                     drag.target: parent
0219                     onPressed: {
0220                         tansItem.z = 200
0221                         if(items.selectedItem && items.selectedItem != tansItem)
0222                         items.selectedItem.selected = false
0223                         items.selectedItem = tansItem
0224                         tansItem.selected = true
0225                         background.checkWin()
0226                     }
0227                     onDoubleClicked: {
0228                         flipMe()
0229                     }
0230                     onReleased: {
0231                         parent.Drag.drop()
0232                         var posTans = positionToTans()
0233                         var closest = Activity.getClosest(posTans)
0234                         if(closest && !items.editionMode) {
0235                             tansItem.xRatio = closest[0]
0236                             tansItem.yRatio = closest[1]
0237                         } else {
0238                             tansItem.xRatio = posTans[0]
0239                             tansItem.yRatio = posTans[1]
0240                         }
0241                         tansItem.restoreBindings()
0242                         background.checkWin()
0243                     }
0244                 }
0245 
0246                 Image {
0247                     id: rotateButton
0248                     source: "qrc:/gcompris/src/core/resource/bar_reload.svg"
0249                     x: - width
0250                     y: parent.height / 2 - height / 2
0251                     visible: tansItem.selected && tansItem.rotable
0252                     width: 40 * ApplicationInfo.ratio
0253                     sourceSize.width: width
0254                     fillMode: Image.PreserveAspectFit
0255                     z: tansItem.z + 1
0256 
0257                     RotateMouseArea {}
0258                 }
0259 
0260                 Image {
0261                     id: flip
0262                     source: "qrc:/gcompris/src/activities/tangram/resource/tangram/flip.svg"
0263                     x: parent.width / 2 - width / 2
0264                     y: parent.height - height / 2
0265                     visible: tansItem.selected && tansItem.flippable
0266                     width: 40 * ApplicationInfo.ratio
0267                     sourceSize.width: width
0268                     fillMode: Image.PreserveAspectFit
0269                     z: tansItem.z + 1
0270 
0271                     MouseArea {
0272                         anchors.fill: parent
0273                         onClicked: tansItem.flipMe()
0274                     }
0275                 }
0276 
0277                 Behavior on x {
0278                     PropertyAnimation  {
0279                         duration: animDuration
0280                         easing.type: Easing.InOutQuad
0281                     }
0282                 }
0283                 Behavior on y {
0284                     PropertyAnimation  {
0285                         duration: animDuration
0286                         easing.type: Easing.InOutQuad
0287                     }
0288                 }
0289             }
0290             // Return the tans model of all the user tans
0291             function asTans() {
0292                 var tans = []
0293                 for(var i = 0; i < userList.count; i++) {
0294                     tans.push(userList.itemAt(i).asTans())
0295                 }
0296                 return tans
0297             }
0298         }
0299 
0300         // We use a timer here because we have to check only once the potential
0301         // animation are over
0302         Timer {
0303             id: checkWinTimer
0304             interval: 200
0305             property bool alreadyStarted: false
0306             onTriggered: {
0307                 if(Activity.check() && !alreadyStarted) {
0308                     alreadyStarted = true
0309                     if(!items.editionMode)
0310                         bonus.good('flower')
0311                 }
0312             }
0313         }
0314 
0315         function checkWin() {
0316             checkWinTimer.start()
0317         }
0318 
0319         GCText {
0320             anchors.top: parent.top
0321             anchors.left: parent.left
0322             text: items.currentTans.name
0323             visible: items.editionMode
0324         }
0325 
0326         DialogHelp {
0327             id: dialogHelp
0328             onClose: home()
0329         }
0330 
0331         File {
0332             id: file
0333         }
0334 
0335         Bar {
0336             id: bar
0337             level: items.currentLevel + 1
0338             content: BarEnumContent { value: help | home | level |
0339                                              (items.editionMode ? repeat : 0) }
0340             onHelpClicked: {
0341                 displayDialog(dialogHelp)
0342             }
0343             onPreviousLevelClicked: Activity.previousLevel()
0344             onNextLevelClicked: Activity.nextLevel()
0345             onHomeClicked: activity.home()
0346             onRepeatClicked: file.write(Activity.toDataset(), "/tmp/" + items.currentTans.name)
0347         }
0348 
0349         Bonus {
0350             id: bonus
0351             interval: 1600
0352             Component.onCompleted: win.connect(nextLevel)
0353 
0354             function nextLevel() {
0355                 checkWinTimer.alreadyStarted = false
0356                 Activity.nextLevel()
0357             }
0358         }
0359     }
0360 
0361 }