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

0001 /* GCompris - mosaic.qml
0002  *
0003  * SPDX-FileCopyrightText: 2014 Bruno Coudoin <bruno.coudoin@gcompris.net>
0004  *
0005  * Authors:
0006  *   Clement coudoin <clement.coudoin@free.fr> (GTK+ version)
0007  *   Bruno.coudoin <bruno.coudoin@gcompris.net> (Qt Quick port)
0008  *
0009  *   SPDX-License-Identifier: GPL-3.0-or-later
0010  */
0011 import QtQuick 2.12
0012 import GCompris 1.0
0013 
0014 import "../../core"
0015 import "mosaic.js" as Activity
0016 import "qrc:/gcompris/src/core/core.js" as Core
0017 
0018 ActivityBase {
0019     id: activity
0020 
0021     onStart: focus = true
0022     onStop: {}
0023 
0024     pageComponent: Image {
0025         id: background
0026         source: Activity.url + "background.svg"
0027         sourceSize.width: width
0028         sourceSize.height: height
0029         fillMode: Image.PreserveAspectCrop
0030         anchors.fill: parent
0031         signal start
0032         signal stop
0033 
0034         property bool keyboardMode: false
0035         property var areaWithKeyboardFocus: selector
0036 
0037         Component.onCompleted: {
0038             dialogActivityConfig.initialize()
0039             activity.start.connect(start)
0040             activity.stop.connect(stop)
0041         }
0042 
0043         // Add here the QML items you need to access in javascript
0044         QtObject {
0045             id: items
0046             property Item main: activity.main
0047             property GCSfx audioEffects: activity.audioEffects
0048             property alias question: question
0049             property alias answer: answer
0050             property alias selector: selector
0051             property alias background: background
0052             property int currentLevel: activity.currentLevel
0053             property alias bonus: bonus
0054             property string selectedItem
0055             readonly property var levels: activity.datasetLoader.data
0056             property int nbItems
0057             property int questionLayoutColumns
0058             property int questionLayoutRows
0059             property string modelDisplayLayout
0060         }
0061 
0062         onStart: { Activity.start(items) }
0063         onStop: { Activity.stop() }
0064 
0065         Keys.onLeftPressed: {
0066             keyboardMode = true
0067             areaWithKeyboardFocus.moveCurrentIndexLeft()
0068         }
0069 
0070         Keys.onRightPressed: {
0071             keyboardMode = true
0072             areaWithKeyboardFocus.moveCurrentIndexRight()
0073         }
0074 
0075         Keys.onUpPressed: {
0076             keyboardMode = true
0077             areaWithKeyboardFocus.moveCurrentIndexUp()
0078         }
0079 
0080         Keys.onDownPressed: {
0081             keyboardMode = true
0082             areaWithKeyboardFocus.moveCurrentIndexDown()
0083         }
0084 
0085         Keys.onEnterPressed: {
0086             selectCell()
0087         }
0088 
0089         Keys.onSpacePressed: {
0090             selectCell()
0091         }
0092 
0093         Keys.onReturnPressed: {
0094             selectCell()
0095         }
0096 
0097         Keys.onTabPressed: {
0098             keyboardMode = true
0099             areaWithKeyboardFocus.changeAreaWithKeyboardFocus()
0100         }
0101 
0102         function selectCell() {
0103             keyboardMode = true
0104             areaWithKeyboardFocus.selectCurrentCell(areaWithKeyboardFocus.currentItem)
0105         }
0106 
0107         Rectangle {
0108             id: mainArea
0109             anchors.top: background.top
0110             anchors.bottom: bar.top
0111             anchors.left: background.left
0112             anchors.right: background.right
0113             color: "#00FFFFFF"
0114 
0115             property int nbItems: 24
0116             property bool horizontal: background.width >= background.height
0117             property bool smallQuestionMode: items.modelDisplayLayout === "smaller"
0118             property int nbColumns: items.questionLayoutColumns
0119             property int nbLines: items.questionLayoutRows
0120             property int layoutMargin: Math.floor(10 * ApplicationInfo.ratio)
0121             property int itemsMargin: Math.floor(5 * ApplicationInfo.ratio)
0122 
0123             states: [
0124                 State {
0125                     name: "horizontal"
0126                     when: mainArea.horizontal
0127                     AnchorChanges {
0128                         target: questionRectangle
0129                         anchors.top: parent.top
0130                         anchors.left: parent.left
0131                         anchors.horizontalCenter: undefined
0132                     }
0133                     PropertyChanges {
0134                         target: answerRectangle
0135                         height: mainArea.height * 0.66 - mainArea.layoutMargin * 2
0136                         width: mainArea.width * 0.5  - mainArea.layoutMargin * 1.5
0137                     }
0138                     AnchorChanges {
0139                         target: answerRectangle
0140                         anchors.top: parent.top
0141                         anchors.left: questionRectangle.right
0142                     }
0143                 },
0144                 State {
0145                     name: "vertical"
0146                     when: !mainArea.horizontal
0147                     AnchorChanges {
0148                         target: questionRectangle
0149                         anchors.top: parent.top
0150                         anchors.left: parent.left
0151                         anchors.horizontalCenter: undefined
0152                     }
0153                     PropertyChanges {
0154                         target: answerRectangle
0155                         height: mainArea.height / 3 - mainArea.layoutMargin * 2
0156                         width: mainArea.width - mainArea.layoutMargin * 2
0157                     }
0158                     AnchorChanges {
0159                         target: answerRectangle
0160                         anchors.top: questionRectangle.bottom
0161                         anchors.left: parent.left
0162                     }
0163                 }
0164             ]
0165 
0166             // === The Question Area ===
0167             Rectangle {
0168                 id: questionRectangle
0169                 color: "#00FFFFFF"
0170                 height: answerRectangle.height
0171                 width: answerRectangle.width
0172                 anchors.top: mainArea.top
0173                 anchors.left: mainArea.left
0174                 anchors.topMargin: mainArea.layoutMargin
0175                 anchors.leftMargin: mainArea.layoutMargin
0176                 Rectangle {
0177                     id: questionRectangleContent
0178                     anchors.centerIn: parent
0179                     color: "#55333333"
0180                     border.color: "black"
0181                     border.width: 2
0182                     radius: 5
0183 
0184                     states: [
0185                         State {
0186                             name: "smallQuestion"
0187                             when: mainArea.smallQuestionMode
0188                             PropertyChanges {
0189                                 target: questionRectangleContent
0190                                 height: answerRectangle.height * 0.7
0191                                 width: answerRectangle.width * 0.7
0192                             }
0193                         },
0194                         State {
0195                             name: "normalQuestion"
0196                             when: !mainArea.smallQuestionMode
0197                             PropertyChanges {
0198                                 target: questionRectangleContent
0199                                 height: answerRectangle.height
0200                                 width: answerRectangle.width
0201                             }
0202                         }
0203                     ]
0204 
0205                     GridView {
0206                         id: question
0207                         width: cellWidth * mainArea.nbColumns
0208                         height: cellHeight * mainArea.nbLines
0209                         anchors.centerIn: parent
0210                         cellHeight: cellWidth
0211                         cellWidth: Math.floor(Math.min((parent.width - mainArea.itemsMargin) / mainArea.nbColumns, (parent.height - mainArea.itemsMargin) / mainArea.nbLines))
0212                         interactive: false
0213                         keyNavigationWraps: true
0214                         delegate: Item {
0215                             width: question.cellWidth
0216                             height: question.cellHeight
0217                             Image {
0218                                 id: imageQuestionId
0219                                 source: Activity.url + modelData
0220                                 fillMode: Image.PreserveAspectFit
0221                                 width: question.cellWidth - mainArea.itemsMargin
0222                                 height: width
0223                                 sourceSize.width: width
0224                                 sourceSize.height: width
0225                                 anchors.centerIn: parent
0226                             }
0227                         }
0228                     }
0229                 }
0230             }
0231 
0232             // === The Answer Area ===
0233             Rectangle {
0234                 id: answerRectangle
0235                 height: mainArea.height * 0.33 - mainArea.layoutMargin * 2
0236                 width: mainArea.width  - mainArea.layoutMargin * 2
0237                 anchors.top: questionRectangle.bottom
0238                 anchors.left: mainArea.left
0239                 anchors.topMargin: mainArea.layoutMargin
0240                 anchors.leftMargin: mainArea.layoutMargin
0241                 color: "#55333333"
0242                 border.color: "black"
0243                 border.width: 2
0244                 radius: 5
0245 
0246                 GridView {
0247                     id: answer
0248                     width: cellWidth * mainArea.nbColumns
0249                     height: cellHeight * mainArea.nbLines
0250                     anchors.centerIn: parent
0251                     cellHeight: cellWidth
0252                     cellWidth: Math.floor(Math.min((parent.width - mainArea.itemsMargin) / mainArea.nbColumns, (parent.height - mainArea.itemsMargin) / mainArea.nbLines))
0253                     interactive: false
0254                     keyNavigationWraps: true
0255                     highlightFollowsCurrentItem: true
0256                     highlight: Rectangle {
0257                         color: "red"
0258                         border.width: 3
0259                         border.color: "black"
0260                         opacity: 0.6
0261                         visible: background.keyboardMode && (background.areaWithKeyboardFocus === answer)
0262                         Behavior on x { SpringAnimation { spring: 2; damping: 0.2 } }
0263                         Behavior on y { SpringAnimation { spring: 2; damping: 0.2 } }
0264                     }
0265 
0266                     // If the image was directly used as a delegate (without containing it in the item), the highlight element would have been be hard to notice as it would get completely hidden by the image due to the same sizes.
0267                     delegate: Item {
0268                         id: cellItem
0269                         width: answer.cellWidth
0270                         height: answer.cellHeight
0271 
0272                         readonly property int cellIndex: index
0273 
0274                         Image {
0275                             id: imageAnswerId
0276                             source: Activity.url + modelData
0277                             fillMode: Image.PreserveAspectFit
0278                             width: answer.cellWidth - mainArea.itemsMargin
0279                             height: width
0280                             sourceSize.width: width
0281                             sourceSize.height: height
0282                             anchors.centerIn: parent
0283 
0284                             MouseArea {
0285                                 anchors.fill: parent
0286                                 onClicked: answer.selectCurrentCell(cellItem)
0287                             }
0288                         }
0289                     }
0290                     function selectCurrentCell(selectedCell) {
0291                         Activity.answerSelected(selectedCell.cellIndex)
0292                     }
0293 
0294                     function changeAreaWithKeyboardFocus() {
0295                         areaWithKeyboardFocus = selector
0296                     }
0297                 }
0298             }
0299 
0300             // === The Selector ===
0301             Rectangle {
0302                 id: selectorRectangle
0303                 height: mainArea.height * 0.33 - mainArea.layoutMargin * 2
0304                 width: mainArea.width - mainArea.layoutMargin * 2
0305                 anchors.top: answerRectangle.bottom
0306                 anchors.left: mainArea.left
0307                 anchors.topMargin: mainArea.layoutMargin
0308                 anchors.leftMargin: mainArea.layoutMargin
0309                 color: "#661111AA"
0310                 border.color: "black"
0311                 border.width: 2
0312                 radius: 5
0313                 property int selectorItemSize: Core.fitItems(selectorRectangle.width - mainArea.itemsMargin, selectorRectangle.height - mainArea.itemsMargin, selector.count)
0314 
0315                 GridView {
0316                     id: selector
0317                     width: parent.width
0318                     height: parent.height
0319                     anchors.centerIn: parent
0320                     cellHeight: selectorRectangle.selectorItemSize
0321                     cellWidth: selectorRectangle.selectorItemSize
0322                     interactive: false
0323                     keyNavigationWraps: true
0324                     highlightFollowsCurrentItem: true
0325                     highlight: Rectangle {
0326                         color: "red"
0327                         border.width: 3
0328                         border.color: "black"
0329                         opacity: 0.6
0330                         visible: background.keyboardMode && (background.areaWithKeyboardFocus === selector)
0331                         Behavior on x { SpringAnimation { spring: 2; damping: 0.2 } }
0332                         Behavior on y { SpringAnimation { spring: 2; damping: 0.2 } }
0333                     }
0334                     delegate: Item {
0335                         id: selectorItem
0336                         width: selector.cellWidth
0337                         height: width
0338                         Image {
0339                             id: imageId
0340                             source: Activity.url + modelData
0341                             fillMode: Image.PreserveAspectFit
0342                             width: selector.cellWidth - mainArea.itemsMargin
0343                             height: width
0344                             sourceSize.width: width
0345                             sourceSize.height: height
0346                             antialiasing: true
0347                             anchors.centerIn: parent
0348                             z: selectorItem.iAmSelected ? 10 : 1
0349                         }
0350 
0351                         readonly property bool iAmSelected: items.selectedItem === modelData
0352                         readonly property string imageName: modelData
0353 
0354                         states: [
0355                             State {
0356                                 name: "notclicked"
0357                                 when: !selectorItem.iAmSelected && !mouseArea.containsMouse
0358                                 PropertyChanges {
0359                                     target: imageId
0360                                     scale: 0.8
0361                                 }
0362                             },
0363                             State {
0364                                 name: "clicked"
0365                                 when: mouseArea.pressed
0366                                 PropertyChanges {
0367                                     target: imageId
0368                                     scale: 0.7
0369                                 }
0370                             },
0371                             State {
0372                                 name: "hover"
0373                                 when: mouseArea.containsMouse
0374                                 PropertyChanges {
0375                                     target: imageId
0376                                     scale: 1
0377                                 }
0378                             },
0379                             State {
0380                                 name: "selected"
0381                                 when: selectorItem.iAmSelected
0382                                 PropertyChanges {
0383                                     target: imageId
0384                                     scale: 1
0385                                 }
0386                             }
0387                         ]
0388 
0389                         SequentialAnimation {
0390                             id: anim
0391                             running: selectorItem.iAmSelected
0392                             loops: Animation.Infinite
0393                             alwaysRunToEnd: true
0394                             NumberAnimation {
0395                                 target: imageId
0396                                 property: "rotation"
0397                                 from: 0; to: 10
0398                                 duration: 200
0399                                 easing.type: Easing.OutQuad
0400                             }
0401                             NumberAnimation {
0402                                 target: imageId
0403                                 property: "rotation"
0404                                 from: 10; to: -10
0405                                 duration: 400
0406                                 easing.type: Easing.InOutQuad
0407                             }
0408                             NumberAnimation {
0409                                 target: imageId
0410                                 property: "rotation"
0411                                 from: -10; to: 0
0412                                 duration: 200
0413                                 easing.type: Easing.InQuad
0414                             }
0415                         }
0416 
0417                         Behavior on scale { NumberAnimation { duration: 70 } }
0418                         MouseArea {
0419                             id: mouseArea
0420                             anchors.fill: imageId
0421                             hoverEnabled: true
0422                             onClicked: selector.selectCurrentCell(parent)
0423                         }
0424                     }
0425 
0426                     function selectCurrentCell(selectedCell) {
0427                         items.audioEffects.play("qrc:/gcompris/src/core/resource/sounds/scroll.wav")
0428                         items.selectedItem = selectedCell.imageName
0429                     }
0430 
0431                     function changeAreaWithKeyboardFocus() {
0432                         areaWithKeyboardFocus = answer
0433                     }
0434                 }
0435             }
0436         }
0437 
0438         DialogChooseLevel {
0439             id: dialogActivityConfig
0440             currentActivity: activity.activityInfo
0441 
0442             onSaveData: {
0443                 levelFolder = dialogActivityConfig.chosenLevels
0444                 currentActivity.currentLevels = dialogActivityConfig.chosenLevels
0445                 ApplicationSettings.setCurrentLevels(currentActivity.name, dialogActivityConfig.chosenLevels)
0446                 // restart activity on saving
0447                 background.start()
0448             }
0449             onClose: {
0450                 home()
0451             }
0452             onStartActivity: {
0453                 background.start()
0454             }
0455         }
0456 
0457         DialogHelp {
0458             id: dialogHelp
0459             onClose: home()
0460         }
0461 
0462         Bar {
0463             id: bar
0464             level: items.currentLevel + 1
0465             content: BarEnumContent { value: help | home | level | activityConfig}
0466             onHelpClicked: {
0467                 displayDialog(dialogHelp)
0468             }
0469             onActivityConfigClicked: {
0470                 displayDialog(dialogActivityConfig)
0471             }
0472             onPreviousLevelClicked: Activity.previousLevel()
0473             onNextLevelClicked: Activity.nextLevel()
0474             onHomeClicked: activity.home()
0475         }
0476 
0477         Bonus {
0478             id: bonus
0479             Component.onCompleted: win.connect(Activity.nextLevel)
0480         }
0481     }
0482 }