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 }