aboutsummaryrefslogtreecommitdiffstats
path: root/examples/quickcontrols/texteditor
diff options
context:
space:
mode:
Diffstat (limited to 'examples/quickcontrols/texteditor')
-rw-r--r--examples/quickcontrols/texteditor/CMakeLists.txt40
-rw-r--r--examples/quickcontrols/texteditor/doc/src/qtquickcontrols-texteditor.qdoc181
-rw-r--r--examples/quickcontrols/texteditor/documenthandler.cpp348
-rw-r--r--examples/quickcontrols/texteditor/documenthandler.h121
-rw-r--r--examples/quickcontrols/texteditor/example.md10
-rw-r--r--examples/quickcontrols/texteditor/fonts/fontello.ttfbin10208 -> 19044 bytes
-rw-r--r--examples/quickcontrols/texteditor/images/qt-logo.pngbin4039 -> 3255 bytes
-rw-r--r--examples/quickcontrols/texteditor/qml/+touch/texteditor.qml130
-rw-r--r--examples/quickcontrols/texteditor/qml/texteditor.qml349
-rw-r--r--examples/quickcontrols/texteditor/texteditor.cpp12
-rw-r--r--examples/quickcontrols/texteditor/texteditor.html27
-rw-r--r--examples/quickcontrols/texteditor/texteditor.pro7
12 files changed, 448 insertions, 777 deletions
diff --git a/examples/quickcontrols/texteditor/CMakeLists.txt b/examples/quickcontrols/texteditor/CMakeLists.txt
index 37c86142da..6fa24b41c6 100644
--- a/examples/quickcontrols/texteditor/CMakeLists.txt
+++ b/examples/quickcontrols/texteditor/CMakeLists.txt
@@ -1,21 +1,14 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
project(texteditor LANGUAGES CXX)
set(CMAKE_AUTOMOC ON)
-if(NOT DEFINED INSTALL_EXAMPLESDIR)
- set(INSTALL_EXAMPLESDIR "examples")
-endif()
-
-set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/quickcontrols/texteditor")
-
-find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick QuickControls2 OPTIONAL_COMPONENTS Widgets)
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Quick QuickControls2)
qt_add_executable(texteditorexample WIN32 MACOSX_BUNDLE
- documenthandler.cpp
texteditor.cpp
)
@@ -33,18 +26,12 @@ qt_add_qml_module(texteditorexample
)
target_link_libraries(texteditorexample PUBLIC
- Qt::Core
- Qt::Gui
- Qt::Quick
- Qt::QuickControls2
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Quick
+ Qt6::QuickControls2
)
-if(TARGET Qt::Widgets)
- target_link_libraries(texteditorexample PUBLIC
- Qt::Widgets
- )
-endif()
-
if(CMAKE_CROSSCOMPILING)
target_compile_definitions(texteditorexample PUBLIC
QT_EXTRA_FILE_SELECTOR="touch"
@@ -52,7 +39,16 @@ if(CMAKE_CROSSCOMPILING)
endif()
install(TARGETS texteditorexample
- RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
- BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
- LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION .
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+)
+
+qt_generate_deploy_qml_app_script(
+ TARGET texteditorexample
+ OUTPUT_SCRIPT deploy_script
+ MACOS_BUNDLE_POST_BUILD
+ NO_UNSUPPORTED_PLATFORM_ERROR
+ DEPLOY_USER_QML_MODULES_ON_UNSUPPORTED_PLATFORM
)
+install(SCRIPT ${deploy_script})
diff --git a/examples/quickcontrols/texteditor/doc/src/qtquickcontrols-texteditor.qdoc b/examples/quickcontrols/texteditor/doc/src/qtquickcontrols-texteditor.qdoc
index b1d7e842cf..40ab1915e6 100644
--- a/examples/quickcontrols/texteditor/doc/src/qtquickcontrols-texteditor.qdoc
+++ b/examples/quickcontrols/texteditor/doc/src/qtquickcontrols-texteditor.qdoc
@@ -6,95 +6,158 @@
\title Qt Quick Controls - Text Editor
\keyword Qt Quick Controls 2 - Text Editor
\ingroup qtquickcontrols-examples
- \brief A QML app using Qt Quick Controls and a C++ class to
- provide a fully-functional rich-text editor application.
+ \examplecategory {Graphics}
+ \brief A rich-text editor app using Qt Quick Controls.
- The \e {Text Editor Example} presents a sample HTML file using the TextArea
- control, preserving the HTML formatting. The application comes with two user
- interfaces; one for traditional desktop platforms with a mouse pointer, and
- another simpler, touch-oriented version.
+ The \e {Text Editor Example} allows WYSIWYG editing of an HTML, Markdown or
+ plain text file. The application comes with two user interfaces: one for
+ larger screens, and a simplified UI for small touch-based devices. Both are
+ "pure" QML. \c texteditor.cpp contains the \c main() function, which calls
+ QFontDatabase::addApplicationFont() to add an icon font. (\l FontLoader
+ would be an alternative way to achieve the same result.)
\section1 Desktop User Interface
\image qtquickcontrols-texteditor-desktop.jpg
The desktop version is a complete text editor with capabilities for formatting
- text, and opening and saving HTML and plain text files. It demonstrates the
- native-looking dialogs and menus using the \l{Qt Labs Platform} module. These
- types are mostly suitable for desktop platforms with support for multiple
- top-level windows, a mouse pointer, and moderate screen size.
+ text, and opening and saving HTML, Markdown and plain text files.
- The desktop UI uses FileDialog for opening and saving files:
+ In the \l {https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller}{model-view-control (MVC)}
+ design pattern, the \e control layer includes the set of operations that
+ can be performed. In Qt Quick Controls, the \l Action type is used to
+ encapsulate a single operation or command. Accordingly, we begin with a
+ set of Action objects:
\quotefromfile texteditor/qml/texteditor.qml
- \skipto FileDialog
- \printuntil /\bsaveAs\b/
- \printline }
+ \skipto Action
+ \printuntil openAction
+ \printto Action
- It uses FontDialog and ColorDialog for choosing fonts and colors:
+ The \l Action for opening a file must first prompt the user if the existing
+ document has been changed, to avoid losing the user's changes. Otherwise
+ it simply opens the FileDialog which is declared further below.
- \skipto FontDialog
- \printuntil /.*colorDialog$/
- \printuntil /^\s{4}\}$/
+ The \l Action for saving the file is enabled only if there are changes to save:
- It also uses \l[QML QtLabsPlatform]{Menu} and
- \l[QML QtLabsPlatform]{MenuItem} that provide a context menu to format text
- within:
+ \printuntil saveAction
+ \printto Action
- \skipto /\bMenu\b/
- \printuntil /^\s{4}\}$/
+ \skipto quitAction
+ \skipuntil }
- \note There is also a standard menubar with more options than the
- context menu.
+ The \l Action for copying selected text is enabled only if some text is selected:
- \section1 Touch User Interface
+ \printuntil copyAction
+ \printuntil }
- \image qtquickcontrols-texteditor-touch.jpg
+ \skipto pasteAction
+ \skipuntil }
- The touch user interface is a simplified version of the text editor. It is
- suitable for touch devices with limited screen size. The example uses
- \l{Using File Selectors with Qt Quick Controls}{file selectors} to load
- the appropriate user interface automatically.
+ Each Action to change text formatting (such as bold, italic and alignment)
+ is \l {Action::}{checkable}, and its boolean \c checked state
+ is in sync with the relevant property in the
+ \l {TextEdit::selectedText}{selected text}.
+ Since declarative bidirectional synchronization is difficult, we use
+ an \c onTriggered script to change the property when the Action is
+ activated. The \l {TextEdit::}{cursorSelection} property
+ is new in Qt 6.7 and makes this much easier than it was.
+
+ \printuntil boldAction
+ \printto Action
+
+ \skipto alignLeftAction
+ \skipuntil }
+
+ \printuntil alignCenterAction
+ \printto Action
+
+ We have a \l MenuBar containing the hierarchy of \l {Menu}{Menus} and
+ MenuItems. Each \l MenuItem merely needs to bind the relevant
+ \l {AbstractButton::}{action}, which encapsulates the
+ UI representation and the implementation.
+
+ \skipto MenuBar
+ \printuntil copyAction
+ \printuntil }
+ \dots 8
+
+ The same \l Action objects are reused in the \l ToolBar; but here we
+ override each Action's \l {AbstractButton::}{text} property to
+ choose a textual icon from our icon font:
+
+ \skipto ToolBar
+ \printuntil copyButton
+ \printuntil }
+ \dots 12
- Unlike the desktop version, which uses top-level dialogs, the touch version
- uses the QML \l Dialog type, which is not a top-level window. This type of
- dialog is fully supported on mobile and embedded platforms that do not support
- multiple top-level windows.
+ The main part of the text editor is a \l TextArea inside a \l Flickable:
- \quotefromfile texteditor/qml/+touch/texteditor.qml
- \skipto /\bDialog\b/
- \printuntil /^\s{4}\}$/
+ \skipto Flickable
+ \printuntil persistentSelection
+ \dots 12
- \section1 C++ Backend
+ A \l ScrollBar is attached to the vertical axis. Since word-wrapping is
+ enabled via \l {TextEdit::}{wrapMode}, we don't need a horizontal
+ ScrollBar.
- Both user interfaces use the same C++ backend, which supports opening, formatting,
- and editing a document. The C++ class, \c DocumentHandler, extends QObject and is
- registered as a QML type under the namespace \c {io.qt.examples.texteditor 1.0}.
+ The \l {TextArea::flickable}{TextArea.flickable} attached property is used
+ so that when the text cursor is moved out of the viewport (for example via
+ arrow keys, or by typing a lot of text), \l TextArea scrolls the
+ \l Flickable to keep the cursor visible.
- The following snippets show how the type is registered under a namespace and later
- imported and instantiated by \e main.qml. For more information about registering C++
- classes as QML types, see \l {Defining QML Types from C++}.
+ There is a context menu; we use a TapHandler to detect a right-click and
+ open it:
- QML type registration:
+ \skipto TapHandler
+ \printuntil }
- \code
- #include <QtQml/qqml.h>
- ...
- qmlRegisterType<DocumentHandler>("io.qt.examples.texteditor", 1, 0, "DocumentHandler");
- ...
- \endcode
+ The context \l Menu contains \l {MenuItem}{MenuItems} that reuse the same
+ \l Action objects as the main \l MenuBar and \l ToolBar are using.
+ As before, it's enough to bind \l {AbstractButton::}{action} to the
+ reusable Action that represents the operation to be performed. However,
+ we override each menu item's \l {MenuItem::}{text} to omit the
+ underlined mnemonics on the context menu.
- QML namespace import:
+ \skipto Menu
+ \printuntil MenuItem
+ \printuntil }
+ \dots 8
- \code
- import io.qt.examples.texteditor 1.0
- \endcode
+ We consistently use the \l qsTr function to enable translation of UI text,
+ so that the application will make sense regardless of the end user's native
+ language.
- QML instance:
+ We use several kinds of \l {Qt Quick Dialogs QML Types}{dialogs}:
\quotefromfile texteditor/qml/texteditor.qml
- \skipto DocumentHandler
- \printuntil /^\s{4}\}$/
+ \skipto FileDialog
+ \printuntil discardDialog
+ \printuntil }
+ \printuntil }
+
+ It's generally easier to declare separate instances for each purpose.
+ We have two instances of \l {QtQuick.Dialogs::}{FileDialog}, for opening
+ and saving files respectively. This became easier in Qt 6.7, with new
+ features in \l TextDocument.
+
+ A \l {QtQuick.Dialogs::}{FontDialog} and a \l {QtQuick.Dialogs::ColorDialog}{ColorDialog}
+ allow changing text formatting. (In Markdown format, there's no syntax to
+ represent specific font and color choices; but font characteristics such as
+ bold, italic and monospace are saved. In HTML format, all formatting is
+ saved.)
+
+ We have a \l {QtQuick.Dialogs::}{MessageDialog} to show error messages, and
+ two more for prompting the user what to do when a file has been modified.
+
+ \section1 Touch User Interface
+
+ \image qtquickcontrols-texteditor-touch.jpg
+
+ The touch user interface is a simplified version of the text editor. It is
+ suitable for touch devices with limited screen size. The example uses
+ \l{Using File Selectors with Qt Quick Controls}{file selectors} to load
+ the appropriate user interface automatically.
\include examples-run.qdocinc
*/
diff --git a/examples/quickcontrols/texteditor/documenthandler.cpp b/examples/quickcontrols/texteditor/documenthandler.cpp
deleted file mode 100644
index f6ed6aef84..0000000000
--- a/examples/quickcontrols/texteditor/documenthandler.cpp
+++ /dev/null
@@ -1,348 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#include "documenthandler.h"
-
-#include <QFile>
-#include <QFileInfo>
-#include <QFileSelector>
-#include <QMimeDatabase>
-#include <QQmlFile>
-#include <QQmlFileSelector>
-#include <QQuickTextDocument>
-#include <QTextCharFormat>
-#include <QStringDecoder>
-#include <QTextDocument>
-#include <QDebug>
-
-DocumentHandler::DocumentHandler(QObject *parent)
- : QObject(parent)
- , m_document(nullptr)
- , m_cursorPosition(-1)
- , m_selectionStart(0)
- , m_selectionEnd(0)
-{
-}
-
-QQuickTextDocument *DocumentHandler::document() const
-{
- return m_document;
-}
-
-void DocumentHandler::setDocument(QQuickTextDocument *document)
-{
- if (document == m_document)
- return;
-
- if (m_document)
- disconnect(m_document->textDocument(), &QTextDocument::modificationChanged, this, &DocumentHandler::modifiedChanged);
- m_document = document;
- if (m_document)
- connect(m_document->textDocument(), &QTextDocument::modificationChanged, this, &DocumentHandler::modifiedChanged);
- emit documentChanged();
-}
-
-int DocumentHandler::cursorPosition() const
-{
- return m_cursorPosition;
-}
-
-void DocumentHandler::setCursorPosition(int position)
-{
- if (position == m_cursorPosition)
- return;
-
- m_cursorPosition = position;
- reset();
- emit cursorPositionChanged();
-}
-
-int DocumentHandler::selectionStart() const
-{
- return m_selectionStart;
-}
-
-void DocumentHandler::setSelectionStart(int position)
-{
- if (position == m_selectionStart)
- return;
-
- m_selectionStart = position;
- emit selectionStartChanged();
-}
-
-int DocumentHandler::selectionEnd() const
-{
- return m_selectionEnd;
-}
-
-void DocumentHandler::setSelectionEnd(int position)
-{
- if (position == m_selectionEnd)
- return;
-
- m_selectionEnd = position;
- emit selectionEndChanged();
-}
-
-QColor DocumentHandler::textColor() const
-{
- QTextCursor cursor = textCursor();
- if (cursor.isNull())
- return QColor(Qt::black);
- QTextCharFormat format = cursor.charFormat();
- return format.foreground().color();
-}
-
-void DocumentHandler::setTextColor(const QColor &color)
-{
- QTextCharFormat format;
- format.setForeground(QBrush(color));
- mergeFormatOnWordOrSelection(format);
- emit textColorChanged();
-}
-
-Qt::Alignment DocumentHandler::alignment() const
-{
- QTextCursor cursor = textCursor();
- if (cursor.isNull())
- return Qt::AlignLeft;
- return textCursor().blockFormat().alignment();
-}
-
-void DocumentHandler::setAlignment(Qt::Alignment alignment)
-{
- QTextBlockFormat format;
- format.setAlignment(alignment);
- QTextCursor cursor = textCursor();
- cursor.mergeBlockFormat(format);
- emit alignmentChanged();
-}
-
-QString DocumentHandler::fileName() const
-{
- const QString filePath = QQmlFile::urlToLocalFileOrQrc(m_fileUrl);
- const QString fileName = QFileInfo(filePath).fileName();
- if (fileName.isEmpty())
- return QStringLiteral("untitled.txt");
- return fileName;
-}
-
-QString DocumentHandler::fileType() const
-{
- return QFileInfo(fileName()).suffix();
-}
-
-QUrl DocumentHandler::fileUrl() const
-{
- return m_fileUrl;
-}
-
-void DocumentHandler::load(const QUrl &fileUrl)
-{
- if (fileUrl == m_fileUrl)
- return;
-
- QQmlEngine *engine = qmlEngine(this);
- if (!engine) {
- qWarning() << "load() called before DocumentHandler has QQmlEngine";
- return;
- }
-
- const QUrl path = engine->interceptUrl(fileUrl, QQmlAbstractUrlInterceptor::UrlString);
- const QString fileName = QQmlFile::urlToLocalFileOrQrc(path);
- if (QFile::exists(fileName)) {
- QMimeType mime = QMimeDatabase().mimeTypeForFile(fileName);
- QFile file(fileName);
- if (file.open(QFile::ReadOnly)) {
- QByteArray data = file.readAll();
- if (QTextDocument *doc = textDocument()) {
- doc->setBaseUrl(path.adjusted(QUrl::RemoveFilename));
- doc->setModified(false);
- if (mime.inherits("text/markdown")) {
- emit loaded(QString::fromUtf8(data), Qt::MarkdownText);
- } else {
- auto encoding = QStringConverter::encodingForHtml(data);
- if (encoding) {
- QStringDecoder decoder(*encoding);
- emit loaded(decoder(data), Qt::AutoText);
- } else {
- // fall back to utf8
- emit loaded(QString::fromUtf8(data), Qt::AutoText);
- }
- }
- }
-
- reset();
- }
- }
-
- m_fileUrl = fileUrl;
- emit fileUrlChanged();
-}
-
-void DocumentHandler::saveAs(const QUrl &fileUrl)
-{
- QTextDocument *doc = textDocument();
- if (!doc)
- return;
-
- const QString filePath = fileUrl.toLocalFile();
- const bool isHtml = QFileInfo(filePath).suffix().contains(QLatin1String("htm"));
- QFile file(filePath);
- if (!file.open(QFile::WriteOnly | QFile::Truncate | (isHtml ? QFile::NotOpen : QFile::Text))) {
- emit error(tr("Cannot save: ") + file.errorString());
- return;
- }
- file.write((isHtml ? doc->toHtml() : doc->toPlainText()).toUtf8());
- file.close();
-
- if (fileUrl == m_fileUrl)
- return;
-
- m_fileUrl = fileUrl;
- emit fileUrlChanged();
-}
-
-void DocumentHandler::reset()
-{
- emit alignmentChanged();
- emit textColorChanged();
- emit fontChanged();
-}
-
-QTextCursor DocumentHandler::textCursor() const
-{
- QTextDocument *doc = textDocument();
- if (!doc)
- return QTextCursor();
-
- QTextCursor cursor = QTextCursor(doc);
- if (m_selectionStart != m_selectionEnd) {
- cursor.setPosition(m_selectionStart);
- cursor.setPosition(m_selectionEnd, QTextCursor::KeepAnchor);
- } else {
- cursor.setPosition(m_cursorPosition);
- }
- return cursor;
-}
-
-QTextDocument *DocumentHandler::textDocument() const
-{
- if (!m_document)
- return nullptr;
-
- return m_document->textDocument();
-}
-
-void DocumentHandler::mergeFormatOnWordOrSelection(const QTextCharFormat &format)
-{
- QTextCursor cursor = textCursor();
- if (!cursor.hasSelection())
- cursor.select(QTextCursor::WordUnderCursor);
- cursor.mergeCharFormat(format);
-}
-
-bool DocumentHandler::modified() const
-{
- return m_document && m_document->textDocument()->isModified();
-}
-
-void DocumentHandler::setModified(bool m)
-{
- if (m_document)
- m_document->textDocument()->setModified(m);
-}
-
-QFont DocumentHandler::font() const
-{
- QTextCursor cursor = textCursor();
- if (cursor.isNull())
- return m_document->textDocument()->defaultFont();
- QTextCharFormat format = cursor.charFormat();
- return format.font();
-}
-
-void DocumentHandler::setFont(const QFont & font){
-
- QTextCursor cursor = textCursor();
- if (!cursor.isNull() && cursor.charFormat().font() == font)
- return;
-
- QTextCharFormat format;
- format.setFont(font);
- mergeFormatOnWordOrSelection(format);
-
- emit fontChanged();
-}
-
-bool DocumentHandler::bold() const
-{
- const QTextCursor cursor = textCursor();
- if (cursor.isNull())
- return m_document->textDocument()->defaultFont().bold();
- return cursor.charFormat().font().bold();
-}
-
-void DocumentHandler::setBold(bool bold)
-{
- const QTextCursor cursor = textCursor();
- if (!cursor.isNull() && cursor.charFormat().font().bold() == bold)
- return;
-
- QFont font = cursor.charFormat().font();
- font.setBold(bold);
- QTextCharFormat format;
- format.setFont(font);
- mergeFormatOnWordOrSelection(format);
-
- emit boldChanged();
-}
-
-bool DocumentHandler::underline() const
-{
- const QTextCursor cursor = textCursor();
- if (cursor.isNull())
- return m_document->textDocument()->defaultFont().underline();
- return cursor.charFormat().font().underline();
-}
-
-void DocumentHandler::setUnderline(bool underline)
-{
- const QTextCursor cursor = textCursor();
- if (!cursor.isNull() && cursor.charFormat().font().underline() == underline)
- return;
-
- QFont font = cursor.charFormat().font();
- font.setUnderline(underline);
- QTextCharFormat format;
- format.setFont(font);
- mergeFormatOnWordOrSelection(format);
-
- emit underlineChanged();
-}
-
-bool DocumentHandler::italic() const
-{
- const QTextCursor cursor = textCursor();
- if (cursor.isNull())
- return m_document->textDocument()->defaultFont().italic();
- return cursor.charFormat().font().italic();
-}
-
-void DocumentHandler::setItalic(bool italic)
-{
- const QTextCursor cursor = textCursor();
- if (!cursor.isNull() && cursor.charFormat().font().italic() == italic)
- return;
-
- QFont font = cursor.charFormat().font();
- font.setItalic(italic);
- QTextCharFormat format;
- format.setFont(font);
- mergeFormatOnWordOrSelection(format);
-
- emit italicChanged();
-}
-
-#include "moc_documenthandler.cpp"
diff --git a/examples/quickcontrols/texteditor/documenthandler.h b/examples/quickcontrols/texteditor/documenthandler.h
deleted file mode 100644
index b750997885..0000000000
--- a/examples/quickcontrols/texteditor/documenthandler.h
+++ /dev/null
@@ -1,121 +0,0 @@
-// Copyright (C) 2017 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-#ifndef DOCUMENTHANDLER_H
-#define DOCUMENTHANDLER_H
-
-#include <QFont>
-#include <QObject>
-#include <QTextCursor>
-#include <QUrl>
-
-QT_BEGIN_NAMESPACE
-class QTextDocument;
-class QQuickTextDocument;
-QT_END_NAMESPACE
-
-class DocumentHandler : public QObject
-{
- Q_OBJECT
-
- Q_PROPERTY(QQuickTextDocument *document READ document WRITE setDocument NOTIFY documentChanged)
- Q_PROPERTY(int cursorPosition READ cursorPosition WRITE setCursorPosition NOTIFY cursorPositionChanged)
- Q_PROPERTY(int selectionStart READ selectionStart WRITE setSelectionStart NOTIFY selectionStartChanged)
- Q_PROPERTY(int selectionEnd READ selectionEnd WRITE setSelectionEnd NOTIFY selectionEndChanged)
-
- Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor NOTIFY textColorChanged)
- Q_PROPERTY(Qt::Alignment alignment READ alignment WRITE setAlignment NOTIFY alignmentChanged)
-
- Q_PROPERTY(QFont font READ font WRITE setFont NOTIFY fontChanged)
-
- Q_PROPERTY(bool bold READ bold WRITE setBold NOTIFY boldChanged)
- Q_PROPERTY(bool underline READ underline WRITE setUnderline NOTIFY underlineChanged)
- Q_PROPERTY(bool italic READ italic WRITE setItalic NOTIFY italicChanged)
-
- Q_PROPERTY(QString fileName READ fileName NOTIFY fileUrlChanged)
- Q_PROPERTY(QString fileType READ fileType NOTIFY fileUrlChanged)
- Q_PROPERTY(QUrl fileUrl READ fileUrl NOTIFY fileUrlChanged)
-
- Q_PROPERTY(bool modified READ modified WRITE setModified NOTIFY modifiedChanged)
-
-public:
- explicit DocumentHandler(QObject *parent = nullptr);
-
- QQuickTextDocument *document() const;
- void setDocument(QQuickTextDocument *document);
-
- int cursorPosition() const;
- void setCursorPosition(int position);
-
- int selectionStart() const;
- void setSelectionStart(int position);
-
- int selectionEnd() const;
- void setSelectionEnd(int position);
-
- QColor textColor() const;
- void setTextColor(const QColor &color);
-
- Qt::Alignment alignment() const;
- void setAlignment(Qt::Alignment alignment);
-
- QFont font() const;
- void setFont(const QFont & font);
-
- bool bold() const;
- void setBold(bool bold);
-
- bool underline() const;
- void setUnderline(bool underline);
-
- bool italic() const;
- void setItalic(bool italic);
-
- QString fileName() const;
- QString fileType() const;
- QUrl fileUrl() const;
-
- bool modified() const;
- void setModified(bool m);
-
-public Q_SLOTS:
- void load(const QUrl &fileUrl);
- void saveAs(const QUrl &fileUrl);
-
-Q_SIGNALS:
- void documentChanged();
- void cursorPositionChanged();
- void selectionStartChanged();
- void selectionEndChanged();
-
- void fontChanged();
- void boldChanged();
- void underlineChanged();
- void italicChanged();
- void textColorChanged();
- void alignmentChanged();
-
- void textChanged();
- void fileUrlChanged();
-
- void loaded(const QString &text, int format);
- void error(const QString &message);
-
- void modifiedChanged();
-
-private:
- void reset();
- QTextCursor textCursor() const;
- QTextDocument *textDocument() const;
- void mergeFormatOnWordOrSelection(const QTextCharFormat &format);
-
- QQuickTextDocument *m_document;
-
- int m_cursorPosition;
- int m_selectionStart;
- int m_selectionEnd;
-
- QUrl m_fileUrl;
-};
-
-#endif // DOCUMENTHANDLER_H
diff --git a/examples/quickcontrols/texteditor/example.md b/examples/quickcontrols/texteditor/example.md
index e385227e3b..f54edab5ae 100644
--- a/examples/quickcontrols/texteditor/example.md
+++ b/examples/quickcontrols/texteditor/example.md
@@ -1,10 +1,10 @@
# Markdown in Qt Quick
The Text, TextEdit and TextArea items support rich text formatted in HTML.
-Since Qt 5.14, they now support two dialects of Markdown as well:
-[The CommonMark Specification](https://spec.commonmark.org/0.29/) is the
+Since Qt 5.14, they support two dialects of Markdown as well:
+[The CommonMark Specification](https://spec.commonmark.org/0.31.2/) is the
conservative formal specification, while
-[GitHub Flavored Markdown](https://guides.github.com/features/mastering-markdown/#GitHub-flavored-markdown)
+[GitHub Flavored Markdown](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax)
adds extra features such as task lists and tables.
If you are viewing this document in the Qt Quick Controls Text Editor example,
@@ -36,7 +36,7 @@ Block {
Block quotes can be nested, and block quotes can include indented code blocks.
-In [The CommonMark Specification](https://spec.commonmark.org/0.29/)
+In [The CommonMark Specification](https://spec.commonmark.org/0.31.2/)
John MacFarlane writes:
> What distinguishes Markdown from many other lightweight markup syntaxes,
@@ -158,7 +158,7 @@ Some Qt Widgets also support Markdown.
[QTextEdit](https://doc.qt.io/qt-6/qtextedit.html) has similar WYSIWYG
editing features as TextEdit and TextArea: you can edit the rendered text
directly. You can use
-[QTextDocument::toMarkdown](https://doc-snapshots.qt.io/qt5-dev/qtextdocument.html#toMarkdown)
+[QTextDocument::toMarkdown](https://doc.qt.io/qt-6/qtextdocument.html#toMarkdown)
to rewrite the Markdown format, and save it back to a file.
If you have the [KDE Kate Editor](https://kate-editor.org/) installed on your
diff --git a/examples/quickcontrols/texteditor/fonts/fontello.ttf b/examples/quickcontrols/texteditor/fonts/fontello.ttf
index f02b44ec12..2db6e2bc4b 100644
--- a/examples/quickcontrols/texteditor/fonts/fontello.ttf
+++ b/examples/quickcontrols/texteditor/fonts/fontello.ttf
Binary files differ
diff --git a/examples/quickcontrols/texteditor/images/qt-logo.png b/examples/quickcontrols/texteditor/images/qt-logo.png
index 2ebc01aafa..63d0f222d8 100644
--- a/examples/quickcontrols/texteditor/images/qt-logo.png
+++ b/examples/quickcontrols/texteditor/images/qt-logo.png
Binary files differ
diff --git a/examples/quickcontrols/texteditor/qml/+touch/texteditor.qml b/examples/quickcontrols/texteditor/qml/+touch/texteditor.qml
index 2a0d9f117f..376a8341eb 100644
--- a/examples/quickcontrols/texteditor/qml/+touch/texteditor.qml
+++ b/examples/quickcontrols/texteditor/qml/+touch/texteditor.qml
@@ -4,17 +4,73 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
-import QtQuick.Window
-
-import io.qt.examples.texteditor
// TODO:
// - make designer-friendly
ApplicationWindow {
id: window
+ width: 480
+ height: 640
visible: true
- title: document.fileName + " - Text Editor Example"
+ title: textArea.textDocument.source +
+ " - Text Editor Example" + (textArea.textDocument.modified ? " *" : "")
+
+ Action {
+ id: boldAction
+ shortcut: StandardKey.Bold
+ checkable: true
+ checked: textArea.cursorSelection.font.bold
+ onTriggered: textArea.cursorSelection.font = Qt.font({ bold: checked })
+ }
+
+ Action {
+ id: italicAction
+ shortcut: StandardKey.Italic
+ checkable: true
+ checked: textArea.cursorSelection.font.italic
+ onTriggered: textArea.cursorSelection.font = Qt.font({ italic: checked })
+ }
+
+ Action {
+ id: underlineAction
+ shortcut: StandardKey.Underline
+ checkable: true
+ checked: textArea.cursorSelection.font.underline
+ onTriggered: textArea.cursorSelection.font = Qt.font({ underline: checked })
+ }
+
+ Action {
+ id: alignLeftAction
+ shortcut: "Ctrl+{"
+ checkable: true
+ checked: textArea.cursorSelection.alignment === Qt.AlignLeft
+ onTriggered: textArea.cursorSelection.alignment = Qt.AlignLeft
+ }
+
+ Action {
+ id: alignCenterAction
+ shortcut: "Ctrl+|"
+ checkable: true
+ checked: textArea.cursorSelection.alignment === Qt.AlignCenter
+ onTriggered: textArea.cursorSelection.alignment = Qt.AlignCenter
+ }
+
+ Action {
+ id: alignRightAction
+ shortcut: "Ctrl+}"
+ checkable: true
+ checked: textArea.cursorSelection.alignment === Qt.AlignRight
+ onTriggered: textArea.cursorSelection.alignment = Qt.AlignRight
+ }
+
+ Action {
+ id: alignJustifyAction
+ shortcut: "Ctrl+Alt+}"
+ checkable: true
+ checked: textArea.cursorSelection.alignment === Qt.AlignJustify
+ onTriggered: textArea.cursorSelection.alignment = Qt.AlignJustify
+ }
header: ToolBar {
leftPadding: 5
@@ -26,9 +82,10 @@ ApplicationWindow {
ToolButton {
id: doneEditingButton
font.family: "fontello"
- text: "\uE80A" // icon-ok
+ text: "\uE809" // icon-ok
opacity: !textArea.readOnly ? 1 : 0
onClicked: textArea.readOnly = true
+ Layout.fillWidth: false
}
Label {
@@ -43,6 +100,7 @@ ApplicationWindow {
font.family: "fontello"
text: "\uF142" // icon-ellipsis-vert
onClicked: menu.open()
+ Layout.fillWidth: false
Menu {
id: menu
@@ -56,23 +114,6 @@ ApplicationWindow {
}
}
- DocumentHandler {
- id: document
- document: textArea.textDocument
- cursorPosition: textArea.cursorPosition
- selectionStart: textArea.selectionStart
- selectionEnd: textArea.selectionEnd
- // textColor: TODO
- Component.onCompleted: document.load("qrc:/texteditor.html")
- onLoaded: {
- textArea.text = text
- }
- onError: {
- errorDialog.text = message
- errorDialog.visible = true
- }
- }
-
Flickable {
id: flickable
flickableDirection: Flickable.VerticalFlick
@@ -94,6 +135,23 @@ ApplicationWindow {
background: null
onLinkActivated: Qt.openUrlExternally(link)
+
+ Component.onCompleted: textDocument.source = "qrc:/texteditor.html"
+
+ 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()
+ }
+ }
}
ScrollBar.vertical: ScrollBar {}
@@ -117,27 +175,21 @@ ApplicationWindow {
font.family: "fontello"
// Don't want to close the virtual keyboard when this is clicked.
focusPolicy: Qt.NoFocus
- checkable: true
- checked: document.bold
- onClicked: document.bold = !document.bold
+ action: boldAction
}
ToolButton {
id: italicButton
text: "\uE801" // icon-italic
font.family: "fontello"
focusPolicy: Qt.NoFocus
- checkable: true
- checked: document.italic
- onClicked: document.italic = !document.italic
+ action: italicAction
}
ToolButton {
id: underlineButton
text: "\uF0CD" // icon-underline
font.family: "fontello"
focusPolicy: Qt.NoFocus
- checkable: true
- checked: document.underline
- onClicked: document.underline = !document.underline
+ action: underlineAction
}
ToolSeparator {}
@@ -147,36 +199,28 @@ ApplicationWindow {
text: "\uE803" // icon-align-left
font.family: "fontello"
focusPolicy: Qt.NoFocus
- checkable: true
- checked: document.alignment == Qt.AlignLeft
- onClicked: document.alignment = Qt.AlignLeft
+ action: alignLeftAction
}
ToolButton {
id: alignCenterButton
text: "\uE804" // icon-align-center
font.family: "fontello"
focusPolicy: Qt.NoFocus
- checkable: true
- checked: document.alignment == Qt.AlignHCenter
- onClicked: document.alignment = Qt.AlignHCenter
+ action: alignCenterAction
}
ToolButton {
id: alignRightButton
text: "\uE805" // icon-align-right
font.family: "fontello"
focusPolicy: Qt.NoFocus
- checkable: true
- checked: document.alignment == Qt.AlignRight
- onClicked: document.alignment = Qt.AlignRight
+ action: alignRightAction
}
ToolButton {
id: alignJustifyButton
text: "\uE806" // icon-align-justify
font.family: "fontello"
focusPolicy: Qt.NoFocus
- checkable: true
- checked: document.alignment == Qt.AlignJustify
- onClicked: document.alignment = Qt.AlignJustify
+ action: alignJustifyAction
}
}
}
diff --git a/examples/quickcontrols/texteditor/qml/texteditor.qml b/examples/quickcontrols/texteditor/qml/texteditor.qml
index decf0801e4..154a531752 100644
--- a/examples/quickcontrols/texteditor/qml/texteditor.qml
+++ b/examples/quickcontrols/texteditor/qml/texteditor.qml
@@ -6,9 +6,6 @@ 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
@@ -18,7 +15,8 @@ ApplicationWindow {
width: 1024
height: 600
visible: true
- title: document.fileName + " - Text Editor Example"
+ title: textArea.textDocument.source +
+ " - Text Editor Example" + (textArea.textDocument.modified ? " *" : "")
Component.onCompleted: {
x = Screen.width / 2 - width / 2
@@ -27,122 +25,194 @@ ApplicationWindow {
Action {
id: openAction
+ text: qsTr("&Open")
shortcut: StandardKey.Open
- onTriggered: openDialog.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
- onTriggered: document.bold = !document.bold
+ checkable: true
+ checked: textArea.cursorSelection.font.bold
+ onTriggered: textArea.cursorSelection.font = Qt.font({ bold: checked })
}
Action {
id: italicAction
+ text: qsTr("&Italic")
shortcut: StandardKey.Italic
- onTriggered: document.italic = !document.italic
+ checkable: true
+ checked: textArea.cursorSelection.font.italic
+ onTriggered: textArea.cursorSelection.font = Qt.font({ italic: checked })
}
Action {
id: underlineAction
+ text: qsTr("&Underline")
shortcut: StandardKey.Underline
- onTriggered: document.underline = !document.underline
+ checkable: true
+ checked: textArea.cursorSelection.font.underline
+ onTriggered: textArea.cursorSelection.font = Qt.font({ underline: checked })
+ }
+
+ Action {
+ id: strikeoutAction
+ text: qsTr("&Strikeout")
+ checkable: true
+ checked: textArea.cursorSelection.font.strikeout
+ onTriggered: textArea.cursorSelection.font = Qt.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
}
- Platform.MenuBar {
- Platform.Menu {
+ menuBar: MenuBar {
+ Menu {
title: qsTr("&File")
- Platform.MenuItem {
- text: qsTr("&Open")
- onTriggered: openDialog.open()
+ MenuItem {
+ action: openAction
}
- Platform.MenuItem {
- text: qsTr("&Save As...")
- onTriggered: saveDialog.open()
+ MenuItem {
+ action: saveAction
}
- Platform.MenuItem {
- text: qsTr("&Quit")
- onTriggered: close()
+ MenuItem {
+ action: saveAsAction
+ }
+ MenuItem {
+ action: quitAction
}
}
- Platform.Menu {
+ Menu {
title: qsTr("&Edit")
- Platform.MenuItem {
- text: qsTr("&Copy")
- enabled: textArea.selectedText
- onTriggered: textArea.copy()
+ MenuItem {
+ action: copyAction
}
- Platform.MenuItem {
- text: qsTr("Cu&t")
- enabled: textArea.selectedText
- onTriggered: textArea.cut()
+ MenuItem {
+ action: cutAction
}
- Platform.MenuItem {
- text: qsTr("&Paste")
- enabled: textArea.canPaste
- onTriggered: textArea.paste()
+ MenuItem {
+ action: pasteAction
}
}
- Platform.Menu {
+ Menu {
title: qsTr("F&ormat")
- Platform.MenuItem {
- text: qsTr("&Bold")
- checkable: true
- checked: document.bold
- onTriggered: document.bold = !document.bold
+ MenuItem {
+ action: boldAction
+ }
+ MenuItem {
+ action: italicAction
+ }
+ MenuItem {
+ action: underlineAction
+ }
+ MenuItem {
+ action: strikeoutAction
+ }
+
+ MenuSeparator {}
+
+ MenuItem {
+ action: alignLeftAction
}
- Platform.MenuItem {
- text: qsTr("&Italic")
- checkable: true
- checked: document.italic
- onTriggered: document.italic = !document.italic
+ MenuItem {
+ action: alignCenterAction
}
- Platform.MenuItem {
- text: qsTr("&Underline")
- checkable: true
- checked: document.underline
- onTriggered: document.underline = !document.underline
+ MenuItem {
+ action: alignJustifyAction
}
- Platform.MenuItem {
- text: qsTr("&Strikeout")
- checkable: true
- checked: document.strikeout
- onTriggered: document.strikeout = !document.strikeout
+ MenuItem {
+ action: alignRightAction
}
}
}
@@ -153,28 +223,29 @@ ApplicationWindow {
selectedNameFilter.index: 1
nameFilters: ["Text files (*.txt)", "HTML files (*.html *.htm)", "Markdown files (*.md *.markdown)"]
currentFolder: StandardPaths.writableLocation(StandardPaths.DocumentsLocation)
- onAccepted: document.load(selectedFile)
+ onAccepted: {
+ textArea.textDocument.modified = false // we asked earlier, if necessary
+ textArea.textDocument.source = 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)
+ onAccepted: textArea.textDocument.saveAs(selectedFile)
}
FontDialog {
id: fontDialog
- onAccepted: document.font = selectedFont
+ onAccepted: textArea.cursorSelection.font = selectedFont
}
ColorDialog {
id: colorDialog
selectedColor: "black"
- onAccepted: document.textColor = selectedColor
+ onAccepted: textArea.cursorSelection.color = selectedColor
}
MessageDialog {
@@ -187,7 +258,23 @@ ApplicationWindow {
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() }
+ 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 {
@@ -206,6 +293,13 @@ ApplicationWindow {
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
}
@@ -218,7 +312,6 @@ ApplicationWindow {
text: "\uF0C5" // icon-docs
font.family: "fontello"
focusPolicy: Qt.TabFocus
- enabled: textArea.selectedText
action: copyAction
}
ToolButton {
@@ -226,7 +319,6 @@ ApplicationWindow {
text: "\uE802" // icon-scissors
font.family: "fontello"
focusPolicy: Qt.TabFocus
- enabled: textArea.selectedText
action: cutAction
}
ToolButton {
@@ -234,7 +326,6 @@ ApplicationWindow {
text: "\uF0EA" // icon-paste
font.family: "fontello"
focusPolicy: Qt.TabFocus
- enabled: textArea.canPaste
action: pasteAction
}
ToolSeparator {
@@ -249,8 +340,6 @@ ApplicationWindow {
text: "\uE800" // icon-bold
font.family: "fontello"
focusPolicy: Qt.TabFocus
- checkable: true
- checked: document.bold
action: boldAction
}
ToolButton {
@@ -258,8 +347,6 @@ ApplicationWindow {
text: "\uE801" // icon-italic
font.family: "fontello"
focusPolicy: Qt.TabFocus
- checkable: true
- checked: document.italic
action: italicAction
}
ToolButton {
@@ -267,8 +354,6 @@ ApplicationWindow {
text: "\uF0CD" // icon-underline
font.family: "fontello"
focusPolicy: Qt.TabFocus
- checkable: true
- checked: document.underline
action: underlineAction
}
ToolButton {
@@ -276,21 +361,19 @@ ApplicationWindow {
text: "\uF0CC"
font.family: "fontello"
focusPolicy: Qt.TabFocus
- checkable: true
- checked: document.strikeout
- onClicked: document.strikeout = !document.strikeout
+ action: strikeoutAction
}
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
+ 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 = document.font
+ fontDialog.selectedFont = textArea.cursorSelection.font
fontDialog.open()
}
}
@@ -300,14 +383,14 @@ ApplicationWindow {
font.family: "fontello"
focusPolicy: Qt.TabFocus
onClicked: function () {
- colorDialog.selectedColor = document.textColor
+ colorDialog.selectedColor = textArea.cursorSelection.color
colorDialog.open()
}
Rectangle {
width: aFontMetrics.width + 3
height: 2
- color: document.textColor
+ color: textArea.cursorSelection.color
parent: textColorButton.contentItem
anchors.horizontalCenter: parent.horizontalCenter
anchors.baseline: parent.baseline
@@ -332,79 +415,43 @@ ApplicationWindow {
text: "\uE803" // icon-align-left
font.family: "fontello"
focusPolicy: Qt.TabFocus
- checkable: true
- checked: document.alignment == Qt.AlignLeft
- onClicked: document.alignment = Qt.AlignLeft
+ action: alignLeftAction
}
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
+ action: alignCenterAction
}
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
+ action: alignRightAction
}
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
+ action: alignJustifyAction
}
}
}
}
- 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
+ ScrollBar.vertical: ScrollBar {}
+
TextArea.flickable: TextArea {
id: textArea
- textFormat: Qt.RichText
+ textFormat: Qt.AutoText
wrapMode: TextArea.Wrap
focus: true
selectByMouse: true
@@ -418,60 +465,76 @@ ApplicationWindow {
bottomPadding: 0
background: null
- MouseArea {
+ TapHandler {
acceptedButtons: Qt.RightButton
- anchors.fill: parent
- onClicked: contextMenu.open()
+ onTapped: contextMenu.popup()
}
onLinkActivated: function (link) {
Qt.openUrlExternally(link)
}
- }
- ScrollBar.vertical: ScrollBar {}
+ Component.onCompleted: {
+ if (Qt.application.arguments.length === 2)
+ textDocument.source = "file:" + Qt.application.arguments[1]
+ else
+ textDocument.source = "qrc:/texteditor.html"
+ }
+
+ 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()
+ }
+ }
+ }
}
- Platform.Menu {
+ Menu {
id: contextMenu
- Platform.MenuItem {
+ MenuItem {
text: qsTr("Copy")
- enabled: textArea.selectedText
- onTriggered: textArea.copy()
+ action: copyAction
}
- Platform.MenuItem {
+ MenuItem {
text: qsTr("Cut")
- enabled: textArea.selectedText
- onTriggered: textArea.cut()
+ action: cutAction
}
- Platform.MenuItem {
+ MenuItem {
text: qsTr("Paste")
- enabled: textArea.canPaste
- onTriggered: textArea.paste()
+ action: pasteAction
}
- Platform.MenuSeparator {}
+ MenuSeparator {}
- Platform.MenuItem {
+ MenuItem {
text: qsTr("Font...")
onTriggered: function () {
- fontDialog.selectedFont = document.font
+ fontDialog.selectedFont = textArea.cursorSelection.font
fontDialog.open()
}
}
- Platform.MenuItem {
+ MenuItem {
text: qsTr("Color...")
onTriggered: function () {
- colorDialog.selectedColor = document.textColor
+ colorDialog.selectedColor = textArea.cursorSelection.color
colorDialog.open()
}
}
}
onClosing: function (close) {
- if (document.modified) {
+ if (textArea.textDocument.modified) {
quitDialog.open()
close.accepted = false
}
diff --git a/examples/quickcontrols/texteditor/texteditor.cpp b/examples/quickcontrols/texteditor/texteditor.cpp
index 79cd6d8071..36f27bc5c2 100644
--- a/examples/quickcontrols/texteditor/texteditor.cpp
+++ b/examples/quickcontrols/texteditor/texteditor.cpp
@@ -1,35 +1,23 @@
// Copyright (C) 2017 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-#ifdef QT_WIDGETS_LIB
-#include <QApplication>
-#else
#include <QGuiApplication>
-#endif
#include <QFontDatabase>
#include <QDebug>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQuickStyle>
-#include "documenthandler.h"
-
int main(int argc, char *argv[])
{
QGuiApplication::setApplicationName("Text Editor");
QGuiApplication::setOrganizationName("QtProject");
-#ifdef QT_WIDGETS_LIB
- QApplication app(argc, argv);
-#else
QGuiApplication app(argc, argv);
-#endif
if (QFontDatabase::addApplicationFont(":/fonts/fontello.ttf") == -1)
qWarning() << "Failed to load fontello.ttf";
- qmlRegisterType<DocumentHandler>("io.qt.examples.texteditor", 1, 0, "DocumentHandler");
-
QStringList selectors;
#ifdef QT_EXTRA_FILE_SELECTOR
selectors += QT_EXTRA_FILE_SELECTOR;
diff --git a/examples/quickcontrols/texteditor/texteditor.html b/examples/quickcontrols/texteditor/texteditor.html
index 3c59a053c5..0cd406ea1c 100644
--- a/examples/quickcontrols/texteditor/texteditor.html
+++ b/examples/quickcontrols/texteditor/texteditor.html
@@ -20,24 +20,15 @@
Qt Quick Controls 2
</h2>
<p align="center">
- This example demonstrates a modern rich text editor. The UI uses Qt Labs Platforms to provide native menus and dialogs.
+ This example demonstrates a modern rich text editor.
+ In recent Qt versions, components in
+ <a href="https://doc.qt.io/qt-6/qtquick-controls-qmlmodule.html">Qt Quick Controls</a> and
+ <a href="https://doc.qt.io/qt-6/qtquick-dialogs-qmlmodule.html">Qt Quick Dialogs</a>
+ are implemented via their native-platform counterparts as much as possible.
+ <a href="https://doc.qt.io/qt-6/qml-qtquick-controls-textarea.html">TextArea</a>,
+ <a href="https://doc.qt.io/qt-6/qml-qtquick-textedit.html#cursorSelection-prop">TextSelection</a> and
+ <a href="https://doc.qt.io/qt-6/qml-qtquick-textedit.html#textDocument-prop">TextDocument</a>
+ provide text-editing API.
</p>
- <br />
- <br />
- <br />
-
- <p>
- Below you'll find a list of the native controls used in this application.
- </p>
-
- <ul>
- <li><a href="https://doc.qt.io/qt-6/qml-qt-labs-platform-menu.html">Menu</a> - provides a QML API for native platform menu popups.</li>
- <li><a href="https://doc.qt.io/qt-6/qml-qt-labs-platform-menubar.html">MenuBar</a> - provides a QML API for native platform menubars.</li>
- <li><a href="https://doc.qt.io/qt-6/qml-qt-labs-platform-menuitem.html">MenuItem</a> - provides a QML API for native platform menu items.</li>
- <li><a href="https://doc.qt.io/qt-6/qml-qt-labs-platform-filedialog.html">FileDialog</a> - provides a QML API for native platform file dialogs.</li>
- <li><a href="https://doc.qt.io/qt-6/qml-qt-labs-platform-fontdialog.html">FontDialog</a> - provides a QML API for native platform font dialogs.</li>
- <li><a href="https://doc.qt.io/qt-6/qml-qt-labs-platform-colordialog.html">ColorDialog</a> - provides a QML API for native platform color dialogs.</li>
- <li><a href="https://doc.qt.io/qt-6/qml-qt-labs-platform-messagedialog.html">MessageDialog</a> - provides a QML API for native platform message dialogs.</li>
- </ul>
</body>
</html>
diff --git a/examples/quickcontrols/texteditor/texteditor.pro b/examples/quickcontrols/texteditor/texteditor.pro
index 5f021865d3..c224fe6c41 100644
--- a/examples/quickcontrols/texteditor/texteditor.pro
+++ b/examples/quickcontrols/texteditor/texteditor.pro
@@ -1,16 +1,11 @@
TEMPLATE = app
TARGET = texteditor
QT += quick quickcontrols2
-qtHaveModule(widgets): QT += widgets
cross_compile: DEFINES += QT_EXTRA_FILE_SELECTOR=\\\"touch\\\"
-HEADERS += \
- documenthandler.h
-
SOURCES += \
- texteditor.cpp \
- documenthandler.cpp
+ texteditor.cpp
OTHER_FILES += \
qml/*.qml