Warning, /education/gcompris/src/core/GCCreationHandler.qml is written in an unsupported language. File is not indexed.
0001 /* GCompris - GCCreationHandler.qml 0002 * 0003 * SPDX-FileCopyrightText: 2018 Aman Kumar Gupta <gupta2140@gmail.com> 0004 * 0005 * Authors: 0006 * Aman Kumar Gupta <gupta2140@gmail.com> 0007 * 0008 * SPDX-License-Identifier: GPL-3.0-or-later 0009 */ 0010 0011 import QtQuick 2.12 0012 import GCompris 1.0 0013 // TextField 0014 import QtQuick.Controls 2.12 0015 import "qrc:/gcompris/src/core/core.js" as Core 0016 0017 Rectangle { 0018 id: creationHandler 0019 0020 width: parent.width 0021 height: parent.height 0022 color: "#ABCDEF" 0023 border.color: "white" 0024 border.width: 2 0025 radius: 20 0026 visible: false 0027 z: 2000 0028 focus: true 0029 0030 onVisibleChanged: { 0031 if(visible) { 0032 creationHandler.forceActiveFocus(); 0033 parent.Keys.enabled = false; 0034 dialogOpened = false; 0035 keyboardCreation.populate(); 0036 } else { 0037 parent.Keys.enabled = true; 0038 parent.forceActiveFocus(); 0039 } 0040 } 0041 0042 signal close 0043 signal fileLoaded(var data, var filePath) 0044 0045 onClose: { 0046 fileNameInput.focus = false 0047 fileNameInput.text = "" 0048 visible = false 0049 viewContainer.selectedFileIndex = -1 0050 creationsList.flick(0, 1400) 0051 } 0052 0053 MouseArea { 0054 anchors.fill: parent 0055 onClicked: viewContainer.selectedFileIndex = -1 0056 } 0057 0058 0059 property var dataToSave 0060 property bool isSaveMode: false 0061 property bool dialogOpened: false 0062 readonly property string activityName: ActivityInfoTree.currentActivity.name.split('/')[0] 0063 readonly property string sharedDirectoryPath: ApplicationSettings.userDataPath + "/" + activityName + "/" 0064 readonly property string fileSavePath: "file://" + sharedDirectoryPath + '/' + fileNameInput.text + ".json" 0065 0066 ListModel { 0067 id: fileNames 0068 } 0069 0070 Directory { 0071 id: directory 0072 } 0073 0074 File { 0075 id: file 0076 onError: console.error("File error: " + msg); 0077 } 0078 0079 JsonParser { 0080 id: parser 0081 onError: console.error("Error parsing JSON: " + msg); 0082 } 0083 0084 Timer { 0085 id: restoreFocusTimer 0086 interval: 500 0087 onTriggered: { 0088 dialogOpened = false; 0089 } 0090 } 0091 0092 Timer { 0093 id: writeDataTimer 0094 interval: 500 0095 onTriggered: { 0096 writeData(); 0097 } 0098 } 0099 0100 function refreshWindow(filterText) { 0101 var pathExists = file.exists(sharedDirectoryPath) 0102 if(!pathExists) 0103 return 0104 0105 fileNames.clear() 0106 0107 var files = directory.getFiles(sharedDirectoryPath) 0108 for(var i = 0; i < files.length; i++) { 0109 if(filterText === undefined || filterText === "" || 0110 (files[i].toLowerCase()).indexOf(filterText) !== -1) 0111 fileNames.append({ "name": files[i] }) 0112 } 0113 } 0114 0115 function loadWindow() { 0116 creationHandler.visible = true 0117 creationHandler.isSaveMode = false 0118 fileNameInput.forceActiveFocus() 0119 refreshWindow() 0120 } 0121 0122 function loadFile(fileName) { 0123 var filePath = "file://" + sharedDirectoryPath + fileNames.get(viewContainer.selectedFileIndex).name 0124 var data = parser.parseFromUrl(filePath) 0125 creationHandler.fileLoaded(data, filePath) 0126 creationHandler.close() 0127 } 0128 0129 function deleteFile() { 0130 dialogOpened = true; 0131 var filePath = "file://" + sharedDirectoryPath + fileNames.get(viewContainer.selectedFileIndex).name 0132 if(file.rmpath(filePath)) { 0133 Core.showMessageDialog(creationHandler, 0134 qsTr("%1 deleted successfully!").arg(filePath), 0135 qsTr("Ok"), null, "", null, function() { restoreFocusTimer.restart(); }); 0136 } 0137 else { 0138 Core.showMessageDialog(creationHandler, 0139 qsTr("Unable to delete %1!").arg(filePath), 0140 qsTr("Ok"), null, "", null, function() { restoreFocusTimer.restart(); }); 0141 } 0142 0143 viewContainer.selectedFileIndex = -1 0144 refreshWindow() 0145 } 0146 0147 function saveWindow(data) { 0148 creationHandler.visible = true 0149 creationHandler.isSaveMode = true 0150 creationHandler.dataToSave = data 0151 fileNameInput.forceActiveFocus() 0152 refreshWindow() 0153 } 0154 0155 function saveFile() { 0156 if(activityName === "" || fileNameInput.text === "") 0157 return 0158 0159 if(!file.exists(sharedDirectoryPath)) 0160 file.mkpath(sharedDirectoryPath) 0161 0162 if(file.exists(fileSavePath)) { 0163 replaceFileDialog(); 0164 } 0165 else 0166 writeData() 0167 } 0168 0169 function replaceFileDialog() { 0170 dialogOpened = true; 0171 Core.showMessageDialog(creationHandler, 0172 qsTr("A file with this name already exists. Do you want to replace it?"), 0173 qsTr("Yes"), function() { writeDataTimer.restart(); }, qsTr("No"), function() { restoreFocusTimer.restart(); }, null); 0174 } 0175 0176 function writeData() { 0177 dialogOpened = true; 0178 file.write(JSON.stringify(creationHandler.dataToSave), fileSavePath); 0179 Core.showMessageDialog(creationHandler, 0180 qsTr("Saved successfully!"), 0181 qsTr("Ok"), null, "", null, function() { restoreFocusTimer.restart(); }); 0182 refreshWindow(); 0183 } 0184 0185 function searchFiles() { 0186 viewContainer.selectedFileIndex = -1 0187 refreshWindow(fileNameInput.text.toLowerCase()) 0188 } 0189 0190 TextField { 0191 id: fileNameInput 0192 width: parent.width / 2 0193 font.pointSize: NaN 0194 font.pixelSize: height * 0.6 0195 height: cancelButton.height * 0.5 0196 anchors.verticalCenter: saveButton.verticalCenter 0197 anchors.left: parent.left 0198 anchors.leftMargin: 20 0199 verticalAlignment: TextInput.AlignVCenter 0200 selectByMouse: true 0201 maximumLength: 15 0202 placeholderText: creationHandler.isSaveMode ? qsTr("Enter file name") : qsTr("Search") 0203 onTextChanged: { 0204 if(!creationHandler.isSaveMode) 0205 searchFiles() 0206 } 0207 color: "black" 0208 background: Rectangle { 0209 border.color: "black" 0210 border.width: 1 0211 radius: fileNameInput.height / 4 0212 } 0213 } 0214 0215 GCButton { 0216 id: saveButton 0217 height: fileNameInput.height 0218 visible: creationHandler.isSaveMode 0219 text: qsTr("Save") 0220 theme: "highContrast" 0221 anchors.verticalCenter: cancelButton.verticalCenter 0222 anchors.left: fileNameInput.right 0223 anchors.right: cancelButton.left 0224 anchors.margins: 20 * ApplicationInfo.ratio 0225 onClicked: saveFile() 0226 } 0227 0228 property real cellWidth: 50 * ApplicationInfo.ratio 0229 property real cellHeight: cellWidth * 1.3 0230 0231 Rectangle { 0232 id: viewContainer 0233 anchors.top: cancelButton.bottom 0234 anchors.bottom: buttonRow.top 0235 anchors.margins: 10 * ApplicationInfo.ratio 0236 border.color: "black" 0237 border.width: 2 0238 radius: 20 0239 anchors.left: parent.left 0240 anchors.right: parent.right 0241 0242 property int selectedFileIndex: -1 0243 0244 MouseArea { 0245 anchors.fill: parent 0246 onClicked: viewContainer.selectedFileIndex = -1 0247 } 0248 0249 GridView { 0250 id: creationsList 0251 model: fileNames 0252 flickDeceleration: 1500 0253 width: parent.width - 10 0254 height: parent.height - 10 0255 interactive: true 0256 cellHeight: creationHandler.cellHeight 0257 cellWidth: creationHandler.cellWidth 0258 anchors.top: parent.top 0259 anchors.topMargin: 10 0260 anchors.left: parent.left 0261 anchors.leftMargin: 5 0262 clip: true 0263 0264 MouseArea { 0265 anchors.fill: parent 0266 enabled: !creationHandler.isSaveMode 0267 onClicked: { 0268 var itemIndex = creationsList.indexAt(mouseX, mouseY+creationsList.contentY) 0269 if(itemIndex === -1) 0270 viewContainer.selectedFileIndex = -1 0271 else 0272 viewContainer.selectedFileIndex = itemIndex 0273 } 0274 } 0275 0276 delegate: Item { 0277 height: creationHandler.cellHeight 0278 width: creationHandler.cellWidth 0279 readonly property string fileName: fileName.text 0280 Rectangle { 0281 anchors.fill: parent 0282 visible: index === viewContainer.selectedFileIndex 0283 color: "#E77936" 0284 opacity: 0.4 0285 radius: 10 0286 } 0287 0288 Image { 0289 id: fileIcon 0290 width: creationHandler.cellWidth 0291 height: parent.height / 1.5 0292 anchors.top: parent.top 0293 anchors.topMargin: 3 0294 source: "qrc:/gcompris/src/core/resource/file_icon.svg" 0295 } 0296 0297 GCText { 0298 id: fileName 0299 anchors.top: fileIcon.bottom 0300 height: parent.height - fileIcon.height - 15 0301 width: creationHandler.cellWidth 0302 font.pointSize: tinySize 0303 fontSizeMode: Text.Fit 0304 wrapMode: Text.WordWrap 0305 horizontalAlignment: Text.AlignHCenter 0306 // Exclude ".json" while displaying file name 0307 text: name.slice(0, name.length - 5) 0308 } 0309 } 0310 } 0311 } 0312 0313 Row { 0314 id: buttonRow 0315 spacing: 20 * ApplicationInfo.ratio 0316 anchors.horizontalCenter: viewContainer.horizontalCenter 0317 anchors.bottom: keyboardCreation.top 0318 anchors.bottomMargin: 10 * ApplicationInfo.ratio 0319 visible: !creationHandler.isSaveMode 0320 GCButton { 0321 id: loadButton 0322 width: viewContainer.width * 0.5 - 20 * ApplicationInfo.ratio 0323 height: saveButton.height 0324 text: qsTr("Load") 0325 enabled: viewContainer.selectedFileIndex != -1 0326 theme: "highContrast" 0327 onClicked: creationHandler.loadFile() 0328 } 0329 0330 GCButton { 0331 id: deleteButton 0332 width: loadButton.width 0333 height: saveButton.height 0334 text: qsTr("Delete") 0335 enabled: viewContainer.selectedFileIndex != -1 0336 theme: "highContrast" 0337 onClicked: deleteFile() 0338 } 0339 } 0340 0341 GCButtonCancel { 0342 id: cancelButton 0343 onClose: { 0344 parent.close() 0345 } 0346 } 0347 0348 // The scroll buttons 0349 GCButtonScroll { 0350 anchors.right: viewContainer.right 0351 anchors.rightMargin: 5 * ApplicationInfo.ratio 0352 anchors.bottom: viewContainer.bottom 0353 anchors.bottomMargin: 5 * ApplicationInfo.ratio 0354 onUp: creationsList.flick(0, 1000) 0355 onDown: creationsList.flick(0, -1000) 0356 upVisible: creationsList.atYBeginning ? false : true 0357 downVisible: creationsList.atYEnd ? false : true 0358 } 0359 0360 VirtualKeyboard { 0361 id: keyboardCreation 0362 anchors.bottom: parent.bottom 0363 anchors.horizontalCenter: parent.horizontalCenter 0364 width: parent.width 0365 visible: ApplicationSettings.isVirtualKeyboard && !ApplicationInfo.isMobile && !dialogOpened 0366 onKeypress: { 0367 var textArray = fileNameInput.text.split(""); 0368 var cursorPosition = fileNameInput.cursorPosition 0369 if(text == backspace) { 0370 --cursorPosition; 0371 textArray.splice(cursorPosition , 1); 0372 } 0373 else { 0374 textArray.splice(cursorPosition, 0, text); 0375 ++cursorPosition; 0376 } 0377 fileNameInput.text = textArray.join(""); 0378 fileNameInput.cursorPosition = cursorPosition; 0379 } 0380 shiftKey: true 0381 onError: console.log("VirtualKeyboard error: " + msg); 0382 readonly property string newline: "\u21B2" 0383 0384 function populate() { 0385 layout = [ 0386 [ 0387 { label: "0" }, 0388 { label: "1" }, 0389 { label: "2" }, 0390 { label: "3" }, 0391 { label: "4" }, 0392 { label: "5" }, 0393 { label: "6" }, 0394 { label: "7" }, 0395 { label: "8" }, 0396 { label: "9" } 0397 ], 0398 [ 0399 { label: "A" }, 0400 { label: "B" }, 0401 { label: "C" }, 0402 { label: "D" }, 0403 { label: "E" }, 0404 { label: "F" }, 0405 { label: "G" }, 0406 { label: "H" }, 0407 { label: "I" } 0408 ], 0409 [ 0410 { label: "J" }, 0411 { label: "K" }, 0412 { label: "L" }, 0413 { label: "M" }, 0414 { label: "N" }, 0415 { label: "O" }, 0416 { label: "P" }, 0417 { label: "Q" }, 0418 { label: "R" } 0419 ], 0420 [ 0421 { label: "S" }, 0422 { label: "T" }, 0423 { label: "U" }, 0424 { label: "V" }, 0425 { label: "W" }, 0426 { label: "X" }, 0427 { label: "Y" }, 0428 { label: "Z" }, 0429 { label: " " }, 0430 { label: backspace } 0431 ] 0432 ] 0433 } 0434 } 0435 0436 Keys.onEscapePressed: { 0437 cancelButton.close(); 0438 } 0439 0440 Keys.onTabPressed: { 0441 return; 0442 } 0443 0444 Keys.onPressed: { 0445 if(event.key === Qt.Key_Left) { 0446 if(viewContainer.selectedFileIndex > 0) { 0447 viewContainer.selectedFileIndex -= 1; 0448 } else { 0449 viewContainer.selectedFileIndex = creationsList.count - 1; 0450 } 0451 } 0452 if(event.key === Qt.Key_Right) { 0453 if(viewContainer.selectedFileIndex < creationsList.count - 1) { 0454 viewContainer.selectedFileIndex += 1; 0455 } else { 0456 viewContainer.selectedFileIndex = 0; 0457 } 0458 } 0459 if(event.key === Qt.Key_Up || event.key === Qt.Key_Down) { 0460 if(!dialogOpened) 0461 fileNameInput.forceActiveFocus(); 0462 } 0463 } 0464 0465 Keys.onReleased: { 0466 if(event.key === Qt.Key_Enter || event.key === Qt.Key_Return) { 0467 if(saveButton.visible && !dialogOpened) { 0468 saveButton.clicked(); 0469 } else if(buttonRow.visible && viewContainer.selectedFileIndex != -1){ 0470 loadButton.clicked(); 0471 } 0472 } 0473 else if(event.key === Qt.Key_Delete) { 0474 if(buttonRow.visible && viewContainer.selectedFileIndex != -1){ 0475 deleteButton.clicked(); 0476 } 0477 } 0478 else if(event.key === Qt.Key_Back) { 0479 cancelButton.close(); 0480 event.accepted = true; 0481 } 0482 } 0483 }