// Copyright (C) 2021 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause import QtQuick import QtCore import QtQuick.Controls import QtQuick.Window import QtQuick.Dialogs import Qt.labs.platform as Platform import io.qt.examples.texteditor // TODO: // - make designer-friendly ApplicationWindow { id: window width: 1024 height: 600 visible: true title: document.fileName + " - Text Editor Example" Component.onCompleted: { x = Screen.width / 2 - width / 2 y = Screen.height / 2 - height / 2 } Action { id: openAction shortcut: StandardKey.Open onTriggered: openDialog.open() } Action { id: saveAsAction shortcut: StandardKey.SaveAs onTriggered: saveDialog.open() } Action { id: quitAction shortcut: StandardKey.Quit onTriggered: close() } Action { id: copyAction shortcut: StandardKey.Copy onTriggered: textArea.copy() } Action { id: cutAction shortcut: StandardKey.Cut onTriggered: textArea.cut() } Action { id: pasteAction shortcut: StandardKey.Paste onTriggered: textArea.paste() } Action { id: boldAction shortcut: StandardKey.Bold onTriggered: document.bold = !document.bold } Action { id: italicAction shortcut: StandardKey.Italic onTriggered: document.italic = !document.italic } Action { id: underlineAction shortcut: StandardKey.Underline onTriggered: document.underline = !document.underline } Platform.MenuBar { Platform.Menu { title: qsTr("&File") Platform.MenuItem { text: qsTr("&Open") onTriggered: openDialog.open() } Platform.MenuItem { text: qsTr("&Save As...") onTriggered: saveDialog.open() } Platform.MenuItem { text: qsTr("&Quit") onTriggered: close() } } Platform.Menu { title: qsTr("&Edit") Platform.MenuItem { text: qsTr("&Copy") enabled: textArea.selectedText onTriggered: textArea.copy() } Platform.MenuItem { text: qsTr("Cu&t") enabled: textArea.selectedText onTriggered: textArea.cut() } Platform.MenuItem { text: qsTr("&Paste") enabled: textArea.canPaste onTriggered: textArea.paste() } } Platform.Menu { title: qsTr("F&ormat") Platform.MenuItem { text: qsTr("&Bold") checkable: true checked: document.bold onTriggered: document.bold = !document.bold } Platform.MenuItem { text: qsTr("&Italic") checkable: true checked: document.italic onTriggered: document.italic = !document.italic } Platform.MenuItem { text: qsTr("&Underline") checkable: true checked: document.underline onTriggered: document.underline = !document.underline } Platform.MenuItem { text: qsTr("&Strikeout") checkable: true checked: document.strikeout onTriggered: document.strikeout = !document.strikeout } } } 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: document.load(selectedFile) } FileDialog { id: saveDialog fileMode: FileDialog.SaveFile defaultSuffix: document.fileType nameFilters: openDialog.nameFilters selectedNameFilter.index: document.fileType === "txt" ? 0 : 1 currentFolder: StandardPaths.writableLocation(StandardPaths.DocumentsLocation) onAccepted: document.saveAs(selectedFile) } FontDialog { id: fontDialog onAccepted: document.font = selectedFont } ColorDialog { id: colorDialog selectedColor: "black" onAccepted: document.textColor = 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) Qt.quit() } } header: ToolBar { leftPadding: 8 Flow { id: flow width: parent.width Row { id: fileRow ToolButton { id: openButton text: "\uF115" // icon-folder-open-empty font.family: "fontello" action: openAction 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 enabled: textArea.selectedText action: copyAction } ToolButton { id: cutButton text: "\uE802" // icon-scissors font.family: "fontello" focusPolicy: Qt.TabFocus enabled: textArea.selectedText action: cutAction } ToolButton { id: pasteButton text: "\uF0EA" // icon-paste font.family: "fontello" focusPolicy: Qt.TabFocus enabled: textArea.canPaste action: pasteAction } ToolSeparator { contentItem.visible: editRow.y === formatRow.y } } Row { id: formatRow ToolButton { id: boldButton text: "\uE800" // icon-bold font.family: "fontello" focusPolicy: Qt.TabFocus checkable: true checked: document.bold action: boldAction } ToolButton { id: italicButton text: "\uE801" // icon-italic font.family: "fontello" focusPolicy: Qt.TabFocus checkable: true checked: document.italic action: italicAction } ToolButton { id: underlineButton text: "\uF0CD" // icon-underline font.family: "fontello" focusPolicy: Qt.TabFocus checkable: true checked: document.underline action: underlineAction } ToolButton { id: strikeoutButton text: "\uF0CC" font.family: "fontello" focusPolicy: Qt.TabFocus checkable: true checked: document.strikeout onClicked: document.strikeout = !document.strikeout } ToolButton { id: fontFamilyToolButton text: qsTr("\uE808") // icon-font font.family: "fontello" font.bold: document.bold font.italic: document.italic font.underline: document.underline font.strikeout: document.strikeout focusPolicy: Qt.TabFocus onClicked: function () { fontDialog.selectedFont = document.font fontDialog.open() } } ToolButton { id: textColorButton text: "\uF1FC" // icon-brush font.family: "fontello" focusPolicy: Qt.TabFocus onClicked: function () { colorDialog.selectedColor = document.textColor colorDialog.open() } Rectangle { width: aFontMetrics.width + 3 height: 2 color: document.textColor parent: textColorButton.contentItem anchors.horizontalCenter: parent.horizontalCenter anchors.baseline: parent.baseline anchors.baselineOffset: 6 TextMetrics { id: aFontMetrics font: textColorButton.font text: textColorButton.text } } } ToolSeparator { contentItem.visible: formatRow.y === alignRow.y } } Row { id: alignRow ToolButton { id: alignLeftButton text: "\uE803" // icon-align-left font.family: "fontello" focusPolicy: Qt.TabFocus checkable: true checked: document.alignment == Qt.AlignLeft onClicked: document.alignment = Qt.AlignLeft } ToolButton { id: alignCenterButton text: "\uE804" // icon-align-center font.family: "fontello" focusPolicy: Qt.TabFocus checkable: true checked: document.alignment == Qt.AlignHCenter onClicked: document.alignment = Qt.AlignHCenter } ToolButton { id: alignRightButton text: "\uE805" // icon-align-right font.family: "fontello" focusPolicy: Qt.TabFocus checkable: true checked: document.alignment == Qt.AlignRight onClicked: document.alignment = Qt.AlignRight } ToolButton { id: alignJustifyButton text: "\uE806" // icon-align-justify font.family: "fontello" focusPolicy: Qt.TabFocus checkable: true checked: document.alignment == Qt.AlignJustify onClicked: document.alignment = Qt.AlignJustify } } } } DocumentHandler { id: document document: textArea.textDocument cursorPosition: textArea.cursorPosition selectionStart: textArea.selectionStart selectionEnd: textArea.selectionEnd property alias family: document.font.family property alias bold: document.font.bold property alias italic: document.font.italic property alias underline: document.font.underline property alias strikeout: document.font.strikeout property alias size: document.font.pointSize Component.onCompleted: { if (Qt.application.arguments.length === 2) document.load("file:" + Qt.application.arguments[1]); else document.load("qrc:/texteditor.html") } onLoaded: function (text, format) { textArea.textFormat = format textArea.text = text } onError: function (message) { errorDialog.text = message errorDialog.open() } } Flickable { id: flickable flickableDirection: Flickable.VerticalFlick anchors.fill: parent TextArea.flickable: TextArea { id: textArea textFormat: Qt.RichText wrapMode: TextArea.Wrap focus: true selectByMouse: true persistentSelection: true // Different styles have different padding and background // decorations, but since this editor is almost taking up the // entire window, we don't need them. leftPadding: 6 rightPadding: 6 topPadding: 0 bottomPadding: 0 background: null MouseArea { acceptedButtons: Qt.RightButton anchors.fill: parent onClicked: contextMenu.open() } onLinkActivated: function (link) { Qt.openUrlExternally(link) } } ScrollBar.vertical: ScrollBar {} } Platform.Menu { id: contextMenu Platform.MenuItem { text: qsTr("Copy") enabled: textArea.selectedText onTriggered: textArea.copy() } Platform.MenuItem { text: qsTr("Cut") enabled: textArea.selectedText onTriggered: textArea.cut() } Platform.MenuItem { text: qsTr("Paste") enabled: textArea.canPaste onTriggered: textArea.paste() } Platform.MenuSeparator {} Platform.MenuItem { text: qsTr("Font...") onTriggered: function () { fontDialog.selectedFont = document.font fontDialog.open() } } Platform.MenuItem { text: qsTr("Color...") onTriggered: function () { colorDialog.selectedColor = document.textColor colorDialog.open() } } } onClosing: function (close) { if (document.modified) { quitDialog.open() close.accepted = false } } }