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 }