Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qquicktextedit.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qquicktextedit_p.h"
8#include "qquickwindow.h"
11
12#include <QtCore/qmath.h>
13#include <QtGui/qguiapplication.h>
14#include <QtGui/qevent.h>
15#include <QtGui/qpainter.h>
16#include <QtGui/qtextobject.h>
17#include <QtGui/qtexttable.h>
18#include <QtQml/qqmlinfo.h>
19#include <QtQuick/qsgsimplerectnode.h>
20
21#include <private/qqmlglobal_p.h>
22#include <private/qqmlproperty_p.h>
23#include <private/qtextengine_p.h>
24#include <private/qsgadaptationlayer_p.h>
25#include <QtQuick/private/qquickpixmapcache_p.h>
26
27#if QT_CONFIG(accessibility)
28#include <private/qquickaccessibleattached_p.h>
29#endif
30
31#include "qquicktextdocument.h"
32
33#include <algorithm>
34
36
37Q_STATIC_LOGGING_CATEGORY(lcTextEdit, "qt.quick.textedit")
38
39using namespace Qt::StringLiterals;
40
98// This is a pretty arbitrary figure. The idea is that we don't want to break down the document
99// into text nodes corresponding to a text block each so that the glyph node grouping doesn't become pointless.
100static const int nodeBreakingSize = 300;
101
102#if !defined(QQUICKTEXT_LARGETEXT_THRESHOLD)
103 #define QQUICKTEXT_LARGETEXT_THRESHOLD 10000
104#endif
105// if QString::size() > largeTextSizeThreshold, we render more often, but only visible lines
107
108namespace {
109 class RootNode : public QSGTransformNode
110 {
111 public:
112 RootNode() : cursorNode(nullptr), frameDecorationsNode(nullptr)
113 { }
114
115 void resetFrameDecorations(QSGInternalTextNode* newNode)
116 {
117 if (frameDecorationsNode) {
118 removeChildNode(frameDecorationsNode);
119 delete frameDecorationsNode;
120 }
121 frameDecorationsNode = newNode;
123 }
124
125 void resetCursorNode(QSGInternalRectangleNode* newNode)
126 {
127 if (cursorNode)
128 removeChildNode(cursorNode);
129 delete cursorNode;
130 cursorNode = newNode;
131 if (cursorNode) {
132 appendChildNode(cursorNode);
133 cursorNode->setFlag(QSGNode::OwnedByParent);
134 }
135 }
136
137 QSGInternalRectangleNode *cursorNode;
138 QSGInternalTextNode* frameDecorationsNode;
139
140 };
141}
142
145{
146 Q_D(QQuickTextEdit);
147 d->init();
148}
149
151{
152 Q_D(QQuickTextEdit);
153 qDeleteAll(d->pixmapsInProgress);
154}
155
157: QQuickImplicitSizeItem(dd, parent)
158{
159 Q_D(QQuickTextEdit);
160 d->init();
161}
162
164{
165 Q_D(const QQuickTextEdit);
166 if (!d->textCached && isComponentComplete()) {
167 QQuickTextEditPrivate *d = const_cast<QQuickTextEditPrivate *>(d_func());
168#if QT_CONFIG(texthtmlparser)
169 if (d->richText)
170 d->text = d->control->toHtml();
171 else
172#endif
173#if QT_CONFIG(textmarkdownwriter)
174 if (d->markdownText)
175 d->text = d->control->toMarkdown();
176 else
177#endif
178 d->text = d->control->toPlainText();
179 d->textCached = true;
180 }
181 return d->text;
182}
183
421{
422 Q_D(QQuickTextEdit);
423 if (QQuickTextEdit::text() == text)
424 return;
425
426 d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(text));
427 d->markdownText = d->format == MarkdownText;
428 if (!isComponentComplete()) {
429 d->text = text;
430 } else if (d->richText) {
431#if QT_CONFIG(texthtmlparser)
432 d->control->setHtml(text);
433#else
434 d->control->setPlainText(text);
435#endif
436 } else if (d->markdownText) {
437 d->control->setMarkdownText(text);
438 } else {
439 d->control->setPlainText(text);
440 }
442}
443
445{
446 QMetaObject::invokeMethod(this, &QQuickTextEdit::q_invalidate);
447}
448
449void QQuickTextEdit::q_invalidate()
450{
451 Q_D(QQuickTextEdit);
452 if (isComponentComplete()) {
453 if (d->document != nullptr)
454 d->document->markContentsDirty(0, d->document->characterCount());
455 invalidateFontCaches();
457 update();
458 }
459}
460
474{
475 Q_D(const QQuickTextEdit);
476 return d->control->preeditText();
477}
478
534{
535 Q_D(const QQuickTextEdit);
536 return d->format;
537}
538
540{
541 Q_D(QQuickTextEdit);
542 if (format == d->format)
543 return;
544
545 auto mightBeRichText = [this]() {
546 return Qt::mightBeRichText(text());
547 };
548
549 auto findSourceFormat = [d, mightBeRichText](Qt::TextFormat detectedFormat) {
550 if (d->format == PlainText)
551 return PlainText;
552 if (d->richText) return RichText;
553 if (d->markdownText) return MarkdownText;
554 if (detectedFormat == Qt::AutoText && mightBeRichText())
555 return RichText;
556 return PlainText;
557 };
558
559 auto findDestinationFormat = [format, mightBeRichText](Qt::TextFormat detectedFormat, TextFormat sourceFormat) {
560 if (format == AutoText) {
561 if (detectedFormat == Qt::MarkdownText || (detectedFormat == Qt::AutoText && sourceFormat == MarkdownText))
562 return MarkdownText;
563 if (detectedFormat == Qt::RichText || (detectedFormat == Qt::AutoText && (sourceFormat == RichText || mightBeRichText())))
564 return RichText;
565 return PlainText; // fallback
566 }
567 return format;
568 };
569
570 bool textCachedChanged = false;
571 bool converted = false;
572
573 if (isComponentComplete()) {
574 Qt::TextFormat detectedFormat = Qt::AutoText; // default if we don't know
575 if (d->quickDocument) {
576 // If QQuickTextDocument is in use, content can be loaded from a file,
577 // and then mime type detection overrides mightBeRichText().
578 detectedFormat = QQuickTextDocumentPrivate::get(d->quickDocument)->detectedFormat;
579 }
580
581 const TextFormat sourceFormat = findSourceFormat(detectedFormat);
582 const TextFormat destinationFormat = findDestinationFormat(detectedFormat, sourceFormat);
583
584 d->richText = destinationFormat == RichText;
585 d->markdownText = destinationFormat == MarkdownText;
586
587 // If converting between markdown and HTML, avoid using cached text: have QTD re-generate it
588 if (format != PlainText && (sourceFormat != destinationFormat)) {
589 d->textCached = false;
590 textCachedChanged = true;
591 }
592
593 switch (destinationFormat) {
594 case PlainText:
595#if QT_CONFIG(texthtmlparser)
596 if (sourceFormat == RichText) {
597 // If rich or unknown text was loaded and now the user wants plain text, get the raw HTML.
598 // But if we didn't set textCached to false above, assume d->text already contains HTML.
599 // This will allow the user to see the actual HTML they loaded (rather than Qt regenerating crufty HTML).
600 d->control->setPlainText(d->textCached ? d->text : d->control->toHtml());
601 converted = true;
602 }
603#endif
604#if QT_CONFIG(textmarkdownwriter) && QT_CONFIG(textmarkdownreader)
605 if (sourceFormat == MarkdownText) {
606 // If markdown or unknown text was loaded and now the user wants plain text, get the raw Markdown.
607 // But if we didn't set textCached to false above, assume d->text already contains markdown.
608 // This will allow the user to see the actual markdown they loaded.
609 d->control->setPlainText(d->textCached ? d->text : d->control->toMarkdown());
610 converted = true;
611 }
612#endif
613 break;
614 case RichText:
615#if QT_CONFIG(texthtmlparser)
616 switch (sourceFormat) {
617 case MarkdownText:
618 // If markdown was loaded and now the user wants HTML, convert markdown to HTML.
619 d->control->setHtml(d->control->toHtml());
620 converted = true;
621 break;
622 case PlainText:
623 // If plain text was loaded and now the user wants HTML, interpret plain text as HTML.
624 // But if we didn't set textCached to false above, assume d->text already contains HTML.
625 d->control->setHtml(d->textCached ? d->text : d->control->toPlainText());
626 converted = true;
627 break;
628 case AutoText:
629 case RichText: // nothing to do
630 break;
631 }
632#endif
633 break;
634 case MarkdownText:
635#if QT_CONFIG(textmarkdownwriter) && QT_CONFIG(textmarkdownreader)
636 switch (sourceFormat) {
637 case RichText:
638 // If HTML was loaded and now the user wants markdown, convert HTML to markdown.
639 d->control->setMarkdownText(d->control->toMarkdown());
640 converted = true;
641 break;
642 case PlainText:
643 // If plain text was loaded and now the user wants markdown, interpret plain text as markdown.
644 // But if we didn't set textCached to false above, assume d->text already contains markdown.
645 d->control->setMarkdownText(d->textCached ? d->text : d->control->toPlainText());
646 converted = true;
647 break;
648 case AutoText:
649 case MarkdownText: // nothing to do
650 break;
651 }
652#endif
653 break;
654 case AutoText: // nothing to do
655 break;
656 }
657
658 if (converted)
659 updateSize();
660 } else {
661 d->richText = format == RichText || (format == AutoText && (d->richText || mightBeRichText()));
662 d->markdownText = format == MarkdownText;
663 }
664
665 qCDebug(lcTextEdit) << d->format << "->" << format
666 << "rich?" << d->richText << "md?" << d->markdownText
667 << "converted?" << converted << "cache invalidated?" << textCachedChanged;
668
669 d->format = format;
670 d->control->setAcceptRichText(d->format != PlainText);
671 emit textFormatChanged(d->format);
672 if (textCachedChanged)
674}
675
702{
703 Q_D(const QQuickTextEdit);
704 return d->renderType;
705}
706
708{
709 Q_D(QQuickTextEdit);
710 if (d->renderType == renderType)
711 return;
712
713 d->renderType = renderType;
715 d->updateDefaultTextOption();
716
718 updateSize();
719}
720
722{
723 Q_D(const QQuickTextEdit);
724 return d->sourceFont;
725}
726
728{
729 Q_D(QQuickTextEdit);
730 if (d->sourceFont == font)
731 return;
732
733 d->sourceFont = font;
734 QFont oldFont = d->font;
735 d->font = font;
736 if (d->font.pointSizeF() != -1) {
737 // 0.5pt resolution
738 qreal size = qRound(d->font.pointSizeF()*2.0);
739 d->font.setPointSizeF(size/2.0);
740 }
741
742 if (oldFont != d->font) {
743 d->document->setDefaultFont(d->font);
744 if (d->cursorItem) {
745 d->cursorItem->setHeight(QFontMetrics(d->font).height());
746 moveCursorDelegate();
747 }
748 updateSize();
749 updateWholeDocument();
750#if QT_CONFIG(im)
752#endif
753 }
754 emit fontChanged(d->sourceFont);
755}
756
773{
774 Q_D(const QQuickTextEdit);
775 return d->color;
776}
777
779{
780 Q_D(QQuickTextEdit);
781 if (d->color == color)
782 return;
783
784 d->color = color;
785 updateWholeDocument();
786 emit colorChanged(d->color);
787}
788
795{
796 Q_D(const QQuickTextEdit);
797 return d->selectionColor;
798}
799
801{
802 Q_D(QQuickTextEdit);
803 if (d->selectionColor == color)
804 return;
805
806 d->selectionColor = color;
807 updateWholeDocument();
808 emit selectionColorChanged(d->selectionColor);
809}
810
817{
818 Q_D(const QQuickTextEdit);
819 return d->selectedTextColor;
820}
821
823{
824 Q_D(QQuickTextEdit);
825 if (d->selectedTextColor == color)
826 return;
827
828 d->selectedTextColor = color;
829 updateWholeDocument();
830 emit selectedTextColorChanged(d->selectedTextColor);
831}
832
866{
867 Q_D(const QQuickTextEdit);
868 return d->hAlign;
869}
870
872{
873 Q_D(QQuickTextEdit);
874
875 if (d->setHAlign(align, true) && isComponentComplete()) {
876 d->updateDefaultTextOption();
877 updateSize();
878 updateWholeDocument();
879 }
880}
881
883{
884 Q_D(QQuickTextEdit);
885 d->hAlignImplicit = true;
886 if (d->determineHorizontalAlignment() && isComponentComplete()) {
887 d->updateDefaultTextOption();
888 updateSize();
889 }
890}
891
893{
894 Q_D(const QQuickTextEdit);
895 QQuickTextEdit::HAlignment effectiveAlignment = d->hAlign;
896 if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
897 switch (d->hAlign) {
899 effectiveAlignment = QQuickTextEdit::AlignRight;
900 break;
902 effectiveAlignment = QQuickTextEdit::AlignLeft;
903 break;
904 default:
905 break;
906 }
907 }
908 return effectiveAlignment;
909}
910
912{
913 Q_Q(QQuickTextEdit);
914 if (hAlign == align && !forceAlign)
915 return false;
916
917 const bool wasImplicit = hAlignImplicit;
918 const auto oldEffectiveHAlign = q->effectiveHAlign();
919
920 hAlignImplicit = !forceAlign;
921 if (hAlign != align) {
922 hAlign = align;
923 emit q->horizontalAlignmentChanged(align);
924 }
925
926 if (q->effectiveHAlign() != oldEffectiveHAlign) {
927 emit q->effectiveHorizontalAlignmentChanged();
928 return true;
929 }
930
931 if (forceAlign && wasImplicit) {
932 // QTBUG-120052 - when horizontal text alignment is set explicitly,
933 // we need notify any other controls that may depend on it, like QQuickPlaceholderText
934 emit q->effectiveHorizontalAlignmentChanged();
935 }
936 return false;
937}
938
940{
941 const QChar *character = text.constData();
942 while (!character->isNull()) {
943 switch (character->direction()) {
944 case QChar::DirL:
945 return Qt::LeftToRight;
946 case QChar::DirR:
947 case QChar::DirAL:
948 case QChar::DirAN:
949 return Qt::RightToLeft;
950 default:
951 break;
952 }
953 character++;
954 }
956}
957
959{
960 Q_Q(QQuickTextEdit);
961 if (!hAlignImplicit || !q->isComponentComplete())
962 return false;
963
965#if QT_CONFIG(im)
967 QTextBlock block = control->textCursor().block();
968 if (!block.layout())
969 return false;
971 }
973 direction = qGuiApp->inputMethod()->inputDirection();
974#endif
975
976 const auto implicitHAlign = direction == Qt::RightToLeft ?
978 return setHAlign(implicitHAlign);
979}
980
982{
983 Q_Q(QQuickTextEdit);
984 if (q->isComponentComplete()) {
987 q->updateSize();
988 emit q->effectiveHorizontalAlignmentChanged();
989 }
990 }
991}
992
994{
995 Q_Q(QQuickTextEdit);
996 qCDebug(lcVP) << q << "sees that" << transformedItem << "moved in VP" << q->clipRect();
997
998 // If there's a lot of text, and the TextEdit has been scrolled so that the viewport
999 // no longer completely covers the rendered region, we need QQuickTextEdit::updatePaintNode()
1000 // to re-iterate blocks and populate a different range.
1002 if (QQuickItem *viewport = q->viewportItem()) {
1003 QRectF vp = q->mapRectFromItem(viewport, viewport->clipRect());
1004 if (!(vp.top() > renderedRegion.top() && vp.bottom() < renderedRegion.bottom())) {
1005 qCDebug(lcVP) << "viewport" << vp << "now goes beyond rendered region" << renderedRegion << "; updating";
1006 q->updateWholeDocument();
1007 }
1008 const bool textCursorVisible = cursorVisible && q->cursorRectangle().intersects(vp);
1009 if (cursorItem)
1010 cursorItem->setVisible(textCursorVisible);
1011 else
1012 control->setCursorVisible(textCursorVisible);
1013 }
1014 }
1016}
1017
1018#if QT_CONFIG(im)
1019Qt::InputMethodHints QQuickTextEditPrivate::effectiveInputMethodHints() const
1020{
1021 return inputMethodHints | Qt::ImhMultiLine;
1022}
1023#endif
1024
1025#if QT_CONFIG(accessibility)
1026void QQuickTextEditPrivate::accessibilityActiveChanged(bool active)
1027{
1028 if (!active)
1029 return;
1030
1031 Q_Q(QQuickTextEdit);
1032 if (QQuickAccessibleAttached *accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(
1033 qmlAttachedPropertiesObject<QQuickAccessibleAttached>(q, true))) {
1034 accessibleAttached->setRole(effectiveAccessibleRole());
1035 accessibleAttached->set_readOnly(q->isReadOnly());
1036 }
1037}
1038
1039QAccessible::Role QQuickTextEditPrivate::accessibleRole() const
1040{
1041 return QAccessible::EditableText;
1042}
1043#endif
1044
1046{
1047 Q_Q(QQuickTextEdit);
1048 qreal oldPadding = q->topPadding();
1049 if (!reset || extra.isAllocated()) {
1052 }
1053 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
1054 q->updateSize();
1055 q->updateWholeDocument();
1056 emit q->topPaddingChanged();
1057 }
1058}
1059
1061{
1062 Q_Q(QQuickTextEdit);
1063 qreal oldPadding = q->leftPadding();
1064 if (!reset || extra.isAllocated()) {
1067 }
1068 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
1069 q->updateSize();
1070 q->updateWholeDocument();
1071 emit q->leftPaddingChanged();
1072 }
1073}
1074
1076{
1077 Q_Q(QQuickTextEdit);
1078 qreal oldPadding = q->rightPadding();
1079 if (!reset || extra.isAllocated()) {
1082 }
1083 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
1084 q->updateSize();
1085 q->updateWholeDocument();
1086 emit q->rightPaddingChanged();
1087 }
1088}
1089
1091{
1092 Q_Q(QQuickTextEdit);
1093 qreal oldPadding = q->bottomPadding();
1094 if (!reset || extra.isAllocated()) {
1097 }
1098 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
1099 q->updateSize();
1100 q->updateWholeDocument();
1101 emit q->bottomPaddingChanged();
1102 }
1103}
1104
1109
1111{
1112 if (!enabled)
1113 extra.value().implicitResize = false;
1114 else if (extra.isAllocated())
1115 extra->implicitResize = true;
1116}
1117
1119{
1120 Q_D(const QQuickTextEdit);
1121 return d->vAlign;
1122}
1123
1125{
1126 Q_D(QQuickTextEdit);
1127 if (alignment == d->vAlign)
1128 return;
1129 d->vAlign = alignment;
1130 d->updateDefaultTextOption();
1131 updateSize();
1132 moveCursorDelegate();
1134}
1135
1157{
1158 Q_D(const QQuickTextEdit);
1159 return d->wrapMode;
1160}
1161
1163{
1164 Q_D(QQuickTextEdit);
1165 if (mode == d->wrapMode)
1166 return;
1167 d->wrapMode = mode;
1168 d->updateDefaultTextOption();
1169 updateSize();
1171}
1172
1179{
1180 Q_D(const QQuickTextEdit);
1181 return d->lineCount;
1182}
1183
1197{
1198 Q_D(const QQuickTextEdit);
1199 // QTextDocument::characterCount() includes the terminating null character.
1200 return qMax(0, d->document->characterCount() - 1);
1201}
1202
1210{
1211 Q_D(const QQuickTextEdit);
1212 return d->contentSize.width();
1213}
1214
1222{
1223 Q_D(const QQuickTextEdit);
1224 return d->contentSize.height();
1225}
1226
1237{
1238 Q_D(const QQuickTextEdit);
1239 if (d->baseUrl.isEmpty()) {
1240 if (QQmlContext *context = qmlContext(this))
1241 const_cast<QQuickTextEditPrivate *>(d)->baseUrl = context->baseUrl();
1242 }
1243 return d->baseUrl;
1244}
1245
1247{
1248 Q_D(QQuickTextEdit);
1249 if (baseUrl() != url) {
1250 d->baseUrl = url;
1251
1252 d->document->setBaseUrl(url);
1254 }
1255}
1256
1258{
1259 if (QQmlContext *context = qmlContext(this))
1260 setBaseUrl(context->baseUrl());
1261 else
1262 setBaseUrl(QUrl());
1263}
1264
1273{
1274 Q_D(const QQuickTextEdit);
1275 QTextCursor c(d->document);
1276 c.setPosition(pos);
1277 return d->control->cursorRect(c).translated(d->xoff, d->yoff);
1278
1279}
1280
1290{
1291 Q_D(const QQuickTextEdit);
1292 x -= d->xoff;
1293 y -= d->yoff;
1294
1295 int r = d->document->documentLayout()->hitTest(QPointF(x, y), Qt::FuzzyHit);
1296#if QT_CONFIG(im)
1297 QTextCursor cursor = d->control->textCursor();
1298 if (r > cursor.position()) {
1299 // The cursor position includes positions within the preedit text, but only positions in the
1300 // same text block are offset so it is possible to get a position that is either part of the
1301 // preedit or the next text block.
1302 QTextLayout *layout = cursor.block().layout();
1303 const int preeditLength = layout
1304 ? layout->preeditAreaText().size()
1305 : 0;
1306 if (preeditLength > 0
1307 && d->document->documentLayout()->blockBoundingRect(cursor.block()).contains(x, y)) {
1308 r = r > cursor.position() + preeditLength
1309 ? r - preeditLength
1310 : cursor.position();
1311 }
1312 }
1313#endif
1314 return r;
1315}
1316
1328{
1329 Q_D(const QQuickTextEdit);
1330 if (!d->cursorSelection)
1331 d->cursorSelection = new QQuickTextSelection(const_cast<QQuickTextEdit *>(this));
1332 return d->cursorSelection;
1333}
1334
1372{
1373 //Note that this is the same as setCursorPosition but with the KeepAnchor flag set
1374 Q_D(QQuickTextEdit);
1375 QTextCursor cursor = d->control->textCursor();
1376 if (cursor.position() == pos)
1377 return;
1378 cursor.setPosition(pos, QTextCursor::KeepAnchor);
1379 d->control->setTextCursor(cursor);
1380}
1381
1383{
1384 Q_D(QQuickTextEdit);
1385 QTextCursor cursor = d->control->textCursor();
1386 if (cursor.position() == pos)
1387 return;
1388 if (mode == SelectCharacters) {
1389 cursor.setPosition(pos, QTextCursor::KeepAnchor);
1390 } else if (cursor.anchor() < pos || (cursor.anchor() == pos && cursor.position() < pos)) {
1391 if (cursor.anchor() > cursor.position()) {
1392 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1394 if (cursor.position() == cursor.anchor())
1396 else
1397 cursor.setPosition(cursor.position(), QTextCursor::MoveAnchor);
1398 } else {
1399 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1401 }
1402
1403 cursor.setPosition(pos, QTextCursor::KeepAnchor);
1405 if (cursor.position() != pos)
1407 } else if (cursor.anchor() > pos || (cursor.anchor() == pos && cursor.position() > pos)) {
1408 if (cursor.anchor() < cursor.position()) {
1409 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1411 } else {
1412 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1415 if (cursor.position() != cursor.anchor()) {
1416 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1418 }
1419 }
1420
1421 cursor.setPosition(pos, QTextCursor::KeepAnchor);
1423 if (cursor.position() != pos) {
1426 }
1427 }
1428 d->control->setTextCursor(cursor);
1429}
1430
1439{
1440 Q_D(const QQuickTextEdit);
1441 return d->cursorVisible;
1442}
1443
1445{
1446 Q_D(QQuickTextEdit);
1447 if (d->cursorVisible == on)
1448 return;
1449 d->cursorVisible = on;
1450 if (on && isComponentComplete())
1452 if (!on && !d->persistentSelection)
1453 d->control->setCursorIsFocusIndicator(true);
1454 d->control->setCursorVisible(on);
1455 emit cursorVisibleChanged(d->cursorVisible);
1456}
1457
1471{
1472 Q_D(const QQuickTextEdit);
1473 return d->control->textCursor().position();
1474}
1475
1477{
1478 Q_D(QQuickTextEdit);
1479 if (pos < 0 || pos >= d->document->characterCount()) // characterCount includes the terminating null.
1480 return;
1481 QTextCursor cursor = d->control->textCursor();
1482 if (cursor.position() == pos && cursor.anchor() == pos)
1483 return;
1484 cursor.setPosition(pos);
1485 d->control->setTextCursor(cursor);
1486 d->control->updateCursorRectangle(true);
1487}
1488
1503{
1504 Q_D(const QQuickTextEdit);
1505 return d->cursorComponent;
1506}
1507
1513
1514void QQuickTextEdit::createCursor()
1515{
1516 Q_D(QQuickTextEdit);
1517 d->cursorPending = true;
1519}
1520
1532{
1533 Q_D(const QQuickTextEdit);
1534 return d->control->textCursor().selectionStart();
1535}
1536
1548{
1549 Q_D(const QQuickTextEdit);
1550 return d->control->textCursor().selectionEnd();
1551}
1552
1568{
1569 Q_D(const QQuickTextEdit);
1570#if QT_CONFIG(texthtmlparser)
1571 return d->richText || d->markdownText
1572 ? d->control->textCursor().selectedText()
1573 : d->control->textCursor().selection().toPlainText();
1574#else
1575 return d->control->textCursor().selection().toPlainText();
1576#endif
1577}
1578
1586{
1587 Q_D(const QQuickTextEdit);
1588 return d->focusOnPress;
1589}
1590
1592{
1593 Q_D(QQuickTextEdit);
1594 if (d->focusOnPress == on)
1595 return;
1596 d->focusOnPress = on;
1597 emit activeFocusOnPressChanged(d->focusOnPress);
1598}
1599
1607{
1608 Q_D(const QQuickTextEdit);
1609 return d->persistentSelection;
1610}
1611
1613{
1614 Q_D(QQuickTextEdit);
1615 if (d->persistentSelection == on)
1616 return;
1617 d->persistentSelection = on;
1618 emit persistentSelectionChanged(d->persistentSelection);
1619}
1620
1627{
1628 Q_D(const QQuickTextEdit);
1629 return d->textMargin;
1630}
1631
1633{
1634 Q_D(QQuickTextEdit);
1635 if (d->textMargin == margin)
1636 return;
1637 d->textMargin = margin;
1638 d->document->setDocumentMargin(d->textMargin);
1639 emit textMarginChanged(d->textMargin);
1640}
1641
1679Qt::InputMethodHints QQuickTextEdit::inputMethodHints() const
1680{
1681#if !QT_CONFIG(im)
1682 return Qt::ImhNone;
1683#else
1684 Q_D(const QQuickTextEdit);
1685 return d->inputMethodHints;
1686#endif // im
1687}
1688
1689void QQuickTextEdit::setInputMethodHints(Qt::InputMethodHints hints)
1690{
1691#if !QT_CONFIG(im)
1692 Q_UNUSED(hints);
1693#else
1694 Q_D(QQuickTextEdit);
1695
1696 if (hints == d->inputMethodHints)
1697 return;
1698
1699 d->inputMethodHints = hints;
1700 updateInputMethod(Qt::ImHints);
1702#endif // im
1703}
1704
1705void QQuickTextEdit::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
1706{
1707 Q_D(QQuickTextEdit);
1708 if (!d->inLayout && ((newGeometry.width() != oldGeometry.width())
1709 || (newGeometry.height() != oldGeometry.height()))) {
1710 updateSize();
1711 updateWholeDocument();
1712 if (widthValid() || heightValid())
1713 moveCursorDelegate();
1714 }
1715 QQuickImplicitSizeItem::geometryChange(newGeometry, oldGeometry);
1716}
1717
1719{
1720 Q_D(QQuickTextEdit);
1721 Q_UNUSED(value);
1722 switch (change) {
1724 if (d->renderType == NativeRendering) {
1725 // Native rendering optimizes for a given pixel grid, so its results must not be scaled.
1726 // Text layout code respects the current device pixel ratio automatically, we only need
1727 // to rerun layout after the ratio changed.
1728 updateSize();
1729 updateWholeDocument();
1730 }
1731 break;
1732
1733 default:
1734 break;
1735 }
1737}
1738
1744{
1745 Q_D(QQuickTextEdit);
1747
1748 const QUrl url = baseUrl();
1749 const QQmlContext *context = qmlContext(this);
1750 d->document->setBaseUrl(context ? context->resolvedUrl(url) : url);
1751 if (!d->text.isEmpty()) {
1752#if QT_CONFIG(texthtmlparser)
1753 if (d->richText)
1754 d->control->setHtml(d->text);
1755 else
1756#endif
1757#if QT_CONFIG(textmarkdownreader)
1758 if (d->markdownText)
1759 d->control->setMarkdownText(d->text);
1760 else
1761#endif
1762 d->control->setPlainText(d->text);
1763 }
1764
1765 if (d->dirty) {
1766 d->determineHorizontalAlignment();
1767 d->updateDefaultTextOption();
1768 updateSize();
1769 d->dirty = false;
1770 }
1771 if (d->cursorComponent && isCursorVisible())
1773 polish();
1774
1775#if QT_CONFIG(accessibility)
1776 if (QAccessible::isActive())
1777 d->accessibilityActiveChanged(true);
1778#endif
1779}
1780
1782{
1783 Q_D(const QQuickTextEdit);
1784 return d->pixmapsInProgress.size();
1785}
1786
1802{
1803 Q_D(const QQuickTextEdit);
1804 if (d->selectByKeyboardSet)
1805 return d->selectByKeyboard;
1806 return !isReadOnly();
1807}
1808
1810{
1811 Q_D(QQuickTextEdit);
1812 bool was = selectByKeyboard();
1813 if (!d->selectByKeyboardSet || was != on) {
1814 d->selectByKeyboardSet = true;
1815 d->selectByKeyboard = on;
1816 if (on)
1817 d->control->setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByKeyboard);
1818 else
1819 d->control->setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByKeyboard);
1820 emit selectByKeyboardChanged(on);
1821 }
1822}
1823
1848{
1849 Q_D(const QQuickTextEdit);
1850 return d->selectByMouse;
1851}
1852
1854{
1855 Q_D(QQuickTextEdit);
1856 if (d->selectByMouse == on)
1857 return;
1858
1859 d->selectByMouse = on;
1860 setKeepMouseGrab(on);
1861 if (on)
1862 d->control->setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByMouse);
1863 else
1864 d->control->setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByMouse);
1865
1866#if QT_CONFIG(cursor)
1867 d->updateMouseCursorShape();
1868#endif
1870}
1871
1883{
1884 Q_D(const QQuickTextEdit);
1885 return d->mouseSelectionMode;
1886}
1887
1889{
1890 Q_D(QQuickTextEdit);
1891 if (d->mouseSelectionMode != mode) {
1892 d->mouseSelectionMode = mode;
1893 d->control->setWordSelectionEnabled(mode == SelectWords);
1895 }
1896}
1897
1907{
1908 Q_D(QQuickTextEdit);
1909 if (r == isReadOnly())
1910 return;
1911
1912#if QT_CONFIG(im)
1913 setFlag(QQuickItem::ItemAcceptsInputMethod, !r);
1914#endif
1915 Qt::TextInteractionFlags flags = Qt::LinksAccessibleByMouse;
1916 if (d->selectByMouse)
1918 if (d->selectByKeyboardSet && d->selectByKeyboard)
1920 else if (!d->selectByKeyboardSet && !r)
1922 if (!r)
1924 d->control->setTextInteractionFlags(flags);
1925 d->control->moveCursor(QTextCursor::End);
1926
1927#if QT_CONFIG(im)
1928 updateInputMethod(Qt::ImEnabled);
1929#endif
1930#if QT_CONFIG(cursor)
1931 d->updateMouseCursorShape();
1932#endif
1933 q_canPasteChanged();
1935 if (!d->selectByKeyboardSet)
1936 emit selectByKeyboardChanged(!r);
1937 if (r) {
1938 setCursorVisible(false);
1939 } else if (hasActiveFocus()) {
1940 setCursorVisible(true);
1941 }
1942
1943#if QT_CONFIG(accessibility)
1944 if (QAccessible::isActive()) {
1945 if (QQuickAccessibleAttached *accessibleAttached = QQuickAccessibleAttached::attachedProperties(this))
1946 accessibleAttached->set_readOnly(r);
1947 }
1948#endif
1949}
1950
1952{
1953 Q_D(const QQuickTextEdit);
1954 return !(d->control->textInteractionFlags() & Qt::TextEditable);
1955}
1956
1968{
1969 Q_D(const QQuickTextEdit);
1970 return d->control->cursorRect().translated(d->xoff, d->yoff);
1971}
1972
1974{
1975 Q_D(QQuickTextEdit);
1976 if (event->type() == QEvent::ShortcutOverride) {
1977 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
1978 if (event->isAccepted())
1979 return true;
1980 }
1982}
1983
1999{
2000 Q_D(const QQuickTextEdit);
2001 return d->control->overwriteMode();
2002}
2003
2005{
2006 Q_D(QQuickTextEdit);
2007 d->control->setOverwriteMode(overwrite);
2008}
2009
2015{
2016 Q_D(QQuickTextEdit);
2017 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2018 if (!event->isAccepted())
2020}
2021
2027{
2028 Q_D(QQuickTextEdit);
2029 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2030 if (!event->isAccepted())
2032}
2033
2040{
2041 Q_D(QQuickTextEdit);
2042 QTextCursor c = d->control->textCursor();
2043 c.clearSelection();
2044 d->control->setTextCursor(c);
2045}
2046
2053{
2054 Q_D(QQuickTextEdit);
2055 d->control->selectAll();
2056}
2057
2064{
2065 Q_D(QQuickTextEdit);
2066 QTextCursor c = d->control->textCursor();
2068 d->control->setTextCursor(c);
2069}
2070
2085{
2086 Q_D(QQuickTextEdit);
2087 if (start < 0 || end < 0 || start >= d->document->characterCount() || end >= d->document->characterCount())
2088 return;
2089 QTextCursor cursor = d->control->textCursor();
2090 cursor.beginEditBlock();
2091 cursor.setPosition(start, QTextCursor::MoveAnchor);
2092 cursor.setPosition(end, QTextCursor::KeepAnchor);
2093 cursor.endEditBlock();
2094 d->control->setTextCursor(cursor);
2095
2096 // QTBUG-11100
2097 updateSelection();
2098#if QT_CONFIG(im)
2099 updateInputMethod();
2100#endif
2101}
2102
2110{
2111 if (start > end) {
2112 qmlWarning(this) << "isRightToLeft(start, end) called with the end property being smaller than the start.";
2113 return false;
2114 } else {
2115 return getText(start, end).isRightToLeft();
2116 }
2117}
2118
2119#if QT_CONFIG(clipboard)
2125void QQuickTextEdit::cut()
2126{
2127 Q_D(QQuickTextEdit);
2128 d->control->cut();
2129}
2130
2136void QQuickTextEdit::copy()
2137{
2138 Q_D(QQuickTextEdit);
2139 d->control->copy();
2140}
2141
2147void QQuickTextEdit::paste()
2148{
2149 Q_D(QQuickTextEdit);
2150 d->control->paste();
2151}
2152#endif // clipboard
2153
2154
2164{
2165 Q_D(QQuickTextEdit);
2166 d->control->undo();
2167}
2168
2176{
2177 Q_D(QQuickTextEdit);
2178 d->control->redo();
2179}
2180
2186{
2187 Q_D(QQuickTextEdit);
2189 setKeepMouseGrab(d->selectByMouse && isMouse);
2190 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2191 if (d->focusOnPress){
2192 bool hadActiveFocus = hasActiveFocus();
2194 // re-open input panel on press if already focused
2195#if QT_CONFIG(im)
2196 if (hasActiveFocus() && hadActiveFocus && !isReadOnly())
2197 qGuiApp->inputMethod()->show();
2198#else
2199 Q_UNUSED(hadActiveFocus);
2200#endif
2201 }
2202 if (!event->isAccepted())
2204}
2205
2211{
2212 Q_D(QQuickTextEdit);
2213 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2214
2215 if (!event->isAccepted())
2217}
2218
2224{
2225 Q_D(QQuickTextEdit);
2226 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2227 if (!event->isAccepted())
2229}
2230
2236{
2237 Q_D(QQuickTextEdit);
2238 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2239 if (!event->isAccepted())
2241}
2242
2243#if QT_CONFIG(im)
2248void QQuickTextEdit::inputMethodEvent(QInputMethodEvent *event)
2249{
2250 Q_D(QQuickTextEdit);
2251 const bool wasComposing = isInputMethodComposing();
2252 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2253 setCursorVisible(d->control->cursorVisible());
2254 if (wasComposing != isInputMethodComposing())
2256}
2257
2262QVariant QQuickTextEdit::inputMethodQuery(Qt::InputMethodQuery property, QVariant argument) const
2263{
2264 Q_D(const QQuickTextEdit);
2265
2266 QVariant v;
2267 switch (property) {
2268 case Qt::ImEnabled:
2269 v = (bool)(flags() & ItemAcceptsInputMethod);
2270 break;
2271 case Qt::ImHints:
2272 v = (int)d->effectiveInputMethodHints();
2273 break;
2275 v = QQuickItem::inputMethodQuery(property);
2276 break;
2277 case Qt::ImReadOnly:
2278 v = isReadOnly();
2279 break;
2280 default:
2281 if (property == Qt::ImCursorPosition && !argument.isNull())
2282 argument = QVariant(argument.toPointF() - QPointF(d->xoff, d->yoff));
2283 v = d->control->inputMethodQuery(property, argument);
2285 v = QVariant(v.toRectF().translated(d->xoff, d->yoff));
2286 break;
2287 }
2288 return v;
2289}
2290
2295QVariant QQuickTextEdit::inputMethodQuery(Qt::InputMethodQuery property) const
2296{
2297 return inputMethodQuery(property, QVariant());
2298}
2299#endif // im
2300
2301void QQuickTextEdit::triggerPreprocess()
2302{
2303 Q_D(QQuickTextEdit);
2304 if (d->updateType == QQuickTextEditPrivate::UpdateNone)
2306 polish();
2307 update();
2308}
2309
2315QVariant QQuickTextEdit::loadResource(int type, const QUrl &source)
2316{
2317 Q_D(QQuickTextEdit);
2318 const QUrl url = d->document->baseUrl().resolved(source);
2319 if (url.isLocalFile()) {
2320 // qmlWarning if the file doesn't exist (because QTextDocument::loadResource() can't do that)
2322 if (!fi.exists())
2323 qmlWarning(this) << "Cannot open: " << url.toString();
2324 // let QTextDocument::loadResource() handle local file loading
2325 return {};
2326 }
2327
2328 // see if we already started a load job
2329 auto existingJobIter = std::find_if(
2330 d->pixmapsInProgress.cbegin(), d->pixmapsInProgress.cend(),
2331 [&url](const auto *job) { return job->url() == url; } );
2332 if (existingJobIter != d->pixmapsInProgress.cend()) {
2333 const QQuickPixmap *job = *existingJobIter;
2334 if (job->isError()) {
2335 qmlWarning(this) << job->error();
2336 d->pixmapsInProgress.erase(existingJobIter);
2337 delete job;
2338 return QImage();
2339 } else {
2340 qCDebug(lcTextEdit) << "already downloading" << url;
2341 // existing job: return a null variant if it's not done yet
2342 return job->isReady() ? job->image() : QVariant();
2343 }
2344 }
2345
2346 // not found: start a new load job
2347 qCDebug(lcTextEdit) << "loading" << source << "resolved" << url
2348 << "type" << static_cast<QTextDocument::ResourceType>(type);
2350 Q_ASSERT(context);
2351 // don't cache it in QQuickPixmapCache, because it's cached in QTextDocumentPrivate::cachedResources
2352 QQuickPixmap *p = new QQuickPixmap(context->engine(), url, QQuickPixmap::Options{});
2353 p->connectFinished(this, SLOT(resourceRequestFinished()));
2354 d->pixmapsInProgress.append(p);
2355 // the new job is probably not done; return a null variant if the caller should poll again
2356 return p->isReady() ? p->image() : QVariant();
2357}
2358
2362void QQuickTextEdit::resourceRequestFinished()
2363{
2364 Q_D(QQuickTextEdit);
2365 for (auto it = d->pixmapsInProgress.cbegin(); it != d->pixmapsInProgress.cend(); ++it) {
2366 auto *job = *it;
2367 if (job->isError()) {
2368 // get QTextDocument::loadResource() to call QQuickTextEdit::loadResource() again, to return the placeholder
2369 qCDebug(lcTextEdit) << "failed to load (error)" << job->url();
2370 d->document->resource(QTextDocument::ImageResource, job->url());
2371 // that will call QQuickTextEdit::loadResource() which will delete the job;
2372 // so leave it in pixmapsInProgress for now, and stop this loop
2373 break;
2374 } else if (job->isReady()) {
2375 // get QTextDocument::loadResource() to call QQuickTextEdit::loadResource() again, and cache the result
2376 auto res = d->document->resource(QTextDocument::ImageResource, job->url());
2377 // If QTextDocument::resource() returned a valid variant, it's been cached too. Either way, the job is done.
2378 qCDebug(lcTextEdit) << (res.isValid() ? "done downloading" : "failed to load") << job->url() << job->rect();
2379 d->pixmapsInProgress.erase(it);
2380 delete job;
2381 break;
2382 }
2383 }
2384 if (d->pixmapsInProgress.isEmpty()) {
2385 invalidate();
2386 updateSize();
2387 q_invalidate();
2388 }
2389}
2390
2393
2394static inline bool operator<(const TextNode &n1, const TextNode &n2)
2395{
2396 return n1.startPos() < n2.startPos();
2397}
2398
2399static inline void updateNodeTransform(QSGInternalTextNode *node, const QPointF &topLeft)
2400{
2402 transformMatrix.translate(topLeft.x(), topLeft.y());
2404}
2405
2412void QQuickTextEdit::invalidateFontCaches()
2413{
2414 Q_D(QQuickTextEdit);
2415 if (d->document == nullptr)
2416 return;
2417
2418 QTextBlock block;
2419 for (block = d->document->firstBlock(); block.isValid(); block = block.next()) {
2420 if (block.layout() != nullptr && block.layout()->engine() != nullptr)
2421 block.layout()->engine()->resetFontEngineCache();
2422 }
2423}
2424
2425QTextDocument *QQuickTextEdit::document() const
2426{
2427 Q_D(const QQuickTextEdit);
2428 return d->document;
2429}
2430
2431void QQuickTextEdit::setDocument(QTextDocument *doc)
2432{
2433 Q_D(QQuickTextEdit);
2434 if (d->ownsDocument)
2435 delete d->document;
2436 d->document = doc;
2437 d->ownsDocument = false;
2438 d->control->setDocument(doc);
2439 q_textChanged();
2440}
2441
2442inline void resetEngine(QQuickTextNodeEngine *engine, const QColor& textColor, const QColor& selectedTextColor, const QColor& selectionColor)
2443{
2445 engine->setTextColor(textColor);
2446 engine->setSelectedTextColor(selectedTextColor);
2447 engine->setSelectionColor(selectionColor);
2448}
2449
2451{
2452 Q_UNUSED(updatePaintNodeData);
2453 Q_D(QQuickTextEdit);
2454
2455 if (d->updateType != QQuickTextEditPrivate::UpdatePaintNode
2456 && d->updateType != QQuickTextEditPrivate::UpdateAll
2457 && oldNode != nullptr) {
2458 // Update done in preprocess() in the nodes
2460 return oldNode;
2461 }
2462
2463 if (!oldNode || d->updateType == QQuickTextEditPrivate::UpdateAll) {
2464 delete oldNode;
2465 oldNode = nullptr;
2466
2467 // If we had any QSGInternalTextNode node references, they were deleted along with the root node
2468 // But here we must delete the Node structures in textNodeMap
2469 d->textNodeMap.clear();
2470 }
2471
2473
2474 RootNode *rootNode = static_cast<RootNode *>(oldNode);
2475 TextNodeIterator nodeIterator = d->textNodeMap.begin();
2476 std::optional<int> firstPosAcrossAllNodes;
2477 if (nodeIterator != d->textNodeMap.end())
2478 firstPosAcrossAllNodes = nodeIterator->startPos();
2479
2480 while (nodeIterator != d->textNodeMap.end() && !nodeIterator->dirty())
2481 ++nodeIterator;
2482
2484 QQuickTextNodeEngine frameDecorationsEngine;
2485
2486 if (!oldNode || nodeIterator < d->textNodeMap.end() || d->textNodeMap.isEmpty()) {
2487
2488 if (!oldNode)
2489 rootNode = new RootNode;
2490
2491 int firstDirtyPos = 0;
2492 if (nodeIterator != d->textNodeMap.end()) {
2493 firstDirtyPos = nodeIterator->startPos();
2494 // ### this could be optimized if the first and last dirty nodes are not connected
2495 // as the intermediate text nodes would usually only need to be transformed differently.
2496 QSGInternalTextNode *firstCleanNode = nullptr;
2497 auto it = d->textNodeMap.constEnd();
2498 while (it != nodeIterator) {
2499 --it;
2500 if (it->dirty())
2501 break;
2502 firstCleanNode = it->textNode();
2503 }
2504 do {
2505 rootNode->removeChildNode(nodeIterator->textNode());
2506 delete nodeIterator->textNode();
2507 nodeIterator = d->textNodeMap.erase(nodeIterator);
2508 } while (nodeIterator != d->textNodeMap.constEnd() && nodeIterator->textNode() != firstCleanNode);
2509 }
2510
2511 // If there's a lot of text, insert only the range of blocks that can possibly be visible within the viewport.
2513 if (flags().testFlag(QQuickItem::ItemObservesViewport)) {
2514 viewport = clipRect();
2515 qCDebug(lcVP) << "text viewport" << viewport;
2516 }
2517
2518 // FIXME: the text decorations could probably be handled separately (only updated for affected textFrames)
2519 rootNode->resetFrameDecorations(d->createTextNode());
2520 resetEngine(&frameDecorationsEngine, d->color, d->selectedTextColor, d->selectionColor);
2521
2522 QSGInternalTextNode *node = nullptr;
2523
2524 int currentNodeSize = 0;
2525 int nodeStart = firstDirtyPos;
2526 QPointF basePosition(d->xoff, d->yoff);
2527 QMatrix4x4 basePositionMatrix;
2528 basePositionMatrix.translate(basePosition.x(), basePosition.y());
2529 rootNode->setMatrix(basePositionMatrix);
2530
2531 QPointF nodeOffset;
2532 const TextNode firstCleanNode = (nodeIterator != d->textNodeMap.end()) ? *nodeIterator
2533 : TextNode();
2534
2535 QList<QTextFrame *> frames;
2536 frames.append(d->document->rootFrame());
2537
2538
2539 d->firstBlockInViewport = -1;
2540 d->firstBlockPastViewport = -1;
2541 int frameCount = -1;
2542 while (!frames.isEmpty()) {
2543 QTextFrame *textFrame = frames.takeFirst();
2544 ++frameCount;
2545 if (frameCount > 0)
2546 firstDirtyPos = 0;
2547 qCDebug(lcVP) << "frame" << frameCount << textFrame
2548 << "from" << positionToRectangle(textFrame->firstPosition()).topLeft()
2549 << "to" << positionToRectangle(textFrame->lastPosition()).bottomRight();
2550 frames.append(textFrame->childFrames());
2551 frameDecorationsEngine.addFrameDecorations(d->document, textFrame);
2552 resetEngine(&engine, d->color, d->selectedTextColor, d->selectionColor);
2553
2554 if (textFrame->firstPosition() > textFrame->lastPosition()
2555 && textFrame->frameFormat().position() != QTextFrameFormat::InFlow) {
2556 node = d->createTextNode();
2557 updateNodeTransform(node, d->document->documentLayout()->frameBoundingRect(textFrame).topLeft());
2558 const int pos = textFrame->firstPosition() - 1;
2559 auto *a = static_cast<QtPrivate::ProtectedLayoutAccessor *>(d->document->documentLayout());
2560 QTextCharFormat format = a->formatAccessor(pos);
2561 QTextBlock block = textFrame->firstCursorPosition().block();
2562 nodeOffset = d->document->documentLayout()->blockBoundingRect(block).topLeft();
2563 bool inView = true;
2564 if (!viewport.isNull() && block.layout()) {
2565 QRectF coveredRegion = block.layout()->boundingRect().adjusted(nodeOffset.x(), nodeOffset.y(), nodeOffset.x(), nodeOffset.y());
2566 inView = coveredRegion.bottom() >= viewport.top() && coveredRegion.top() <= viewport.bottom();
2567 qCDebug(lcVP) << "non-flow frame" << coveredRegion << "in viewport?" << inView;
2568 }
2569 if (inView) {
2570 engine.setCurrentLine(block.layout()->lineForTextPosition(pos - block.position()));
2571 engine.addTextObject(block, QPointF(0, 0), format, QQuickTextNodeEngine::Unselected, d->document,
2572 pos, textFrame->frameFormat().position());
2573 }
2574 nodeStart = pos;
2575 } else {
2576 // Having nodes spanning across frame boundaries will break the current bookkeeping mechanism. We need to prevent that.
2577 QList<int> frameBoundaries;
2578 frameBoundaries.reserve(frames.size());
2579 for (QTextFrame *frame : std::as_const(frames))
2580 frameBoundaries.append(frame->firstPosition());
2581 std::sort(frameBoundaries.begin(), frameBoundaries.end());
2582
2583 QTextFrame::iterator it = textFrame->begin();
2584 while (!it.atEnd()) {
2585 QTextBlock block = it.currentBlock();
2586 if (block.position() < firstDirtyPos) {
2587 ++it;
2588 continue;
2589 }
2590
2591 if (!engine.hasContents())
2592 nodeOffset = d->document->documentLayout()->blockBoundingRect(block).topLeft();
2593
2594 bool inView = true;
2595 if (!viewport.isNull()) {
2596 QRectF coveredRegion;
2597 if (block.layout()) {
2598 coveredRegion = block.layout()->boundingRect().adjusted(nodeOffset.x(), nodeOffset.y(), nodeOffset.x(), nodeOffset.y());
2599 inView = coveredRegion.bottom() > viewport.top();
2600 }
2601 const bool potentiallyScrollingBackwards = firstPosAcrossAllNodes && *firstPosAcrossAllNodes == firstDirtyPos;
2602 if (d->firstBlockInViewport < 0 && inView && potentiallyScrollingBackwards) {
2603 // During backward scrolling, we need to iterate backwards from textNodeMap.begin() to fill the top of the viewport.
2604 if (coveredRegion.top() > viewport.top() + 1) {
2605 qCDebug(lcVP) << "checking backwards from block" << block.blockNumber() << "@" << nodeOffset.y() << coveredRegion;
2606 while (it != textFrame->begin() && it.currentBlock().layout() &&
2607 it.currentBlock().layout()->boundingRect().top() + nodeOffset.y() > viewport.top()) {
2608 nodeOffset = d->document->documentLayout()->blockBoundingRect(it.currentBlock()).topLeft();
2609 --it;
2610 }
2611 if (!it.currentBlock().layout())
2612 ++it;
2613 if (Q_LIKELY(it.currentBlock().layout())) {
2614 block = it.currentBlock();
2615 coveredRegion = block.layout()->boundingRect().adjusted(nodeOffset.x(), nodeOffset.y(), nodeOffset.x(), nodeOffset.y());
2616 firstDirtyPos = it.currentBlock().position();
2617 } else {
2618 qCWarning(lcVP) << "failed to find a text block with layout during back-scrolling";
2619 }
2620 }
2621 qCDebug(lcVP) << "first block in viewport" << block.blockNumber() << "@" << nodeOffset.y() << coveredRegion;
2622 if (block.layout())
2623 d->renderedRegion = coveredRegion;
2624 } else {
2625 if (nodeOffset.y() > viewport.bottom()) {
2626 inView = false;
2627 if (d->firstBlockInViewport >= 0 && d->firstBlockPastViewport < 0) {
2628 qCDebug(lcVP) << "first block past viewport" << viewport << block.blockNumber()
2629 << "@" << nodeOffset.y() << "total region rendered" << d->renderedRegion;
2630 d->firstBlockPastViewport = block.blockNumber();
2631 }
2632 break; // skip rest of blocks in this frame
2633 }
2634 if (inView && !block.text().isEmpty() && coveredRegion.isValid()) {
2635 d->renderedRegion = d->renderedRegion.united(coveredRegion);
2636 // In case we're going to visit more (nested) frames after this, ensure that we
2637 // don't omit any blocks that fit within the region that we claim as fully rendered.
2638 if (!frames.isEmpty())
2639 viewport = viewport.united(d->renderedRegion);
2640 }
2641 }
2642 if (inView && d->firstBlockInViewport < 0)
2643 d->firstBlockInViewport = block.blockNumber();
2644 }
2645
2646 bool createdNodeInView = false;
2647 if (inView) {
2648 if (!engine.hasContents()) {
2649 if (node && !node->parent())
2650 d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart);
2651 node = d->createTextNode();
2652 createdNodeInView = true;
2653 updateNodeTransform(node, nodeOffset);
2654 nodeStart = block.position();
2655 }
2656 engine.addTextBlock(d->document, block, -nodeOffset, d->color, QColor(), selectionStart(), selectionEnd() - 1);
2657 currentNodeSize += block.length();
2658 }
2659
2660 if ((it.atEnd()) || block.next().position() >= firstCleanNode.startPos())
2661 break; // last node that needed replacing or last block of the frame
2662 QList<int>::const_iterator lowerBound = std::lower_bound(frameBoundaries.constBegin(), frameBoundaries.constEnd(), block.next().position());
2663 if (node && (currentNodeSize > nodeBreakingSize || lowerBound == frameBoundaries.constEnd() || *lowerBound > nodeStart)) {
2664 currentNodeSize = 0;
2665 if (!node->parent())
2666 d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart);
2667 if (!createdNodeInView)
2668 node = d->createTextNode();
2669 resetEngine(&engine, d->color, d->selectedTextColor, d->selectionColor);
2670 nodeStart = block.next().position();
2671 }
2672 ++it;
2673 } // loop over blocks in frame
2674 }
2675 if (Q_LIKELY(node && !node->parent()))
2676 d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart);
2677 }
2678 frameDecorationsEngine.addToSceneGraph(rootNode->frameDecorationsNode, QQuickText::Normal, QColor());
2679 // Now prepend the frame decorations since we want them rendered first, with the text nodes and cursor in front.
2680 rootNode->prependChildNode(rootNode->frameDecorationsNode);
2681
2682 Q_ASSERT(nodeIterator == d->textNodeMap.end()
2683 || (nodeIterator->textNode() == firstCleanNode.textNode()
2684 && nodeIterator->startPos() == firstCleanNode.startPos()));
2685 // Update the position of the subsequent text blocks.
2686 if (firstCleanNode.textNode() != nullptr) {
2687 QPointF oldOffset = firstCleanNode.textNode()->matrix().map(QPointF(0,0));
2688 QPointF currentOffset = d->document->documentLayout()->blockBoundingRect(
2689 d->document->findBlock(firstCleanNode.startPos())).topLeft();
2690 QPointF delta = currentOffset - oldOffset;
2691 while (nodeIterator != d->textNodeMap.end()) {
2692 QMatrix4x4 transformMatrix = nodeIterator->textNode()->matrix();
2693 transformMatrix.translate(delta.x(), delta.y());
2694 nodeIterator->textNode()->setMatrix(transformMatrix);
2695 ++nodeIterator;
2696 }
2697
2698 }
2699
2700 // Since we iterate over blocks from different text frames that are potentially not sorted
2701 // we need to ensure that our list of nodes is sorted again:
2702 std::sort(d->textNodeMap.begin(), d->textNodeMap.end());
2703 }
2704
2705 if (d->cursorComponent == nullptr) {
2707 if (!isReadOnly() && d->cursorVisible && d->control->cursorOn() && d->control->cursorVisible())
2708 cursor = d->sceneGraphContext()->createInternalRectangleNode(d->control->cursorRect(), d->color);
2709 rootNode->resetCursorNode(cursor);
2710 }
2711
2712 invalidateFontCaches();
2713
2714 return rootNode;
2715}
2716
2718{
2719 invalidateFontCaches();
2720}
2721
2729{
2730 Q_D(const QQuickTextEdit);
2731 if (!d->canPasteValid) {
2732 const_cast<QQuickTextEditPrivate *>(d)->canPaste = d->control->canPaste();
2733 const_cast<QQuickTextEditPrivate *>(d)->canPasteValid = true;
2734 }
2735 return d->canPaste;
2736}
2737
2746{
2747 Q_D(const QQuickTextEdit);
2748 return d->document->isUndoAvailable();
2749}
2750
2759{
2760 Q_D(const QQuickTextEdit);
2761 return d->document->isRedoAvailable();
2762}
2763
2777{
2778#if !QT_CONFIG(im)
2779 return false;
2780#else
2781 Q_D(const QQuickTextEdit);
2782 return d->control->hasImState();
2783#endif // im
2784}
2785
2787 : explicitTopPadding(false)
2788 , explicitLeftPadding(false)
2789 , explicitRightPadding(false)
2790 , explicitBottomPadding(false)
2791 , implicitResize(true)
2792{
2793}
2794
2796{
2797 Q_Q(QQuickTextEdit);
2798
2799#if QT_CONFIG(clipboard)
2800 if (QGuiApplication::clipboard()->supportsSelection())
2801 q->setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton);
2802 else
2803#endif
2804 q->setAcceptedMouseButtons(Qt::LeftButton);
2805
2806#if QT_CONFIG(im)
2807 q->setFlag(QQuickItem::ItemAcceptsInputMethod);
2808#endif
2810
2811 q->setAcceptHoverEvents(true);
2812
2813 document = new QTextDocument(q);
2814 ownsDocument = true;
2815 auto *imageHandler = new QQuickTextImageHandler(document);
2817
2820 control->setAcceptRichText(false);
2822 q->setKeepMouseGrab(true);
2823
2824 qmlobject_connect(control, QQuickTextControl, SIGNAL(updateCursorRequest()), q, QQuickTextEdit, SLOT(updateCursor()));
2825 qmlobject_connect(control, QQuickTextControl, SIGNAL(selectionChanged()), q, QQuickTextEdit, SIGNAL(selectedTextChanged()));
2826 qmlobject_connect(control, QQuickTextControl, SIGNAL(selectionChanged()), q, QQuickTextEdit, SLOT(updateSelection()));
2827 qmlobject_connect(control, QQuickTextControl, SIGNAL(cursorPositionChanged()), q, QQuickTextEdit, SLOT(updateSelection()));
2828 qmlobject_connect(control, QQuickTextControl, SIGNAL(cursorPositionChanged()), q, QQuickTextEdit, SIGNAL(cursorPositionChanged()));
2829 qmlobject_connect(control, QQuickTextControl, SIGNAL(cursorRectangleChanged()), q, QQuickTextEdit, SLOT(moveCursorDelegate()));
2831 qmlobject_connect(control, QQuickTextControl, SIGNAL(overwriteModeChanged(bool)), q, QQuickTextEdit, SIGNAL(overwriteModeChanged(bool)));
2833 qmlobject_connect(control, QQuickTextControl, SIGNAL(preeditTextChanged()), q, QQuickTextEdit, SIGNAL(preeditTextChanged()));
2834#if QT_CONFIG(clipboard)
2835 qmlobject_connect(QGuiApplication::clipboard(), QClipboard, SIGNAL(dataChanged()), q, QQuickTextEdit, SLOT(q_canPasteChanged()));
2836#endif
2837 qmlobject_connect(document, QTextDocument, SIGNAL(undoAvailable(bool)), q, QQuickTextEdit, SIGNAL(canUndoChanged()));
2838 qmlobject_connect(document, QTextDocument, SIGNAL(redoAvailable(bool)), q, QQuickTextEdit, SIGNAL(canRedoChanged()));
2839 QObject::connect(document, &QTextDocument::contentsChange, q, &QQuickTextEdit::q_contentsChange);
2841 QObject::connect(control, &QQuickTextControl::linkHovered, q, &QQuickTextEdit::q_linkHovered);
2842 QObject::connect(control, &QQuickTextControl::markerHovered, q, &QQuickTextEdit::q_markerHovered);
2843
2846 document->setUndoRedoEnabled(false); // flush undo buffer.
2849 document->setModified(false); // we merely changed some defaults: no edits worth saving yet
2850 q->updateSize();
2851#if QT_CONFIG(cursor)
2852 updateMouseCursorShape();
2853#endif
2855}
2856
2858{
2859 Q_Q(QQuickTextEdit);
2860 if (!q->isReadOnly() && q->hasActiveFocus() && qGuiApp)
2862}
2863
2864void QQuickTextEdit::q_textChanged()
2865{
2866 Q_D(QQuickTextEdit);
2867 d->textCached = false;
2868 for (QTextBlock it = d->document->begin(); it != d->document->end(); it = it.next()) {
2869 d->contentDirection = d->textDirection(it.text());
2870 if (d->contentDirection != Qt::LayoutDirectionAuto)
2871 break;
2872 }
2873 d->determineHorizontalAlignment();
2874 d->updateDefaultTextOption();
2875 updateSize();
2876
2877 markDirtyNodesForRange(0, d->document->characterCount(), 0);
2878 if (isComponentComplete()) {
2879 polish();
2881 update();
2882 }
2883
2884 emit textChanged();
2885}
2886
2887void QQuickTextEdit::markDirtyNodesForRange(int start, int end, int charDelta)
2888{
2889 Q_D(QQuickTextEdit);
2890 if (start == end)
2891 return;
2892
2893 TextNode dummyNode(start);
2894
2895 const TextNodeIterator textNodeMapBegin = d->textNodeMap.begin();
2896 const TextNodeIterator textNodeMapEnd = d->textNodeMap.end();
2897
2898 TextNodeIterator it = std::lower_bound(textNodeMapBegin, textNodeMapEnd, dummyNode);
2899 // qLowerBound gives us the first node past the start of the affected portion, rewind to the first node
2900 // that starts at the last position before the edit position. (there might be several because of images)
2901 if (it != textNodeMapBegin) {
2902 --it;
2903 TextNode otherDummy(it->startPos());
2904 it = std::lower_bound(textNodeMapBegin, textNodeMapEnd, otherDummy);
2905 }
2906
2907 // mark the affected nodes as dirty
2908 while (it != textNodeMapEnd) {
2909 if (it->startPos() <= end)
2910 it->setDirty();
2911 else if (charDelta)
2912 it->moveStartPos(charDelta);
2913 else
2914 return;
2915 ++it;
2916 }
2917}
2918
2919void QQuickTextEdit::q_contentsChange(int pos, int charsRemoved, int charsAdded)
2920{
2921 Q_D(QQuickTextEdit);
2922
2923 const int editRange = pos + qMax(charsAdded, charsRemoved);
2924 const int delta = charsAdded - charsRemoved;
2925
2926 markDirtyNodesForRange(pos, editRange, delta);
2927
2928 if (isComponentComplete()) {
2929 polish();
2931 update();
2932 }
2933}
2934
2935void QQuickTextEdit::moveCursorDelegate()
2936{
2937 Q_D(QQuickTextEdit);
2938#if QT_CONFIG(im)
2939 updateInputMethod();
2940#endif
2941 emit cursorRectangleChanged();
2942 if (!d->cursorItem)
2943 return;
2944 QRectF cursorRect = cursorRectangle();
2945 d->cursorItem->setX(cursorRect.x());
2946 d->cursorItem->setY(cursorRect.y());
2947 d->cursorItem->setHeight(cursorRect.height());
2948}
2949
2950void QQuickTextEdit::updateSelection()
2951{
2952 Q_D(QQuickTextEdit);
2953
2954 // No need for node updates when we go from an empty selection to another empty selection
2955 if (d->control->textCursor().hasSelection() || d->hadSelection) {
2956 markDirtyNodesForRange(qMin(d->lastSelectionStart, d->control->textCursor().selectionStart()), qMax(d->control->textCursor().selectionEnd(), d->lastSelectionEnd), 0);
2957 if (isComponentComplete()) {
2958 polish();
2960 update();
2961 }
2962 }
2963
2964 d->hadSelection = d->control->textCursor().hasSelection();
2965
2966 if (d->lastSelectionStart != d->control->textCursor().selectionStart()) {
2967 d->lastSelectionStart = d->control->textCursor().selectionStart();
2968 emit selectionStartChanged();
2969 }
2970 if (d->lastSelectionEnd != d->control->textCursor().selectionEnd()) {
2971 d->lastSelectionEnd = d->control->textCursor().selectionEnd();
2972 emit selectionEndChanged();
2973 }
2974}
2975
2977{
2978 Q_D(const QQuickTextEdit);
2979 QRectF r(
2980 QQuickTextUtil::alignedX(d->contentSize.width(), width(), effectiveHAlign()),
2981 d->yoff,
2982 d->contentSize.width(),
2983 d->contentSize.height());
2984
2985 int cursorWidth = 1;
2986 if (d->cursorItem)
2987 cursorWidth = 0;
2988 else if (!d->document->isEmpty())
2989 cursorWidth += 3;// ### Need a better way of accounting for space between char and cursor
2990
2991 // Could include font max left/right bearings to either side of rectangle.
2992 r.setRight(r.right() + cursorWidth);
2993
2994 return r;
2995}
2996
2998{
2999 Q_D(const QQuickTextEdit);
3001 int cursorWidth = 1;
3002 if (d->cursorItem)
3003 cursorWidth = d->cursorItem->width();
3004 if (!d->document->isEmpty())
3005 cursorWidth += 3;// ### Need a better way of accounting for space between char and cursor
3006
3007 // Could include font max left/right bearings to either side of rectangle.
3008
3009 r.setRight(r.right() + cursorWidth);
3010 return r;
3011}
3012
3014{
3015 Q_Q(const QQuickTextEdit);
3016 if (!requireImplicitWidth) {
3017 // We don't calculate implicitWidth unless it is required.
3018 // We need to force a size update now to ensure implicitWidth is calculated
3019 const_cast<QQuickTextEditPrivate*>(this)->requireImplicitWidth = true;
3020 const_cast<QQuickTextEdit*>(q)->updateSize();
3021 }
3022 return implicitWidth;
3023}
3024
3025//### we should perhaps be a bit smarter here -- depending on what has changed, we shouldn't
3026// need to do all the calculations each time
3027void QQuickTextEdit::updateSize()
3028{
3029 Q_D(QQuickTextEdit);
3030 if (!isComponentComplete()) {
3031 d->dirty = true;
3032 return;
3033 }
3034
3035 // ### assumes that if the width is set, the text will fill to edges
3036 // ### (unless wrap is false, then clipping will occur)
3037 if (widthValid()) {
3038 if (!d->requireImplicitWidth) {
3040 // if the implicitWidth is used, then updateSize() has already been called (recursively)
3041 if (d->requireImplicitWidth)
3042 return;
3043 }
3044 if (d->requireImplicitWidth) {
3045 d->document->setTextWidth(-1);
3046 const qreal naturalWidth = d->document->idealWidth();
3047 const bool wasInLayout = d->inLayout;
3048 d->inLayout = true;
3049 if (d->isImplicitResizeEnabled())
3050 setImplicitWidth(naturalWidth + leftPadding() + rightPadding());
3051 d->inLayout = wasInLayout;
3052 if (d->inLayout) // probably the result of a binding loop, but by letting it
3053 return; // get this far we'll get a warning to that effect.
3054 }
3055 const qreal newTextWidth = width() - leftPadding() - rightPadding();
3056 if (d->document->textWidth() != newTextWidth)
3057 d->document->setTextWidth(newTextWidth);
3058 } else if (d->wrapMode == NoWrap) {
3059 // normally, if explicit width is not set, we should call setTextWidth(-1) here,
3060 // as we don't need to fit the text to any fixed width. But because of some bug
3061 // in QTextDocument it also breaks RTL text alignment, so we use "idealWidth" instead.
3062 const qreal newTextWidth = d->document->idealWidth();
3063 if (d->document->textWidth() != newTextWidth)
3064 d->document->setTextWidth(newTextWidth);
3065 } else {
3066 d->document->setTextWidth(-1);
3067 }
3068
3069 QFontMetricsF fm(d->font);
3070 const qreal newHeight = d->document->isEmpty() ? qCeil(fm.height()) : d->document->size().height();
3071 const qreal newWidth = d->document->idealWidth();
3072
3073 if (d->isImplicitResizeEnabled()) {
3074 // ### Setting the implicitWidth triggers another updateSize(), and unless there are bindings nothing has changed.
3075 if (!widthValid())
3076 setImplicitSize(newWidth + leftPadding() + rightPadding(), newHeight + topPadding() + bottomPadding());
3077 else
3078 setImplicitHeight(newHeight + topPadding() + bottomPadding());
3079 }
3080
3081 d->xoff = leftPadding() + qMax(qreal(0), QQuickTextUtil::alignedX(d->document->size().width(), width() - leftPadding() - rightPadding(), effectiveHAlign()));
3082 d->yoff = topPadding() + QQuickTextUtil::alignedY(d->document->size().height(), height() - topPadding() - bottomPadding(), d->vAlign);
3083
3084 qreal baseline = fm.ascent();
3085 QTextBlock firstBlock = d->document->firstBlock();
3086 if (firstBlock.isValid() && firstBlock.layout() != nullptr && firstBlock.lineCount() > 0) {
3087 QTextLine firstLine = firstBlock.layout()->lineAt(0);
3088 if (firstLine.isValid())
3089 baseline = firstLine.ascent();
3090 }
3091
3092 setBaselineOffset(baseline + d->yoff + d->textMargin);
3093
3094 QSizeF size(newWidth, newHeight);
3095 if (d->contentSize != size) {
3096 d->contentSize = size;
3097 // Note: inResize is a bitfield so QScopedValueRollback can't be used here
3098 const bool wasInResize = d->inResize;
3099 d->inResize = true;
3100 if (!wasInResize)
3101 emit contentSizeChanged();
3102 d->inResize = wasInResize;
3103 updateTotalLines();
3104 }
3105}
3106
3107void QQuickTextEdit::updateWholeDocument()
3108{
3109 Q_D(QQuickTextEdit);
3110 if (!d->textNodeMap.isEmpty()) {
3111 for (TextNode &node : d->textNodeMap)
3112 node.setDirty();
3113 }
3114
3115 if (isComponentComplete()) {
3116 polish();
3118 update();
3119 }
3120}
3121
3122void QQuickTextEdit::invalidateBlock(const QTextBlock &block)
3123{
3124 Q_D(QQuickTextEdit);
3125 markDirtyNodesForRange(block.position(), block.position() + block.length(), 0);
3126
3127 if (isComponentComplete()) {
3128 polish();
3130 update();
3131 }
3132}
3133
3134void QQuickTextEdit::updateCursor()
3135{
3136 Q_D(QQuickTextEdit);
3137 if (isComponentComplete() && isVisible()) {
3138 polish();
3140 update();
3141 }
3142}
3143
3144void QQuickTextEdit::q_linkHovered(const QString &link)
3145{
3146 Q_D(QQuickTextEdit);
3147 emit linkHovered(link);
3148#if QT_CONFIG(cursor)
3149 if (link.isEmpty()) {
3150 d->updateMouseCursorShape();
3151 } else if (cursor().shape() != Qt::PointingHandCursor) {
3153 }
3154#endif
3155}
3156
3157void QQuickTextEdit::q_markerHovered(bool hovered)
3158{
3159 Q_D(QQuickTextEdit);
3160#if QT_CONFIG(cursor)
3161 if (!hovered) {
3162 d->updateMouseCursorShape();
3163 } else if (cursor().shape() != Qt::PointingHandCursor) {
3165 }
3166#endif
3167}
3168
3169void QQuickTextEdit::q_updateAlignment()
3170{
3171 Q_D(QQuickTextEdit);
3172 if (d->determineHorizontalAlignment()) {
3173 d->updateDefaultTextOption();
3174 d->xoff = qMax(qreal(0), QQuickTextUtil::alignedX(d->document->size().width(), width(), effectiveHAlign()));
3175 moveCursorDelegate();
3176 }
3177}
3178
3179void QQuickTextEdit::updateTotalLines()
3180{
3181 Q_D(QQuickTextEdit);
3182
3183 int subLines = 0;
3184
3185 for (QTextBlock it = d->document->begin(); it != d->document->end(); it = it.next()) {
3186 QTextLayout *layout = it.layout();
3187 if (!layout)
3188 continue;
3189 subLines += layout->lineCount()-1;
3190 }
3191
3192 int newTotalLines = d->document->lineCount() + subLines;
3193 if (d->lineCount != newTotalLines) {
3194 d->lineCount = newTotalLines;
3195 emit lineCountChanged();
3196 }
3197}
3198
3200{
3201 Q_Q(QQuickTextEdit);
3203 const Qt::Alignment oldAlignment = opt.alignment();
3204 Qt::LayoutDirection oldTextDirection = opt.textDirection();
3205
3206 QQuickTextEdit::HAlignment horizontalAlignment = q->effectiveHAlign();
3208 if (horizontalAlignment == QQuickTextEdit::AlignLeft)
3209 horizontalAlignment = QQuickTextEdit::AlignRight;
3210 else if (horizontalAlignment == QQuickTextEdit::AlignRight)
3211 horizontalAlignment = QQuickTextEdit::AlignLeft;
3212 }
3213 if (!hAlignImplicit)
3214 opt.setAlignment((Qt::Alignment)(int)(horizontalAlignment | vAlign));
3215 else
3216 opt.setAlignment(Qt::Alignment(vAlign));
3217
3218#if QT_CONFIG(im)
3220 opt.setTextDirection(qGuiApp->inputMethod()->inputDirection());
3221 } else
3222#endif
3223 {
3224 opt.setTextDirection(contentDirection);
3225 }
3226
3227 QTextOption::WrapMode oldWrapMode = opt.wrapMode();
3228 opt.setWrapMode(QTextOption::WrapMode(wrapMode));
3229
3230 bool oldUseDesignMetrics = opt.useDesignMetrics();
3231 opt.setUseDesignMetrics(renderType != QQuickTextEdit::NativeRendering);
3232
3233 if (oldWrapMode != opt.wrapMode() || oldAlignment != opt.alignment()
3234 || oldTextDirection != opt.textDirection()
3235 || oldUseDesignMetrics != opt.useDesignMetrics()) {
3237 }
3238}
3239
3241{
3243 switch (quickDocument->status()) {
3246 switch (QQuickTextDocumentPrivate::get(quickDocument)->detectedFormat) {
3247 case Qt::RichText:
3249 markdownText = false;
3250 break;
3251 case Qt::MarkdownText:
3252 richText = false;
3254 break;
3255 case Qt::PlainText:
3256 richText = false;
3257 markdownText = false;
3258 break;
3259 case Qt::AutoText: // format not detected
3260 break;
3261 }
3262 break;
3263 default:
3264 break;
3265 }
3266}
3267
3274
3281
3283{
3284 Q_Q(QQuickTextEdit);
3285 bool focus = event->type() == QEvent::FocusIn;
3286 if (!q->isReadOnly())
3287 q->setCursorVisible(focus);
3289 if (focus) {
3290 q->q_updateAlignment();
3291#if QT_CONFIG(im)
3292 if (focusOnPress && !q->isReadOnly())
3293 qGuiApp->inputMethod()->show();
3294 q->connect(QGuiApplication::inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
3295 q, SLOT(q_updateAlignment()));
3296#endif
3297 } else {
3298#if QT_CONFIG(im)
3299 q->disconnect(QGuiApplication::inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
3300 q, SLOT(q_updateAlignment()));
3301#endif
3302 if (event->reason() != Qt::ActiveWindowFocusReason
3303 && event->reason() != Qt::PopupFocusReason
3306 q->deselect();
3307
3308 emit q->editingFinished();
3309 }
3310}
3311
3313{
3314 engine->addToSceneGraph(node, QQuickText::Normal, QColor());
3315 it = textNodeMap.insert(it, TextNode(startPos, node));
3316 ++it;
3317 root->appendChildNode(node);
3319}
3320
3329
3330void QQuickTextEdit::q_canPasteChanged()
3331{
3332 Q_D(QQuickTextEdit);
3333 bool old = d->canPaste;
3334 d->canPaste = d->control->canPaste();
3335 bool changed = old!=d->canPaste || !d->canPasteValid;
3336 d->canPasteValid = true;
3337 if (changed)
3338 emit canPasteChanged();
3339}
3340
3350{
3351 Q_D(const QQuickTextEdit);
3352 start = qBound(0, start, d->document->characterCount() - 1);
3353 end = qBound(0, end, d->document->characterCount() - 1);
3354 QTextCursor cursor(d->document);
3355 cursor.setPosition(start, QTextCursor::MoveAnchor);
3356 cursor.setPosition(end, QTextCursor::KeepAnchor);
3357#if QT_CONFIG(texthtmlparser)
3358 return d->richText || d->markdownText
3359 ? cursor.selectedText()
3360 : cursor.selection().toPlainText();
3361#else
3362 return cursor.selection().toPlainText();
3363#endif
3364}
3365
3375{
3376 Q_D(const QQuickTextEdit);
3377
3378 start = qBound(0, start, d->document->characterCount() - 1);
3379 end = qBound(0, end, d->document->characterCount() - 1);
3380
3381 QTextCursor cursor(d->document);
3382 cursor.setPosition(start, QTextCursor::MoveAnchor);
3383 cursor.setPosition(end, QTextCursor::KeepAnchor);
3384
3385 if (d->richText) {
3386#if QT_CONFIG(texthtmlparser)
3387 return cursor.selection().toHtml();
3388#else
3389 return cursor.selection().toPlainText();
3390#endif
3391 } else if (d->markdownText) {
3392#if QT_CONFIG(textmarkdownwriter)
3393 return cursor.selection().toMarkdown();
3394#else
3395 return cursor.selection().toPlainText();
3396#endif
3397 } else {
3398 return cursor.selection().toPlainText();
3399 }
3400}
3401
3408{
3409 Q_D(QQuickTextEdit);
3410 if (position < 0 || position >= d->document->characterCount())
3411 return;
3412 QTextCursor cursor(d->document);
3413 cursor.setPosition(position);
3414 d->richText = d->richText || (d->format == AutoText && Qt::mightBeRichText(text));
3415 if (d->richText) {
3416#if QT_CONFIG(texthtmlparser)
3417 cursor.insertHtml(text);
3418#else
3419 cursor.insertText(text);
3420#endif
3421 } else if (d->markdownText) {
3422#if QT_CONFIG(textmarkdownreader)
3423 cursor.insertMarkdown(text);
3424#else
3425 cursor.insertText(text);
3426#endif
3427 } else {
3428 cursor.insertText(text);
3429 }
3430 d->control->updateCursorRectangle(false);
3431}
3432
3440{
3441 Q_D(QQuickTextEdit);
3442 start = qBound(0, start, d->document->characterCount() - 1);
3443 end = qBound(0, end, d->document->characterCount() - 1);
3444 QTextCursor cursor(d->document);
3445 cursor.setPosition(start, QTextCursor::MoveAnchor);
3446 cursor.setPosition(end, QTextCursor::KeepAnchor);
3447 cursor.removeSelectedText();
3448 d->control->updateCursorRectangle(false);
3449}
3450
3464{
3465 Q_D(QQuickTextEdit);
3466 if (!d->quickDocument) {
3467 d->quickDocument = new QQuickTextDocument(this);
3468 connect(d->quickDocument, &QQuickTextDocument::statusChanged, d->quickDocument,
3469 [d]() { d->onDocumentStatusChanged(); } );
3470 }
3471 return d->quickDocument;
3472}
3473
3479
3480#if QT_CONFIG(cursor)
3481void QQuickTextEditPrivate::updateMouseCursorShape()
3482{
3483 Q_Q(QQuickTextEdit);
3484 q->setCursor(q->isReadOnly() && !q->selectByMouse() ? Qt::ArrowCursor : Qt::IBeamCursor);
3485}
3486#endif
3487
3518{
3519 Q_D(const QQuickTextEdit);
3520 if (const_cast<QQuickTextEditPrivate *>(d)->isLinkHoveredConnected()) {
3521 return d->control->hoveredLink();
3522 } else {
3523#if QT_CONFIG(cursor)
3524 if (QQuickWindow *wnd = window()) {
3525 QPointF pos = QCursor::pos(wnd->screen()) - wnd->position() - mapToScene(QPointF(0, 0));
3526 return d->control->anchorAt(pos);
3527 }
3528#endif // cursor
3529 }
3530 return QString();
3531}
3532
3534{
3535 Q_D(QQuickTextEdit);
3536 if (d->isLinkHoveredConnected())
3537 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
3538 event->ignore();
3539}
3540
3542{
3543 Q_D(QQuickTextEdit);
3544 if (d->isLinkHoveredConnected())
3545 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
3546 event->ignore();
3547}
3548
3550{
3551 Q_D(QQuickTextEdit);
3552 if (d->isLinkHoveredConnected())
3553 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
3554 event->ignore();
3555}
3556
3566void QQuickTextEdit::append(const QString &text)
3567{
3568 Q_D(QQuickTextEdit);
3569 QTextCursor cursor(d->document);
3570 cursor.beginEditBlock();
3571 cursor.movePosition(QTextCursor::End);
3572
3573 if (!d->document->isEmpty())
3574 cursor.insertBlock();
3575
3576 if (d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(text))) {
3577#if QT_CONFIG(texthtmlparser)
3578 cursor.insertHtml(text);
3579#else
3580 cursor.insertText(text);
3581#endif
3582 } else if (d->format == MarkdownText) {
3583#if QT_CONFIG(textmarkdownreader)
3584 cursor.insertMarkdown(text);
3585#else
3586 cursor.insertText(text);
3587#endif
3588 } else {
3589 cursor.insertText(text);
3590 }
3591
3592 cursor.endEditBlock();
3593 d->control->updateCursorRectangle(false);
3594}
3595
3605QString QQuickTextEdit::linkAt(qreal x, qreal y) const
3606{
3607 Q_D(const QQuickTextEdit);
3608 return d->control->anchorAt(QPointF(x + topPadding(), y + leftPadding()));
3609}
3610
3623{
3624 Q_D(const QQuickTextEdit);
3625 return d->padding();
3626}
3627
3629{
3630 Q_D(QQuickTextEdit);
3631 if (qFuzzyCompare(d->padding(), padding))
3632 return;
3633
3634 d->extra.value().padding = padding;
3635 updateSize();
3636 if (isComponentComplete()) {
3638 update();
3639 }
3640 emit paddingChanged();
3641 if (!d->extra.isAllocated() || !d->extra->explicitTopPadding)
3642 emit topPaddingChanged();
3643 if (!d->extra.isAllocated() || !d->extra->explicitLeftPadding)
3644 emit leftPaddingChanged();
3645 if (!d->extra.isAllocated() || !d->extra->explicitRightPadding)
3646 emit rightPaddingChanged();
3647 if (!d->extra.isAllocated() || !d->extra->explicitBottomPadding)
3648 emit bottomPaddingChanged();
3649}
3650
3652{
3653 setPadding(0);
3654}
3655
3657{
3658 Q_D(const QQuickTextEdit);
3659 if (d->extra.isAllocated() && d->extra->explicitTopPadding)
3660 return d->extra->topPadding;
3661 return d->padding();
3662}
3663
3665{
3666 Q_D(QQuickTextEdit);
3667 d->setTopPadding(padding);
3668}
3669
3671{
3672 Q_D(QQuickTextEdit);
3673 d->setTopPadding(0, true);
3674}
3675
3677{
3678 Q_D(const QQuickTextEdit);
3679 if (d->extra.isAllocated() && d->extra->explicitLeftPadding)
3680 return d->extra->leftPadding;
3681 return d->padding();
3682}
3683
3685{
3686 Q_D(QQuickTextEdit);
3687 d->setLeftPadding(padding);
3688}
3689
3691{
3692 Q_D(QQuickTextEdit);
3693 d->setLeftPadding(0, true);
3694}
3695
3697{
3698 Q_D(const QQuickTextEdit);
3699 if (d->extra.isAllocated() && d->extra->explicitRightPadding)
3700 return d->extra->rightPadding;
3701 return d->padding();
3702}
3703
3705{
3706 Q_D(QQuickTextEdit);
3707 d->setRightPadding(padding);
3708}
3709
3711{
3712 Q_D(QQuickTextEdit);
3713 d->setRightPadding(0, true);
3714}
3715
3717{
3718 Q_D(const QQuickTextEdit);
3719 if (d->extra.isAllocated() && d->extra->explicitBottomPadding)
3720 return d->extra->bottomPadding;
3721 return d->padding();
3722}
3723
3725{
3726 Q_D(QQuickTextEdit);
3727 d->setBottomPadding(padding);
3728}
3729
3731{
3732 Q_D(QQuickTextEdit);
3733 d->setBottomPadding(0, true);
3734}
3735
3745{
3746 Q_D(const QQuickTextEdit);
3747 return d->document->defaultTextOption().tabStopDistance();
3748}
3749
3751{
3752 Q_D(QQuickTextEdit);
3753 QTextOption textOptions = d->document->defaultTextOption();
3754 if (textOptions.tabStopDistance() == distance)
3755 return;
3756
3757 textOptions.setTabStopDistance(distance);
3758 d->document->setDefaultTextOption(textOptions);
3759 emit tabStopDistanceChanged(distance);
3760}
3761
3773void QQuickTextEdit::clear()
3774{
3775 Q_D(QQuickTextEdit);
3776 d->resetInputMethod();
3777 d->control->clear();
3778}
3779
3780#ifndef QT_NO_DEBUG_STREAM
3782{
3783 QDebugStateSaver saver(debug);
3784 debug.space();
3785 debug << "Node(startPos:" << n.m_startPos << "dirty:" << n.m_dirty << n.m_node << ')';
3786 return debug;
3787}
3788#endif
3789
3790#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
3791void QQuickTextEdit::setOldSelectionDefault()
3792{
3793 Q_D(QQuickTextEdit);
3794 d->selectByMouse = false;
3795 setKeepMouseGrab(false);
3796 d->control->setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByMouse);
3797 d->control->setTouchDragSelectionEnabled(true);
3798 qCDebug(lcTextEdit, "pre-6.4 behavior chosen: selectByMouse defaults false; if enabled, touchscreen acts like a mouse");
3799}
3800
3801// TODO in 6.7.0: remove the note about versions prior to 6.4 in selectByMouse() documentation
3802QQuickPre64TextEdit::QQuickPre64TextEdit(QQuickItem *parent)
3804{
3805 setOldSelectionDefault();
3806}
3807#endif
3808
3810
3811#include "moc_qquicktextedit_p.cpp"
void registerHandler(int objectType, QObject *component)
Registers the given component as a handler for items of the given objectType.
void updateBlock(const QTextBlock &block)
\inmodule QtCore
The QClipboard class provides access to the window system clipboard.
Definition qclipboard.h:20
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
static QPoint pos()
Returns the position of the cursor (hot spot) of the primary screen in global screen coordinates.
Definition qcursor.cpp:188
\inmodule QtCore
\inmodule QtCore
\inmodule QtCore
Definition qcoreevent.h:45
@ ShortcutOverride
Definition qcoreevent.h:158
@ FocusIn
Definition qcoreevent.h:66
The QFocusEvent class contains event parameters for widget focus events.
Definition qevent.h:470
\reentrant \inmodule QtGui
\reentrant \inmodule QtGui
int height() const
Returns the height of the font.
\reentrant
Definition qfont.h:22
static QClipboard * clipboard()
Returns the object for interacting with the clipboard.
static QInputMethod * inputMethod()
returns the input method.
\inmodule QtGui
Definition qevent.h:246
\inmodule QtGui
Definition qimage.h:37
The QInputMethodEvent class provides parameters for input method events.
Definition qevent.h:625
The QKeyEvent class describes a key event.
Definition qevent.h:424
static constexpr Policy Preferred
bool isAllocated() const
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
Definition qmatrix4x4.h:25
void translate(const QVector3D &vector)
Multiplies this matrix by another that translates coordinates by the components of vector.
\inmodule QtGui
Definition qevent.h:196
QObject * parent
Definition qobject.h:73
static QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer< Func1 >::Object *sender, Func1 signal, const typename QtPrivate::FunctionPointer< Func2 >::Object *receiverPrivate, Func2 slot, Qt::ConnectionType type=Qt::AutoConnection)
Definition qobject_p.h:299
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
virtual bool event(QEvent *event)
This virtual function receives events to an object and should return true if the event e was recogniz...
Definition qobject.cpp:1389
\inmodule QtCore\reentrant
Definition qpoint.h:217
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:343
constexpr qreal y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:348
The QQmlComponent class encapsulates a QML component definition.
The QQmlContext class defines a context within a QML engine.
Definition qqmlcontext.h:25
static QString urlToLocalFileOrQrc(const QString &)
If url is a local file returns a path suitable for passing to \l{QFile}.
Definition qqmlfile.cpp:742
virtual void componentComplete()=0
Invoked after the root component that caused this instantiation has completed construction.
static bool isEventFromMouseOrTouchpad(const QPointerEvent *ev)
virtual bool transformChanged(QQuickItem *transformedItem)
QSGRenderContext * sceneGraphRenderContext() const
QSGContext * sceneGraphContext() const
void setSizePolicy(const QLayoutPolicy::Policy &horizontalPolicy, const QLayoutPolicy::Policy &verticalPolicy)
bool widthValid() const
QQuickWindow * window
virtual void implicitWidthChanged()
QQuickAnchorLine baseline() const
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:63
virtual void focusOutEvent(QFocusEvent *)
This event handler can be reimplemented in a subclass to receive focus-out events for an item.
virtual void mouseReleaseEvent(QMouseEvent *event)
This event handler can be reimplemented in a subclass to receive mouse release events for an item.
Flags flags() const
Returns the item flags for this item.
QQuickItem * viewportItem() const
If the \l ItemObservesViewport flag is set, returns the nearest parent with the \l ItemIsViewport fla...
virtual void mouseDoubleClickEvent(QMouseEvent *event)
This event handler can be reimplemented in a subclass to receive mouse double-click events for an ite...
void setFlag(Flag flag, bool enabled=true)
Enables the specified flag for this item if enabled is true; if enabled is false, the flag is disable...
virtual void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
virtual void keyPressEvent(QKeyEvent *event)
This event handler can be reimplemented in a subclass to receive key press events for an item.
bool hasActiveFocus() const
virtual void mousePressEvent(QMouseEvent *event)
This event handler can be reimplemented in a subclass to receive mouse press events for an item.
void setVisible(bool)
virtual void itemChange(ItemChange, const ItemChangeData &)
Called when change occurs for this item.
bool isComponentComplete() const
Returns true if construction of the QML component is complete; otherwise returns false.
bool heightValid() const
Returns whether the height property has been set explicitly.
void setKeepMouseGrab(bool)
Sets whether the mouse input should remain exclusively with this item.
Q_INVOKABLE void forceActiveFocus()
\qmlmethod point QtQuick::Item::mapToItem(Item item, real x, real y) \qmlmethod point QtQuick::Item::...
bool widthValid() const
Returns whether the width property has been set explicitly.
virtual void keyReleaseEvent(QKeyEvent *event)
This event handler can be reimplemented in a subclass to receive key release events for an item.
virtual QRectF clipRect() const
Returns the rectangular area within this item that is currently visible in \l viewportItem(),...
ItemChange
Used in conjunction with QQuickItem::itemChange() to notify the item about certain types of changes.
Definition qquickitem.h:144
@ ItemDevicePixelRatioHasChanged
Definition qquickitem.h:154
virtual void focusInEvent(QFocusEvent *)
This event handler can be reimplemented in a subclass to receive focus-in events for an item.
friend class QQuickAccessibleAttached
Definition qquickitem.h:479
@ ItemObservesViewport
Definition qquickitem.h:138
void update()
Schedules a call to updatePaintNode() for this item.
void polish()
Schedules a polish event for this item.
virtual void mouseMoveEvent(QMouseEvent *event)
This event handler can be reimplemented in a subclass to receive mouse move events for an item.
const QUrl & url() const
QImage image() const
QString error() const
void setTextInteractionFlags(Qt::TextInteractionFlags flags)
void linkHovered(const QString &link)
virtual void processEvent(QEvent *e, const QTransform &transform)
void setAcceptRichText(bool accept)
void markerHovered(bool marker)
QTextCursor textCursor() const
void setCursorIsFocusIndicator(bool b)
void setCursorVisible(bool visible)
static QQuickTextDocumentPrivate * get(QQuickTextDocument *doc)
\qmltype TextDocument \instantiates QQuickTextDocument \inqmlmodule QtQuick
Status status
the status of document loading or saving
QList< Node >::iterator TextNodeIterator
QQuickTextEdit::HAlignment hAlign
void setImplicitResizeEnabled(bool enabled)
bool isImplicitResizeEnabled() const
void setRightPadding(qreal value, bool reset=false)
QLazilyAllocated< ExtraData > extra
Qt::LayoutDirection textDirection(const QString &text) const
Qt::LayoutDirection contentDirection
bool setHAlign(QQuickTextEdit::HAlignment, bool forceAlign=false)
static const int largeTextSizeThreshold
QQuickTextControl * control
qreal getImplicitWidth() const override
void setLeftPadding(qreal value, bool reset=false)
QSGInternalTextNode * createTextNode()
QQuickTextDocument * quickDocument
void addCurrentTextNodeToRoot(QQuickTextNodeEngine *, QSGTransformNode *, QSGInternalTextNode *, TextNodeIterator &, int startPos)
QQuickTextEdit::WrapMode wrapMode
bool transformChanged(QQuickItem *transformedItem) override
QQuickTextEdit::VAlignment vAlign
void handleFocusEvent(QFocusEvent *event)
QQuickTextEdit::RenderType renderType
void setBottomPadding(qreal value, bool reset=false)
void setTopPadding(qreal value, bool reset=false)
void mirrorChange() override
void setFont(const QFont &font)
void setRenderType(RenderType renderType)
void selectWord()
\qmlmethod QtQuick::TextEdit::selectWord()
void keyReleaseEvent(QKeyEvent *) override
This is an overloaded member function, provided for convenience. It differs from the above function o...
void setInputMethodHints(Qt::InputMethodHints hints)
void textChanged()
void setWrapMode(WrapMode w)
bool isReadOnly() const
bool isInputMethodComposing() const
\qmlproperty bool QtQuick::TextEdit::inputMethodComposing
void inputMethodComposingChanged()
void setCursorDelegate(QQmlComponent *)
void focusInEvent(QFocusEvent *event) override
This event handler can be reimplemented in a subclass to receive focus-in events for an item.
void hoverMoveEvent(QHoverEvent *event) override
This event handler can be reimplemented in a subclass to receive hover-move events for an item.
void setPersistentSelection(bool on)
void setSelectionColor(const QColor &c)
void setFocusOnPress(bool on)
SelectionMode mouseSelectionMode
void invalidate() override
void keyPressEvent(QKeyEvent *) override
This is an overloaded member function, provided for convenience. It differs from the above function o...
TextFormat textFormat
QQuickTextDocument * textDocument
\qmlproperty TextDocument QtQuick::TextEdit::textDocument
HAlignment hAlign() const
\qmlproperty enumeration QtQuick::TextEdit::horizontalAlignment \qmlproperty enumeration QtQuick::Tex...
void mouseMoveEvent(QMouseEvent *event) override
This is an overloaded member function, provided for convenience. It differs from the above function o...
int resourcesLoading() const
void remove(int start, int end)
\qmlmethod string QtQuick::TextEdit::remove(int start, int end)
QQuickTextSelection * cursorSelection
void setSelectByMouse(bool)
void setColor(const QColor &c)
void setMouseSelectionMode(SelectionMode mode)
QRectF clipRect() const override
Returns the rectangular area within this item that is currently visible in \l viewportItem(),...
bool event(QEvent *) override
\reimp
void componentComplete() override
Ensures any delayed caching or data loading the class needs to performed is complete.
void setRightPadding(qreal padding)
Qt::InputMethodHints inputMethodHints
void hoverLeaveEvent(QHoverEvent *event) override
This event handler can be reimplemented in a subclass to receive hover-leave events for an item.
Q_INVOKABLE int positionAt(qreal x, qreal y) const
\qmlmethod int QtQuick::TextEdit::positionAt(int x, int y)
bool focusOnPress() const
\qmlproperty bool QtQuick::TextEdit::activeFocusOnPress
void select(int start, int end)
\qmlmethod QtQuick::TextEdit::select(int start, int end)
void setLeftPadding(qreal padding)
void selectAll()
\qmlmethod QtQuick::TextEdit::selectAll()
void activeFocusOnPressChanged(bool activeFocusOnPressed)
void setOverwriteMode(bool overwrite)
void setHAlign(HAlignment align)
void selectedTextColorChanged(const QColor &color)
void setTopPadding(qreal padding)
void setCursorPosition(int pos)
QSGNode * updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData) override
Called on the render thread when it is time to sync the state of the item with the scene graph.
void mouseReleaseEvent(QMouseEvent *event) override
This is an overloaded member function, provided for convenience. It differs from the above function o...
void setPadding(qreal padding)
void insert(int position, const QString &text)
\qmlmethod QtQuick::TextEdit::insert(int position, string text)
void setTextFormat(TextFormat format)
void mouseDoubleClickEvent(QMouseEvent *event) override
This is an overloaded member function, provided for convenience. It differs from the above function o...
void textMarginChanged(qreal textMargin)
void inputMethodHintsChanged()
void updatePolish() override
This function should perform any layout as required for this item.
void renderTypeChanged()
~QQuickTextEdit() override
void readOnlyChanged(bool isReadOnly)
Q_INVOKABLE QString getFormattedText(int start, int end) const
\qmlmethod string QtQuick::TextEdit::getFormattedText(int start, int end)
QQmlComponent * cursorDelegate
void setTabStopDistance(qreal distance)
VAlignment vAlign() const
void verticalAlignmentChanged(QQuickTextEdit::VAlignment alignment)
void setSelectedTextColor(const QColor &c)
void itemChange(ItemChange change, const ItemChangeData &value) override
Called when change occurs for this item.
void cursorVisibleChanged(bool isCursorVisible)
void mousePressEvent(QMouseEvent *event) override
This is an overloaded member function, provided for convenience. It differs from the above function o...
void hoverEnterEvent(QHoverEvent *event) override
This event handler can be reimplemented in a subclass to receive hover-enter events for an item.
Q_INVOKABLE QString getText(int start, int end) const
\qmlmethod string QtQuick::TextEdit::getText(int start, int end)
void setBottomPadding(qreal padding)
bool isRightToLeft(int start, int end)
\qmlmethod QtQuick::TextEdit::isRightToLeft(int start, int end)
void selectByMouseChanged(bool selectByMouse)
void setReadOnly(bool)
\qmlproperty bool QtQuick::TextEdit::readOnly
void setSelectByKeyboard(bool)
void deselect()
\qmlmethod QtQuick::TextEdit::deselect()
void fontChanged(const QFont &font)
bool isCursorVisible() const
\qmlproperty bool QtQuick::TextEdit::cursorVisible If true the text edit shows a cursor.
void selectionColorChanged(const QColor &color)
void undo()
\qmlmethod QtQuick::TextEdit::undo()
QQuickTextEdit(QQuickItem *parent=nullptr)
void mouseSelectionModeChanged(QQuickTextEdit::SelectionMode mode)
void focusOutEvent(QFocusEvent *event) override
This event handler can be reimplemented in a subclass to receive focus-out events for an item.
void textFormatChanged(QQuickTextEdit::TextFormat textFormat)
void setVAlign(VAlignment align)
QRectF boundingRect() const override
Returns the extents of the item in its own coordinate system: a rectangle from {0,...
void setBaseUrl(const QUrl &url)
QString preeditText
\qmlproperty string QtQuick::TextEdit::preeditText \readonly
void setTextMargin(qreal margin)
Q_INVOKABLE QRectF positionToRectangle(int) const
\qmlmethod rectangle QtQuick::TextEdit::positionToRectangle(position)
HAlignment effectiveHAlign() const
void wrapModeChanged()
void baseUrlChanged()
void redo()
\qmlmethod QtQuick::TextEdit::redo()
void setCursorVisible(bool on)
Q_INVOKABLE void moveCursorSelection(int pos)
\qmlmethod QtQuick::TextEdit::moveCursorSelection(int position, SelectionMode mode)
void persistentSelectionChanged(bool isPersistentSelection)
RenderType renderType
void setText(const QString &)
\qmlproperty string QtQuick::TextEdit::font.family
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override
static qreal alignedX(qreal textWidth, qreal itemWidth, int alignment)
static void createCursor(Private *d)
static qreal alignedY(qreal textHeight, qreal itemHeight, int alignment)
static void setCursorDelegate(Private *d, QQmlComponent *delegate)
\qmltype Window \instantiates QQuickWindow \inqmlmodule QtQuick
\inmodule QtCore\reentrant
Definition qrect.h:484
constexpr qreal bottom() const noexcept
Returns the y-coordinate of the rectangle's bottom edge.
Definition qrect.h:500
constexpr qreal y() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:672
constexpr qreal height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:732
constexpr qreal width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:729
constexpr qreal x() const noexcept
Returns the x-coordinate of the rectangle's left edge.
Definition qrect.h:669
constexpr QRectF adjusted(qreal x1, qreal y1, qreal x2, qreal y2) const noexcept
Returns a new rectangle with dx1, dy1, dx2 and dy2 added respectively to the existing coordinates of ...
Definition qrect.h:813
bool intersects(const QRectF &r) const noexcept
Returns true if this rectangle intersects with the given rectangle (i.e.
Definition qrect.cpp:2271
constexpr QPointF topLeft() const noexcept
Returns the position of the rectangle's top-left corner.
Definition qrect.h:511
constexpr QPointF bottomRight() const noexcept
Returns the position of the rectangle's bottom-right corner.
Definition qrect.h:512
constexpr qreal top() const noexcept
Returns the y-coordinate of the rectangle's top edge.
Definition qrect.h:498
virtual QSGInternalTextNode * createInternalTextNode(QSGRenderContext *renderContext)
void setFiltering(QSGTexture::Filtering filtering) override
Sets the sampling mode used when scaling images that are part of the displayed text to filtering.
void setRenderType(RenderType renderType) override
Sets the type of glyph node in use to renderType.
\group qtquick-scenegraph-nodes \title Qt Quick Scene Graph Node classes
Definition qsgnode.h:37
@ OwnedByParent
Definition qsgnode.h:51
void appendChildNode(QSGNode *node)
Appends node to this node's list of children.
Definition qsgnode.cpp:396
QSGNode * parent() const
Returns the parent node of this node.
Definition qsgnode.h:93
void setFlag(Flag, bool=true)
Sets the flag f on this node if enabled is true; otherwise clears the flag.
Definition qsgnode.cpp:584
RenderType
This enum type describes type of glyph node used for rendering the text.
Definition qsgtextnode.h:29
The QSGTransformNode class implements transformations in the scene graph.
Definition qsgnode.h:241
void setMatrix(const QMatrix4x4 &matrix)
Sets this transform node's matrix to matrix.
Definition qsgnode.cpp:1160
iterator begin()
Definition qset.h:137
iterator end()
Definition qset.h:141
\inmodule QtCore
Definition qsize.h:208
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
const QChar * constData() const
Returns a pointer to the data stored in the QString.
Definition qstring.h:1246
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
bool isRightToLeft() const
Returns true if the string is read right to left.
Definition qstring.cpp:9319
\reentrant
int length() const
Returns the length of the block in characters.
int lineCount() const
int blockNumber() const
const QTextDocument * document() const
Returns the text document this text block belongs to, or \nullptr if the text block does not belong t...
bool isValid() const
Returns true if this text block is valid; otherwise returns false.
QTextBlock next() const
Returns the text block in the document after this block, or an empty text block if this is the last o...
QTextLayout * layout() const
Returns the QTextLayout that is used to lay out and display the block's contents.
int position() const
Returns the index of the block's first character within the document.
QString text() const
Returns the block's contents as plain text.
\reentrant \inmodule QtGui
Definition qtextcursor.h:30
QTextBlock block() const
Returns the block that contains the cursor.
bool hasSelection() const
Returns true if the cursor contains a selection; otherwise returns false.
\reentrant \inmodule QtGui
void setModified(bool m=true)
ResourceType
This enum describes the types of resources that can be loaded by QTextDocument's loadResource() funct...
void contentsChange(int from, int charsRemoved, int charsAdded)
This signal is emitted whenever the document's content changes; for example, when text is inserted or...
qreal idealWidth() const
void setDefaultTextOption(const QTextOption &option)
QAbstractTextDocumentLayout * documentLayout() const
Returns the document layout for this document.
QTextOption defaultTextOption() const
the default text option will be set on all \l{QTextLayout}s in the document.
void setDefaultFont(const QFont &font)
Sets the default font to use in the document layout.
void setUndoRedoEnabled(bool enable)
void setDocumentMargin(qreal margin)
QTextBlock firstBlock() const
void resetFontEngineCache()
\reentrant
Definition qtextobject.h:81
\reentrant
Definition qtextlayout.h:70
QTextLine lineForTextPosition(int pos) const
Returns the line that contains the cursor position specified by pos.
QTextEngine * engine() const
QTextLine lineAt(int i) const
Returns the {i}-th line of text in this text layout.
QString preeditAreaText() const
Returns the text that is inserted in the layout before editing occurs.
QRectF boundingRect() const
The smallest rectangle that contains all the lines in the layout.
\reentrant
\reentrant
Definition qtextoption.h:18
WrapMode
This enum describes how text is wrapped in a document.
Definition qtextoption.h:60
void setTabStopDistance(qreal tabStopDistance)
\inmodule QtCore
Definition qurl.h:94
bool isLocalFile() const
Definition qurl.cpp:3445
QString toString(FormattingOptions options=FormattingOptions(PrettyDecoded)) const
Returns a string representation of the URL.
Definition qurl.cpp:2831
\inmodule QtCore
Definition qvariant.h:65
QString text
void colorChanged()
QCursor cursor
void textChanged(const QString &newText)
qDeleteAll(list.begin(), list.end())
QSet< QString >::iterator it
uint alignment
direction
QStyleOptionButton opt
Combined button and popup list for selecting options.
Definition qcompare.h:63
InputMethodQuery
@ ImAnchorRectangle
@ ImInputItemClipRectangle
@ ImCursorPosition
@ ImReadOnly
@ ImFont
@ ImCursorRectangle
@ ImHints
@ ImEnabled
@ LeftButton
Definition qnamespace.h:58
@ MiddleButton
Definition qnamespace.h:60
@ TextSelectableByMouse
@ TextEditable
@ LinksAccessibleByMouse
@ TextSelectableByKeyboard
TextFormat
@ RichText
@ MarkdownText
@ PlainText
@ AutoText
LayoutDirection
@ LeftToRight
@ LayoutDirectionAuto
@ RightToLeft
Q_GUI_EXPORT bool mightBeRichText(QAnyStringView)
Returns true if the string text is likely to be rich text; otherwise returns false.
@ PointingHandCursor
@ ArrowCursor
@ ImhNone
@ ImhMultiLine
@ FuzzyHit
Definition qnamespace.h:203
@ PopupFocusReason
@ MouseFocusReason
@ ActiveWindowFocusReason
#define Q_LIKELY(x)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:333
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:327
#define qGuiApp
#define qCWarning(category,...)
#define qCDebug(category,...)
#define Q_STATIC_LOGGING_CATEGORY(name,...)
int qCeil(T v)
Definition qmath.h:36
QT_BEGIN_NAMESPACE constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:19
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:23
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:21
#define SLOT(a)
Definition qobjectdefs.h:52
#define SIGNAL(a)
Definition qobjectdefs.h:53
GLsizei const GLfloat * v
[13]
GLint GLint GLint GLint GLint x
[0]
GLenum mode
GLint GLsizei GLsizei height
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLboolean r
[2]
GLuint GLuint end
GLenum GLenum GLsizei const GLuint GLboolean enabled
GLsizei GLsizei GLfloat distance
GLint GLsizei width
GLuint color
[2]
GLenum type
GLbitfield flags
GLuint start
GLfloat n
GLint GLsizei GLsizei GLenum format
GLint y
GLsizei GLsizei GLchar * source
struct _cl_event * event
GLboolean reset
GLuint res
const GLubyte * c
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLfloat GLfloat p
[1]
static QMatrix4x4 transformMatrix(const QVector3D &position, const QQuaternion &rotation)
QQmlContext * qmlContext(const QObject *obj)
Definition qqml.cpp:72
#define qmlobject_connect(Sender, SenderType, Signal, Receiver, ReceiverType, Method)
Connect Signal of Sender to Method of Receiver.
#define IS_SIGNAL_CONNECTED(Sender, SenderType, Name, Arguments)
Q_QML_EXPORT QQmlInfo qmlWarning(const QObject *me)
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
static const int nodeBreakingSize
\qmlsignal QtQuick::TextEdit::linkActivated(string link)
#define QQUICKTEXT_LARGETEXT_THRESHOLD
static bool operator<(const TextNode &n1, const TextNode &n2)
static void updateNodeTransform(QSGInternalTextNode *node, const QPointF &topLeft)
QQuickTextEditPrivate::Node TextNode
void resetEngine(QQuickTextNodeEngine *engine, const QColor &textColor, const QColor &selectedTextColor, const QColor &selectionColor)
QDebug Q_QUICK_EXPORT operator<<(QDebug debug, const QQuickTextEditPrivate::Node &)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
#define Q_UNUSED(x)
double qreal
Definition qtypes.h:187
const char property[13]
Definition qwizard.cpp:100
QUrl url("example.com")
[constructor-url-reference]
QVBoxLayout * layout
item setCursor(Qt::IBeamCursor)
[1]
view viewport() -> scroll(dx, dy, deviceRect)
edit isVisible()
QFrame frame
[0]
QDBusArgument argument
QJSEngine engine
[0]
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
\threadsafe This is an overloaded member function, provided for convenience. It differs from the abov...
\inmodule QtQuick
Definition qquickitem.h:159