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
38Q_LOGGING_CATEGORY(lcTextEdit, "qt.quick.textedit")
39
40using namespace Qt::StringLiterals;
41
99// This is a pretty arbitrary figure. The idea is that we don't want to break down the document
100// into text nodes corresponding to a text block each so that the glyph node grouping doesn't become pointless.
101static const int nodeBreakingSize = 300;
102
103#if !defined(QQUICKTEXT_LARGETEXT_THRESHOLD)
104 #define QQUICKTEXT_LARGETEXT_THRESHOLD 10000
105#endif
106// if QString::size() > largeTextSizeThreshold, we render more often, but only visible lines
108
109namespace {
110 class RootNode : public QSGTransformNode
111 {
112 public:
113 RootNode() : cursorNode(nullptr), frameDecorationsNode(nullptr)
114 { }
115
116 void resetFrameDecorations(QSGInternalTextNode* newNode)
117 {
118 if (frameDecorationsNode) {
119 removeChildNode(frameDecorationsNode);
120 delete frameDecorationsNode;
121 }
122 frameDecorationsNode = newNode;
124 }
125
126 void resetCursorNode(QSGInternalRectangleNode* newNode)
127 {
128 if (cursorNode)
129 removeChildNode(cursorNode);
130 delete cursorNode;
131 cursorNode = newNode;
132 if (cursorNode) {
133 appendChildNode(cursorNode);
134 cursorNode->setFlag(QSGNode::OwnedByParent);
135 }
136 }
137
138 QSGInternalRectangleNode *cursorNode;
139 QSGInternalTextNode* frameDecorationsNode;
140
141 };
142}
143
146{
147 Q_D(QQuickTextEdit);
148 d->init();
149}
150
152{
153 Q_D(QQuickTextEdit);
154 qDeleteAll(d->pixmapsInProgress);
155}
156
158: QQuickImplicitSizeItem(dd, parent)
159{
160 Q_D(QQuickTextEdit);
161 d->init();
162}
163
165{
166 Q_D(const QQuickTextEdit);
167 if (!d->textCached && isComponentComplete()) {
168 QQuickTextEditPrivate *d = const_cast<QQuickTextEditPrivate *>(d_func());
169#if QT_CONFIG(texthtmlparser)
170 if (d->richText)
171 d->text = d->control->toHtml();
172 else
173#endif
174#if QT_CONFIG(textmarkdownwriter)
175 if (d->markdownText)
176 d->text = d->control->toMarkdown();
177 else
178#endif
179 d->text = d->control->toPlainText();
180 d->textCached = true;
181 }
182 return d->text;
183}
184
415{
416 Q_D(QQuickTextEdit);
417 if (QQuickTextEdit::text() == text)
418 return;
419
420 d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(text));
421 d->markdownText = d->format == MarkdownText;
422 if (!isComponentComplete()) {
423 d->text = text;
424 } else if (d->richText) {
425#if QT_CONFIG(texthtmlparser)
426 d->control->setHtml(text);
427#else
428 d->control->setPlainText(text);
429#endif
430 } else if (d->markdownText) {
431 d->control->setMarkdownText(text);
432 } else {
433 d->control->setPlainText(text);
434 }
436}
437
439{
440 QMetaObject::invokeMethod(this, &QQuickTextEdit::q_invalidate);
441}
442
443void QQuickTextEdit::q_invalidate()
444{
445 Q_D(QQuickTextEdit);
446 if (isComponentComplete()) {
447 if (d->document != nullptr)
448 d->document->markContentsDirty(0, d->document->characterCount());
449 invalidateFontCaches();
451 update();
452 }
453}
454
468{
469 Q_D(const QQuickTextEdit);
470 return d->control->preeditText();
471}
472
528{
529 Q_D(const QQuickTextEdit);
530 return d->format;
531}
532
534{
535 Q_D(QQuickTextEdit);
536 if (format == d->format)
537 return;
538
539 auto mightBeRichText = [this]() {
540 return Qt::mightBeRichText(text());
541 };
542
543 auto findSourceFormat = [d, mightBeRichText](Qt::TextFormat detectedFormat) {
544 if (d->format == PlainText)
545 return PlainText;
546 if (d->richText) return RichText;
547 if (d->markdownText) return MarkdownText;
548 if (detectedFormat == Qt::AutoText && mightBeRichText())
549 return RichText;
550 return PlainText;
551 };
552
553 auto findDestinationFormat = [format, mightBeRichText](Qt::TextFormat detectedFormat, TextFormat sourceFormat) {
554 if (format == AutoText) {
555 if (detectedFormat == Qt::MarkdownText || (detectedFormat == Qt::AutoText && sourceFormat == MarkdownText))
556 return MarkdownText;
557 if (detectedFormat == Qt::RichText || (detectedFormat == Qt::AutoText && (sourceFormat == RichText || mightBeRichText())))
558 return RichText;
559 return PlainText; // fallback
560 }
561 return format;
562 };
563
564 bool textCachedChanged = false;
565 bool converted = false;
566
567 if (isComponentComplete()) {
568 Qt::TextFormat detectedFormat = Qt::AutoText; // default if we don't know
569 if (d->quickDocument) {
570 // If QQuickTextDocument is in use, content can be loaded from a file,
571 // and then mime type detection overrides mightBeRichText().
572 detectedFormat = QQuickTextDocumentPrivate::get(d->quickDocument)->detectedFormat;
573 }
574
575 const TextFormat sourceFormat = findSourceFormat(detectedFormat);
576 const TextFormat destinationFormat = findDestinationFormat(detectedFormat, sourceFormat);
577
578 d->richText = destinationFormat == RichText;
579 d->markdownText = destinationFormat == MarkdownText;
580
581 // If converting between markdown and HTML, avoid using cached text: have QTD re-generate it
582 if (format != PlainText && (sourceFormat != destinationFormat)) {
583 d->textCached = false;
584 textCachedChanged = true;
585 }
586
587 switch (destinationFormat) {
588 case PlainText:
589#if QT_CONFIG(texthtmlparser)
590 if (sourceFormat == RichText) {
591 // If rich or unknown text was loaded and now the user wants plain text, get the raw HTML.
592 // But if we didn't set textCached to false above, assume d->text already contains HTML.
593 // This will allow the user to see the actual HTML they loaded (rather than Qt regenerating crufty HTML).
594 d->control->setPlainText(d->textCached ? d->text : d->control->toHtml());
595 converted = true;
596 }
597#endif
598#if QT_CONFIG(textmarkdownwriter) && QT_CONFIG(textmarkdownreader)
599 if (sourceFormat == MarkdownText) {
600 // If markdown or unknown text was loaded and now the user wants plain text, get the raw Markdown.
601 // But if we didn't set textCached to false above, assume d->text already contains markdown.
602 // This will allow the user to see the actual markdown they loaded.
603 d->control->setPlainText(d->textCached ? d->text : d->control->toMarkdown());
604 converted = true;
605 }
606#endif
607 break;
608 case RichText:
609#if QT_CONFIG(texthtmlparser)
610 switch (sourceFormat) {
611 case MarkdownText:
612 // If markdown was loaded and now the user wants HTML, convert markdown to HTML.
613 d->control->setHtml(d->control->toHtml());
614 converted = true;
615 break;
616 case PlainText:
617 // If plain text was loaded and now the user wants HTML, interpret plain text as HTML.
618 // But if we didn't set textCached to false above, assume d->text already contains HTML.
619 d->control->setHtml(d->textCached ? d->text : d->control->toPlainText());
620 converted = true;
621 break;
622 case AutoText:
623 case RichText: // nothing to do
624 break;
625 }
626#endif
627 break;
628 case MarkdownText:
629#if QT_CONFIG(textmarkdownwriter) && QT_CONFIG(textmarkdownreader)
630 switch (sourceFormat) {
631 case RichText:
632 // If HTML was loaded and now the user wants markdown, convert HTML to markdown.
633 d->control->setMarkdownText(d->control->toMarkdown());
634 converted = true;
635 break;
636 case PlainText:
637 // If plain text was loaded and now the user wants markdown, interpret plain text as markdown.
638 // But if we didn't set textCached to false above, assume d->text already contains markdown.
639 d->control->setMarkdownText(d->textCached ? d->text : d->control->toPlainText());
640 converted = true;
641 break;
642 case AutoText:
643 case MarkdownText: // nothing to do
644 break;
645 }
646#endif
647 break;
648 case AutoText: // nothing to do
649 break;
650 }
651
652 if (converted)
653 updateSize();
654 } else {
655 d->richText = format == RichText || (format == AutoText && (d->richText || mightBeRichText()));
656 d->markdownText = format == MarkdownText;
657 }
658
659 qCDebug(lcTextEdit) << d->format << "->" << format
660 << "rich?" << d->richText << "md?" << d->markdownText
661 << "converted?" << converted << "cache invalidated?" << textCachedChanged;
662
663 d->format = format;
664 d->control->setAcceptRichText(d->format != PlainText);
665 emit textFormatChanged(d->format);
666 if (textCachedChanged)
668}
669
696{
697 Q_D(const QQuickTextEdit);
698 return d->renderType;
699}
700
702{
703 Q_D(QQuickTextEdit);
704 if (d->renderType == renderType)
705 return;
706
707 d->renderType = renderType;
709 d->updateDefaultTextOption();
710
712 updateSize();
713}
714
716{
717 Q_D(const QQuickTextEdit);
718 return d->sourceFont;
719}
720
722{
723 Q_D(QQuickTextEdit);
724 if (d->sourceFont == font)
725 return;
726
727 d->sourceFont = font;
728 QFont oldFont = d->font;
729 d->font = font;
730 if (d->font.pointSizeF() != -1) {
731 // 0.5pt resolution
732 qreal size = qRound(d->font.pointSizeF()*2.0);
733 d->font.setPointSizeF(size/2.0);
734 }
735
736 if (oldFont != d->font) {
737 d->document->setDefaultFont(d->font);
738 if (d->cursorItem) {
739 d->cursorItem->setHeight(QFontMetrics(d->font).height());
740 moveCursorDelegate();
741 }
742 updateSize();
743 updateWholeDocument();
744#if QT_CONFIG(im)
746#endif
747 }
748 emit fontChanged(d->sourceFont);
749}
750
767{
768 Q_D(const QQuickTextEdit);
769 return d->color;
770}
771
773{
774 Q_D(QQuickTextEdit);
775 if (d->color == color)
776 return;
777
778 d->color = color;
779 updateWholeDocument();
780 emit colorChanged(d->color);
781}
782
789{
790 Q_D(const QQuickTextEdit);
791 return d->selectionColor;
792}
793
795{
796 Q_D(QQuickTextEdit);
797 if (d->selectionColor == color)
798 return;
799
800 d->selectionColor = color;
801 updateWholeDocument();
802 emit selectionColorChanged(d->selectionColor);
803}
804
811{
812 Q_D(const QQuickTextEdit);
813 return d->selectedTextColor;
814}
815
817{
818 Q_D(QQuickTextEdit);
819 if (d->selectedTextColor == color)
820 return;
821
822 d->selectedTextColor = color;
823 updateWholeDocument();
824 emit selectedTextColorChanged(d->selectedTextColor);
825}
826
860{
861 Q_D(const QQuickTextEdit);
862 return d->hAlign;
863}
864
866{
867 Q_D(QQuickTextEdit);
868
869 if (d->setHAlign(align, true) && isComponentComplete()) {
870 d->updateDefaultTextOption();
871 updateSize();
872 }
873}
874
876{
877 Q_D(QQuickTextEdit);
878 d->hAlignImplicit = true;
879 if (d->determineHorizontalAlignment() && isComponentComplete()) {
880 d->updateDefaultTextOption();
881 updateSize();
882 }
883}
884
886{
887 Q_D(const QQuickTextEdit);
888 QQuickTextEdit::HAlignment effectiveAlignment = d->hAlign;
889 if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
890 switch (d->hAlign) {
892 effectiveAlignment = QQuickTextEdit::AlignRight;
893 break;
895 effectiveAlignment = QQuickTextEdit::AlignLeft;
896 break;
897 default:
898 break;
899 }
900 }
901 return effectiveAlignment;
902}
903
905{
906 Q_Q(QQuickTextEdit);
907 if (hAlign == align && !forceAlign)
908 return false;
909
910 const bool wasImplicit = hAlignImplicit;
911 const auto oldEffectiveHAlign = q->effectiveHAlign();
912
913 hAlignImplicit = !forceAlign;
914 if (hAlign != align) {
915 hAlign = align;
916 emit q->horizontalAlignmentChanged(align);
917 }
918
919 if (q->effectiveHAlign() != oldEffectiveHAlign) {
920 emit q->effectiveHorizontalAlignmentChanged();
921 return true;
922 }
923
924 if (forceAlign && wasImplicit) {
925 // QTBUG-120052 - when horizontal text alignment is set explicitly,
926 // we need notify any other controls that may depend on it, like QQuickPlaceholderText
927 emit q->effectiveHorizontalAlignmentChanged();
928 }
929 return false;
930}
931
933{
934 const QChar *character = text.constData();
935 while (!character->isNull()) {
936 switch (character->direction()) {
937 case QChar::DirL:
938 return Qt::LeftToRight;
939 case QChar::DirR:
940 case QChar::DirAL:
941 case QChar::DirAN:
942 return Qt::RightToLeft;
943 default:
944 break;
945 }
946 character++;
947 }
949}
950
952{
953 Q_Q(QQuickTextEdit);
954 if (!hAlignImplicit || !q->isComponentComplete())
955 return false;
956
958#if QT_CONFIG(im)
960 QTextBlock block = control->textCursor().block();
961 if (!block.layout())
962 return false;
964 }
966 direction = qGuiApp->inputMethod()->inputDirection();
967#endif
968
969 const auto implicitHAlign = direction == Qt::RightToLeft ?
971 return setHAlign(implicitHAlign);
972}
973
975{
976 Q_Q(QQuickTextEdit);
977 if (q->isComponentComplete()) {
980 q->updateSize();
981 emit q->effectiveHorizontalAlignmentChanged();
982 }
983 }
984}
985
987{
988 Q_Q(QQuickTextEdit);
989 qCDebug(lcVP) << q << "sees that" << transformedItem << "moved in VP" << q->clipRect();
990
991 // If there's a lot of text, and the TextEdit has been scrolled so that the viewport
992 // no longer completely covers the rendered region, we need QQuickTextEdit::updatePaintNode()
993 // to re-iterate blocks and populate a different range.
995 if (QQuickItem *viewport = q->viewportItem()) {
996 QRectF vp = q->mapRectFromItem(viewport, viewport->clipRect());
997 if (!(vp.top() > renderedRegion.top() && vp.bottom() < renderedRegion.bottom())) {
998 qCDebug(lcVP) << "viewport" << vp << "now goes beyond rendered region" << renderedRegion << "; updating";
999 q->updateWholeDocument();
1000 }
1001 const bool textCursorVisible = cursorVisible && q->cursorRectangle().intersects(vp);
1002 if (cursorItem)
1003 cursorItem->setVisible(textCursorVisible);
1004 else
1005 control->setCursorVisible(textCursorVisible);
1006 }
1007 }
1009}
1010
1011#if QT_CONFIG(im)
1012Qt::InputMethodHints QQuickTextEditPrivate::effectiveInputMethodHints() const
1013{
1014 return inputMethodHints | Qt::ImhMultiLine;
1015}
1016#endif
1017
1018#if QT_CONFIG(accessibility)
1019void QQuickTextEditPrivate::accessibilityActiveChanged(bool active)
1020{
1021 if (!active)
1022 return;
1023
1024 Q_Q(QQuickTextEdit);
1025 if (QQuickAccessibleAttached *accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(
1026 qmlAttachedPropertiesObject<QQuickAccessibleAttached>(q, true))) {
1027 accessibleAttached->setRole(effectiveAccessibleRole());
1028 accessibleAttached->set_readOnly(q->isReadOnly());
1029 }
1030}
1031
1032QAccessible::Role QQuickTextEditPrivate::accessibleRole() const
1033{
1034 return QAccessible::EditableText;
1035}
1036#endif
1037
1039{
1040 Q_Q(QQuickTextEdit);
1041 qreal oldPadding = q->topPadding();
1042 if (!reset || extra.isAllocated()) {
1043 extra.value().topPadding = value;
1044 extra.value().explicitTopPadding = !reset;
1045 }
1046 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
1047 q->updateSize();
1048 q->updateWholeDocument();
1049 emit q->topPaddingChanged();
1050 }
1051}
1052
1054{
1055 Q_Q(QQuickTextEdit);
1056 qreal oldPadding = q->leftPadding();
1057 if (!reset || extra.isAllocated()) {
1058 extra.value().leftPadding = value;
1059 extra.value().explicitLeftPadding = !reset;
1060 }
1061 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
1062 q->updateSize();
1063 q->updateWholeDocument();
1064 emit q->leftPaddingChanged();
1065 }
1066}
1067
1069{
1070 Q_Q(QQuickTextEdit);
1071 qreal oldPadding = q->rightPadding();
1072 if (!reset || extra.isAllocated()) {
1073 extra.value().rightPadding = value;
1074 extra.value().explicitRightPadding = !reset;
1075 }
1076 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
1077 q->updateSize();
1078 q->updateWholeDocument();
1079 emit q->rightPaddingChanged();
1080 }
1081}
1082
1084{
1085 Q_Q(QQuickTextEdit);
1086 qreal oldPadding = q->bottomPadding();
1087 if (!reset || extra.isAllocated()) {
1088 extra.value().bottomPadding = value;
1089 extra.value().explicitBottomPadding = !reset;
1090 }
1091 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
1092 q->updateSize();
1093 q->updateWholeDocument();
1094 emit q->bottomPaddingChanged();
1095 }
1096}
1097
1099{
1100 return !extra.isAllocated() || extra->implicitResize;
1101}
1102
1104{
1105 if (!enabled)
1106 extra.value().implicitResize = false;
1107 else if (extra.isAllocated())
1108 extra->implicitResize = true;
1109}
1110
1112{
1113 Q_D(const QQuickTextEdit);
1114 return d->vAlign;
1115}
1116
1118{
1119 Q_D(QQuickTextEdit);
1120 if (alignment == d->vAlign)
1121 return;
1122 d->vAlign = alignment;
1123 d->updateDefaultTextOption();
1124 updateSize();
1125 moveCursorDelegate();
1127}
1128
1150{
1151 Q_D(const QQuickTextEdit);
1152 return d->wrapMode;
1153}
1154
1156{
1157 Q_D(QQuickTextEdit);
1158 if (mode == d->wrapMode)
1159 return;
1160 d->wrapMode = mode;
1161 d->updateDefaultTextOption();
1162 updateSize();
1164}
1165
1172{
1173 Q_D(const QQuickTextEdit);
1174 return d->lineCount;
1175}
1176
1190{
1191 Q_D(const QQuickTextEdit);
1192 // QTextDocument::characterCount() includes the terminating null character.
1193 return qMax(0, d->document->characterCount() - 1);
1194}
1195
1203{
1204 Q_D(const QQuickTextEdit);
1205 return d->contentSize.width();
1206}
1207
1215{
1216 Q_D(const QQuickTextEdit);
1217 return d->contentSize.height();
1218}
1219
1230{
1231 Q_D(const QQuickTextEdit);
1232 if (d->baseUrl.isEmpty()) {
1233 if (QQmlContext *context = qmlContext(this))
1234 const_cast<QQuickTextEditPrivate *>(d)->baseUrl = context->baseUrl();
1235 }
1236 return d->baseUrl;
1237}
1238
1240{
1241 Q_D(QQuickTextEdit);
1242 if (baseUrl() != url) {
1243 d->baseUrl = url;
1244
1245 d->document->setBaseUrl(url);
1247 }
1248}
1249
1251{
1252 if (QQmlContext *context = qmlContext(this))
1253 setBaseUrl(context->baseUrl());
1254 else
1255 setBaseUrl(QUrl());
1256}
1257
1266{
1267 Q_D(const QQuickTextEdit);
1268 QTextCursor c(d->document);
1269 c.setPosition(pos);
1270 return d->control->cursorRect(c).translated(d->xoff, d->yoff);
1271
1272}
1273
1283{
1284 Q_D(const QQuickTextEdit);
1285 x -= d->xoff;
1286 y -= d->yoff;
1287
1288 int r = d->document->documentLayout()->hitTest(QPointF(x, y), Qt::FuzzyHit);
1289#if QT_CONFIG(im)
1290 QTextCursor cursor = d->control->textCursor();
1291 if (r > cursor.position()) {
1292 // The cursor position includes positions within the preedit text, but only positions in the
1293 // same text block are offset so it is possible to get a position that is either part of the
1294 // preedit or the next text block.
1295 QTextLayout *layout = cursor.block().layout();
1296 const int preeditLength = layout
1297 ? layout->preeditAreaText().size()
1298 : 0;
1299 if (preeditLength > 0
1300 && d->document->documentLayout()->blockBoundingRect(cursor.block()).contains(x, y)) {
1301 r = r > cursor.position() + preeditLength
1302 ? r - preeditLength
1303 : cursor.position();
1304 }
1305 }
1306#endif
1307 return r;
1308}
1309
1321{
1322 Q_D(const QQuickTextEdit);
1323 if (!d->cursorSelection)
1324 d->cursorSelection = new QQuickTextSelection(const_cast<QQuickTextEdit *>(this));
1325 return d->cursorSelection;
1326}
1327
1365{
1366 //Note that this is the same as setCursorPosition but with the KeepAnchor flag set
1367 Q_D(QQuickTextEdit);
1368 QTextCursor cursor = d->control->textCursor();
1369 if (cursor.position() == pos)
1370 return;
1371 cursor.setPosition(pos, QTextCursor::KeepAnchor);
1372 d->control->setTextCursor(cursor);
1373}
1374
1376{
1377 Q_D(QQuickTextEdit);
1378 QTextCursor cursor = d->control->textCursor();
1379 if (cursor.position() == pos)
1380 return;
1381 if (mode == SelectCharacters) {
1382 cursor.setPosition(pos, QTextCursor::KeepAnchor);
1383 } else if (cursor.anchor() < pos || (cursor.anchor() == pos && cursor.position() < pos)) {
1384 if (cursor.anchor() > cursor.position()) {
1385 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1387 if (cursor.position() == cursor.anchor())
1389 else
1390 cursor.setPosition(cursor.position(), QTextCursor::MoveAnchor);
1391 } else {
1392 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1394 }
1395
1396 cursor.setPosition(pos, QTextCursor::KeepAnchor);
1398 if (cursor.position() != pos)
1400 } else if (cursor.anchor() > pos || (cursor.anchor() == pos && cursor.position() > pos)) {
1401 if (cursor.anchor() < cursor.position()) {
1402 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1404 } else {
1405 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1408 if (cursor.position() != cursor.anchor()) {
1409 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1411 }
1412 }
1413
1414 cursor.setPosition(pos, QTextCursor::KeepAnchor);
1416 if (cursor.position() != pos) {
1419 }
1420 }
1421 d->control->setTextCursor(cursor);
1422}
1423
1432{
1433 Q_D(const QQuickTextEdit);
1434 return d->cursorVisible;
1435}
1436
1438{
1439 Q_D(QQuickTextEdit);
1440 if (d->cursorVisible == on)
1441 return;
1442 d->cursorVisible = on;
1443 if (on && isComponentComplete())
1445 if (!on && !d->persistentSelection)
1446 d->control->setCursorIsFocusIndicator(true);
1447 d->control->setCursorVisible(on);
1448 emit cursorVisibleChanged(d->cursorVisible);
1449}
1450
1464{
1465 Q_D(const QQuickTextEdit);
1466 return d->control->textCursor().position();
1467}
1468
1470{
1471 Q_D(QQuickTextEdit);
1472 if (pos < 0 || pos >= d->document->characterCount()) // characterCount includes the terminating null.
1473 return;
1474 QTextCursor cursor = d->control->textCursor();
1475 if (cursor.position() == pos && cursor.anchor() == pos)
1476 return;
1477 cursor.setPosition(pos);
1478 d->control->setTextCursor(cursor);
1479 d->control->updateCursorRectangle(true);
1480}
1481
1496{
1497 Q_D(const QQuickTextEdit);
1498 return d->cursorComponent;
1499}
1500
1506
1507void QQuickTextEdit::createCursor()
1508{
1509 Q_D(QQuickTextEdit);
1510 d->cursorPending = true;
1512}
1513
1525{
1526 Q_D(const QQuickTextEdit);
1527 return d->control->textCursor().selectionStart();
1528}
1529
1541{
1542 Q_D(const QQuickTextEdit);
1543 return d->control->textCursor().selectionEnd();
1544}
1545
1561{
1562 Q_D(const QQuickTextEdit);
1563#if QT_CONFIG(texthtmlparser)
1564 return d->richText || d->markdownText
1565 ? d->control->textCursor().selectedText()
1566 : d->control->textCursor().selection().toPlainText();
1567#else
1568 return d->control->textCursor().selection().toPlainText();
1569#endif
1570}
1571
1579{
1580 Q_D(const QQuickTextEdit);
1581 return d->focusOnPress;
1582}
1583
1585{
1586 Q_D(QQuickTextEdit);
1587 if (d->focusOnPress == on)
1588 return;
1589 d->focusOnPress = on;
1590 emit activeFocusOnPressChanged(d->focusOnPress);
1591}
1592
1600{
1601 Q_D(const QQuickTextEdit);
1602 return d->persistentSelection;
1603}
1604
1606{
1607 Q_D(QQuickTextEdit);
1608 if (d->persistentSelection == on)
1609 return;
1610 d->persistentSelection = on;
1611 emit persistentSelectionChanged(d->persistentSelection);
1612}
1613
1620{
1621 Q_D(const QQuickTextEdit);
1622 return d->textMargin;
1623}
1624
1626{
1627 Q_D(QQuickTextEdit);
1628 if (d->textMargin == margin)
1629 return;
1630 d->textMargin = margin;
1631 d->document->setDocumentMargin(d->textMargin);
1632 emit textMarginChanged(d->textMargin);
1633}
1634
1672Qt::InputMethodHints QQuickTextEdit::inputMethodHints() const
1673{
1674#if !QT_CONFIG(im)
1675 return Qt::ImhNone;
1676#else
1677 Q_D(const QQuickTextEdit);
1678 return d->inputMethodHints;
1679#endif // im
1680}
1681
1682void QQuickTextEdit::setInputMethodHints(Qt::InputMethodHints hints)
1683{
1684#if !QT_CONFIG(im)
1685 Q_UNUSED(hints);
1686#else
1687 Q_D(QQuickTextEdit);
1688
1689 if (hints == d->inputMethodHints)
1690 return;
1691
1692 d->inputMethodHints = hints;
1693 updateInputMethod(Qt::ImHints);
1695#endif // im
1696}
1697
1698void QQuickTextEdit::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
1699{
1700 Q_D(QQuickTextEdit);
1701 if (!d->inLayout && ((newGeometry.width() != oldGeometry.width())
1702 || (newGeometry.height() != oldGeometry.height()))) {
1703 updateSize();
1704 updateWholeDocument();
1705 if (widthValid() || heightValid())
1706 moveCursorDelegate();
1707 }
1708 QQuickImplicitSizeItem::geometryChange(newGeometry, oldGeometry);
1709}
1710
1712{
1713 Q_D(QQuickTextEdit);
1714 Q_UNUSED(value);
1715 switch (change) {
1717 if (d->renderType == NativeRendering) {
1718 // Native rendering optimizes for a given pixel grid, so its results must not be scaled.
1719 // Text layout code respects the current device pixel ratio automatically, we only need
1720 // to rerun layout after the ratio changed.
1721 updateSize();
1722 updateWholeDocument();
1723 }
1724 break;
1725
1726 default:
1727 break;
1728 }
1730}
1731
1737{
1738 Q_D(QQuickTextEdit);
1740
1741 const QUrl url = baseUrl();
1742 const QQmlContext *context = qmlContext(this);
1743 d->document->setBaseUrl(context ? context->resolvedUrl(url) : url);
1744 if (!d->text.isEmpty()) {
1745#if QT_CONFIG(texthtmlparser)
1746 if (d->richText)
1747 d->control->setHtml(d->text);
1748 else
1749#endif
1750#if QT_CONFIG(textmarkdownreader)
1751 if (d->markdownText)
1752 d->control->setMarkdownText(d->text);
1753 else
1754#endif
1755 d->control->setPlainText(d->text);
1756 }
1757
1758 if (d->dirty) {
1759 d->determineHorizontalAlignment();
1760 d->updateDefaultTextOption();
1761 updateSize();
1762 d->dirty = false;
1763 }
1764 if (d->cursorComponent && isCursorVisible())
1766 polish();
1767
1768#if QT_CONFIG(accessibility)
1769 if (QAccessible::isActive())
1770 d->accessibilityActiveChanged(true);
1771#endif
1772}
1773
1775{
1776 Q_D(const QQuickTextEdit);
1777 return d->pixmapsInProgress.size();
1778}
1779
1795{
1796 Q_D(const QQuickTextEdit);
1797 if (d->selectByKeyboardSet)
1798 return d->selectByKeyboard;
1799 return !isReadOnly();
1800}
1801
1803{
1804 Q_D(QQuickTextEdit);
1805 bool was = selectByKeyboard();
1806 if (!d->selectByKeyboardSet || was != on) {
1807 d->selectByKeyboardSet = true;
1808 d->selectByKeyboard = on;
1809 if (on)
1810 d->control->setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByKeyboard);
1811 else
1812 d->control->setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByKeyboard);
1813 emit selectByKeyboardChanged(on);
1814 }
1815}
1816
1841{
1842 Q_D(const QQuickTextEdit);
1843 return d->selectByMouse;
1844}
1845
1847{
1848 Q_D(QQuickTextEdit);
1849 if (d->selectByMouse == on)
1850 return;
1851
1852 d->selectByMouse = on;
1853 setKeepMouseGrab(on);
1854 if (on)
1855 d->control->setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByMouse);
1856 else
1857 d->control->setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByMouse);
1858
1859#if QT_CONFIG(cursor)
1860 d->updateMouseCursorShape();
1861#endif
1863}
1864
1876{
1877 Q_D(const QQuickTextEdit);
1878 return d->mouseSelectionMode;
1879}
1880
1882{
1883 Q_D(QQuickTextEdit);
1884 if (d->mouseSelectionMode != mode) {
1885 d->mouseSelectionMode = mode;
1886 d->control->setWordSelectionEnabled(mode == SelectWords);
1888 }
1889}
1890
1900{
1901 Q_D(QQuickTextEdit);
1902 if (r == isReadOnly())
1903 return;
1904
1905#if QT_CONFIG(im)
1906 setFlag(QQuickItem::ItemAcceptsInputMethod, !r);
1907#endif
1908 Qt::TextInteractionFlags flags = Qt::LinksAccessibleByMouse;
1909 if (d->selectByMouse)
1911 if (d->selectByKeyboardSet && d->selectByKeyboard)
1913 else if (!d->selectByKeyboardSet && !r)
1915 if (!r)
1917 d->control->setTextInteractionFlags(flags);
1918 d->control->moveCursor(QTextCursor::End);
1919
1920#if QT_CONFIG(im)
1921 updateInputMethod(Qt::ImEnabled);
1922#endif
1923#if QT_CONFIG(cursor)
1924 d->updateMouseCursorShape();
1925#endif
1926 q_canPasteChanged();
1928 if (!d->selectByKeyboardSet)
1929 emit selectByKeyboardChanged(!r);
1930 if (r) {
1931 setCursorVisible(false);
1932 } else if (hasActiveFocus()) {
1933 setCursorVisible(true);
1934 }
1935
1936#if QT_CONFIG(accessibility)
1937 if (QAccessible::isActive()) {
1938 if (QQuickAccessibleAttached *accessibleAttached = QQuickAccessibleAttached::attachedProperties(this))
1939 accessibleAttached->set_readOnly(r);
1940 }
1941#endif
1942}
1943
1945{
1946 Q_D(const QQuickTextEdit);
1947 return !(d->control->textInteractionFlags() & Qt::TextEditable);
1948}
1949
1961{
1962 Q_D(const QQuickTextEdit);
1963 return d->control->cursorRect().translated(d->xoff, d->yoff);
1964}
1965
1967{
1968 Q_D(QQuickTextEdit);
1969 if (event->type() == QEvent::ShortcutOverride) {
1970 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
1971 if (event->isAccepted())
1972 return true;
1973 }
1975}
1976
1992{
1993 Q_D(const QQuickTextEdit);
1994 return d->control->overwriteMode();
1995}
1996
1998{
1999 Q_D(QQuickTextEdit);
2000 d->control->setOverwriteMode(overwrite);
2001}
2002
2008{
2009 Q_D(QQuickTextEdit);
2010 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2011 if (!event->isAccepted())
2013}
2014
2020{
2021 Q_D(QQuickTextEdit);
2022 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2023 if (!event->isAccepted())
2025}
2026
2033{
2034 Q_D(QQuickTextEdit);
2035 QTextCursor c = d->control->textCursor();
2036 c.clearSelection();
2037 d->control->setTextCursor(c);
2038}
2039
2046{
2047 Q_D(QQuickTextEdit);
2048 d->control->selectAll();
2049}
2050
2057{
2058 Q_D(QQuickTextEdit);
2059 QTextCursor c = d->control->textCursor();
2061 d->control->setTextCursor(c);
2062}
2063
2078{
2079 Q_D(QQuickTextEdit);
2080 if (start < 0 || end < 0 || start >= d->document->characterCount() || end >= d->document->characterCount())
2081 return;
2082 QTextCursor cursor = d->control->textCursor();
2083 cursor.beginEditBlock();
2084 cursor.setPosition(start, QTextCursor::MoveAnchor);
2085 cursor.setPosition(end, QTextCursor::KeepAnchor);
2086 cursor.endEditBlock();
2087 d->control->setTextCursor(cursor);
2088
2089 // QTBUG-11100
2090 updateSelection();
2091#if QT_CONFIG(im)
2092 updateInputMethod();
2093#endif
2094}
2095
2103{
2104 if (start > end) {
2105 qmlWarning(this) << "isRightToLeft(start, end) called with the end property being smaller than the start.";
2106 return false;
2107 } else {
2108 return getText(start, end).isRightToLeft();
2109 }
2110}
2111
2112#if QT_CONFIG(clipboard)
2118void QQuickTextEdit::cut()
2119{
2120 Q_D(QQuickTextEdit);
2121 d->control->cut();
2122}
2123
2129void QQuickTextEdit::copy()
2130{
2131 Q_D(QQuickTextEdit);
2132 d->control->copy();
2133}
2134
2140void QQuickTextEdit::paste()
2141{
2142 Q_D(QQuickTextEdit);
2143 d->control->paste();
2144}
2145#endif // clipboard
2146
2147
2157{
2158 Q_D(QQuickTextEdit);
2159 d->control->undo();
2160}
2161
2169{
2170 Q_D(QQuickTextEdit);
2171 d->control->redo();
2172}
2173
2179{
2180 Q_D(QQuickTextEdit);
2182 setKeepMouseGrab(d->selectByMouse && isMouse);
2183 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2184 if (d->focusOnPress){
2185 bool hadActiveFocus = hasActiveFocus();
2187 // re-open input panel on press if already focused
2188#if QT_CONFIG(im)
2189 if (hasActiveFocus() && hadActiveFocus && !isReadOnly())
2190 qGuiApp->inputMethod()->show();
2191#else
2192 Q_UNUSED(hadActiveFocus);
2193#endif
2194 }
2195 if (!event->isAccepted())
2197}
2198
2204{
2205 Q_D(QQuickTextEdit);
2206 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2207
2208 if (!event->isAccepted())
2210}
2211
2217{
2218 Q_D(QQuickTextEdit);
2219 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2220 if (!event->isAccepted())
2222}
2223
2229{
2230 Q_D(QQuickTextEdit);
2231 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2232 if (!event->isAccepted())
2234}
2235
2236#if QT_CONFIG(im)
2241void QQuickTextEdit::inputMethodEvent(QInputMethodEvent *event)
2242{
2243 Q_D(QQuickTextEdit);
2244 const bool wasComposing = isInputMethodComposing();
2245 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2246 setCursorVisible(d->control->cursorVisible());
2247 if (wasComposing != isInputMethodComposing())
2249}
2250
2255QVariant QQuickTextEdit::inputMethodQuery(Qt::InputMethodQuery property, QVariant argument) const
2256{
2257 Q_D(const QQuickTextEdit);
2258
2259 QVariant v;
2260 switch (property) {
2261 case Qt::ImEnabled:
2262 v = (bool)(flags() & ItemAcceptsInputMethod);
2263 break;
2264 case Qt::ImHints:
2265 v = (int)d->effectiveInputMethodHints();
2266 break;
2268 v = QQuickItem::inputMethodQuery(property);
2269 break;
2270 case Qt::ImReadOnly:
2271 v = isReadOnly();
2272 break;
2273 default:
2274 if (property == Qt::ImCursorPosition && !argument.isNull())
2275 argument = QVariant(argument.toPointF() - QPointF(d->xoff, d->yoff));
2276 v = d->control->inputMethodQuery(property, argument);
2278 v = QVariant(v.toRectF().translated(d->xoff, d->yoff));
2279 break;
2280 }
2281 return v;
2282}
2283
2288QVariant QQuickTextEdit::inputMethodQuery(Qt::InputMethodQuery property) const
2289{
2290 return inputMethodQuery(property, QVariant());
2291}
2292#endif // im
2293
2294void QQuickTextEdit::triggerPreprocess()
2295{
2296 Q_D(QQuickTextEdit);
2297 if (d->updateType == QQuickTextEditPrivate::UpdateNone)
2299 polish();
2300 update();
2301}
2302
2308QVariant QQuickTextEdit::loadResource(int type, const QUrl &source)
2309{
2310 Q_D(QQuickTextEdit);
2311 const QUrl url = d->document->baseUrl().resolved(source);
2312 if (url.isLocalFile()) {
2313 // qmlWarning if the file doesn't exist (because QTextDocument::loadResource() can't do that)
2315 if (!fi.exists())
2316 qmlWarning(this) << "Cannot open: " << url.toString();
2317 // let QTextDocument::loadResource() handle local file loading
2318 return {};
2319 }
2320
2321 // see if we already started a load job
2322 auto existingJobIter = std::find_if(
2323 d->pixmapsInProgress.cbegin(), d->pixmapsInProgress.cend(),
2324 [&url](const auto *job) { return job->url() == url; } );
2325 if (existingJobIter != d->pixmapsInProgress.cend()) {
2326 const QQuickPixmap *job = *existingJobIter;
2327 if (job->isError()) {
2328 qmlWarning(this) << job->error();
2329 d->pixmapsInProgress.erase(existingJobIter);
2330 delete job;
2331 return QImage();
2332 } else {
2333 qCDebug(lcTextEdit) << "already downloading" << url;
2334 // existing job: return a null variant if it's not done yet
2335 return job->isReady() ? job->image() : QVariant();
2336 }
2337 }
2338
2339 // not found: start a new load job
2340 qCDebug(lcTextEdit) << "loading" << source << "resolved" << url
2341 << "type" << static_cast<QTextDocument::ResourceType>(type);
2344 // don't cache it in QQuickPixmapCache, because it's cached in QTextDocumentPrivate::cachedResources
2345 QQuickPixmap *p = new QQuickPixmap(context->engine(), url, QQuickPixmap::Options{});
2346 p->connectFinished(this, SLOT(resourceRequestFinished()));
2347 d->pixmapsInProgress.append(p);
2348 // the new job is probably not done; return a null variant if the caller should poll again
2349 return p->isReady() ? p->image() : QVariant();
2350}
2351
2355void QQuickTextEdit::resourceRequestFinished()
2356{
2357 Q_D(QQuickTextEdit);
2358 for (auto it = d->pixmapsInProgress.cbegin(); it != d->pixmapsInProgress.cend(); ++it) {
2359 auto *job = *it;
2360 if (job->isError()) {
2361 // get QTextDocument::loadResource() to call QQuickTextEdit::loadResource() again, to return the placeholder
2362 qCDebug(lcTextEdit) << "failed to load (error)" << job->url();
2363 d->document->resource(QTextDocument::ImageResource, job->url());
2364 // that will call QQuickTextEdit::loadResource() which will delete the job;
2365 // so leave it in pixmapsInProgress for now, and stop this loop
2366 break;
2367 } else if (job->isReady()) {
2368 // get QTextDocument::loadResource() to call QQuickTextEdit::loadResource() again, and cache the result
2369 auto res = d->document->resource(QTextDocument::ImageResource, job->url());
2370 // If QTextDocument::resource() returned a valid variant, it's been cached too. Either way, the job is done.
2371 qCDebug(lcTextEdit) << (res.isValid() ? "done downloading" : "failed to load") << job->url() << job->rect();
2372 d->pixmapsInProgress.erase(it);
2373 delete job;
2374 break;
2375 }
2376 }
2377 if (d->pixmapsInProgress.isEmpty()) {
2378 invalidate();
2379 updateSize();
2380 q_invalidate();
2381 }
2382}
2383
2386
2387static inline bool operator<(const TextNode &n1, const TextNode &n2)
2388{
2389 return n1.startPos() < n2.startPos();
2390}
2391
2392static inline void updateNodeTransform(QSGInternalTextNode *node, const QPointF &topLeft)
2393{
2394 QMatrix4x4 transformMatrix;
2395 transformMatrix.translate(topLeft.x(), topLeft.y());
2396 node->setMatrix(transformMatrix);
2397}
2398
2405void QQuickTextEdit::invalidateFontCaches()
2406{
2407 Q_D(QQuickTextEdit);
2408 if (d->document == nullptr)
2409 return;
2410
2411 QTextBlock block;
2412 for (block = d->document->firstBlock(); block.isValid(); block = block.next()) {
2413 if (block.layout() != nullptr && block.layout()->engine() != nullptr)
2414 block.layout()->engine()->resetFontEngineCache();
2415 }
2416}
2417
2418QTextDocument *QQuickTextEdit::document() const
2419{
2420 Q_D(const QQuickTextEdit);
2421 return d->document;
2422}
2423
2424void QQuickTextEdit::setDocument(QTextDocument *doc)
2425{
2426 Q_D(QQuickTextEdit);
2427 if (d->ownsDocument)
2428 delete d->document;
2429 d->document = doc;
2430 d->ownsDocument = false;
2431 d->control->setDocument(doc);
2432}
2433
2434inline void resetEngine(QQuickTextNodeEngine *engine, const QColor& textColor, const QColor& selectedTextColor, const QColor& selectionColor)
2435{
2437 engine->setTextColor(textColor);
2438 engine->setSelectedTextColor(selectedTextColor);
2439 engine->setSelectionColor(selectionColor);
2440}
2441
2443{
2444 Q_UNUSED(updatePaintNodeData);
2445 Q_D(QQuickTextEdit);
2446
2447 if (d->updateType != QQuickTextEditPrivate::UpdatePaintNode
2448 && d->updateType != QQuickTextEditPrivate::UpdateAll
2449 && oldNode != nullptr) {
2450 // Update done in preprocess() in the nodes
2452 return oldNode;
2453 }
2454
2455 if (!oldNode || d->updateType == QQuickTextEditPrivate::UpdateAll) {
2456 delete oldNode;
2457 oldNode = nullptr;
2458
2459 // If we had any QSGInternalTextNode node references, they were deleted along with the root node
2460 // But here we must delete the Node structures in textNodeMap
2461 d->textNodeMap.clear();
2462 }
2463
2465
2466 RootNode *rootNode = static_cast<RootNode *>(oldNode);
2467 TextNodeIterator nodeIterator = d->textNodeMap.begin();
2468 std::optional<int> firstPosAcrossAllNodes;
2469 if (nodeIterator != d->textNodeMap.end())
2470 firstPosAcrossAllNodes = nodeIterator->startPos();
2471
2472 while (nodeIterator != d->textNodeMap.end() && !nodeIterator->dirty())
2473 ++nodeIterator;
2474
2476 QQuickTextNodeEngine frameDecorationsEngine;
2477
2478 if (!oldNode || nodeIterator < d->textNodeMap.end() || d->textNodeMap.isEmpty()) {
2479
2480 if (!oldNode)
2481 rootNode = new RootNode;
2482
2483 int firstDirtyPos = 0;
2484 if (nodeIterator != d->textNodeMap.end()) {
2485 firstDirtyPos = nodeIterator->startPos();
2486 // ### this could be optimized if the first and last dirty nodes are not connected
2487 // as the intermediate text nodes would usually only need to be transformed differently.
2488 QSGInternalTextNode *firstCleanNode = nullptr;
2489 auto it = d->textNodeMap.constEnd();
2490 while (it != nodeIterator) {
2491 --it;
2492 if (it->dirty())
2493 break;
2494 firstCleanNode = it->textNode();
2495 }
2496 do {
2497 rootNode->removeChildNode(nodeIterator->textNode());
2498 delete nodeIterator->textNode();
2499 nodeIterator = d->textNodeMap.erase(nodeIterator);
2500 } while (nodeIterator != d->textNodeMap.constEnd() && nodeIterator->textNode() != firstCleanNode);
2501 }
2502
2503 // If there's a lot of text, insert only the range of blocks that can possibly be visible within the viewport.
2505 if (flags().testFlag(QQuickItem::ItemObservesViewport)) {
2506 viewport = clipRect();
2507 qCDebug(lcVP) << "text viewport" << viewport;
2508 }
2509
2510 // FIXME: the text decorations could probably be handled separately (only updated for affected textFrames)
2511 rootNode->resetFrameDecorations(d->createTextNode());
2512 resetEngine(&frameDecorationsEngine, d->color, d->selectedTextColor, d->selectionColor);
2513
2514 QSGInternalTextNode *node = nullptr;
2515
2516 int currentNodeSize = 0;
2517 int nodeStart = firstDirtyPos;
2518 QPointF basePosition(d->xoff, d->yoff);
2519 QMatrix4x4 basePositionMatrix;
2520 basePositionMatrix.translate(basePosition.x(), basePosition.y());
2521 rootNode->setMatrix(basePositionMatrix);
2522
2523 QPointF nodeOffset;
2524 const TextNode firstCleanNode = (nodeIterator != d->textNodeMap.end()) ? *nodeIterator
2525 : TextNode();
2526
2527 QList<QTextFrame *> frames;
2528 frames.append(d->document->rootFrame());
2529
2530
2531 d->firstBlockInViewport = -1;
2532 d->firstBlockPastViewport = -1;
2533 int frameCount = -1;
2534 while (!frames.isEmpty()) {
2535 QTextFrame *textFrame = frames.takeFirst();
2536 ++frameCount;
2537 if (frameCount > 0)
2538 firstDirtyPos = 0;
2539 qCDebug(lcVP) << "frame" << frameCount << textFrame
2540 << "from" << positionToRectangle(textFrame->firstPosition()).topLeft()
2541 << "to" << positionToRectangle(textFrame->lastPosition()).bottomRight();
2542 frames.append(textFrame->childFrames());
2543 frameDecorationsEngine.addFrameDecorations(d->document, textFrame);
2544 resetEngine(&engine, d->color, d->selectedTextColor, d->selectionColor);
2545
2546 if (textFrame->firstPosition() > textFrame->lastPosition()
2547 && textFrame->frameFormat().position() != QTextFrameFormat::InFlow) {
2548 node = d->createTextNode();
2549 updateNodeTransform(node, d->document->documentLayout()->frameBoundingRect(textFrame).topLeft());
2550 const int pos = textFrame->firstPosition() - 1;
2551 auto *a = static_cast<QtPrivate::ProtectedLayoutAccessor *>(d->document->documentLayout());
2552 QTextCharFormat format = a->formatAccessor(pos);
2553 QTextBlock block = textFrame->firstCursorPosition().block();
2554 nodeOffset = d->document->documentLayout()->blockBoundingRect(block).topLeft();
2555 bool inView = true;
2556 if (!viewport.isNull() && block.layout()) {
2557 QRectF coveredRegion = block.layout()->boundingRect().adjusted(nodeOffset.x(), nodeOffset.y(), nodeOffset.x(), nodeOffset.y());
2558 inView = coveredRegion.bottom() >= viewport.top() && coveredRegion.top() <= viewport.bottom();
2559 qCDebug(lcVP) << "non-flow frame" << coveredRegion << "in viewport?" << inView;
2560 }
2561 if (inView) {
2562 engine.setCurrentLine(block.layout()->lineForTextPosition(pos - block.position()));
2563 engine.addTextObject(block, QPointF(0, 0), format, QQuickTextNodeEngine::Unselected, d->document,
2564 pos, textFrame->frameFormat().position());
2565 }
2566 nodeStart = pos;
2567 } else {
2568 // Having nodes spanning across frame boundaries will break the current bookkeeping mechanism. We need to prevent that.
2569 QList<int> frameBoundaries;
2570 frameBoundaries.reserve(frames.size());
2571 for (QTextFrame *frame : std::as_const(frames))
2572 frameBoundaries.append(frame->firstPosition());
2573 std::sort(frameBoundaries.begin(), frameBoundaries.end());
2574
2575 QTextFrame::iterator it = textFrame->begin();
2576 while (!it.atEnd()) {
2577 QTextBlock block = it.currentBlock();
2578 if (block.position() < firstDirtyPos) {
2579 ++it;
2580 continue;
2581 }
2582
2583 if (!engine.hasContents())
2584 nodeOffset = d->document->documentLayout()->blockBoundingRect(block).topLeft();
2585
2586 bool inView = true;
2587 if (!viewport.isNull()) {
2588 QRectF coveredRegion;
2589 if (block.layout()) {
2590 coveredRegion = block.layout()->boundingRect().adjusted(nodeOffset.x(), nodeOffset.y(), nodeOffset.x(), nodeOffset.y());
2591 inView = coveredRegion.bottom() > viewport.top();
2592 }
2593 const bool potentiallyScrollingBackwards = firstPosAcrossAllNodes && *firstPosAcrossAllNodes == firstDirtyPos;
2594 if (d->firstBlockInViewport < 0 && inView && potentiallyScrollingBackwards) {
2595 // During backward scrolling, we need to iterate backwards from textNodeMap.begin() to fill the top of the viewport.
2596 if (coveredRegion.top() > viewport.top() + 1) {
2597 qCDebug(lcVP) << "checking backwards from block" << block.blockNumber() << "@" << nodeOffset.y() << coveredRegion;
2598 while (it != textFrame->begin() && it.currentBlock().layout() &&
2599 it.currentBlock().layout()->boundingRect().top() + nodeOffset.y() > viewport.top()) {
2600 nodeOffset = d->document->documentLayout()->blockBoundingRect(it.currentBlock()).topLeft();
2601 --it;
2602 }
2603 if (!it.currentBlock().layout())
2604 ++it;
2605 if (Q_LIKELY(it.currentBlock().layout())) {
2606 block = it.currentBlock();
2607 coveredRegion = block.layout()->boundingRect().adjusted(nodeOffset.x(), nodeOffset.y(), nodeOffset.x(), nodeOffset.y());
2608 firstDirtyPos = it.currentBlock().position();
2609 } else {
2610 qCWarning(lcVP) << "failed to find a text block with layout during back-scrolling";
2611 }
2612 }
2613 qCDebug(lcVP) << "first block in viewport" << block.blockNumber() << "@" << nodeOffset.y() << coveredRegion;
2614 if (block.layout())
2615 d->renderedRegion = coveredRegion;
2616 } else {
2617 if (nodeOffset.y() > viewport.bottom()) {
2618 inView = false;
2619 if (d->firstBlockInViewport >= 0 && d->firstBlockPastViewport < 0) {
2620 qCDebug(lcVP) << "first block past viewport" << viewport << block.blockNumber()
2621 << "@" << nodeOffset.y() << "total region rendered" << d->renderedRegion;
2622 d->firstBlockPastViewport = block.blockNumber();
2623 }
2624 break; // skip rest of blocks in this frame
2625 }
2626 if (inView && !block.text().isEmpty() && coveredRegion.isValid()) {
2627 d->renderedRegion = d->renderedRegion.united(coveredRegion);
2628 // In case we're going to visit more (nested) frames after this, ensure that we
2629 // don't omit any blocks that fit within the region that we claim as fully rendered.
2630 if (!frames.isEmpty())
2631 viewport = viewport.united(d->renderedRegion);
2632 }
2633 }
2634 if (inView && d->firstBlockInViewport < 0)
2635 d->firstBlockInViewport = block.blockNumber();
2636 }
2637
2638 bool createdNodeInView = false;
2639 if (inView) {
2640 if (!engine.hasContents()) {
2641 if (node && !node->parent())
2642 d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart);
2643 node = d->createTextNode();
2644 createdNodeInView = true;
2645 updateNodeTransform(node, nodeOffset);
2646 nodeStart = block.position();
2647 }
2648 engine.addTextBlock(d->document, block, -nodeOffset, d->color, QColor(), selectionStart(), selectionEnd() - 1);
2649 currentNodeSize += block.length();
2650 }
2651
2652 if ((it.atEnd()) || block.next().position() >= firstCleanNode.startPos())
2653 break; // last node that needed replacing or last block of the frame
2654 QList<int>::const_iterator lowerBound = std::lower_bound(frameBoundaries.constBegin(), frameBoundaries.constEnd(), block.next().position());
2655 if (node && (currentNodeSize > nodeBreakingSize || lowerBound == frameBoundaries.constEnd() || *lowerBound > nodeStart)) {
2656 currentNodeSize = 0;
2657 if (!node->parent())
2658 d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart);
2659 if (!createdNodeInView)
2660 node = d->createTextNode();
2661 resetEngine(&engine, d->color, d->selectedTextColor, d->selectionColor);
2662 nodeStart = block.next().position();
2663 }
2664 ++it;
2665 } // loop over blocks in frame
2666 }
2667 if (Q_LIKELY(node && !node->parent()))
2668 d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart);
2669 }
2670 frameDecorationsEngine.addToSceneGraph(rootNode->frameDecorationsNode, QQuickText::Normal, QColor());
2671 // Now prepend the frame decorations since we want them rendered first, with the text nodes and cursor in front.
2672 rootNode->prependChildNode(rootNode->frameDecorationsNode);
2673
2674 Q_ASSERT(nodeIterator == d->textNodeMap.end()
2675 || (nodeIterator->textNode() == firstCleanNode.textNode()
2676 && nodeIterator->startPos() == firstCleanNode.startPos()));
2677 // Update the position of the subsequent text blocks.
2678 if (firstCleanNode.textNode() != nullptr) {
2679 QPointF oldOffset = firstCleanNode.textNode()->matrix().map(QPointF(0,0));
2680 QPointF currentOffset = d->document->documentLayout()->blockBoundingRect(
2681 d->document->findBlock(firstCleanNode.startPos())).topLeft();
2682 QPointF delta = currentOffset - oldOffset;
2683 while (nodeIterator != d->textNodeMap.end()) {
2684 QMatrix4x4 transformMatrix = nodeIterator->textNode()->matrix();
2685 transformMatrix.translate(delta.x(), delta.y());
2686 nodeIterator->textNode()->setMatrix(transformMatrix);
2687 ++nodeIterator;
2688 }
2689
2690 }
2691
2692 // Since we iterate over blocks from different text frames that are potentially not sorted
2693 // we need to ensure that our list of nodes is sorted again:
2694 std::sort(d->textNodeMap.begin(), d->textNodeMap.end());
2695 }
2696
2697 if (d->cursorComponent == nullptr) {
2699 if (!isReadOnly() && d->cursorVisible && d->control->cursorOn() && d->control->cursorVisible())
2700 cursor = d->sceneGraphContext()->createInternalRectangleNode(d->control->cursorRect(), d->color);
2701 rootNode->resetCursorNode(cursor);
2702 }
2703
2704 invalidateFontCaches();
2705
2706 return rootNode;
2707}
2708
2710{
2711 invalidateFontCaches();
2712}
2713
2721{
2722 Q_D(const QQuickTextEdit);
2723 if (!d->canPasteValid) {
2724 const_cast<QQuickTextEditPrivate *>(d)->canPaste = d->control->canPaste();
2725 const_cast<QQuickTextEditPrivate *>(d)->canPasteValid = true;
2726 }
2727 return d->canPaste;
2728}
2729
2738{
2739 Q_D(const QQuickTextEdit);
2740 return d->document->isUndoAvailable();
2741}
2742
2751{
2752 Q_D(const QQuickTextEdit);
2753 return d->document->isRedoAvailable();
2754}
2755
2769{
2770#if !QT_CONFIG(im)
2771 return false;
2772#else
2773 Q_D(const QQuickTextEdit);
2774 return d->control->hasImState();
2775#endif // im
2776}
2777
2779 : explicitTopPadding(false)
2780 , explicitLeftPadding(false)
2781 , explicitRightPadding(false)
2782 , explicitBottomPadding(false)
2783 , implicitResize(true)
2784{
2785}
2786
2788{
2789 Q_Q(QQuickTextEdit);
2790
2791#if QT_CONFIG(clipboard)
2792 if (QGuiApplication::clipboard()->supportsSelection())
2793 q->setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton);
2794 else
2795#endif
2796 q->setAcceptedMouseButtons(Qt::LeftButton);
2797
2798#if QT_CONFIG(im)
2799 q->setFlag(QQuickItem::ItemAcceptsInputMethod);
2800#endif
2802
2803 q->setAcceptHoverEvents(true);
2804
2805 document = new QTextDocument(q);
2806 ownsDocument = true;
2807 auto *imageHandler = new QQuickTextImageHandler(document);
2809
2812 control->setAcceptRichText(false);
2814 q->setKeepMouseGrab(true);
2815
2816 qmlobject_connect(control, QQuickTextControl, SIGNAL(updateCursorRequest()), q, QQuickTextEdit, SLOT(updateCursor()));
2817 qmlobject_connect(control, QQuickTextControl, SIGNAL(selectionChanged()), q, QQuickTextEdit, SIGNAL(selectedTextChanged()));
2818 qmlobject_connect(control, QQuickTextControl, SIGNAL(selectionChanged()), q, QQuickTextEdit, SLOT(updateSelection()));
2819 qmlobject_connect(control, QQuickTextControl, SIGNAL(cursorPositionChanged()), q, QQuickTextEdit, SLOT(updateSelection()));
2820 qmlobject_connect(control, QQuickTextControl, SIGNAL(cursorPositionChanged()), q, QQuickTextEdit, SIGNAL(cursorPositionChanged()));
2821 qmlobject_connect(control, QQuickTextControl, SIGNAL(cursorRectangleChanged()), q, QQuickTextEdit, SLOT(moveCursorDelegate()));
2823 qmlobject_connect(control, QQuickTextControl, SIGNAL(overwriteModeChanged(bool)), q, QQuickTextEdit, SIGNAL(overwriteModeChanged(bool)));
2825 qmlobject_connect(control, QQuickTextControl, SIGNAL(preeditTextChanged()), q, QQuickTextEdit, SIGNAL(preeditTextChanged()));
2826#if QT_CONFIG(clipboard)
2827 qmlobject_connect(QGuiApplication::clipboard(), QClipboard, SIGNAL(dataChanged()), q, QQuickTextEdit, SLOT(q_canPasteChanged()));
2828#endif
2829 qmlobject_connect(document, QTextDocument, SIGNAL(undoAvailable(bool)), q, QQuickTextEdit, SIGNAL(canUndoChanged()));
2830 qmlobject_connect(document, QTextDocument, SIGNAL(redoAvailable(bool)), q, QQuickTextEdit, SIGNAL(canRedoChanged()));
2831 QObject::connect(document, &QTextDocument::contentsChange, q, &QQuickTextEdit::q_contentsChange);
2833 QObject::connect(control, &QQuickTextControl::linkHovered, q, &QQuickTextEdit::q_linkHovered);
2834 QObject::connect(control, &QQuickTextControl::markerHovered, q, &QQuickTextEdit::q_markerHovered);
2835
2838 document->setUndoRedoEnabled(false); // flush undo buffer.
2841 document->setModified(false); // we merely changed some defaults: no edits worth saving yet
2842 q->updateSize();
2843#if QT_CONFIG(cursor)
2844 updateMouseCursorShape();
2845#endif
2847}
2848
2850{
2851 Q_Q(QQuickTextEdit);
2852 if (!q->isReadOnly() && q->hasActiveFocus() && qGuiApp)
2854}
2855
2856void QQuickTextEdit::q_textChanged()
2857{
2858 Q_D(QQuickTextEdit);
2859 d->textCached = false;
2860 for (QTextBlock it = d->document->begin(); it != d->document->end(); it = it.next()) {
2861 d->contentDirection = d->textDirection(it.text());
2862 if (d->contentDirection != Qt::LayoutDirectionAuto)
2863 break;
2864 }
2865 d->determineHorizontalAlignment();
2866 d->updateDefaultTextOption();
2867 updateSize();
2868
2869 markDirtyNodesForRange(0, d->document->characterCount(), 0);
2870 if (isComponentComplete()) {
2871 polish();
2873 update();
2874 }
2875
2876 emit textChanged();
2877}
2878
2879void QQuickTextEdit::markDirtyNodesForRange(int start, int end, int charDelta)
2880{
2881 Q_D(QQuickTextEdit);
2882 if (start == end)
2883 return;
2884
2885 TextNode dummyNode(start);
2886
2887 const TextNodeIterator textNodeMapBegin = d->textNodeMap.begin();
2888 const TextNodeIterator textNodeMapEnd = d->textNodeMap.end();
2889
2890 TextNodeIterator it = std::lower_bound(textNodeMapBegin, textNodeMapEnd, dummyNode);
2891 // qLowerBound gives us the first node past the start of the affected portion, rewind to the first node
2892 // that starts at the last position before the edit position. (there might be several because of images)
2893 if (it != textNodeMapBegin) {
2894 --it;
2895 TextNode otherDummy(it->startPos());
2896 it = std::lower_bound(textNodeMapBegin, textNodeMapEnd, otherDummy);
2897 }
2898
2899 // mark the affected nodes as dirty
2900 while (it != textNodeMapEnd) {
2901 if (it->startPos() <= end)
2902 it->setDirty();
2903 else if (charDelta)
2904 it->moveStartPos(charDelta);
2905 else
2906 return;
2907 ++it;
2908 }
2909}
2910
2911void QQuickTextEdit::q_contentsChange(int pos, int charsRemoved, int charsAdded)
2912{
2913 Q_D(QQuickTextEdit);
2914
2915 const int editRange = pos + qMax(charsAdded, charsRemoved);
2916 const int delta = charsAdded - charsRemoved;
2917
2918 markDirtyNodesForRange(pos, editRange, delta);
2919
2920 if (isComponentComplete()) {
2921 polish();
2923 update();
2924 }
2925}
2926
2927void QQuickTextEdit::moveCursorDelegate()
2928{
2929 Q_D(QQuickTextEdit);
2930#if QT_CONFIG(im)
2931 updateInputMethod();
2932#endif
2933 emit cursorRectangleChanged();
2934 if (!d->cursorItem)
2935 return;
2936 QRectF cursorRect = cursorRectangle();
2937 d->cursorItem->setX(cursorRect.x());
2938 d->cursorItem->setY(cursorRect.y());
2939 d->cursorItem->setHeight(cursorRect.height());
2940}
2941
2942void QQuickTextEdit::updateSelection()
2943{
2944 Q_D(QQuickTextEdit);
2945
2946 // No need for node updates when we go from an empty selection to another empty selection
2947 if (d->control->textCursor().hasSelection() || d->hadSelection) {
2948 markDirtyNodesForRange(qMin(d->lastSelectionStart, d->control->textCursor().selectionStart()), qMax(d->control->textCursor().selectionEnd(), d->lastSelectionEnd), 0);
2949 if (isComponentComplete()) {
2950 polish();
2952 update();
2953 }
2954 }
2955
2956 d->hadSelection = d->control->textCursor().hasSelection();
2957
2958 if (d->lastSelectionStart != d->control->textCursor().selectionStart()) {
2959 d->lastSelectionStart = d->control->textCursor().selectionStart();
2960 emit selectionStartChanged();
2961 }
2962 if (d->lastSelectionEnd != d->control->textCursor().selectionEnd()) {
2963 d->lastSelectionEnd = d->control->textCursor().selectionEnd();
2964 emit selectionEndChanged();
2965 }
2966}
2967
2969{
2970 Q_D(const QQuickTextEdit);
2971 QRectF r(
2972 QQuickTextUtil::alignedX(d->contentSize.width(), width(), effectiveHAlign()),
2973 d->yoff,
2974 d->contentSize.width(),
2975 d->contentSize.height());
2976
2977 int cursorWidth = 1;
2978 if (d->cursorItem)
2979 cursorWidth = 0;
2980 else if (!d->document->isEmpty())
2981 cursorWidth += 3;// ### Need a better way of accounting for space between char and cursor
2982
2983 // Could include font max left/right bearings to either side of rectangle.
2984 r.setRight(r.right() + cursorWidth);
2985
2986 return r;
2987}
2988
2990{
2991 Q_D(const QQuickTextEdit);
2993 int cursorWidth = 1;
2994 if (d->cursorItem)
2995 cursorWidth = d->cursorItem->width();
2996 if (!d->document->isEmpty())
2997 cursorWidth += 3;// ### Need a better way of accounting for space between char and cursor
2998
2999 // Could include font max left/right bearings to either side of rectangle.
3000
3001 r.setRight(r.right() + cursorWidth);
3002 return r;
3003}
3004
3006{
3007 Q_Q(const QQuickTextEdit);
3008 if (!requireImplicitWidth) {
3009 // We don't calculate implicitWidth unless it is required.
3010 // We need to force a size update now to ensure implicitWidth is calculated
3011 const_cast<QQuickTextEditPrivate*>(this)->requireImplicitWidth = true;
3012 const_cast<QQuickTextEdit*>(q)->updateSize();
3013 }
3014 return implicitWidth;
3015}
3016
3017//### we should perhaps be a bit smarter here -- depending on what has changed, we shouldn't
3018// need to do all the calculations each time
3019void QQuickTextEdit::updateSize()
3020{
3021 Q_D(QQuickTextEdit);
3022 if (!isComponentComplete()) {
3023 d->dirty = true;
3024 return;
3025 }
3026
3027 // ### assumes that if the width is set, the text will fill to edges
3028 // ### (unless wrap is false, then clipping will occur)
3029 if (widthValid()) {
3030 if (!d->requireImplicitWidth) {
3032 // if the implicitWidth is used, then updateSize() has already been called (recursively)
3033 if (d->requireImplicitWidth)
3034 return;
3035 }
3036 if (d->requireImplicitWidth) {
3037 d->document->setTextWidth(-1);
3038 const qreal naturalWidth = d->document->idealWidth();
3039 const bool wasInLayout = d->inLayout;
3040 d->inLayout = true;
3041 if (d->isImplicitResizeEnabled())
3042 setImplicitWidth(naturalWidth + leftPadding() + rightPadding());
3043 d->inLayout = wasInLayout;
3044 if (d->inLayout) // probably the result of a binding loop, but by letting it
3045 return; // get this far we'll get a warning to that effect.
3046 }
3047 const qreal newTextWidth = width() - leftPadding() - rightPadding();
3048 if (d->document->textWidth() != newTextWidth)
3049 d->document->setTextWidth(newTextWidth);
3050 } else if (d->wrapMode == NoWrap) {
3051 // normally, if explicit width is not set, we should call setTextWidth(-1) here,
3052 // as we don't need to fit the text to any fixed width. But because of some bug
3053 // in QTextDocument it also breaks RTL text alignment, so we use "idealWidth" instead.
3054 const qreal newTextWidth = d->document->idealWidth();
3055 if (d->document->textWidth() != newTextWidth)
3056 d->document->setTextWidth(newTextWidth);
3057 } else {
3058 d->document->setTextWidth(-1);
3059 }
3060
3061 QFontMetricsF fm(d->font);
3062 const qreal newHeight = d->document->isEmpty() ? qCeil(fm.height()) : d->document->size().height();
3063 const qreal newWidth = d->document->idealWidth();
3064
3065 if (d->isImplicitResizeEnabled()) {
3066 // ### Setting the implicitWidth triggers another updateSize(), and unless there are bindings nothing has changed.
3067 if (!widthValid())
3068 setImplicitSize(newWidth + leftPadding() + rightPadding(), newHeight + topPadding() + bottomPadding());
3069 else
3070 setImplicitHeight(newHeight + topPadding() + bottomPadding());
3071 }
3072
3073 d->xoff = leftPadding() + qMax(qreal(0), QQuickTextUtil::alignedX(d->document->size().width(), width() - leftPadding() - rightPadding(), effectiveHAlign()));
3074 d->yoff = topPadding() + QQuickTextUtil::alignedY(d->document->size().height(), height() - topPadding() - bottomPadding(), d->vAlign);
3075
3076 qreal baseline = fm.ascent();
3077 QTextBlock firstBlock = d->document->firstBlock();
3078 if (firstBlock.isValid() && firstBlock.layout() != nullptr && firstBlock.lineCount() > 0)
3079 baseline = firstBlock.layout()->lineAt(0).ascent();
3080
3081 setBaselineOffset(baseline + d->yoff + d->textMargin);
3082
3083 QSizeF size(newWidth, newHeight);
3084 if (d->contentSize != size) {
3085 d->contentSize = size;
3086 // Note: inResize is a bitfield so QScopedValueRollback can't be used here
3087 const bool wasInResize = d->inResize;
3088 d->inResize = true;
3089 if (!wasInResize)
3090 emit contentSizeChanged();
3091 d->inResize = wasInResize;
3092 updateTotalLines();
3093 }
3094}
3095
3096void QQuickTextEdit::updateWholeDocument()
3097{
3098 Q_D(QQuickTextEdit);
3099 if (!d->textNodeMap.isEmpty()) {
3100 for (TextNode &node : d->textNodeMap)
3101 node.setDirty();
3102 }
3103
3104 if (isComponentComplete()) {
3105 polish();
3107 update();
3108 }
3109}
3110
3111void QQuickTextEdit::invalidateBlock(const QTextBlock &block)
3112{
3113 Q_D(QQuickTextEdit);
3114 markDirtyNodesForRange(block.position(), block.position() + block.length(), 0);
3115
3116 if (isComponentComplete()) {
3117 polish();
3119 update();
3120 }
3121}
3122
3123void QQuickTextEdit::updateCursor()
3124{
3125 Q_D(QQuickTextEdit);
3126 if (isComponentComplete() && isVisible()) {
3127 polish();
3129 update();
3130 }
3131}
3132
3133void QQuickTextEdit::q_linkHovered(const QString &link)
3134{
3135 Q_D(QQuickTextEdit);
3136 emit linkHovered(link);
3137#if QT_CONFIG(cursor)
3138 if (link.isEmpty()) {
3139 d->updateMouseCursorShape();
3140 } else if (cursor().shape() != Qt::PointingHandCursor) {
3142 }
3143#endif
3144}
3145
3146void QQuickTextEdit::q_markerHovered(bool hovered)
3147{
3148 Q_D(QQuickTextEdit);
3149#if QT_CONFIG(cursor)
3150 if (!hovered) {
3151 d->updateMouseCursorShape();
3152 } else if (cursor().shape() != Qt::PointingHandCursor) {
3154 }
3155#endif
3156}
3157
3158void QQuickTextEdit::q_updateAlignment()
3159{
3160 Q_D(QQuickTextEdit);
3161 if (d->determineHorizontalAlignment()) {
3162 d->updateDefaultTextOption();
3163 d->xoff = qMax(qreal(0), QQuickTextUtil::alignedX(d->document->size().width(), width(), effectiveHAlign()));
3164 moveCursorDelegate();
3165 }
3166}
3167
3168void QQuickTextEdit::updateTotalLines()
3169{
3170 Q_D(QQuickTextEdit);
3171
3172 int subLines = 0;
3173
3174 for (QTextBlock it = d->document->begin(); it != d->document->end(); it = it.next()) {
3175 QTextLayout *layout = it.layout();
3176 if (!layout)
3177 continue;
3178 subLines += layout->lineCount()-1;
3179 }
3180
3181 int newTotalLines = d->document->lineCount() + subLines;
3182 if (d->lineCount != newTotalLines) {
3183 d->lineCount = newTotalLines;
3184 emit lineCountChanged();
3185 }
3186}
3187
3189{
3190 Q_Q(QQuickTextEdit);
3192 const Qt::Alignment oldAlignment = opt.alignment();
3193 Qt::LayoutDirection oldTextDirection = opt.textDirection();
3194
3195 QQuickTextEdit::HAlignment horizontalAlignment = q->effectiveHAlign();
3197 if (horizontalAlignment == QQuickTextEdit::AlignLeft)
3198 horizontalAlignment = QQuickTextEdit::AlignRight;
3199 else if (horizontalAlignment == QQuickTextEdit::AlignRight)
3200 horizontalAlignment = QQuickTextEdit::AlignLeft;
3201 }
3202 if (!hAlignImplicit)
3203 opt.setAlignment((Qt::Alignment)(int)(horizontalAlignment | vAlign));
3204 else
3205 opt.setAlignment(Qt::Alignment(vAlign));
3206
3207#if QT_CONFIG(im)
3209 opt.setTextDirection(qGuiApp->inputMethod()->inputDirection());
3210 } else
3211#endif
3212 {
3213 opt.setTextDirection(contentDirection);
3214 }
3215
3216 QTextOption::WrapMode oldWrapMode = opt.wrapMode();
3217 opt.setWrapMode(QTextOption::WrapMode(wrapMode));
3218
3219 bool oldUseDesignMetrics = opt.useDesignMetrics();
3220 opt.setUseDesignMetrics(renderType != QQuickTextEdit::NativeRendering);
3221
3222 if (oldWrapMode != opt.wrapMode() || oldAlignment != opt.alignment()
3223 || oldTextDirection != opt.textDirection()
3224 || oldUseDesignMetrics != opt.useDesignMetrics()) {
3226 }
3227}
3228
3230{
3232 switch (quickDocument->status()) {
3235 switch (QQuickTextDocumentPrivate::get(quickDocument)->detectedFormat) {
3236 case Qt::RichText:
3238 markdownText = false;
3239 break;
3240 case Qt::MarkdownText:
3241 richText = false;
3243 break;
3244 case Qt::PlainText:
3245 richText = false;
3246 markdownText = false;
3247 break;
3248 case Qt::AutoText: // format not detected
3249 break;
3250 }
3251 break;
3252 default:
3253 break;
3254 }
3255}
3256
3263
3270
3272{
3273 Q_Q(QQuickTextEdit);
3274 bool focus = event->type() == QEvent::FocusIn;
3275 if (!q->isReadOnly())
3276 q->setCursorVisible(focus);
3278 if (focus) {
3279 q->q_updateAlignment();
3280#if QT_CONFIG(im)
3281 if (focusOnPress && !q->isReadOnly())
3282 qGuiApp->inputMethod()->show();
3283 q->connect(QGuiApplication::inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
3284 q, SLOT(q_updateAlignment()));
3285#endif
3286 } else {
3287#if QT_CONFIG(im)
3288 q->disconnect(QGuiApplication::inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
3289 q, SLOT(q_updateAlignment()));
3290#endif
3291 if (event->reason() != Qt::ActiveWindowFocusReason
3292 && event->reason() != Qt::PopupFocusReason
3295 q->deselect();
3296
3297 emit q->editingFinished();
3298 }
3299}
3300
3302{
3303 engine->addToSceneGraph(node, QQuickText::Normal, QColor());
3304 it = textNodeMap.insert(it, TextNode(startPos, node));
3305 ++it;
3306 root->appendChildNode(node);
3308}
3309
3318
3319void QQuickTextEdit::q_canPasteChanged()
3320{
3321 Q_D(QQuickTextEdit);
3322 bool old = d->canPaste;
3323 d->canPaste = d->control->canPaste();
3324 bool changed = old!=d->canPaste || !d->canPasteValid;
3325 d->canPasteValid = true;
3326 if (changed)
3327 emit canPasteChanged();
3328}
3329
3339{
3340 Q_D(const QQuickTextEdit);
3341 start = qBound(0, start, d->document->characterCount() - 1);
3342 end = qBound(0, end, d->document->characterCount() - 1);
3343 QTextCursor cursor(d->document);
3344 cursor.setPosition(start, QTextCursor::MoveAnchor);
3345 cursor.setPosition(end, QTextCursor::KeepAnchor);
3346#if QT_CONFIG(texthtmlparser)
3347 return d->richText || d->markdownText
3348 ? cursor.selectedText()
3349 : cursor.selection().toPlainText();
3350#else
3351 return cursor.selection().toPlainText();
3352#endif
3353}
3354
3364{
3365 Q_D(const QQuickTextEdit);
3366
3367 start = qBound(0, start, d->document->characterCount() - 1);
3368 end = qBound(0, end, d->document->characterCount() - 1);
3369
3370 QTextCursor cursor(d->document);
3371 cursor.setPosition(start, QTextCursor::MoveAnchor);
3372 cursor.setPosition(end, QTextCursor::KeepAnchor);
3373
3374 if (d->richText) {
3375#if QT_CONFIG(texthtmlparser)
3376 return cursor.selection().toHtml();
3377#else
3378 return cursor.selection().toPlainText();
3379#endif
3380 } else if (d->markdownText) {
3381#if QT_CONFIG(textmarkdownwriter)
3382 return cursor.selection().toMarkdown();
3383#else
3384 return cursor.selection().toPlainText();
3385#endif
3386 } else {
3387 return cursor.selection().toPlainText();
3388 }
3389}
3390
3397{
3398 Q_D(QQuickTextEdit);
3399 if (position < 0 || position >= d->document->characterCount())
3400 return;
3401 QTextCursor cursor(d->document);
3402 cursor.setPosition(position);
3403 d->richText = d->richText || (d->format == AutoText && Qt::mightBeRichText(text));
3404 if (d->richText) {
3405#if QT_CONFIG(texthtmlparser)
3406 cursor.insertHtml(text);
3407#else
3408 cursor.insertText(text);
3409#endif
3410 } else if (d->markdownText) {
3411#if QT_CONFIG(textmarkdownreader)
3412 cursor.insertMarkdown(text);
3413#else
3414 cursor.insertText(text);
3415#endif
3416 } else {
3417 cursor.insertText(text);
3418 }
3419 d->control->updateCursorRectangle(false);
3420}
3421
3429{
3430 Q_D(QQuickTextEdit);
3431 start = qBound(0, start, d->document->characterCount() - 1);
3432 end = qBound(0, end, d->document->characterCount() - 1);
3433 QTextCursor cursor(d->document);
3434 cursor.setPosition(start, QTextCursor::MoveAnchor);
3435 cursor.setPosition(end, QTextCursor::KeepAnchor);
3436 cursor.removeSelectedText();
3437 d->control->updateCursorRectangle(false);
3438}
3439
3453{
3454 Q_D(QQuickTextEdit);
3455 if (!d->quickDocument) {
3456 d->quickDocument = new QQuickTextDocument(this);
3457 connect(d->quickDocument, &QQuickTextDocument::statusChanged, d->quickDocument,
3458 [d]() { d->onDocumentStatusChanged(); } );
3459 }
3460 return d->quickDocument;
3461}
3462
3468
3469#if QT_CONFIG(cursor)
3470void QQuickTextEditPrivate::updateMouseCursorShape()
3471{
3472 Q_Q(QQuickTextEdit);
3473 q->setCursor(q->isReadOnly() && !q->selectByMouse() ? Qt::ArrowCursor : Qt::IBeamCursor);
3474}
3475#endif
3476
3507{
3508 Q_D(const QQuickTextEdit);
3509 if (const_cast<QQuickTextEditPrivate *>(d)->isLinkHoveredConnected()) {
3510 return d->control->hoveredLink();
3511 } else {
3512#if QT_CONFIG(cursor)
3513 if (QQuickWindow *wnd = window()) {
3514 QPointF pos = QCursor::pos(wnd->screen()) - wnd->position() - mapToScene(QPointF(0, 0));
3515 return d->control->anchorAt(pos);
3516 }
3517#endif // cursor
3518 }
3519 return QString();
3520}
3521
3523{
3524 Q_D(QQuickTextEdit);
3525 if (d->isLinkHoveredConnected())
3526 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
3527 event->ignore();
3528}
3529
3531{
3532 Q_D(QQuickTextEdit);
3533 if (d->isLinkHoveredConnected())
3534 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
3535 event->ignore();
3536}
3537
3539{
3540 Q_D(QQuickTextEdit);
3541 if (d->isLinkHoveredConnected())
3542 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
3543 event->ignore();
3544}
3545
3555void QQuickTextEdit::append(const QString &text)
3556{
3557 Q_D(QQuickTextEdit);
3558 QTextCursor cursor(d->document);
3559 cursor.beginEditBlock();
3560 cursor.movePosition(QTextCursor::End);
3561
3562 if (!d->document->isEmpty())
3563 cursor.insertBlock();
3564
3565 if (d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(text))) {
3566#if QT_CONFIG(texthtmlparser)
3567 cursor.insertHtml(text);
3568#else
3569 cursor.insertText(text);
3570#endif
3571 } else if (d->format == MarkdownText) {
3572#if QT_CONFIG(textmarkdownreader)
3573 cursor.insertMarkdown(text);
3574#else
3575 cursor.insertText(text);
3576#endif
3577 } else {
3578 cursor.insertText(text);
3579 }
3580
3581 cursor.endEditBlock();
3582 d->control->updateCursorRectangle(false);
3583}
3584
3594QString QQuickTextEdit::linkAt(qreal x, qreal y) const
3595{
3596 Q_D(const QQuickTextEdit);
3597 return d->control->anchorAt(QPointF(x + topPadding(), y + leftPadding()));
3598}
3599
3612{
3613 Q_D(const QQuickTextEdit);
3614 return d->padding();
3615}
3616
3618{
3619 Q_D(QQuickTextEdit);
3620 if (qFuzzyCompare(d->padding(), padding))
3621 return;
3622
3623 d->extra.value().padding = padding;
3624 updateSize();
3625 if (isComponentComplete()) {
3627 update();
3628 }
3629 emit paddingChanged();
3630 if (!d->extra.isAllocated() || !d->extra->explicitTopPadding)
3631 emit topPaddingChanged();
3632 if (!d->extra.isAllocated() || !d->extra->explicitLeftPadding)
3633 emit leftPaddingChanged();
3634 if (!d->extra.isAllocated() || !d->extra->explicitRightPadding)
3635 emit rightPaddingChanged();
3636 if (!d->extra.isAllocated() || !d->extra->explicitBottomPadding)
3637 emit bottomPaddingChanged();
3638}
3639
3641{
3642 setPadding(0);
3643}
3644
3646{
3647 Q_D(const QQuickTextEdit);
3648 if (d->extra.isAllocated() && d->extra->explicitTopPadding)
3649 return d->extra->topPadding;
3650 return d->padding();
3651}
3652
3654{
3655 Q_D(QQuickTextEdit);
3656 d->setTopPadding(padding);
3657}
3658
3660{
3661 Q_D(QQuickTextEdit);
3662 d->setTopPadding(0, true);
3663}
3664
3666{
3667 Q_D(const QQuickTextEdit);
3668 if (d->extra.isAllocated() && d->extra->explicitLeftPadding)
3669 return d->extra->leftPadding;
3670 return d->padding();
3671}
3672
3674{
3675 Q_D(QQuickTextEdit);
3676 d->setLeftPadding(padding);
3677}
3678
3680{
3681 Q_D(QQuickTextEdit);
3682 d->setLeftPadding(0, true);
3683}
3684
3686{
3687 Q_D(const QQuickTextEdit);
3688 if (d->extra.isAllocated() && d->extra->explicitRightPadding)
3689 return d->extra->rightPadding;
3690 return d->padding();
3691}
3692
3694{
3695 Q_D(QQuickTextEdit);
3696 d->setRightPadding(padding);
3697}
3698
3700{
3701 Q_D(QQuickTextEdit);
3702 d->setRightPadding(0, true);
3703}
3704
3706{
3707 Q_D(const QQuickTextEdit);
3708 if (d->extra.isAllocated() && d->extra->explicitBottomPadding)
3709 return d->extra->bottomPadding;
3710 return d->padding();
3711}
3712
3714{
3715 Q_D(QQuickTextEdit);
3716 d->setBottomPadding(padding);
3717}
3718
3720{
3721 Q_D(QQuickTextEdit);
3722 d->setBottomPadding(0, true);
3723}
3724
3734{
3735 Q_D(const QQuickTextEdit);
3736 return d->document->defaultTextOption().tabStopDistance();
3737}
3738
3740{
3741 Q_D(QQuickTextEdit);
3742 QTextOption textOptions = d->document->defaultTextOption();
3743 if (textOptions.tabStopDistance() == distance)
3744 return;
3745
3746 textOptions.setTabStopDistance(distance);
3747 d->document->setDefaultTextOption(textOptions);
3748 emit tabStopDistanceChanged(distance);
3749}
3750
3762void QQuickTextEdit::clear()
3763{
3764 Q_D(QQuickTextEdit);
3765 d->resetInputMethod();
3766 d->control->clear();
3767}
3768
3769#ifndef QT_NO_DEBUG_STREAM
3771{
3772 QDebugStateSaver saver(debug);
3773 debug.space();
3774 debug << "Node(startPos:" << n.m_startPos << "dirty:" << n.m_dirty << n.m_node << ')';
3775 return debug;
3776}
3777#endif
3778
3779#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0)
3780void QQuickTextEdit::setOldSelectionDefault()
3781{
3782 Q_D(QQuickTextEdit);
3783 d->selectByMouse = false;
3784 setKeepMouseGrab(false);
3785 d->control->setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByMouse);
3786 d->control->setTouchDragSelectionEnabled(true);
3787 qCDebug(lcTextEdit, "pre-6.4 behavior chosen: selectByMouse defaults false; if enabled, touchscreen acts like a mouse");
3788}
3789
3790// TODO in 6.7.0: remove the note about versions prior to 6.4 in selectByMouse() documentation
3791QQuickPre64TextEdit::QQuickPre64TextEdit(QQuickItem *parent)
3793{
3794 setOldSelectionDefault();
3795}
3796#endif
3797
3799
3800#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
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:398
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:586
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:1162
iterator begin()
Definition qset.h:136
iterator end()
Definition qset.h:140
\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:9307
\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.
qreal ascent() const
Returns the line's ascent.
\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
static void * context
#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 Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
int qCeil(T v)
Definition qmath.h:36
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:44
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
#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]
QQmlContext * qmlContext(const QObject *obj)
Definition qqml.cpp:75
#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:101
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