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 }