Sur cette page

Qt Quick Contrôles - Éditeur de texte

Une application d'éditeur de texte riche utilisant Qt Quick Controls.

L'exemple de l'éditeur de texte permet l'édition WYSIWYG d'un fichier HTML, Markdown ou texte brut. L'application est livrée avec deux interfaces utilisateur : une pour les grands écrans et une autre simplifiée pour les petits appareils tactiles. Les deux sont en QML "pur". 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 avec barre de menu, barre d'outils et contenu de document formaté

La version de bureau est un éditeur de texte complet qui permet de formater le texte et d'ouvrir et d'enregistrer des fichiers HTML, Markdown et de texte brut.

Dans le modèle de conception modèle-vue-contrôle (MVC), la couche de contrôle comprend l'ensemble des opérations qui peuvent être effectuées. Dans les contrôles Qt Quick, le type Action est utilisé pour encapsuler une opération ou une commande unique. En conséquence, 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()
        }
    }

L'objet Action pour l'ouverture d'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. Dans le cas contraire, il ouvre simplement l'objet FileDialog qui est déclaré plus loin.

La fonction 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()
    }

La fonction Action de copie du 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 le formatage du texte (gras, italique et alignement) est checkable, et son état booléen checked est synchronisé avec la propriété correspondante dans selected text. La synchronisation bidirectionnelle déclarative étant difficile, nous utilisons un script onTriggered pour modifier la propriété lorsque l'action est activée. La propriété cursorSelection est nouvelle dans Qt 6.7 et rend cela beaucoup plus facile qu'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 avons un MenuBar qui contient la hiérarchie des Menus et des MenuItems. Chaque MenuItem doit simplement lier le 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 ToolBar; mais ici, nous surchargeons la propriété text de chaque action pour choisir une icône textuelle à partir de 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 à 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. Étant donné que l'habillage des mots est activé via wrapMode, nous n'avons pas besoin d'un ScrollBar horizontal.

La propriété TextArea.flickable attached est utilisée pour que, lorsque le curseur de texte est déplacé hors de la fenêtre de visualisation (par exemple à l'aide des touches fléchées ou en tapant beaucoup de texte), TextArea fasse défiler Flickable pour que le curseur reste visible.

Nous prenons les mêmes actions que nous avons déclarées dans MenuBar et ToolBar et les ajoutons aux éléments existants dans la norme context menu 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 ait un sens 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 facile de déclarer des instances distinctes pour chaque usage. Nous avons deux instances de FileDialog, pour l'ouverture et l'enregistrement de fichiers respectivement. Cela est devenu plus facile dans Qt 6.7, avec les nouvelles fonctionnalités de TextDocument.

Un FontDialog et un ColorDialog permettent de modifier le formatage du texte. (Au format Markdown, il n'y a pas de syntaxe pour représenter des choix spécifiques de polices et de couleurs ; mais les caractéristiques des polices telles que gras, italique et monospace sont sauvegardées. Au format HTML, toutes les mises en forme sont sauvegardées).

Nous disposons d'une page MessageDialog pour afficher les messages d'erreur et de deux autres pour indiquer à l'utilisateur ce qu'il doit faire lorsqu'un fichier a été modifié.

Interface utilisateur tactile

Interface d'édition de texte pour appareils tactiles avec barre d'outils de formatage

L'interface utilisateur tactile est une version simplifiée de l'éditeur de texte. Elle convient aux appareils tactiles dont la taille de l'écran est limitée. L'exemple utilise des sélecteurs de fichiers pour charger automatiquement l'interface utilisateur appropriée.

Exécution de l'exemple

Pour exécuter l'exemple à partir de Qt Creatorouvrez le mode Welcome et sélectionnez l'exemple à partir de Examples. Pour plus d'informations, voir Qt Creator: Tutoriel : Construire et exécuter.

Exemple de projet @ 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.