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 }