diff options
Diffstat (limited to 'examples/quickcontrols/texteditor')
-rw-r--r-- | examples/quickcontrols/texteditor/CMakeLists.txt | 40 | ||||
-rw-r--r-- | examples/quickcontrols/texteditor/doc/src/qtquickcontrols-texteditor.qdoc | 181 | ||||
-rw-r--r-- | examples/quickcontrols/texteditor/documenthandler.cpp | 348 | ||||
-rw-r--r-- | examples/quickcontrols/texteditor/documenthandler.h | 121 | ||||
-rw-r--r-- | examples/quickcontrols/texteditor/example.md | 10 | ||||
-rw-r--r-- | examples/quickcontrols/texteditor/fonts/fontello.ttf | bin | 10208 -> 19044 bytes | |||
-rw-r--r-- | examples/quickcontrols/texteditor/images/qt-logo.png | bin | 4039 -> 3255 bytes | |||
-rw-r--r-- | examples/quickcontrols/texteditor/qml/+touch/texteditor.qml | 130 | ||||
-rw-r--r-- | examples/quickcontrols/texteditor/qml/texteditor.qml | 349 | ||||
-rw-r--r-- | examples/quickcontrols/texteditor/texteditor.cpp | 12 | ||||
-rw-r--r-- | examples/quickcontrols/texteditor/texteditor.html | 27 | ||||
-rw-r--r-- | examples/quickcontrols/texteditor/texteditor.pro | 7 |
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 Binary files differindex f02b44ec12..2db6e2bc4b 100644 --- a/examples/quickcontrols/texteditor/fonts/fontello.ttf +++ b/examples/quickcontrols/texteditor/fonts/fontello.ttf diff --git a/examples/quickcontrols/texteditor/images/qt-logo.png b/examples/quickcontrols/texteditor/images/qt-logo.png Binary files differindex 2ebc01aafa..63d0f222d8 100644 --- a/examples/quickcontrols/texteditor/images/qt-logo.png +++ b/examples/quickcontrols/texteditor/images/qt-logo.png 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 |