Qt Quick Controls - Editor de texto
Una aplicación de editor de texto enriquecido que utiliza Qt Quick Controls.
Text Editor Example permite la edición WYSIWYG de un archivo HTML, Markdown o de texto plano. La aplicación incluye dos interfaces de usuario: una para pantallas grandes y otra simplificada para dispositivos táctiles pequeños. Ambas son QML "puro". texteditor.cpp contiene la función main(), que llama a QFontDatabase::addApplicationFont() para añadir un icono de fuente. (FontLoader sería una forma alternativa de conseguir el mismo resultado).
Interfaz de usuario de escritorio

La versión de escritorio es un completo editor de texto con capacidades para formatear texto, y abrir y guardar archivos HTML, Markdown y texto plano.
En el patrón de diseño modelo-vista-control (MVC), la capa de control incluye el conjunto de operaciones que se pueden realizar. En Qt Quick Controls, el tipo Action se utiliza para encapsular una única operación o comando. En consecuencia, comenzamos con un conjunto de objetos Action:
Action { id: openAction text: qsTr("&Open") shortcut: StandardKey.Open onTriggered: { if (textArea.textDocument.modified) discardDialog.open() else openDialog.open() } }
El Action para abrir un archivo debe primero preguntar al usuario si el documento existente ha sido modificado, para evitar perder los cambios del usuario. En caso contrario, simplemente abre el FileDialog que se declara más adelante.
El Action para guardar el archivo sólo se activa si hay cambios que guardar:
Action { id: saveAction text: qsTr("&Save…") shortcut: StandardKey.Save enabled: textArea.textDocument.modified onTriggered: textArea.textDocument.save() }
Action para copiar el texto seleccionado sólo se activa si hay texto seleccionado:
Action { id: copyAction text: qsTr("&Copy") shortcut: StandardKey.Copy enabled: textArea.selectedText onTriggered: textArea.copy() }
Cada Acción para cambiar el formato del texto (como negrita, cursiva y alineación) es checkable, y su estado booleano checked está sincronizado con la propiedad correspondiente en selected text. Como la sincronización bidireccional declarativa es difícil, utilizamos un script onTriggered para cambiar la propiedad cuando se activa la Acción. La propiedad cursorSelection es nueva en Qt 6.7 y hace esto mucho más fácil de lo que era.
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 }
Tenemos un MenuBar que contiene la jerarquía de Menus y MenuItems. Cada MenuItem simplemente necesita enlazar el action relevante, que encapsula la representación UI y la implementación.
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 } ...
Los mismos objetos Action se reutilizan en ToolBar; pero aquí anulamos la propiedad text de cada Acción para elegir un icono textual de nuestra fuente de iconos:
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 parte principal del editor de texto es un TextArea dentro de 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á unido al eje vertical. Dado que el word-wrapping se activa a través de wrapMode, no necesitamos un ScrollBar horizontal.
La propiedad adjunta TextArea.flickable se utiliza para que cuando el cursor de texto se mueva fuera de la ventana gráfica (por ejemplo mediante las teclas de flecha, o escribiendo mucho texto), TextArea desplace el Flickable para mantener el cursor visible.
Tomamos las mismas acciones que declaramos en MenuBar y ToolBar y las añadimos a los elementos existentes en el estándar context menu proporcionado por TextArea:
const menu = textArea.ContextMenu.menu menu.addItem(menuSeparatorComponent.createObject(menu.contentItem)) menu.addAction(fontDialogAction) menu.addAction(colorDialogAction)
Utilizamos sistemáticamente la función qsTr() para permitir la traducción del texto de la interfaz de usuario, de modo que la aplicación tenga sentido independientemente del idioma nativo del usuario final.
Utilizamos varios tipos 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() } }
Generalmente es más fácil declarar instancias separadas para cada propósito. Tenemos dos instancias de FileDialog, para abrir y guardar archivos respectivamente. Esto se hizo más fácil en Qt 6.7, con las nuevas características de TextDocument.
Un FontDialog y un ColorDialog permiten cambiar el formato del texto. (En formato Markdown, no hay sintaxis para representar opciones específicas de fuente y color; pero se guardan características de fuente como negrita, cursiva y monospace. En formato HTML, se guarda todo el formato).
Tenemos un MessageDialog para mostrar mensajes de error, y dos más para indicar al usuario qué hacer cuando se ha modificado un archivo.
Interfaz de usuario táctil

La interfaz de usuario táctil es una versión simplificada del editor de texto. Es adecuada para dispositivos táctiles con pantalla de tamaño limitado. El ejemplo utiliza selectores de archivo para cargar automáticamente la interfaz de usuario apropiada.
Ejecutar el ejemplo
Para ejecutar el ejemplo desde Qt Creator, abra el modo Welcome y seleccione el ejemplo de Examples. Para más información, consulte Qt Creator: Tutorial: Construir y ejecutar.
© 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.