Warning, /education/gcompris/src/activities/frieze/Frieze.qml is written in an unsupported language. File is not indexed.
0001 /* GCompris - Frieze.qml 0002 * 0003 * SPDX-FileCopyrightText: 2023 Bruno ANSELME <be.root@free.fr> 0004 * SPDX-FileCopyrightText: 2024 Timothée Giet <animtim@gmail.com> 0005 * SPDX-License-Identifier: GPL-3.0-or-later 0006 * 0007 * References : https://del-en-maternelle.fr/les-domaines/maths/les-algorithmes/ 0008 * https://irem.univ-nantes.fr/wp-content/uploads/2019/12/Algorithmes.pdf 0009 */ 0010 import QtQuick 2.12 0011 import GCompris 1.0 0012 0013 import "../../core" 0014 import "qrc:/gcompris/src/core/core.js" as Core 0015 import "frieze.js" as Activity 0016 0017 ActivityBase { 0018 id: activity 0019 0020 onStart: focus = true 0021 onStop: {} 0022 0023 pageComponent: Image { 0024 id: background 0025 source: "qrc:/gcompris/src/activities/crane/resource/background.svg" 0026 anchors.fill: parent 0027 fillMode: Image.PreserveAspectCrop 0028 sourceSize.height: height 0029 signal start 0030 signal stop 0031 0032 Component.onCompleted: { 0033 activity.start.connect(start) 0034 activity.stop.connect(stop) 0035 } 0036 0037 // Add here the QML items you need to access in javascript 0038 QtObject { 0039 id: items 0040 property Item main: activity.main 0041 property alias background: background 0042 property alias bonus: bonus 0043 property alias score: score 0044 property GCSfx audioEffects: activity.audioEffects 0045 0046 property var levels: activity.datasetLoader.data 0047 property int subLevelCount: 0 0048 property int currentLevel: activity.currentLevel 0049 property int currentSubLevel: 0 0050 property alias solution: solution 0051 property alias answer: answer 0052 property alias tokens: tokens 0053 property alias solutionModel: solutionModel 0054 property alias answerModel: answerModel 0055 property alias tokensModel: tokensModel 0056 property int currentAnswer: 0 0057 property alias currentToken: tokens.currentIndex 0058 property alias animationToken: animationToken 0059 property alias readyButton: readyButton 0060 property alias errorRectangle: errorRectangle 0061 0062 property alias instruction: instruction 0063 property alias file: file 0064 property bool buttonsBlocked: false 0065 0066 function toggleReady() { 0067 solution.visible = !solution.visible 0068 readyButton.enabled = solution.visible 0069 tokens.visible = !tokens.visible 0070 } 0071 } 0072 property int baseMargins: 10 * ApplicationInfo.ratio 0073 property int baseSizeValue: 60 * ApplicationInfo.ratio 0074 0075 onStart: { Activity.start(items) } 0076 onStop: { Activity.stop() } 0077 0078 ListModel { id: solutionModel } 0079 ListModel { id: answerModel } 0080 ListModel { 0081 id: tokensModel 0082 function randPosition() { return (Math.floor(Math.random() * count)) } // choose a random position 0083 function shuffleModel() { for (var i = 0 ; i < count; i++) { move(randPosition(), randPosition(), 1) } } // shuffle elements 0084 } 0085 0086 File { 0087 id: file 0088 onError: console.error("File error: " + msg) 0089 } 0090 0091 Rectangle { 0092 id: instructionArea 0093 opacity: 1 0094 radius: background.baseMargins 0095 color: "#373737" 0096 height: 40 * ApplicationInfo.ratio 0097 width: Math.min(320 * ApplicationInfo.ratio, parent.width - 2 * background.baseMargins) 0098 anchors.horizontalCenter: parent.horizontalCenter 0099 anchors.top: parent.top 0100 anchors.topMargin: background.baseMargins 0101 0102 GCText { 0103 id: instruction 0104 wrapMode: TextEdit.WordWrap 0105 horizontalAlignment: Text.AlignHCenter 0106 verticalAlignment: Text.AlignVCenter 0107 height: parent.height - background.baseMargins 0108 width: parent.width - 2 * background.baseMargins 0109 fontSizeMode: Text.Fit 0110 color: 'white' 0111 anchors.centerIn: instructionArea 0112 } 0113 } 0114 0115 Item { 0116 id: layoutArea 0117 anchors.top: instructionArea.bottom 0118 anchors.bottom: okButton.top 0119 anchors.left: background.left 0120 anchors.right: background.right 0121 anchors.margins: background.baseMargins 0122 } 0123 0124 Item { 0125 id: referenceArea // used to calculate the ideal token and flow size for solution and answer 0126 width: layoutArea.width - background.baseMargins 0127 height: (layoutArea.height - 5 * background.baseMargins) * 0.33 0128 property int tokenSize: Math.floor(Math.min(Core.fitItems(width, height, solutionModel.count), background.baseSizeValue)) 0129 } 0130 0131 Rectangle { 0132 id: solutionRect 0133 width: solution.childrenRect.width + background.baseMargins 0134 height: solution.childrenRect.height + background.baseMargins 0135 anchors.top: layoutArea.top 0136 anchors.horizontalCenter: parent.horizontalCenter 0137 color: "#E9E9E9" 0138 radius: background.baseMargins 0139 Flow { 0140 id: solution 0141 width: referenceArea.width 0142 height: referenceArea.height 0143 anchors.top: parent.top 0144 anchors.topMargin: background.baseMargins * 0.5 0145 anchors.left: parent.left 0146 anchors.leftMargin: anchors.topMargin 0147 layoutDirection: (Core.isLeftToRightLocale(ApplicationSettings.locale)) ? Qt.LeftToRight : Qt.RightToLeft 0148 Repeater { 0149 model: solutionModel 0150 delegate: TokenFrieze { 0151 width: referenceArea.tokenSize 0152 } 0153 } 0154 } 0155 0156 } 0157 0158 Rectangle { 0159 id: answerRect 0160 width: answer.childrenRect.width + background.baseMargins 0161 height: answer.childrenRect.height + background.baseMargins 0162 anchors.horizontalCenter: parent.horizontalCenter 0163 anchors.top: solutionRect.bottom 0164 anchors.topMargin: background.baseMargins 0165 color: "#E9E9E9" 0166 radius: background.baseMargins 0167 Flow { 0168 id: answer 0169 width: referenceArea.width 0170 height: referenceArea.height 0171 anchors.top: parent.top 0172 anchors.topMargin: background.baseMargins * 0.5 0173 anchors.left: parent.left 0174 anchors.leftMargin: anchors.topMargin 0175 layoutDirection: (Core.isLeftToRightLocale(ApplicationSettings.locale)) ? Qt.LeftToRight : Qt.RightToLeft 0176 Repeater { 0177 model: answerModel 0178 delegate: TokenFrieze { 0179 width: referenceArea.tokenSize 0180 } 0181 } 0182 } 0183 0184 ErrorRectangle { 0185 id: errorRectangle 0186 anchors.fill: parent 0187 radius: background.baseMargins 0188 imageSize: parent.height * 0.75 0189 function releaseControls() { 0190 items.buttonsBlocked = false; 0191 } 0192 } 0193 } 0194 0195 Item { 0196 id: controlsArea 0197 width: layoutArea.width 0198 height: referenceArea.height 0199 anchors.top: answerRect.bottom 0200 anchors.topMargin: background.baseMargins 0201 anchors.horizontalCenter: parent.horizontalCenter 0202 Item { 0203 id: tokensArea 0204 width: parent.width - background.baseSizeValue * 2 - background.baseMargins * 2 0205 height: parent.height 0206 property int tokenSize: Math.floor(Math.min(Core.fitItems(width, height, tokensModel.count), background.baseSizeValue)) 0207 } 0208 Rectangle { 0209 id: tokensRect 0210 width: tokens.contentItem.childrenRect.width + background.baseMargins 0211 height: tokens.contentItem.childrenRect.height + background.baseMargins 0212 anchors.top: parent.top 0213 anchors.horizontalCenter: parent.horizontalCenter 0214 color: "#E9E9E9" 0215 radius: background.baseMargins 0216 enabled: !items.buttonsBlocked 0217 GridView { 0218 id: tokens 0219 width: tokensArea.width 0220 height: tokensArea.height 0221 cellWidth: tokensArea.tokenSize 0222 cellHeight: tokensArea.tokenSize 0223 anchors.top: parent.top 0224 anchors.topMargin: background.baseMargins * 0.5 0225 anchors.left: parent.left 0226 anchors.leftMargin: anchors.topMargin 0227 boundsBehavior: Flickable.StopAtBounds 0228 keyNavigationWraps: true 0229 enabled: (items.currentAnswer < answerModel.count) 0230 opacity: (enabled || items.buttonsBlocked) ? 1.0 : 0.3 0231 model: tokensModel 0232 delegate: TokenFrieze { 0233 width: tokensArea.tokenSize 0234 } 0235 highlightMoveDuration: 0 0236 highlight: Rectangle { 0237 color: "#00FFFFFF" 0238 radius: height * 0.1 0239 border.color: "#80373737" 0240 border.width: 2 * ApplicationInfo.ratio 0241 } 0242 } 0243 TokenFrieze { // Animated token visible during drops 0244 id: animationToken 0245 width: tokensArea.tokenSize 0246 content: Activity.emptyToken 0247 animated: true 0248 clickable: false 0249 shown: true 0250 visible:false 0251 z: 10 0252 states: [ 0253 State { 0254 name: "moveto" 0255 PropertyChanges { 0256 target: animationToken 0257 visible: true 0258 x: tokens.mapFromItem(answer.children[items.currentAnswer], 0, 0).x 0259 y: tokens.mapFromItem(answer.children[items.currentAnswer], 0, 0).y 0260 } 0261 } 0262 ] 0263 transitions: [ 0264 Transition { 0265 to: "moveto" 0266 SequentialAnimation { 0267 alwaysRunToEnd: true 0268 SmoothedAnimation { properties: "x,y"; duration: 300 } 0269 ScriptAction { 0270 script: { // End of moveto 0271 animationToken.state = "" 0272 answerModel.setProperty(items.currentAnswer, "content_", animationToken.content) 0273 tokens.currentItem.opacity = 1.0 0274 items.currentAnswer++ 0275 items.buttonsBlocked = false 0276 } 0277 } 0278 } 0279 } 0280 ] 0281 } 0282 } 0283 0284 GCButton { 0285 id: readyButton 0286 width: Math.min(2 * background.baseSizeValue, tokensRect.width) 0287 height: Math.min(background.baseSizeValue, tokensRect.height) 0288 anchors.centerIn: tokensRect 0289 text: qsTr("I am Ready") 0290 opacity: (!enabled) ? 0.0 : 1.0 0291 theme: "dark" 0292 MouseArea { 0293 anchors.fill: parent 0294 onClicked: items.toggleReady() 0295 enabled: !items.buttonsBlocked 0296 } 0297 } 0298 0299 Image { 0300 id: hintButton 0301 source: "qrc:/gcompris/src/core/resource/bar_hint.svg" 0302 smooth: true 0303 width: background.baseSizeValue 0304 height: background.baseSizeValue 0305 sourceSize.width: background.baseSizeValue 0306 sourceSize.height: background.baseSizeValue 0307 fillMode: Image.PreserveAspectFit 0308 anchors.right: tokensRect.left 0309 anchors.rightMargin: background.baseMargins 0310 anchors.verticalCenter: tokensRect.verticalCenter 0311 enabled: !solution.visible 0312 opacity: (!enabled) ? 0.0 : 1.0 0313 MouseArea { 0314 anchors.fill: parent 0315 onClicked: items.toggleReady() 0316 enabled: !items.buttonsBlocked 0317 } 0318 } 0319 0320 Image { 0321 id: cancelButton 0322 source: enabled ? "qrc:/gcompris/src/core/resource/cancel.svg" : "qrc:/gcompris/src/core/resource/cancel_disabled.svg" 0323 smooth: true 0324 width: background.baseSizeValue 0325 height: background.baseSizeValue 0326 sourceSize.width: background.baseSizeValue 0327 sourceSize.height: background.baseSizeValue 0328 fillMode: Image.PreserveAspectFit 0329 anchors.left: tokensRect.right 0330 anchors.leftMargin: background.baseMargins 0331 anchors.verticalCenter: tokensRect.verticalCenter 0332 enabled: (items.currentAnswer > 0) && (!readyButton.enabled) 0333 MouseArea { 0334 anchors.fill: parent 0335 enabled: !items.buttonsBlocked 0336 onClicked: Activity.cancelDrop() 0337 } 0338 } 0339 } 0340 0341 DialogChooseLevel { 0342 id: dialogActivityConfig 0343 currentActivity: activity.activityInfo 0344 0345 onSaveData: { 0346 levelFolder = dialogActivityConfig.chosenLevels 0347 currentActivity.currentLevels = dialogActivityConfig.chosenLevels 0348 ApplicationSettings.setCurrentLevels(currentActivity.name, dialogActivityConfig.chosenLevels) 0349 } 0350 onClose: { 0351 home() 0352 } 0353 onStartActivity: { 0354 background.stop() 0355 background.start() 0356 } 0357 } 0358 0359 DialogHelp { 0360 id: dialogHelp 0361 onClose: home() 0362 } 0363 0364 BarButton { 0365 id: okButton 0366 anchors.right: score.left 0367 anchors.rightMargin: background.baseMargins 0368 anchors.verticalCenter: score.verticalCenter 0369 source: "qrc:/gcompris/src/core/resource/bar_ok.svg" 0370 width: background.baseSizeValue 0371 height: background.baseSizeValue 0372 sourceSize.height: background.baseSizeValue 0373 sourceSize.width: background.baseSizeValue 0374 onClicked: Activity.checkResult() 0375 visible: (items.currentAnswer === answerModel.count) 0376 mouseArea.enabled: !items.buttonsBlocked 0377 } 0378 0379 Score { 0380 id: score 0381 numberOfSubLevels: items.subLevelCount 0382 currentSubLevel: items.currentSubLevel 0383 anchors.top: undefined 0384 anchors.bottom: background.bottom 0385 anchors.bottomMargin: bar.height * 1.5 0386 anchors.right: background.right 0387 anchors.rightMargin: background.baseMargins 0388 onStop: { 0389 Activity.nextSubLevel() 0390 } 0391 } 0392 Bar { 0393 id: bar 0394 level: items.currentLevel + 1 0395 content: BarEnumContent { value: help | home | level | activityConfig } 0396 onHelpClicked: displayDialog(dialogHelp) 0397 onActivityConfigClicked: displayDialog(dialogActivityConfig) 0398 onPreviousLevelClicked: Activity.previousLevel() 0399 onNextLevelClicked: Activity.nextLevel() 0400 onHomeClicked: activity.home() 0401 } 0402 0403 Bonus { 0404 id: bonus 0405 Component.onCompleted: win.connect(Activity.nextLevel) 0406 onStop: (items.solution.visible = !items.levels[items.currentLevel].hidden) 0407 } 0408 0409 Keys.onPressed: Activity.handleKeys(event) 0410 } 0411 }