70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
94
95
96
97
98
99
105#if !defined(QQUICKTEXT_LARGETEXT_THRESHOLD)
106 #define QQUICKTEXT_LARGETEXT_THRESHOLD 10000
112 class RootNode :
public QSGTransformNode
115 RootNode() : cursorNode(
nullptr), frameDecorationsNode(
nullptr)
118 void resetFrameDecorations(QSGInternalTextNode* newNode)
120 if (frameDecorationsNode) {
121 removeChildNode(frameDecorationsNode);
122 delete frameDecorationsNode;
124 frameDecorationsNode = newNode;
125 newNode->setFlag(QSGNode::OwnedByParent);
128 void resetCursorNode(QSGInternalRectangleNode* newNode)
131 removeChildNode(cursorNode);
133 cursorNode = newNode;
135 appendChildNode(cursorNode);
136 cursorNode->setFlag(QSGNode::OwnedByParent);
140 QSGInternalRectangleNode *cursorNode;
141 QSGInternalTextNode* frameDecorationsNode;
146QQuickTextEdit::QQuickTextEdit(QQuickItem *parent)
147: QQuickImplicitSizeItem(*(
new QQuickTextEditPrivate), parent)
153QQuickTextEdit::~QQuickTextEdit()
156 qDeleteAll(d->pixmapsInProgress);
159QQuickTextEdit::QQuickTextEdit(QQuickTextEditPrivate &dd, QQuickItem *parent)
160: QQuickImplicitSizeItem(dd, parent)
166QString QQuickTextEdit::text()
const
168 Q_D(
const QQuickTextEdit);
169 if (!d->textCached && isComponentComplete()) {
170 QQuickTextEditPrivate *d =
const_cast<QQuickTextEditPrivate *>(d_func());
171#if QT_CONFIG(texthtmlparser)
173 d->text = d->control->toHtml();
176#if QT_CONFIG(textmarkdownwriter)
178 d->text = d->control->toMarkdown();
181 d->text = d->control->toPlainText();
182 d->textCached =
true;
188
189
190
191
192
193
196
197
198
199
200
201
202
203
207
208
209
210
213
214
215
216
219
220
221
222
225
226
227
228
231
232
233
234
237
238
239
240
243
244
245
246
247
248
249
250
253
254
255
256
257
258
261
262
263
264
265
266
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
286
287
288
289
290
291
292
295
296
297
298
299
302
303
304
305
306
309
310
311
312
313
316
317
318
319
320
323
324
325
326
327
330
331
332
333
334
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365void QQuickTextEdit::setText(
const QString &text)
368 if (QQuickTextEdit::text() == text)
371 d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(text));
372 d->markdownText = d->format == MarkdownText;
373 if (!isComponentComplete()) {
375 }
else if (d->richText) {
376#if QT_CONFIG(texthtmlparser)
377 d->control->setHtml(text);
379 d->control->setPlainText(text);
381 }
else if (d->markdownText) {
382 d->control->setMarkdownText(text);
384 d->control->setPlainText(text);
386 setFlag(QQuickItem::ItemObservesViewport, text.size() > QQuickTextEditPrivate::largeTextSizeThreshold);
389void QQuickTextEdit::invalidate()
391 QMetaObject::invokeMethod(
this, &QQuickTextEdit::q_invalidate);
394void QQuickTextEdit::q_invalidate()
397 if (isComponentComplete()) {
398 if (d->document !=
nullptr)
399 d->document->markContentsDirty(0, d->document->characterCount());
400 invalidateFontCaches();
401 d->updateType = QQuickTextEditPrivate::UpdateAll;
407
408
409
410
411
412
413
414
415
416
417
418QString QQuickTextEdit::preeditText()
const
420 Q_D(
const QQuickTextEdit);
421 return d->control->preeditText();
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479QQuickTextEdit::TextFormat QQuickTextEdit::textFormat()
const
481 Q_D(
const QQuickTextEdit);
485void QQuickTextEdit::setTextFormat(TextFormat format)
488 if (format == d->format)
491 auto mightBeRichText = [
this]() {
492 return Qt::mightBeRichText(text());
495 auto findSourceFormat = [d, mightBeRichText](Qt::TextFormat detectedFormat) {
496 if (d->format == PlainText)
498 if (d->richText)
return RichText;
499 if (d->markdownText)
return MarkdownText;
500 if (detectedFormat == Qt::AutoText && mightBeRichText())
505 auto findDestinationFormat = [format, mightBeRichText](Qt::TextFormat detectedFormat, TextFormat sourceFormat) {
506 if (format == AutoText) {
507 if (detectedFormat == Qt::MarkdownText || (detectedFormat == Qt::AutoText && sourceFormat == MarkdownText))
509 if (detectedFormat == Qt::RichText || (detectedFormat == Qt::AutoText && (sourceFormat == RichText || mightBeRichText())))
516 bool textCachedChanged =
false;
517 bool converted =
false;
519 if (isComponentComplete()) {
520 Qt::TextFormat detectedFormat = Qt::AutoText;
521 if (d->quickDocument) {
524 detectedFormat = QQuickTextDocumentPrivate::get(d->quickDocument)->detectedFormat;
527 const TextFormat sourceFormat = findSourceFormat(detectedFormat);
528 const TextFormat destinationFormat = findDestinationFormat(detectedFormat, sourceFormat);
530 d->richText = destinationFormat == RichText;
531 d->markdownText = destinationFormat == MarkdownText;
534 if (format != PlainText && (sourceFormat != destinationFormat)) {
535 d->textCached =
false;
536 textCachedChanged =
true;
539 switch (destinationFormat) {
541#if QT_CONFIG(texthtmlparser)
542 if (sourceFormat == RichText) {
546 d->control->setPlainText(d->textCached ? d->text : d->control->toHtml());
550#if QT_CONFIG(textmarkdownwriter) && QT_CONFIG(textmarkdownreader)
551 if (sourceFormat == MarkdownText) {
555 d->control->setPlainText(d->textCached ? d->text : d->control->toMarkdown());
561#if QT_CONFIG(texthtmlparser)
562 switch (sourceFormat) {
565 d->control->setHtml(d->control->toHtml());
571 d->control->setHtml(d->textCached ? d->text : d->control->toPlainText());
581#if QT_CONFIG(textmarkdownwriter) && QT_CONFIG(textmarkdownreader)
582 switch (sourceFormat) {
585 d->control->setMarkdownText(d->control->toMarkdown());
591 d->control->setMarkdownText(d->textCached ? d->text : d->control->toPlainText());
607 d->richText = format == RichText || (format == AutoText && (d->richText || mightBeRichText()));
608 d->markdownText = format == MarkdownText;
611 qCDebug(lcTextEdit) << d->format <<
"->" << format
612 <<
"rich?" << d->richText <<
"md?" << d->markdownText
613 <<
"converted?" << converted <<
"cache invalidated?" << textCachedChanged;
616 d->control->setAcceptRichText(d->format != PlainText);
617 emit textFormatChanged(d->format);
618 if (textCachedChanged)
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647QQuickTextEdit::RenderType QQuickTextEdit::renderType()
const
649 Q_D(
const QQuickTextEdit);
650 return d->renderType;
653void QQuickTextEdit::setRenderType(QQuickTextEdit::RenderType renderType)
656 if (d->renderType == renderType)
659 d->renderType = renderType;
660 emit renderTypeChanged();
661 d->updateDefaultTextOption();
663 if (isComponentComplete())
667QFont QQuickTextEdit::font()
const
669 Q_D(
const QQuickTextEdit);
670 return d->sourceFont;
673void QQuickTextEdit::setFont(
const QFont &font)
676 if (d->sourceFont == font)
679 d->sourceFont = font;
680 QFont oldFont = d->font;
682 if (d->font.pointSizeF() != -1) {
684 qreal size = qRound(d->font.pointSizeF()*2.0);
685 d->font.setPointSizeF(size/2.0);
688 if (oldFont != d->font) {
689 d->document->setDefaultFont(d->font);
691 d->cursorItem->setHeight(QFontMetrics(d->font).height());
692 moveCursorDelegate();
695 updateWholeDocument();
697 updateInputMethod(Qt::ImCursorRectangle | Qt::ImAnchorRectangle | Qt::ImFont);
700 emit fontChanged(d->sourceFont);
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718QColor QQuickTextEdit::color()
const
720 Q_D(
const QQuickTextEdit);
724void QQuickTextEdit::setColor(
const QColor &color)
727 if (d->color == color)
731 updateWholeDocument();
732 emit colorChanged(d->color);
736
737
738
739
740QColor QQuickTextEdit::selectionColor()
const
742 Q_D(
const QQuickTextEdit);
743 return d->selectionColor;
746void QQuickTextEdit::setSelectionColor(
const QColor &color)
749 if (d->selectionColor == color)
752 d->selectionColor = color;
753 updateWholeDocument();
754 emit selectionColorChanged(d->selectionColor);
758
759
760
761
762QColor QQuickTextEdit::selectedTextColor()
const
764 Q_D(
const QQuickTextEdit);
765 return d->selectedTextColor;
768void QQuickTextEdit::setSelectedTextColor(
const QColor &color)
771 if (d->selectedTextColor == color)
774 d->selectedTextColor = color;
775 updateWholeDocument();
776 emit selectedTextColorChanged(d->selectedTextColor);
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811QQuickTextEdit::HAlignment QQuickTextEdit::hAlign()
const
813 Q_D(
const QQuickTextEdit);
817void QQuickTextEdit::setHAlign(HAlignment align)
821 if (d->setHAlign(align,
true) && isComponentComplete()) {
822 d->updateDefaultTextOption();
824 updateWholeDocument();
825 moveCursorDelegate();
829void QQuickTextEdit::resetHAlign()
832 d->hAlignImplicit =
true;
833 if (d->determineHorizontalAlignment() && isComponentComplete()) {
834 d->updateDefaultTextOption();
836 updateWholeDocument();
837 moveCursorDelegate();
841QQuickTextEdit::HAlignment QQuickTextEdit::effectiveHAlign()
const
843 Q_D(
const QQuickTextEdit);
844 QQuickTextEdit::HAlignment effectiveAlignment = d->hAlign;
845 if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
847 case QQuickTextEdit::AlignLeft:
848 effectiveAlignment = QQuickTextEdit::AlignRight;
850 case QQuickTextEdit::AlignRight:
851 effectiveAlignment = QQuickTextEdit::AlignLeft;
857 return effectiveAlignment;
860bool QQuickTextEditPrivate::setHAlign(QQuickTextEdit::HAlignment align,
bool forceAlign)
863 if (hAlign == align && !forceAlign)
866 const bool wasImplicit = hAlignImplicit;
867 const auto oldEffectiveHAlign = q->effectiveHAlign();
869 hAlignImplicit = !forceAlign;
870 if (hAlign != align) {
872 emit q->horizontalAlignmentChanged(align);
875 if (q->effectiveHAlign() != oldEffectiveHAlign) {
876 emit q->effectiveHorizontalAlignmentChanged();
880 if (forceAlign && wasImplicit) {
883 emit q->effectiveHorizontalAlignmentChanged();
888Qt::LayoutDirection QQuickTextEditPrivate::textDirection(
const QString &text)
const
890 const QChar *character = text.constData();
891 while (!character->isNull()) {
892 switch (character->direction()) {
894 return Qt::LeftToRight;
898 return Qt::RightToLeft;
904 return Qt::LayoutDirectionAuto;
907bool QQuickTextEditPrivate::determineHorizontalAlignment()
910 if (!hAlignImplicit || !q->isComponentComplete())
913 Qt::LayoutDirection direction = contentDirection;
915 if (direction == Qt::LayoutDirectionAuto) {
916 QTextBlock block = control->textCursor().block();
919 direction = textDirection(block.layout()->preeditAreaText());
921 if (direction == Qt::LayoutDirectionAuto)
922 direction = qGuiApp->inputMethod()->inputDirection();
925 const auto implicitHAlign = direction == Qt::RightToLeft ?
926 QQuickTextEdit::AlignRight : QQuickTextEdit::AlignLeft;
927 return setHAlign(implicitHAlign);
930void QQuickTextEditPrivate::mirrorChange()
933 if (q->isComponentComplete()) {
934 if (!hAlignImplicit && (hAlign == QQuickTextEdit::AlignRight || hAlign == QQuickTextEdit::AlignLeft)) {
935 updateDefaultTextOption();
937 q->updateWholeDocument();
938 emit q->effectiveHorizontalAlignmentChanged();
943bool QQuickTextEditPrivate::transformChanged(QQuickItem *transformedItem)
946 qCDebug(lcVP) << q <<
"sees that" << transformedItem <<
"moved in VP" << q->clipRect();
951 if (flags & QQuickItem::ItemObservesViewport) {
952 if (QQuickItem *viewport = q->viewportItem()) {
953 QRectF vp = q->mapRectFromItem(viewport, viewport->clipRect());
954 if (!(vp.top() > renderedRegion.top() && vp.bottom() < renderedRegion.bottom())) {
955 qCDebug(lcVP) <<
"viewport" << vp <<
"now goes beyond rendered region" << renderedRegion <<
"; updating";
956 q->updateWholeDocument();
958 const bool textCursorVisible = cursorVisible && q->cursorRectangle().intersects(vp);
960 cursorItem->setVisible(textCursorVisible);
962 control->setCursorVisible(textCursorVisible);
965 return QQuickImplicitSizeItemPrivate::transformChanged(transformedItem);
969Qt::InputMethodHints QQuickTextEditPrivate::effectiveInputMethodHints()
const
971 return inputMethodHints | Qt::ImhMultiLine;
975#if QT_CONFIG(accessibility)
976void QQuickTextEditPrivate::accessibilityActiveChanged(
bool active)
982 if (QQuickAccessibleAttached *accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(
983 qmlAttachedPropertiesObject<QQuickAccessibleAttached>(q,
true))) {
984 accessibleAttached->setRole(effectiveAccessibleRole());
985 accessibleAttached->set_readOnly(q->isReadOnly());
989QAccessible::Role QQuickTextEditPrivate::accessibleRole()
const
991 return QAccessible::EditableText;
995void QQuickTextEditPrivate::setTopPadding(qreal value,
bool reset)
998 qreal oldPadding = q->topPadding();
999 if (!reset || extra.isAllocated()) {
1000 extra.value().topPadding = value;
1001 extra.value().explicitTopPadding = !reset;
1003 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
1005 q->updateWholeDocument();
1006 emit q->topPaddingChanged();
1010void QQuickTextEditPrivate::setLeftPadding(qreal value,
bool reset)
1012 Q_Q(QQuickTextEdit);
1013 qreal oldPadding = q->leftPadding();
1014 if (!reset || extra.isAllocated()) {
1015 extra.value().leftPadding = value;
1016 extra.value().explicitLeftPadding = !reset;
1018 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
1020 q->updateWholeDocument();
1021 emit q->leftPaddingChanged();
1025void QQuickTextEditPrivate::setRightPadding(qreal value,
bool reset)
1027 Q_Q(QQuickTextEdit);
1028 qreal oldPadding = q->rightPadding();
1029 if (!reset || extra.isAllocated()) {
1030 extra.value().rightPadding = value;
1031 extra.value().explicitRightPadding = !reset;
1033 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
1035 q->updateWholeDocument();
1036 emit q->rightPaddingChanged();
1040void QQuickTextEditPrivate::setBottomPadding(qreal value,
bool reset)
1042 Q_Q(QQuickTextEdit);
1043 qreal oldPadding = q->bottomPadding();
1044 if (!reset || extra.isAllocated()) {
1045 extra.value().bottomPadding = value;
1046 extra.value().explicitBottomPadding = !reset;
1048 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
1050 q->updateWholeDocument();
1051 emit q->bottomPaddingChanged();
1055bool QQuickTextEditPrivate::isImplicitResizeEnabled()
const
1057 return !extra.isAllocated() || extra->implicitResize;
1060void QQuickTextEditPrivate::setImplicitResizeEnabled(
bool enabled)
1063 extra.value().implicitResize =
false;
1064 else if (extra.isAllocated())
1065 extra->implicitResize =
true;
1068QQuickTextEdit::VAlignment QQuickTextEdit::vAlign()
const
1070 Q_D(
const QQuickTextEdit);
1074void QQuickTextEdit::setVAlign(QQuickTextEdit::VAlignment alignment)
1076 Q_D(QQuickTextEdit);
1077 if (alignment == d->vAlign)
1079 d->vAlign = alignment;
1080 d->updateDefaultTextOption();
1082 updateWholeDocument();
1083 moveCursorDelegate();
1084 emit verticalAlignmentChanged(d->vAlign);
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107QQuickTextEdit::WrapMode QQuickTextEdit::wrapMode()
const
1109 Q_D(
const QQuickTextEdit);
1113void QQuickTextEdit::setWrapMode(WrapMode mode)
1115 Q_D(QQuickTextEdit);
1116 if (mode == d->wrapMode)
1119 d->updateDefaultTextOption();
1121 emit wrapModeChanged();
1125
1126
1127
1128
1129int QQuickTextEdit::lineCount()
const
1131 Q_D(
const QQuickTextEdit);
1132 return d->lineCount;
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1147int QQuickTextEdit::length()
const
1149 Q_D(
const QQuickTextEdit);
1151 return qMax(0, d->document->characterCount() - 1);
1155
1156
1157
1158
1159
1160qreal QQuickTextEdit::contentWidth()
const
1162 Q_D(
const QQuickTextEdit);
1163 return d->contentSize.width();
1167
1168
1169
1170
1171
1172qreal QQuickTextEdit::contentHeight()
const
1174 Q_D(
const QQuickTextEdit);
1175 return d->contentSize.height();
1179
1180
1181
1182
1183
1184
1185
1187QUrl QQuickTextEdit::baseUrl()
const
1189 Q_D(
const QQuickTextEdit);
1190 if (d->baseUrl.isEmpty()) {
1191 if (QQmlContext *context = qmlContext(
this))
1192 const_cast<QQuickTextEditPrivate *>(d)->baseUrl = context->baseUrl();
1197void QQuickTextEdit::setBaseUrl(
const QUrl &url)
1199 Q_D(QQuickTextEdit);
1200 if (baseUrl() != url) {
1203 d->document->setBaseUrl(url);
1204 emit baseUrlChanged();
1208void QQuickTextEdit::resetBaseUrl()
1210 if (QQmlContext *context = qmlContext(
this))
1211 setBaseUrl(context->baseUrl());
1217
1218
1219
1220
1221
1222
1223QRectF QQuickTextEdit::positionToRectangle(
int pos)
const
1225 Q_D(
const QQuickTextEdit);
1226 QTextCursor c(d->document);
1228 return d->control->cursorRect(c).translated(d->xoff, d->yoff);
1233
1234
1235
1236
1237
1238
1239
1240int QQuickTextEdit::positionAt(qreal x, qreal y)
const
1242 Q_D(
const QQuickTextEdit);
1246 int r = d->document->documentLayout()->hitTest(QPointF(x, y), Qt::FuzzyHit);
1248 QTextCursor cursor = d->control->textCursor();
1249 if (r > cursor.position()) {
1253 QTextLayout *layout = cursor.block().layout();
1254 const int preeditLength = layout
1255 ? layout->preeditAreaText().size()
1257 if (preeditLength > 0
1258 && d->document->documentLayout()->blockBoundingRect(cursor.block()).contains(x, y)) {
1259 r = r > cursor.position() + preeditLength
1261 : cursor.position();
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278QQuickTextSelection *QQuickTextEdit::cursorSelection()
const
1280 Q_D(
const QQuickTextEdit);
1281 if (!d->cursorSelection)
1282 d->cursorSelection =
new QQuickTextSelection(
const_cast<QQuickTextEdit *>(
this));
1283 return d->cursorSelection;
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322void QQuickTextEdit::moveCursorSelection(
int pos)
1325 Q_D(QQuickTextEdit);
1326 QTextCursor cursor = d->control->textCursor();
1327 if (cursor.position() == pos)
1329 cursor.setPosition(pos, QTextCursor::KeepAnchor);
1330 d->control->setTextCursor(cursor);
1333void QQuickTextEdit::moveCursorSelection(
int pos, SelectionMode mode)
1335 Q_D(QQuickTextEdit);
1336 QTextCursor cursor = d->control->textCursor();
1337 if (cursor.position() == pos)
1339 if (mode == SelectCharacters) {
1340 cursor.setPosition(pos, QTextCursor::KeepAnchor);
1341 }
else if (cursor.anchor() < pos || (cursor.anchor() == pos && cursor.position() < pos)) {
1342 if (cursor.anchor() > cursor.position()) {
1343 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1344 cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
1345 if (cursor.position() == cursor.anchor())
1346 cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::MoveAnchor);
1348 cursor.setPosition(cursor.position(), QTextCursor::MoveAnchor);
1350 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1351 cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor);
1354 cursor.setPosition(pos, QTextCursor::KeepAnchor);
1355 cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
1356 if (cursor.position() != pos)
1357 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
1358 }
else if (cursor.anchor() > pos || (cursor.anchor() == pos && cursor.position() > pos)) {
1359 if (cursor.anchor() < cursor.position()) {
1360 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1361 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::MoveAnchor);
1363 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1364 cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
1365 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
1366 if (cursor.position() != cursor.anchor()) {
1367 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1368 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::MoveAnchor);
1372 cursor.setPosition(pos, QTextCursor::KeepAnchor);
1373 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
1374 if (cursor.position() != pos) {
1375 cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
1376 cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
1379 d->control->setTextCursor(cursor);
1383
1384
1385
1386
1387
1388
1389bool QQuickTextEdit::isCursorVisible()
const
1391 Q_D(
const QQuickTextEdit);
1392 return d->cursorVisible;
1395void QQuickTextEdit::setCursorVisible(
bool on)
1397 Q_D(QQuickTextEdit);
1398 if (d->cursorVisible == on)
1400 d->cursorVisible = on;
1401 if (on && isComponentComplete())
1402 QQuickTextUtil::createCursor(d);
1403 if (!on && !d->persistentSelection)
1404 d->control->setCursorIsFocusIndicator(
true);
1405 d->control->setCursorVisible(on);
1406 emit cursorVisibleChanged(d->cursorVisible);
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421int QQuickTextEdit::cursorPosition()
const
1423 Q_D(
const QQuickTextEdit);
1424 return d->control->textCursor().position();
1427void QQuickTextEdit::setCursorPosition(
int pos)
1429 Q_D(QQuickTextEdit);
1430 if (pos < 0 || pos >= d->document->characterCount())
1432 QTextCursor cursor = d->control->textCursor();
1433 if (cursor.position() == pos && cursor.anchor() == pos)
1435 cursor.setPosition(pos);
1436 d->control->setTextCursor(cursor);
1437 d->control->updateCursorRectangle(
true);
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453QQmlComponent* QQuickTextEdit::cursorDelegate()
const
1455 Q_D(
const QQuickTextEdit);
1456 return d->cursorComponent;
1459void QQuickTextEdit::setCursorDelegate(QQmlComponent* c)
1461 Q_D(QQuickTextEdit);
1462 QQuickTextUtil::setCursorDelegate(d, c);
1465void QQuickTextEdit::createCursor()
1467 Q_D(QQuickTextEdit);
1468 d->cursorPending =
true;
1469 QQuickTextUtil::createCursor(d);
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482int QQuickTextEdit::selectionStart()
const
1484 Q_D(
const QQuickTextEdit);
1485 return d->control->textCursor().selectionStart();
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498int QQuickTextEdit::selectionEnd()
const
1500 Q_D(
const QQuickTextEdit);
1501 return d->control->textCursor().selectionEnd();
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518QString QQuickTextEdit::selectedText()
const
1520 Q_D(
const QQuickTextEdit);
1521#if QT_CONFIG(texthtmlparser)
1522 return d->richText || d->markdownText
1523 ? d->control->textCursor().selectedText()
1524 : d->control->textCursor().selection().toPlainText();
1526 return d->control->textCursor().selection().toPlainText();
1531
1532
1533
1534
1535
1536bool QQuickTextEdit::focusOnPress()
const
1538 Q_D(
const QQuickTextEdit);
1539 return d->focusOnPress;
1542void QQuickTextEdit::setFocusOnPress(
bool on)
1544 Q_D(QQuickTextEdit);
1545 if (d->focusOnPress == on)
1547 d->focusOnPress = on;
1548 emit activeFocusOnPressChanged(d->focusOnPress);
1552
1553
1554
1555
1556
1557bool QQuickTextEdit::persistentSelection()
const
1559 Q_D(
const QQuickTextEdit);
1560 return d->persistentSelection;
1563void QQuickTextEdit::setPersistentSelection(
bool on)
1565 Q_D(QQuickTextEdit);
1566 if (d->persistentSelection == on)
1568 d->persistentSelection = on;
1569 emit persistentSelectionChanged(d->persistentSelection);
1573
1574
1575
1576
1577qreal QQuickTextEdit::textMargin()
const
1579 Q_D(
const QQuickTextEdit);
1580 return d->textMargin;
1583void QQuickTextEdit::setTextMargin(qreal margin)
1585 Q_D(QQuickTextEdit);
1586 if (d->textMargin == margin)
1588 d->textMargin = margin;
1589 d->document->setDocumentMargin(d->textMargin);
1590 emit textMarginChanged(d->textMargin);
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1630Qt::InputMethodHints QQuickTextEdit::inputMethodHints()
const
1635 Q_D(
const QQuickTextEdit);
1636 return d->inputMethodHints;
1640void QQuickTextEdit::setInputMethodHints(Qt::InputMethodHints hints)
1645 Q_D(QQuickTextEdit);
1647 if (hints == d->inputMethodHints)
1650 d->inputMethodHints = hints;
1651 updateInputMethod(Qt::ImHints);
1652 emit inputMethodHintsChanged();
1656void QQuickTextEdit::geometryChange(
const QRectF &newGeometry,
const QRectF &oldGeometry)
1658 Q_D(QQuickTextEdit);
1659 if (!d->inLayout && ((newGeometry.width() != oldGeometry.width())
1660 || (newGeometry.height() != oldGeometry.height()))) {
1662 updateWholeDocument();
1663 if (widthValid() || heightValid())
1664 moveCursorDelegate();
1666 QQuickImplicitSizeItem::geometryChange(newGeometry, oldGeometry);
1669void QQuickTextEdit::itemChange(ItemChange change,
const ItemChangeData &value)
1671 Q_D(QQuickTextEdit);
1674 case ItemDevicePixelRatioHasChanged:
1675 if (d->containsUnscalableGlyphs) {
1680 updateWholeDocument();
1687 QQuickImplicitSizeItem::itemChange(change, value);
1691
1692
1693
1694void QQuickTextEdit::componentComplete()
1696 Q_D(QQuickTextEdit);
1697 QQuickImplicitSizeItem::componentComplete();
1699 const QUrl url = baseUrl();
1700 const QQmlContext *context = qmlContext(
this);
1701 d->document->setBaseUrl(context ? context->resolvedUrl(url) : url);
1702 if (!d->text.isEmpty()) {
1703#if QT_CONFIG(texthtmlparser)
1705 d->control->setHtml(d->text);
1708#if QT_CONFIG(textmarkdownreader)
1709 if (d->markdownText)
1710 d->control->setMarkdownText(d->text);
1713 d->control->setPlainText(d->text);
1714 }
else if (!d->document->isEmpty()) {
1718 d->textCached =
false;
1723 d->determineHorizontalAlignment();
1724 d->updateDefaultTextOption();
1728 if (d->cursorComponent && isCursorVisible())
1729 QQuickTextUtil::createCursor(d);
1732#if QT_CONFIG(accessibility)
1733 if (QAccessible::isActive())
1734 d->accessibilityActiveChanged(
true);
1738int QQuickTextEdit::resourcesLoading()
const
1740 Q_D(
const QQuickTextEdit);
1741 return d->pixmapsInProgress.size();
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758bool QQuickTextEdit::selectByKeyboard()
const
1760 Q_D(
const QQuickTextEdit);
1761 if (d->selectByKeyboardSet)
1762 return d->selectByKeyboard;
1763 return !isReadOnly();
1766void QQuickTextEdit::setSelectByKeyboard(
bool on)
1768 Q_D(QQuickTextEdit);
1769 bool was = selectByKeyboard();
1770 if (!d->selectByKeyboardSet || was != on) {
1771 d->selectByKeyboardSet =
true;
1772 d->selectByKeyboard = on;
1774 d->control->setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByKeyboard);
1776 d->control->setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByKeyboard);
1777 emit selectByKeyboardChanged(on);
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804bool QQuickTextEdit::selectByMouse()
const
1806 Q_D(
const QQuickTextEdit);
1807 return d->selectByMouse;
1810void QQuickTextEdit::setSelectByMouse(
bool on)
1812 Q_D(QQuickTextEdit);
1813 if (d->selectByMouse == on)
1816 d->selectByMouse = on;
1817 setKeepMouseGrab(on);
1819 d->control->setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByMouse);
1821 d->control->setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByMouse);
1823#if QT_CONFIG(cursor)
1824 d->updateMouseCursorShape();
1826 emit selectByMouseChanged(on);
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839QQuickTextEdit::SelectionMode QQuickTextEdit::mouseSelectionMode()
const
1841 Q_D(
const QQuickTextEdit);
1842 return d->mouseSelectionMode;
1845void QQuickTextEdit::setMouseSelectionMode(SelectionMode mode)
1847 Q_D(QQuickTextEdit);
1848 if (d->mouseSelectionMode != mode) {
1849 d->mouseSelectionMode = mode;
1850 d->control->setWordSelectionEnabled(mode == SelectWords);
1851 emit mouseSelectionModeChanged(mode);
1856
1857
1858
1859
1860
1861
1862
1863void QQuickTextEdit::setReadOnly(
bool r)
1865 Q_D(QQuickTextEdit);
1866 if (r == isReadOnly())
1870 setFlag(QQuickItem::ItemAcceptsInputMethod, !r);
1872 Qt::TextInteractionFlags flags = Qt::LinksAccessibleByMouse;
1873 if (d->selectByMouse)
1874 flags = flags | Qt::TextSelectableByMouse;
1875 if (d->selectByKeyboardSet && d->selectByKeyboard)
1876 flags = flags | Qt::TextSelectableByKeyboard;
1877 else if (!d->selectByKeyboardSet && !r)
1878 flags = flags | Qt::TextSelectableByKeyboard;
1880 flags = flags | Qt::TextEditable;
1881 d->control->setTextInteractionFlags(flags);
1882 d->control->moveCursor(QTextCursor::End);
1885 updateInputMethod(Qt::ImEnabled);
1887#if QT_CONFIG(cursor)
1888 d->updateMouseCursorShape();
1890 q_canPasteChanged();
1891 emit readOnlyChanged(r);
1892 if (!d->selectByKeyboardSet)
1893 emit selectByKeyboardChanged(!r);
1895 setCursorVisible(
false);
1896 }
else if (hasActiveFocus()) {
1897 setCursorVisible(
true);
1900#if QT_CONFIG(accessibility)
1901 if (QAccessible::isActive()) {
1902 if (QQuickAccessibleAttached *accessibleAttached = QQuickAccessibleAttached::attachedProperties(
this))
1903 accessibleAttached->set_readOnly(r);
1908bool QQuickTextEdit::isReadOnly()
const
1910 Q_D(
const QQuickTextEdit);
1911 return !(d->control->textInteractionFlags() & Qt::TextEditable);
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924QRectF QQuickTextEdit::cursorRectangle()
const
1926 Q_D(
const QQuickTextEdit);
1927 return d->control->cursorRect().translated(d->xoff, d->yoff);
1930bool QQuickTextEdit::event(QEvent *event)
1932 Q_D(QQuickTextEdit);
1933 bool state = QQuickImplicitSizeItem::event(event);
1934 if (event->type() == QEvent::ShortcutOverride && !event->isAccepted()) {
1935 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955bool QQuickTextEdit::overwriteMode()
const
1957 Q_D(
const QQuickTextEdit);
1958 return d->control->overwriteMode();
1961void QQuickTextEdit::setOverwriteMode(
bool overwrite)
1963 Q_D(QQuickTextEdit);
1964 d->control->setOverwriteMode(overwrite);
1968
1969
1970
1971void QQuickTextEdit::keyPressEvent(QKeyEvent *event)
1973 Q_D(QQuickTextEdit);
1974 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
1975 if (!event->isAccepted())
1976 QQuickImplicitSizeItem::keyPressEvent(event);
1980
1981
1982
1983void QQuickTextEdit::keyReleaseEvent(QKeyEvent *event)
1985 Q_D(QQuickTextEdit);
1986 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
1987 if (!event->isAccepted())
1988 QQuickImplicitSizeItem::keyReleaseEvent(event);
1992
1993
1994
1995
1996void QQuickTextEdit::deselect()
1998 Q_D(QQuickTextEdit);
1999 QTextCursor c = d->control->textCursor();
2001 d->control->setTextCursor(c);
2005
2006
2007
2008
2009void QQuickTextEdit::selectAll()
2011 Q_D(QQuickTextEdit);
2012 d->control->selectAll();
2016
2017
2018
2019
2020void QQuickTextEdit::selectWord()
2022 Q_D(QQuickTextEdit);
2023 QTextCursor c = d->control->textCursor();
2024 c.select(QTextCursor::WordUnderCursor);
2025 d->control->setTextCursor(c);
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041void QQuickTextEdit::select(
int start,
int end)
2043 Q_D(QQuickTextEdit);
2044 if (start < 0 || end < 0 || start >= d->document->characterCount() || end >= d->document->characterCount())
2046 QTextCursor cursor = d->control->textCursor();
2047 cursor.setPosition(start, QTextCursor::MoveAnchor);
2048 cursor.setPosition(end, QTextCursor::KeepAnchor);
2049 d->control->setTextCursor(cursor);
2054 updateInputMethod();
2059
2060
2061
2062
2063
2064bool QQuickTextEdit::isRightToLeft(
int start,
int end)
2067 qmlWarning(
this) <<
"isRightToLeft(start, end) called with the end property being smaller than the start.";
2070 return getText(start, end).isRightToLeft();
2074#if QT_CONFIG(clipboard)
2076
2077
2078
2079
2080void QQuickTextEdit::cut()
2082 Q_D(QQuickTextEdit);
2087
2088
2089
2090
2091void QQuickTextEdit::copy()
2093 Q_D(QQuickTextEdit);
2098
2099
2100
2101
2102void QQuickTextEdit::paste()
2104 Q_D(QQuickTextEdit);
2105 d->control->paste();
2111
2112
2113
2114
2115
2116
2118void QQuickTextEdit::undo()
2120 Q_D(QQuickTextEdit);
2125
2126
2127
2128
2130void QQuickTextEdit::redo()
2132 Q_D(QQuickTextEdit);
2137
2138
2139
2140void QQuickTextEdit::mousePressEvent(QMouseEvent *event)
2142 Q_D(QQuickTextEdit);
2143 const bool isMouse = QQuickDeliveryAgentPrivate::isEventFromMouseOrTouchpad(event);
2144 setKeepMouseGrab(d->selectByMouse && isMouse);
2145 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2146 if (d->focusOnPress){
2147 bool hadActiveFocus = hasActiveFocus();
2148 forceActiveFocus(Qt::MouseFocusReason);
2151 if (hasActiveFocus() && hadActiveFocus && !isReadOnly())
2152 qGuiApp->inputMethod()->show();
2154 Q_UNUSED(hadActiveFocus);
2157 if (!event->isAccepted())
2158 QQuickImplicitSizeItem::mousePressEvent(event);
2162
2163
2164
2165void QQuickTextEdit::mouseReleaseEvent(QMouseEvent *event)
2167 Q_D(QQuickTextEdit);
2168 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2170 if (!event->isAccepted())
2171 QQuickImplicitSizeItem::mouseReleaseEvent(event);
2175
2176
2177
2178void QQuickTextEdit::mouseDoubleClickEvent(QMouseEvent *event)
2180 Q_D(QQuickTextEdit);
2181 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2182 if (!event->isAccepted())
2183 QQuickImplicitSizeItem::mouseDoubleClickEvent(event);
2187
2188
2189
2190void QQuickTextEdit::mouseMoveEvent(QMouseEvent *event)
2192 Q_D(QQuickTextEdit);
2193 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2194 if (!event->isAccepted())
2195 QQuickImplicitSizeItem::mouseMoveEvent(event);
2200
2201
2202
2203void QQuickTextEdit::inputMethodEvent(QInputMethodEvent *event)
2205 Q_D(QQuickTextEdit);
2206 const bool wasComposing = isInputMethodComposing();
2207 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2208 setCursorVisible(d->control->cursorVisible());
2209 if (wasComposing != isInputMethodComposing())
2210 emit inputMethodComposingChanged();
2214
2215
2216
2217QVariant QQuickTextEdit::inputMethodQuery(Qt::InputMethodQuery property, QVariant argument)
const
2219 Q_D(
const QQuickTextEdit);
2224 v = (
bool)(flags() & ItemAcceptsInputMethod);
2227 v = (
int)d->effectiveInputMethodHints();
2229 case Qt::ImInputItemClipRectangle:
2230 v = QQuickItem::inputMethodQuery(property);
2232 case Qt::ImReadOnly:
2236 if (property == Qt::ImCursorPosition && !argument.isNull())
2237 argument = QVariant(argument.toPointF() - QPointF(d->xoff, d->yoff));
2238 v = d->control->inputMethodQuery(property, argument);
2239 if (property == Qt::ImCursorRectangle || property == Qt::ImAnchorRectangle)
2240 v = QVariant(v.toRectF().translated(d->xoff, d->yoff));
2247
2248
2249
2250QVariant QQuickTextEdit::inputMethodQuery(Qt::InputMethodQuery property)
const
2252 return inputMethodQuery(property, QVariant());
2256void QQuickTextEdit::triggerPreprocess()
2258 Q_D(QQuickTextEdit);
2259 if (d->updateType == QQuickTextEditPrivate::UpdateNone)
2260 d->updateType = QQuickTextEditPrivate::UpdateOnlyPreprocess;
2266
2267
2268
2269
2270QVariant QQuickTextEdit::loadResource(
int type,
const QUrl &source)
2272 Q_D(QQuickTextEdit);
2273 const QUrl url = d->document->baseUrl().resolved(source);
2274 if (url.isLocalFile()) {
2276 QFileInfo fi(QQmlFile::urlToLocalFileOrQrc(url));
2278 qmlWarning(
this) <<
"Cannot open: " << url.toString();
2284 if (!url.scheme().compare(
"qrc"_L1, Qt::CaseInsensitive)) {
2286 QFile f(QQmlFile::urlToLocalFileOrQrc(url));
2287 if (f.open(QFile::ReadOnly)) {
2288 QByteArray buf = f.readAll();
2291 image.loadFromData(buf);
2292 if (!image.isNull())
2296 qmlWarning(
this) <<
"Cannot read resource: " << f.fileName();
2301 auto existingJobIter = std::find_if(
2302 d->pixmapsInProgress.cbegin(), d->pixmapsInProgress.cend(),
2303 [&url](
const auto *job) {
return job->url() == url; } );
2304 if (existingJobIter != d->pixmapsInProgress.cend()) {
2305 const QQuickPixmap *job = *existingJobIter;
2306 if (job->isError()) {
2307 qmlWarning(
this) << job->error();
2308 d->pixmapsInProgress.erase(existingJobIter);
2312 qCDebug(lcTextEdit) <<
"already downloading" << url;
2314 return job->isReady() ? job->image() : QVariant();
2319 qCDebug(lcTextEdit) <<
"loading" << source <<
"resolved" << url
2320 <<
"type" <<
static_cast<QTextDocument::ResourceType>(type);
2321 QQmlContext *context = qmlContext(
this);
2324 QQuickPixmap *p =
new QQuickPixmap(context->engine(), url, QQuickPixmap::Options{});
2325 p->connectFinished(
this, SLOT(resourceRequestFinished()));
2326 d->pixmapsInProgress.append(p);
2328 return p->isReady() ? p->image() : QVariant();
2332
2333
2334void QQuickTextEdit::resourceRequestFinished()
2336 Q_D(QQuickTextEdit);
2337 for (
auto it = d->pixmapsInProgress.cbegin(); it != d->pixmapsInProgress.cend(); ++it) {
2339 if (job->isError()) {
2341 qCDebug(lcTextEdit) <<
"failed to load (error)" << job->url();
2342 d->document->resource(QTextDocument::ImageResource, job->url());
2346 }
else if (job->isReady()) {
2348 auto res = d->document->resource(QTextDocument::ImageResource, job->url());
2350 qCDebug(lcTextEdit) << (res.isValid() ?
"done downloading" :
"failed to load") << job->url() << job->rect();
2351 d->pixmapsInProgress.erase(it);
2356 if (d->pixmapsInProgress.isEmpty()) {
2364using TextNodeIterator = QQuickTextEditPrivate::TextNodeIterator;
2368 return n1.startPos() < n2.startPos();
2373 QMatrix4x4 transformMatrix;
2374 transformMatrix.translate(topLeft.x(), topLeft.y());
2375 node->setMatrix(transformMatrix);
2379
2380
2381
2382
2383
2384void QQuickTextEdit::invalidateFontCaches()
2386 Q_D(QQuickTextEdit);
2387 if (d->document ==
nullptr)
2391 for (block = d->document->firstBlock(); block.isValid(); block = block.next()) {
2392 if (block.layout() !=
nullptr && block.layout()->engine() !=
nullptr)
2393 block.layout()->engine()->resetFontEngineCache();
2397QTextDocument *QQuickTextEdit::document()
const
2399 Q_D(
const QQuickTextEdit);
2403void QQuickTextEdit::setDocument(QTextDocument *doc)
2405 Q_D(QQuickTextEdit);
2407 std::unique_ptr<QTextDocument> cleanup(d->ownsDocument ? d->document :
nullptr);
2409 d->ownsDocument =
false;
2410 d->control->setDocument(doc);
2420 engine->setDevicePixelRatio(dpr);
2423QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData)
2425 Q_UNUSED(updatePaintNodeData);
2426 Q_D(QQuickTextEdit);
2428 if (d->updateType != QQuickTextEditPrivate::UpdatePaintNode
2429 && d->updateType != QQuickTextEditPrivate::UpdateAll
2430 && oldNode !=
nullptr) {
2432 d->updateType = QQuickTextEditPrivate::UpdateNone;
2436 d->containsUnscalableGlyphs =
false;
2437 if (!oldNode || d->updateType == QQuickTextEditPrivate::UpdateAll) {
2443 d->textNodeMap.clear();
2446 d->updateType = QQuickTextEditPrivate::UpdateNone;
2448 RootNode *rootNode =
static_cast<RootNode *>(oldNode);
2449 TextNodeIterator nodeIterator = d->textNodeMap.begin();
2450 std::optional<
int> firstPosAcrossAllNodes;
2451 if (nodeIterator != d->textNodeMap.end())
2452 firstPosAcrossAllNodes = nodeIterator->startPos();
2454 while (nodeIterator != d->textNodeMap.end() && !nodeIterator->dirty())
2457 const auto dpr = d->effectiveDevicePixelRatio();
2458 QQuickTextNodeEngine engine;
2459 engine.setDevicePixelRatio(dpr);
2460 QQuickTextNodeEngine frameDecorationsEngine;
2461 frameDecorationsEngine.setDevicePixelRatio(dpr);
2463 if (!oldNode || nodeIterator < d->textNodeMap.end() || d->textNodeMap.isEmpty()) {
2466 rootNode =
new RootNode;
2468 int firstDirtyPos = 0;
2469 if (nodeIterator != d->textNodeMap.end()) {
2470 firstDirtyPos = nodeIterator->startPos();
2473 QSGInternalTextNode *firstCleanNode =
nullptr;
2474 auto it = d->textNodeMap.constEnd();
2475 while (it != nodeIterator) {
2479 firstCleanNode = it->textNode();
2482 rootNode->removeChildNode(nodeIterator->textNode());
2483 delete nodeIterator->textNode();
2484 nodeIterator = d->textNodeMap.erase(nodeIterator);
2485 }
while (nodeIterator != d->textNodeMap.constEnd() && nodeIterator->textNode() != firstCleanNode);
2490 if (flags().testFlag(QQuickItem::ItemObservesViewport)) {
2491 viewport = clipRect();
2492 qCDebug(lcVP) <<
"text viewport" << viewport;
2496 rootNode->resetFrameDecorations(d->createTextNode());
2497 resetEngine(&frameDecorationsEngine, d->color, d->selectedTextColor, d->selectionColor, dpr);
2499 QSGInternalTextNode *node =
nullptr;
2501 int currentNodeSize = 0;
2502 int nodeStart = firstDirtyPos;
2503 QPointF basePosition(d->xoff, d->yoff);
2504 QMatrix4x4 basePositionMatrix;
2505 basePositionMatrix.translate(basePosition.x(), basePosition.y());
2506 rootNode->setMatrix(basePositionMatrix);
2509 const TextNode firstCleanNode = (nodeIterator != d->textNodeMap.end()) ? *nodeIterator
2512 QList<QTextFrame *> frames;
2513 frames.append(d->document->rootFrame());
2516 d->firstBlockInViewport = -1;
2517 d->firstBlockPastViewport = -1;
2518 int frameCount = -1;
2519 while (!frames.isEmpty()) {
2520 QTextFrame *textFrame = frames.takeFirst();
2524 qCDebug(lcVP) <<
"frame" << frameCount << textFrame
2525 <<
"from" << positionToRectangle(textFrame->firstPosition()).topLeft()
2526 <<
"to" << positionToRectangle(textFrame->lastPosition()).bottomRight();
2527 frames.append(textFrame->childFrames());
2528 frameDecorationsEngine.addFrameDecorations(d->document, textFrame);
2529 resetEngine(&engine, d->color, d->selectedTextColor, d->selectionColor, dpr);
2531 if (textFrame->firstPosition() > textFrame->lastPosition()
2532 && textFrame->frameFormat().position() != QTextFrameFormat::InFlow) {
2533 node = d->createTextNode();
2534 updateNodeTransform(node, d->document->documentLayout()->frameBoundingRect(textFrame).topLeft());
2535 const int pos = textFrame->firstPosition() - 1;
2536 auto *a =
static_cast<QtPrivate::ProtectedLayoutAccessor *>(d->document->documentLayout());
2537 QTextCharFormat format = a->formatAccessor(pos);
2538 QTextBlock block = textFrame->firstCursorPosition().block();
2539 nodeOffset = d->document->documentLayout()->blockBoundingRect(block).topLeft();
2541 if (!viewport.isNull() && block.layout()) {
2542 QRectF coveredRegion = block.layout()->boundingRect().adjusted(nodeOffset.x(), nodeOffset.y(), nodeOffset.x(), nodeOffset.y());
2543 inView = coveredRegion.bottom() >= viewport.top() && coveredRegion.top() <= viewport.bottom();
2544 qCDebug(lcVP) <<
"non-flow frame" << coveredRegion <<
"in viewport?" << inView;
2547 engine.setCurrentLine(block.layout()->lineForTextPosition(pos - block.position()));
2548 engine.addTextObject(block, QPointF(0, 0), format, QQuickTextNodeEngine::Unselected, d->document,
2549 pos, textFrame->frameFormat().position());
2554 QVarLengthArray<
int, 8> frameBoundaries;
2555 frameBoundaries.reserve(frames.size());
2556 for (QTextFrame *frame : std::as_const(frames))
2557 frameBoundaries.append(frame->firstPosition());
2558 std::sort(frameBoundaries.begin(), frameBoundaries.end());
2560 QTextFrame::iterator it = textFrame->begin();
2561 while (!it.atEnd()) {
2562 QTextBlock block = it.currentBlock();
2563 if (block.position() < firstDirtyPos) {
2568 if (!engine.hasContents())
2569 nodeOffset = d->document->documentLayout()->blockBoundingRect(block).topLeft();
2572 if (!viewport.isNull()) {
2573 QRectF coveredRegion;
2574 if (block.layout()) {
2575 coveredRegion = block.layout()->boundingRect().adjusted(nodeOffset.x(), nodeOffset.y(), nodeOffset.x(), nodeOffset.y());
2576 inView = coveredRegion.bottom() > viewport.top();
2578 const bool potentiallyScrollingBackwards = firstPosAcrossAllNodes && *firstPosAcrossAllNodes == firstDirtyPos;
2579 if (d->firstBlockInViewport < 0 && inView && potentiallyScrollingBackwards) {
2581 if (coveredRegion.top() > viewport.top() + 1) {
2582 qCDebug(lcVP) <<
"checking backwards from block" << block.blockNumber() <<
"@" << nodeOffset.y() << coveredRegion;
2583 while (it != textFrame->begin() && it.currentBlock().layout() &&
2584 it.currentBlock().layout()->boundingRect().top() + nodeOffset.y() > viewport.top()) {
2585 nodeOffset = d->document->documentLayout()->blockBoundingRect(it.currentBlock()).topLeft();
2588 if (!it.currentBlock().layout())
2590 if (Q_LIKELY(it.currentBlock().layout())) {
2591 block = it.currentBlock();
2592 coveredRegion = block.layout()->boundingRect().adjusted(nodeOffset.x(), nodeOffset.y(), nodeOffset.x(), nodeOffset.y());
2593 firstDirtyPos = it.currentBlock().position();
2595 qCWarning(lcVP) <<
"failed to find a text block with layout during back-scrolling";
2598 qCDebug(lcVP) <<
"first block in viewport" << block.blockNumber() <<
"@" << nodeOffset.y() << coveredRegion;
2600 d->renderedRegion = coveredRegion;
2602 if (nodeOffset.y() > viewport.bottom()) {
2604 if (d->firstBlockInViewport >= 0 && d->firstBlockPastViewport < 0) {
2605 qCDebug(lcVP) <<
"first block past viewport" << viewport << block.blockNumber()
2606 <<
"@" << nodeOffset.y() <<
"total region rendered" << d->renderedRegion;
2607 d->firstBlockPastViewport = block.blockNumber();
2611 if (inView && !block.text().isEmpty() && coveredRegion.isValid()) {
2612 d->renderedRegion = d->renderedRegion.united(coveredRegion);
2615 if (!frames.isEmpty())
2616 viewport = viewport.united(d->renderedRegion);
2619 if (inView && d->firstBlockInViewport < 0)
2620 d->firstBlockInViewport = block.blockNumber();
2623 bool createdNodeInView =
false;
2625 if (!engine.hasContents()) {
2627 d->containsUnscalableGlyphs = d->containsUnscalableGlyphs
2628 || node->containsUnscalableGlyphs();
2629 if (!node->parent())
2630 d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart);
2632 node = d->createTextNode();
2633 createdNodeInView =
true;
2634 updateNodeTransform(node, nodeOffset);
2635 nodeStart = block.position();
2637 engine.addTextBlock(d->document, block, -nodeOffset, d->color, QColor(), selectionStart(), selectionEnd() - 1);
2638 currentNodeSize += block.length();
2641 if ((it.atEnd()) || block.next().position() >= firstCleanNode.startPos())
2643 const auto lowerBound =
2644 std::lower_bound(frameBoundaries.constBegin(),
2645 frameBoundaries.constEnd(), block.next().position());
2646 if (node && (currentNodeSize > nodeBreakingSize || lowerBound == frameBoundaries.constEnd() || *lowerBound > nodeStart)) {
2647 currentNodeSize = 0;
2648 d->containsUnscalableGlyphs = d->containsUnscalableGlyphs
2649 || node->containsUnscalableGlyphs();
2650 if (!node->parent())
2651 d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart);
2652 if (!createdNodeInView)
2653 node = d->createTextNode();
2654 resetEngine(&engine, d->color, d->selectedTextColor, d->selectionColor, dpr);
2655 nodeStart = block.next().position();
2660 if (Q_LIKELY(node)) {
2661 d->containsUnscalableGlyphs = d->containsUnscalableGlyphs
2662 || node->containsUnscalableGlyphs();
2663 if (Q_LIKELY(!node->parent()))
2664 d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart);
2667 frameDecorationsEngine.addToSceneGraph(rootNode->frameDecorationsNode,
nullptr, QQuickText::Normal, QColor());
2669 rootNode->prependChildNode(rootNode->frameDecorationsNode);
2671 Q_ASSERT(nodeIterator == d->textNodeMap.end()
2672 || (nodeIterator->textNode() == firstCleanNode.textNode()
2673 && nodeIterator->startPos() == firstCleanNode.startPos()));
2675 if (firstCleanNode.textNode() !=
nullptr) {
2676 QPointF oldOffset = firstCleanNode.textNode()->matrix().map(QPointF(0,0));
2677 QPointF currentOffset = d->document->documentLayout()->blockBoundingRect(
2678 d->document->findBlock(firstCleanNode.startPos())).topLeft();
2679 QPointF delta = currentOffset - oldOffset;
2680 while (nodeIterator != d->textNodeMap.end()) {
2681 QMatrix4x4 transformMatrix = nodeIterator->textNode()->matrix();
2682 transformMatrix.translate(delta.x(), delta.y());
2683 nodeIterator->textNode()->setMatrix(transformMatrix);
2691 std::sort(d->textNodeMap.begin(), d->textNodeMap.end());
2694 if (d->cursorComponent ==
nullptr) {
2695 QSGInternalRectangleNode* cursor =
nullptr;
2696 if (!isReadOnly() && d->cursorVisible && d->control->cursorOn() && d->control->cursorVisible())
2697 cursor = d->sceneGraphContext()->createInternalRectangleNode(d->control->cursorRect(), d->color);
2698 rootNode->resetCursorNode(cursor);
2701 invalidateFontCaches();
2706void QQuickTextEdit::updatePolish()
2708 invalidateFontCaches();
2712
2713
2714
2715
2716
2717bool QQuickTextEdit::canPaste()
const
2719 Q_D(
const QQuickTextEdit);
2720 if (!d->canPasteValid) {
2721 const_cast<QQuickTextEditPrivate *>(d)->canPaste = d->control->canPaste();
2722 const_cast<QQuickTextEditPrivate *>(d)->canPasteValid =
true;
2728
2729
2730
2731
2732
2734bool QQuickTextEdit::canUndo()
const
2736 Q_D(
const QQuickTextEdit);
2737 return d->document->isUndoAvailable();
2741
2742
2743
2744
2745
2747bool QQuickTextEdit::canRedo()
const
2749 Q_D(
const QQuickTextEdit);
2750 return d->document->isRedoAvailable();
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765bool QQuickTextEdit::isInputMethodComposing()
const
2770 Q_D(
const QQuickTextEdit);
2771 return d->control->hasImState();
2775QQuickTextEditPrivate::ExtraData::ExtraData()
2776 : explicitTopPadding(
false)
2777 , explicitLeftPadding(
false)
2778 , explicitRightPadding(
false)
2779 , explicitBottomPadding(
false)
2780 , implicitResize(
true)
2784void QQuickTextEditPrivate::init()
2786 Q_Q(QQuickTextEdit);
2788#if QT_CONFIG(clipboard)
2789 if (QGuiApplication::clipboard()->supportsSelection())
2790 q->setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton);
2793 q->setAcceptedMouseButtons(Qt::LeftButton);
2796 q->setFlag(QQuickItem::ItemAcceptsInputMethod);
2798 q->setFlag(QQuickItem::ItemHasContents);
2800 q->setAcceptHoverEvents(
true);
2802 document =
new QTextDocument(q);
2803 ownsDocument =
true;
2804 auto *imageHandler =
new QQuickTextImageHandler(document);
2805 document->documentLayout()->registerHandler(QTextFormat::ImageObject, imageHandler);
2807 control =
new QQuickTextControl(document, q);
2808 control->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard | Qt::TextEditable);
2809 control->setAcceptRichText(
false);
2810 control->setCursorIsFocusIndicator(
true);
2811 q->setKeepMouseGrab(
true);
2813 qmlobject_connect(control, QQuickTextControl, SIGNAL(updateCursorRequest()), q, QQuickTextEdit, SLOT(updateCursor()));
2814 qmlobject_connect(control, QQuickTextControl, SIGNAL(selectionChanged()), q, QQuickTextEdit, SIGNAL(selectedTextChanged()));
2815 qmlobject_connect(control, QQuickTextControl, SIGNAL(selectionChanged()), q, QQuickTextEdit, SLOT(updateSelection()));
2816 qmlobject_connect(control, QQuickTextControl, SIGNAL(cursorPositionChanged()), q, QQuickTextEdit, SLOT(updateSelection()));
2817 qmlobject_connect(control, QQuickTextControl, SIGNAL(cursorPositionChanged()), q, QQuickTextEdit, SIGNAL(cursorPositionChanged()));
2818 qmlobject_connect(control, QQuickTextControl, SIGNAL(cursorRectangleChanged()), q, QQuickTextEdit, SLOT(moveCursorDelegate()));
2819 qmlobject_connect(control, QQuickTextControl, SIGNAL(linkActivated(QString)), q, QQuickTextEdit, SIGNAL(linkActivated(QString)));
2820 qmlobject_connect(control, QQuickTextControl, SIGNAL(overwriteModeChanged(
bool)), q, QQuickTextEdit, SIGNAL(overwriteModeChanged(
bool)));
2821 qmlobject_connect(control, QQuickTextControl, SIGNAL(textChanged()), q, QQuickTextEdit, SLOT(q_textChanged()));
2822 qmlobject_connect(control, QQuickTextControl, SIGNAL(preeditTextChanged()), q, QQuickTextEdit, SIGNAL(preeditTextChanged()));
2823#if QT_CONFIG(clipboard)
2824 qmlobject_connect(QGuiApplication::clipboard(), QClipboard, SIGNAL(dataChanged()), q, QQuickTextEdit, SLOT(q_canPasteChanged()));
2826 qmlobject_connect(document, QTextDocument, SIGNAL(undoAvailable(
bool)), q, QQuickTextEdit, SIGNAL(canUndoChanged()));
2827 qmlobject_connect(document, QTextDocument, SIGNAL(redoAvailable(
bool)), q, QQuickTextEdit, SIGNAL(canRedoChanged()));
2828 QObject::connect(document, &QTextDocument::contentsChange, q, &QQuickTextEdit::q_contentsChange);
2829 QObject::connect(document->documentLayout(), &QAbstractTextDocumentLayout::updateBlock, q, &QQuickTextEdit::invalidateBlock);
2830 QObject::connect(control, &QQuickTextControl::linkHovered, q, &QQuickTextEdit::q_linkHovered);
2831 QObject::connect(control, &QQuickTextControl::markerHovered, q, &QQuickTextEdit::q_markerHovered);
2832 QObject::connect(control, &QQuickTextControl::hoveredToolTipChanged, q, &QQuickTextEdit::hoveredToolTipChanged);
2834 document->setPageSize(QSizeF(0, 0));
2835 document->setDefaultFont(font);
2836 document->setDocumentMargin(textMargin);
2837 document->setUndoRedoEnabled(
false);
2838 document->setUndoRedoEnabled(
true);
2839 updateDefaultTextOption();
2840 document->setModified(
false);
2842#if QT_CONFIG(cursor)
2843 updateMouseCursorShape();
2845 setSizePolicy(QLayoutPolicy::Expanding, QLayoutPolicy::Expanding);
2848void QQuickTextEditPrivate::resetInputMethod()
2850 Q_Q(QQuickTextEdit);
2851 if (!q->isReadOnly() && q->hasActiveFocus() && qGuiApp)
2852 QGuiApplication::inputMethod()->reset();
2855void QQuickTextEdit::q_textChanged()
2857 Q_D(QQuickTextEdit);
2858 d->textCached =
false;
2859 for (QTextBlock it = d->document->begin(); it != d->document->end(); it = it.next()) {
2860 d->contentDirection = d->textDirection(it.text());
2861 if (d->contentDirection != Qt::LayoutDirectionAuto)
2864 d->determineHorizontalAlignment();
2865 d->updateDefaultTextOption();
2868 markDirtyNodesForRange(0, d->document->characterCount(), 0);
2869 if (isComponentComplete()) {
2871 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
2876 if (d->control->isBeingEdited())
2880void QQuickTextEdit::markDirtyNodesForRange(
int start,
int end,
int charDelta)
2882 Q_D(QQuickTextEdit);
2886 TextNode dummyNode(start);
2888 const TextNodeIterator textNodeMapBegin = d->textNodeMap.begin();
2889 const TextNodeIterator textNodeMapEnd = d->textNodeMap.end();
2891 TextNodeIterator it = std::lower_bound(textNodeMapBegin, textNodeMapEnd, dummyNode);
2894 if (it != textNodeMapBegin) {
2896 TextNode otherDummy(it->startPos());
2897 it = std::lower_bound(textNodeMapBegin, textNodeMapEnd, otherDummy);
2901 while (it != textNodeMapEnd) {
2902 if (it->startPos() <= end)
2905 it->moveStartPos(charDelta);
2912void QQuickTextEdit::q_contentsChange(
int pos,
int charsRemoved,
int charsAdded)
2914 Q_D(QQuickTextEdit);
2916 const int editRange = pos + qMax(charsAdded, charsRemoved);
2917 const int delta = charsAdded - charsRemoved;
2919 markDirtyNodesForRange(pos, editRange, delta);
2921 if (isComponentComplete()) {
2923 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
2928void QQuickTextEdit::moveCursorDelegate()
2930 Q_D(QQuickTextEdit);
2932 updateInputMethod();
2934 emit cursorRectangleChanged();
2937 QRectF cursorRect = cursorRectangle();
2938 d->cursorItem->setX(cursorRect.x());
2939 d->cursorItem->setY(cursorRect.y());
2940 d->cursorItem->setHeight(cursorRect.height());
2943void QQuickTextEdit::updateSelection()
2945 Q_D(QQuickTextEdit);
2948 if (d->control->textCursor().hasSelection() || d->hadSelection) {
2949 markDirtyNodesForRange(qMin(d->lastSelectionStart, d->control->textCursor().selectionStart()), qMax(d->control->textCursor().selectionEnd(), d->lastSelectionEnd), 0);
2950 if (isComponentComplete()) {
2952 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
2957 d->hadSelection = d->control->textCursor().hasSelection();
2959 if (d->lastSelectionStart != d->control->textCursor().selectionStart()) {
2960 d->lastSelectionStart = d->control->textCursor().selectionStart();
2961 emit selectionStartChanged();
2963 if (d->lastSelectionEnd != d->control->textCursor().selectionEnd()) {
2964 d->lastSelectionEnd = d->control->textCursor().selectionEnd();
2965 emit selectionEndChanged();
2969QRectF QQuickTextEdit::boundingRect()
const
2971 Q_D(
const QQuickTextEdit);
2973 QQuickTextUtil::alignedX(d->contentSize.width(), width(), effectiveHAlign()),
2975 d->contentSize.width(),
2976 d->contentSize.height());
2978 int cursorWidth = 1;
2981 else if (!d->document->isEmpty())
2985 r.setRight(r.right() + cursorWidth);
2990QRectF QQuickTextEdit::clipRect()
const
2992 Q_D(
const QQuickTextEdit);
2993 QRectF r = QQuickImplicitSizeItem::clipRect();
2994 int cursorWidth = 1;
2996 cursorWidth = d->cursorItem->width();
2997 if (!d->document->isEmpty())
3002 r.setRight(r.right() + cursorWidth);
3006qreal QQuickTextEditPrivate::getImplicitWidth()
const
3008 Q_Q(
const QQuickTextEdit);
3009 if (!requireImplicitWidth) {
3012 const_cast<QQuickTextEditPrivate*>(
this)->requireImplicitWidth =
true;
3013 const_cast<QQuickTextEdit*>(q)->updateSize();
3015 return implicitWidth;
3020void QQuickTextEdit::updateSize()
3022 Q_D(QQuickTextEdit);
3023 if (!isComponentComplete()) {
3031 if (!d->requireImplicitWidth) {
3032 emit implicitWidthChanged();
3034 if (d->requireImplicitWidth)
3037 if (d->requireImplicitWidth) {
3038 d->document->setTextWidth(-1);
3039 const qreal naturalWidth = d->document->idealWidth();
3040 const bool wasInLayout = d->inLayout;
3042 if (d->isImplicitResizeEnabled())
3043 setImplicitWidth(naturalWidth + leftPadding() + rightPadding());
3044 d->inLayout = wasInLayout;
3048 const qreal newTextWidth = width() - leftPadding() - rightPadding();
3049 if (d->document->textWidth() != newTextWidth)
3050 d->document->setTextWidth(newTextWidth);
3051 }
else if (d->wrapMode == NoWrap) {
3055 const qreal newTextWidth = d->document->idealWidth();
3056 if (d->document->textWidth() != newTextWidth)
3057 d->document->setTextWidth(newTextWidth);
3059 d->document->setTextWidth(-1);
3062 QFontMetricsF fm(d->font);
3063 const qreal newHeight = d->document->isEmpty() ? qCeil(fm.height()) : d->document->size().height();
3064 const qreal newWidth = d->document->idealWidth();
3066 if (d->isImplicitResizeEnabled()) {
3069 setImplicitSize(newWidth + leftPadding() + rightPadding(), newHeight + topPadding() + bottomPadding());
3071 setImplicitHeight(newHeight + topPadding() + bottomPadding());
3074 d->xoff = leftPadding() + qMax(qreal(0), QQuickTextUtil::alignedX(d->document->size().width(), width() - leftPadding() - rightPadding(), effectiveHAlign()));
3075 d->yoff = topPadding() + QQuickTextUtil::alignedY(d->document->size().height(), height() - topPadding() - bottomPadding(), d->vAlign);
3077 qreal baseline = fm.ascent();
3078 QTextBlock firstBlock = d->document->firstBlock();
3079 if (firstBlock.isValid() && firstBlock.layout() !=
nullptr && firstBlock.lineCount() > 0) {
3080 QTextLine firstLine = firstBlock.layout()->lineAt(0);
3081 if (firstLine.isValid())
3082 baseline = firstLine.ascent();
3085 setBaselineOffset(baseline + d->yoff + d->textMargin);
3087 QSizeF size(newWidth, newHeight);
3088 if (d->contentSize != size) {
3089 d->contentSize = size;
3091 const bool wasInResize = d->inResize;
3094 emit contentSizeChanged();
3095 d->inResize = wasInResize;
3100void QQuickTextEdit::updateWholeDocument()
3102 Q_D(QQuickTextEdit);
3103 if (!d->textNodeMap.isEmpty()) {
3104 for (TextNode &node : d->textNodeMap)
3108 if (isComponentComplete()) {
3110 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
3115void QQuickTextEdit::invalidateBlock(
const QTextBlock &block)
3117 Q_D(QQuickTextEdit);
3118 markDirtyNodesForRange(block.position(), block.position() + block.length(), 0);
3120 if (isComponentComplete()) {
3122 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
3127void QQuickTextEdit::updateCursor()
3129 Q_D(QQuickTextEdit);
3130 if (isComponentComplete() && isVisible()) {
3132 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
3137void QQuickTextEdit::q_linkHovered(
const QString &link)
3139 Q_D(QQuickTextEdit);
3140 emit linkHovered(link);
3141#if QT_CONFIG(cursor)
3142 if (link.isEmpty()) {
3143 d->updateMouseCursorShape();
3144 }
else if (cursor().shape() != Qt::PointingHandCursor) {
3145 setCursor(Qt::PointingHandCursor);
3150void QQuickTextEdit::q_markerHovered(
bool hovered)
3152 Q_D(QQuickTextEdit);
3153#if QT_CONFIG(cursor)
3155 d->updateMouseCursorShape();
3156 }
else if (cursor().shape() != Qt::PointingHandCursor) {
3157 setCursor(Qt::PointingHandCursor);
3162void QQuickTextEdit::q_updateAlignment()
3164 Q_D(QQuickTextEdit);
3165 if (d->determineHorizontalAlignment()) {
3166 d->updateDefaultTextOption();
3167 d->xoff = qMax(qreal(0), QQuickTextUtil::alignedX(d->document->size().width(), width(), effectiveHAlign()));
3168 moveCursorDelegate();
3169 updateWholeDocument();
3173void QQuickTextEdit::updateTotalLines()
3175 Q_D(QQuickTextEdit);
3179 for (QTextBlock it = d->document->begin(); it != d->document->end(); it = it.next()) {
3180 QTextLayout *layout = it.layout();
3183 subLines += layout->lineCount()-1;
3186 int newTotalLines = d->document->lineCount() + subLines;
3187 if (d->lineCount != newTotalLines) {
3188 d->lineCount = newTotalLines;
3189 emit lineCountChanged();
3193void QQuickTextEditPrivate::updateDefaultTextOption()
3195 Q_Q(QQuickTextEdit);
3196 QTextOption opt = document->defaultTextOption();
3197 const Qt::Alignment oldAlignment = opt.alignment();
3198 Qt::LayoutDirection oldTextDirection = opt.textDirection();
3200 QQuickTextEdit::HAlignment horizontalAlignment = q->effectiveHAlign();
3201 if (contentDirection == Qt::RightToLeft) {
3202 if (horizontalAlignment == QQuickTextEdit::AlignLeft)
3203 horizontalAlignment = QQuickTextEdit::AlignRight;
3204 else if (horizontalAlignment == QQuickTextEdit::AlignRight)
3205 horizontalAlignment = QQuickTextEdit::AlignLeft;
3207 if (!hAlignImplicit)
3208 opt.setAlignment((Qt::Alignment)(
int)(horizontalAlignment | vAlign));
3210 opt.setAlignment(Qt::Alignment(vAlign));
3213 if (contentDirection == Qt::LayoutDirectionAuto) {
3214 opt.setTextDirection(qGuiApp->inputMethod()->inputDirection());
3218 opt.setTextDirection(contentDirection);
3221 QTextOption::WrapMode oldWrapMode = opt.wrapMode();
3222 opt.setWrapMode(QTextOption::WrapMode(wrapMode));
3224 bool oldUseDesignMetrics = opt.useDesignMetrics();
3225 opt.setUseDesignMetrics(renderType != QQuickTextEdit::NativeRendering);
3227 if (oldWrapMode != opt.wrapMode() || oldAlignment != opt.alignment()
3228 || oldTextDirection != opt.textDirection()
3229 || oldUseDesignMetrics != opt.useDesignMetrics()) {
3230 document->setDefaultTextOption(opt);
3234void QQuickTextEditPrivate::onDocumentStatusChanged()
3236 Q_ASSERT(quickDocument);
3237 switch (quickDocument->status()) {
3238 case QQuickTextDocument::Status::Loaded:
3239 case QQuickTextDocument::Status::Saved:
3240 switch (QQuickTextDocumentPrivate::get(quickDocument)->detectedFormat) {
3242 richText = (format == QQuickTextEdit::RichText || format == QQuickTextEdit::AutoText);
3243 markdownText =
false;
3245 case Qt::MarkdownText:
3247 markdownText = (format == QQuickTextEdit::MarkdownText || format == QQuickTextEdit::AutoText);
3251 markdownText =
false;
3262void QQuickTextEdit::focusInEvent(QFocusEvent *event)
3264 Q_D(QQuickTextEdit);
3265 d->handleFocusEvent(event);
3266 QQuickImplicitSizeItem::focusInEvent(event);
3269void QQuickTextEdit::focusOutEvent(QFocusEvent *event)
3271 Q_D(QQuickTextEdit);
3272 d->handleFocusEvent(event);
3273 QQuickImplicitSizeItem::focusOutEvent(event);
3276#if QT_VERSION < QT_VERSION_CHECK(7
, 0
, 0
)
3277bool QQuickTextEditPrivate::handleContextMenuEvent(QContextMenuEvent *event)
3279bool QQuickTextEdit::contextMenuEvent(QContextMenuEvent *event)
3282 Q_Q(QQuickTextEdit);
3283 QContextMenuEvent mapped(event->reason(),
3284 q->mapToScene(q->cursorRectangle().center()).toPoint(), event->globalPos(),
3285 event->modifiers());
3286 const bool eventProcessed = QQuickItemPrivate::handleContextMenuEvent(&mapped);
3287 event->setAccepted(mapped.isAccepted());
3288 return eventProcessed;
3291void QQuickTextEditPrivate::handleFocusEvent(QFocusEvent *event)
3293 Q_Q(QQuickTextEdit);
3294 bool focus = event->type() == QEvent::FocusIn;
3295 if (!q->isReadOnly())
3296 q->setCursorVisible(focus);
3297 control->processEvent(event, QPointF(-xoff, -yoff));
3299 q->q_updateAlignment();
3301 if (focusOnPress && !q->isReadOnly())
3302 qGuiApp->inputMethod()->show();
3303 q->connect(QGuiApplication::inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
3304 q, SLOT(q_updateAlignment()));
3308 q->disconnect(QGuiApplication::inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
3309 q, SLOT(q_updateAlignment()));
3311 if (event->reason() != Qt::ActiveWindowFocusReason
3312 && event->reason() != Qt::PopupFocusReason
3313 && control->textCursor().hasSelection()
3314 && !persistentSelection)
3317 emit q->editingFinished();
3321void QQuickTextEditPrivate::addCurrentTextNodeToRoot(QQuickTextNodeEngine *engine, QSGTransformNode *root, QSGInternalTextNode *node, TextNodeIterator &it,
int startPos)
3323 engine->addToSceneGraph(node,
nullptr, QQuickText::Normal, QColor());
3324 it = textNodeMap.insert(it, TextNode(startPos, node));
3326 root->appendChildNode(node);
3327 ++renderedBlockCount;
3330QSGInternalTextNode *QQuickTextEditPrivate::createTextNode()
3332 Q_Q(QQuickTextEdit);
3333 QSGInternalTextNode* node = sceneGraphContext()->createInternalTextNode(sceneGraphRenderContext());
3334 node->setRenderType(QSGTextNode::RenderType(renderType));
3335 node->setFiltering(q->smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
3339void QQuickTextEdit::q_canPasteChanged()
3341 Q_D(QQuickTextEdit);
3342 bool old = d->canPaste;
3343 d->canPaste = d->control->canPaste();
3344 bool changed = old!=d->canPaste || !d->canPasteValid;
3345 d->canPasteValid =
true;
3347 emit canPasteChanged();
3351
3352
3353
3354
3355
3356
3358QString QQuickTextEdit::getText(
int start,
int end)
const
3360 Q_D(
const QQuickTextEdit);
3361 start = qBound(0, start, d->document->characterCount() - 1);
3362 end = qBound(0, end, d->document->characterCount() - 1);
3363 QTextCursor cursor(d->document);
3364 cursor.setPosition(start, QTextCursor::MoveAnchor);
3365 cursor.setPosition(end, QTextCursor::KeepAnchor);
3366#if QT_CONFIG(texthtmlparser)
3367 return d->richText || d->markdownText
3368 ? cursor.selectedText()
3369 : cursor.selection().toPlainText();
3371 return cursor.selection().toPlainText();
3376
3377
3378
3379
3380
3381
3383QString QQuickTextEdit::getFormattedText(
int start,
int end)
const
3385 Q_D(
const QQuickTextEdit);
3387 start = qBound(0, start, d->document->characterCount() - 1);
3388 end = qBound(0, end, d->document->characterCount() - 1);
3390 QTextCursor cursor(d->document);
3391 cursor.setPosition(start, QTextCursor::MoveAnchor);
3392 cursor.setPosition(end, QTextCursor::KeepAnchor);
3395#if QT_CONFIG(texthtmlparser)
3396 return cursor.selection().toHtml();
3398 return cursor.selection().toPlainText();
3400 }
else if (d->markdownText) {
3401#if QT_CONFIG(textmarkdownwriter)
3402 return cursor.selection().toMarkdown();
3404 return cursor.selection().toPlainText();
3407 return cursor.selection().toPlainText();
3412
3413
3414
3415
3416void QQuickTextEdit::insert(
int position,
const QString &text)
3418 Q_D(QQuickTextEdit);
3419 if (position < 0 || position >= d->document->characterCount())
3421 QTextCursor cursor(d->document);
3422 cursor.setPosition(position);
3423 d->richText = d->richText || (d->format == AutoText && Qt::mightBeRichText(text));
3425#if QT_CONFIG(texthtmlparser)
3426 cursor.insertHtml(text);
3428 cursor.insertText(text);
3430 }
else if (d->markdownText) {
3431#if QT_CONFIG(textmarkdownreader)
3432 cursor.insertMarkdown(text);
3434 cursor.insertText(text);
3437 cursor.insertText(text);
3439 d->control->updateCursorRectangle(
false);
3443
3444
3445
3446
3448void QQuickTextEdit::remove(
int start,
int end)
3450 Q_D(QQuickTextEdit);
3451 start = qBound(0, start, d->document->characterCount() - 1);
3452 end = qBound(0, end, d->document->characterCount() - 1);
3453 QTextCursor cursor(d->document);
3454 cursor.setPosition(start, QTextCursor::MoveAnchor);
3455 cursor.setPosition(end, QTextCursor::KeepAnchor);
3456 cursor.removeSelectedText();
3457 d->control->updateCursorRectangle(
false);
3461
3462
3463
3464
3465
3466
3467
3468
3469
3470
3472QQuickTextDocument *QQuickTextEdit::textDocument()
3474 Q_D(QQuickTextEdit);
3475 if (!d->quickDocument) {
3476 d->quickDocument =
new QQuickTextDocument(
this);
3477 connect(d->quickDocument, &QQuickTextDocument::statusChanged, d->quickDocument,
3478 [d]() { d->onDocumentStatusChanged(); } );
3480 return d->quickDocument;
3483bool QQuickTextEditPrivate::isLinkHoveredConnected()
3485 Q_Q(QQuickTextEdit);
3486 IS_SIGNAL_CONNECTED(q, QQuickTextEdit, linkHovered, (
const QString &));
3489bool QQuickTextEditPrivate::isHoveredToolTipChangedConnected()
3491 Q_Q(QQuickTextEdit);
3492 IS_SIGNAL_CONNECTED(q, QQuickTextEdit, hoveredToolTipChanged, ());
3495bool QQuickTextEditPrivate::isHoveredSignalConnected()
3497 return isLinkHoveredConnected() || isHoveredToolTipChangedConnected();
3500#if QT_CONFIG(cursor)
3501void QQuickTextEditPrivate::updateMouseCursorShape()
3503 Q_Q(QQuickTextEdit);
3504 q->setCursor(q->isReadOnly() && !q->selectByMouse() ? Qt::ArrowCursor : Qt::IBeamCursor);
3509
3510
3511
3512
3513
3514
3515
3516
3517
3520
3521
3522
3523
3524
3527
3528
3529
3530
3531
3532
3533
3534
3535
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3560
3561
3562
3563
3564
3565
3566
3568QString QQuickTextEdit::hoveredLink()
const
3570 Q_D(
const QQuickTextEdit);
3571 if (
const_cast<QQuickTextEditPrivate *>(d)->isLinkHoveredConnected()) {
3572 return d->control->hoveredLink();
3574#if QT_CONFIG(cursor)
3575 if (QQuickWindow *wnd = window()) {
3576 QPointF pos = QCursor::pos(wnd->screen()) - wnd->position() - mapToScene(QPointF(0, 0));
3577 return d->control->anchorAt(pos);
3584QString QQuickTextEdit::hoveredToolTip()
const
3586 Q_D(
const QQuickTextEdit);
3587 if (
const_cast<QQuickTextEditPrivate *>(d)->isHoveredToolTipChangedConnected()) {
3588 return d->control->hoveredToolTip();
3590#if QT_CONFIG(cursor)
3591 if (QQuickWindow *wnd = window()) {
3592 const QPointF pos = QCursor::pos(wnd->screen()) - wnd->position() - mapToScene(QPointF(0, 0));
3593 return d->control->charFormatAt(pos).toolTip();
3600void QQuickTextEdit::hoverEnterEvent(QHoverEvent *event)
3602 Q_D(QQuickTextEdit);
3603 if (d->isHoveredSignalConnected())
3604 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
3608void QQuickTextEdit::hoverMoveEvent(QHoverEvent *event)
3610 Q_D(QQuickTextEdit);
3611 if (d->isHoveredSignalConnected())
3612 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
3616void QQuickTextEdit::hoverLeaveEvent(QHoverEvent *event)
3618 Q_D(QQuickTextEdit);
3619 if (d->isHoveredSignalConnected())
3620 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
3625
3626
3627
3628
3629
3630
3631
3632
3633void QQuickTextEdit::append(
const QString &text)
3635 Q_D(QQuickTextEdit);
3636 QTextCursor cursor(d->document);
3637 cursor.beginEditBlock();
3638 cursor.movePosition(QTextCursor::End);
3640 if (!d->document->isEmpty())
3641 cursor.insertBlock();
3643 if (d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(text))) {
3644#if QT_CONFIG(texthtmlparser)
3645 cursor.insertHtml(text);
3647 cursor.insertText(text);
3649 }
else if (d->format == MarkdownText) {
3650#if QT_CONFIG(textmarkdownreader)
3651 cursor.insertMarkdown(text);
3653 cursor.insertText(text);
3656 cursor.insertText(text);
3659 cursor.endEditBlock();
3660 d->control->updateCursorRectangle(
false);
3664
3665
3666
3667
3668
3669
3670
3671
3672QString QQuickTextEdit::linkAt(qreal x, qreal y)
const
3674 Q_D(
const QQuickTextEdit);
3675 return d->control->anchorAt(QPointF(x + topPadding(), y + leftPadding()));
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689qreal QQuickTextEdit::padding()
const
3691 Q_D(
const QQuickTextEdit);
3692 return d->padding();
3695void QQuickTextEdit::setPadding(qreal padding)
3697 Q_D(QQuickTextEdit);
3698 if (qFuzzyCompare(d->padding(), padding))
3701 d->extra.value().padding = padding;
3703 if (isComponentComplete()) {
3704 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
3707 emit paddingChanged();
3708 if (!d->extra.isAllocated() || !d->extra->explicitTopPadding)
3709 emit topPaddingChanged();
3710 if (!d->extra.isAllocated() || !d->extra->explicitLeftPadding)
3711 emit leftPaddingChanged();
3712 if (!d->extra.isAllocated() || !d->extra->explicitRightPadding)
3713 emit rightPaddingChanged();
3714 if (!d->extra.isAllocated() || !d->extra->explicitBottomPadding)
3715 emit bottomPaddingChanged();
3718void QQuickTextEdit::resetPadding()
3723qreal QQuickTextEdit::topPadding()
const
3725 Q_D(
const QQuickTextEdit);
3726 if (d->extra.isAllocated() && d->extra->explicitTopPadding)
3727 return d->extra->topPadding;
3728 return d->padding();
3731void QQuickTextEdit::setTopPadding(qreal padding)
3733 Q_D(QQuickTextEdit);
3734 d->setTopPadding(padding);
3737void QQuickTextEdit::resetTopPadding()
3739 Q_D(QQuickTextEdit);
3740 d->setTopPadding(0,
true);
3743qreal QQuickTextEdit::leftPadding()
const
3745 Q_D(
const QQuickTextEdit);
3746 if (d->extra.isAllocated() && d->extra->explicitLeftPadding)
3747 return d->extra->leftPadding;
3748 return d->padding();
3751void QQuickTextEdit::setLeftPadding(qreal padding)
3753 Q_D(QQuickTextEdit);
3754 d->setLeftPadding(padding);
3757void QQuickTextEdit::resetLeftPadding()
3759 Q_D(QQuickTextEdit);
3760 d->setLeftPadding(0,
true);
3763qreal QQuickTextEdit::rightPadding()
const
3765 Q_D(
const QQuickTextEdit);
3766 if (d->extra.isAllocated() && d->extra->explicitRightPadding)
3767 return d->extra->rightPadding;
3768 return d->padding();
3771void QQuickTextEdit::setRightPadding(qreal padding)
3773 Q_D(QQuickTextEdit);
3774 d->setRightPadding(padding);
3777void QQuickTextEdit::resetRightPadding()
3779 Q_D(QQuickTextEdit);
3780 d->setRightPadding(0,
true);
3783qreal QQuickTextEdit::bottomPadding()
const
3785 Q_D(
const QQuickTextEdit);
3786 if (d->extra.isAllocated() && d->extra->explicitBottomPadding)
3787 return d->extra->bottomPadding;
3788 return d->padding();
3791void QQuickTextEdit::setBottomPadding(qreal padding)
3793 Q_D(QQuickTextEdit);
3794 d->setBottomPadding(padding);
3797void QQuickTextEdit::resetBottomPadding()
3799 Q_D(QQuickTextEdit);
3800 d->setBottomPadding(0,
true);
3804
3805
3806
3807
3808
3809
3810
3811int QQuickTextEdit::tabStopDistance()
const
3813 Q_D(
const QQuickTextEdit);
3814 return d->document->defaultTextOption().tabStopDistance();
3817void QQuickTextEdit::setTabStopDistance(qreal distance)
3819 Q_D(QQuickTextEdit);
3820 QTextOption textOptions = d->document->defaultTextOption();
3821 if (textOptions.tabStopDistance() == distance)
3824 textOptions.setTabStopDistance(distance);
3825 d->document->setDefaultTextOption(textOptions);
3826 emit tabStopDistanceChanged(distance);
3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840void QQuickTextEdit::clear()
3842 Q_D(QQuickTextEdit);
3843 d->resetInputMethod();
3844 d->control->clear();
3847#ifndef QT_NO_DEBUG_STREAM
3850 QDebugStateSaver saver(debug);
3852 debug <<
"Node(startPos:" << n.m_startPos <<
"dirty:" << n.m_dirty << n.m_node <<
')';