70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
92
93
94
95
96
97
103#if !defined(QQUICKTEXT_LARGETEXT_THRESHOLD)
104 #define QQUICKTEXT_LARGETEXT_THRESHOLD 10000
110 class RootNode :
public QSGTransformNode
113 RootNode() : cursorNode(
nullptr), frameDecorationsNode(
nullptr)
116 void resetFrameDecorations(QSGInternalTextNode* newNode)
118 if (frameDecorationsNode) {
119 removeChildNode(frameDecorationsNode);
120 delete frameDecorationsNode;
122 frameDecorationsNode = newNode;
123 newNode->setFlag(QSGNode::OwnedByParent);
126 void resetCursorNode(QSGInternalRectangleNode* newNode)
129 removeChildNode(cursorNode);
131 cursorNode = newNode;
133 appendChildNode(cursorNode);
134 cursorNode->setFlag(QSGNode::OwnedByParent);
138 QSGInternalRectangleNode *cursorNode;
139 QSGInternalTextNode* frameDecorationsNode;
144QQuickTextEdit::QQuickTextEdit(QQuickItem *parent)
145: QQuickImplicitSizeItem(*(
new QQuickTextEditPrivate), parent)
151QQuickTextEdit::~QQuickTextEdit()
154 qDeleteAll(d->pixmapsInProgress);
157QQuickTextEdit::QQuickTextEdit(QQuickTextEditPrivate &dd, QQuickItem *parent)
158: QQuickImplicitSizeItem(dd, parent)
164QString QQuickTextEdit::text()
const
166 Q_D(
const QQuickTextEdit);
167 if (!d->textCached && isComponentComplete()) {
168 QQuickTextEditPrivate *d =
const_cast<QQuickTextEditPrivate *>(d_func());
169#if QT_CONFIG(texthtmlparser)
171 d->text = d->control->toHtml();
174#if QT_CONFIG(textmarkdownwriter)
176 d->text = d->control->toMarkdown();
179 d->text = d->control->toPlainText();
180 d->textCached =
true;
186
187
188
189
190
191
192
193
196
197
198
199
200
201
202
203
207
208
209
210
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
234
235
236
237
240
241
242
243
246
247
248
249
252
253
254
255
258
259
260
261
262
263
264
265
268
269
270
271
272
273
274
277
278
279
280
281
282
283
284
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
335
336
337
338
339
340
341
342
343
344
345
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
365
366
367
368
369
372
373
374
375
376
379
380
381
382
383
386
387
388
389
390
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421void QQuickTextEdit::setText(
const QString &text)
424 if (QQuickTextEdit::text() == text)
427 d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(text));
428 d->markdownText = d->format == MarkdownText;
429 if (!isComponentComplete()) {
431 }
else if (d->richText) {
432#if QT_CONFIG(texthtmlparser)
433 d->control->setHtml(text);
435 d->control->setPlainText(text);
437 }
else if (d->markdownText) {
438 d->control->setMarkdownText(text);
440 d->control->setPlainText(text);
442 setFlag(QQuickItem::ItemObservesViewport, text.size() > QQuickTextEditPrivate::largeTextSizeThreshold);
445void QQuickTextEdit::invalidate()
447 QMetaObject::invokeMethod(
this, &QQuickTextEdit::q_invalidate);
450void QQuickTextEdit::q_invalidate()
453 if (isComponentComplete()) {
454 if (d->document !=
nullptr)
455 d->document->markContentsDirty(0, d->document->characterCount());
456 invalidateFontCaches();
457 d->updateType = QQuickTextEditPrivate::UpdateAll;
463
464
465
466
467
468
469
470
471
472
473
474QString QQuickTextEdit::preeditText()
const
476 Q_D(
const QQuickTextEdit);
477 return d->control->preeditText();
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534QQuickTextEdit::TextFormat QQuickTextEdit::textFormat()
const
536 Q_D(
const QQuickTextEdit);
540void QQuickTextEdit::setTextFormat(TextFormat format)
543 if (format == d->format)
546 auto mightBeRichText = [
this]() {
547 return Qt::mightBeRichText(text());
550 auto findSourceFormat = [d, mightBeRichText](Qt::TextFormat detectedFormat) {
551 if (d->format == PlainText)
553 if (d->richText)
return RichText;
554 if (d->markdownText)
return MarkdownText;
555 if (detectedFormat == Qt::AutoText && mightBeRichText())
560 auto findDestinationFormat = [format, mightBeRichText](Qt::TextFormat detectedFormat, TextFormat sourceFormat) {
561 if (format == AutoText) {
562 if (detectedFormat == Qt::MarkdownText || (detectedFormat == Qt::AutoText && sourceFormat == MarkdownText))
564 if (detectedFormat == Qt::RichText || (detectedFormat == Qt::AutoText && (sourceFormat == RichText || mightBeRichText())))
571 bool textCachedChanged =
false;
572 bool converted =
false;
574 if (isComponentComplete()) {
575 Qt::TextFormat detectedFormat = Qt::AutoText;
576 if (d->quickDocument) {
579 detectedFormat = QQuickTextDocumentPrivate::get(d->quickDocument)->detectedFormat;
582 const TextFormat sourceFormat = findSourceFormat(detectedFormat);
583 const TextFormat destinationFormat = findDestinationFormat(detectedFormat, sourceFormat);
585 d->richText = destinationFormat == RichText;
586 d->markdownText = destinationFormat == MarkdownText;
589 if (format != PlainText && (sourceFormat != destinationFormat)) {
590 d->textCached =
false;
591 textCachedChanged =
true;
594 switch (destinationFormat) {
596#if QT_CONFIG(texthtmlparser)
597 if (sourceFormat == RichText) {
601 d->control->setPlainText(d->textCached ? d->text : d->control->toHtml());
605#if QT_CONFIG(textmarkdownwriter) && QT_CONFIG(textmarkdownreader)
606 if (sourceFormat == MarkdownText) {
610 d->control->setPlainText(d->textCached ? d->text : d->control->toMarkdown());
616#if QT_CONFIG(texthtmlparser)
617 switch (sourceFormat) {
620 d->control->setHtml(d->control->toHtml());
626 d->control->setHtml(d->textCached ? d->text : d->control->toPlainText());
636#if QT_CONFIG(textmarkdownwriter) && QT_CONFIG(textmarkdownreader)
637 switch (sourceFormat) {
640 d->control->setMarkdownText(d->control->toMarkdown());
646 d->control->setMarkdownText(d->textCached ? d->text : d->control->toPlainText());
662 d->richText = format == RichText || (format == AutoText && (d->richText || mightBeRichText()));
663 d->markdownText = format == MarkdownText;
666 qCDebug(lcTextEdit) << d->format <<
"->" << format
667 <<
"rich?" << d->richText <<
"md?" << d->markdownText
668 <<
"converted?" << converted <<
"cache invalidated?" << textCachedChanged;
671 d->control->setAcceptRichText(d->format != PlainText);
672 emit textFormatChanged(d->format);
673 if (textCachedChanged)
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702QQuickTextEdit::RenderType QQuickTextEdit::renderType()
const
704 Q_D(
const QQuickTextEdit);
705 return d->renderType;
708void QQuickTextEdit::setRenderType(QQuickTextEdit::RenderType renderType)
711 if (d->renderType == renderType)
714 d->renderType = renderType;
715 emit renderTypeChanged();
716 d->updateDefaultTextOption();
718 if (isComponentComplete())
722QFont QQuickTextEdit::font()
const
724 Q_D(
const QQuickTextEdit);
725 return d->sourceFont;
728void QQuickTextEdit::setFont(
const QFont &font)
731 if (d->sourceFont == font)
734 d->sourceFont = font;
735 QFont oldFont = d->font;
737 if (d->font.pointSizeF() != -1) {
739 qreal size = qRound(d->font.pointSizeF()*2.0);
740 d->font.setPointSizeF(size/2.0);
743 if (oldFont != d->font) {
744 d->document->setDefaultFont(d->font);
746 d->cursorItem->setHeight(QFontMetrics(d->font).height());
747 moveCursorDelegate();
750 updateWholeDocument();
752 updateInputMethod(Qt::ImCursorRectangle | Qt::ImAnchorRectangle | Qt::ImFont);
755 emit fontChanged(d->sourceFont);
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773QColor QQuickTextEdit::color()
const
775 Q_D(
const QQuickTextEdit);
779void QQuickTextEdit::setColor(
const QColor &color)
782 if (d->color == color)
786 updateWholeDocument();
787 emit colorChanged(d->color);
791
792
793
794
795QColor QQuickTextEdit::selectionColor()
const
797 Q_D(
const QQuickTextEdit);
798 return d->selectionColor;
801void QQuickTextEdit::setSelectionColor(
const QColor &color)
804 if (d->selectionColor == color)
807 d->selectionColor = color;
808 updateWholeDocument();
809 emit selectionColorChanged(d->selectionColor);
813
814
815
816
817QColor QQuickTextEdit::selectedTextColor()
const
819 Q_D(
const QQuickTextEdit);
820 return d->selectedTextColor;
823void QQuickTextEdit::setSelectedTextColor(
const QColor &color)
826 if (d->selectedTextColor == color)
829 d->selectedTextColor = color;
830 updateWholeDocument();
831 emit selectedTextColorChanged(d->selectedTextColor);
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866QQuickTextEdit::HAlignment QQuickTextEdit::hAlign()
const
868 Q_D(
const QQuickTextEdit);
872void QQuickTextEdit::setHAlign(HAlignment align)
876 if (d->setHAlign(align,
true) && isComponentComplete()) {
877 d->updateDefaultTextOption();
879 updateWholeDocument();
883void QQuickTextEdit::resetHAlign()
886 d->hAlignImplicit =
true;
887 if (d->determineHorizontalAlignment() && isComponentComplete()) {
888 d->updateDefaultTextOption();
893QQuickTextEdit::HAlignment QQuickTextEdit::effectiveHAlign()
const
895 Q_D(
const QQuickTextEdit);
896 QQuickTextEdit::HAlignment effectiveAlignment = d->hAlign;
897 if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
899 case QQuickTextEdit::AlignLeft:
900 effectiveAlignment = QQuickTextEdit::AlignRight;
902 case QQuickTextEdit::AlignRight:
903 effectiveAlignment = QQuickTextEdit::AlignLeft;
909 return effectiveAlignment;
912bool QQuickTextEditPrivate::setHAlign(QQuickTextEdit::HAlignment align,
bool forceAlign)
915 if (hAlign == align && !forceAlign)
918 const bool wasImplicit = hAlignImplicit;
919 const auto oldEffectiveHAlign = q->effectiveHAlign();
921 hAlignImplicit = !forceAlign;
922 if (hAlign != align) {
924 emit q->horizontalAlignmentChanged(align);
927 if (q->effectiveHAlign() != oldEffectiveHAlign) {
928 emit q->effectiveHorizontalAlignmentChanged();
932 if (forceAlign && wasImplicit) {
935 emit q->effectiveHorizontalAlignmentChanged();
940Qt::LayoutDirection QQuickTextEditPrivate::textDirection(
const QString &text)
const
942 const QChar *character = text.constData();
943 while (!character->isNull()) {
944 switch (character->direction()) {
946 return Qt::LeftToRight;
950 return Qt::RightToLeft;
956 return Qt::LayoutDirectionAuto;
959bool QQuickTextEditPrivate::determineHorizontalAlignment()
962 if (!hAlignImplicit || !q->isComponentComplete())
965 Qt::LayoutDirection direction = contentDirection;
967 if (direction == Qt::LayoutDirectionAuto) {
968 QTextBlock block = control->textCursor().block();
971 direction = textDirection(block.layout()->preeditAreaText());
973 if (direction == Qt::LayoutDirectionAuto)
974 direction = qGuiApp->inputMethod()->inputDirection();
977 const auto implicitHAlign = direction == Qt::RightToLeft ?
978 QQuickTextEdit::AlignRight : QQuickTextEdit::AlignLeft;
979 return setHAlign(implicitHAlign);
982void QQuickTextEditPrivate::mirrorChange()
985 if (q->isComponentComplete()) {
986 if (!hAlignImplicit && (hAlign == QQuickTextEdit::AlignRight || hAlign == QQuickTextEdit::AlignLeft)) {
987 updateDefaultTextOption();
989 q->updateWholeDocument();
990 emit q->effectiveHorizontalAlignmentChanged();
995bool QQuickTextEditPrivate::transformChanged(QQuickItem *transformedItem)
998 qCDebug(lcVP) << q <<
"sees that" << transformedItem <<
"moved in VP" << q->clipRect();
1003 if (flags & QQuickItem::ItemObservesViewport) {
1004 if (QQuickItem *viewport = q->viewportItem()) {
1005 QRectF vp = q->mapRectFromItem(viewport, viewport->clipRect());
1006 if (!(vp.top() > renderedRegion.top() && vp.bottom() < renderedRegion.bottom())) {
1007 qCDebug(lcVP) <<
"viewport" << vp <<
"now goes beyond rendered region" << renderedRegion <<
"; updating";
1008 q->updateWholeDocument();
1010 const bool textCursorVisible = cursorVisible && q->cursorRectangle().intersects(vp);
1012 cursorItem->setVisible(textCursorVisible);
1014 control->setCursorVisible(textCursorVisible);
1017 return QQuickImplicitSizeItemPrivate::transformChanged(transformedItem);
1021Qt::InputMethodHints QQuickTextEditPrivate::effectiveInputMethodHints()
const
1023 return inputMethodHints | Qt::ImhMultiLine;
1027#if QT_CONFIG(accessibility)
1028void QQuickTextEditPrivate::accessibilityActiveChanged(
bool active)
1033 Q_Q(QQuickTextEdit);
1034 if (QQuickAccessibleAttached *accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(
1035 qmlAttachedPropertiesObject<QQuickAccessibleAttached>(q,
true))) {
1036 accessibleAttached->setRole(effectiveAccessibleRole());
1037 accessibleAttached->set_readOnly(q->isReadOnly());
1041QAccessible::Role QQuickTextEditPrivate::accessibleRole()
const
1043 return QAccessible::EditableText;
1047void QQuickTextEditPrivate::setTopPadding(qreal value,
bool reset)
1049 Q_Q(QQuickTextEdit);
1050 qreal oldPadding = q->topPadding();
1051 if (!reset || extra.isAllocated()) {
1052 extra.value().topPadding = value;
1053 extra.value().explicitTopPadding = !reset;
1055 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
1057 q->updateWholeDocument();
1058 emit q->topPaddingChanged();
1062void QQuickTextEditPrivate::setLeftPadding(qreal value,
bool reset)
1064 Q_Q(QQuickTextEdit);
1065 qreal oldPadding = q->leftPadding();
1066 if (!reset || extra.isAllocated()) {
1067 extra.value().leftPadding = value;
1068 extra.value().explicitLeftPadding = !reset;
1070 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
1072 q->updateWholeDocument();
1073 emit q->leftPaddingChanged();
1077void QQuickTextEditPrivate::setRightPadding(qreal value,
bool reset)
1079 Q_Q(QQuickTextEdit);
1080 qreal oldPadding = q->rightPadding();
1081 if (!reset || extra.isAllocated()) {
1082 extra.value().rightPadding = value;
1083 extra.value().explicitRightPadding = !reset;
1085 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
1087 q->updateWholeDocument();
1088 emit q->rightPaddingChanged();
1092void QQuickTextEditPrivate::setBottomPadding(qreal value,
bool reset)
1094 Q_Q(QQuickTextEdit);
1095 qreal oldPadding = q->bottomPadding();
1096 if (!reset || extra.isAllocated()) {
1097 extra.value().bottomPadding = value;
1098 extra.value().explicitBottomPadding = !reset;
1100 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
1102 q->updateWholeDocument();
1103 emit q->bottomPaddingChanged();
1107bool QQuickTextEditPrivate::isImplicitResizeEnabled()
const
1109 return !extra.isAllocated() || extra->implicitResize;
1112void QQuickTextEditPrivate::setImplicitResizeEnabled(
bool enabled)
1115 extra.value().implicitResize =
false;
1116 else if (extra.isAllocated())
1117 extra->implicitResize =
true;
1120QQuickTextEdit::VAlignment QQuickTextEdit::vAlign()
const
1122 Q_D(
const QQuickTextEdit);
1126void QQuickTextEdit::setVAlign(QQuickTextEdit::VAlignment alignment)
1128 Q_D(QQuickTextEdit);
1129 if (alignment == d->vAlign)
1131 d->vAlign = alignment;
1132 d->updateDefaultTextOption();
1134 moveCursorDelegate();
1135 emit verticalAlignmentChanged(d->vAlign);
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158QQuickTextEdit::WrapMode QQuickTextEdit::wrapMode()
const
1160 Q_D(
const QQuickTextEdit);
1164void QQuickTextEdit::setWrapMode(WrapMode mode)
1166 Q_D(QQuickTextEdit);
1167 if (mode == d->wrapMode)
1170 d->updateDefaultTextOption();
1172 emit wrapModeChanged();
1176
1177
1178
1179
1180int QQuickTextEdit::lineCount()
const
1182 Q_D(
const QQuickTextEdit);
1183 return d->lineCount;
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1198int QQuickTextEdit::length()
const
1200 Q_D(
const QQuickTextEdit);
1202 return qMax(0, d->document->characterCount() - 1);
1206
1207
1208
1209
1210
1211qreal QQuickTextEdit::contentWidth()
const
1213 Q_D(
const QQuickTextEdit);
1214 return d->contentSize.width();
1218
1219
1220
1221
1222
1223qreal QQuickTextEdit::contentHeight()
const
1225 Q_D(
const QQuickTextEdit);
1226 return d->contentSize.height();
1230
1231
1232
1233
1234
1235
1236
1238QUrl QQuickTextEdit::baseUrl()
const
1240 Q_D(
const QQuickTextEdit);
1241 if (d->baseUrl.isEmpty()) {
1242 if (QQmlContext *context = qmlContext(
this))
1243 const_cast<QQuickTextEditPrivate *>(d)->baseUrl = context->baseUrl();
1248void QQuickTextEdit::setBaseUrl(
const QUrl &url)
1250 Q_D(QQuickTextEdit);
1251 if (baseUrl() != url) {
1254 d->document->setBaseUrl(url);
1255 emit baseUrlChanged();
1259void QQuickTextEdit::resetBaseUrl()
1261 if (QQmlContext *context = qmlContext(
this))
1262 setBaseUrl(context->baseUrl());
1268
1269
1270
1271
1272
1273
1274QRectF QQuickTextEdit::positionToRectangle(
int pos)
const
1276 Q_D(
const QQuickTextEdit);
1277 QTextCursor c(d->document);
1279 return d->control->cursorRect(c).translated(d->xoff, d->yoff);
1284
1285
1286
1287
1288
1289
1290
1291int QQuickTextEdit::positionAt(qreal x, qreal y)
const
1293 Q_D(
const QQuickTextEdit);
1297 int r = d->document->documentLayout()->hitTest(QPointF(x, y), Qt::FuzzyHit);
1299 QTextCursor cursor = d->control->textCursor();
1300 if (r > cursor.position()) {
1304 QTextLayout *layout = cursor.block().layout();
1305 const int preeditLength = layout
1306 ? layout->preeditAreaText().size()
1308 if (preeditLength > 0
1309 && d->document->documentLayout()->blockBoundingRect(cursor.block()).contains(x, y)) {
1310 r = r > cursor.position() + preeditLength
1312 : cursor.position();
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329QQuickTextSelection *QQuickTextEdit::cursorSelection()
const
1331 Q_D(
const QQuickTextEdit);
1332 if (!d->cursorSelection)
1333 d->cursorSelection =
new QQuickTextSelection(
const_cast<QQuickTextEdit *>(
this));
1334 return d->cursorSelection;
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373void QQuickTextEdit::moveCursorSelection(
int pos)
1376 Q_D(QQuickTextEdit);
1377 QTextCursor cursor = d->control->textCursor();
1378 if (cursor.position() == pos)
1380 cursor.setPosition(pos, QTextCursor::KeepAnchor);
1381 d->control->setTextCursor(cursor);
1384void QQuickTextEdit::moveCursorSelection(
int pos, SelectionMode mode)
1386 Q_D(QQuickTextEdit);
1387 QTextCursor cursor = d->control->textCursor();
1388 if (cursor.position() == pos)
1390 if (mode == SelectCharacters) {
1391 cursor.setPosition(pos, QTextCursor::KeepAnchor);
1392 }
else if (cursor.anchor() < pos || (cursor.anchor() == pos && cursor.position() < pos)) {
1393 if (cursor.anchor() > cursor.position()) {
1394 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1395 cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
1396 if (cursor.position() == cursor.anchor())
1397 cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::MoveAnchor);
1399 cursor.setPosition(cursor.position(), QTextCursor::MoveAnchor);
1401 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1402 cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor);
1405 cursor.setPosition(pos, QTextCursor::KeepAnchor);
1406 cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
1407 if (cursor.position() != pos)
1408 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
1409 }
else if (cursor.anchor() > pos || (cursor.anchor() == pos && cursor.position() > pos)) {
1410 if (cursor.anchor() < cursor.position()) {
1411 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1412 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::MoveAnchor);
1414 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1415 cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
1416 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
1417 if (cursor.position() != cursor.anchor()) {
1418 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1419 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::MoveAnchor);
1423 cursor.setPosition(pos, QTextCursor::KeepAnchor);
1424 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
1425 if (cursor.position() != pos) {
1426 cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
1427 cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
1430 d->control->setTextCursor(cursor);
1434
1435
1436
1437
1438
1439
1440bool QQuickTextEdit::isCursorVisible()
const
1442 Q_D(
const QQuickTextEdit);
1443 return d->cursorVisible;
1446void QQuickTextEdit::setCursorVisible(
bool on)
1448 Q_D(QQuickTextEdit);
1449 if (d->cursorVisible == on)
1451 d->cursorVisible = on;
1452 if (on && isComponentComplete())
1453 QQuickTextUtil::createCursor(d);
1454 if (!on && !d->persistentSelection)
1455 d->control->setCursorIsFocusIndicator(
true);
1456 d->control->setCursorVisible(on);
1457 emit cursorVisibleChanged(d->cursorVisible);
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472int QQuickTextEdit::cursorPosition()
const
1474 Q_D(
const QQuickTextEdit);
1475 return d->control->textCursor().position();
1478void QQuickTextEdit::setCursorPosition(
int pos)
1480 Q_D(QQuickTextEdit);
1481 if (pos < 0 || pos >= d->document->characterCount())
1483 QTextCursor cursor = d->control->textCursor();
1484 if (cursor.position() == pos && cursor.anchor() == pos)
1486 cursor.setPosition(pos);
1487 d->control->setTextCursor(cursor);
1488 d->control->updateCursorRectangle(
true);
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504QQmlComponent* QQuickTextEdit::cursorDelegate()
const
1506 Q_D(
const QQuickTextEdit);
1507 return d->cursorComponent;
1510void QQuickTextEdit::setCursorDelegate(QQmlComponent* c)
1512 Q_D(QQuickTextEdit);
1513 QQuickTextUtil::setCursorDelegate(d, c);
1516void QQuickTextEdit::createCursor()
1518 Q_D(QQuickTextEdit);
1519 d->cursorPending =
true;
1520 QQuickTextUtil::createCursor(d);
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533int QQuickTextEdit::selectionStart()
const
1535 Q_D(
const QQuickTextEdit);
1536 return d->control->textCursor().selectionStart();
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549int QQuickTextEdit::selectionEnd()
const
1551 Q_D(
const QQuickTextEdit);
1552 return d->control->textCursor().selectionEnd();
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569QString QQuickTextEdit::selectedText()
const
1571 Q_D(
const QQuickTextEdit);
1572#if QT_CONFIG(texthtmlparser)
1573 return d->richText || d->markdownText
1574 ? d->control->textCursor().selectedText()
1575 : d->control->textCursor().selection().toPlainText();
1577 return d->control->textCursor().selection().toPlainText();
1582
1583
1584
1585
1586
1587bool QQuickTextEdit::focusOnPress()
const
1589 Q_D(
const QQuickTextEdit);
1590 return d->focusOnPress;
1593void QQuickTextEdit::setFocusOnPress(
bool on)
1595 Q_D(QQuickTextEdit);
1596 if (d->focusOnPress == on)
1598 d->focusOnPress = on;
1599 emit activeFocusOnPressChanged(d->focusOnPress);
1603
1604
1605
1606
1607
1608bool QQuickTextEdit::persistentSelection()
const
1610 Q_D(
const QQuickTextEdit);
1611 return d->persistentSelection;
1614void QQuickTextEdit::setPersistentSelection(
bool on)
1616 Q_D(QQuickTextEdit);
1617 if (d->persistentSelection == on)
1619 d->persistentSelection = on;
1620 emit persistentSelectionChanged(d->persistentSelection);
1624
1625
1626
1627
1628qreal QQuickTextEdit::textMargin()
const
1630 Q_D(
const QQuickTextEdit);
1631 return d->textMargin;
1634void QQuickTextEdit::setTextMargin(qreal margin)
1636 Q_D(QQuickTextEdit);
1637 if (d->textMargin == margin)
1639 d->textMargin = margin;
1640 d->document->setDocumentMargin(d->textMargin);
1641 emit textMarginChanged(d->textMargin);
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1681Qt::InputMethodHints QQuickTextEdit::inputMethodHints()
const
1686 Q_D(
const QQuickTextEdit);
1687 return d->inputMethodHints;
1691void QQuickTextEdit::setInputMethodHints(Qt::InputMethodHints hints)
1696 Q_D(QQuickTextEdit);
1698 if (hints == d->inputMethodHints)
1701 d->inputMethodHints = hints;
1702 updateInputMethod(Qt::ImHints);
1703 emit inputMethodHintsChanged();
1707void QQuickTextEdit::geometryChange(
const QRectF &newGeometry,
const QRectF &oldGeometry)
1709 Q_D(QQuickTextEdit);
1710 if (!d->inLayout && ((newGeometry.width() != oldGeometry.width())
1711 || (newGeometry.height() != oldGeometry.height()))) {
1713 updateWholeDocument();
1714 if (widthValid() || heightValid())
1715 moveCursorDelegate();
1717 QQuickImplicitSizeItem::geometryChange(newGeometry, oldGeometry);
1720void QQuickTextEdit::itemChange(ItemChange change,
const ItemChangeData &value)
1722 Q_D(QQuickTextEdit);
1725 case ItemDevicePixelRatioHasChanged:
1726 if (d->containsUnscalableGlyphs) {
1731 updateWholeDocument();
1738 QQuickImplicitSizeItem::itemChange(change, value);
1742
1743
1744
1745void QQuickTextEdit::componentComplete()
1747 Q_D(QQuickTextEdit);
1748 QQuickImplicitSizeItem::componentComplete();
1750 const QUrl url = baseUrl();
1751 const QQmlContext *context = qmlContext(
this);
1752 d->document->setBaseUrl(context ? context->resolvedUrl(url) : url);
1753 if (!d->text.isEmpty()) {
1754#if QT_CONFIG(texthtmlparser)
1756 d->control->setHtml(d->text);
1759#if QT_CONFIG(textmarkdownreader)
1760 if (d->markdownText)
1761 d->control->setMarkdownText(d->text);
1764 d->control->setPlainText(d->text);
1768 d->determineHorizontalAlignment();
1769 d->updateDefaultTextOption();
1773 if (d->cursorComponent && isCursorVisible())
1774 QQuickTextUtil::createCursor(d);
1777#if QT_CONFIG(accessibility)
1778 if (QAccessible::isActive())
1779 d->accessibilityActiveChanged(
true);
1783int QQuickTextEdit::resourcesLoading()
const
1785 Q_D(
const QQuickTextEdit);
1786 return d->pixmapsInProgress.size();
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803bool QQuickTextEdit::selectByKeyboard()
const
1805 Q_D(
const QQuickTextEdit);
1806 if (d->selectByKeyboardSet)
1807 return d->selectByKeyboard;
1808 return !isReadOnly();
1811void QQuickTextEdit::setSelectByKeyboard(
bool on)
1813 Q_D(QQuickTextEdit);
1814 bool was = selectByKeyboard();
1815 if (!d->selectByKeyboardSet || was != on) {
1816 d->selectByKeyboardSet =
true;
1817 d->selectByKeyboard = on;
1819 d->control->setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByKeyboard);
1821 d->control->setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByKeyboard);
1822 emit selectByKeyboardChanged(on);
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849bool QQuickTextEdit::selectByMouse()
const
1851 Q_D(
const QQuickTextEdit);
1852 return d->selectByMouse;
1855void QQuickTextEdit::setSelectByMouse(
bool on)
1857 Q_D(QQuickTextEdit);
1858 if (d->selectByMouse == on)
1861 d->selectByMouse = on;
1862 setKeepMouseGrab(on);
1864 d->control->setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByMouse);
1866 d->control->setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByMouse);
1868#if QT_CONFIG(cursor)
1869 d->updateMouseCursorShape();
1871 emit selectByMouseChanged(on);
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884QQuickTextEdit::SelectionMode QQuickTextEdit::mouseSelectionMode()
const
1886 Q_D(
const QQuickTextEdit);
1887 return d->mouseSelectionMode;
1890void QQuickTextEdit::setMouseSelectionMode(SelectionMode mode)
1892 Q_D(QQuickTextEdit);
1893 if (d->mouseSelectionMode != mode) {
1894 d->mouseSelectionMode = mode;
1895 d->control->setWordSelectionEnabled(mode == SelectWords);
1896 emit mouseSelectionModeChanged(mode);
1901
1902
1903
1904
1905
1906
1907
1908void QQuickTextEdit::setReadOnly(
bool r)
1910 Q_D(QQuickTextEdit);
1911 if (r == isReadOnly())
1915 setFlag(QQuickItem::ItemAcceptsInputMethod, !r);
1917 Qt::TextInteractionFlags flags = Qt::LinksAccessibleByMouse;
1918 if (d->selectByMouse)
1919 flags = flags | Qt::TextSelectableByMouse;
1920 if (d->selectByKeyboardSet && d->selectByKeyboard)
1921 flags = flags | Qt::TextSelectableByKeyboard;
1922 else if (!d->selectByKeyboardSet && !r)
1923 flags = flags | Qt::TextSelectableByKeyboard;
1925 flags = flags | Qt::TextEditable;
1926 d->control->setTextInteractionFlags(flags);
1927 d->control->moveCursor(QTextCursor::End);
1930 updateInputMethod(Qt::ImEnabled);
1932#if QT_CONFIG(cursor)
1933 d->updateMouseCursorShape();
1935 q_canPasteChanged();
1936 emit readOnlyChanged(r);
1937 if (!d->selectByKeyboardSet)
1938 emit selectByKeyboardChanged(!r);
1940 setCursorVisible(
false);
1941 }
else if (hasActiveFocus()) {
1942 setCursorVisible(
true);
1945#if QT_CONFIG(accessibility)
1946 if (QAccessible::isActive()) {
1947 if (QQuickAccessibleAttached *accessibleAttached = QQuickAccessibleAttached::attachedProperties(
this))
1948 accessibleAttached->set_readOnly(r);
1953bool QQuickTextEdit::isReadOnly()
const
1955 Q_D(
const QQuickTextEdit);
1956 return !(d->control->textInteractionFlags() & Qt::TextEditable);
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969QRectF QQuickTextEdit::cursorRectangle()
const
1971 Q_D(
const QQuickTextEdit);
1972 return d->control->cursorRect().translated(d->xoff, d->yoff);
1975bool QQuickTextEdit::event(QEvent *event)
1977 Q_D(QQuickTextEdit);
1978 bool state = QQuickImplicitSizeItem::event(event);
1979 if (event->type() == QEvent::ShortcutOverride && !event->isAccepted()) {
1980 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000bool QQuickTextEdit::overwriteMode()
const
2002 Q_D(
const QQuickTextEdit);
2003 return d->control->overwriteMode();
2006void QQuickTextEdit::setOverwriteMode(
bool overwrite)
2008 Q_D(QQuickTextEdit);
2009 d->control->setOverwriteMode(overwrite);
2013
2014
2015
2016void QQuickTextEdit::keyPressEvent(QKeyEvent *event)
2018 Q_D(QQuickTextEdit);
2019 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2020 if (!event->isAccepted())
2021 QQuickImplicitSizeItem::keyPressEvent(event);
2025
2026
2027
2028void QQuickTextEdit::keyReleaseEvent(QKeyEvent *event)
2030 Q_D(QQuickTextEdit);
2031 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2032 if (!event->isAccepted())
2033 QQuickImplicitSizeItem::keyReleaseEvent(event);
2037
2038
2039
2040
2041void QQuickTextEdit::deselect()
2043 Q_D(QQuickTextEdit);
2044 QTextCursor c = d->control->textCursor();
2046 d->control->setTextCursor(c);
2050
2051
2052
2053
2054void QQuickTextEdit::selectAll()
2056 Q_D(QQuickTextEdit);
2057 d->control->selectAll();
2061
2062
2063
2064
2065void QQuickTextEdit::selectWord()
2067 Q_D(QQuickTextEdit);
2068 QTextCursor c = d->control->textCursor();
2069 c.select(QTextCursor::WordUnderCursor);
2070 d->control->setTextCursor(c);
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086void QQuickTextEdit::select(
int start,
int end)
2088 Q_D(QQuickTextEdit);
2089 if (start < 0 || end < 0 || start >= d->document->characterCount() || end >= d->document->characterCount())
2091 QTextCursor cursor = d->control->textCursor();
2092 cursor.beginEditBlock();
2093 cursor.setPosition(start, QTextCursor::MoveAnchor);
2094 cursor.setPosition(end, QTextCursor::KeepAnchor);
2095 cursor.endEditBlock();
2096 d->control->setTextCursor(cursor);
2101 updateInputMethod();
2106
2107
2108
2109
2110
2111bool QQuickTextEdit::isRightToLeft(
int start,
int end)
2114 qmlWarning(
this) <<
"isRightToLeft(start, end) called with the end property being smaller than the start.";
2117 return getText(start, end).isRightToLeft();
2121#if QT_CONFIG(clipboard)
2123
2124
2125
2126
2127void QQuickTextEdit::cut()
2129 Q_D(QQuickTextEdit);
2134
2135
2136
2137
2138void QQuickTextEdit::copy()
2140 Q_D(QQuickTextEdit);
2145
2146
2147
2148
2149void QQuickTextEdit::paste()
2151 Q_D(QQuickTextEdit);
2152 d->control->paste();
2158
2159
2160
2161
2162
2163
2165void QQuickTextEdit::undo()
2167 Q_D(QQuickTextEdit);
2172
2173
2174
2175
2177void QQuickTextEdit::redo()
2179 Q_D(QQuickTextEdit);
2184
2185
2186
2187void QQuickTextEdit::mousePressEvent(QMouseEvent *event)
2189 Q_D(QQuickTextEdit);
2190 const bool isMouse = QQuickDeliveryAgentPrivate::isEventFromMouseOrTouchpad(event);
2191 setKeepMouseGrab(d->selectByMouse && isMouse);
2192 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2193 if (d->focusOnPress){
2194 bool hadActiveFocus = hasActiveFocus();
2195 forceActiveFocus(Qt::MouseFocusReason);
2198 if (hasActiveFocus() && hadActiveFocus && !isReadOnly())
2199 qGuiApp->inputMethod()->show();
2201 Q_UNUSED(hadActiveFocus);
2204 if (!event->isAccepted())
2205 QQuickImplicitSizeItem::mousePressEvent(event);
2209
2210
2211
2212void QQuickTextEdit::mouseReleaseEvent(QMouseEvent *event)
2214 Q_D(QQuickTextEdit);
2215 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2217 if (!event->isAccepted())
2218 QQuickImplicitSizeItem::mouseReleaseEvent(event);
2222
2223
2224
2225void QQuickTextEdit::mouseDoubleClickEvent(QMouseEvent *event)
2227 Q_D(QQuickTextEdit);
2228 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2229 if (!event->isAccepted())
2230 QQuickImplicitSizeItem::mouseDoubleClickEvent(event);
2234
2235
2236
2237void QQuickTextEdit::mouseMoveEvent(QMouseEvent *event)
2239 Q_D(QQuickTextEdit);
2240 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2241 if (!event->isAccepted())
2242 QQuickImplicitSizeItem::mouseMoveEvent(event);
2247
2248
2249
2250void QQuickTextEdit::inputMethodEvent(QInputMethodEvent *event)
2252 Q_D(QQuickTextEdit);
2253 const bool wasComposing = isInputMethodComposing();
2254 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2255 setCursorVisible(d->control->cursorVisible());
2256 if (wasComposing != isInputMethodComposing())
2257 emit inputMethodComposingChanged();
2261
2262
2263
2264QVariant QQuickTextEdit::inputMethodQuery(Qt::InputMethodQuery property, QVariant argument)
const
2266 Q_D(
const QQuickTextEdit);
2271 v = (
bool)(flags() & ItemAcceptsInputMethod);
2274 v = (
int)d->effectiveInputMethodHints();
2276 case Qt::ImInputItemClipRectangle:
2277 v = QQuickItem::inputMethodQuery(property);
2279 case Qt::ImReadOnly:
2283 if (property == Qt::ImCursorPosition && !argument.isNull())
2284 argument = QVariant(argument.toPointF() - QPointF(d->xoff, d->yoff));
2285 v = d->control->inputMethodQuery(property, argument);
2286 if (property == Qt::ImCursorRectangle || property == Qt::ImAnchorRectangle)
2287 v = QVariant(v.toRectF().translated(d->xoff, d->yoff));
2294
2295
2296
2297QVariant QQuickTextEdit::inputMethodQuery(Qt::InputMethodQuery property)
const
2299 return inputMethodQuery(property, QVariant());
2303void QQuickTextEdit::triggerPreprocess()
2305 Q_D(QQuickTextEdit);
2306 if (d->updateType == QQuickTextEditPrivate::UpdateNone)
2307 d->updateType = QQuickTextEditPrivate::UpdateOnlyPreprocess;
2313
2314
2315
2316
2317QVariant QQuickTextEdit::loadResource(
int type,
const QUrl &source)
2319 Q_D(QQuickTextEdit);
2320 const QUrl url = d->document->baseUrl().resolved(source);
2321 if (url.isLocalFile()) {
2323 QFileInfo fi(QQmlFile::urlToLocalFileOrQrc(url));
2325 qmlWarning(
this) <<
"Cannot open: " << url.toString();
2331 if (!url.scheme().compare(
"qrc"_L1, Qt::CaseInsensitive)) {
2333 QFile f(QQmlFile::urlToLocalFileOrQrc(url));
2334 if (f.open(QFile::ReadOnly)) {
2335 QByteArray buf = f.readAll();
2338 image.loadFromData(buf);
2339 if (!image.isNull())
2343 qmlWarning(
this) <<
"Cannot read resource: " << f.fileName();
2348 auto existingJobIter = std::find_if(
2349 d->pixmapsInProgress.cbegin(), d->pixmapsInProgress.cend(),
2350 [&url](
const auto *job) {
return job->url() == url; } );
2351 if (existingJobIter != d->pixmapsInProgress.cend()) {
2352 const QQuickPixmap *job = *existingJobIter;
2353 if (job->isError()) {
2354 qmlWarning(
this) << job->error();
2355 d->pixmapsInProgress.erase(existingJobIter);
2359 qCDebug(lcTextEdit) <<
"already downloading" << url;
2361 return job->isReady() ? job->image() : QVariant();
2366 qCDebug(lcTextEdit) <<
"loading" << source <<
"resolved" << url
2367 <<
"type" <<
static_cast<QTextDocument::ResourceType>(type);
2368 QQmlContext *context = qmlContext(
this);
2371 QQuickPixmap *p =
new QQuickPixmap(context->engine(), url, QQuickPixmap::Options{});
2372 p->connectFinished(
this, SLOT(resourceRequestFinished()));
2373 d->pixmapsInProgress.append(p);
2375 return p->isReady() ? p->image() : QVariant();
2379
2380
2381void QQuickTextEdit::resourceRequestFinished()
2383 Q_D(QQuickTextEdit);
2384 for (
auto it = d->pixmapsInProgress.cbegin(); it != d->pixmapsInProgress.cend(); ++it) {
2386 if (job->isError()) {
2388 qCDebug(lcTextEdit) <<
"failed to load (error)" << job->url();
2389 d->document->resource(QTextDocument::ImageResource, job->url());
2393 }
else if (job->isReady()) {
2395 auto res = d->document->resource(QTextDocument::ImageResource, job->url());
2397 qCDebug(lcTextEdit) << (res.isValid() ?
"done downloading" :
"failed to load") << job->url() << job->rect();
2398 d->pixmapsInProgress.erase(it);
2403 if (d->pixmapsInProgress.isEmpty()) {
2411using TextNodeIterator = QQuickTextEditPrivate::TextNodeIterator;
2415 return n1.startPos() < n2.startPos();
2420 QMatrix4x4 transformMatrix;
2421 transformMatrix.translate(topLeft.x(), topLeft.y());
2422 node->setMatrix(transformMatrix);
2426
2427
2428
2429
2430
2431void QQuickTextEdit::invalidateFontCaches()
2433 Q_D(QQuickTextEdit);
2434 if (d->document ==
nullptr)
2438 for (block = d->document->firstBlock(); block.isValid(); block = block.next()) {
2439 if (block.layout() !=
nullptr && block.layout()->engine() !=
nullptr)
2440 block.layout()->engine()->resetFontEngineCache();
2444QTextDocument *QQuickTextEdit::document()
const
2446 Q_D(
const QQuickTextEdit);
2450void QQuickTextEdit::setDocument(QTextDocument *doc)
2452 Q_D(QQuickTextEdit);
2454 std::unique_ptr<QTextDocument> cleanup(d->ownsDocument ? d->document :
nullptr);
2456 d->ownsDocument =
false;
2457 d->control->setDocument(doc);
2467 engine->setDevicePixelRatio(dpr);
2470QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData)
2472 Q_UNUSED(updatePaintNodeData);
2473 Q_D(QQuickTextEdit);
2475 if (d->updateType != QQuickTextEditPrivate::UpdatePaintNode
2476 && d->updateType != QQuickTextEditPrivate::UpdateAll
2477 && oldNode !=
nullptr) {
2479 d->updateType = QQuickTextEditPrivate::UpdateNone;
2483 d->containsUnscalableGlyphs =
false;
2484 if (!oldNode || d->updateType == QQuickTextEditPrivate::UpdateAll) {
2490 d->textNodeMap.clear();
2493 d->updateType = QQuickTextEditPrivate::UpdateNone;
2495 RootNode *rootNode =
static_cast<RootNode *>(oldNode);
2496 TextNodeIterator nodeIterator = d->textNodeMap.begin();
2497 std::optional<
int> firstPosAcrossAllNodes;
2498 if (nodeIterator != d->textNodeMap.end())
2499 firstPosAcrossAllNodes = nodeIterator->startPos();
2501 while (nodeIterator != d->textNodeMap.end() && !nodeIterator->dirty())
2504 const auto dpr = d->effectiveDevicePixelRatio();
2505 QQuickTextNodeEngine engine;
2506 engine.setDevicePixelRatio(dpr);
2507 QQuickTextNodeEngine frameDecorationsEngine;
2508 frameDecorationsEngine.setDevicePixelRatio(dpr);
2510 if (!oldNode || nodeIterator < d->textNodeMap.end() || d->textNodeMap.isEmpty()) {
2513 rootNode =
new RootNode;
2515 int firstDirtyPos = 0;
2516 if (nodeIterator != d->textNodeMap.end()) {
2517 firstDirtyPos = nodeIterator->startPos();
2520 QSGInternalTextNode *firstCleanNode =
nullptr;
2521 auto it = d->textNodeMap.constEnd();
2522 while (it != nodeIterator) {
2526 firstCleanNode = it->textNode();
2529 rootNode->removeChildNode(nodeIterator->textNode());
2530 delete nodeIterator->textNode();
2531 nodeIterator = d->textNodeMap.erase(nodeIterator);
2532 }
while (nodeIterator != d->textNodeMap.constEnd() && nodeIterator->textNode() != firstCleanNode);
2537 if (flags().testFlag(QQuickItem::ItemObservesViewport)) {
2538 viewport = clipRect();
2539 qCDebug(lcVP) <<
"text viewport" << viewport;
2543 rootNode->resetFrameDecorations(d->createTextNode());
2544 resetEngine(&frameDecorationsEngine, d->color, d->selectedTextColor, d->selectionColor, dpr);
2546 QSGInternalTextNode *node =
nullptr;
2548 int currentNodeSize = 0;
2549 int nodeStart = firstDirtyPos;
2550 QPointF basePosition(d->xoff, d->yoff);
2551 QMatrix4x4 basePositionMatrix;
2552 basePositionMatrix.translate(basePosition.x(), basePosition.y());
2553 rootNode->setMatrix(basePositionMatrix);
2556 const TextNode firstCleanNode = (nodeIterator != d->textNodeMap.end()) ? *nodeIterator
2559 QList<QTextFrame *> frames;
2560 frames.append(d->document->rootFrame());
2563 d->firstBlockInViewport = -1;
2564 d->firstBlockPastViewport = -1;
2565 int frameCount = -1;
2566 while (!frames.isEmpty()) {
2567 QTextFrame *textFrame = frames.takeFirst();
2571 qCDebug(lcVP) <<
"frame" << frameCount << textFrame
2572 <<
"from" << positionToRectangle(textFrame->firstPosition()).topLeft()
2573 <<
"to" << positionToRectangle(textFrame->lastPosition()).bottomRight();
2574 frames.append(textFrame->childFrames());
2575 frameDecorationsEngine.addFrameDecorations(d->document, textFrame);
2576 resetEngine(&engine, d->color, d->selectedTextColor, d->selectionColor, dpr);
2578 if (textFrame->firstPosition() > textFrame->lastPosition()
2579 && textFrame->frameFormat().position() != QTextFrameFormat::InFlow) {
2580 node = d->createTextNode();
2581 updateNodeTransform(node, d->document->documentLayout()->frameBoundingRect(textFrame).topLeft());
2582 const int pos = textFrame->firstPosition() - 1;
2583 auto *a =
static_cast<QtPrivate::ProtectedLayoutAccessor *>(d->document->documentLayout());
2584 QTextCharFormat format = a->formatAccessor(pos);
2585 QTextBlock block = textFrame->firstCursorPosition().block();
2586 nodeOffset = d->document->documentLayout()->blockBoundingRect(block).topLeft();
2588 if (!viewport.isNull() && block.layout()) {
2589 QRectF coveredRegion = block.layout()->boundingRect().adjusted(nodeOffset.x(), nodeOffset.y(), nodeOffset.x(), nodeOffset.y());
2590 inView = coveredRegion.bottom() >= viewport.top() && coveredRegion.top() <= viewport.bottom();
2591 qCDebug(lcVP) <<
"non-flow frame" << coveredRegion <<
"in viewport?" << inView;
2594 engine.setCurrentLine(block.layout()->lineForTextPosition(pos - block.position()));
2595 engine.addTextObject(block, QPointF(0, 0), format, QQuickTextNodeEngine::Unselected, d->document,
2596 pos, textFrame->frameFormat().position());
2601 QList<
int> frameBoundaries;
2602 frameBoundaries.reserve(frames.size());
2603 for (QTextFrame *frame : std::as_const(frames))
2604 frameBoundaries.append(frame->firstPosition());
2605 std::sort(frameBoundaries.begin(), frameBoundaries.end());
2607 QTextFrame::iterator it = textFrame->begin();
2608 while (!it.atEnd()) {
2609 QTextBlock block = it.currentBlock();
2610 if (block.position() < firstDirtyPos) {
2615 if (!engine.hasContents())
2616 nodeOffset = d->document->documentLayout()->blockBoundingRect(block).topLeft();
2619 if (!viewport.isNull()) {
2620 QRectF coveredRegion;
2621 if (block.layout()) {
2622 coveredRegion = block.layout()->boundingRect().adjusted(nodeOffset.x(), nodeOffset.y(), nodeOffset.x(), nodeOffset.y());
2623 inView = coveredRegion.bottom() > viewport.top();
2625 const bool potentiallyScrollingBackwards = firstPosAcrossAllNodes && *firstPosAcrossAllNodes == firstDirtyPos;
2626 if (d->firstBlockInViewport < 0 && inView && potentiallyScrollingBackwards) {
2628 if (coveredRegion.top() > viewport.top() + 1) {
2629 qCDebug(lcVP) <<
"checking backwards from block" << block.blockNumber() <<
"@" << nodeOffset.y() << coveredRegion;
2630 while (it != textFrame->begin() && it.currentBlock().layout() &&
2631 it.currentBlock().layout()->boundingRect().top() + nodeOffset.y() > viewport.top()) {
2632 nodeOffset = d->document->documentLayout()->blockBoundingRect(it.currentBlock()).topLeft();
2635 if (!it.currentBlock().layout())
2637 if (Q_LIKELY(it.currentBlock().layout())) {
2638 block = it.currentBlock();
2639 coveredRegion = block.layout()->boundingRect().adjusted(nodeOffset.x(), nodeOffset.y(), nodeOffset.x(), nodeOffset.y());
2640 firstDirtyPos = it.currentBlock().position();
2642 qCWarning(lcVP) <<
"failed to find a text block with layout during back-scrolling";
2645 qCDebug(lcVP) <<
"first block in viewport" << block.blockNumber() <<
"@" << nodeOffset.y() << coveredRegion;
2647 d->renderedRegion = coveredRegion;
2649 if (nodeOffset.y() > viewport.bottom()) {
2651 if (d->firstBlockInViewport >= 0 && d->firstBlockPastViewport < 0) {
2652 qCDebug(lcVP) <<
"first block past viewport" << viewport << block.blockNumber()
2653 <<
"@" << nodeOffset.y() <<
"total region rendered" << d->renderedRegion;
2654 d->firstBlockPastViewport = block.blockNumber();
2658 if (inView && !block.text().isEmpty() && coveredRegion.isValid()) {
2659 d->renderedRegion = d->renderedRegion.united(coveredRegion);
2662 if (!frames.isEmpty())
2663 viewport = viewport.united(d->renderedRegion);
2666 if (inView && d->firstBlockInViewport < 0)
2667 d->firstBlockInViewport = block.blockNumber();
2670 bool createdNodeInView =
false;
2672 if (!engine.hasContents()) {
2674 d->containsUnscalableGlyphs = d->containsUnscalableGlyphs
2675 || node->containsUnscalableGlyphs();
2676 if (!node->parent())
2677 d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart);
2679 node = d->createTextNode();
2680 createdNodeInView =
true;
2681 updateNodeTransform(node, nodeOffset);
2682 nodeStart = block.position();
2684 engine.addTextBlock(d->document, block, -nodeOffset, d->color, QColor(), selectionStart(), selectionEnd() - 1);
2685 currentNodeSize += block.length();
2688 if ((it.atEnd()) || block.next().position() >= firstCleanNode.startPos())
2690 QList<
int>::const_iterator lowerBound = std::lower_bound(frameBoundaries.constBegin(), frameBoundaries.constEnd(), block.next().position());
2691 if (node && (currentNodeSize > nodeBreakingSize || lowerBound == frameBoundaries.constEnd() || *lowerBound > nodeStart)) {
2692 currentNodeSize = 0;
2693 d->containsUnscalableGlyphs = d->containsUnscalableGlyphs
2694 || node->containsUnscalableGlyphs();
2695 if (!node->parent())
2696 d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart);
2697 if (!createdNodeInView)
2698 node = d->createTextNode();
2699 resetEngine(&engine, d->color, d->selectedTextColor, d->selectionColor, dpr);
2700 nodeStart = block.next().position();
2705 if (Q_LIKELY(node)) {
2706 d->containsUnscalableGlyphs = d->containsUnscalableGlyphs
2707 || node->containsUnscalableGlyphs();
2708 if (Q_LIKELY(!node->parent()))
2709 d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart);
2712 frameDecorationsEngine.addToSceneGraph(rootNode->frameDecorationsNode, QQuickText::Normal, QColor());
2714 rootNode->prependChildNode(rootNode->frameDecorationsNode);
2716 Q_ASSERT(nodeIterator == d->textNodeMap.end()
2717 || (nodeIterator->textNode() == firstCleanNode.textNode()
2718 && nodeIterator->startPos() == firstCleanNode.startPos()));
2720 if (firstCleanNode.textNode() !=
nullptr) {
2721 QPointF oldOffset = firstCleanNode.textNode()->matrix().map(QPointF(0,0));
2722 QPointF currentOffset = d->document->documentLayout()->blockBoundingRect(
2723 d->document->findBlock(firstCleanNode.startPos())).topLeft();
2724 QPointF delta = currentOffset - oldOffset;
2725 while (nodeIterator != d->textNodeMap.end()) {
2726 QMatrix4x4 transformMatrix = nodeIterator->textNode()->matrix();
2727 transformMatrix.translate(delta.x(), delta.y());
2728 nodeIterator->textNode()->setMatrix(transformMatrix);
2736 std::sort(d->textNodeMap.begin(), d->textNodeMap.end());
2739 if (d->cursorComponent ==
nullptr) {
2740 QSGInternalRectangleNode* cursor =
nullptr;
2741 if (!isReadOnly() && d->cursorVisible && d->control->cursorOn() && d->control->cursorVisible())
2742 cursor = d->sceneGraphContext()->createInternalRectangleNode(d->control->cursorRect(), d->color);
2743 rootNode->resetCursorNode(cursor);
2746 invalidateFontCaches();
2751void QQuickTextEdit::updatePolish()
2753 invalidateFontCaches();
2757
2758
2759
2760
2761
2762bool QQuickTextEdit::canPaste()
const
2764 Q_D(
const QQuickTextEdit);
2765 if (!d->canPasteValid) {
2766 const_cast<QQuickTextEditPrivate *>(d)->canPaste = d->control->canPaste();
2767 const_cast<QQuickTextEditPrivate *>(d)->canPasteValid =
true;
2773
2774
2775
2776
2777
2779bool QQuickTextEdit::canUndo()
const
2781 Q_D(
const QQuickTextEdit);
2782 return d->document->isUndoAvailable();
2786
2787
2788
2789
2790
2792bool QQuickTextEdit::canRedo()
const
2794 Q_D(
const QQuickTextEdit);
2795 return d->document->isRedoAvailable();
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810bool QQuickTextEdit::isInputMethodComposing()
const
2815 Q_D(
const QQuickTextEdit);
2816 return d->control->hasImState();
2820QQuickTextEditPrivate::ExtraData::ExtraData()
2821 : explicitTopPadding(
false)
2822 , explicitLeftPadding(
false)
2823 , explicitRightPadding(
false)
2824 , explicitBottomPadding(
false)
2825 , implicitResize(
true)
2829void QQuickTextEditPrivate::init()
2831 Q_Q(QQuickTextEdit);
2833#if QT_CONFIG(clipboard)
2834 if (QGuiApplication::clipboard()->supportsSelection())
2835 q->setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton);
2838 q->setAcceptedMouseButtons(Qt::LeftButton);
2841 q->setFlag(QQuickItem::ItemAcceptsInputMethod);
2843 q->setFlag(QQuickItem::ItemHasContents);
2845 q->setAcceptHoverEvents(
true);
2847 document =
new QTextDocument(q);
2848 ownsDocument =
true;
2849 auto *imageHandler =
new QQuickTextImageHandler(document);
2850 document->documentLayout()->registerHandler(QTextFormat::ImageObject, imageHandler);
2852 control =
new QQuickTextControl(document, q);
2853 control->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard | Qt::TextEditable);
2854 control->setAcceptRichText(
false);
2855 control->setCursorIsFocusIndicator(
true);
2856 q->setKeepMouseGrab(
true);
2858 qmlobject_connect(control, QQuickTextControl, SIGNAL(updateCursorRequest()), q, QQuickTextEdit, SLOT(updateCursor()));
2859 qmlobject_connect(control, QQuickTextControl, SIGNAL(selectionChanged()), q, QQuickTextEdit, SIGNAL(selectedTextChanged()));
2860 qmlobject_connect(control, QQuickTextControl, SIGNAL(selectionChanged()), q, QQuickTextEdit, SLOT(updateSelection()));
2861 qmlobject_connect(control, QQuickTextControl, SIGNAL(cursorPositionChanged()), q, QQuickTextEdit, SLOT(updateSelection()));
2862 qmlobject_connect(control, QQuickTextControl, SIGNAL(cursorPositionChanged()), q, QQuickTextEdit, SIGNAL(cursorPositionChanged()));
2863 qmlobject_connect(control, QQuickTextControl, SIGNAL(cursorRectangleChanged()), q, QQuickTextEdit, SLOT(moveCursorDelegate()));
2864 qmlobject_connect(control, QQuickTextControl, SIGNAL(linkActivated(QString)), q, QQuickTextEdit, SIGNAL(linkActivated(QString)));
2865 qmlobject_connect(control, QQuickTextControl, SIGNAL(overwriteModeChanged(
bool)), q, QQuickTextEdit, SIGNAL(overwriteModeChanged(
bool)));
2866 qmlobject_connect(control, QQuickTextControl, SIGNAL(textChanged()), q, QQuickTextEdit, SLOT(q_textChanged()));
2867 qmlobject_connect(control, QQuickTextControl, SIGNAL(preeditTextChanged()), q, QQuickTextEdit, SIGNAL(preeditTextChanged()));
2868#if QT_CONFIG(clipboard)
2869 qmlobject_connect(QGuiApplication::clipboard(), QClipboard, SIGNAL(dataChanged()), q, QQuickTextEdit, SLOT(q_canPasteChanged()));
2871 qmlobject_connect(document, QTextDocument, SIGNAL(undoAvailable(
bool)), q, QQuickTextEdit, SIGNAL(canUndoChanged()));
2872 qmlobject_connect(document, QTextDocument, SIGNAL(redoAvailable(
bool)), q, QQuickTextEdit, SIGNAL(canRedoChanged()));
2873 QObject::connect(document, &QTextDocument::contentsChange, q, &QQuickTextEdit::q_contentsChange);
2874 QObject::connect(document->documentLayout(), &QAbstractTextDocumentLayout::updateBlock, q, &QQuickTextEdit::invalidateBlock);
2875 QObject::connect(control, &QQuickTextControl::linkHovered, q, &QQuickTextEdit::q_linkHovered);
2876 QObject::connect(control, &QQuickTextControl::markerHovered, q, &QQuickTextEdit::q_markerHovered);
2878 document->setPageSize(QSizeF(0, 0));
2879 document->setDefaultFont(font);
2880 document->setDocumentMargin(textMargin);
2881 document->setUndoRedoEnabled(
false);
2882 document->setUndoRedoEnabled(
true);
2883 updateDefaultTextOption();
2884 document->setModified(
false);
2886#if QT_CONFIG(cursor)
2887 updateMouseCursorShape();
2889 setSizePolicy(QLayoutPolicy::Expanding, QLayoutPolicy::Expanding);
2892void QQuickTextEditPrivate::resetInputMethod()
2894 Q_Q(QQuickTextEdit);
2895 if (!q->isReadOnly() && q->hasActiveFocus() && qGuiApp)
2896 QGuiApplication::inputMethod()->reset();
2899void QQuickTextEdit::q_textChanged()
2901 Q_D(QQuickTextEdit);
2902 d->textCached =
false;
2903 for (QTextBlock it = d->document->begin(); it != d->document->end(); it = it.next()) {
2904 d->contentDirection = d->textDirection(it.text());
2905 if (d->contentDirection != Qt::LayoutDirectionAuto)
2908 d->determineHorizontalAlignment();
2909 d->updateDefaultTextOption();
2912 markDirtyNodesForRange(0, d->document->characterCount(), 0);
2913 if (isComponentComplete()) {
2915 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
2920 if (d->control->isBeingEdited())
2924void QQuickTextEdit::markDirtyNodesForRange(
int start,
int end,
int charDelta)
2926 Q_D(QQuickTextEdit);
2930 TextNode dummyNode(start);
2932 const TextNodeIterator textNodeMapBegin = d->textNodeMap.begin();
2933 const TextNodeIterator textNodeMapEnd = d->textNodeMap.end();
2935 TextNodeIterator it = std::lower_bound(textNodeMapBegin, textNodeMapEnd, dummyNode);
2938 if (it != textNodeMapBegin) {
2940 TextNode otherDummy(it->startPos());
2941 it = std::lower_bound(textNodeMapBegin, textNodeMapEnd, otherDummy);
2945 while (it != textNodeMapEnd) {
2946 if (it->startPos() <= end)
2949 it->moveStartPos(charDelta);
2956void QQuickTextEdit::q_contentsChange(
int pos,
int charsRemoved,
int charsAdded)
2958 Q_D(QQuickTextEdit);
2960 const int editRange = pos + qMax(charsAdded, charsRemoved);
2961 const int delta = charsAdded - charsRemoved;
2963 markDirtyNodesForRange(pos, editRange, delta);
2965 if (isComponentComplete()) {
2967 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
2972void QQuickTextEdit::moveCursorDelegate()
2974 Q_D(QQuickTextEdit);
2976 updateInputMethod();
2978 emit cursorRectangleChanged();
2981 QRectF cursorRect = cursorRectangle();
2982 d->cursorItem->setX(cursorRect.x());
2983 d->cursorItem->setY(cursorRect.y());
2984 d->cursorItem->setHeight(cursorRect.height());
2987void QQuickTextEdit::updateSelection()
2989 Q_D(QQuickTextEdit);
2992 if (d->control->textCursor().hasSelection() || d->hadSelection) {
2993 markDirtyNodesForRange(qMin(d->lastSelectionStart, d->control->textCursor().selectionStart()), qMax(d->control->textCursor().selectionEnd(), d->lastSelectionEnd), 0);
2994 if (isComponentComplete()) {
2996 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
3001 d->hadSelection = d->control->textCursor().hasSelection();
3003 if (d->lastSelectionStart != d->control->textCursor().selectionStart()) {
3004 d->lastSelectionStart = d->control->textCursor().selectionStart();
3005 emit selectionStartChanged();
3007 if (d->lastSelectionEnd != d->control->textCursor().selectionEnd()) {
3008 d->lastSelectionEnd = d->control->textCursor().selectionEnd();
3009 emit selectionEndChanged();
3013QRectF QQuickTextEdit::boundingRect()
const
3015 Q_D(
const QQuickTextEdit);
3017 QQuickTextUtil::alignedX(d->contentSize.width(), width(), effectiveHAlign()),
3019 d->contentSize.width(),
3020 d->contentSize.height());
3022 int cursorWidth = 1;
3025 else if (!d->document->isEmpty())
3029 r.setRight(r.right() + cursorWidth);
3034QRectF QQuickTextEdit::clipRect()
const
3036 Q_D(
const QQuickTextEdit);
3037 QRectF r = QQuickImplicitSizeItem::clipRect();
3038 int cursorWidth = 1;
3040 cursorWidth = d->cursorItem->width();
3041 if (!d->document->isEmpty())
3046 r.setRight(r.right() + cursorWidth);
3050qreal QQuickTextEditPrivate::getImplicitWidth()
const
3052 Q_Q(
const QQuickTextEdit);
3053 if (!requireImplicitWidth) {
3056 const_cast<QQuickTextEditPrivate*>(
this)->requireImplicitWidth =
true;
3057 const_cast<QQuickTextEdit*>(q)->updateSize();
3059 return implicitWidth;
3064void QQuickTextEdit::updateSize()
3066 Q_D(QQuickTextEdit);
3067 if (!isComponentComplete()) {
3075 if (!d->requireImplicitWidth) {
3076 emit implicitWidthChanged();
3078 if (d->requireImplicitWidth)
3081 if (d->requireImplicitWidth) {
3082 d->document->setTextWidth(-1);
3083 const qreal naturalWidth = d->document->idealWidth();
3084 const bool wasInLayout = d->inLayout;
3086 if (d->isImplicitResizeEnabled())
3087 setImplicitWidth(naturalWidth + leftPadding() + rightPadding());
3088 d->inLayout = wasInLayout;
3092 const qreal newTextWidth = width() - leftPadding() - rightPadding();
3093 if (d->document->textWidth() != newTextWidth)
3094 d->document->setTextWidth(newTextWidth);
3095 }
else if (d->wrapMode == NoWrap) {
3099 const qreal newTextWidth = d->document->idealWidth();
3100 if (d->document->textWidth() != newTextWidth)
3101 d->document->setTextWidth(newTextWidth);
3103 d->document->setTextWidth(-1);
3106 QFontMetricsF fm(d->font);
3107 const qreal newHeight = d->document->isEmpty() ? qCeil(fm.height()) : d->document->size().height();
3108 const qreal newWidth = d->document->idealWidth();
3110 if (d->isImplicitResizeEnabled()) {
3113 setImplicitSize(newWidth + leftPadding() + rightPadding(), newHeight + topPadding() + bottomPadding());
3115 setImplicitHeight(newHeight + topPadding() + bottomPadding());
3118 d->xoff = leftPadding() + qMax(qreal(0), QQuickTextUtil::alignedX(d->document->size().width(), width() - leftPadding() - rightPadding(), effectiveHAlign()));
3119 d->yoff = topPadding() + QQuickTextUtil::alignedY(d->document->size().height(), height() - topPadding() - bottomPadding(), d->vAlign);
3121 qreal baseline = fm.ascent();
3122 QTextBlock firstBlock = d->document->firstBlock();
3123 if (firstBlock.isValid() && firstBlock.layout() !=
nullptr && firstBlock.lineCount() > 0) {
3124 QTextLine firstLine = firstBlock.layout()->lineAt(0);
3125 if (firstLine.isValid())
3126 baseline = firstLine.ascent();
3129 setBaselineOffset(baseline + d->yoff + d->textMargin);
3131 QSizeF size(newWidth, newHeight);
3132 if (d->contentSize != size) {
3133 d->contentSize = size;
3135 const bool wasInResize = d->inResize;
3138 emit contentSizeChanged();
3139 d->inResize = wasInResize;
3144void QQuickTextEdit::updateWholeDocument()
3146 Q_D(QQuickTextEdit);
3147 if (!d->textNodeMap.isEmpty()) {
3148 for (TextNode &node : d->textNodeMap)
3152 if (isComponentComplete()) {
3154 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
3159void QQuickTextEdit::invalidateBlock(
const QTextBlock &block)
3161 Q_D(QQuickTextEdit);
3162 markDirtyNodesForRange(block.position(), block.position() + block.length(), 0);
3164 if (isComponentComplete()) {
3166 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
3171void QQuickTextEdit::updateCursor()
3173 Q_D(QQuickTextEdit);
3174 if (isComponentComplete() && isVisible()) {
3176 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
3181void QQuickTextEdit::q_linkHovered(
const QString &link)
3183 Q_D(QQuickTextEdit);
3184 emit linkHovered(link);
3185#if QT_CONFIG(cursor)
3186 if (link.isEmpty()) {
3187 d->updateMouseCursorShape();
3188 }
else if (cursor().shape() != Qt::PointingHandCursor) {
3189 setCursor(Qt::PointingHandCursor);
3194void QQuickTextEdit::q_markerHovered(
bool hovered)
3196 Q_D(QQuickTextEdit);
3197#if QT_CONFIG(cursor)
3199 d->updateMouseCursorShape();
3200 }
else if (cursor().shape() != Qt::PointingHandCursor) {
3201 setCursor(Qt::PointingHandCursor);
3206void QQuickTextEdit::q_updateAlignment()
3208 Q_D(QQuickTextEdit);
3209 if (d->determineHorizontalAlignment()) {
3210 d->updateDefaultTextOption();
3211 d->xoff = qMax(qreal(0), QQuickTextUtil::alignedX(d->document->size().width(), width(), effectiveHAlign()));
3212 moveCursorDelegate();
3216void QQuickTextEdit::updateTotalLines()
3218 Q_D(QQuickTextEdit);
3222 for (QTextBlock it = d->document->begin(); it != d->document->end(); it = it.next()) {
3223 QTextLayout *layout = it.layout();
3226 subLines += layout->lineCount()-1;
3229 int newTotalLines = d->document->lineCount() + subLines;
3230 if (d->lineCount != newTotalLines) {
3231 d->lineCount = newTotalLines;
3232 emit lineCountChanged();
3236void QQuickTextEditPrivate::updateDefaultTextOption()
3238 Q_Q(QQuickTextEdit);
3239 QTextOption opt = document->defaultTextOption();
3240 const Qt::Alignment oldAlignment = opt.alignment();
3241 Qt::LayoutDirection oldTextDirection = opt.textDirection();
3243 QQuickTextEdit::HAlignment horizontalAlignment = q->effectiveHAlign();
3244 if (contentDirection == Qt::RightToLeft) {
3245 if (horizontalAlignment == QQuickTextEdit::AlignLeft)
3246 horizontalAlignment = QQuickTextEdit::AlignRight;
3247 else if (horizontalAlignment == QQuickTextEdit::AlignRight)
3248 horizontalAlignment = QQuickTextEdit::AlignLeft;
3250 if (!hAlignImplicit)
3251 opt.setAlignment((Qt::Alignment)(
int)(horizontalAlignment | vAlign));
3253 opt.setAlignment(Qt::Alignment(vAlign));
3256 if (contentDirection == Qt::LayoutDirectionAuto) {
3257 opt.setTextDirection(qGuiApp->inputMethod()->inputDirection());
3261 opt.setTextDirection(contentDirection);
3264 QTextOption::WrapMode oldWrapMode = opt.wrapMode();
3265 opt.setWrapMode(QTextOption::WrapMode(wrapMode));
3267 bool oldUseDesignMetrics = opt.useDesignMetrics();
3268 opt.setUseDesignMetrics(renderType != QQuickTextEdit::NativeRendering);
3270 if (oldWrapMode != opt.wrapMode() || oldAlignment != opt.alignment()
3271 || oldTextDirection != opt.textDirection()
3272 || oldUseDesignMetrics != opt.useDesignMetrics()) {
3273 document->setDefaultTextOption(opt);
3277void QQuickTextEditPrivate::onDocumentStatusChanged()
3279 Q_ASSERT(quickDocument);
3280 switch (quickDocument->status()) {
3281 case QQuickTextDocument::Status::Loaded:
3282 case QQuickTextDocument::Status::Saved:
3283 switch (QQuickTextDocumentPrivate::get(quickDocument)->detectedFormat) {
3285 richText = (format == QQuickTextEdit::RichText || format == QQuickTextEdit::AutoText);
3286 markdownText =
false;
3288 case Qt::MarkdownText:
3290 markdownText = (format == QQuickTextEdit::MarkdownText || format == QQuickTextEdit::AutoText);
3294 markdownText =
false;
3305void QQuickTextEdit::focusInEvent(QFocusEvent *event)
3307 Q_D(QQuickTextEdit);
3308 d->handleFocusEvent(event);
3309 QQuickImplicitSizeItem::focusInEvent(event);
3312void QQuickTextEdit::focusOutEvent(QFocusEvent *event)
3314 Q_D(QQuickTextEdit);
3315 d->handleFocusEvent(event);
3316 QQuickImplicitSizeItem::focusOutEvent(event);
3319#if QT_VERSION < QT_VERSION_CHECK(7
, 0
, 0
)
3320bool QQuickTextEditPrivate::handleContextMenuEvent(QContextMenuEvent *event)
3322bool QQuickTextEdit::contextMenuEvent(QContextMenuEvent *event)
3325 Q_Q(QQuickTextEdit);
3326 QContextMenuEvent mapped(event->reason(), q->cursorRectangle().center().toPoint(),
3327 event->globalPos(), event->modifiers());
3328 const bool eventProcessed = QQuickItemPrivate::handleContextMenuEvent(&mapped);
3329 event->setAccepted(mapped.isAccepted());
3330 return eventProcessed;
3333void QQuickTextEditPrivate::handleFocusEvent(QFocusEvent *event)
3335 Q_Q(QQuickTextEdit);
3336 bool focus = event->type() == QEvent::FocusIn;
3337 if (!q->isReadOnly())
3338 q->setCursorVisible(focus);
3339 control->processEvent(event, QPointF(-xoff, -yoff));
3341 q->q_updateAlignment();
3343 if (focusOnPress && !q->isReadOnly())
3344 qGuiApp->inputMethod()->show();
3345 q->connect(QGuiApplication::inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
3346 q, SLOT(q_updateAlignment()));
3350 q->disconnect(QGuiApplication::inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
3351 q, SLOT(q_updateAlignment()));
3353 if (event->reason() != Qt::ActiveWindowFocusReason
3354 && event->reason() != Qt::PopupFocusReason
3355 && control->textCursor().hasSelection()
3356 && !persistentSelection)
3359 emit q->editingFinished();
3363void QQuickTextEditPrivate::addCurrentTextNodeToRoot(QQuickTextNodeEngine *engine, QSGTransformNode *root, QSGInternalTextNode *node, TextNodeIterator &it,
int startPos)
3365 engine->addToSceneGraph(node, QQuickText::Normal, QColor());
3366 it = textNodeMap.insert(it, TextNode(startPos, node));
3368 root->appendChildNode(node);
3369 ++renderedBlockCount;
3372QSGInternalTextNode *QQuickTextEditPrivate::createTextNode()
3374 Q_Q(QQuickTextEdit);
3375 QSGInternalTextNode* node = sceneGraphContext()->createInternalTextNode(sceneGraphRenderContext());
3376 node->setRenderType(QSGTextNode::RenderType(renderType));
3377 node->setFiltering(q->smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
3381void QQuickTextEdit::q_canPasteChanged()
3383 Q_D(QQuickTextEdit);
3384 bool old = d->canPaste;
3385 d->canPaste = d->control->canPaste();
3386 bool changed = old!=d->canPaste || !d->canPasteValid;
3387 d->canPasteValid =
true;
3389 emit canPasteChanged();
3393
3394
3395
3396
3397
3398
3400QString QQuickTextEdit::getText(
int start,
int end)
const
3402 Q_D(
const QQuickTextEdit);
3403 start = qBound(0, start, d->document->characterCount() - 1);
3404 end = qBound(0, end, d->document->characterCount() - 1);
3405 QTextCursor cursor(d->document);
3406 cursor.setPosition(start, QTextCursor::MoveAnchor);
3407 cursor.setPosition(end, QTextCursor::KeepAnchor);
3408#if QT_CONFIG(texthtmlparser)
3409 return d->richText || d->markdownText
3410 ? cursor.selectedText()
3411 : cursor.selection().toPlainText();
3413 return cursor.selection().toPlainText();
3418
3419
3420
3421
3422
3423
3425QString QQuickTextEdit::getFormattedText(
int start,
int end)
const
3427 Q_D(
const QQuickTextEdit);
3429 start = qBound(0, start, d->document->characterCount() - 1);
3430 end = qBound(0, end, d->document->characterCount() - 1);
3432 QTextCursor cursor(d->document);
3433 cursor.setPosition(start, QTextCursor::MoveAnchor);
3434 cursor.setPosition(end, QTextCursor::KeepAnchor);
3437#if QT_CONFIG(texthtmlparser)
3438 return cursor.selection().toHtml();
3440 return cursor.selection().toPlainText();
3442 }
else if (d->markdownText) {
3443#if QT_CONFIG(textmarkdownwriter)
3444 return cursor.selection().toMarkdown();
3446 return cursor.selection().toPlainText();
3449 return cursor.selection().toPlainText();
3454
3455
3456
3457
3458void QQuickTextEdit::insert(
int position,
const QString &text)
3460 Q_D(QQuickTextEdit);
3461 if (position < 0 || position >= d->document->characterCount())
3463 QTextCursor cursor(d->document);
3464 cursor.setPosition(position);
3465 d->richText = d->richText || (d->format == AutoText && Qt::mightBeRichText(text));
3467#if QT_CONFIG(texthtmlparser)
3468 cursor.insertHtml(text);
3470 cursor.insertText(text);
3472 }
else if (d->markdownText) {
3473#if QT_CONFIG(textmarkdownreader)
3474 cursor.insertMarkdown(text);
3476 cursor.insertText(text);
3479 cursor.insertText(text);
3481 d->control->updateCursorRectangle(
false);
3485
3486
3487
3488
3490void QQuickTextEdit::remove(
int start,
int end)
3492 Q_D(QQuickTextEdit);
3493 start = qBound(0, start, d->document->characterCount() - 1);
3494 end = qBound(0, end, d->document->characterCount() - 1);
3495 QTextCursor cursor(d->document);
3496 cursor.setPosition(start, QTextCursor::MoveAnchor);
3497 cursor.setPosition(end, QTextCursor::KeepAnchor);
3498 cursor.removeSelectedText();
3499 d->control->updateCursorRectangle(
false);
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3514QQuickTextDocument *QQuickTextEdit::textDocument()
3516 Q_D(QQuickTextEdit);
3517 if (!d->quickDocument) {
3518 d->quickDocument =
new QQuickTextDocument(
this);
3519 connect(d->quickDocument, &QQuickTextDocument::statusChanged, d->quickDocument,
3520 [d]() { d->onDocumentStatusChanged(); } );
3522 return d->quickDocument;
3525bool QQuickTextEditPrivate::isLinkHoveredConnected()
3527 Q_Q(QQuickTextEdit);
3528 IS_SIGNAL_CONNECTED(q, QQuickTextEdit, linkHovered, (
const QString &));
3531#if QT_CONFIG(cursor)
3532void QQuickTextEditPrivate::updateMouseCursorShape()
3534 Q_Q(QQuickTextEdit);
3535 q->setCursor(q->isReadOnly() && !q->selectByMouse() ? Qt::ArrowCursor : Qt::IBeamCursor);
3540
3541
3542
3543
3544
3545
3546
3547
3548
3551
3552
3553
3554
3555
3558
3559
3560
3561
3562
3563
3564
3565
3566
3569
3570
3571
3572
3573
3574
3575
3577QString QQuickTextEdit::hoveredLink()
const
3579 Q_D(
const QQuickTextEdit);
3580 if (
const_cast<QQuickTextEditPrivate *>(d)->isLinkHoveredConnected()) {
3581 return d->control->hoveredLink();
3583#if QT_CONFIG(cursor)
3584 if (QQuickWindow *wnd = window()) {
3585 QPointF pos = QCursor::pos(wnd->screen()) - wnd->position() - mapToScene(QPointF(0, 0));
3586 return d->control->anchorAt(pos);
3593void QQuickTextEdit::hoverEnterEvent(QHoverEvent *event)
3595 Q_D(QQuickTextEdit);
3596 if (d->isLinkHoveredConnected())
3597 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
3601void QQuickTextEdit::hoverMoveEvent(QHoverEvent *event)
3603 Q_D(QQuickTextEdit);
3604 if (d->isLinkHoveredConnected())
3605 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
3609void QQuickTextEdit::hoverLeaveEvent(QHoverEvent *event)
3611 Q_D(QQuickTextEdit);
3612 if (d->isLinkHoveredConnected())
3613 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
3618
3619
3620
3621
3622
3623
3624
3625
3626void QQuickTextEdit::append(
const QString &text)
3628 Q_D(QQuickTextEdit);
3629 QTextCursor cursor(d->document);
3630 cursor.beginEditBlock();
3631 cursor.movePosition(QTextCursor::End);
3633 if (!d->document->isEmpty())
3634 cursor.insertBlock();
3636 if (d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(text))) {
3637#if QT_CONFIG(texthtmlparser)
3638 cursor.insertHtml(text);
3640 cursor.insertText(text);
3642 }
else if (d->format == MarkdownText) {
3643#if QT_CONFIG(textmarkdownreader)
3644 cursor.insertMarkdown(text);
3646 cursor.insertText(text);
3649 cursor.insertText(text);
3652 cursor.endEditBlock();
3653 d->control->updateCursorRectangle(
false);
3657
3658
3659
3660
3661
3662
3663
3664
3665QString QQuickTextEdit::linkAt(qreal x, qreal y)
const
3667 Q_D(
const QQuickTextEdit);
3668 return d->control->anchorAt(QPointF(x + topPadding(), y + leftPadding()));
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682qreal QQuickTextEdit::padding()
const
3684 Q_D(
const QQuickTextEdit);
3685 return d->padding();
3688void QQuickTextEdit::setPadding(qreal padding)
3690 Q_D(QQuickTextEdit);
3691 if (qFuzzyCompare(d->padding(), padding))
3694 d->extra.value().padding = padding;
3696 if (isComponentComplete()) {
3697 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
3700 emit paddingChanged();
3701 if (!d->extra.isAllocated() || !d->extra->explicitTopPadding)
3702 emit topPaddingChanged();
3703 if (!d->extra.isAllocated() || !d->extra->explicitLeftPadding)
3704 emit leftPaddingChanged();
3705 if (!d->extra.isAllocated() || !d->extra->explicitRightPadding)
3706 emit rightPaddingChanged();
3707 if (!d->extra.isAllocated() || !d->extra->explicitBottomPadding)
3708 emit bottomPaddingChanged();
3711void QQuickTextEdit::resetPadding()
3716qreal QQuickTextEdit::topPadding()
const
3718 Q_D(
const QQuickTextEdit);
3719 if (d->extra.isAllocated() && d->extra->explicitTopPadding)
3720 return d->extra->topPadding;
3721 return d->padding();
3724void QQuickTextEdit::setTopPadding(qreal padding)
3726 Q_D(QQuickTextEdit);
3727 d->setTopPadding(padding);
3730void QQuickTextEdit::resetTopPadding()
3732 Q_D(QQuickTextEdit);
3733 d->setTopPadding(0,
true);
3736qreal QQuickTextEdit::leftPadding()
const
3738 Q_D(
const QQuickTextEdit);
3739 if (d->extra.isAllocated() && d->extra->explicitLeftPadding)
3740 return d->extra->leftPadding;
3741 return d->padding();
3744void QQuickTextEdit::setLeftPadding(qreal padding)
3746 Q_D(QQuickTextEdit);
3747 d->setLeftPadding(padding);
3750void QQuickTextEdit::resetLeftPadding()
3752 Q_D(QQuickTextEdit);
3753 d->setLeftPadding(0,
true);
3756qreal QQuickTextEdit::rightPadding()
const
3758 Q_D(
const QQuickTextEdit);
3759 if (d->extra.isAllocated() && d->extra->explicitRightPadding)
3760 return d->extra->rightPadding;
3761 return d->padding();
3764void QQuickTextEdit::setRightPadding(qreal padding)
3766 Q_D(QQuickTextEdit);
3767 d->setRightPadding(padding);
3770void QQuickTextEdit::resetRightPadding()
3772 Q_D(QQuickTextEdit);
3773 d->setRightPadding(0,
true);
3776qreal QQuickTextEdit::bottomPadding()
const
3778 Q_D(
const QQuickTextEdit);
3779 if (d->extra.isAllocated() && d->extra->explicitBottomPadding)
3780 return d->extra->bottomPadding;
3781 return d->padding();
3784void QQuickTextEdit::setBottomPadding(qreal padding)
3786 Q_D(QQuickTextEdit);
3787 d->setBottomPadding(padding);
3790void QQuickTextEdit::resetBottomPadding()
3792 Q_D(QQuickTextEdit);
3793 d->setBottomPadding(0,
true);
3797
3798
3799
3800
3801
3802
3803
3804int QQuickTextEdit::tabStopDistance()
const
3806 Q_D(
const QQuickTextEdit);
3807 return d->document->defaultTextOption().tabStopDistance();
3810void QQuickTextEdit::setTabStopDistance(qreal distance)
3812 Q_D(QQuickTextEdit);
3813 QTextOption textOptions = d->document->defaultTextOption();
3814 if (textOptions.tabStopDistance() == distance)
3817 textOptions.setTabStopDistance(distance);
3818 d->document->setDefaultTextOption(textOptions);
3819 emit tabStopDistanceChanged(distance);
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833void QQuickTextEdit::clear()
3835 Q_D(QQuickTextEdit);
3836 d->resetInputMethod();
3837 d->control->clear();
3840#ifndef QT_NO_DEBUG_STREAM
3843 QDebugStateSaver saver(debug);
3845 debug <<
"Node(startPos:" << n.m_startPos <<
"dirty:" << n.m_dirty << n.m_node <<
')';