Sur cette page

Qt Quick Controls - Éditeur de texte

Une application d'édition de texte enrichi utilisant Qt Quick Controls.

L'exemple d'éditeur de texte permet l'édition WYSIWYG d'un fichier HTML, Markdown ou de texte brut. L'application propose deux interfaces utilisateur : l'une pour les grands écrans, et une interface simplifiée pour les petits appareils tactiles. Les deux sont en QML « pur ». La classe texteditor.cpp contient la fonction main(), qui appelle QFontDatabase::addApplicationFont() pour ajouter une police d'icônes. (FontLoader serait une autre façon d'obtenir le même résultat.)

Interface utilisateur de bureau

Éditeur de texte doté d'une barre de menus, d'une barre d'outils et d'un contenu de document mis en forme

La version de bureau est un éditeur de texte complet permettant de mettre en forme du texte, ainsi que d’ouvrir et d’enregistrer des fichiers HTML, Markdown et de texte brut.

Dans le modèle de conception MVC (modèle-vue-contrôle), la couche de contrôle comprend l’ensemble des opérations pouvant être effectuées. Dans Qt Quick Controls, le type ` Action ` est utilisé pour encapsuler une opération ou une commande unique. Par conséquent, nous commençons par un ensemble d’objets `Action` :

    Action {
        id: openAction
        text: qsTr("&Open")
        shortcut: StandardKey.Open
        onTriggered: {
            if (textArea.textDocument.modified)
                discardDialog.open()
            else
                openDialog.open()
        }
    }

Action L’ FileDialog, qui permet d’ouvrir un fichier, doit d’abord demander à l’utilisateur si le document existant a été modifié, afin d’éviter de perdre les modifications apportées par l’utilisateur. Sinon, elle ouvre simplement l’ déclarée plus bas.

L'Action d'enregistrement du fichier n'est activée que s'il y a des modifications à enregistrer :

    Action {
        id: saveAction
        text: qsTr("&Save…")
        shortcut: StandardKey.Save
        enabled: textArea.textDocument.modified
        onTriggered: textArea.textDocument.save()
    }

L’ Action e permettant de copier le texte sélectionné n’est activée que si du texte est sélectionné :

    Action {
        id: copyAction
        text: qsTr("&Copy")
        shortcut: StandardKey.Copy
        enabled: textArea.selectedText
        onTriggered: textArea.copy()
    }

Chaque action permettant de modifier la mise en forme du texte (gras, italique et alignement, par exemple) est une « checkable », et l’état booléen de son « checked » est synchronisé avec la propriété correspondante dans l’« selected text ». La synchronisation bidirectionnelle déclarative étant complexe, nous utilisons un script « onTriggered » pour modifier la propriété lorsque l’action est activée. La propriété ` cursorSelection ` est une nouveauté de Qt 6.7 et facilite considérablement cette opération par rapport à auparavant.

    Action {
        id: boldAction
        text: qsTr("&Bold")
        shortcut: StandardKey.Bold
        checkable: true
        checked: textArea.cursorSelection.font.bold
        onTriggered: textArea.cursorSelection.font.bold = checked
    }

    Action {
        id: alignCenterAction
        text: qsTr("&Center")
        shortcut: "Ctrl+|"
        checkable: true
        checked: textArea.cursorSelection.alignment === Qt.AlignCenter
        onTriggered: textArea.cursorSelection.alignment = Qt.AlignCenter
    }

Nous disposons d’un fichier « MenuBar » contenant la hiérarchie des objets « Menus » et « MenuItems ». Chaque « MenuItem » doit simplement lier l’objet « action » correspondant, qui encapsule la représentation de l’interface utilisateur et l’implémentation.

    menuBar: MenuBar {
        Menu {
            title: qsTr("&File")

            MenuItem {
                action: openAction
            }
            MenuItem {
                action: saveAction
            }
            MenuItem {
                action: saveAsAction
            }
            MenuItem {
                action: quitAction
            }
        }

        Menu {
            title: qsTr("&Edit")

            MenuItem {
                action: copyAction
            }
        ...

Les mêmes objets ` Action ` sont réutilisés dans l’ ToolBar; mais ici, nous redéfinissons la propriété ` text ` de chaque Action afin de choisir une icône textuelle dans notre police d’icônes :

    header: ToolBar {
        Flow {
            width: parent.width

            Row {
                id: fileRow
                ToolButton {
                    id: openButton
                    text: "\uF115" // icon-folder-open-empty
                    font.family: "fontello"
                    action: openAction
                    focusPolicy: Qt.TabFocus
                }
                ToolButton {
                    id: saveButton
                    text: "\uE80A" // icon-floppy-disk
                    font.family: "fontello"
                    action: saveAction
                    focusPolicy: Qt.TabFocus
                }
                ToolSeparator {
                    contentItem.visible: fileRow.y === editRow.y
                }
            }

            Row {
                id: editRow
                ToolButton {
                    id: copyButton
                    text: "\uF0C5" // icon-docs
                    font.family: "fontello"
                    focusPolicy: Qt.TabFocus
                    action: copyAction
                }
            ...

La partie principale de l’éditeur de texte est un ` TextArea ` situé à l’intérieur d’un ` Flickable` :

    Flickable {
        id: flickable
        flickableDirection: Flickable.VerticalFlick
        anchors.fill: parent

        ScrollBar.vertical: ScrollBar {}

        TextArea.flickable: TextArea {
            id: textArea
            textFormat: Qt.AutoText
            wrapMode: TextArea.Wrap
            focus: true
            selectByMouse: true
            persistentSelection: true
            ...

Un ScrollBar est attaché à l’axe vertical. Le retour à la ligne automatique étant activé via wrapMode, nous n’avons pas besoin d’un ScrollBar horizontal.

La propriété attachée « TextArea.flickable » est utilisée pour que, lorsque le curseur de texte sort de la zone d’affichage (par exemple à l’aide des touches fléchées ou en saisissant beaucoup de texte), « TextArea » fasse défiler la « Flickable » afin de maintenir le curseur visible.

context menu Nous reprenons les mêmes actions que celles déclarées dans les fonctions ` MenuBar ` et ` ToolBar `, puis nous les ajoutons aux éléments existants de la liste de défilement standard fournie par ` TextArea` :

                const menu = textArea.ContextMenu.menu
                menu.addItem(menuSeparatorComponent.createObject(menu.contentItem))
                menu.addAction(fontDialogAction)
                menu.addAction(colorDialogAction)

Nous utilisons systématiquement la fonction ` qsTr()` pour permettre la traduction du texte de l’interface utilisateur, afin que l’application reste compréhensible quelle que soit la langue maternelle de l’utilisateur final.

Nous utilisons plusieurs types de dialogs:

    FileDialog {
        id: openDialog
        fileMode: FileDialog.OpenFile
        selectedNameFilter.index: 1
        nameFilters: ["Text files (*.txt)", "HTML files (*.html *.htm)", "Markdown files (*.md *.markdown)"]
        currentFolder: StandardPaths.writableLocation(StandardPaths.DocumentsLocation)
        onAccepted: {
            textArea.textDocument.modified = false // we asked earlier, if necessary
            textArea.textDocument.source = selectedFile
        }
    }

    FileDialog {
        id: saveDialog
        fileMode: FileDialog.SaveFile
        nameFilters: openDialog.nameFilters
        currentFolder: StandardPaths.writableLocation(StandardPaths.DocumentsLocation)
        onAccepted: textArea.textDocument.saveAs(selectedFile)
    }

    FontDialog {
        id: fontDialog
        onAccepted: textArea.cursorSelection.font = selectedFont
    }

    ColorDialog {
        id: colorDialog
        selectedColor: textArea.cursorSelection.color
        onAccepted: textArea.cursorSelection.color = selectedColor
    }

    MessageDialog {
        title: qsTr("Error")
        id: errorDialog
    }

    MessageDialog {
        id : quitDialog
        title: qsTr("Quit?")
        text: qsTr("The file has been modified. Quit anyway?")
        buttons: MessageDialog.Yes | MessageDialog.No
        onButtonClicked: function (button, role) {
            if (role === MessageDialog.YesRole) {
                textArea.textDocument.modified = false
                Qt.quit()
            }
        }
    }

    MessageDialog {
        id : discardDialog
        title: qsTr("Discard changes?")
        text: qsTr("The file has been modified. Open a new file anyway?")
        buttons: MessageDialog.Yes | MessageDialog.No
        onButtonClicked: function (button, role) {
            if (role === MessageDialog.YesRole)
                openDialog.open()
        }
    }

Il est généralement plus simple de déclarer des instances distinctes pour chaque usage. Nous disposons de deux instances de ` FileDialog`, respectivement pour l’ouverture et l’enregistrement de fichiers. Cela est devenu plus facile dans Qt 6.7, grâce aux nouvelles fonctionnalités de ` TextDocument`.

Un ` FontDialog ` et un ` ColorDialog ` permettent de modifier la mise en forme du texte. (Au format Markdown, il n’existe pas de syntaxe permettant de spécifier des choix de police et de couleur ; cependant, les caractéristiques de police telles que le gras, l’italique et la police à espacement fixe sont conservées. Au format HTML, toute la mise en forme est conservée.)

Nous disposons d’un « MessageDialog » pour afficher les messages d’erreur, et de deux autres pour indiquer à l’utilisateur la marche à suivre lorsqu’un fichier a été modifié.

Interface utilisateur tactile

Interface d'éditeur de texte pour appareils tactiles avec barre d'outils de mise en forme

L’interface utilisateur tactile est une version simplifiée de l’éditeur de texte. Elle est adaptée aux appareils tactiles dotés d’un écran de taille réduite. L’exemple utilise des sélecteurs de fichiers pour charger automatiquement l’interface utilisateur appropriée.

Exécution de l’exemple

Vous pouvez exécuter l’exemple à partir de :

Exemple de projet sur code.qt.io

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.