Revision control
++ // Copyright (C) 2021 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
++
++ import QtCore
++ import QtQuick
++ import QtQuick.Controls
++ import QtQuick.Dialogs
++
++ // TODO:
++ // - make designer-friendly
++
++ ApplicationWindow {
++ id: window
++ width: 1024
++ height: 600
++ visible: true
++ title: textArea.textDocument.source +
++ " - Text Editor Example" + (textArea.textDocument.modified ? " *" : "")
++
++ Action {
++ id: openAction
++ text: qsTr("&Open")
++ shortcut: StandardKey.Open
++ onTriggered: {
++ if (textArea.textDocument.modified)
++ discardDialog.open()
++ else
++ openDialog.open()
++ }
++ }
++
++ Action {
++ id: saveAction
++ text: qsTr("&Save…")
++ shortcut: StandardKey.Save
++ enabled: textArea.textDocument.modified
++ onTriggered: textArea.textDocument.save()
++ }
++
++ Action {
++ id: saveAsAction
++ text: qsTr("Save &As…")
++ shortcut: StandardKey.SaveAs
++ onTriggered: saveDialog.open()
++ }
++
++ Action {
++ id: quitAction
++ text: qsTr("&Quit")
++ shortcut: StandardKey.Quit
++ onTriggered: close()
++ }
++
++ Action {
++ id: copyAction
++ text: qsTr("&Copy")
++ shortcut: StandardKey.Copy
++ enabled: textArea.selectedText
++ onTriggered: textArea.copy()
++ }
++
++ Action {
++ id: cutAction
++ text: qsTr("Cu&t")
++ shortcut: StandardKey.Cut
++ enabled: textArea.selectedText
++ onTriggered: textArea.cut()
++ }
++
++ Action {
++ id: pasteAction
++ text: qsTr("&Paste")
++ shortcut: StandardKey.Paste
++ enabled: textArea.canPaste
++ onTriggered: textArea.paste()
++ }
++
++ Action {
++ id: boldAction
++ text: qsTr("&Bold")
++ shortcut: StandardKey.Bold
++ checkable: true
++ checked: textArea.cursorSelection.font.bold
++ onTriggered: textArea.cursorSelection.font.bold = checked
++ }
++
++ Action {
++ id: italicAction
++ text: qsTr("&Italic")
++ shortcut: StandardKey.Italic
++ checkable: true
++ checked: textArea.cursorSelection.font.italic
++ onTriggered: textArea.cursorSelection.font.italic = checked
++ }
++
++ Action {
++ id: underlineAction
++ text: qsTr("&Underline")
++ shortcut: StandardKey.Underline
++ checkable: true
++ checked: textArea.cursorSelection.font.underline
++ onTriggered: textArea.cursorSelection.font.underline = checked
++ }
++
++ Action {
++ id: strikeoutAction
++ text: qsTr("&Strikeout")
++ checkable: true
++ checked: textArea.cursorSelection.font.strikeout
++ onTriggered: textArea.cursorSelection.font.strikeout = checked
++ }
++
++ Action {
++ id: alignLeftAction
++ text: qsTr("Align &Left")
++ shortcut: "Ctrl+{"
++ checkable: true
++ checked: textArea.cursorSelection.alignment === Qt.AlignLeft
++ onTriggered: textArea.cursorSelection.alignment = Qt.AlignLeft
++ }
++
++ Action {
++ id: alignCenterAction
++ text: qsTr("&Center")
++ shortcut: "Ctrl+|"
++ checkable: true
++ checked: textArea.cursorSelection.alignment === Qt.AlignCenter
++ onTriggered: textArea.cursorSelection.alignment = Qt.AlignCenter
++ }
++
++ Action {
++ id: alignRightAction
++ text: qsTr("Align &Right")
++ shortcut: "Ctrl+}"
++ checkable: true
++ checked: textArea.cursorSelection.alignment === Qt.AlignRight
++ onTriggered: textArea.cursorSelection.alignment = Qt.AlignRight
++ }
++
++ Action {
++ id: alignJustifyAction
++ text: qsTr("&Justify")
++ shortcut: "Ctrl+Alt+}"
++ checkable: true
++ checked: textArea.cursorSelection.alignment === Qt.AlignJustify
++ onTriggered: textArea.cursorSelection.alignment = Qt.AlignJustify
++ }
++
++ Action {
++ id: fontDialogAction
++ text: qsTr("Fon&t…")
++ shortcut: "Ctrl+T"
++ onTriggered: {
++ fontDialog.selectedFont = textArea.cursorSelection.font
++ fontDialog.open()
++ }
++ }
++
++ Action {
++ id: colorDialogAction
++ text: qsTr("Color…")
++ shortcut: "Ctrl+Shift+C"
++ onTriggered: {
++ colorDialog.selectedColor = textArea.cursorSelection.color
++ colorDialog.open()
++ }
++ }
++
++ Component {
++ id: menuSeparatorComponent
++
++ MenuSeparator {}
++ }
++
++ 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
++ }
++ MenuItem {
++ action: cutAction
++ }
++ MenuItem {
++ action: pasteAction
++ }
++ }
++
++ Menu {
++ title: qsTr("F&ormat")
++
++ MenuItem {
++ action: boldAction
++ }
++ MenuItem {
++ action: italicAction
++ }
++ MenuItem {
++ action: underlineAction
++ }
++ MenuItem {
++ action: strikeoutAction
++ }
++ MenuItem {
++ action: fontDialogAction
++ }
++ MenuItem {
++ action: colorDialogAction
++ }
++
++ MenuSeparator {}
++
++ MenuItem {
++ action: alignLeftAction
++ }
++ MenuItem {
++ action: alignCenterAction
++ }
++ MenuItem {
++ action: alignJustifyAction
++ }
++ MenuItem {
++ action: alignRightAction
++ }
++ }
++ }
++
++ 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()
++ }
++ }
++
++ 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
++ }
++ ToolButton {
++ id: cutButton
++ text: "\uE802" // icon-scissors
++ font.family: "fontello"
++ focusPolicy: Qt.TabFocus
++ action: cutAction
++ }
++ ToolButton {
++ id: pasteButton
++ text: "\uF0EA" // icon-paste
++ font.family: "fontello"
++ focusPolicy: Qt.TabFocus
++ 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
++ action: boldAction
++ }
++ ToolButton {
++ id: italicButton
++ text: "\uE801" // icon-italic
++ font.family: "fontello"
++ focusPolicy: Qt.TabFocus
++ action: italicAction
++ }
++ ToolButton {
++ id: underlineButton
++ text: "\uF0CD" // icon-underline
++ font.family: "fontello"
++ focusPolicy: Qt.TabFocus
++ action: underlineAction
++ }
++ ToolButton {
++ id: strikeoutButton
++ text: "\uF0CC"
++ font.family: "fontello"
++ focusPolicy: Qt.TabFocus
++ action: strikeoutAction
++ }
++ ToolButton {
++ id: fontFamilyToolButton
++ text: qsTr("\uE808") // icon-font
++ font.family: "fontello"
++ font.bold: textArea.cursorSelection.font.bold
++ font.italic: textArea.cursorSelection.font.italic
++ font.underline: textArea.cursorSelection.font.underline
++ font.strikeout: textArea.cursorSelection.font.strikeout
++ focusPolicy: Qt.TabFocus
++ onClicked: function () {
++ fontDialog.selectedFont = textArea.cursorSelection.font
++ fontDialog.open()
++ }
++ }
++ ToolButton {
++ id: textColorButton
++ text: "\uF1FC" // icon-brush
++ font.family: "fontello"
++ focusPolicy: Qt.TabFocus
++ onClicked: function () {
++ colorDialog.selectedColor = textArea.cursorSelection.color
++ colorDialog.open()
++ }
++
++ Rectangle {
++ width: aFontMetrics.width + 3
++ height: 2
++ color: textArea.cursorSelection.color
++ 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
++ action: alignLeftAction
++ }
++ ToolButton {
++ id: alignCenterButton
++ text: "\uE804" // icon-align-center
++ font.family: "fontello"
++ focusPolicy: Qt.TabFocus
++ action: alignCenterAction
++ }
++ ToolButton {
++ id: alignRightButton
++ text: "\uE805" // icon-align-right
++ font.family: "fontello"
++ focusPolicy: Qt.TabFocus
++ action: alignRightAction
++ }
++ ToolButton {
++ id: alignJustifyButton
++ text: "\uE806" // icon-align-justify
++ font.family: "fontello"
++ focusPolicy: Qt.TabFocus
++ action: alignJustifyAction
++ }
++ }
++ }
++ }
++
++ 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
++ // 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
++
++ onLinkActivated: function (link) {
++ Qt.openUrlExternally(link)
++ }
++
++ Component.onCompleted: {
++ if (Qt.application.arguments.length === 2)
++ textDocument.source = "file:" + Qt.application.arguments[1]
++ else
++ textDocument.source = "qrc:/texteditor.html"
++ const menu = textArea.ContextMenu.menu
++ menu.addItem(menuSeparatorComponent.createObject(menu.contentItem))
++ menu.addAction(fontDialogAction)
++ menu.addAction(colorDialogAction)
++ }
++
++ textDocument.onStatusChanged: {
++ // a message lookup table using computed properties:
++ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer
++ const statusMessages = {
++ [ TextDocument.ReadError ]: qsTr("Failed to load “%1”"),
++ [ TextDocument.WriteError ]: qsTr("Failed to save “%1”"),
++ [ TextDocument.NonLocalFileError ]: qsTr("Not a local file: “%1”"),
++ }
++ const err = statusMessages[textDocument.status]
++ if (err) {
++ errorDialog.text = err.arg(textDocument.source)
++ errorDialog.open()
++ }
++ }
++
++ }
++ }
++
++ onClosing: function (close) {
++ if (textArea.textDocument.modified) {
++ quitDialog.open()
++ close.accepted = false
++ }
++ }
++ }