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 QQuickItemPrivate *itemPriv = QQuickItemPrivate::get(
this);
1981 if (!itemPriv->extra.isAllocated() || !itemPriv->extra->keyHandler) {
1982 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003bool QQuickTextEdit::overwriteMode()
const
2005 Q_D(
const QQuickTextEdit);
2006 return d->control->overwriteMode();
2009void QQuickTextEdit::setOverwriteMode(
bool overwrite)
2011 Q_D(QQuickTextEdit);
2012 d->control->setOverwriteMode(overwrite);
2016
2017
2018
2019void QQuickTextEdit::keyPressEvent(QKeyEvent *event)
2021 Q_D(QQuickTextEdit);
2022 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2023 if (!event->isAccepted())
2024 QQuickImplicitSizeItem::keyPressEvent(event);
2028
2029
2030
2031void QQuickTextEdit::keyReleaseEvent(QKeyEvent *event)
2033 Q_D(QQuickTextEdit);
2034 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2035 if (!event->isAccepted())
2036 QQuickImplicitSizeItem::keyReleaseEvent(event);
2040
2041
2042
2043
2044void QQuickTextEdit::deselect()
2046 Q_D(QQuickTextEdit);
2047 QTextCursor c = d->control->textCursor();
2049 d->control->setTextCursor(c);
2053
2054
2055
2056
2057void QQuickTextEdit::selectAll()
2059 Q_D(QQuickTextEdit);
2060 d->control->selectAll();
2064
2065
2066
2067
2068void QQuickTextEdit::selectWord()
2070 Q_D(QQuickTextEdit);
2071 QTextCursor c = d->control->textCursor();
2072 c.select(QTextCursor::WordUnderCursor);
2073 d->control->setTextCursor(c);
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089void QQuickTextEdit::select(
int start,
int end)
2091 Q_D(QQuickTextEdit);
2092 if (start < 0 || end < 0 || start >= d->document->characterCount() || end >= d->document->characterCount())
2094 QTextCursor cursor = d->control->textCursor();
2095 cursor.beginEditBlock();
2096 cursor.setPosition(start, QTextCursor::MoveAnchor);
2097 cursor.setPosition(end, QTextCursor::KeepAnchor);
2098 cursor.endEditBlock();
2099 d->control->setTextCursor(cursor);
2104 updateInputMethod();
2109
2110
2111
2112
2113
2114bool QQuickTextEdit::isRightToLeft(
int start,
int end)
2117 qmlWarning(
this) <<
"isRightToLeft(start, end) called with the end property being smaller than the start.";
2120 return getText(start, end).isRightToLeft();
2124#if QT_CONFIG(clipboard)
2126
2127
2128
2129
2130void QQuickTextEdit::cut()
2132 Q_D(QQuickTextEdit);
2137
2138
2139
2140
2141void QQuickTextEdit::copy()
2143 Q_D(QQuickTextEdit);
2148
2149
2150
2151
2152void QQuickTextEdit::paste()
2154 Q_D(QQuickTextEdit);
2155 d->control->paste();
2161
2162
2163
2164
2165
2166
2168void QQuickTextEdit::undo()
2170 Q_D(QQuickTextEdit);
2175
2176
2177
2178
2180void QQuickTextEdit::redo()
2182 Q_D(QQuickTextEdit);
2187
2188
2189
2190void QQuickTextEdit::mousePressEvent(QMouseEvent *event)
2192 Q_D(QQuickTextEdit);
2193 const bool isMouse = QQuickDeliveryAgentPrivate::isEventFromMouseOrTouchpad(event);
2194 setKeepMouseGrab(d->selectByMouse && isMouse);
2195 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2196 if (d->focusOnPress){
2197 bool hadActiveFocus = hasActiveFocus();
2198 forceActiveFocus(Qt::MouseFocusReason);
2201 if (hasActiveFocus() && hadActiveFocus && !isReadOnly())
2202 qGuiApp->inputMethod()->show();
2204 Q_UNUSED(hadActiveFocus);
2207 if (!event->isAccepted())
2208 QQuickImplicitSizeItem::mousePressEvent(event);
2212
2213
2214
2215void QQuickTextEdit::mouseReleaseEvent(QMouseEvent *event)
2217 Q_D(QQuickTextEdit);
2218 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2220 if (!event->isAccepted())
2221 QQuickImplicitSizeItem::mouseReleaseEvent(event);
2225
2226
2227
2228void QQuickTextEdit::mouseDoubleClickEvent(QMouseEvent *event)
2230 Q_D(QQuickTextEdit);
2231 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2232 if (!event->isAccepted())
2233 QQuickImplicitSizeItem::mouseDoubleClickEvent(event);
2237
2238
2239
2240void QQuickTextEdit::mouseMoveEvent(QMouseEvent *event)
2242 Q_D(QQuickTextEdit);
2243 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2244 if (!event->isAccepted())
2245 QQuickImplicitSizeItem::mouseMoveEvent(event);
2250
2251
2252
2253void QQuickTextEdit::inputMethodEvent(QInputMethodEvent *event)
2255 Q_D(QQuickTextEdit);
2256 const bool wasComposing = isInputMethodComposing();
2257 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2258 setCursorVisible(d->control->cursorVisible());
2259 if (wasComposing != isInputMethodComposing())
2260 emit inputMethodComposingChanged();
2264
2265
2266
2267QVariant QQuickTextEdit::inputMethodQuery(Qt::InputMethodQuery property, QVariant argument)
const
2269 Q_D(
const QQuickTextEdit);
2274 v = (
bool)(flags() & ItemAcceptsInputMethod);
2277 v = (
int)d->effectiveInputMethodHints();
2279 case Qt::ImInputItemClipRectangle:
2280 v = QQuickItem::inputMethodQuery(property);
2282 case Qt::ImReadOnly:
2286 if (property == Qt::ImCursorPosition && !argument.isNull())
2287 argument = QVariant(argument.toPointF() - QPointF(d->xoff, d->yoff));
2288 v = d->control->inputMethodQuery(property, argument);
2289 if (property == Qt::ImCursorRectangle || property == Qt::ImAnchorRectangle)
2290 v = QVariant(v.toRectF().translated(d->xoff, d->yoff));
2297
2298
2299
2300QVariant QQuickTextEdit::inputMethodQuery(Qt::InputMethodQuery property)
const
2302 return inputMethodQuery(property, QVariant());
2306void QQuickTextEdit::triggerPreprocess()
2308 Q_D(QQuickTextEdit);
2309 if (d->updateType == QQuickTextEditPrivate::UpdateNone)
2310 d->updateType = QQuickTextEditPrivate::UpdateOnlyPreprocess;
2316
2317
2318
2319
2320QVariant QQuickTextEdit::loadResource(
int type,
const QUrl &source)
2322 Q_D(QQuickTextEdit);
2323 const QUrl url = d->document->baseUrl().resolved(source);
2324 if (url.isLocalFile()) {
2326 QFileInfo fi(QQmlFile::urlToLocalFileOrQrc(url));
2328 qmlWarning(
this) <<
"Cannot open: " << url.toString();
2334 if (!url.scheme().compare(
"qrc"_L1, Qt::CaseInsensitive)) {
2336 QFile f(QQmlFile::urlToLocalFileOrQrc(url));
2337 if (f.open(QFile::ReadOnly)) {
2338 QByteArray buf = f.readAll();
2341 image.loadFromData(buf);
2342 if (!image.isNull())
2346 qmlWarning(
this) <<
"Cannot read resource: " << f.fileName();
2351 auto existingJobIter = std::find_if(
2352 d->pixmapsInProgress.cbegin(), d->pixmapsInProgress.cend(),
2353 [&url](
const auto *job) {
return job->url() == url; } );
2354 if (existingJobIter != d->pixmapsInProgress.cend()) {
2355 const QQuickPixmap *job = *existingJobIter;
2356 if (job->isError()) {
2357 qmlWarning(
this) << job->error();
2358 d->pixmapsInProgress.erase(existingJobIter);
2362 qCDebug(lcTextEdit) <<
"already downloading" << url;
2364 return job->isReady() ? job->image() : QVariant();
2369 qCDebug(lcTextEdit) <<
"loading" << source <<
"resolved" << url
2370 <<
"type" <<
static_cast<QTextDocument::ResourceType>(type);
2371 QQmlContext *context = qmlContext(
this);
2374 QQuickPixmap *p =
new QQuickPixmap(context->engine(), url, QQuickPixmap::Options{});
2375 p->connectFinished(
this, SLOT(resourceRequestFinished()));
2376 d->pixmapsInProgress.append(p);
2378 return p->isReady() ? p->image() : QVariant();
2382
2383
2384void QQuickTextEdit::resourceRequestFinished()
2386 Q_D(QQuickTextEdit);
2387 for (
auto it = d->pixmapsInProgress.cbegin(); it != d->pixmapsInProgress.cend(); ++it) {
2389 if (job->isError()) {
2391 qCDebug(lcTextEdit) <<
"failed to load (error)" << job->url();
2392 d->document->resource(QTextDocument::ImageResource, job->url());
2396 }
else if (job->isReady()) {
2398 auto res = d->document->resource(QTextDocument::ImageResource, job->url());
2400 qCDebug(lcTextEdit) << (res.isValid() ?
"done downloading" :
"failed to load") << job->url() << job->rect();
2401 d->pixmapsInProgress.erase(it);
2406 if (d->pixmapsInProgress.isEmpty()) {
2414using TextNodeIterator = QQuickTextEditPrivate::TextNodeIterator;
2418 return n1.startPos() < n2.startPos();
2423 QMatrix4x4 transformMatrix;
2424 transformMatrix.translate(topLeft.x(), topLeft.y());
2425 node->setMatrix(transformMatrix);
2429
2430
2431
2432
2433
2434void QQuickTextEdit::invalidateFontCaches()
2436 Q_D(QQuickTextEdit);
2437 if (d->document ==
nullptr)
2441 for (block = d->document->firstBlock(); block.isValid(); block = block.next()) {
2442 if (block.layout() !=
nullptr && block.layout()->engine() !=
nullptr)
2443 block.layout()->engine()->resetFontEngineCache();
2447QTextDocument *QQuickTextEdit::document()
const
2449 Q_D(
const QQuickTextEdit);
2453void QQuickTextEdit::setDocument(QTextDocument *doc)
2455 Q_D(QQuickTextEdit);
2457 std::unique_ptr<QTextDocument> cleanup(d->ownsDocument ? d->document :
nullptr);
2459 d->ownsDocument =
false;
2460 d->control->setDocument(doc);
2470 engine->setDevicePixelRatio(dpr);
2473QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData)
2475 Q_UNUSED(updatePaintNodeData);
2476 Q_D(QQuickTextEdit);
2478 if (d->updateType != QQuickTextEditPrivate::UpdatePaintNode
2479 && d->updateType != QQuickTextEditPrivate::UpdateAll
2480 && oldNode !=
nullptr) {
2482 d->updateType = QQuickTextEditPrivate::UpdateNone;
2486 d->containsUnscalableGlyphs =
false;
2487 if (!oldNode || d->updateType == QQuickTextEditPrivate::UpdateAll) {
2493 d->textNodeMap.clear();
2496 d->updateType = QQuickTextEditPrivate::UpdateNone;
2498 RootNode *rootNode =
static_cast<RootNode *>(oldNode);
2499 TextNodeIterator nodeIterator = d->textNodeMap.begin();
2500 std::optional<
int> firstPosAcrossAllNodes;
2501 if (nodeIterator != d->textNodeMap.end())
2502 firstPosAcrossAllNodes = nodeIterator->startPos();
2504 while (nodeIterator != d->textNodeMap.end() && !nodeIterator->dirty())
2507 const auto dpr = d->effectiveDevicePixelRatio();
2508 QQuickTextNodeEngine engine;
2509 engine.setDevicePixelRatio(dpr);
2510 QQuickTextNodeEngine frameDecorationsEngine;
2511 frameDecorationsEngine.setDevicePixelRatio(dpr);
2513 if (!oldNode || nodeIterator < d->textNodeMap.end() || d->textNodeMap.isEmpty()) {
2516 rootNode =
new RootNode;
2518 int firstDirtyPos = 0;
2519 if (nodeIterator != d->textNodeMap.end()) {
2520 firstDirtyPos = nodeIterator->startPos();
2523 QSGInternalTextNode *firstCleanNode =
nullptr;
2524 auto it = d->textNodeMap.constEnd();
2525 while (it != nodeIterator) {
2529 firstCleanNode = it->textNode();
2532 rootNode->removeChildNode(nodeIterator->textNode());
2533 delete nodeIterator->textNode();
2534 nodeIterator = d->textNodeMap.erase(nodeIterator);
2535 }
while (nodeIterator != d->textNodeMap.constEnd() && nodeIterator->textNode() != firstCleanNode);
2540 if (flags().testFlag(QQuickItem::ItemObservesViewport)) {
2541 viewport = clipRect();
2542 qCDebug(lcVP) <<
"text viewport" << viewport;
2546 rootNode->resetFrameDecorations(d->createTextNode());
2547 resetEngine(&frameDecorationsEngine, d->color, d->selectedTextColor, d->selectionColor, dpr);
2549 QSGInternalTextNode *node =
nullptr;
2551 int currentNodeSize = 0;
2552 int nodeStart = firstDirtyPos;
2553 QPointF basePosition(d->xoff, d->yoff);
2554 QMatrix4x4 basePositionMatrix;
2555 basePositionMatrix.translate(basePosition.x(), basePosition.y());
2556 rootNode->setMatrix(basePositionMatrix);
2559 const TextNode firstCleanNode = (nodeIterator != d->textNodeMap.end()) ? *nodeIterator
2562 QList<QTextFrame *> frames;
2563 frames.append(d->document->rootFrame());
2566 d->firstBlockInViewport = -1;
2567 d->firstBlockPastViewport = -1;
2568 int frameCount = -1;
2569 while (!frames.isEmpty()) {
2570 QTextFrame *textFrame = frames.takeFirst();
2574 qCDebug(lcVP) <<
"frame" << frameCount << textFrame
2575 <<
"from" << positionToRectangle(textFrame->firstPosition()).topLeft()
2576 <<
"to" << positionToRectangle(textFrame->lastPosition()).bottomRight();
2577 frames.append(textFrame->childFrames());
2578 frameDecorationsEngine.addFrameDecorations(d->document, textFrame);
2579 resetEngine(&engine, d->color, d->selectedTextColor, d->selectionColor, dpr);
2581 if (textFrame->firstPosition() > textFrame->lastPosition()
2582 && textFrame->frameFormat().position() != QTextFrameFormat::InFlow) {
2583 node = d->createTextNode();
2584 updateNodeTransform(node, d->document->documentLayout()->frameBoundingRect(textFrame).topLeft());
2585 const int pos = textFrame->firstPosition() - 1;
2586 auto *a =
static_cast<QtPrivate::ProtectedLayoutAccessor *>(d->document->documentLayout());
2587 QTextCharFormat format = a->formatAccessor(pos);
2588 QTextBlock block = textFrame->firstCursorPosition().block();
2589 nodeOffset = d->document->documentLayout()->blockBoundingRect(block).topLeft();
2591 if (!viewport.isNull() && block.layout()) {
2592 QRectF coveredRegion = block.layout()->boundingRect().adjusted(nodeOffset.x(), nodeOffset.y(), nodeOffset.x(), nodeOffset.y());
2593 inView = coveredRegion.bottom() >= viewport.top() && coveredRegion.top() <= viewport.bottom();
2594 qCDebug(lcVP) <<
"non-flow frame" << coveredRegion <<
"in viewport?" << inView;
2597 engine.setCurrentLine(block.layout()->lineForTextPosition(pos - block.position()));
2598 engine.addTextObject(block, QPointF(0, 0), format, QQuickTextNodeEngine::Unselected, d->document,
2599 pos, textFrame->frameFormat().position());
2604 QList<
int> frameBoundaries;
2605 frameBoundaries.reserve(frames.size());
2606 for (QTextFrame *frame : std::as_const(frames))
2607 frameBoundaries.append(frame->firstPosition());
2608 std::sort(frameBoundaries.begin(), frameBoundaries.end());
2610 QTextFrame::iterator it = textFrame->begin();
2611 while (!it.atEnd()) {
2612 QTextBlock block = it.currentBlock();
2613 if (block.position() < firstDirtyPos) {
2618 if (!engine.hasContents())
2619 nodeOffset = d->document->documentLayout()->blockBoundingRect(block).topLeft();
2622 if (!viewport.isNull()) {
2623 QRectF coveredRegion;
2624 if (block.layout()) {
2625 coveredRegion = block.layout()->boundingRect().adjusted(nodeOffset.x(), nodeOffset.y(), nodeOffset.x(), nodeOffset.y());
2626 inView = coveredRegion.bottom() > viewport.top();
2628 const bool potentiallyScrollingBackwards = firstPosAcrossAllNodes && *firstPosAcrossAllNodes == firstDirtyPos;
2629 if (d->firstBlockInViewport < 0 && inView && potentiallyScrollingBackwards) {
2631 if (coveredRegion.top() > viewport.top() + 1) {
2632 qCDebug(lcVP) <<
"checking backwards from block" << block.blockNumber() <<
"@" << nodeOffset.y() << coveredRegion;
2633 while (it != textFrame->begin() && it.currentBlock().layout() &&
2634 it.currentBlock().layout()->boundingRect().top() + nodeOffset.y() > viewport.top()) {
2635 nodeOffset = d->document->documentLayout()->blockBoundingRect(it.currentBlock()).topLeft();
2638 if (!it.currentBlock().layout())
2640 if (Q_LIKELY(it.currentBlock().layout())) {
2641 block = it.currentBlock();
2642 coveredRegion = block.layout()->boundingRect().adjusted(nodeOffset.x(), nodeOffset.y(), nodeOffset.x(), nodeOffset.y());
2643 firstDirtyPos = it.currentBlock().position();
2645 qCWarning(lcVP) <<
"failed to find a text block with layout during back-scrolling";
2648 qCDebug(lcVP) <<
"first block in viewport" << block.blockNumber() <<
"@" << nodeOffset.y() << coveredRegion;
2650 d->renderedRegion = coveredRegion;
2652 if (nodeOffset.y() > viewport.bottom()) {
2654 if (d->firstBlockInViewport >= 0 && d->firstBlockPastViewport < 0) {
2655 qCDebug(lcVP) <<
"first block past viewport" << viewport << block.blockNumber()
2656 <<
"@" << nodeOffset.y() <<
"total region rendered" << d->renderedRegion;
2657 d->firstBlockPastViewport = block.blockNumber();
2661 if (inView && !block.text().isEmpty() && coveredRegion.isValid()) {
2662 d->renderedRegion = d->renderedRegion.united(coveredRegion);
2665 if (!frames.isEmpty())
2666 viewport = viewport.united(d->renderedRegion);
2669 if (inView && d->firstBlockInViewport < 0)
2670 d->firstBlockInViewport = block.blockNumber();
2673 bool createdNodeInView =
false;
2675 if (!engine.hasContents()) {
2677 d->containsUnscalableGlyphs = d->containsUnscalableGlyphs
2678 || node->containsUnscalableGlyphs();
2679 if (!node->parent())
2680 d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart);
2682 node = d->createTextNode();
2683 createdNodeInView =
true;
2684 updateNodeTransform(node, nodeOffset);
2685 nodeStart = block.position();
2687 engine.addTextBlock(d->document, block, -nodeOffset, d->color, QColor(), selectionStart(), selectionEnd() - 1);
2688 currentNodeSize += block.length();
2691 if ((it.atEnd()) || block.next().position() >= firstCleanNode.startPos())
2693 QList<
int>::const_iterator lowerBound = std::lower_bound(frameBoundaries.constBegin(), frameBoundaries.constEnd(), block.next().position());
2694 if (node && (currentNodeSize > nodeBreakingSize || lowerBound == frameBoundaries.constEnd() || *lowerBound > nodeStart)) {
2695 currentNodeSize = 0;
2696 d->containsUnscalableGlyphs = d->containsUnscalableGlyphs
2697 || node->containsUnscalableGlyphs();
2698 if (!node->parent())
2699 d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart);
2700 if (!createdNodeInView)
2701 node = d->createTextNode();
2702 resetEngine(&engine, d->color, d->selectedTextColor, d->selectionColor, dpr);
2703 nodeStart = block.next().position();
2708 if (Q_LIKELY(node)) {
2709 d->containsUnscalableGlyphs = d->containsUnscalableGlyphs
2710 || node->containsUnscalableGlyphs();
2711 if (Q_LIKELY(!node->parent()))
2712 d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart);
2715 frameDecorationsEngine.addToSceneGraph(rootNode->frameDecorationsNode, QQuickText::Normal, QColor());
2717 rootNode->prependChildNode(rootNode->frameDecorationsNode);
2719 Q_ASSERT(nodeIterator == d->textNodeMap.end()
2720 || (nodeIterator->textNode() == firstCleanNode.textNode()
2721 && nodeIterator->startPos() == firstCleanNode.startPos()));
2723 if (firstCleanNode.textNode() !=
nullptr) {
2724 QPointF oldOffset = firstCleanNode.textNode()->matrix().map(QPointF(0,0));
2725 QPointF currentOffset = d->document->documentLayout()->blockBoundingRect(
2726 d->document->findBlock(firstCleanNode.startPos())).topLeft();
2727 QPointF delta = currentOffset - oldOffset;
2728 while (nodeIterator != d->textNodeMap.end()) {
2729 QMatrix4x4 transformMatrix = nodeIterator->textNode()->matrix();
2730 transformMatrix.translate(delta.x(), delta.y());
2731 nodeIterator->textNode()->setMatrix(transformMatrix);
2739 std::sort(d->textNodeMap.begin(), d->textNodeMap.end());
2742 if (d->cursorComponent ==
nullptr) {
2743 QSGInternalRectangleNode* cursor =
nullptr;
2744 if (!isReadOnly() && d->cursorVisible && d->control->cursorOn() && d->control->cursorVisible())
2745 cursor = d->sceneGraphContext()->createInternalRectangleNode(d->control->cursorRect(), d->color);
2746 rootNode->resetCursorNode(cursor);
2749 invalidateFontCaches();
2754void QQuickTextEdit::updatePolish()
2756 invalidateFontCaches();
2760
2761
2762
2763
2764
2765bool QQuickTextEdit::canPaste()
const
2767 Q_D(
const QQuickTextEdit);
2768 if (!d->canPasteValid) {
2769 const_cast<QQuickTextEditPrivate *>(d)->canPaste = d->control->canPaste();
2770 const_cast<QQuickTextEditPrivate *>(d)->canPasteValid =
true;
2776
2777
2778
2779
2780
2782bool QQuickTextEdit::canUndo()
const
2784 Q_D(
const QQuickTextEdit);
2785 return d->document->isUndoAvailable();
2789
2790
2791
2792
2793
2795bool QQuickTextEdit::canRedo()
const
2797 Q_D(
const QQuickTextEdit);
2798 return d->document->isRedoAvailable();
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813bool QQuickTextEdit::isInputMethodComposing()
const
2818 Q_D(
const QQuickTextEdit);
2819 return d->control->hasImState();
2823QQuickTextEditPrivate::ExtraData::ExtraData()
2824 : explicitTopPadding(
false)
2825 , explicitLeftPadding(
false)
2826 , explicitRightPadding(
false)
2827 , explicitBottomPadding(
false)
2828 , implicitResize(
true)
2832void QQuickTextEditPrivate::init()
2834 Q_Q(QQuickTextEdit);
2836#if QT_CONFIG(clipboard)
2837 if (QGuiApplication::clipboard()->supportsSelection())
2838 q->setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton);
2841 q->setAcceptedMouseButtons(Qt::LeftButton);
2844 q->setFlag(QQuickItem::ItemAcceptsInputMethod);
2846 q->setFlag(QQuickItem::ItemHasContents);
2848 q->setAcceptHoverEvents(
true);
2850 document =
new QTextDocument(q);
2851 ownsDocument =
true;
2852 auto *imageHandler =
new QQuickTextImageHandler(document);
2853 document->documentLayout()->registerHandler(QTextFormat::ImageObject, imageHandler);
2855 control =
new QQuickTextControl(document, q);
2856 control->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard | Qt::TextEditable);
2857 control->setAcceptRichText(
false);
2858 control->setCursorIsFocusIndicator(
true);
2859 q->setKeepMouseGrab(
true);
2861 qmlobject_connect(control, QQuickTextControl, SIGNAL(updateCursorRequest()), q, QQuickTextEdit, SLOT(updateCursor()));
2862 qmlobject_connect(control, QQuickTextControl, SIGNAL(selectionChanged()), q, QQuickTextEdit, SIGNAL(selectedTextChanged()));
2863 qmlobject_connect(control, QQuickTextControl, SIGNAL(selectionChanged()), q, QQuickTextEdit, SLOT(updateSelection()));
2864 qmlobject_connect(control, QQuickTextControl, SIGNAL(cursorPositionChanged()), q, QQuickTextEdit, SLOT(updateSelection()));
2865 qmlobject_connect(control, QQuickTextControl, SIGNAL(cursorPositionChanged()), q, QQuickTextEdit, SIGNAL(cursorPositionChanged()));
2866 qmlobject_connect(control, QQuickTextControl, SIGNAL(cursorRectangleChanged()), q, QQuickTextEdit, SLOT(moveCursorDelegate()));
2867 qmlobject_connect(control, QQuickTextControl, SIGNAL(linkActivated(QString)), q, QQuickTextEdit, SIGNAL(linkActivated(QString)));
2868 qmlobject_connect(control, QQuickTextControl, SIGNAL(overwriteModeChanged(
bool)), q, QQuickTextEdit, SIGNAL(overwriteModeChanged(
bool)));
2869 qmlobject_connect(control, QQuickTextControl, SIGNAL(textChanged()), q, QQuickTextEdit, SLOT(q_textChanged()));
2870 qmlobject_connect(control, QQuickTextControl, SIGNAL(preeditTextChanged()), q, QQuickTextEdit, SIGNAL(preeditTextChanged()));
2871#if QT_CONFIG(clipboard)
2872 qmlobject_connect(QGuiApplication::clipboard(), QClipboard, SIGNAL(dataChanged()), q, QQuickTextEdit, SLOT(q_canPasteChanged()));
2874 qmlobject_connect(document, QTextDocument, SIGNAL(undoAvailable(
bool)), q, QQuickTextEdit, SIGNAL(canUndoChanged()));
2875 qmlobject_connect(document, QTextDocument, SIGNAL(redoAvailable(
bool)), q, QQuickTextEdit, SIGNAL(canRedoChanged()));
2876 QObject::connect(document, &QTextDocument::contentsChange, q, &QQuickTextEdit::q_contentsChange);
2877 QObject::connect(document->documentLayout(), &QAbstractTextDocumentLayout::updateBlock, q, &QQuickTextEdit::invalidateBlock);
2878 QObject::connect(control, &QQuickTextControl::linkHovered, q, &QQuickTextEdit::q_linkHovered);
2879 QObject::connect(control, &QQuickTextControl::markerHovered, q, &QQuickTextEdit::q_markerHovered);
2881 document->setPageSize(QSizeF(0, 0));
2882 document->setDefaultFont(font);
2883 document->setDocumentMargin(textMargin);
2884 document->setUndoRedoEnabled(
false);
2885 document->setUndoRedoEnabled(
true);
2886 updateDefaultTextOption();
2887 document->setModified(
false);
2889#if QT_CONFIG(cursor)
2890 updateMouseCursorShape();
2892 setSizePolicy(QLayoutPolicy::Expanding, QLayoutPolicy::Expanding);
2895void QQuickTextEditPrivate::resetInputMethod()
2897 Q_Q(QQuickTextEdit);
2898 if (!q->isReadOnly() && q->hasActiveFocus() && qGuiApp)
2899 QGuiApplication::inputMethod()->reset();
2902void QQuickTextEdit::q_textChanged()
2904 Q_D(QQuickTextEdit);
2905 d->textCached =
false;
2906 for (QTextBlock it = d->document->begin(); it != d->document->end(); it = it.next()) {
2907 d->contentDirection = d->textDirection(it.text());
2908 if (d->contentDirection != Qt::LayoutDirectionAuto)
2911 d->determineHorizontalAlignment();
2912 d->updateDefaultTextOption();
2915 markDirtyNodesForRange(0, d->document->characterCount(), 0);
2916 if (isComponentComplete()) {
2918 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
2923 if (d->control->isBeingEdited())
2927void QQuickTextEdit::markDirtyNodesForRange(
int start,
int end,
int charDelta)
2929 Q_D(QQuickTextEdit);
2933 TextNode dummyNode(start);
2935 const TextNodeIterator textNodeMapBegin = d->textNodeMap.begin();
2936 const TextNodeIterator textNodeMapEnd = d->textNodeMap.end();
2938 TextNodeIterator it = std::lower_bound(textNodeMapBegin, textNodeMapEnd, dummyNode);
2941 if (it != textNodeMapBegin) {
2943 TextNode otherDummy(it->startPos());
2944 it = std::lower_bound(textNodeMapBegin, textNodeMapEnd, otherDummy);
2948 while (it != textNodeMapEnd) {
2949 if (it->startPos() <= end)
2952 it->moveStartPos(charDelta);
2959void QQuickTextEdit::q_contentsChange(
int pos,
int charsRemoved,
int charsAdded)
2961 Q_D(QQuickTextEdit);
2963 const int editRange = pos + qMax(charsAdded, charsRemoved);
2964 const int delta = charsAdded - charsRemoved;
2966 markDirtyNodesForRange(pos, editRange, delta);
2968 if (isComponentComplete()) {
2970 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
2975void QQuickTextEdit::moveCursorDelegate()
2977 Q_D(QQuickTextEdit);
2979 updateInputMethod();
2981 emit cursorRectangleChanged();
2984 QRectF cursorRect = cursorRectangle();
2985 d->cursorItem->setX(cursorRect.x());
2986 d->cursorItem->setY(cursorRect.y());
2987 d->cursorItem->setHeight(cursorRect.height());
2990void QQuickTextEdit::updateSelection()
2992 Q_D(QQuickTextEdit);
2995 if (d->control->textCursor().hasSelection() || d->hadSelection) {
2996 markDirtyNodesForRange(qMin(d->lastSelectionStart, d->control->textCursor().selectionStart()), qMax(d->control->textCursor().selectionEnd(), d->lastSelectionEnd), 0);
2997 if (isComponentComplete()) {
2999 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
3004 d->hadSelection = d->control->textCursor().hasSelection();
3006 if (d->lastSelectionStart != d->control->textCursor().selectionStart()) {
3007 d->lastSelectionStart = d->control->textCursor().selectionStart();
3008 emit selectionStartChanged();
3010 if (d->lastSelectionEnd != d->control->textCursor().selectionEnd()) {
3011 d->lastSelectionEnd = d->control->textCursor().selectionEnd();
3012 emit selectionEndChanged();
3016QRectF QQuickTextEdit::boundingRect()
const
3018 Q_D(
const QQuickTextEdit);
3020 QQuickTextUtil::alignedX(d->contentSize.width(), width(), effectiveHAlign()),
3022 d->contentSize.width(),
3023 d->contentSize.height());
3025 int cursorWidth = 1;
3028 else if (!d->document->isEmpty())
3032 r.setRight(r.right() + cursorWidth);
3037QRectF QQuickTextEdit::clipRect()
const
3039 Q_D(
const QQuickTextEdit);
3040 QRectF r = QQuickImplicitSizeItem::clipRect();
3041 int cursorWidth = 1;
3043 cursorWidth = d->cursorItem->width();
3044 if (!d->document->isEmpty())
3049 r.setRight(r.right() + cursorWidth);
3053qreal QQuickTextEditPrivate::getImplicitWidth()
const
3055 Q_Q(
const QQuickTextEdit);
3056 if (!requireImplicitWidth) {
3059 const_cast<QQuickTextEditPrivate*>(
this)->requireImplicitWidth =
true;
3060 const_cast<QQuickTextEdit*>(q)->updateSize();
3062 return implicitWidth;
3067void QQuickTextEdit::updateSize()
3069 Q_D(QQuickTextEdit);
3070 if (!isComponentComplete()) {
3078 if (!d->requireImplicitWidth) {
3079 emit implicitWidthChanged();
3081 if (d->requireImplicitWidth)
3084 if (d->requireImplicitWidth) {
3085 d->document->setTextWidth(-1);
3086 const qreal naturalWidth = d->document->idealWidth();
3087 const bool wasInLayout = d->inLayout;
3089 if (d->isImplicitResizeEnabled())
3090 setImplicitWidth(naturalWidth + leftPadding() + rightPadding());
3091 d->inLayout = wasInLayout;
3095 const qreal newTextWidth = width() - leftPadding() - rightPadding();
3096 if (d->document->textWidth() != newTextWidth)
3097 d->document->setTextWidth(newTextWidth);
3098 }
else if (d->wrapMode == NoWrap) {
3102 const qreal newTextWidth = d->document->idealWidth();
3103 if (d->document->textWidth() != newTextWidth)
3104 d->document->setTextWidth(newTextWidth);
3106 d->document->setTextWidth(-1);
3109 QFontMetricsF fm(d->font);
3110 const qreal newHeight = d->document->isEmpty() ? qCeil(fm.height()) : d->document->size().height();
3111 const qreal newWidth = d->document->idealWidth();
3113 if (d->isImplicitResizeEnabled()) {
3116 setImplicitSize(newWidth + leftPadding() + rightPadding(), newHeight + topPadding() + bottomPadding());
3118 setImplicitHeight(newHeight + topPadding() + bottomPadding());
3121 d->xoff = leftPadding() + qMax(qreal(0), QQuickTextUtil::alignedX(d->document->size().width(), width() - leftPadding() - rightPadding(), effectiveHAlign()));
3122 d->yoff = topPadding() + QQuickTextUtil::alignedY(d->document->size().height(), height() - topPadding() - bottomPadding(), d->vAlign);
3124 qreal baseline = fm.ascent();
3125 QTextBlock firstBlock = d->document->firstBlock();
3126 if (firstBlock.isValid() && firstBlock.layout() !=
nullptr && firstBlock.lineCount() > 0) {
3127 QTextLine firstLine = firstBlock.layout()->lineAt(0);
3128 if (firstLine.isValid())
3129 baseline = firstLine.ascent();
3132 setBaselineOffset(baseline + d->yoff + d->textMargin);
3134 QSizeF size(newWidth, newHeight);
3135 if (d->contentSize != size) {
3136 d->contentSize = size;
3138 const bool wasInResize = d->inResize;
3141 emit contentSizeChanged();
3142 d->inResize = wasInResize;
3147void QQuickTextEdit::updateWholeDocument()
3149 Q_D(QQuickTextEdit);
3150 if (!d->textNodeMap.isEmpty()) {
3151 for (TextNode &node : d->textNodeMap)
3155 if (isComponentComplete()) {
3157 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
3162void QQuickTextEdit::invalidateBlock(
const QTextBlock &block)
3164 Q_D(QQuickTextEdit);
3165 markDirtyNodesForRange(block.position(), block.position() + block.length(), 0);
3167 if (isComponentComplete()) {
3169 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
3174void QQuickTextEdit::updateCursor()
3176 Q_D(QQuickTextEdit);
3177 if (isComponentComplete() && isVisible()) {
3179 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
3184void QQuickTextEdit::q_linkHovered(
const QString &link)
3186 Q_D(QQuickTextEdit);
3187 emit linkHovered(link);
3188#if QT_CONFIG(cursor)
3189 if (link.isEmpty()) {
3190 d->updateMouseCursorShape();
3191 }
else if (cursor().shape() != Qt::PointingHandCursor) {
3192 setCursor(Qt::PointingHandCursor);
3197void QQuickTextEdit::q_markerHovered(
bool hovered)
3199 Q_D(QQuickTextEdit);
3200#if QT_CONFIG(cursor)
3202 d->updateMouseCursorShape();
3203 }
else if (cursor().shape() != Qt::PointingHandCursor) {
3204 setCursor(Qt::PointingHandCursor);
3209void QQuickTextEdit::q_updateAlignment()
3211 Q_D(QQuickTextEdit);
3212 if (d->determineHorizontalAlignment()) {
3213 d->updateDefaultTextOption();
3214 d->xoff = qMax(qreal(0), QQuickTextUtil::alignedX(d->document->size().width(), width(), effectiveHAlign()));
3215 moveCursorDelegate();
3219void QQuickTextEdit::updateTotalLines()
3221 Q_D(QQuickTextEdit);
3225 for (QTextBlock it = d->document->begin(); it != d->document->end(); it = it.next()) {
3226 QTextLayout *layout = it.layout();
3229 subLines += layout->lineCount()-1;
3232 int newTotalLines = d->document->lineCount() + subLines;
3233 if (d->lineCount != newTotalLines) {
3234 d->lineCount = newTotalLines;
3235 emit lineCountChanged();
3239void QQuickTextEditPrivate::updateDefaultTextOption()
3241 Q_Q(QQuickTextEdit);
3242 QTextOption opt = document->defaultTextOption();
3243 const Qt::Alignment oldAlignment = opt.alignment();
3244 Qt::LayoutDirection oldTextDirection = opt.textDirection();
3246 QQuickTextEdit::HAlignment horizontalAlignment = q->effectiveHAlign();
3247 if (contentDirection == Qt::RightToLeft) {
3248 if (horizontalAlignment == QQuickTextEdit::AlignLeft)
3249 horizontalAlignment = QQuickTextEdit::AlignRight;
3250 else if (horizontalAlignment == QQuickTextEdit::AlignRight)
3251 horizontalAlignment = QQuickTextEdit::AlignLeft;
3253 if (!hAlignImplicit)
3254 opt.setAlignment((Qt::Alignment)(
int)(horizontalAlignment | vAlign));
3256 opt.setAlignment(Qt::Alignment(vAlign));
3259 if (contentDirection == Qt::LayoutDirectionAuto) {
3260 opt.setTextDirection(qGuiApp->inputMethod()->inputDirection());
3264 opt.setTextDirection(contentDirection);
3267 QTextOption::WrapMode oldWrapMode = opt.wrapMode();
3268 opt.setWrapMode(QTextOption::WrapMode(wrapMode));
3270 bool oldUseDesignMetrics = opt.useDesignMetrics();
3271 opt.setUseDesignMetrics(renderType != QQuickTextEdit::NativeRendering);
3273 if (oldWrapMode != opt.wrapMode() || oldAlignment != opt.alignment()
3274 || oldTextDirection != opt.textDirection()
3275 || oldUseDesignMetrics != opt.useDesignMetrics()) {
3276 document->setDefaultTextOption(opt);
3280void QQuickTextEditPrivate::onDocumentStatusChanged()
3282 Q_ASSERT(quickDocument);
3283 switch (quickDocument->status()) {
3284 case QQuickTextDocument::Status::Loaded:
3285 case QQuickTextDocument::Status::Saved:
3286 switch (QQuickTextDocumentPrivate::get(quickDocument)->detectedFormat) {
3288 richText = (format == QQuickTextEdit::RichText || format == QQuickTextEdit::AutoText);
3289 markdownText =
false;
3291 case Qt::MarkdownText:
3293 markdownText = (format == QQuickTextEdit::MarkdownText || format == QQuickTextEdit::AutoText);
3297 markdownText =
false;
3308void QQuickTextEdit::focusInEvent(QFocusEvent *event)
3310 Q_D(QQuickTextEdit);
3311 d->handleFocusEvent(event);
3312 QQuickImplicitSizeItem::focusInEvent(event);
3315void QQuickTextEdit::focusOutEvent(QFocusEvent *event)
3317 Q_D(QQuickTextEdit);
3318 d->handleFocusEvent(event);
3319 QQuickImplicitSizeItem::focusOutEvent(event);
3322#if QT_VERSION < QT_VERSION_CHECK(7
, 0
, 0
)
3323bool QQuickTextEditPrivate::handleContextMenuEvent(QContextMenuEvent *event)
3325bool QQuickTextEdit::contextMenuEvent(QContextMenuEvent *event)
3328 Q_Q(QQuickTextEdit);
3329 QContextMenuEvent mapped(event->reason(), q->cursorRectangle().center().toPoint(),
3330 event->globalPos(), event->modifiers());
3331 const bool eventProcessed = QQuickItemPrivate::handleContextMenuEvent(&mapped);
3332 event->setAccepted(mapped.isAccepted());
3333 return eventProcessed;
3336void QQuickTextEditPrivate::handleFocusEvent(QFocusEvent *event)
3338 Q_Q(QQuickTextEdit);
3339 bool focus = event->type() == QEvent::FocusIn;
3340 if (!q->isReadOnly())
3341 q->setCursorVisible(focus);
3342 control->processEvent(event, QPointF(-xoff, -yoff));
3344 q->q_updateAlignment();
3346 if (focusOnPress && !q->isReadOnly())
3347 qGuiApp->inputMethod()->show();
3348 q->connect(QGuiApplication::inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
3349 q, SLOT(q_updateAlignment()));
3353 q->disconnect(QGuiApplication::inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
3354 q, SLOT(q_updateAlignment()));
3356 if (event->reason() != Qt::ActiveWindowFocusReason
3357 && event->reason() != Qt::PopupFocusReason
3358 && control->textCursor().hasSelection()
3359 && !persistentSelection)
3362 emit q->editingFinished();
3366void QQuickTextEditPrivate::addCurrentTextNodeToRoot(QQuickTextNodeEngine *engine, QSGTransformNode *root, QSGInternalTextNode *node, TextNodeIterator &it,
int startPos)
3368 engine->addToSceneGraph(node, QQuickText::Normal, QColor());
3369 it = textNodeMap.insert(it, TextNode(startPos, node));
3371 root->appendChildNode(node);
3372 ++renderedBlockCount;
3375QSGInternalTextNode *QQuickTextEditPrivate::createTextNode()
3377 Q_Q(QQuickTextEdit);
3378 QSGInternalTextNode* node = sceneGraphContext()->createInternalTextNode(sceneGraphRenderContext());
3379 node->setRenderType(QSGTextNode::RenderType(renderType));
3380 node->setFiltering(q->smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
3384void QQuickTextEdit::q_canPasteChanged()
3386 Q_D(QQuickTextEdit);
3387 bool old = d->canPaste;
3388 d->canPaste = d->control->canPaste();
3389 bool changed = old!=d->canPaste || !d->canPasteValid;
3390 d->canPasteValid =
true;
3392 emit canPasteChanged();
3396
3397
3398
3399
3400
3401
3403QString QQuickTextEdit::getText(
int start,
int end)
const
3405 Q_D(
const QQuickTextEdit);
3406 start = qBound(0, start, d->document->characterCount() - 1);
3407 end = qBound(0, end, d->document->characterCount() - 1);
3408 QTextCursor cursor(d->document);
3409 cursor.setPosition(start, QTextCursor::MoveAnchor);
3410 cursor.setPosition(end, QTextCursor::KeepAnchor);
3411#if QT_CONFIG(texthtmlparser)
3412 return d->richText || d->markdownText
3413 ? cursor.selectedText()
3414 : cursor.selection().toPlainText();
3416 return cursor.selection().toPlainText();
3421
3422
3423
3424
3425
3426
3428QString QQuickTextEdit::getFormattedText(
int start,
int end)
const
3430 Q_D(
const QQuickTextEdit);
3432 start = qBound(0, start, d->document->characterCount() - 1);
3433 end = qBound(0, end, d->document->characterCount() - 1);
3435 QTextCursor cursor(d->document);
3436 cursor.setPosition(start, QTextCursor::MoveAnchor);
3437 cursor.setPosition(end, QTextCursor::KeepAnchor);
3440#if QT_CONFIG(texthtmlparser)
3441 return cursor.selection().toHtml();
3443 return cursor.selection().toPlainText();
3445 }
else if (d->markdownText) {
3446#if QT_CONFIG(textmarkdownwriter)
3447 return cursor.selection().toMarkdown();
3449 return cursor.selection().toPlainText();
3452 return cursor.selection().toPlainText();
3457
3458
3459
3460
3461void QQuickTextEdit::insert(
int position,
const QString &text)
3463 Q_D(QQuickTextEdit);
3464 if (position < 0 || position >= d->document->characterCount())
3466 QTextCursor cursor(d->document);
3467 cursor.setPosition(position);
3468 d->richText = d->richText || (d->format == AutoText && Qt::mightBeRichText(text));
3470#if QT_CONFIG(texthtmlparser)
3471 cursor.insertHtml(text);
3473 cursor.insertText(text);
3475 }
else if (d->markdownText) {
3476#if QT_CONFIG(textmarkdownreader)
3477 cursor.insertMarkdown(text);
3479 cursor.insertText(text);
3482 cursor.insertText(text);
3484 d->control->updateCursorRectangle(
false);
3488
3489
3490
3491
3493void QQuickTextEdit::remove(
int start,
int end)
3495 Q_D(QQuickTextEdit);
3496 start = qBound(0, start, d->document->characterCount() - 1);
3497 end = qBound(0, end, d->document->characterCount() - 1);
3498 QTextCursor cursor(d->document);
3499 cursor.setPosition(start, QTextCursor::MoveAnchor);
3500 cursor.setPosition(end, QTextCursor::KeepAnchor);
3501 cursor.removeSelectedText();
3502 d->control->updateCursorRectangle(
false);
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3517QQuickTextDocument *QQuickTextEdit::textDocument()
3519 Q_D(QQuickTextEdit);
3520 if (!d->quickDocument) {
3521 d->quickDocument =
new QQuickTextDocument(
this);
3522 connect(d->quickDocument, &QQuickTextDocument::statusChanged, d->quickDocument,
3523 [d]() { d->onDocumentStatusChanged(); } );
3525 return d->quickDocument;
3528bool QQuickTextEditPrivate::isLinkHoveredConnected()
3530 Q_Q(QQuickTextEdit);
3531 IS_SIGNAL_CONNECTED(q, QQuickTextEdit, linkHovered, (
const QString &));
3534#if QT_CONFIG(cursor)
3535void QQuickTextEditPrivate::updateMouseCursorShape()
3537 Q_Q(QQuickTextEdit);
3538 q->setCursor(q->isReadOnly() && !q->selectByMouse() ? Qt::ArrowCursor : Qt::IBeamCursor);
3543
3544
3545
3546
3547
3548
3549
3550
3551
3554
3555
3556
3557
3558
3561
3562
3563
3564
3565
3566
3567
3568
3569
3572
3573
3574
3575
3576
3577
3578
3580QString QQuickTextEdit::hoveredLink()
const
3582 Q_D(
const QQuickTextEdit);
3583 if (
const_cast<QQuickTextEditPrivate *>(d)->isLinkHoveredConnected()) {
3584 return d->control->hoveredLink();
3586#if QT_CONFIG(cursor)
3587 if (QQuickWindow *wnd = window()) {
3588 QPointF pos = QCursor::pos(wnd->screen()) - wnd->position() - mapToScene(QPointF(0, 0));
3589 return d->control->anchorAt(pos);
3596void QQuickTextEdit::hoverEnterEvent(QHoverEvent *event)
3598 Q_D(QQuickTextEdit);
3599 if (d->isLinkHoveredConnected())
3600 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
3604void QQuickTextEdit::hoverMoveEvent(QHoverEvent *event)
3606 Q_D(QQuickTextEdit);
3607 if (d->isLinkHoveredConnected())
3608 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
3612void QQuickTextEdit::hoverLeaveEvent(QHoverEvent *event)
3614 Q_D(QQuickTextEdit);
3615 if (d->isLinkHoveredConnected())
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 <<
')';