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);
1717 d->determineHorizontalAlignment();
1718 d->updateDefaultTextOption();
1722 if (d->cursorComponent && isCursorVisible())
1723 QQuickTextUtil::createCursor(d);
1726#if QT_CONFIG(accessibility)
1727 if (QAccessible::isActive())
1728 d->accessibilityActiveChanged(
true);
1732int QQuickTextEdit::resourcesLoading()
const
1734 Q_D(
const QQuickTextEdit);
1735 return d->pixmapsInProgress.size();
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752bool QQuickTextEdit::selectByKeyboard()
const
1754 Q_D(
const QQuickTextEdit);
1755 if (d->selectByKeyboardSet)
1756 return d->selectByKeyboard;
1757 return !isReadOnly();
1760void QQuickTextEdit::setSelectByKeyboard(
bool on)
1762 Q_D(QQuickTextEdit);
1763 bool was = selectByKeyboard();
1764 if (!d->selectByKeyboardSet || was != on) {
1765 d->selectByKeyboardSet =
true;
1766 d->selectByKeyboard = on;
1768 d->control->setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByKeyboard);
1770 d->control->setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByKeyboard);
1771 emit selectByKeyboardChanged(on);
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798bool QQuickTextEdit::selectByMouse()
const
1800 Q_D(
const QQuickTextEdit);
1801 return d->selectByMouse;
1804void QQuickTextEdit::setSelectByMouse(
bool on)
1806 Q_D(QQuickTextEdit);
1807 if (d->selectByMouse == on)
1810 d->selectByMouse = on;
1811 setKeepMouseGrab(on);
1813 d->control->setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByMouse);
1815 d->control->setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByMouse);
1817#if QT_CONFIG(cursor)
1818 d->updateMouseCursorShape();
1820 emit selectByMouseChanged(on);
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833QQuickTextEdit::SelectionMode QQuickTextEdit::mouseSelectionMode()
const
1835 Q_D(
const QQuickTextEdit);
1836 return d->mouseSelectionMode;
1839void QQuickTextEdit::setMouseSelectionMode(SelectionMode mode)
1841 Q_D(QQuickTextEdit);
1842 if (d->mouseSelectionMode != mode) {
1843 d->mouseSelectionMode = mode;
1844 d->control->setWordSelectionEnabled(mode == SelectWords);
1845 emit mouseSelectionModeChanged(mode);
1850
1851
1852
1853
1854
1855
1856
1857void QQuickTextEdit::setReadOnly(
bool r)
1859 Q_D(QQuickTextEdit);
1860 if (r == isReadOnly())
1864 setFlag(QQuickItem::ItemAcceptsInputMethod, !r);
1866 Qt::TextInteractionFlags flags = Qt::LinksAccessibleByMouse;
1867 if (d->selectByMouse)
1868 flags = flags | Qt::TextSelectableByMouse;
1869 if (d->selectByKeyboardSet && d->selectByKeyboard)
1870 flags = flags | Qt::TextSelectableByKeyboard;
1871 else if (!d->selectByKeyboardSet && !r)
1872 flags = flags | Qt::TextSelectableByKeyboard;
1874 flags = flags | Qt::TextEditable;
1875 d->control->setTextInteractionFlags(flags);
1876 d->control->moveCursor(QTextCursor::End);
1879 updateInputMethod(Qt::ImEnabled);
1881#if QT_CONFIG(cursor)
1882 d->updateMouseCursorShape();
1884 q_canPasteChanged();
1885 emit readOnlyChanged(r);
1886 if (!d->selectByKeyboardSet)
1887 emit selectByKeyboardChanged(!r);
1889 setCursorVisible(
false);
1890 }
else if (hasActiveFocus()) {
1891 setCursorVisible(
true);
1894#if QT_CONFIG(accessibility)
1895 if (QAccessible::isActive()) {
1896 if (QQuickAccessibleAttached *accessibleAttached = QQuickAccessibleAttached::attachedProperties(
this))
1897 accessibleAttached->set_readOnly(r);
1902bool QQuickTextEdit::isReadOnly()
const
1904 Q_D(
const QQuickTextEdit);
1905 return !(d->control->textInteractionFlags() & Qt::TextEditable);
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918QRectF QQuickTextEdit::cursorRectangle()
const
1920 Q_D(
const QQuickTextEdit);
1921 return d->control->cursorRect().translated(d->xoff, d->yoff);
1924bool QQuickTextEdit::event(QEvent *event)
1926 Q_D(QQuickTextEdit);
1927 bool state = QQuickImplicitSizeItem::event(event);
1928 if (event->type() == QEvent::ShortcutOverride && !event->isAccepted()) {
1929 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949bool QQuickTextEdit::overwriteMode()
const
1951 Q_D(
const QQuickTextEdit);
1952 return d->control->overwriteMode();
1955void QQuickTextEdit::setOverwriteMode(
bool overwrite)
1957 Q_D(QQuickTextEdit);
1958 d->control->setOverwriteMode(overwrite);
1962
1963
1964
1965void QQuickTextEdit::keyPressEvent(QKeyEvent *event)
1967 Q_D(QQuickTextEdit);
1968 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
1969 if (!event->isAccepted())
1970 QQuickImplicitSizeItem::keyPressEvent(event);
1974
1975
1976
1977void QQuickTextEdit::keyReleaseEvent(QKeyEvent *event)
1979 Q_D(QQuickTextEdit);
1980 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
1981 if (!event->isAccepted())
1982 QQuickImplicitSizeItem::keyReleaseEvent(event);
1986
1987
1988
1989
1990void QQuickTextEdit::deselect()
1992 Q_D(QQuickTextEdit);
1993 QTextCursor c = d->control->textCursor();
1995 d->control->setTextCursor(c);
1999
2000
2001
2002
2003void QQuickTextEdit::selectAll()
2005 Q_D(QQuickTextEdit);
2006 d->control->selectAll();
2010
2011
2012
2013
2014void QQuickTextEdit::selectWord()
2016 Q_D(QQuickTextEdit);
2017 QTextCursor c = d->control->textCursor();
2018 c.select(QTextCursor::WordUnderCursor);
2019 d->control->setTextCursor(c);
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035void QQuickTextEdit::select(
int start,
int end)
2037 Q_D(QQuickTextEdit);
2038 if (start < 0 || end < 0 || start >= d->document->characterCount() || end >= d->document->characterCount())
2040 QTextCursor cursor = d->control->textCursor();
2041 cursor.beginEditBlock();
2042 cursor.setPosition(start, QTextCursor::MoveAnchor);
2043 cursor.setPosition(end, QTextCursor::KeepAnchor);
2044 cursor.endEditBlock();
2045 d->control->setTextCursor(cursor);
2050 updateInputMethod();
2055
2056
2057
2058
2059
2060bool QQuickTextEdit::isRightToLeft(
int start,
int end)
2063 qmlWarning(
this) <<
"isRightToLeft(start, end) called with the end property being smaller than the start.";
2066 return getText(start, end).isRightToLeft();
2070#if QT_CONFIG(clipboard)
2072
2073
2074
2075
2076void QQuickTextEdit::cut()
2078 Q_D(QQuickTextEdit);
2083
2084
2085
2086
2087void QQuickTextEdit::copy()
2089 Q_D(QQuickTextEdit);
2094
2095
2096
2097
2098void QQuickTextEdit::paste()
2100 Q_D(QQuickTextEdit);
2101 d->control->paste();
2107
2108
2109
2110
2111
2112
2114void QQuickTextEdit::undo()
2116 Q_D(QQuickTextEdit);
2121
2122
2123
2124
2126void QQuickTextEdit::redo()
2128 Q_D(QQuickTextEdit);
2133
2134
2135
2136void QQuickTextEdit::mousePressEvent(QMouseEvent *event)
2138 Q_D(QQuickTextEdit);
2139 const bool isMouse = QQuickDeliveryAgentPrivate::isEventFromMouseOrTouchpad(event);
2140 setKeepMouseGrab(d->selectByMouse && isMouse);
2141 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2142 if (d->focusOnPress){
2143 bool hadActiveFocus = hasActiveFocus();
2144 forceActiveFocus(Qt::MouseFocusReason);
2147 if (hasActiveFocus() && hadActiveFocus && !isReadOnly())
2148 qGuiApp->inputMethod()->show();
2150 Q_UNUSED(hadActiveFocus);
2153 if (!event->isAccepted())
2154 QQuickImplicitSizeItem::mousePressEvent(event);
2158
2159
2160
2161void QQuickTextEdit::mouseReleaseEvent(QMouseEvent *event)
2163 Q_D(QQuickTextEdit);
2164 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2166 if (!event->isAccepted())
2167 QQuickImplicitSizeItem::mouseReleaseEvent(event);
2171
2172
2173
2174void QQuickTextEdit::mouseDoubleClickEvent(QMouseEvent *event)
2176 Q_D(QQuickTextEdit);
2177 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2178 if (!event->isAccepted())
2179 QQuickImplicitSizeItem::mouseDoubleClickEvent(event);
2183
2184
2185
2186void QQuickTextEdit::mouseMoveEvent(QMouseEvent *event)
2188 Q_D(QQuickTextEdit);
2189 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2190 if (!event->isAccepted())
2191 QQuickImplicitSizeItem::mouseMoveEvent(event);
2196
2197
2198
2199void QQuickTextEdit::inputMethodEvent(QInputMethodEvent *event)
2201 Q_D(QQuickTextEdit);
2202 const bool wasComposing = isInputMethodComposing();
2203 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2204 setCursorVisible(d->control->cursorVisible());
2205 if (wasComposing != isInputMethodComposing())
2206 emit inputMethodComposingChanged();
2210
2211
2212
2213QVariant QQuickTextEdit::inputMethodQuery(Qt::InputMethodQuery property, QVariant argument)
const
2215 Q_D(
const QQuickTextEdit);
2220 v = (
bool)(flags() & ItemAcceptsInputMethod);
2223 v = (
int)d->effectiveInputMethodHints();
2225 case Qt::ImInputItemClipRectangle:
2226 v = QQuickItem::inputMethodQuery(property);
2228 case Qt::ImReadOnly:
2232 if (property == Qt::ImCursorPosition && !argument.isNull())
2233 argument = QVariant(argument.toPointF() - QPointF(d->xoff, d->yoff));
2234 v = d->control->inputMethodQuery(property, argument);
2235 if (property == Qt::ImCursorRectangle || property == Qt::ImAnchorRectangle)
2236 v = QVariant(v.toRectF().translated(d->xoff, d->yoff));
2243
2244
2245
2246QVariant QQuickTextEdit::inputMethodQuery(Qt::InputMethodQuery property)
const
2248 return inputMethodQuery(property, QVariant());
2252void QQuickTextEdit::triggerPreprocess()
2254 Q_D(QQuickTextEdit);
2255 if (d->updateType == QQuickTextEditPrivate::UpdateNone)
2256 d->updateType = QQuickTextEditPrivate::UpdateOnlyPreprocess;
2262
2263
2264
2265
2266QVariant QQuickTextEdit::loadResource(
int type,
const QUrl &source)
2268 Q_D(QQuickTextEdit);
2269 const QUrl url = d->document->baseUrl().resolved(source);
2270 if (url.isLocalFile()) {
2272 QFileInfo fi(QQmlFile::urlToLocalFileOrQrc(url));
2274 qmlWarning(
this) <<
"Cannot open: " << url.toString();
2280 if (!url.scheme().compare(
"qrc"_L1, Qt::CaseInsensitive)) {
2282 QFile f(QQmlFile::urlToLocalFileOrQrc(url));
2283 if (f.open(QFile::ReadOnly)) {
2284 QByteArray buf = f.readAll();
2287 image.loadFromData(buf);
2288 if (!image.isNull())
2292 qmlWarning(
this) <<
"Cannot read resource: " << f.fileName();
2297 auto existingJobIter = std::find_if(
2298 d->pixmapsInProgress.cbegin(), d->pixmapsInProgress.cend(),
2299 [&url](
const auto *job) {
return job->url() == url; } );
2300 if (existingJobIter != d->pixmapsInProgress.cend()) {
2301 const QQuickPixmap *job = *existingJobIter;
2302 if (job->isError()) {
2303 qmlWarning(
this) << job->error();
2304 d->pixmapsInProgress.erase(existingJobIter);
2308 qCDebug(lcTextEdit) <<
"already downloading" << url;
2310 return job->isReady() ? job->image() : QVariant();
2315 qCDebug(lcTextEdit) <<
"loading" << source <<
"resolved" << url
2316 <<
"type" <<
static_cast<QTextDocument::ResourceType>(type);
2317 QQmlContext *context = qmlContext(
this);
2320 QQuickPixmap *p =
new QQuickPixmap(context->engine(), url, QQuickPixmap::Options{});
2321 p->connectFinished(
this, SLOT(resourceRequestFinished()));
2322 d->pixmapsInProgress.append(p);
2324 return p->isReady() ? p->image() : QVariant();
2328
2329
2330void QQuickTextEdit::resourceRequestFinished()
2332 Q_D(QQuickTextEdit);
2333 for (
auto it = d->pixmapsInProgress.cbegin(); it != d->pixmapsInProgress.cend(); ++it) {
2335 if (job->isError()) {
2337 qCDebug(lcTextEdit) <<
"failed to load (error)" << job->url();
2338 d->document->resource(QTextDocument::ImageResource, job->url());
2342 }
else if (job->isReady()) {
2344 auto res = d->document->resource(QTextDocument::ImageResource, job->url());
2346 qCDebug(lcTextEdit) << (res.isValid() ?
"done downloading" :
"failed to load") << job->url() << job->rect();
2347 d->pixmapsInProgress.erase(it);
2352 if (d->pixmapsInProgress.isEmpty()) {
2360using TextNodeIterator = QQuickTextEditPrivate::TextNodeIterator;
2364 return n1.startPos() < n2.startPos();
2369 QMatrix4x4 transformMatrix;
2370 transformMatrix.translate(topLeft.x(), topLeft.y());
2371 node->setMatrix(transformMatrix);
2375
2376
2377
2378
2379
2380void QQuickTextEdit::invalidateFontCaches()
2382 Q_D(QQuickTextEdit);
2383 if (d->document ==
nullptr)
2387 for (block = d->document->firstBlock(); block.isValid(); block = block.next()) {
2388 if (block.layout() !=
nullptr && block.layout()->engine() !=
nullptr)
2389 block.layout()->engine()->resetFontEngineCache();
2393QTextDocument *QQuickTextEdit::document()
const
2395 Q_D(
const QQuickTextEdit);
2399void QQuickTextEdit::setDocument(QTextDocument *doc)
2401 Q_D(QQuickTextEdit);
2403 std::unique_ptr<QTextDocument> cleanup(d->ownsDocument ? d->document :
nullptr);
2405 d->ownsDocument =
false;
2406 d->control->setDocument(doc);
2416 engine->setDevicePixelRatio(dpr);
2419QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData)
2421 Q_UNUSED(updatePaintNodeData);
2422 Q_D(QQuickTextEdit);
2424 if (d->updateType != QQuickTextEditPrivate::UpdatePaintNode
2425 && d->updateType != QQuickTextEditPrivate::UpdateAll
2426 && oldNode !=
nullptr) {
2428 d->updateType = QQuickTextEditPrivate::UpdateNone;
2432 d->containsUnscalableGlyphs =
false;
2433 if (!oldNode || d->updateType == QQuickTextEditPrivate::UpdateAll) {
2439 d->textNodeMap.clear();
2442 d->updateType = QQuickTextEditPrivate::UpdateNone;
2444 RootNode *rootNode =
static_cast<RootNode *>(oldNode);
2445 TextNodeIterator nodeIterator = d->textNodeMap.begin();
2446 std::optional<
int> firstPosAcrossAllNodes;
2447 if (nodeIterator != d->textNodeMap.end())
2448 firstPosAcrossAllNodes = nodeIterator->startPos();
2450 while (nodeIterator != d->textNodeMap.end() && !nodeIterator->dirty())
2453 const auto dpr = d->effectiveDevicePixelRatio();
2454 QQuickTextNodeEngine engine;
2455 engine.setDevicePixelRatio(dpr);
2456 QQuickTextNodeEngine frameDecorationsEngine;
2457 frameDecorationsEngine.setDevicePixelRatio(dpr);
2459 if (!oldNode || nodeIterator < d->textNodeMap.end() || d->textNodeMap.isEmpty()) {
2462 rootNode =
new RootNode;
2464 int firstDirtyPos = 0;
2465 if (nodeIterator != d->textNodeMap.end()) {
2466 firstDirtyPos = nodeIterator->startPos();
2469 QSGInternalTextNode *firstCleanNode =
nullptr;
2470 auto it = d->textNodeMap.constEnd();
2471 while (it != nodeIterator) {
2475 firstCleanNode = it->textNode();
2478 rootNode->removeChildNode(nodeIterator->textNode());
2479 delete nodeIterator->textNode();
2480 nodeIterator = d->textNodeMap.erase(nodeIterator);
2481 }
while (nodeIterator != d->textNodeMap.constEnd() && nodeIterator->textNode() != firstCleanNode);
2486 if (flags().testFlag(QQuickItem::ItemObservesViewport)) {
2487 viewport = clipRect();
2488 qCDebug(lcVP) <<
"text viewport" << viewport;
2492 rootNode->resetFrameDecorations(d->createTextNode());
2493 resetEngine(&frameDecorationsEngine, d->color, d->selectedTextColor, d->selectionColor, dpr);
2495 QSGInternalTextNode *node =
nullptr;
2497 int currentNodeSize = 0;
2498 int nodeStart = firstDirtyPos;
2499 QPointF basePosition(d->xoff, d->yoff);
2500 QMatrix4x4 basePositionMatrix;
2501 basePositionMatrix.translate(basePosition.x(), basePosition.y());
2502 rootNode->setMatrix(basePositionMatrix);
2505 const TextNode firstCleanNode = (nodeIterator != d->textNodeMap.end()) ? *nodeIterator
2508 QList<QTextFrame *> frames;
2509 frames.append(d->document->rootFrame());
2512 d->firstBlockInViewport = -1;
2513 d->firstBlockPastViewport = -1;
2514 int frameCount = -1;
2515 while (!frames.isEmpty()) {
2516 QTextFrame *textFrame = frames.takeFirst();
2520 qCDebug(lcVP) <<
"frame" << frameCount << textFrame
2521 <<
"from" << positionToRectangle(textFrame->firstPosition()).topLeft()
2522 <<
"to" << positionToRectangle(textFrame->lastPosition()).bottomRight();
2523 frames.append(textFrame->childFrames());
2524 frameDecorationsEngine.addFrameDecorations(d->document, textFrame);
2525 resetEngine(&engine, d->color, d->selectedTextColor, d->selectionColor, dpr);
2527 if (textFrame->firstPosition() > textFrame->lastPosition()
2528 && textFrame->frameFormat().position() != QTextFrameFormat::InFlow) {
2529 node = d->createTextNode();
2530 updateNodeTransform(node, d->document->documentLayout()->frameBoundingRect(textFrame).topLeft());
2531 const int pos = textFrame->firstPosition() - 1;
2532 auto *a =
static_cast<QtPrivate::ProtectedLayoutAccessor *>(d->document->documentLayout());
2533 QTextCharFormat format = a->formatAccessor(pos);
2534 QTextBlock block = textFrame->firstCursorPosition().block();
2535 nodeOffset = d->document->documentLayout()->blockBoundingRect(block).topLeft();
2537 if (!viewport.isNull() && block.layout()) {
2538 QRectF coveredRegion = block.layout()->boundingRect().adjusted(nodeOffset.x(), nodeOffset.y(), nodeOffset.x(), nodeOffset.y());
2539 inView = coveredRegion.bottom() >= viewport.top() && coveredRegion.top() <= viewport.bottom();
2540 qCDebug(lcVP) <<
"non-flow frame" << coveredRegion <<
"in viewport?" << inView;
2543 engine.setCurrentLine(block.layout()->lineForTextPosition(pos - block.position()));
2544 engine.addTextObject(block, QPointF(0, 0), format, QQuickTextNodeEngine::Unselected, d->document,
2545 pos, textFrame->frameFormat().position());
2550 QVarLengthArray<
int, 8> frameBoundaries;
2551 frameBoundaries.reserve(frames.size());
2552 for (QTextFrame *frame : std::as_const(frames))
2553 frameBoundaries.append(frame->firstPosition());
2554 std::sort(frameBoundaries.begin(), frameBoundaries.end());
2556 QTextFrame::iterator it = textFrame->begin();
2557 while (!it.atEnd()) {
2558 QTextBlock block = it.currentBlock();
2559 if (block.position() < firstDirtyPos) {
2564 if (!engine.hasContents())
2565 nodeOffset = d->document->documentLayout()->blockBoundingRect(block).topLeft();
2568 if (!viewport.isNull()) {
2569 QRectF coveredRegion;
2570 if (block.layout()) {
2571 coveredRegion = block.layout()->boundingRect().adjusted(nodeOffset.x(), nodeOffset.y(), nodeOffset.x(), nodeOffset.y());
2572 inView = coveredRegion.bottom() > viewport.top();
2574 const bool potentiallyScrollingBackwards = firstPosAcrossAllNodes && *firstPosAcrossAllNodes == firstDirtyPos;
2575 if (d->firstBlockInViewport < 0 && inView && potentiallyScrollingBackwards) {
2577 if (coveredRegion.top() > viewport.top() + 1) {
2578 qCDebug(lcVP) <<
"checking backwards from block" << block.blockNumber() <<
"@" << nodeOffset.y() << coveredRegion;
2579 while (it != textFrame->begin() && it.currentBlock().layout() &&
2580 it.currentBlock().layout()->boundingRect().top() + nodeOffset.y() > viewport.top()) {
2581 nodeOffset = d->document->documentLayout()->blockBoundingRect(it.currentBlock()).topLeft();
2584 if (!it.currentBlock().layout())
2586 if (Q_LIKELY(it.currentBlock().layout())) {
2587 block = it.currentBlock();
2588 coveredRegion = block.layout()->boundingRect().adjusted(nodeOffset.x(), nodeOffset.y(), nodeOffset.x(), nodeOffset.y());
2589 firstDirtyPos = it.currentBlock().position();
2591 qCWarning(lcVP) <<
"failed to find a text block with layout during back-scrolling";
2594 qCDebug(lcVP) <<
"first block in viewport" << block.blockNumber() <<
"@" << nodeOffset.y() << coveredRegion;
2596 d->renderedRegion = coveredRegion;
2598 if (nodeOffset.y() > viewport.bottom()) {
2600 if (d->firstBlockInViewport >= 0 && d->firstBlockPastViewport < 0) {
2601 qCDebug(lcVP) <<
"first block past viewport" << viewport << block.blockNumber()
2602 <<
"@" << nodeOffset.y() <<
"total region rendered" << d->renderedRegion;
2603 d->firstBlockPastViewport = block.blockNumber();
2607 if (inView && !block.text().isEmpty() && coveredRegion.isValid()) {
2608 d->renderedRegion = d->renderedRegion.united(coveredRegion);
2611 if (!frames.isEmpty())
2612 viewport = viewport.united(d->renderedRegion);
2615 if (inView && d->firstBlockInViewport < 0)
2616 d->firstBlockInViewport = block.blockNumber();
2619 bool createdNodeInView =
false;
2621 if (!engine.hasContents()) {
2623 d->containsUnscalableGlyphs = d->containsUnscalableGlyphs
2624 || node->containsUnscalableGlyphs();
2625 if (!node->parent())
2626 d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart);
2628 node = d->createTextNode();
2629 createdNodeInView =
true;
2630 updateNodeTransform(node, nodeOffset);
2631 nodeStart = block.position();
2633 engine.addTextBlock(d->document, block, -nodeOffset, d->color, QColor(), selectionStart(), selectionEnd() - 1);
2634 currentNodeSize += block.length();
2637 if ((it.atEnd()) || block.next().position() >= firstCleanNode.startPos())
2639 const auto lowerBound =
2640 std::lower_bound(frameBoundaries.constBegin(),
2641 frameBoundaries.constEnd(), block.next().position());
2642 if (node && (currentNodeSize > nodeBreakingSize || lowerBound == frameBoundaries.constEnd() || *lowerBound > nodeStart)) {
2643 currentNodeSize = 0;
2644 d->containsUnscalableGlyphs = d->containsUnscalableGlyphs
2645 || node->containsUnscalableGlyphs();
2646 if (!node->parent())
2647 d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart);
2648 if (!createdNodeInView)
2649 node = d->createTextNode();
2650 resetEngine(&engine, d->color, d->selectedTextColor, d->selectionColor, dpr);
2651 nodeStart = block.next().position();
2656 if (Q_LIKELY(node)) {
2657 d->containsUnscalableGlyphs = d->containsUnscalableGlyphs
2658 || node->containsUnscalableGlyphs();
2659 if (Q_LIKELY(!node->parent()))
2660 d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart);
2663 frameDecorationsEngine.addToSceneGraph(rootNode->frameDecorationsNode,
nullptr, QQuickText::Normal, QColor());
2665 rootNode->prependChildNode(rootNode->frameDecorationsNode);
2667 Q_ASSERT(nodeIterator == d->textNodeMap.end()
2668 || (nodeIterator->textNode() == firstCleanNode.textNode()
2669 && nodeIterator->startPos() == firstCleanNode.startPos()));
2671 if (firstCleanNode.textNode() !=
nullptr) {
2672 QPointF oldOffset = firstCleanNode.textNode()->matrix().map(QPointF(0,0));
2673 QPointF currentOffset = d->document->documentLayout()->blockBoundingRect(
2674 d->document->findBlock(firstCleanNode.startPos())).topLeft();
2675 QPointF delta = currentOffset - oldOffset;
2676 while (nodeIterator != d->textNodeMap.end()) {
2677 QMatrix4x4 transformMatrix = nodeIterator->textNode()->matrix();
2678 transformMatrix.translate(delta.x(), delta.y());
2679 nodeIterator->textNode()->setMatrix(transformMatrix);
2687 std::sort(d->textNodeMap.begin(), d->textNodeMap.end());
2690 if (d->cursorComponent ==
nullptr) {
2691 QSGInternalRectangleNode* cursor =
nullptr;
2692 if (!isReadOnly() && d->cursorVisible && d->control->cursorOn() && d->control->cursorVisible())
2693 cursor = d->sceneGraphContext()->createInternalRectangleNode(d->control->cursorRect(), d->color);
2694 rootNode->resetCursorNode(cursor);
2697 invalidateFontCaches();
2702void QQuickTextEdit::updatePolish()
2704 invalidateFontCaches();
2708
2709
2710
2711
2712
2713bool QQuickTextEdit::canPaste()
const
2715 Q_D(
const QQuickTextEdit);
2716 if (!d->canPasteValid) {
2717 const_cast<QQuickTextEditPrivate *>(d)->canPaste = d->control->canPaste();
2718 const_cast<QQuickTextEditPrivate *>(d)->canPasteValid =
true;
2724
2725
2726
2727
2728
2730bool QQuickTextEdit::canUndo()
const
2732 Q_D(
const QQuickTextEdit);
2733 return d->document->isUndoAvailable();
2737
2738
2739
2740
2741
2743bool QQuickTextEdit::canRedo()
const
2745 Q_D(
const QQuickTextEdit);
2746 return d->document->isRedoAvailable();
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761bool QQuickTextEdit::isInputMethodComposing()
const
2766 Q_D(
const QQuickTextEdit);
2767 return d->control->hasImState();
2771QQuickTextEditPrivate::ExtraData::ExtraData()
2772 : explicitTopPadding(
false)
2773 , explicitLeftPadding(
false)
2774 , explicitRightPadding(
false)
2775 , explicitBottomPadding(
false)
2776 , implicitResize(
true)
2780void QQuickTextEditPrivate::init()
2782 Q_Q(QQuickTextEdit);
2784#if QT_CONFIG(clipboard)
2785 if (QGuiApplication::clipboard()->supportsSelection())
2786 q->setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton);
2789 q->setAcceptedMouseButtons(Qt::LeftButton);
2792 q->setFlag(QQuickItem::ItemAcceptsInputMethod);
2794 q->setFlag(QQuickItem::ItemHasContents);
2796 q->setAcceptHoverEvents(
true);
2798 document =
new QTextDocument(q);
2799 ownsDocument =
true;
2800 auto *imageHandler =
new QQuickTextImageHandler(document);
2801 document->documentLayout()->registerHandler(QTextFormat::ImageObject, imageHandler);
2803 control =
new QQuickTextControl(document, q);
2804 control->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard | Qt::TextEditable);
2805 control->setAcceptRichText(
false);
2806 control->setCursorIsFocusIndicator(
true);
2807 q->setKeepMouseGrab(
true);
2809 qmlobject_connect(control, QQuickTextControl, SIGNAL(updateCursorRequest()), q, QQuickTextEdit, SLOT(updateCursor()));
2810 qmlobject_connect(control, QQuickTextControl, SIGNAL(selectionChanged()), q, QQuickTextEdit, SIGNAL(selectedTextChanged()));
2811 qmlobject_connect(control, QQuickTextControl, SIGNAL(selectionChanged()), q, QQuickTextEdit, SLOT(updateSelection()));
2812 qmlobject_connect(control, QQuickTextControl, SIGNAL(cursorPositionChanged()), q, QQuickTextEdit, SLOT(updateSelection()));
2813 qmlobject_connect(control, QQuickTextControl, SIGNAL(cursorPositionChanged()), q, QQuickTextEdit, SIGNAL(cursorPositionChanged()));
2814 qmlobject_connect(control, QQuickTextControl, SIGNAL(cursorRectangleChanged()), q, QQuickTextEdit, SLOT(moveCursorDelegate()));
2815 qmlobject_connect(control, QQuickTextControl, SIGNAL(linkActivated(QString)), q, QQuickTextEdit, SIGNAL(linkActivated(QString)));
2816 qmlobject_connect(control, QQuickTextControl, SIGNAL(overwriteModeChanged(
bool)), q, QQuickTextEdit, SIGNAL(overwriteModeChanged(
bool)));
2817 qmlobject_connect(control, QQuickTextControl, SIGNAL(textChanged()), q, QQuickTextEdit, SLOT(q_textChanged()));
2818 qmlobject_connect(control, QQuickTextControl, SIGNAL(preeditTextChanged()), q, QQuickTextEdit, SIGNAL(preeditTextChanged()));
2819#if QT_CONFIG(clipboard)
2820 qmlobject_connect(QGuiApplication::clipboard(), QClipboard, SIGNAL(dataChanged()), q, QQuickTextEdit, SLOT(q_canPasteChanged()));
2822 qmlobject_connect(document, QTextDocument, SIGNAL(undoAvailable(
bool)), q, QQuickTextEdit, SIGNAL(canUndoChanged()));
2823 qmlobject_connect(document, QTextDocument, SIGNAL(redoAvailable(
bool)), q, QQuickTextEdit, SIGNAL(canRedoChanged()));
2824 QObject::connect(document, &QTextDocument::contentsChange, q, &QQuickTextEdit::q_contentsChange);
2825 QObject::connect(document->documentLayout(), &QAbstractTextDocumentLayout::updateBlock, q, &QQuickTextEdit::invalidateBlock);
2826 QObject::connect(control, &QQuickTextControl::linkHovered, q, &QQuickTextEdit::q_linkHovered);
2827 QObject::connect(control, &QQuickTextControl::markerHovered, q, &QQuickTextEdit::q_markerHovered);
2828 QObject::connect(control, &QQuickTextControl::hoveredToolTipChanged, q, &QQuickTextEdit::hoveredToolTipChanged);
2830 document->setPageSize(QSizeF(0, 0));
2831 document->setDefaultFont(font);
2832 document->setDocumentMargin(textMargin);
2833 document->setUndoRedoEnabled(
false);
2834 document->setUndoRedoEnabled(
true);
2835 updateDefaultTextOption();
2836 document->setModified(
false);
2838#if QT_CONFIG(cursor)
2839 updateMouseCursorShape();
2841 setSizePolicy(QLayoutPolicy::Expanding, QLayoutPolicy::Expanding);
2844void QQuickTextEditPrivate::resetInputMethod()
2846 Q_Q(QQuickTextEdit);
2847 if (!q->isReadOnly() && q->hasActiveFocus() && qGuiApp)
2848 QGuiApplication::inputMethod()->reset();
2851void QQuickTextEdit::q_textChanged()
2853 Q_D(QQuickTextEdit);
2854 d->textCached =
false;
2855 for (QTextBlock it = d->document->begin(); it != d->document->end(); it = it.next()) {
2856 d->contentDirection = d->textDirection(it.text());
2857 if (d->contentDirection != Qt::LayoutDirectionAuto)
2860 d->determineHorizontalAlignment();
2861 d->updateDefaultTextOption();
2864 markDirtyNodesForRange(0, d->document->characterCount(), 0);
2865 if (isComponentComplete()) {
2867 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
2872 if (d->control->isBeingEdited())
2876void QQuickTextEdit::markDirtyNodesForRange(
int start,
int end,
int charDelta)
2878 Q_D(QQuickTextEdit);
2882 TextNode dummyNode(start);
2884 const TextNodeIterator textNodeMapBegin = d->textNodeMap.begin();
2885 const TextNodeIterator textNodeMapEnd = d->textNodeMap.end();
2887 TextNodeIterator it = std::lower_bound(textNodeMapBegin, textNodeMapEnd, dummyNode);
2890 if (it != textNodeMapBegin) {
2892 TextNode otherDummy(it->startPos());
2893 it = std::lower_bound(textNodeMapBegin, textNodeMapEnd, otherDummy);
2897 while (it != textNodeMapEnd) {
2898 if (it->startPos() <= end)
2901 it->moveStartPos(charDelta);
2908void QQuickTextEdit::q_contentsChange(
int pos,
int charsRemoved,
int charsAdded)
2910 Q_D(QQuickTextEdit);
2912 const int editRange = pos + qMax(charsAdded, charsRemoved);
2913 const int delta = charsAdded - charsRemoved;
2915 markDirtyNodesForRange(pos, editRange, delta);
2917 if (isComponentComplete()) {
2919 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
2924void QQuickTextEdit::moveCursorDelegate()
2926 Q_D(QQuickTextEdit);
2928 updateInputMethod();
2930 emit cursorRectangleChanged();
2933 QRectF cursorRect = cursorRectangle();
2934 d->cursorItem->setX(cursorRect.x());
2935 d->cursorItem->setY(cursorRect.y());
2936 d->cursorItem->setHeight(cursorRect.height());
2939void QQuickTextEdit::updateSelection()
2941 Q_D(QQuickTextEdit);
2944 if (d->control->textCursor().hasSelection() || d->hadSelection) {
2945 markDirtyNodesForRange(qMin(d->lastSelectionStart, d->control->textCursor().selectionStart()), qMax(d->control->textCursor().selectionEnd(), d->lastSelectionEnd), 0);
2946 if (isComponentComplete()) {
2948 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
2953 d->hadSelection = d->control->textCursor().hasSelection();
2955 if (d->lastSelectionStart != d->control->textCursor().selectionStart()) {
2956 d->lastSelectionStart = d->control->textCursor().selectionStart();
2957 emit selectionStartChanged();
2959 if (d->lastSelectionEnd != d->control->textCursor().selectionEnd()) {
2960 d->lastSelectionEnd = d->control->textCursor().selectionEnd();
2961 emit selectionEndChanged();
2965QRectF QQuickTextEdit::boundingRect()
const
2967 Q_D(
const QQuickTextEdit);
2969 QQuickTextUtil::alignedX(d->contentSize.width(), width(), effectiveHAlign()),
2971 d->contentSize.width(),
2972 d->contentSize.height());
2974 int cursorWidth = 1;
2977 else if (!d->document->isEmpty())
2981 r.setRight(r.right() + cursorWidth);
2986QRectF QQuickTextEdit::clipRect()
const
2988 Q_D(
const QQuickTextEdit);
2989 QRectF r = QQuickImplicitSizeItem::clipRect();
2990 int cursorWidth = 1;
2992 cursorWidth = d->cursorItem->width();
2993 if (!d->document->isEmpty())
2998 r.setRight(r.right() + cursorWidth);
3002qreal QQuickTextEditPrivate::getImplicitWidth()
const
3004 Q_Q(
const QQuickTextEdit);
3005 if (!requireImplicitWidth) {
3008 const_cast<QQuickTextEditPrivate*>(
this)->requireImplicitWidth =
true;
3009 const_cast<QQuickTextEdit*>(q)->updateSize();
3011 return implicitWidth;
3016void QQuickTextEdit::updateSize()
3018 Q_D(QQuickTextEdit);
3019 if (!isComponentComplete()) {
3027 if (!d->requireImplicitWidth) {
3028 emit implicitWidthChanged();
3030 if (d->requireImplicitWidth)
3033 if (d->requireImplicitWidth) {
3034 d->document->setTextWidth(-1);
3035 const qreal naturalWidth = d->document->idealWidth();
3036 const bool wasInLayout = d->inLayout;
3038 if (d->isImplicitResizeEnabled())
3039 setImplicitWidth(naturalWidth + leftPadding() + rightPadding());
3040 d->inLayout = wasInLayout;
3044 const qreal newTextWidth = width() - leftPadding() - rightPadding();
3045 if (d->document->textWidth() != newTextWidth)
3046 d->document->setTextWidth(newTextWidth);
3047 }
else if (d->wrapMode == NoWrap) {
3051 const qreal newTextWidth = d->document->idealWidth();
3052 if (d->document->textWidth() != newTextWidth)
3053 d->document->setTextWidth(newTextWidth);
3055 d->document->setTextWidth(-1);
3058 QFontMetricsF fm(d->font);
3059 const qreal newHeight = d->document->isEmpty() ? qCeil(fm.height()) : d->document->size().height();
3060 const qreal newWidth = d->document->idealWidth();
3062 if (d->isImplicitResizeEnabled()) {
3065 setImplicitSize(newWidth + leftPadding() + rightPadding(), newHeight + topPadding() + bottomPadding());
3067 setImplicitHeight(newHeight + topPadding() + bottomPadding());
3070 d->xoff = leftPadding() + qMax(qreal(0), QQuickTextUtil::alignedX(d->document->size().width(), width() - leftPadding() - rightPadding(), effectiveHAlign()));
3071 d->yoff = topPadding() + QQuickTextUtil::alignedY(d->document->size().height(), height() - topPadding() - bottomPadding(), d->vAlign);
3073 qreal baseline = fm.ascent();
3074 QTextBlock firstBlock = d->document->firstBlock();
3075 if (firstBlock.isValid() && firstBlock.layout() !=
nullptr && firstBlock.lineCount() > 0) {
3076 QTextLine firstLine = firstBlock.layout()->lineAt(0);
3077 if (firstLine.isValid())
3078 baseline = firstLine.ascent();
3081 setBaselineOffset(baseline + d->yoff + d->textMargin);
3083 QSizeF size(newWidth, newHeight);
3084 if (d->contentSize != size) {
3085 d->contentSize = size;
3087 const bool wasInResize = d->inResize;
3090 emit contentSizeChanged();
3091 d->inResize = wasInResize;
3096void QQuickTextEdit::updateWholeDocument()
3098 Q_D(QQuickTextEdit);
3099 if (!d->textNodeMap.isEmpty()) {
3100 for (TextNode &node : d->textNodeMap)
3104 if (isComponentComplete()) {
3106 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
3111void QQuickTextEdit::invalidateBlock(
const QTextBlock &block)
3113 Q_D(QQuickTextEdit);
3114 markDirtyNodesForRange(block.position(), block.position() + block.length(), 0);
3116 if (isComponentComplete()) {
3118 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
3123void QQuickTextEdit::updateCursor()
3125 Q_D(QQuickTextEdit);
3126 if (isComponentComplete() && isVisible()) {
3128 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
3133void QQuickTextEdit::q_linkHovered(
const QString &link)
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) {
3141 setCursor(Qt::PointingHandCursor);
3146void QQuickTextEdit::q_markerHovered(
bool hovered)
3148 Q_D(QQuickTextEdit);
3149#if QT_CONFIG(cursor)
3151 d->updateMouseCursorShape();
3152 }
else if (cursor().shape() != Qt::PointingHandCursor) {
3153 setCursor(Qt::PointingHandCursor);
3158void QQuickTextEdit::q_updateAlignment()
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 updateWholeDocument();
3169void QQuickTextEdit::updateTotalLines()
3171 Q_D(QQuickTextEdit);
3175 for (QTextBlock it = d->document->begin(); it != d->document->end(); it = it.next()) {
3176 QTextLayout *layout = it.layout();
3179 subLines += layout->lineCount()-1;
3182 int newTotalLines = d->document->lineCount() + subLines;
3183 if (d->lineCount != newTotalLines) {
3184 d->lineCount = newTotalLines;
3185 emit lineCountChanged();
3189void QQuickTextEditPrivate::updateDefaultTextOption()
3191 Q_Q(QQuickTextEdit);
3192 QTextOption opt = document->defaultTextOption();
3193 const Qt::Alignment oldAlignment = opt.alignment();
3194 Qt::LayoutDirection oldTextDirection = opt.textDirection();
3196 QQuickTextEdit::HAlignment horizontalAlignment = q->effectiveHAlign();
3197 if (contentDirection == Qt::RightToLeft) {
3198 if (horizontalAlignment == QQuickTextEdit::AlignLeft)
3199 horizontalAlignment = QQuickTextEdit::AlignRight;
3200 else if (horizontalAlignment == QQuickTextEdit::AlignRight)
3201 horizontalAlignment = QQuickTextEdit::AlignLeft;
3203 if (!hAlignImplicit)
3204 opt.setAlignment((Qt::Alignment)(
int)(horizontalAlignment | vAlign));
3206 opt.setAlignment(Qt::Alignment(vAlign));
3209 if (contentDirection == Qt::LayoutDirectionAuto) {
3210 opt.setTextDirection(qGuiApp->inputMethod()->inputDirection());
3214 opt.setTextDirection(contentDirection);
3217 QTextOption::WrapMode oldWrapMode = opt.wrapMode();
3218 opt.setWrapMode(QTextOption::WrapMode(wrapMode));
3220 bool oldUseDesignMetrics = opt.useDesignMetrics();
3221 opt.setUseDesignMetrics(renderType != QQuickTextEdit::NativeRendering);
3223 if (oldWrapMode != opt.wrapMode() || oldAlignment != opt.alignment()
3224 || oldTextDirection != opt.textDirection()
3225 || oldUseDesignMetrics != opt.useDesignMetrics()) {
3226 document->setDefaultTextOption(opt);
3230void QQuickTextEditPrivate::onDocumentStatusChanged()
3232 Q_ASSERT(quickDocument);
3233 switch (quickDocument->status()) {
3234 case QQuickTextDocument::Status::Loaded:
3235 case QQuickTextDocument::Status::Saved:
3236 switch (QQuickTextDocumentPrivate::get(quickDocument)->detectedFormat) {
3238 richText = (format == QQuickTextEdit::RichText || format == QQuickTextEdit::AutoText);
3239 markdownText =
false;
3241 case Qt::MarkdownText:
3243 markdownText = (format == QQuickTextEdit::MarkdownText || format == QQuickTextEdit::AutoText);
3247 markdownText =
false;
3258void QQuickTextEdit::focusInEvent(QFocusEvent *event)
3260 Q_D(QQuickTextEdit);
3261 d->handleFocusEvent(event);
3262 QQuickImplicitSizeItem::focusInEvent(event);
3265void QQuickTextEdit::focusOutEvent(QFocusEvent *event)
3267 Q_D(QQuickTextEdit);
3268 d->handleFocusEvent(event);
3269 QQuickImplicitSizeItem::focusOutEvent(event);
3272#if QT_VERSION < QT_VERSION_CHECK(7
, 0
, 0
)
3273bool QQuickTextEditPrivate::handleContextMenuEvent(QContextMenuEvent *event)
3275bool QQuickTextEdit::contextMenuEvent(QContextMenuEvent *event)
3278 Q_Q(QQuickTextEdit);
3279 QContextMenuEvent mapped(event->reason(),
3280 q->mapToScene(q->cursorRectangle().center()).toPoint(), event->globalPos(),
3281 event->modifiers());
3282 const bool eventProcessed = QQuickItemPrivate::handleContextMenuEvent(&mapped);
3283 event->setAccepted(mapped.isAccepted());
3284 return eventProcessed;
3287void QQuickTextEditPrivate::handleFocusEvent(QFocusEvent *event)
3289 Q_Q(QQuickTextEdit);
3290 bool focus = event->type() == QEvent::FocusIn;
3291 if (!q->isReadOnly())
3292 q->setCursorVisible(focus);
3293 control->processEvent(event, QPointF(-xoff, -yoff));
3295 q->q_updateAlignment();
3297 if (focusOnPress && !q->isReadOnly())
3298 qGuiApp->inputMethod()->show();
3299 q->connect(QGuiApplication::inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
3300 q, SLOT(q_updateAlignment()));
3304 q->disconnect(QGuiApplication::inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
3305 q, SLOT(q_updateAlignment()));
3307 if (event->reason() != Qt::ActiveWindowFocusReason
3308 && event->reason() != Qt::PopupFocusReason
3309 && control->textCursor().hasSelection()
3310 && !persistentSelection)
3313 emit q->editingFinished();
3317void QQuickTextEditPrivate::addCurrentTextNodeToRoot(QQuickTextNodeEngine *engine, QSGTransformNode *root, QSGInternalTextNode *node, TextNodeIterator &it,
int startPos)
3319 engine->addToSceneGraph(node,
nullptr, QQuickText::Normal, QColor());
3320 it = textNodeMap.insert(it, TextNode(startPos, node));
3322 root->appendChildNode(node);
3323 ++renderedBlockCount;
3326QSGInternalTextNode *QQuickTextEditPrivate::createTextNode()
3328 Q_Q(QQuickTextEdit);
3329 QSGInternalTextNode* node = sceneGraphContext()->createInternalTextNode(sceneGraphRenderContext());
3330 node->setRenderType(QSGTextNode::RenderType(renderType));
3331 node->setFiltering(q->smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
3335void QQuickTextEdit::q_canPasteChanged()
3337 Q_D(QQuickTextEdit);
3338 bool old = d->canPaste;
3339 d->canPaste = d->control->canPaste();
3340 bool changed = old!=d->canPaste || !d->canPasteValid;
3341 d->canPasteValid =
true;
3343 emit canPasteChanged();
3347
3348
3349
3350
3351
3352
3354QString QQuickTextEdit::getText(
int start,
int end)
const
3356 Q_D(
const QQuickTextEdit);
3357 start = qBound(0, start, d->document->characterCount() - 1);
3358 end = qBound(0, end, d->document->characterCount() - 1);
3359 QTextCursor cursor(d->document);
3360 cursor.setPosition(start, QTextCursor::MoveAnchor);
3361 cursor.setPosition(end, QTextCursor::KeepAnchor);
3362#if QT_CONFIG(texthtmlparser)
3363 return d->richText || d->markdownText
3364 ? cursor.selectedText()
3365 : cursor.selection().toPlainText();
3367 return cursor.selection().toPlainText();
3372
3373
3374
3375
3376
3377
3379QString QQuickTextEdit::getFormattedText(
int start,
int end)
const
3381 Q_D(
const QQuickTextEdit);
3383 start = qBound(0, start, d->document->characterCount() - 1);
3384 end = qBound(0, end, d->document->characterCount() - 1);
3386 QTextCursor cursor(d->document);
3387 cursor.setPosition(start, QTextCursor::MoveAnchor);
3388 cursor.setPosition(end, QTextCursor::KeepAnchor);
3391#if QT_CONFIG(texthtmlparser)
3392 return cursor.selection().toHtml();
3394 return cursor.selection().toPlainText();
3396 }
else if (d->markdownText) {
3397#if QT_CONFIG(textmarkdownwriter)
3398 return cursor.selection().toMarkdown();
3400 return cursor.selection().toPlainText();
3403 return cursor.selection().toPlainText();
3408
3409
3410
3411
3412void QQuickTextEdit::insert(
int position,
const QString &text)
3414 Q_D(QQuickTextEdit);
3415 if (position < 0 || position >= d->document->characterCount())
3417 QTextCursor cursor(d->document);
3418 cursor.setPosition(position);
3419 d->richText = d->richText || (d->format == AutoText && Qt::mightBeRichText(text));
3421#if QT_CONFIG(texthtmlparser)
3422 cursor.insertHtml(text);
3424 cursor.insertText(text);
3426 }
else if (d->markdownText) {
3427#if QT_CONFIG(textmarkdownreader)
3428 cursor.insertMarkdown(text);
3430 cursor.insertText(text);
3433 cursor.insertText(text);
3435 d->control->updateCursorRectangle(
false);
3439
3440
3441
3442
3444void QQuickTextEdit::remove(
int start,
int end)
3446 Q_D(QQuickTextEdit);
3447 start = qBound(0, start, d->document->characterCount() - 1);
3448 end = qBound(0, end, d->document->characterCount() - 1);
3449 QTextCursor cursor(d->document);
3450 cursor.setPosition(start, QTextCursor::MoveAnchor);
3451 cursor.setPosition(end, QTextCursor::KeepAnchor);
3452 cursor.removeSelectedText();
3453 d->control->updateCursorRectangle(
false);
3457
3458
3459
3460
3461
3462
3463
3464
3465
3466
3468QQuickTextDocument *QQuickTextEdit::textDocument()
3470 Q_D(QQuickTextEdit);
3471 if (!d->quickDocument) {
3472 d->quickDocument =
new QQuickTextDocument(
this);
3473 connect(d->quickDocument, &QQuickTextDocument::statusChanged, d->quickDocument,
3474 [d]() { d->onDocumentStatusChanged(); } );
3476 return d->quickDocument;
3479bool QQuickTextEditPrivate::isLinkHoveredConnected()
3481 Q_Q(QQuickTextEdit);
3482 IS_SIGNAL_CONNECTED(q, QQuickTextEdit, linkHovered, (
const QString &));
3485bool QQuickTextEditPrivate::isHoveredToolTipChangedConnected()
3487 Q_Q(QQuickTextEdit);
3488 IS_SIGNAL_CONNECTED(q, QQuickTextEdit, hoveredToolTipChanged, ());
3491bool QQuickTextEditPrivate::isHoveredSignalConnected()
3493 return isLinkHoveredConnected() || isHoveredToolTipChangedConnected();
3496#if QT_CONFIG(cursor)
3497void QQuickTextEditPrivate::updateMouseCursorShape()
3499 Q_Q(QQuickTextEdit);
3500 q->setCursor(q->isReadOnly() && !q->selectByMouse() ? Qt::ArrowCursor : Qt::IBeamCursor);
3505
3506
3507
3508
3509
3510
3511
3512
3513
3516
3517
3518
3519
3520
3523
3524
3525
3526
3527
3528
3529
3530
3531
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3556
3557
3558
3559
3560
3561
3562
3564QString QQuickTextEdit::hoveredLink()
const
3566 Q_D(
const QQuickTextEdit);
3567 if (
const_cast<QQuickTextEditPrivate *>(d)->isLinkHoveredConnected()) {
3568 return d->control->hoveredLink();
3570#if QT_CONFIG(cursor)
3571 if (QQuickWindow *wnd = window()) {
3572 QPointF pos = QCursor::pos(wnd->screen()) - wnd->position() - mapToScene(QPointF(0, 0));
3573 return d->control->anchorAt(pos);
3580QString QQuickTextEdit::hoveredToolTip()
const
3582 Q_D(
const QQuickTextEdit);
3583 if (
const_cast<QQuickTextEditPrivate *>(d)->isHoveredToolTipChangedConnected()) {
3584 return d->control->hoveredToolTip();
3586#if QT_CONFIG(cursor)
3587 if (QQuickWindow *wnd = window()) {
3588 const QPointF pos = QCursor::pos(wnd->screen()) - wnd->position() - mapToScene(QPointF(0, 0));
3589 return d->control->charFormatAt(pos).toolTip();
3596void QQuickTextEdit::hoverEnterEvent(QHoverEvent *event)
3598 Q_D(QQuickTextEdit);
3599 if (d->isHoveredSignalConnected())
3600 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
3604void QQuickTextEdit::hoverMoveEvent(QHoverEvent *event)
3606 Q_D(QQuickTextEdit);
3607 if (d->isHoveredSignalConnected())
3608 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
3612void QQuickTextEdit::hoverLeaveEvent(QHoverEvent *event)
3614 Q_D(QQuickTextEdit);
3615 if (d->isHoveredSignalConnected())
3616 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
3621
3622
3623
3624
3625
3626
3627
3628
3629void QQuickTextEdit::append(
const QString &text)
3631 Q_D(QQuickTextEdit);
3632 QTextCursor cursor(d->document);
3633 cursor.beginEditBlock();
3634 cursor.movePosition(QTextCursor::End);
3636 if (!d->document->isEmpty())
3637 cursor.insertBlock();
3639 if (d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(text))) {
3640#if QT_CONFIG(texthtmlparser)
3641 cursor.insertHtml(text);
3643 cursor.insertText(text);
3645 }
else if (d->format == MarkdownText) {
3646#if QT_CONFIG(textmarkdownreader)
3647 cursor.insertMarkdown(text);
3649 cursor.insertText(text);
3652 cursor.insertText(text);
3655 cursor.endEditBlock();
3656 d->control->updateCursorRectangle(
false);
3660
3661
3662
3663
3664
3665
3666
3667
3668QString QQuickTextEdit::linkAt(qreal x, qreal y)
const
3670 Q_D(
const QQuickTextEdit);
3671 return d->control->anchorAt(QPointF(x + topPadding(), y + leftPadding()));
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685qreal QQuickTextEdit::padding()
const
3687 Q_D(
const QQuickTextEdit);
3688 return d->padding();
3691void QQuickTextEdit::setPadding(qreal padding)
3693 Q_D(QQuickTextEdit);
3694 if (qFuzzyCompare(d->padding(), padding))
3697 d->extra.value().padding = padding;
3699 if (isComponentComplete()) {
3700 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
3703 emit paddingChanged();
3704 if (!d->extra.isAllocated() || !d->extra->explicitTopPadding)
3705 emit topPaddingChanged();
3706 if (!d->extra.isAllocated() || !d->extra->explicitLeftPadding)
3707 emit leftPaddingChanged();
3708 if (!d->extra.isAllocated() || !d->extra->explicitRightPadding)
3709 emit rightPaddingChanged();
3710 if (!d->extra.isAllocated() || !d->extra->explicitBottomPadding)
3711 emit bottomPaddingChanged();
3714void QQuickTextEdit::resetPadding()
3719qreal QQuickTextEdit::topPadding()
const
3721 Q_D(
const QQuickTextEdit);
3722 if (d->extra.isAllocated() && d->extra->explicitTopPadding)
3723 return d->extra->topPadding;
3724 return d->padding();
3727void QQuickTextEdit::setTopPadding(qreal padding)
3729 Q_D(QQuickTextEdit);
3730 d->setTopPadding(padding);
3733void QQuickTextEdit::resetTopPadding()
3735 Q_D(QQuickTextEdit);
3736 d->setTopPadding(0,
true);
3739qreal QQuickTextEdit::leftPadding()
const
3741 Q_D(
const QQuickTextEdit);
3742 if (d->extra.isAllocated() && d->extra->explicitLeftPadding)
3743 return d->extra->leftPadding;
3744 return d->padding();
3747void QQuickTextEdit::setLeftPadding(qreal padding)
3749 Q_D(QQuickTextEdit);
3750 d->setLeftPadding(padding);
3753void QQuickTextEdit::resetLeftPadding()
3755 Q_D(QQuickTextEdit);
3756 d->setLeftPadding(0,
true);
3759qreal QQuickTextEdit::rightPadding()
const
3761 Q_D(
const QQuickTextEdit);
3762 if (d->extra.isAllocated() && d->extra->explicitRightPadding)
3763 return d->extra->rightPadding;
3764 return d->padding();
3767void QQuickTextEdit::setRightPadding(qreal padding)
3769 Q_D(QQuickTextEdit);
3770 d->setRightPadding(padding);
3773void QQuickTextEdit::resetRightPadding()
3775 Q_D(QQuickTextEdit);
3776 d->setRightPadding(0,
true);
3779qreal QQuickTextEdit::bottomPadding()
const
3781 Q_D(
const QQuickTextEdit);
3782 if (d->extra.isAllocated() && d->extra->explicitBottomPadding)
3783 return d->extra->bottomPadding;
3784 return d->padding();
3787void QQuickTextEdit::setBottomPadding(qreal padding)
3789 Q_D(QQuickTextEdit);
3790 d->setBottomPadding(padding);
3793void QQuickTextEdit::resetBottomPadding()
3795 Q_D(QQuickTextEdit);
3796 d->setBottomPadding(0,
true);
3800
3801
3802
3803
3804
3805
3806
3807int QQuickTextEdit::tabStopDistance()
const
3809 Q_D(
const QQuickTextEdit);
3810 return d->document->defaultTextOption().tabStopDistance();
3813void QQuickTextEdit::setTabStopDistance(qreal distance)
3815 Q_D(QQuickTextEdit);
3816 QTextOption textOptions = d->document->defaultTextOption();
3817 if (textOptions.tabStopDistance() == distance)
3820 textOptions.setTabStopDistance(distance);
3821 d->document->setDefaultTextOption(textOptions);
3822 emit tabStopDistanceChanged(distance);
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836void QQuickTextEdit::clear()
3838 Q_D(QQuickTextEdit);
3839 d->resetInputMethod();
3840 d->control->clear();
3843#ifndef QT_NO_DEBUG_STREAM
3846 QDebugStateSaver saver(debug);
3848 debug <<
"Node(startPos:" << n.m_startPos <<
"dirty:" << n.m_dirty << n.m_node <<
')';