Warning, /education/gcompris/src/activities/baby_wordprocessor/BabyWordprocessor.qml is written in an unsupported language. File is not indexed.
0001 /* GCompris - baby_wordprocessor.qml 0002 * 0003 * SPDX-FileCopyrightText: 2015 Bruno Coudoin <bruno.coudoin@gcompris.net> 0004 * 0005 * Authors: 0006 * Bruno Coudoin <bruno.coudoin@gcompris.net> (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 "baby_wordprocessor.js" as Activity 0016 0017 0018 ActivityBase { 0019 id: activity 0020 0021 onStart: focus = true 0022 onStop: {} 0023 0024 // When going on configuration, it steals the focus and re set it to the activity. 0025 // We need to set it back to the textinput item in order to have key events. 0026 onFocusChanged: { 0027 if(focus) { 0028 Activity.focusTextInput(); 0029 } 0030 } 0031 0032 pageComponent: Rectangle { 0033 id: background 0034 anchors.fill: parent 0035 color: 'white' 0036 signal start 0037 signal stop 0038 0039 Component.onCompleted: { 0040 dialogActivityConfig.initialize() 0041 activity.start.connect(start) 0042 } 0043 onStart: { 0044 keyboard.populate(); 0045 Activity.start(items); 0046 } 0047 0048 onStop: { 0049 Activity.stop(); 0050 } 0051 0052 property bool horizontalMode: height <= width 0053 property bool isLoadingCreation: false 0054 QtObject { 0055 id: items 0056 property alias edit: edit 0057 property GCAudio audioVoices: activity.audioVoices 0058 property GCSfx audioEffects: activity.audioEffects 0059 property alias fileId: fileId 0060 property bool audioMode: false 0061 property string locale: ApplicationSettings.locale 0062 } 0063 0064 GCCreationHandler { 0065 id: creationHandler 0066 onFileLoaded: { 0067 isLoadingCreation = true 0068 edit.clear() 0069 edit.text = data["text"] 0070 edit.cursorPosition = edit.length 0071 isLoadingCreation = false 0072 } 0073 onClose: { 0074 Activity.focusTextInput(); 0075 keyboard.hide = false; 0076 } 0077 } 0078 0079 Column { 0080 id: controls 0081 width: 120 * ApplicationInfo.ratio 0082 anchors { 0083 right: parent.right 0084 top: parent.top 0085 margins: 10 0086 } 0087 spacing: 10 0088 0089 GCButton { 0090 textSize: "title" 0091 text: qsTr("Title") 0092 width: parent.width 0093 onClicked: edit.formatLineWith('h2') 0094 } 0095 GCButton { 0096 textSize: "subtitle" 0097 text: qsTr("Subtitle") 0098 width: parent.width 0099 onClicked: edit.formatLineWith('h3') 0100 } 0101 GCButton { 0102 textSize: "regular" 0103 width: parent.width 0104 text: qsTr("Paragraph") 0105 onClicked: edit.formatLineWith('p') 0106 } 0107 } 0108 0109 Column { 0110 id: saveAndLoadButtons 0111 width: controls.width 0112 0113 property bool isEnoughSpace: { 0114 if(ApplicationInfo.isMobile && parent.horizontalMode) 0115 return false 0116 return (parent.height - keyboard.height - controls.height) > 2.5 * loadButton.height 0117 } 0118 0119 anchors { 0120 right: !isEnoughSpace ? controls.left : parent.right 0121 top: !isEnoughSpace ? parent.top : controls.bottom 0122 margins: 10 0123 } 0124 spacing: 10 0125 0126 GCButton { 0127 id: loadButton 0128 textSize: "regular" 0129 width: parent.width 0130 text: qsTr("Load") 0131 onClicked: { 0132 keyboard.hide = true; 0133 creationHandler.loadWindow(); 0134 } 0135 } 0136 GCButton { 0137 id: saveButton 0138 textSize: "regular" 0139 width: parent.width 0140 text: qsTr("Save") 0141 onClicked: { 0142 keyboard.hide = true; 0143 var textToSave = {}; 0144 // Remove focus to force text storing within the TextEdit 0145 edit.focus = false; 0146 textToSave["text"] = edit.getFormattedText(0, edit.length); 0147 creationHandler.saveWindow(textToSave); 0148 } 0149 } 0150 } 0151 0152 Flickable { 0153 id: flick 0154 0155 anchors { 0156 left: parent.left 0157 right: saveAndLoadButtons.left 0158 top: parent.top 0159 bottom: bar.top 0160 margins: 10 0161 } 0162 contentWidth: edit.paintedWidth 0163 contentHeight: edit.paintedHeight 0164 clip: true 0165 flickableDirection: Flickable.VerticalFlick 0166 0167 function ensureVisible(r) 0168 { 0169 if (contentX >= r.x) 0170 contentX = r.x; 0171 else if (contentX+width <= r.x+r.width) 0172 contentX = r.x+r.width-width; 0173 if (contentY >= r.y) 0174 contentY = r.y; 0175 else if (contentY+height <= r.y+r.height) 0176 contentY = r.y+r.height-height; 0177 } 0178 0179 TextEdit { 0180 id: edit 0181 width: flick.width 0182 height: flick.height 0183 focus: true 0184 wrapMode: TextEdit.Wrap 0185 onCursorRectangleChanged: flick.ensureVisible(cursorRectangle) 0186 textFormat: TextEdit.RichText 0187 color: "#373737" 0188 font { 0189 pointSize: (18 + ApplicationSettings.baseFontSize) * ApplicationInfo.fontRatio 0190 capitalization: ApplicationSettings.fontCapitalization 0191 weight: Font.DemiBold 0192 family: GCSingletonFontLoader.fontLoader.name 0193 letterSpacing: ApplicationSettings.fontLetterSpacing 0194 wordSpacing: 10 0195 } 0196 cursorDelegate: Rectangle { 0197 id: cursor 0198 width: 10 0199 // height should be set automatically as mention in cursorRectangle property 0200 // documentation but it does not work 0201 height: parent.cursorRectangle.height 0202 color: '#DF543D' 0203 SequentialAnimation on opacity { 0204 running: true 0205 loops: Animation.Infinite 0206 PropertyAnimation { 0207 to: 0.2 0208 duration: 1000 0209 } 0210 PropertyAnimation { 0211 to: 1 0212 duration: 1000 0213 } 0214 } 0215 } 0216 0217 property string previousText: text; 0218 function getDiffBetweenTexts(previousText, newText) { 0219 var diff = ""; 0220 newText.split('').forEach(function(val, i) { 0221 if(val != previousText.charAt(i) && diff === "") { 0222 diff = val; 0223 } 0224 }); 0225 return diff; 0226 } 0227 0228 onTextChanged: { 0229 var newText = getText(0, text.length) 0230 // if more characters are present, we added a new one and 0231 // and wants to play a voice if available 0232 if(previousText.length < newText.length && !isLoadingCreation) { 0233 var letterTyped = getDiffBetweenTexts(previousText, newText) 0234 if(letterTyped !== "") { 0235 Activity.playLetter(letterTyped) 0236 } 0237 } 0238 previousText = newText; 0239 } 0240 function insertText(text) { 0241 edit.insert(cursorPosition, text) 0242 } 0243 function backspace() { 0244 if(cursorPosition > 0) { 0245 moveCursorSelection(cursorPosition - 1, TextEdit.SelectCharacters) 0246 cut() 0247 } 0248 } 0249 function newline() { 0250 insert(cursorPosition, "<br></br>") 0251 } 0252 function formatLineWith(tag) { 0253 var text = getText(0, length) 0254 var initialPosition = cursorPosition 0255 var first = cursorPosition - 1 0256 for(; first >= 0; first--) { 0257 if(text.charCodeAt(first) === 8233) 0258 break 0259 } 0260 first++ 0261 var last = cursorPosition 0262 for(; last < text.length; last++) { 0263 if(text.charCodeAt(last) === 8233) 0264 break 0265 } 0266 var line = getText(first, last) 0267 remove(first, last) 0268 insert(first, '<' + tag + '>' + line + '</' + tag + '>') 0269 cursorPosition = initialPosition 0270 } 0271 } 0272 } 0273 0274 File { 0275 id: fileId 0276 } 0277 0278 DialogHelp { 0279 id: dialogHelp 0280 onClose: home() 0281 } 0282 0283 DialogChooseLevel { 0284 id: dialogActivityConfig 0285 currentActivity: activity.activityInfo 0286 0287 onClose: { 0288 home() 0289 } 0290 onLoadData: { 0291 if(activityData && activityData["audioMode"]) { 0292 items.audioMode = activityData["audioMode"] === "true" ? true : false; 0293 } 0294 } 0295 } 0296 0297 Bar { 0298 id: bar 0299 anchors.bottom: keyboard.top 0300 content: BarEnumContent { value: help | home | reload | activityConfig } 0301 onHelpClicked: { 0302 displayDialog(dialogHelp) 0303 } 0304 onHomeClicked: activity.home() 0305 onReloadClicked: edit.text = '' 0306 onActivityConfigClicked: { 0307 displayDialog(dialogActivityConfig); 0308 } 0309 0310 } 0311 0312 VirtualKeyboard { 0313 id: keyboard 0314 anchors.bottom: parent.bottom 0315 anchors.horizontalCenter: parent.horizontalCenter 0316 width: parent.width 0317 visible: ApplicationSettings.isVirtualKeyboard && !ApplicationInfo.isMobile && !hide 0318 onKeypress: { 0319 if(text == backspace) { 0320 edit.backspace(); 0321 } 0322 else if(text == newline) { 0323 edit.newline(); 0324 } 0325 else { 0326 edit.insertText(text); 0327 } 0328 } 0329 shiftKey: true 0330 onError: console.log("VirtualKeyboard error: " + msg); 0331 readonly property string newline: "\u21B2" 0332 0333 function populate() { 0334 layout = [ 0335 [ 0336 { label: "0" }, 0337 { label: "1" }, 0338 { label: "2" }, 0339 { label: "3" }, 0340 { label: "4" }, 0341 { label: "5" }, 0342 { label: "6" }, 0343 { label: "7" }, 0344 { label: "8" }, 0345 { label: "9" } 0346 ], 0347 [ 0348 { label: "A" }, 0349 { label: "B" }, 0350 { label: "C" }, 0351 { label: "D" }, 0352 { label: "E" }, 0353 { label: "F" }, 0354 { label: "G" }, 0355 { label: "H" }, 0356 { label: "I" } 0357 ], 0358 [ 0359 { label: "J" }, 0360 { label: "K" }, 0361 { label: "L" }, 0362 { label: "M" }, 0363 { label: "N" }, 0364 { label: "O" }, 0365 { label: "P" }, 0366 { label: "Q" }, 0367 { label: "R" } 0368 ], 0369 [ 0370 { label: "S" }, 0371 { label: "T" }, 0372 { label: "U" }, 0373 { label: "V" }, 0374 { label: "W" }, 0375 { label: "X" }, 0376 { label: "Y" }, 0377 { label: "Z" }, 0378 { label: " " }, 0379 { label: backspace }, 0380 { label: newline } 0381 ] 0382 ] 0383 } 0384 0385 } 0386 0387 } 0388 0389 }