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

0001 /* GCompris - WordAndClass.qml
0002  *
0003  * Copyright (C) 2022-2023 Bruno ANSELME <be.root@free.fr>
0004  *
0005  * Authors:
0006  *   Bruno ANSELME <be.root@free.fr> (Qt Quick native)
0007  *   Timothée Giet <animtim@gmail.com> (Graphics and layout refactoring)
0008  *
0009  *   SPDX-License-Identifier: GPL-3.0-or-later
0010  */
0011 import QtQuick 2.12
0012 
0013 import GCompris 1.0
0014 import "../../core"
0015 import "grammar_analysis.js" as Activity
0016 import "qrc:/gcompris/src/core/core.js" as Core
0017 
0018 /**
0019  * A WordAndClass item display a word and below nothing or one or more grammatical classes symbols.
0020  * Spaces, punctuation and unwanted words are initialized only with wordText. Other properties are empty.
0021  * expected contains single class codes or merged (nou, vrb, nou+adj)
0022  *  if expected is empty, token frame is not displayed and not droppable
0023  *  else token frame is displayed, token is droppable but an empty (or "_") proposition is expected
0024  * proposition is made of merged proposals from each individual class
0025  * boxModel contains boxes for grammatical classes
0026  */
0027 
0028 Item {
0029     id: wordClassItem
0030     property string wordText: ""            // Word, group of word, space or punctuation
0031     property string expected: ""            // Grammatical(s) class(es) expected
0032     property string proposition: ""         // Grammatical(s) class(es) proposed by user
0033     property int startPos: -1               // Number from this position
0034     property var classList: []              // Splitted expected value
0035     property bool moveForward: true         // Move to next word after proposal (for keys navigation)
0036     property alias rowWords: rowWords
0037     property alias boxModel: boxModel
0038 
0039     width: Math.max(textView.width, rowWords.width) + 2
0040     height: textView.height + rowWords.height + background.baseMargins
0041 
0042     // Set proposal for grammatical class number idx
0043     function setProposal(idx, forward = true) {
0044         if (expected === "") return
0045         if (items.selectedClass === -1) return
0046         moveForward = forward
0047         Activity.animRunning = true
0048         rowWords.children[idx].imgSvg.state = "vanish"
0049         var proposal = items.goalModel.get(items.selectedClass).code
0050         if (proposal !== "eraser") {
0051             items.gridGoalTokens.children[items.selectedClass].imgSvg.state = "moveto"
0052             buildProposition()
0053         }
0054     }
0055 
0056     // Transition is finished. It time to set proposal and image.
0057     function endProposal(idx) {
0058         rowWords.children[idx].imgSvg.state = ""
0059         var proposal = items.goalModel.get(items.selectedClass).code
0060         if (proposal === "eraser") {
0061             proposal = "_"
0062             boxModel.set(idx, { "svgSource": "", "proposal": proposal })
0063         } else {
0064             boxModel.set(idx, { "svgSource": items.goalModel.get(items.selectedClass).image, "proposal": proposal })
0065         }
0066         buildProposition()
0067         if (moveForward)
0068             items.selectedBox = (items.selectedBox + 1) % Activity.boxCount
0069     }
0070 
0071     // Build global proposition with boxModel's proposals
0072     function buildProposition() {
0073         var props = []
0074         for (var i = 0; i < boxModel.count; i++) {
0075             props.push(boxModel.get(i).proposal)
0076         }
0077         proposition = props.join('+')
0078     }
0079 
0080     ListModel { id: boxModel }
0081 
0082     //--- Debugging zone.
0083     Rectangle {     // show presence of a word/punctuation/space in debug mode
0084         width: 2 * parent.width / 3
0085         anchors.top: parent.top
0086         anchors.horizontalCenter: parent.horizontalCenter
0087         height: 2
0088         color: "lightcoral"
0089         visible: items.debugActive
0090     }
0091     Text {          // show expected value
0092         text: expected
0093         anchors.horizontalCenter: parent.horizontalCenter
0094         anchors.top: parent.top
0095         opacity: 0.5
0096         visible: (expected !== "") && (items.debugActive)
0097     }
0098     //--- End of debugging zone.
0099 
0100     Column {
0101         anchors.fill: parent
0102         spacing: ApplicationInfo.ratio
0103         Item {
0104             width: Math.max(textView.width, rowWords.width)
0105             height: textView.height
0106             anchors.horizontalCenter: parent.horizontalCenter
0107             GCText {        // Word display
0108                 id: textView
0109                 anchors.horizontalCenter: parent.horizontalCenter
0110                 anchors.verticalCenter: parent.verticalCenter
0111                 fontSize: wordsArea.isSmallHeight ? tinySize : regularSize
0112                 text: wordText
0113             }
0114         }
0115         Row {               // Row of grammar classes (boxes for tokens)
0116             id: rowWords
0117             anchors.horizontalCenter: parent.horizontalCenter
0118             spacing: ApplicationInfo.ratio
0119             layoutDirection: (Core.isLeftToRightLocale(items.locale)) ? Qt.LeftToRight : Qt.RightToLeft
0120             Repeater {
0121                 model: boxModel
0122                 Rectangle {
0123                     id: token
0124                     property int order: order_
0125                     property alias imgSvg: imgSvg
0126                     property bool boxExpected: boxExpected_
0127                     width: imgSvg.width + background.baseMargins
0128                     height: width
0129                     radius: background.baseRadius
0130                     color: "transparent"
0131                     border.color: boxExpected ? background.selectionColor : "transparent"
0132                     border.width: (order === items.selectedBox)  ? 4 * ApplicationInfo.ratio : ApplicationInfo.ratio
0133                     visible: (expected !== "")
0134 
0135                     Image {         //
0136                         id: imgSvg
0137                         source: (svgSource == "") ? "qrc:/gcompris/src/core/resource/empty.svg" : svgSource
0138                         width: (expected == "") ? background.baseMargins : wordsArea.itemHeight
0139                         height: width
0140                         sourceSize.width: width
0141                         sourceSize.height: width
0142                         anchors.centerIn: parent
0143                         states: [
0144                             State {
0145                                 name: ""
0146                                 PropertyChanges { target: imgSvg; opacity: 1.0 }
0147                             },
0148                             State {     // Token vanish when erased or replaced
0149                                 name: "vanish"
0150                                 PropertyChanges { target: imgSvg; opacity: 0.0 }
0151                             }
0152                         ]
0153                         transitions: [
0154                             Transition {
0155                                 to: "vanish"
0156                                 reversible: false
0157                                 SequentialAnimation {
0158                                     alwaysRunToEnd: true
0159                                     NumberAnimation {
0160                                         properties: "opacity"
0161                                         duration: 250
0162                                     }
0163                                     ScriptAction {
0164                                         script: {
0165                                             var pos = items.boxIndexes[items.selectedBox].split('-')
0166                                             items.wordsFlow.children[pos[0]].endProposal(pos[1])
0167                                             Activity.animRunning = false
0168                                         }
0169                                     }
0170                                 }
0171                             }
0172                         ]
0173                     }
0174                     MouseArea {
0175                         id: mouseArea
0176                         anchors.fill: parent
0177                         hoverEnabled: true
0178                         enabled: (order !== -1)
0179                         onClicked: {
0180                             if (Activity.animRunning) return            // No click or key or drop during animation
0181                             items.selectedBox = order
0182                             items.keysOnTokens = false
0183                             setProposal(index, false)
0184                         }
0185                     }
0186                     SwingAnimation {
0187                         running: (!items.keysOnTokens) && (order === items.selectedBox)
0188                         amplitude: 4
0189                         loops: 1
0190                         target: token
0191                     }
0192                 }
0193             }
0194         }
0195     }
0196     // Build boxModel from expected
0197     Component.onCompleted: {
0198         var prop = "_"
0199         boxModel.clear()
0200         classList = expected.split(/\+/)
0201         var currentOrder = wordClassItem.startPos
0202         for (var j = 0; j < classList.length; j++) {                    // Loop on merged classes
0203             var order_ = (classList[j] === "") ? -1 : currentOrder
0204             boxModel.append(        // Empty values until a GrammarToken is dropped
0205                         {
0206                             "svgSource" : "",
0207                             "proposal" : prop,
0208                             "boxExpected_": (classList[j] !== ""),
0209                             "order_": order_
0210                         })
0211             if (classList[j] !== "") currentOrder++
0212         }
0213         buildProposition()
0214         if (wordText.trim() !== wordText.replace(/\s+/,'').replace(/\s+/,''))             // Check for spaces between words (it was parentheses).
0215           // We replace twice, one for the spaces before the word, one for the ones after
0216             wordText = "<u>" + wordText + "</u>"
0217     }
0218 }