Warning, /maui/mauikit-texteditor/src/controls.5/TextEditor.qml is written in an unsupported language. File is not indexed.

0001 import QtQuick 2.15
0002 import QtQml 2.14
0003 import QtQuick.Controls 2.15
0004 import QtQuick.Layouts 1.3
0005 import QtQuick.Templates 2.15 as T
0006 
0007 import org.mauikit.controls 1.3 as Maui
0008 import org.mauikit.texteditor 1.0 as TE
0009 
0010 import org.kde.sonnet 1.0 as Sonnet
0011 
0012 /*!
0013  * \ since org.mauikit.texteditor 1.0
0014  * \inqmlmodule org.mauikit.texteditor
0015  *  \brief Integrated text editor component
0016  *
0017  *  A text area for editing text with convinient functions.
0018  *  The Editor is controlled by the DocumentHandler which controls the files I/O,
0019  *  the syntax highlighting styles, and many more text editing properties.
0020  */
0021 Page
0022 {
0023     id: control
0024 
0025     padding: 0
0026     focus: false
0027     clip: false
0028     title: document.fileName + (document.modified ? "*" : "")
0029     //     showTitle: false
0030 
0031     property bool showFindBar: false
0032 
0033     onShowFindBarChanged:
0034     {
0035         if(showFindBar)
0036         {
0037             _findField.forceActiveFocus()
0038         }else
0039         {
0040             body.forceActiveFocus()
0041         }
0042     }
0043 
0044     /*!
0045      *      \qmlproperty TextArea Editor::body
0046      * Access to the editor text area.
0047      */
0048     readonly property alias body : body
0049 
0050     /*!
0051      *      \qmlproperty DocumentHandler Editor::document
0052      */
0053     readonly property alias document : document
0054 
0055     /*!
0056      *      \qmlproperty ScrollablePage Editor::scrollView
0057      */
0058     readonly property alias scrollView: _scrollView
0059 
0060     readonly property alias documentMenu : _documentMenuLoader.item
0061 
0062     /*!
0063      *      \qmlproperty string Editor::text
0064      */
0065     property alias text: body.text
0066 
0067     /*!
0068      *      \qmlproperty bool Editor::uppercase
0069      */
0070     property alias uppercase: document.uppercase
0071 
0072     /*!
0073      *      \qmlproperty bool Editor::underline
0074      */
0075     property alias underline: document.underline
0076 
0077     /*!
0078      *      \qmlproperty bool Editor::italic
0079      */
0080     property alias italic: document.italic
0081 
0082     /*!
0083      *      \qmlproperty bool Editor::bold
0084      */
0085     property alias bold: document.bold
0086 
0087     /*!
0088      *      \qmlproperty bool Editor::canRedo
0089      */
0090     property alias canRedo: body.canRedo
0091 
0092     /*!
0093      *      \qmlproperty url Editor::fileUrl
0094      *
0095      *      If a file url is provided the DocumentHandler will try to open its contents and display it.
0096      */
0097     property alias fileUrl : document.fileUrl
0098 
0099     /*!
0100      *      \qmlproperty bool Editor::showLineNumbers
0101      *
0102      *      If a sidebar listing each line number should be visible.
0103      */
0104     property bool showLineNumbers : false
0105 
0106     property bool spellcheckEnabled: false
0107 
0108     TE.DocumentHandler
0109     {
0110         id: document
0111         document: body.textDocument
0112         cursorPosition: body.cursorPosition
0113         selectionStart: body.selectionStart
0114         selectionEnd: body.selectionEnd
0115         backgroundColor: control.Maui.Theme.backgroundColor
0116         enableSyntaxHighlighting: false
0117         findCaseSensitively:  _findCaseSensitively.checked
0118         findWholeWords: _findWholeWords.checked
0119 
0120         onSearchFound:
0121         {
0122             body.select(start, end)
0123         }
0124     }
0125 
0126     Loader
0127     {
0128         id: spellcheckhighlighterLoader
0129         property bool activable: control.spellcheckEnabled
0130         property Sonnet.Settings settings: Sonnet.Settings {}
0131         active: activable && settings.checkerEnabledByDefault
0132         onActiveChanged: if (active) {
0133                              item.active = true;
0134                          }
0135         sourceComponent: Sonnet.SpellcheckHighlighter {
0136             id: spellcheckhighlighter
0137             document:  body.textDocument
0138             cursorPosition: body.cursorPosition
0139             selectionStart: body.selectionStart
0140             selectionEnd: body.selectionEnd
0141             misspelledColor: Maui.Theme.negativeTextColor
0142             active: spellcheckhighlighterLoader.activable && settings.checkerEnabledByDefault
0143 
0144             onChangeCursorPosition: {
0145                 body.cursorPosition = start;
0146                 body.moveCursorSelection(end, TextEdit.SelectCharacters);
0147             }
0148         }
0149     }
0150 
0151     Loader
0152     {
0153         id: _documentMenuLoader
0154 
0155         asynchronous: true
0156         sourceComponent: Maui.ContextualMenu
0157         {
0158             property var spellcheckhighlighter: null
0159             property var spellcheckhighlighterLoader: null
0160             property int restoredCursorPosition: 0
0161             property int restoredSelectionStart
0162             property int restoredSelectionEnd
0163             property var suggestions: []
0164             property bool deselectWhenMenuClosed: true
0165             property var runOnMenuClose: () => {}
0166             property bool persistentSelectionSetting
0167             Component.onCompleted: persistentSelectionSetting = body.persistentSelection
0168 
0169             Maui.MenuItemActionRow
0170             {
0171                 Action
0172                 {
0173                     icon.name: "edit-undo-symbolic"
0174                     text: qsTr("Undo")
0175                     shortcut: StandardKey.Undo
0176 
0177                     onTriggered:
0178                     {
0179                         documentMenu.deselectWhenMenuClosed = false;
0180                         documentMenu.runOnMenuClose = () => body.undo();
0181                     }
0182                 }
0183 
0184                 Action
0185                 {
0186                     icon.name: "edit-redo-symbolic"
0187                     text: qsTr("Redo")
0188                     shortcut: StandardKey.Redo
0189 
0190                     onTriggered:
0191                     {
0192                         documentMenu.deselectWhenMenuClosed = false;
0193                         documentMenu.runOnMenuClose = () => body.redo();
0194                     }
0195                 }
0196             }
0197 
0198             MenuItem
0199             {
0200                 action: Action {
0201                     icon.name: "edit-copy-symbolic"
0202                     text: qsTr("Copy")
0203                     shortcut: StandardKey.Copy
0204                 }
0205                 onTriggered:
0206                 {
0207                     documentMenu.deselectWhenMenuClosed = false;
0208                     documentMenu.runOnMenuClose = () => control.body.copy();
0209                 }
0210                 enabled: body.selectedText.length
0211             }
0212 
0213             MenuItem
0214             {
0215                 action: Action {
0216                     icon.name: "edit-cut-symbolic"
0217                     text: qsTr("Cut")
0218                     shortcut: StandardKey.Cut
0219                 }
0220                 onTriggered:
0221                 {
0222                     documentMenu.deselectWhenMenuClosed = false;
0223                     documentMenu.runOnMenuClose = () => control.body.cut();
0224                 }
0225                 enabled: !body.readOnly && body.selectedText.length
0226             }
0227 
0228             MenuItem
0229             {
0230                 action: Action {
0231                     icon.name: "edit-paste-symbolic"
0232                     text: qsTr("Paste")
0233                     shortcut: StandardKey.Paste
0234                 }
0235                 onTriggered:
0236                 {
0237                     documentMenu.deselectWhenMenuClosed = false;
0238                     documentMenu.runOnMenuClose = () => control.body.paste();
0239                 }
0240                 enabled: !body.readOnly
0241             }
0242 
0243 
0244             MenuItem
0245             {
0246                 action: Action {
0247                     icon.name: "edit-select-all-symbolic"
0248                     text: qsTr("Select All")
0249                     shortcut: StandardKey.SelectAll
0250                 }
0251                 onTriggered:
0252                 {
0253                     documentMenu.deselectWhenMenuClosed = false
0254                     documentMenu.runOnMenuClose = () => control.body.selectAll();
0255                 }
0256             }
0257 
0258             MenuItem
0259             {
0260                 text: i18nd("mauikittexteditor","Search Selected Text on Google...")
0261                 onTriggered: Qt.openUrlExternally("https://www.google.com/search?q="+body.selectedText)
0262                 enabled: body.selectedText.length
0263             }
0264 
0265             MenuItem
0266             {
0267                 enabled: !control.body.readOnly && control.body.selectedText
0268                 action: Action {
0269                     icon.name: "edit-delete-symbolic"
0270                     text: qsTr("Delete")
0271                     shortcut: StandardKey.Delete
0272                 }
0273                 onTriggered: {
0274                     documentMenu.deselectWhenMenuClosed = false;
0275                     documentMenu.runOnMenuClose = () => control.body.remove(control.body.selectionStart, control.body.selectionEnd);
0276                 }
0277             }
0278 
0279 
0280             MenuSeparator
0281             {
0282             }
0283 
0284             Menu
0285             {
0286                 id: _spellingMenu
0287                 title: i18nd("mauikittexteditor","Spelling")
0288                 enabled: control.spellcheckEnabled
0289 
0290                 Instantiator
0291                 {
0292                     id: _suggestions
0293                     active: !control.body.readOnly && documentMenu.spellcheckhighlighter !== null && documentMenu.spellcheckhighlighter.active && documentMenu.spellcheckhighlighter.wordIsMisspelled
0294                     model: documentMenu.suggestions
0295                     delegate: MenuItem
0296                     {
0297                         text: modelData
0298                         onClicked:
0299                         {
0300                             documentMenu.deselectWhenMenuClosed = false;
0301                             documentMenu.runOnMenuClose = () => documentMenu.spellcheckhighlighter.replaceWord(modelData);
0302                         }
0303                     }
0304                     onObjectAdded:
0305                     {
0306                         _spellingMenu.insertItem(0, object)
0307                     }
0308                     onObjectRemoved: _spellingMenu.removeItem(0)
0309                 }
0310 
0311                 MenuSeparator
0312                 {
0313                     enabled: !control.body.readOnly && ((documentMenu.spellcheckhighlighter !== null && documentMenu.spellcheckhighlighter.active && documentMenu.spellcheckhighlighter.wordIsMisspelled) || (documentMenu.spellcheckhighlighterLoader && documentMenu.spellcheckhighlighterLoader.activable))
0314                 }
0315 
0316                 MenuItem {
0317                     enabled: !control.body.readOnly && documentMenu.spellcheckhighlighter !== null && documentMenu.spellcheckhighlighter.active && documentMenu.spellcheckhighlighter.wordIsMisspelled && documentMenu.suggestions.length === 0
0318                     action: Action {
0319                         text: documentMenu.spellcheckhighlighter ? qsTr("No suggestions for \"%1\"").arg(documentMenu.spellcheckhighlighter.wordUnderMouse) : ''
0320                         enabled: false
0321                     }
0322                 }
0323                 MenuItem {
0324                     enabled: !control.body.readOnly && documentMenu.spellcheckhighlighter !== null && documentMenu.spellcheckhighlighter.active && documentMenu.spellcheckhighlighter.wordIsMisspelled
0325                     action: Action {
0326                         text: documentMenu.spellcheckhighlighter ? qsTr("Add \"%1\" to dictionary").arg(documentMenu.spellcheckhighlighter.wordUnderMouse) : ''
0327                         onTriggered: {
0328                             documentMenu.deselectWhenMenuClosed = false;
0329                             documentMenu.runOnMenuClose = () => spellcheckhighlighter.addWordToDictionary(documentMenu.spellcheckhighlighter.wordUnderMouse);
0330                         }
0331                     }
0332                 }
0333 
0334                 MenuItem {
0335                     enabled: !control.body.readOnly && documentMenu.spellcheckhighlighter !== null && documentMenu.spellcheckhighlighter.active && documentMenu.spellcheckhighlighter.wordIsMisspelled
0336                     action: Action {
0337                         text: qsTr("Ignore")
0338                         onTriggered: {
0339                             documentMenu.deselectWhenMenuClosed = false;
0340                             documentMenu.runOnMenuClose = () => documentMenu.spellcheckhighlighter.ignoreWord(documentMenu.spellcheckhighlighter.wordUnderMouse);
0341                         }
0342                     }
0343                 }
0344 
0345                 MenuItem
0346                 {
0347                     enabled: !control.body.readOnly && documentMenu.spellcheckhighlighterLoader && documentMenu.spellcheckhighlighterLoader.activable
0348                     checkable: true
0349                     checked: documentMenu.spellcheckhighlighter ? documentMenu.spellcheckhighlighter.active : false
0350                     text: qsTr("Enable Spellchecker")
0351                     onCheckedChanged: {
0352                         spellcheckhighlighterLoader.active = checked;
0353                         documentMenu.spellcheckhighlighter = documentMenu.spellcheckhighlighterLoader.item;
0354                     }
0355                 }
0356             }
0357 
0358             function targetClick(spellcheckhighlighter, mousePosition)
0359             {
0360                 control.body.persistentSelection = true; // persist selection when menu is opened
0361                 documentMenu.spellcheckhighlighterLoader = spellcheckhighlighter;
0362                 if (spellcheckhighlighter && spellcheckhighlighter.active) {
0363                     documentMenu.spellcheckhighlighter = spellcheckhighlighter.item;
0364                     documentMenu.suggestions = mousePosition ? spellcheckhighlighter.item.suggestions(mousePosition) : [];
0365                 } else {
0366                     documentMenu.spellcheckhighlighter = null;
0367                     documentMenu.suggestions = [];
0368                 }
0369 
0370                 storeCursorAndSelection();
0371                 documentMenu.show()
0372             }
0373 
0374             function storeCursorAndSelection()
0375             {
0376                 documentMenu.restoredCursorPosition = control.body.cursorPosition;
0377                 documentMenu.restoredSelectionStart = control.body.selectionStart;
0378                 documentMenu.restoredSelectionEnd = control.body.selectionEnd;
0379             }
0380 
0381             onOpened:
0382             {
0383                 runOnMenuClose = () => {};
0384             }
0385 
0386             onClosed:
0387             {
0388                 // restore text field's original persistent selection setting
0389                 body.persistentSelection = documentMenu.persistentSelectionSetting
0390                 // deselect text field text if menu is closed not because of a right click on the text field
0391                 if (documentMenu.deselectWhenMenuClosed)
0392                 {
0393                     body.deselect();
0394                 }
0395                 documentMenu.deselectWhenMenuClosed = true;
0396 
0397                 // restore cursor position
0398                 body.forceActiveFocus();
0399                 body.cursorPosition = documentMenu.restoredCursorPosition;
0400                 body.select(documentMenu.restoredSelectionStart, documentMenu.restoredSelectionEnd);
0401 
0402                 // run action, and free memory
0403                 runOnMenuClose();
0404                 runOnMenuClose = () => {};
0405             }
0406         }
0407     }
0408 
0409     footer: Column
0410     {
0411         width: parent.width
0412 
0413         Maui.ToolBar
0414         {
0415             id: _findToolBar
0416             visible: showFindBar
0417             width: parent.width
0418             position: ToolBar.Footer
0419 
0420             rightContent: ToolButton
0421             {
0422                 id: _replaceButton
0423                 icon.name: "edit-find-replace"
0424                 checkable: true
0425                 checked: false
0426             }
0427 
0428             leftContent: Maui.ToolButtonMenu
0429             {
0430                 icon.name: "overflow-menu"
0431 
0432                 MenuItem
0433                 {
0434                     id: _findCaseSensitively
0435                     checkable: true
0436                     text: i18nd("mauikittexteditor","Case Sensitive")
0437                 }
0438 
0439                 MenuItem
0440                 {
0441                     id: _findWholeWords
0442                     checkable: true
0443                     text: i18nd("mauikittexteditor","Whole Words Only")
0444                 }
0445             }
0446 
0447             middleContent: Maui.SearchField
0448             {
0449                 id: _findField
0450                 Layout.fillWidth: true
0451                 Layout.maximumWidth: 500
0452                 Layout.alignment: Qt.AlignCenter
0453                 placeholderText: i18nd("mauikittexteditor","Find")
0454 
0455                 onAccepted:
0456                 {
0457                     document.find(text)
0458                 }
0459 
0460                 actions:[
0461 
0462                 Action
0463                 {
0464                     enabled: _findField.text.length
0465                     icon.name: "arrow-up"
0466                     onTriggered: document.find(_findField.text, false)
0467                 }
0468                 ]
0469             }
0470         }
0471 
0472         Maui.ToolBar
0473         {
0474             id: _replaceToolBar
0475             position: ToolBar.Footer
0476             visible: _replaceButton.checked && _findToolBar.visible
0477             width: parent.width
0478             enabled: !body.readOnly
0479             forceCenterMiddleContent: false
0480 
0481             middleContent: Maui.SearchField
0482             {
0483                 id: _replaceField
0484                 placeholderText: i18nd("mauikittexteditor","Replace")
0485                 Layout.fillWidth: true
0486                 Layout.maximumWidth: 500
0487                 Layout.alignment: Qt.AlignCenter
0488                 icon.source: "edit-find-replace"
0489                 actions: Action
0490                 {
0491                     text: i18nd("mauikittexteditor","Replace")
0492                     enabled: _replaceField.text.length
0493                     icon.name: "checkmark"
0494                     onTriggered: document.replace(_findField.text, _replaceField.text)
0495                 }
0496             }
0497 
0498             rightContent: Button
0499             {
0500                 enabled: _replaceField.text.length
0501                 text: i18nd("mauikittexteditor","Replace All")
0502                 onClicked: document.replaceAll(_findField.text, _replaceField.text)
0503             }
0504         }
0505     }
0506 
0507     header: Column
0508     {
0509         width: parent.width
0510 
0511         Repeater
0512         {
0513             model: document.alerts
0514 
0515             Maui.ToolBar
0516             {
0517                 id: _alertBar
0518                 property var alert : model.alert
0519                 readonly property int index_ : index
0520                 width: parent.width
0521 
0522                 Maui.Theme.backgroundColor:
0523                 {
0524                     switch(alert.level)
0525                     {
0526                     case 0: return Maui.Theme.positiveBackgroundColor
0527                     case 1: return Maui.Theme.neutralBackgroundColor
0528                     case 2: return Maui.Theme.negativeBackgroundColor
0529                     }
0530                 }
0531 
0532                 Maui.Theme.textColor:
0533                 {
0534                     switch(alert.level)
0535                     {
0536                     case 0: return Maui.Theme.positiveTextColor
0537                     case 1: return Maui.Theme.neutralTextColor
0538                     case 2: return Maui.Theme.negativeTextColor
0539                     }
0540                 }
0541 
0542                 forceCenterMiddleContent: false
0543                 middleContent: Maui.ListItemTemplate
0544                 {
0545                     Maui.Theme.inherit: true
0546                     Layout.fillWidth: true
0547                     Layout.fillHeight: true
0548                     label1.text: alert.title
0549                     label2.text: alert.body
0550                 }
0551 
0552                 rightContent: Repeater
0553                 {
0554                     model: alert.actionLabels
0555 
0556                     Button
0557                     {
0558                         id: _alertAction
0559                         property int index_ : index
0560                         text: modelData
0561                         onClicked: alert.triggerAction(_alertAction.index_, _alertBar.index_)
0562 
0563                         Maui.Theme.backgroundColor: Qt.lighter(_alertBar.Maui.Theme.backgroundColor, 1.2)
0564                         Maui.Theme.hoverColor: Qt.lighter(_alertBar.Maui.Theme.backgroundColor, 1)
0565                         Maui.Theme.textColor: Qt.darker(Maui.Theme.backgroundColor)
0566                     }
0567                 }
0568             }
0569         }
0570     }
0571 
0572     contentItem: Item
0573     {
0574         clip: false
0575 
0576         ScrollView
0577         {
0578             id: _scrollView
0579             anchors.fill: parent
0580             clip: false
0581             contentWidth: availableWidth
0582 
0583             Keys.enabled: true
0584             Keys.forwardTo: body
0585             Keys.onPressed:
0586             {
0587                 if((event.key === Qt.Key_F) && (event.modifiers & Qt.ControlModifier))
0588                 {
0589                     control.showFindBar = true
0590 
0591                     if(control.body.selectedText.length)
0592                     {
0593                         _findField.text =  control.body.selectedText
0594                     }else
0595                     {
0596                         _findField.selectAll()
0597                     }
0598 
0599                     _findField.forceActiveFocus()
0600                     event.accepted = true
0601                 }
0602 
0603                 if((event.key === Qt.Key_R) && (event.modifiers & Qt.ControlModifier))
0604                 {
0605                     control.showFindBar = true
0606                     _replaceButton.checked = true
0607                     _findField.text = control.body.selectedText
0608                     _replaceField.forceActiveFocus()
0609                     event.accepted = true
0610                 }
0611             }
0612 
0613             Flickable
0614             {
0615                 id: _flickable
0616                 clip: false
0617                 interactive: true
0618                 boundsBehavior : Flickable.StopAtBounds
0619                 boundsMovement : Flickable.StopAtBounds
0620 
0621                 TextArea.flickable: TextArea
0622                 {
0623                     id: body
0624                     Maui.Theme.inherit: true
0625                     text: document.text
0626 
0627                     placeholderText: i18nd("mauikittexteditor","Body")
0628 
0629                     textFormat: TextEdit.PlainText
0630 
0631                     leftPadding: _linesCounter.width + padding
0632 
0633                     Keys.onPressed:
0634                     {
0635                         if(event.key === Qt.Key_PageUp)
0636                         {
0637                             _flickable.flick(0,  60*Math.sqrt(_flickable.height))
0638                             event.accepted = true
0639                         }
0640 
0641                         if(event.key === Qt.Key_PageDown)
0642                         {
0643                             _flickable.flick(0, -60*Math.sqrt(_flickable.height))
0644                             event.accepted = true
0645                         }                                    // TODO: Move cursor
0646                     }
0647 
0648                     onPressAndHold:
0649                     {
0650                         //                         if(Maui.Handy.isMobile)
0651                         //                         {
0652                         //                             return
0653                         //                         }
0654                         //
0655                         documentMenu.targetClick(spellcheckhighlighterLoader, body.positionAt(point.position.x, point.position.y));
0656                     }
0657 
0658                     onPressed:
0659                     {
0660                         if(Maui.Handy.isMobile)
0661                         {
0662                             return
0663                         }
0664 
0665                         if(event.button === Qt.RightButton)
0666                         {
0667                             documentMenu.targetClick(spellcheckhighlighterLoader, body.positionAt(event.x, event.y))
0668                         }
0669                     }
0670 
0671                     Loader
0672                     {
0673                         id: _linesCounter
0674                         asynchronous: true
0675                         active: control.showLineNumbers && !document.isRich
0676 
0677                         anchors.left: parent.left
0678                         anchors.top: parent.top
0679 
0680                         height: Math.max(_flickable.contentHeight, control.height)
0681                         width: active ? 48 : 0
0682                         sourceComponent: _linesCounterComponent
0683                     }
0684 
0685                     Component
0686                     {
0687                         id: _linesCounterComponent
0688 
0689                         Rectangle
0690                         {
0691                             color: Qt.darker(Maui.Theme.backgroundColor, 1)
0692 
0693                             ListView
0694                             {
0695                                 id: _linesCounterList
0696 
0697                                 anchors.fill: parent
0698                                 anchors.topMargin: body.topPadding + body.textMargin
0699 
0700                                 model: document.lineCount
0701 
0702                                 Binding on currentIndex
0703                                 {
0704                                     value: document.currentLineIndex
0705                                     restoreMode: Binding.RestoreBindingOrValue
0706                                 }
0707 
0708                                 Timer
0709                                 {
0710                                     id: _lineIndexTimer
0711                                     interval: 250
0712                                     onTriggered: _linesCounterList.currentIndex= document.currentLineIndex
0713                                 }
0714 
0715                                 Connections
0716                                 {
0717                                     target: document
0718                                     function onLineCountChanged()
0719                                     {
0720                                         _lineIndexTimer.restart()
0721                                     }
0722                                 }
0723 
0724                                 orientation: ListView.Vertical
0725                                 interactive: false
0726                                 snapMode: ListView.NoSnap
0727 
0728                                 boundsBehavior: Flickable.StopAtBounds
0729                                 boundsMovement :Flickable.StopAtBounds
0730 
0731                                 preferredHighlightBegin: 0
0732                                 preferredHighlightEnd: width
0733 
0734                                 highlightRangeMode: ListView.StrictlyEnforceRange
0735                                 highlightMoveDuration: 0
0736                                 highlightFollowsCurrentItem: false
0737                                 highlightResizeDuration: 0
0738                                 highlightMoveVelocity: -1
0739                                 highlightResizeVelocity: -1
0740 
0741                                 maximumFlickVelocity: 0
0742 
0743                                 delegate: Row
0744                                 {
0745                                     id: _delegate
0746                                     readonly property int line : index
0747                                     property bool foldable : control.document.isFoldable(line)
0748                                     width:  ListView.view.width
0749                                     height: Math.max(fontSize, document.lineHeight(line))
0750 
0751                                     readonly property real fontSize : control.body.font.pointSize
0752 
0753                                     readonly property bool isCurrentItem : ListView.isCurrentItem
0754 
0755                                     Connections
0756                                     {
0757                                         target: control.body
0758 
0759                                         function onContentHeightChanged()
0760                                         {
0761                                             if(_delegate.isCurrentItem)
0762                                             {
0763                                                 console.log("Updating line height")
0764                                                 _delegate.height = control.document.lineHeight(_delegate.line)
0765                                                 _delegate.foldable = control.document.isFoldable(_delegate.line)
0766                                             }
0767                                         }
0768                                     }
0769 
0770                                     Label
0771                                     {
0772                                         width: 32
0773                                         height: parent.height
0774                                         opacity: isCurrentItem  ? 1 : 0.7
0775                                         color: isCurrentItem ? control.Maui.Theme.highlightColor : control.body.color
0776                                         font.pointSize: Math.min(Maui.Style.fontSizes.medium, body.font.pointSize)
0777                                         horizontalAlignment: Text.AlignHCenter
0778                                         verticalAlignment: Text.AlignVCenter
0779                                         //                                         renderType: Text.NativeRendering
0780                                         font.family: "Monospace"
0781                                         text: index+1
0782                                     }
0783 
0784                                     AbstractButton
0785                                     {
0786                                         visible: foldable
0787                                         anchors.verticalCenter: parent.verticalCenter
0788                                         height: 8
0789                                         width: 8
0790                                         //onClicked:
0791                                         //{
0792                                         //control.goToLine(_delegate.line)
0793                                         //control.document.toggleFold(_delegate.line)
0794                                         //}
0795                                         contentItem: Maui.Icon
0796                                         {
0797                                             source: "go-down"
0798                                         }
0799                                     }
0800                                 }
0801                             }
0802 
0803                             //                            Maui.Separator
0804                             //                            {
0805                             //                                anchors.top: parent.top
0806                             //                                anchors.bottom: parent.bottom
0807                             //                                anchors.right: parent.right
0808                             //                                width: 0.5
0809                             //                                weight: Maui.Separator.Weight.Light
0810                             //                            }
0811                         }
0812                     }
0813                 }
0814             }
0815         }
0816 
0817         Loader
0818         {
0819             active: Maui.Handy.isTouch
0820             asynchronous: true
0821 
0822             anchors.bottom: parent.bottom
0823             anchors.right: parent.right
0824             anchors.margins: Maui.Style.space.big
0825 
0826             sourceComponent: Maui.FloatingButton
0827             {
0828                 icon.name: "edit-menu"
0829                 onClicked: documentMenu.targetClick(spellcheckhighlighterLoader, body.cursorPosition)
0830             }
0831         }
0832     }
0833 
0834     function forceActiveFocus()
0835     {
0836         body.forceActiveFocus()
0837     }
0838 
0839     function goToLine(line)
0840     {
0841         if(line>0 && line <= body.lineCount)
0842         {
0843             body.cursorPosition = document.goToLine(line-1)
0844             body.forceActiveFocus()
0845         }
0846     }
0847 }