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 }