69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
91
92
93
94
95
96
102#if !defined(QQUICKTEXT_LARGETEXT_THRESHOLD)
103 #define QQUICKTEXT_LARGETEXT_THRESHOLD 10000
109 class RootNode :
public QSGTransformNode
112 RootNode() : cursorNode(
nullptr), frameDecorationsNode(
nullptr)
115 void resetFrameDecorations(QSGInternalTextNode* newNode)
117 if (frameDecorationsNode) {
118 removeChildNode(frameDecorationsNode);
119 delete frameDecorationsNode;
121 frameDecorationsNode = newNode;
122 newNode->setFlag(QSGNode::OwnedByParent);
125 void resetCursorNode(QSGInternalRectangleNode* newNode)
128 removeChildNode(cursorNode);
130 cursorNode = newNode;
132 appendChildNode(cursorNode);
133 cursorNode->setFlag(QSGNode::OwnedByParent);
137 QSGInternalRectangleNode *cursorNode;
138 QSGInternalTextNode* frameDecorationsNode;
143QQuickTextEdit::QQuickTextEdit(QQuickItem *parent)
144: QQuickImplicitSizeItem(*(
new QQuickTextEditPrivate), parent)
150QQuickTextEdit::~QQuickTextEdit()
153 qDeleteAll(d->pixmapsInProgress);
156QQuickTextEdit::QQuickTextEdit(QQuickTextEditPrivate &dd, QQuickItem *parent)
157: QQuickImplicitSizeItem(dd, parent)
163QString QQuickTextEdit::text()
const
165 Q_D(
const QQuickTextEdit);
166 if (!d->textCached && isComponentComplete()) {
167 QQuickTextEditPrivate *d =
const_cast<QQuickTextEditPrivate *>(d_func());
168#if QT_CONFIG(texthtmlparser)
170 d->text = d->control->toHtml();
173#if QT_CONFIG(textmarkdownwriter)
175 d->text = d->control->toMarkdown();
178 d->text = d->control->toPlainText();
179 d->textCached =
true;
185
186
187
188
189
190
191
192
195
196
197
198
199
200
201
202
206
207
208
209
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
233
234
235
236
239
240
241
242
245
246
247
248
251
252
253
254
257
258
259
260
261
262
263
264
267
268
269
270
271
272
273
276
277
278
279
280
281
282
283
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
303
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
334
335
336
337
338
339
340
341
342
343
344
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
364
365
366
367
368
371
372
373
374
375
378
379
380
381
382
385
386
387
388
389
392
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
420void QQuickTextEdit::setText(
const QString &text)
423 if (QQuickTextEdit::text() == text)
426 d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(text));
427 d->markdownText = d->format == MarkdownText;
428 if (!isComponentComplete()) {
430 }
else if (d->richText) {
431#if QT_CONFIG(texthtmlparser)
432 d->control->setHtml(text);
434 d->control->setPlainText(text);
436 }
else if (d->markdownText) {
437 d->control->setMarkdownText(text);
439 d->control->setPlainText(text);
441 setFlag(QQuickItem::ItemObservesViewport, text.size() > QQuickTextEditPrivate::largeTextSizeThreshold);
444void QQuickTextEdit::invalidate()
446 QMetaObject::invokeMethod(
this, &QQuickTextEdit::q_invalidate);
449void QQuickTextEdit::q_invalidate()
452 if (isComponentComplete()) {
453 if (d->document !=
nullptr)
454 d->document->markContentsDirty(0, d->document->characterCount());
455 invalidateFontCaches();
456 d->updateType = QQuickTextEditPrivate::UpdateAll;
462
463
464
465
466
467
468
469
470
471
472
473QString QQuickTextEdit::preeditText()
const
475 Q_D(
const QQuickTextEdit);
476 return d->control->preeditText();
480
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
533QQuickTextEdit::TextFormat QQuickTextEdit::textFormat()
const
535 Q_D(
const QQuickTextEdit);
539void QQuickTextEdit::setTextFormat(TextFormat format)
542 if (format == d->format)
545 auto mightBeRichText = [
this]() {
546 return Qt::mightBeRichText(text());
549 auto findSourceFormat = [d, mightBeRichText](Qt::TextFormat detectedFormat) {
550 if (d->format == PlainText)
552 if (d->richText)
return RichText;
553 if (d->markdownText)
return MarkdownText;
554 if (detectedFormat == Qt::AutoText && mightBeRichText())
559 auto findDestinationFormat = [format, mightBeRichText](Qt::TextFormat detectedFormat, TextFormat sourceFormat) {
560 if (format == AutoText) {
561 if (detectedFormat == Qt::MarkdownText || (detectedFormat == Qt::AutoText && sourceFormat == MarkdownText))
563 if (detectedFormat == Qt::RichText || (detectedFormat == Qt::AutoText && (sourceFormat == RichText || mightBeRichText())))
570 bool textCachedChanged =
false;
571 bool converted =
false;
573 if (isComponentComplete()) {
574 Qt::TextFormat detectedFormat = Qt::AutoText;
575 if (d->quickDocument) {
578 detectedFormat = QQuickTextDocumentPrivate::get(d->quickDocument)->detectedFormat;
581 const TextFormat sourceFormat = findSourceFormat(detectedFormat);
582 const TextFormat destinationFormat = findDestinationFormat(detectedFormat, sourceFormat);
584 d->richText = destinationFormat == RichText;
585 d->markdownText = destinationFormat == MarkdownText;
588 if (format != PlainText && (sourceFormat != destinationFormat)) {
589 d->textCached =
false;
590 textCachedChanged =
true;
593 switch (destinationFormat) {
595#if QT_CONFIG(texthtmlparser)
596 if (sourceFormat == RichText) {
600 d->control->setPlainText(d->textCached ? d->text : d->control->toHtml());
604#if QT_CONFIG(textmarkdownwriter) && QT_CONFIG(textmarkdownreader)
605 if (sourceFormat == MarkdownText) {
609 d->control->setPlainText(d->textCached ? d->text : d->control->toMarkdown());
615#if QT_CONFIG(texthtmlparser)
616 switch (sourceFormat) {
619 d->control->setHtml(d->control->toHtml());
625 d->control->setHtml(d->textCached ? d->text : d->control->toPlainText());
635#if QT_CONFIG(textmarkdownwriter) && QT_CONFIG(textmarkdownreader)
636 switch (sourceFormat) {
639 d->control->setMarkdownText(d->control->toMarkdown());
645 d->control->setMarkdownText(d->textCached ? d->text : d->control->toPlainText());
661 d->richText = format == RichText || (format == AutoText && (d->richText || mightBeRichText()));
662 d->markdownText = format == MarkdownText;
665 qCDebug(lcTextEdit) << d->format <<
"->" << format
666 <<
"rich?" << d->richText <<
"md?" << d->markdownText
667 <<
"converted?" << converted <<
"cache invalidated?" << textCachedChanged;
670 d->control->setAcceptRichText(d->format != PlainText);
671 emit textFormatChanged(d->format);
672 if (textCachedChanged)
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701QQuickTextEdit::RenderType QQuickTextEdit::renderType()
const
703 Q_D(
const QQuickTextEdit);
704 return d->renderType;
707void QQuickTextEdit::setRenderType(QQuickTextEdit::RenderType renderType)
710 if (d->renderType == renderType)
713 d->renderType = renderType;
714 emit renderTypeChanged();
715 d->updateDefaultTextOption();
717 if (isComponentComplete())
721QFont QQuickTextEdit::font()
const
723 Q_D(
const QQuickTextEdit);
724 return d->sourceFont;
727void QQuickTextEdit::setFont(
const QFont &font)
730 if (d->sourceFont == font)
733 d->sourceFont = font;
734 QFont oldFont = d->font;
736 if (d->font.pointSizeF() != -1) {
738 qreal size = qRound(d->font.pointSizeF()*2.0);
739 d->font.setPointSizeF(size/2.0);
742 if (oldFont != d->font) {
743 d->document->setDefaultFont(d->font);
745 d->cursorItem->setHeight(QFontMetrics(d->font).height());
746 moveCursorDelegate();
749 updateWholeDocument();
751 updateInputMethod(Qt::ImCursorRectangle | Qt::ImAnchorRectangle | Qt::ImFont);
754 emit fontChanged(d->sourceFont);
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772QColor QQuickTextEdit::color()
const
774 Q_D(
const QQuickTextEdit);
778void QQuickTextEdit::setColor(
const QColor &color)
781 if (d->color == color)
785 updateWholeDocument();
786 emit colorChanged(d->color);
790
791
792
793
794QColor QQuickTextEdit::selectionColor()
const
796 Q_D(
const QQuickTextEdit);
797 return d->selectionColor;
800void QQuickTextEdit::setSelectionColor(
const QColor &color)
803 if (d->selectionColor == color)
806 d->selectionColor = color;
807 updateWholeDocument();
808 emit selectionColorChanged(d->selectionColor);
812
813
814
815
816QColor QQuickTextEdit::selectedTextColor()
const
818 Q_D(
const QQuickTextEdit);
819 return d->selectedTextColor;
822void QQuickTextEdit::setSelectedTextColor(
const QColor &color)
825 if (d->selectedTextColor == color)
828 d->selectedTextColor = color;
829 updateWholeDocument();
830 emit selectedTextColorChanged(d->selectedTextColor);
834
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
865QQuickTextEdit::HAlignment QQuickTextEdit::hAlign()
const
867 Q_D(
const QQuickTextEdit);
871void QQuickTextEdit::setHAlign(HAlignment align)
875 if (d->setHAlign(align,
true) && isComponentComplete()) {
876 d->updateDefaultTextOption();
878 updateWholeDocument();
882void QQuickTextEdit::resetHAlign()
885 d->hAlignImplicit =
true;
886 if (d->determineHorizontalAlignment() && isComponentComplete()) {
887 d->updateDefaultTextOption();
892QQuickTextEdit::HAlignment QQuickTextEdit::effectiveHAlign()
const
894 Q_D(
const QQuickTextEdit);
895 QQuickTextEdit::HAlignment effectiveAlignment = d->hAlign;
896 if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
898 case QQuickTextEdit::AlignLeft:
899 effectiveAlignment = QQuickTextEdit::AlignRight;
901 case QQuickTextEdit::AlignRight:
902 effectiveAlignment = QQuickTextEdit::AlignLeft;
908 return effectiveAlignment;
911bool QQuickTextEditPrivate::setHAlign(QQuickTextEdit::HAlignment align,
bool forceAlign)
914 if (hAlign == align && !forceAlign)
917 const bool wasImplicit = hAlignImplicit;
918 const auto oldEffectiveHAlign = q->effectiveHAlign();
920 hAlignImplicit = !forceAlign;
921 if (hAlign != align) {
923 emit q->horizontalAlignmentChanged(align);
926 if (q->effectiveHAlign() != oldEffectiveHAlign) {
927 emit q->effectiveHorizontalAlignmentChanged();
931 if (forceAlign && wasImplicit) {
934 emit q->effectiveHorizontalAlignmentChanged();
939Qt::LayoutDirection QQuickTextEditPrivate::textDirection(
const QString &text)
const
941 const QChar *character = text.constData();
942 while (!character->isNull()) {
943 switch (character->direction()) {
945 return Qt::LeftToRight;
949 return Qt::RightToLeft;
955 return Qt::LayoutDirectionAuto;
958bool QQuickTextEditPrivate::determineHorizontalAlignment()
961 if (!hAlignImplicit || !q->isComponentComplete())
964 Qt::LayoutDirection direction = contentDirection;
966 if (direction == Qt::LayoutDirectionAuto) {
967 QTextBlock block = control->textCursor().block();
970 direction = textDirection(block.layout()->preeditAreaText());
972 if (direction == Qt::LayoutDirectionAuto)
973 direction = qGuiApp->inputMethod()->inputDirection();
976 const auto implicitHAlign = direction == Qt::RightToLeft ?
977 QQuickTextEdit::AlignRight : QQuickTextEdit::AlignLeft;
978 return setHAlign(implicitHAlign);
981void QQuickTextEditPrivate::mirrorChange()
984 if (q->isComponentComplete()) {
985 if (!hAlignImplicit && (hAlign == QQuickTextEdit::AlignRight || hAlign == QQuickTextEdit::AlignLeft)) {
986 updateDefaultTextOption();
988 q->updateWholeDocument();
989 emit q->effectiveHorizontalAlignmentChanged();
994bool QQuickTextEditPrivate::transformChanged(QQuickItem *transformedItem)
997 qCDebug(lcVP) << q <<
"sees that" << transformedItem <<
"moved in VP" << q->clipRect();
1002 if (flags & QQuickItem::ItemObservesViewport) {
1003 if (QQuickItem *viewport = q->viewportItem()) {
1004 QRectF vp = q->mapRectFromItem(viewport, viewport->clipRect());
1005 if (!(vp.top() > renderedRegion.top() && vp.bottom() < renderedRegion.bottom())) {
1006 qCDebug(lcVP) <<
"viewport" << vp <<
"now goes beyond rendered region" << renderedRegion <<
"; updating";
1007 q->updateWholeDocument();
1009 const bool textCursorVisible = cursorVisible && q->cursorRectangle().intersects(vp);
1011 cursorItem->setVisible(textCursorVisible);
1013 control->setCursorVisible(textCursorVisible);
1016 return QQuickImplicitSizeItemPrivate::transformChanged(transformedItem);
1020Qt::InputMethodHints QQuickTextEditPrivate::effectiveInputMethodHints()
const
1022 return inputMethodHints | Qt::ImhMultiLine;
1026#if QT_CONFIG(accessibility)
1027void QQuickTextEditPrivate::accessibilityActiveChanged(
bool active)
1032 Q_Q(QQuickTextEdit);
1033 if (QQuickAccessibleAttached *accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(
1034 qmlAttachedPropertiesObject<QQuickAccessibleAttached>(q,
true))) {
1035 accessibleAttached->setRole(effectiveAccessibleRole());
1036 accessibleAttached->set_readOnly(q->isReadOnly());
1040QAccessible::Role QQuickTextEditPrivate::accessibleRole()
const
1042 return QAccessible::EditableText;
1046void QQuickTextEditPrivate::setTopPadding(qreal value,
bool reset)
1048 Q_Q(QQuickTextEdit);
1049 qreal oldPadding = q->topPadding();
1050 if (!reset || extra.isAllocated()) {
1051 extra.value().topPadding = value;
1052 extra.value().explicitTopPadding = !reset;
1054 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
1056 q->updateWholeDocument();
1057 emit q->topPaddingChanged();
1061void QQuickTextEditPrivate::setLeftPadding(qreal value,
bool reset)
1063 Q_Q(QQuickTextEdit);
1064 qreal oldPadding = q->leftPadding();
1065 if (!reset || extra.isAllocated()) {
1066 extra.value().leftPadding = value;
1067 extra.value().explicitLeftPadding = !reset;
1069 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
1071 q->updateWholeDocument();
1072 emit q->leftPaddingChanged();
1076void QQuickTextEditPrivate::setRightPadding(qreal value,
bool reset)
1078 Q_Q(QQuickTextEdit);
1079 qreal oldPadding = q->rightPadding();
1080 if (!reset || extra.isAllocated()) {
1081 extra.value().rightPadding = value;
1082 extra.value().explicitRightPadding = !reset;
1084 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
1086 q->updateWholeDocument();
1087 emit q->rightPaddingChanged();
1091void QQuickTextEditPrivate::setBottomPadding(qreal value,
bool reset)
1093 Q_Q(QQuickTextEdit);
1094 qreal oldPadding = q->bottomPadding();
1095 if (!reset || extra.isAllocated()) {
1096 extra.value().bottomPadding = value;
1097 extra.value().explicitBottomPadding = !reset;
1099 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
1101 q->updateWholeDocument();
1102 emit q->bottomPaddingChanged();
1106bool QQuickTextEditPrivate::isImplicitResizeEnabled()
const
1108 return !extra.isAllocated() || extra->implicitResize;
1111void QQuickTextEditPrivate::setImplicitResizeEnabled(
bool enabled)
1114 extra.value().implicitResize =
false;
1115 else if (extra.isAllocated())
1116 extra->implicitResize =
true;
1119QQuickTextEdit::VAlignment QQuickTextEdit::vAlign()
const
1121 Q_D(
const QQuickTextEdit);
1125void QQuickTextEdit::setVAlign(QQuickTextEdit::VAlignment alignment)
1127 Q_D(QQuickTextEdit);
1128 if (alignment == d->vAlign)
1130 d->vAlign = alignment;
1131 d->updateDefaultTextOption();
1133 moveCursorDelegate();
1134 emit verticalAlignmentChanged(d->vAlign);
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157QQuickTextEdit::WrapMode QQuickTextEdit::wrapMode()
const
1159 Q_D(
const QQuickTextEdit);
1163void QQuickTextEdit::setWrapMode(WrapMode mode)
1165 Q_D(QQuickTextEdit);
1166 if (mode == d->wrapMode)
1169 d->updateDefaultTextOption();
1171 emit wrapModeChanged();
1175
1176
1177
1178
1179int QQuickTextEdit::lineCount()
const
1181 Q_D(
const QQuickTextEdit);
1182 return d->lineCount;
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1197int QQuickTextEdit::length()
const
1199 Q_D(
const QQuickTextEdit);
1201 return qMax(0, d->document->characterCount() - 1);
1205
1206
1207
1208
1209
1210qreal QQuickTextEdit::contentWidth()
const
1212 Q_D(
const QQuickTextEdit);
1213 return d->contentSize.width();
1217
1218
1219
1220
1221
1222qreal QQuickTextEdit::contentHeight()
const
1224 Q_D(
const QQuickTextEdit);
1225 return d->contentSize.height();
1229
1230
1231
1232
1233
1234
1235
1237QUrl QQuickTextEdit::baseUrl()
const
1239 Q_D(
const QQuickTextEdit);
1240 if (d->baseUrl.isEmpty()) {
1241 if (QQmlContext *context = qmlContext(
this))
1242 const_cast<QQuickTextEditPrivate *>(d)->baseUrl = context->baseUrl();
1247void QQuickTextEdit::setBaseUrl(
const QUrl &url)
1249 Q_D(QQuickTextEdit);
1250 if (baseUrl() != url) {
1253 d->document->setBaseUrl(url);
1254 emit baseUrlChanged();
1258void QQuickTextEdit::resetBaseUrl()
1260 if (QQmlContext *context = qmlContext(
this))
1261 setBaseUrl(context->baseUrl());
1267
1268
1269
1270
1271
1272
1273QRectF QQuickTextEdit::positionToRectangle(
int pos)
const
1275 Q_D(
const QQuickTextEdit);
1276 QTextCursor c(d->document);
1278 return d->control->cursorRect(c).translated(d->xoff, d->yoff);
1283
1284
1285
1286
1287
1288
1289
1290int QQuickTextEdit::positionAt(qreal x, qreal y)
const
1292 Q_D(
const QQuickTextEdit);
1296 int r = d->document->documentLayout()->hitTest(QPointF(x, y), Qt::FuzzyHit);
1298 QTextCursor cursor = d->control->textCursor();
1299 if (r > cursor.position()) {
1303 QTextLayout *layout = cursor.block().layout();
1304 const int preeditLength = layout
1305 ? layout->preeditAreaText().size()
1307 if (preeditLength > 0
1308 && d->document->documentLayout()->blockBoundingRect(cursor.block()).contains(x, y)) {
1309 r = r > cursor.position() + preeditLength
1311 : cursor.position();
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328QQuickTextSelection *QQuickTextEdit::cursorSelection()
const
1330 Q_D(
const QQuickTextEdit);
1331 if (!d->cursorSelection)
1332 d->cursorSelection =
new QQuickTextSelection(
const_cast<QQuickTextEdit *>(
this));
1333 return d->cursorSelection;
1337
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
1372void QQuickTextEdit::moveCursorSelection(
int pos)
1375 Q_D(QQuickTextEdit);
1376 QTextCursor cursor = d->control->textCursor();
1377 if (cursor.position() == pos)
1379 cursor.setPosition(pos, QTextCursor::KeepAnchor);
1380 d->control->setTextCursor(cursor);
1383void QQuickTextEdit::moveCursorSelection(
int pos, SelectionMode mode)
1385 Q_D(QQuickTextEdit);
1386 QTextCursor cursor = d->control->textCursor();
1387 if (cursor.position() == pos)
1389 if (mode == SelectCharacters) {
1390 cursor.setPosition(pos, QTextCursor::KeepAnchor);
1391 }
else if (cursor.anchor() < pos || (cursor.anchor() == pos && cursor.position() < pos)) {
1392 if (cursor.anchor() > cursor.position()) {
1393 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1394 cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
1395 if (cursor.position() == cursor.anchor())
1396 cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::MoveAnchor);
1398 cursor.setPosition(cursor.position(), QTextCursor::MoveAnchor);
1400 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1401 cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor);
1404 cursor.setPosition(pos, QTextCursor::KeepAnchor);
1405 cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
1406 if (cursor.position() != pos)
1407 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
1408 }
else if (cursor.anchor() > pos || (cursor.anchor() == pos && cursor.position() > pos)) {
1409 if (cursor.anchor() < cursor.position()) {
1410 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1411 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::MoveAnchor);
1413 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1414 cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
1415 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
1416 if (cursor.position() != cursor.anchor()) {
1417 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1418 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::MoveAnchor);
1422 cursor.setPosition(pos, QTextCursor::KeepAnchor);
1423 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
1424 if (cursor.position() != pos) {
1425 cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
1426 cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
1429 d->control->setTextCursor(cursor);
1433
1434
1435
1436
1437
1438
1439bool QQuickTextEdit::isCursorVisible()
const
1441 Q_D(
const QQuickTextEdit);
1442 return d->cursorVisible;
1445void QQuickTextEdit::setCursorVisible(
bool on)
1447 Q_D(QQuickTextEdit);
1448 if (d->cursorVisible == on)
1450 d->cursorVisible = on;
1451 if (on && isComponentComplete())
1452 QQuickTextUtil::createCursor(d);
1453 if (!on && !d->persistentSelection)
1454 d->control->setCursorIsFocusIndicator(
true);
1455 d->control->setCursorVisible(on);
1456 emit cursorVisibleChanged(d->cursorVisible);
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471int QQuickTextEdit::cursorPosition()
const
1473 Q_D(
const QQuickTextEdit);
1474 return d->control->textCursor().position();
1477void QQuickTextEdit::setCursorPosition(
int pos)
1479 Q_D(QQuickTextEdit);
1480 if (pos < 0 || pos >= d->document->characterCount())
1482 QTextCursor cursor = d->control->textCursor();
1483 if (cursor.position() == pos && cursor.anchor() == pos)
1485 cursor.setPosition(pos);
1486 d->control->setTextCursor(cursor);
1487 d->control->updateCursorRectangle(
true);
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503QQmlComponent* QQuickTextEdit::cursorDelegate()
const
1505 Q_D(
const QQuickTextEdit);
1506 return d->cursorComponent;
1509void QQuickTextEdit::setCursorDelegate(QQmlComponent* c)
1511 Q_D(QQuickTextEdit);
1512 QQuickTextUtil::setCursorDelegate(d, c);
1515void QQuickTextEdit::createCursor()
1517 Q_D(QQuickTextEdit);
1518 d->cursorPending =
true;
1519 QQuickTextUtil::createCursor(d);
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532int QQuickTextEdit::selectionStart()
const
1534 Q_D(
const QQuickTextEdit);
1535 return d->control->textCursor().selectionStart();
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548int QQuickTextEdit::selectionEnd()
const
1550 Q_D(
const QQuickTextEdit);
1551 return d->control->textCursor().selectionEnd();
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568QString QQuickTextEdit::selectedText()
const
1570 Q_D(
const QQuickTextEdit);
1571#if QT_CONFIG(texthtmlparser)
1572 return d->richText || d->markdownText
1573 ? d->control->textCursor().selectedText()
1574 : d->control->textCursor().selection().toPlainText();
1576 return d->control->textCursor().selection().toPlainText();
1581
1582
1583
1584
1585
1586bool QQuickTextEdit::focusOnPress()
const
1588 Q_D(
const QQuickTextEdit);
1589 return d->focusOnPress;
1592void QQuickTextEdit::setFocusOnPress(
bool on)
1594 Q_D(QQuickTextEdit);
1595 if (d->focusOnPress == on)
1597 d->focusOnPress = on;
1598 emit activeFocusOnPressChanged(d->focusOnPress);
1602
1603
1604
1605
1606
1607bool QQuickTextEdit::persistentSelection()
const
1609 Q_D(
const QQuickTextEdit);
1610 return d->persistentSelection;
1613void QQuickTextEdit::setPersistentSelection(
bool on)
1615 Q_D(QQuickTextEdit);
1616 if (d->persistentSelection == on)
1618 d->persistentSelection = on;
1619 emit persistentSelectionChanged(d->persistentSelection);
1623
1624
1625
1626
1627qreal QQuickTextEdit::textMargin()
const
1629 Q_D(
const QQuickTextEdit);
1630 return d->textMargin;
1633void QQuickTextEdit::setTextMargin(qreal margin)
1635 Q_D(QQuickTextEdit);
1636 if (d->textMargin == margin)
1638 d->textMargin = margin;
1639 d->document->setDocumentMargin(d->textMargin);
1640 emit textMarginChanged(d->textMargin);
1644
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
1680Qt::InputMethodHints QQuickTextEdit::inputMethodHints()
const
1685 Q_D(
const QQuickTextEdit);
1686 return d->inputMethodHints;
1690void QQuickTextEdit::setInputMethodHints(Qt::InputMethodHints hints)
1695 Q_D(QQuickTextEdit);
1697 if (hints == d->inputMethodHints)
1700 d->inputMethodHints = hints;
1701 updateInputMethod(Qt::ImHints);
1702 emit inputMethodHintsChanged();
1706void QQuickTextEdit::geometryChange(
const QRectF &newGeometry,
const QRectF &oldGeometry)
1708 Q_D(QQuickTextEdit);
1709 if (!d->inLayout && ((newGeometry.width() != oldGeometry.width())
1710 || (newGeometry.height() != oldGeometry.height()))) {
1712 updateWholeDocument();
1713 if (widthValid() || heightValid())
1714 moveCursorDelegate();
1716 QQuickImplicitSizeItem::geometryChange(newGeometry, oldGeometry);
1719void QQuickTextEdit::itemChange(ItemChange change,
const ItemChangeData &value)
1721 Q_D(QQuickTextEdit);
1724 case ItemDevicePixelRatioHasChanged:
1725 if (d->containsUnscalableGlyphs) {
1730 updateWholeDocument();
1737 QQuickImplicitSizeItem::itemChange(change, value);
1741
1742
1743
1744void QQuickTextEdit::componentComplete()
1746 Q_D(QQuickTextEdit);
1747 QQuickImplicitSizeItem::componentComplete();
1749 const QUrl url = baseUrl();
1750 const QQmlContext *context = qmlContext(
this);
1751 d->document->setBaseUrl(context ? context->resolvedUrl(url) : url);
1752 if (!d->text.isEmpty()) {
1753#if QT_CONFIG(texthtmlparser)
1755 d->control->setHtml(d->text);
1758#if QT_CONFIG(textmarkdownreader)
1759 if (d->markdownText)
1760 d->control->setMarkdownText(d->text);
1763 d->control->setPlainText(d->text);
1767 d->determineHorizontalAlignment();
1768 d->updateDefaultTextOption();
1772 if (d->cursorComponent && isCursorVisible())
1773 QQuickTextUtil::createCursor(d);
1776#if QT_CONFIG(accessibility)
1777 if (QAccessible::isActive())
1778 d->accessibilityActiveChanged(
true);
1782int QQuickTextEdit::resourcesLoading()
const
1784 Q_D(
const QQuickTextEdit);
1785 return d->pixmapsInProgress.size();
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802bool QQuickTextEdit::selectByKeyboard()
const
1804 Q_D(
const QQuickTextEdit);
1805 if (d->selectByKeyboardSet)
1806 return d->selectByKeyboard;
1807 return !isReadOnly();
1810void QQuickTextEdit::setSelectByKeyboard(
bool on)
1812 Q_D(QQuickTextEdit);
1813 bool was = selectByKeyboard();
1814 if (!d->selectByKeyboardSet || was != on) {
1815 d->selectByKeyboardSet =
true;
1816 d->selectByKeyboard = on;
1818 d->control->setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByKeyboard);
1820 d->control->setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByKeyboard);
1821 emit selectByKeyboardChanged(on);
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848bool QQuickTextEdit::selectByMouse()
const
1850 Q_D(
const QQuickTextEdit);
1851 return d->selectByMouse;
1854void QQuickTextEdit::setSelectByMouse(
bool on)
1856 Q_D(QQuickTextEdit);
1857 if (d->selectByMouse == on)
1860 d->selectByMouse = on;
1861 setKeepMouseGrab(on);
1863 d->control->setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByMouse);
1865 d->control->setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByMouse);
1867#if QT_CONFIG(cursor)
1868 d->updateMouseCursorShape();
1870 emit selectByMouseChanged(on);
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883QQuickTextEdit::SelectionMode QQuickTextEdit::mouseSelectionMode()
const
1885 Q_D(
const QQuickTextEdit);
1886 return d->mouseSelectionMode;
1889void QQuickTextEdit::setMouseSelectionMode(SelectionMode mode)
1891 Q_D(QQuickTextEdit);
1892 if (d->mouseSelectionMode != mode) {
1893 d->mouseSelectionMode = mode;
1894 d->control->setWordSelectionEnabled(mode == SelectWords);
1895 emit mouseSelectionModeChanged(mode);
1900
1901
1902
1903
1904
1905
1906
1907void QQuickTextEdit::setReadOnly(
bool r)
1909 Q_D(QQuickTextEdit);
1910 if (r == isReadOnly())
1914 setFlag(QQuickItem::ItemAcceptsInputMethod, !r);
1916 Qt::TextInteractionFlags flags = Qt::LinksAccessibleByMouse;
1917 if (d->selectByMouse)
1918 flags = flags | Qt::TextSelectableByMouse;
1919 if (d->selectByKeyboardSet && d->selectByKeyboard)
1920 flags = flags | Qt::TextSelectableByKeyboard;
1921 else if (!d->selectByKeyboardSet && !r)
1922 flags = flags | Qt::TextSelectableByKeyboard;
1924 flags = flags | Qt::TextEditable;
1925 d->control->setTextInteractionFlags(flags);
1926 d->control->moveCursor(QTextCursor::End);
1929 updateInputMethod(Qt::ImEnabled);
1931#if QT_CONFIG(cursor)
1932 d->updateMouseCursorShape();
1934 q_canPasteChanged();
1935 emit readOnlyChanged(r);
1936 if (!d->selectByKeyboardSet)
1937 emit selectByKeyboardChanged(!r);
1939 setCursorVisible(
false);
1940 }
else if (hasActiveFocus()) {
1941 setCursorVisible(
true);
1944#if QT_CONFIG(accessibility)
1945 if (QAccessible::isActive()) {
1946 if (QQuickAccessibleAttached *accessibleAttached = QQuickAccessibleAttached::attachedProperties(
this))
1947 accessibleAttached->set_readOnly(r);
1952bool QQuickTextEdit::isReadOnly()
const
1954 Q_D(
const QQuickTextEdit);
1955 return !(d->control->textInteractionFlags() & Qt::TextEditable);
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968QRectF QQuickTextEdit::cursorRectangle()
const
1970 Q_D(
const QQuickTextEdit);
1971 return d->control->cursorRect().translated(d->xoff, d->yoff);
1974bool QQuickTextEdit::event(QEvent *event)
1976 Q_D(QQuickTextEdit);
1977 if (event->type() == QEvent::ShortcutOverride) {
1978 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
1979 if (event->isAccepted())
1982 return QQuickImplicitSizeItem::event(event);
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999bool QQuickTextEdit::overwriteMode()
const
2001 Q_D(
const QQuickTextEdit);
2002 return d->control->overwriteMode();
2005void QQuickTextEdit::setOverwriteMode(
bool overwrite)
2007 Q_D(QQuickTextEdit);
2008 d->control->setOverwriteMode(overwrite);
2012
2013
2014
2015void QQuickTextEdit::keyPressEvent(QKeyEvent *event)
2017 Q_D(QQuickTextEdit);
2018 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2019 if (!event->isAccepted())
2020 QQuickImplicitSizeItem::keyPressEvent(event);
2024
2025
2026
2027void QQuickTextEdit::keyReleaseEvent(QKeyEvent *event)
2029 Q_D(QQuickTextEdit);
2030 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2031 if (!event->isAccepted())
2032 QQuickImplicitSizeItem::keyReleaseEvent(event);
2036
2037
2038
2039
2040void QQuickTextEdit::deselect()
2042 Q_D(QQuickTextEdit);
2043 QTextCursor c = d->control->textCursor();
2045 d->control->setTextCursor(c);
2049
2050
2051
2052
2053void QQuickTextEdit::selectAll()
2055 Q_D(QQuickTextEdit);
2056 d->control->selectAll();
2060
2061
2062
2063
2064void QQuickTextEdit::selectWord()
2066 Q_D(QQuickTextEdit);
2067 QTextCursor c = d->control->textCursor();
2068 c.select(QTextCursor::WordUnderCursor);
2069 d->control->setTextCursor(c);
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085void QQuickTextEdit::select(
int start,
int end)
2087 Q_D(QQuickTextEdit);
2088 if (start < 0 || end < 0 || start >= d->document->characterCount() || end >= d->document->characterCount())
2090 QTextCursor cursor = d->control->textCursor();
2091 cursor.beginEditBlock();
2092 cursor.setPosition(start, QTextCursor::MoveAnchor);
2093 cursor.setPosition(end, QTextCursor::KeepAnchor);
2094 cursor.endEditBlock();
2095 d->control->setTextCursor(cursor);
2100 updateInputMethod();
2105
2106
2107
2108
2109
2110bool QQuickTextEdit::isRightToLeft(
int start,
int end)
2113 qmlWarning(
this) <<
"isRightToLeft(start, end) called with the end property being smaller than the start.";
2116 return getText(start, end).isRightToLeft();
2120#if QT_CONFIG(clipboard)
2122
2123
2124
2125
2126void QQuickTextEdit::cut()
2128 Q_D(QQuickTextEdit);
2133
2134
2135
2136
2137void QQuickTextEdit::copy()
2139 Q_D(QQuickTextEdit);
2144
2145
2146
2147
2148void QQuickTextEdit::paste()
2150 Q_D(QQuickTextEdit);
2151 d->control->paste();
2157
2158
2159
2160
2161
2162
2164void QQuickTextEdit::undo()
2166 Q_D(QQuickTextEdit);
2171
2172
2173
2174
2176void QQuickTextEdit::redo()
2178 Q_D(QQuickTextEdit);
2183
2184
2185
2186void QQuickTextEdit::mousePressEvent(QMouseEvent *event)
2188 Q_D(QQuickTextEdit);
2189 const bool isMouse = QQuickDeliveryAgentPrivate::isEventFromMouseOrTouchpad(event);
2190 setKeepMouseGrab(d->selectByMouse && isMouse);
2191 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2192 if (d->focusOnPress){
2193 bool hadActiveFocus = hasActiveFocus();
2194 forceActiveFocus(Qt::MouseFocusReason);
2197 if (hasActiveFocus() && hadActiveFocus && !isReadOnly())
2198 qGuiApp->inputMethod()->show();
2200 Q_UNUSED(hadActiveFocus);
2203 if (!event->isAccepted())
2204 QQuickImplicitSizeItem::mousePressEvent(event);
2208
2209
2210
2211void QQuickTextEdit::mouseReleaseEvent(QMouseEvent *event)
2213 Q_D(QQuickTextEdit);
2214 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2216 if (!event->isAccepted())
2217 QQuickImplicitSizeItem::mouseReleaseEvent(event);
2221
2222
2223
2224void QQuickTextEdit::mouseDoubleClickEvent(QMouseEvent *event)
2226 Q_D(QQuickTextEdit);
2227 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2228 if (!event->isAccepted())
2229 QQuickImplicitSizeItem::mouseDoubleClickEvent(event);
2233
2234
2235
2236void QQuickTextEdit::mouseMoveEvent(QMouseEvent *event)
2238 Q_D(QQuickTextEdit);
2239 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2240 if (!event->isAccepted())
2241 QQuickImplicitSizeItem::mouseMoveEvent(event);
2246
2247
2248
2249void QQuickTextEdit::inputMethodEvent(QInputMethodEvent *event)
2251 Q_D(QQuickTextEdit);
2252 const bool wasComposing = isInputMethodComposing();
2253 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2254 setCursorVisible(d->control->cursorVisible());
2255 if (wasComposing != isInputMethodComposing())
2256 emit inputMethodComposingChanged();
2260
2261
2262
2263QVariant QQuickTextEdit::inputMethodQuery(Qt::InputMethodQuery property, QVariant argument)
const
2265 Q_D(
const QQuickTextEdit);
2270 v = (
bool)(flags() & ItemAcceptsInputMethod);
2273 v = (
int)d->effectiveInputMethodHints();
2275 case Qt::ImInputItemClipRectangle:
2276 v = QQuickItem::inputMethodQuery(property);
2278 case Qt::ImReadOnly:
2282 if (property == Qt::ImCursorPosition && !argument.isNull())
2283 argument = QVariant(argument.toPointF() - QPointF(d->xoff, d->yoff));
2284 v = d->control->inputMethodQuery(property, argument);
2285 if (property == Qt::ImCursorRectangle || property == Qt::ImAnchorRectangle)
2286 v = QVariant(v.toRectF().translated(d->xoff, d->yoff));
2293
2294
2295
2296QVariant QQuickTextEdit::inputMethodQuery(Qt::InputMethodQuery property)
const
2298 return inputMethodQuery(property, QVariant());
2302void QQuickTextEdit::triggerPreprocess()
2304 Q_D(QQuickTextEdit);
2305 if (d->updateType == QQuickTextEditPrivate::UpdateNone)
2306 d->updateType = QQuickTextEditPrivate::UpdateOnlyPreprocess;
2312
2313
2314
2315
2316QVariant QQuickTextEdit::loadResource(
int type,
const QUrl &source)
2318 Q_D(QQuickTextEdit);
2319 const QUrl url = d->document->baseUrl().resolved(source);
2320 if (url.isLocalFile()) {
2322 QFileInfo fi(QQmlFile::urlToLocalFileOrQrc(url));
2324 qmlWarning(
this) <<
"Cannot open: " << url.toString();
2330 if (!url.scheme().compare(
"qrc"_L1, Qt::CaseInsensitive)) {
2332 QFile f(QQmlFile::urlToLocalFileOrQrc(url));
2333 if (f.open(QFile::ReadOnly)) {
2334 QByteArray buf = f.readAll();
2337 image.loadFromData(buf);
2338 if (!image.isNull())
2342 qmlWarning(
this) <<
"Cannot read resource: " << f.fileName();
2347 auto existingJobIter = std::find_if(
2348 d->pixmapsInProgress.cbegin(), d->pixmapsInProgress.cend(),
2349 [&url](
const auto *job) {
return job->url() == url; } );
2350 if (existingJobIter != d->pixmapsInProgress.cend()) {
2351 const QQuickPixmap *job = *existingJobIter;
2352 if (job->isError()) {
2353 qmlWarning(
this) << job->error();
2354 d->pixmapsInProgress.erase(existingJobIter);
2358 qCDebug(lcTextEdit) <<
"already downloading" << url;
2360 return job->isReady() ? job->image() : QVariant();
2365 qCDebug(lcTextEdit) <<
"loading" << source <<
"resolved" << url
2366 <<
"type" <<
static_cast<QTextDocument::ResourceType>(type);
2367 QQmlContext *context = qmlContext(
this);
2370 QQuickPixmap *p =
new QQuickPixmap(context->engine(), url, QQuickPixmap::Options{});
2371 p->connectFinished(
this, SLOT(resourceRequestFinished()));
2372 d->pixmapsInProgress.append(p);
2374 return p->isReady() ? p->image() : QVariant();
2378
2379
2380void QQuickTextEdit::resourceRequestFinished()
2382 Q_D(QQuickTextEdit);
2383 for (
auto it = d->pixmapsInProgress.cbegin(); it != d->pixmapsInProgress.cend(); ++it) {
2385 if (job->isError()) {
2387 qCDebug(lcTextEdit) <<
"failed to load (error)" << job->url();
2388 d->document->resource(QTextDocument::ImageResource, job->url());
2392 }
else if (job->isReady()) {
2394 auto res = d->document->resource(QTextDocument::ImageResource, job->url());
2396 qCDebug(lcTextEdit) << (res.isValid() ?
"done downloading" :
"failed to load") << job->url() << job->rect();
2397 d->pixmapsInProgress.erase(it);
2402 if (d->pixmapsInProgress.isEmpty()) {
2410using TextNodeIterator = QQuickTextEditPrivate::TextNodeIterator;
2414 return n1.startPos() < n2.startPos();
2419 QMatrix4x4 transformMatrix;
2420 transformMatrix.translate(topLeft.x(), topLeft.y());
2421 node->setMatrix(transformMatrix);
2425
2426
2427
2428
2429
2430void QQuickTextEdit::invalidateFontCaches()
2432 Q_D(QQuickTextEdit);
2433 if (d->document ==
nullptr)
2437 for (block = d->document->firstBlock(); block.isValid(); block = block.next()) {
2438 if (block.layout() !=
nullptr && block.layout()->engine() !=
nullptr)
2439 block.layout()->engine()->resetFontEngineCache();
2443QTextDocument *QQuickTextEdit::document()
const
2445 Q_D(
const QQuickTextEdit);
2449void QQuickTextEdit::setDocument(QTextDocument *doc)
2451 Q_D(QQuickTextEdit);
2452 if (d->ownsDocument)
2455 d->ownsDocument =
false;
2456 d->control->setDocument(doc);
2468QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData)
2470 Q_UNUSED(updatePaintNodeData);
2471 Q_D(QQuickTextEdit);
2473 if (d->updateType != QQuickTextEditPrivate::UpdatePaintNode
2474 && d->updateType != QQuickTextEditPrivate::UpdateAll
2475 && oldNode !=
nullptr) {
2477 d->updateType = QQuickTextEditPrivate::UpdateNone;
2481 d->containsUnscalableGlyphs =
false;
2482 if (!oldNode || d->updateType == QQuickTextEditPrivate::UpdateAll) {
2488 d->textNodeMap.clear();
2491 d->updateType = QQuickTextEditPrivate::UpdateNone;
2493 RootNode *rootNode =
static_cast<RootNode *>(oldNode);
2494 TextNodeIterator nodeIterator = d->textNodeMap.begin();
2495 std::optional<
int> firstPosAcrossAllNodes;
2496 if (nodeIterator != d->textNodeMap.end())
2497 firstPosAcrossAllNodes = nodeIterator->startPos();
2499 while (nodeIterator != d->textNodeMap.end() && !nodeIterator->dirty())
2502 QQuickTextNodeEngine engine;
2503 QQuickTextNodeEngine frameDecorationsEngine;
2505 if (!oldNode || nodeIterator < d->textNodeMap.end() || d->textNodeMap.isEmpty()) {
2508 rootNode =
new RootNode;
2510 int firstDirtyPos = 0;
2511 if (nodeIterator != d->textNodeMap.end()) {
2512 firstDirtyPos = nodeIterator->startPos();
2515 QSGInternalTextNode *firstCleanNode =
nullptr;
2516 auto it = d->textNodeMap.constEnd();
2517 while (it != nodeIterator) {
2521 firstCleanNode = it->textNode();
2524 rootNode->removeChildNode(nodeIterator->textNode());
2525 delete nodeIterator->textNode();
2526 nodeIterator = d->textNodeMap.erase(nodeIterator);
2527 }
while (nodeIterator != d->textNodeMap.constEnd() && nodeIterator->textNode() != firstCleanNode);
2532 if (flags().testFlag(QQuickItem::ItemObservesViewport)) {
2533 viewport = clipRect();
2534 qCDebug(lcVP) <<
"text viewport" << viewport;
2538 rootNode->resetFrameDecorations(d->createTextNode());
2539 resetEngine(&frameDecorationsEngine, d->color, d->selectedTextColor, d->selectionColor);
2541 QSGInternalTextNode *node =
nullptr;
2543 int currentNodeSize = 0;
2544 int nodeStart = firstDirtyPos;
2545 QPointF basePosition(d->xoff, d->yoff);
2546 QMatrix4x4 basePositionMatrix;
2547 basePositionMatrix.translate(basePosition.x(), basePosition.y());
2548 rootNode->setMatrix(basePositionMatrix);
2551 const TextNode firstCleanNode = (nodeIterator != d->textNodeMap.end()) ? *nodeIterator
2554 QList<QTextFrame *> frames;
2555 frames.append(d->document->rootFrame());
2558 d->firstBlockInViewport = -1;
2559 d->firstBlockPastViewport = -1;
2560 int frameCount = -1;
2561 while (!frames.isEmpty()) {
2562 QTextFrame *textFrame = frames.takeFirst();
2566 qCDebug(lcVP) <<
"frame" << frameCount << textFrame
2567 <<
"from" << positionToRectangle(textFrame->firstPosition()).topLeft()
2568 <<
"to" << positionToRectangle(textFrame->lastPosition()).bottomRight();
2569 frames.append(textFrame->childFrames());
2570 frameDecorationsEngine.addFrameDecorations(d->document, textFrame);
2571 resetEngine(&engine, d->color, d->selectedTextColor, d->selectionColor);
2573 if (textFrame->firstPosition() > textFrame->lastPosition()
2574 && textFrame->frameFormat().position() != QTextFrameFormat::InFlow) {
2575 node = d->createTextNode();
2576 updateNodeTransform(node, d->document->documentLayout()->frameBoundingRect(textFrame).topLeft());
2577 const int pos = textFrame->firstPosition() - 1;
2578 auto *a =
static_cast<QtPrivate::ProtectedLayoutAccessor *>(d->document->documentLayout());
2579 QTextCharFormat format = a->formatAccessor(pos);
2580 QTextBlock block = textFrame->firstCursorPosition().block();
2581 nodeOffset = d->document->documentLayout()->blockBoundingRect(block).topLeft();
2583 if (!viewport.isNull() && block.layout()) {
2584 QRectF coveredRegion = block.layout()->boundingRect().adjusted(nodeOffset.x(), nodeOffset.y(), nodeOffset.x(), nodeOffset.y());
2585 inView = coveredRegion.bottom() >= viewport.top() && coveredRegion.top() <= viewport.bottom();
2586 qCDebug(lcVP) <<
"non-flow frame" << coveredRegion <<
"in viewport?" << inView;
2589 engine.setCurrentLine(block.layout()->lineForTextPosition(pos - block.position()));
2590 engine.addTextObject(block, QPointF(0, 0), format, QQuickTextNodeEngine::Unselected, d->document,
2591 pos, textFrame->frameFormat().position());
2596 QList<
int> frameBoundaries;
2597 frameBoundaries.reserve(frames.size());
2598 for (QTextFrame *frame : std::as_const(frames))
2599 frameBoundaries.append(frame->firstPosition());
2600 std::sort(frameBoundaries.begin(), frameBoundaries.end());
2602 QTextFrame::iterator it = textFrame->begin();
2603 while (!it.atEnd()) {
2604 QTextBlock block = it.currentBlock();
2605 if (block.position() < firstDirtyPos) {
2610 if (!engine.hasContents())
2611 nodeOffset = d->document->documentLayout()->blockBoundingRect(block).topLeft();
2614 if (!viewport.isNull()) {
2615 QRectF coveredRegion;
2616 if (block.layout()) {
2617 coveredRegion = block.layout()->boundingRect().adjusted(nodeOffset.x(), nodeOffset.y(), nodeOffset.x(), nodeOffset.y());
2618 inView = coveredRegion.bottom() > viewport.top();
2620 const bool potentiallyScrollingBackwards = firstPosAcrossAllNodes && *firstPosAcrossAllNodes == firstDirtyPos;
2621 if (d->firstBlockInViewport < 0 && inView && potentiallyScrollingBackwards) {
2623 if (coveredRegion.top() > viewport.top() + 1) {
2624 qCDebug(lcVP) <<
"checking backwards from block" << block.blockNumber() <<
"@" << nodeOffset.y() << coveredRegion;
2625 while (it != textFrame->begin() && it.currentBlock().layout() &&
2626 it.currentBlock().layout()->boundingRect().top() + nodeOffset.y() > viewport.top()) {
2627 nodeOffset = d->document->documentLayout()->blockBoundingRect(it.currentBlock()).topLeft();
2630 if (!it.currentBlock().layout())
2632 if (Q_LIKELY(it.currentBlock().layout())) {
2633 block = it.currentBlock();
2634 coveredRegion = block.layout()->boundingRect().adjusted(nodeOffset.x(), nodeOffset.y(), nodeOffset.x(), nodeOffset.y());
2635 firstDirtyPos = it.currentBlock().position();
2637 qCWarning(lcVP) <<
"failed to find a text block with layout during back-scrolling";
2640 qCDebug(lcVP) <<
"first block in viewport" << block.blockNumber() <<
"@" << nodeOffset.y() << coveredRegion;
2642 d->renderedRegion = coveredRegion;
2644 if (nodeOffset.y() > viewport.bottom()) {
2646 if (d->firstBlockInViewport >= 0 && d->firstBlockPastViewport < 0) {
2647 qCDebug(lcVP) <<
"first block past viewport" << viewport << block.blockNumber()
2648 <<
"@" << nodeOffset.y() <<
"total region rendered" << d->renderedRegion;
2649 d->firstBlockPastViewport = block.blockNumber();
2653 if (inView && !block.text().isEmpty() && coveredRegion.isValid()) {
2654 d->renderedRegion = d->renderedRegion.united(coveredRegion);
2657 if (!frames.isEmpty())
2658 viewport = viewport.united(d->renderedRegion);
2661 if (inView && d->firstBlockInViewport < 0)
2662 d->firstBlockInViewport = block.blockNumber();
2665 bool createdNodeInView =
false;
2667 if (!engine.hasContents()) {
2669 d->containsUnscalableGlyphs = d->containsUnscalableGlyphs
2670 || node->containsUnscalableGlyphs();
2671 if (!node->parent())
2672 d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart);
2674 node = d->createTextNode();
2675 createdNodeInView =
true;
2676 updateNodeTransform(node, nodeOffset);
2677 nodeStart = block.position();
2679 engine.addTextBlock(d->document, block, -nodeOffset, d->color, QColor(), selectionStart(), selectionEnd() - 1);
2680 currentNodeSize += block.length();
2683 if ((it.atEnd()) || block.next().position() >= firstCleanNode.startPos())
2685 QList<
int>::const_iterator lowerBound = std::lower_bound(frameBoundaries.constBegin(), frameBoundaries.constEnd(), block.next().position());
2686 if (node && (currentNodeSize > nodeBreakingSize || lowerBound == frameBoundaries.constEnd() || *lowerBound > nodeStart)) {
2687 currentNodeSize = 0;
2688 d->containsUnscalableGlyphs = d->containsUnscalableGlyphs
2689 || node->containsUnscalableGlyphs();
2690 if (!node->parent())
2691 d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart);
2692 if (!createdNodeInView)
2693 node = d->createTextNode();
2694 resetEngine(&engine, d->color, d->selectedTextColor, d->selectionColor);
2695 nodeStart = block.next().position();
2700 if (Q_LIKELY(node)) {
2701 d->containsUnscalableGlyphs = d->containsUnscalableGlyphs
2702 || node->containsUnscalableGlyphs();
2703 if (Q_LIKELY(!node->parent()))
2704 d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart);
2707 frameDecorationsEngine.addToSceneGraph(rootNode->frameDecorationsNode, QQuickText::Normal, QColor());
2709 rootNode->prependChildNode(rootNode->frameDecorationsNode);
2711 Q_ASSERT(nodeIterator == d->textNodeMap.end()
2712 || (nodeIterator->textNode() == firstCleanNode.textNode()
2713 && nodeIterator->startPos() == firstCleanNode.startPos()));
2715 if (firstCleanNode.textNode() !=
nullptr) {
2716 QPointF oldOffset = firstCleanNode.textNode()->matrix().map(QPointF(0,0));
2717 QPointF currentOffset = d->document->documentLayout()->blockBoundingRect(
2718 d->document->findBlock(firstCleanNode.startPos())).topLeft();
2719 QPointF delta = currentOffset - oldOffset;
2720 while (nodeIterator != d->textNodeMap.end()) {
2721 QMatrix4x4 transformMatrix = nodeIterator->textNode()->matrix();
2722 transformMatrix.translate(delta.x(), delta.y());
2723 nodeIterator->textNode()->setMatrix(transformMatrix);
2731 std::sort(d->textNodeMap.begin(), d->textNodeMap.end());
2734 if (d->cursorComponent ==
nullptr) {
2735 QSGInternalRectangleNode* cursor =
nullptr;
2736 if (!isReadOnly() && d->cursorVisible && d->control->cursorOn() && d->control->cursorVisible())
2737 cursor = d->sceneGraphContext()->createInternalRectangleNode(d->control->cursorRect(), d->color);
2738 rootNode->resetCursorNode(cursor);
2741 invalidateFontCaches();
2746void QQuickTextEdit::updatePolish()
2748 invalidateFontCaches();
2752
2753
2754
2755
2756
2757bool QQuickTextEdit::canPaste()
const
2759 Q_D(
const QQuickTextEdit);
2760 if (!d->canPasteValid) {
2761 const_cast<QQuickTextEditPrivate *>(d)->canPaste = d->control->canPaste();
2762 const_cast<QQuickTextEditPrivate *>(d)->canPasteValid =
true;
2768
2769
2770
2771
2772
2774bool QQuickTextEdit::canUndo()
const
2776 Q_D(
const QQuickTextEdit);
2777 return d->document->isUndoAvailable();
2781
2782
2783
2784
2785
2787bool QQuickTextEdit::canRedo()
const
2789 Q_D(
const QQuickTextEdit);
2790 return d->document->isRedoAvailable();
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805bool QQuickTextEdit::isInputMethodComposing()
const
2810 Q_D(
const QQuickTextEdit);
2811 return d->control->hasImState();
2815QQuickTextEditPrivate::ExtraData::ExtraData()
2816 : explicitTopPadding(
false)
2817 , explicitLeftPadding(
false)
2818 , explicitRightPadding(
false)
2819 , explicitBottomPadding(
false)
2820 , implicitResize(
true)
2824void QQuickTextEditPrivate::init()
2826 Q_Q(QQuickTextEdit);
2828#if QT_CONFIG(clipboard)
2829 if (QGuiApplication::clipboard()->supportsSelection())
2830 q->setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton);
2833 q->setAcceptedMouseButtons(Qt::LeftButton);
2836 q->setFlag(QQuickItem::ItemAcceptsInputMethod);
2838 q->setFlag(QQuickItem::ItemHasContents);
2840 q->setAcceptHoverEvents(
true);
2842 document =
new QTextDocument(q);
2843 ownsDocument =
true;
2844 auto *imageHandler =
new QQuickTextImageHandler(document);
2845 document->documentLayout()->registerHandler(QTextFormat::ImageObject, imageHandler);
2847 control =
new QQuickTextControl(document, q);
2848 control->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard | Qt::TextEditable);
2849 control->setAcceptRichText(
false);
2850 control->setCursorIsFocusIndicator(
true);
2851 q->setKeepMouseGrab(
true);
2853 qmlobject_connect(control, QQuickTextControl, SIGNAL(updateCursorRequest()), q, QQuickTextEdit, SLOT(updateCursor()));
2854 qmlobject_connect(control, QQuickTextControl, SIGNAL(selectionChanged()), q, QQuickTextEdit, SIGNAL(selectedTextChanged()));
2855 qmlobject_connect(control, QQuickTextControl, SIGNAL(selectionChanged()), q, QQuickTextEdit, SLOT(updateSelection()));
2856 qmlobject_connect(control, QQuickTextControl, SIGNAL(cursorPositionChanged()), q, QQuickTextEdit, SLOT(updateSelection()));
2857 qmlobject_connect(control, QQuickTextControl, SIGNAL(cursorPositionChanged()), q, QQuickTextEdit, SIGNAL(cursorPositionChanged()));
2858 qmlobject_connect(control, QQuickTextControl, SIGNAL(cursorRectangleChanged()), q, QQuickTextEdit, SLOT(moveCursorDelegate()));
2859 qmlobject_connect(control, QQuickTextControl, SIGNAL(linkActivated(QString)), q, QQuickTextEdit, SIGNAL(linkActivated(QString)));
2860 qmlobject_connect(control, QQuickTextControl, SIGNAL(overwriteModeChanged(
bool)), q, QQuickTextEdit, SIGNAL(overwriteModeChanged(
bool)));
2861 qmlobject_connect(control, QQuickTextControl, SIGNAL(textChanged()), q, QQuickTextEdit, SLOT(q_textChanged()));
2862 qmlobject_connect(control, QQuickTextControl, SIGNAL(preeditTextChanged()), q, QQuickTextEdit, SIGNAL(preeditTextChanged()));
2863#if QT_CONFIG(clipboard)
2864 qmlobject_connect(QGuiApplication::clipboard(), QClipboard, SIGNAL(dataChanged()), q, QQuickTextEdit, SLOT(q_canPasteChanged()));
2866 qmlobject_connect(document, QTextDocument, SIGNAL(undoAvailable(
bool)), q, QQuickTextEdit, SIGNAL(canUndoChanged()));
2867 qmlobject_connect(document, QTextDocument, SIGNAL(redoAvailable(
bool)), q, QQuickTextEdit, SIGNAL(canRedoChanged()));
2868 QObject::connect(document, &QTextDocument::contentsChange, q, &QQuickTextEdit::q_contentsChange);
2869 QObject::connect(document->documentLayout(), &QAbstractTextDocumentLayout::updateBlock, q, &QQuickTextEdit::invalidateBlock);
2870 QObject::connect(control, &QQuickTextControl::linkHovered, q, &QQuickTextEdit::q_linkHovered);
2871 QObject::connect(control, &QQuickTextControl::markerHovered, q, &QQuickTextEdit::q_markerHovered);
2873 document->setDefaultFont(font);
2874 document->setDocumentMargin(textMargin);
2875 document->setUndoRedoEnabled(
false);
2876 document->setUndoRedoEnabled(
true);
2877 updateDefaultTextOption();
2878 document->setModified(
false);
2880#if QT_CONFIG(cursor)
2881 updateMouseCursorShape();
2883 setSizePolicy(QLayoutPolicy::Expanding, QLayoutPolicy::Expanding);
2886void QQuickTextEditPrivate::resetInputMethod()
2888 Q_Q(QQuickTextEdit);
2889 if (!q->isReadOnly() && q->hasActiveFocus() && qGuiApp)
2890 QGuiApplication::inputMethod()->reset();
2893void QQuickTextEdit::q_textChanged()
2895 Q_D(QQuickTextEdit);
2896 d->textCached =
false;
2897 for (QTextBlock it = d->document->begin(); it != d->document->end(); it = it.next()) {
2898 d->contentDirection = d->textDirection(it.text());
2899 if (d->contentDirection != Qt::LayoutDirectionAuto)
2902 d->determineHorizontalAlignment();
2903 d->updateDefaultTextOption();
2906 markDirtyNodesForRange(0, d->document->characterCount(), 0);
2907 if (isComponentComplete()) {
2909 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
2916void QQuickTextEdit::markDirtyNodesForRange(
int start,
int end,
int charDelta)
2918 Q_D(QQuickTextEdit);
2922 TextNode dummyNode(start);
2924 const TextNodeIterator textNodeMapBegin = d->textNodeMap.begin();
2925 const TextNodeIterator textNodeMapEnd = d->textNodeMap.end();
2927 TextNodeIterator it = std::lower_bound(textNodeMapBegin, textNodeMapEnd, dummyNode);
2930 if (it != textNodeMapBegin) {
2932 TextNode otherDummy(it->startPos());
2933 it = std::lower_bound(textNodeMapBegin, textNodeMapEnd, otherDummy);
2937 while (it != textNodeMapEnd) {
2938 if (it->startPos() <= end)
2941 it->moveStartPos(charDelta);
2948void QQuickTextEdit::q_contentsChange(
int pos,
int charsRemoved,
int charsAdded)
2950 Q_D(QQuickTextEdit);
2952 const int editRange = pos + qMax(charsAdded, charsRemoved);
2953 const int delta = charsAdded - charsRemoved;
2955 markDirtyNodesForRange(pos, editRange, delta);
2957 if (isComponentComplete()) {
2959 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
2964void QQuickTextEdit::moveCursorDelegate()
2966 Q_D(QQuickTextEdit);
2968 updateInputMethod();
2970 emit cursorRectangleChanged();
2973 QRectF cursorRect = cursorRectangle();
2974 d->cursorItem->setX(cursorRect.x());
2975 d->cursorItem->setY(cursorRect.y());
2976 d->cursorItem->setHeight(cursorRect.height());
2979void QQuickTextEdit::updateSelection()
2981 Q_D(QQuickTextEdit);
2984 if (d->control->textCursor().hasSelection() || d->hadSelection) {
2985 markDirtyNodesForRange(qMin(d->lastSelectionStart, d->control->textCursor().selectionStart()), qMax(d->control->textCursor().selectionEnd(), d->lastSelectionEnd), 0);
2986 if (isComponentComplete()) {
2988 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
2993 d->hadSelection = d->control->textCursor().hasSelection();
2995 if (d->lastSelectionStart != d->control->textCursor().selectionStart()) {
2996 d->lastSelectionStart = d->control->textCursor().selectionStart();
2997 emit selectionStartChanged();
2999 if (d->lastSelectionEnd != d->control->textCursor().selectionEnd()) {
3000 d->lastSelectionEnd = d->control->textCursor().selectionEnd();
3001 emit selectionEndChanged();
3005QRectF QQuickTextEdit::boundingRect()
const
3007 Q_D(
const QQuickTextEdit);
3009 QQuickTextUtil::alignedX(d->contentSize.width(), width(), effectiveHAlign()),
3011 d->contentSize.width(),
3012 d->contentSize.height());
3014 int cursorWidth = 1;
3017 else if (!d->document->isEmpty())
3021 r.setRight(r.right() + cursorWidth);
3026QRectF QQuickTextEdit::clipRect()
const
3028 Q_D(
const QQuickTextEdit);
3029 QRectF r = QQuickImplicitSizeItem::clipRect();
3030 int cursorWidth = 1;
3032 cursorWidth = d->cursorItem->width();
3033 if (!d->document->isEmpty())
3038 r.setRight(r.right() + cursorWidth);
3042qreal QQuickTextEditPrivate::getImplicitWidth()
const
3044 Q_Q(
const QQuickTextEdit);
3045 if (!requireImplicitWidth) {
3048 const_cast<QQuickTextEditPrivate*>(
this)->requireImplicitWidth =
true;
3049 const_cast<QQuickTextEdit*>(q)->updateSize();
3051 return implicitWidth;
3056void QQuickTextEdit::updateSize()
3058 Q_D(QQuickTextEdit);
3059 if (!isComponentComplete()) {
3067 if (!d->requireImplicitWidth) {
3068 emit implicitWidthChanged();
3070 if (d->requireImplicitWidth)
3073 if (d->requireImplicitWidth) {
3074 d->document->setTextWidth(-1);
3075 const qreal naturalWidth = d->document->idealWidth();
3076 const bool wasInLayout = d->inLayout;
3078 if (d->isImplicitResizeEnabled())
3079 setImplicitWidth(naturalWidth + leftPadding() + rightPadding());
3080 d->inLayout = wasInLayout;
3084 const qreal newTextWidth = width() - leftPadding() - rightPadding();
3085 if (d->document->textWidth() != newTextWidth)
3086 d->document->setTextWidth(newTextWidth);
3087 }
else if (d->wrapMode == NoWrap) {
3091 const qreal newTextWidth = d->document->idealWidth();
3092 if (d->document->textWidth() != newTextWidth)
3093 d->document->setTextWidth(newTextWidth);
3095 d->document->setTextWidth(-1);
3098 QFontMetricsF fm(d->font);
3099 const qreal newHeight = d->document->isEmpty() ? qCeil(fm.height()) : d->document->size().height();
3100 const qreal newWidth = d->document->idealWidth();
3102 if (d->isImplicitResizeEnabled()) {
3105 setImplicitSize(newWidth + leftPadding() + rightPadding(), newHeight + topPadding() + bottomPadding());
3107 setImplicitHeight(newHeight + topPadding() + bottomPadding());
3110 d->xoff = leftPadding() + qMax(qreal(0), QQuickTextUtil::alignedX(d->document->size().width(), width() - leftPadding() - rightPadding(), effectiveHAlign()));
3111 d->yoff = topPadding() + QQuickTextUtil::alignedY(d->document->size().height(), height() - topPadding() - bottomPadding(), d->vAlign);
3113 qreal baseline = fm.ascent();
3114 QTextBlock firstBlock = d->document->firstBlock();
3115 if (firstBlock.isValid() && firstBlock.layout() !=
nullptr && firstBlock.lineCount() > 0) {
3116 QTextLine firstLine = firstBlock.layout()->lineAt(0);
3117 if (firstLine.isValid())
3118 baseline = firstLine.ascent();
3121 setBaselineOffset(baseline + d->yoff + d->textMargin);
3123 QSizeF size(newWidth, newHeight);
3124 if (d->contentSize != size) {
3125 d->contentSize = size;
3127 const bool wasInResize = d->inResize;
3130 emit contentSizeChanged();
3131 d->inResize = wasInResize;
3136void QQuickTextEdit::updateWholeDocument()
3138 Q_D(QQuickTextEdit);
3139 if (!d->textNodeMap.isEmpty()) {
3140 for (TextNode &node : d->textNodeMap)
3144 if (isComponentComplete()) {
3146 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
3151void QQuickTextEdit::invalidateBlock(
const QTextBlock &block)
3153 Q_D(QQuickTextEdit);
3154 markDirtyNodesForRange(block.position(), block.position() + block.length(), 0);
3156 if (isComponentComplete()) {
3158 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
3163void QQuickTextEdit::updateCursor()
3165 Q_D(QQuickTextEdit);
3166 if (isComponentComplete() && isVisible()) {
3168 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
3173void QQuickTextEdit::q_linkHovered(
const QString &link)
3175 Q_D(QQuickTextEdit);
3176 emit linkHovered(link);
3177#if QT_CONFIG(cursor)
3178 if (link.isEmpty()) {
3179 d->updateMouseCursorShape();
3180 }
else if (cursor().shape() != Qt::PointingHandCursor) {
3181 setCursor(Qt::PointingHandCursor);
3186void QQuickTextEdit::q_markerHovered(
bool hovered)
3188 Q_D(QQuickTextEdit);
3189#if QT_CONFIG(cursor)
3191 d->updateMouseCursorShape();
3192 }
else if (cursor().shape() != Qt::PointingHandCursor) {
3193 setCursor(Qt::PointingHandCursor);
3198void QQuickTextEdit::q_updateAlignment()
3200 Q_D(QQuickTextEdit);
3201 if (d->determineHorizontalAlignment()) {
3202 d->updateDefaultTextOption();
3203 d->xoff = qMax(qreal(0), QQuickTextUtil::alignedX(d->document->size().width(), width(), effectiveHAlign()));
3204 moveCursorDelegate();
3208void QQuickTextEdit::updateTotalLines()
3210 Q_D(QQuickTextEdit);
3214 for (QTextBlock it = d->document->begin(); it != d->document->end(); it = it.next()) {
3215 QTextLayout *layout = it.layout();
3218 subLines += layout->lineCount()-1;
3221 int newTotalLines = d->document->lineCount() + subLines;
3222 if (d->lineCount != newTotalLines) {
3223 d->lineCount = newTotalLines;
3224 emit lineCountChanged();
3228void QQuickTextEditPrivate::updateDefaultTextOption()
3230 Q_Q(QQuickTextEdit);
3231 QTextOption opt = document->defaultTextOption();
3232 const Qt::Alignment oldAlignment = opt.alignment();
3233 Qt::LayoutDirection oldTextDirection = opt.textDirection();
3235 QQuickTextEdit::HAlignment horizontalAlignment = q->effectiveHAlign();
3236 if (contentDirection == Qt::RightToLeft) {
3237 if (horizontalAlignment == QQuickTextEdit::AlignLeft)
3238 horizontalAlignment = QQuickTextEdit::AlignRight;
3239 else if (horizontalAlignment == QQuickTextEdit::AlignRight)
3240 horizontalAlignment = QQuickTextEdit::AlignLeft;
3242 if (!hAlignImplicit)
3243 opt.setAlignment((Qt::Alignment)(
int)(horizontalAlignment | vAlign));
3245 opt.setAlignment(Qt::Alignment(vAlign));
3248 if (contentDirection == Qt::LayoutDirectionAuto) {
3249 opt.setTextDirection(qGuiApp->inputMethod()->inputDirection());
3253 opt.setTextDirection(contentDirection);
3256 QTextOption::WrapMode oldWrapMode = opt.wrapMode();
3257 opt.setWrapMode(QTextOption::WrapMode(wrapMode));
3259 bool oldUseDesignMetrics = opt.useDesignMetrics();
3260 opt.setUseDesignMetrics(renderType != QQuickTextEdit::NativeRendering);
3262 if (oldWrapMode != opt.wrapMode() || oldAlignment != opt.alignment()
3263 || oldTextDirection != opt.textDirection()
3264 || oldUseDesignMetrics != opt.useDesignMetrics()) {
3265 document->setDefaultTextOption(opt);
3269void QQuickTextEditPrivate::onDocumentStatusChanged()
3271 Q_ASSERT(quickDocument);
3272 switch (quickDocument->status()) {
3273 case QQuickTextDocument::Status::Loaded:
3274 case QQuickTextDocument::Status::Saved:
3275 switch (QQuickTextDocumentPrivate::get(quickDocument)->detectedFormat) {
3277 richText = (format == QQuickTextEdit::RichText || format == QQuickTextEdit::AutoText);
3278 markdownText =
false;
3280 case Qt::MarkdownText:
3282 markdownText = (format == QQuickTextEdit::MarkdownText || format == QQuickTextEdit::AutoText);
3286 markdownText =
false;
3297void QQuickTextEdit::focusInEvent(QFocusEvent *event)
3299 Q_D(QQuickTextEdit);
3300 d->handleFocusEvent(event);
3301 QQuickImplicitSizeItem::focusInEvent(event);
3304void QQuickTextEdit::focusOutEvent(QFocusEvent *event)
3306 Q_D(QQuickTextEdit);
3307 d->handleFocusEvent(event);
3308 QQuickImplicitSizeItem::focusOutEvent(event);
3311void QQuickTextEditPrivate::handleFocusEvent(QFocusEvent *event)
3313 Q_Q(QQuickTextEdit);
3314 bool focus = event->type() == QEvent::FocusIn;
3315 if (!q->isReadOnly())
3316 q->setCursorVisible(focus);
3317 control->processEvent(event, QPointF(-xoff, -yoff));
3319 q->q_updateAlignment();
3321 if (focusOnPress && !q->isReadOnly())
3322 qGuiApp->inputMethod()->show();
3323 q->connect(QGuiApplication::inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
3324 q, SLOT(q_updateAlignment()));
3328 q->disconnect(QGuiApplication::inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
3329 q, SLOT(q_updateAlignment()));
3331 if (event->reason() != Qt::ActiveWindowFocusReason
3332 && event->reason() != Qt::PopupFocusReason
3333 && control->textCursor().hasSelection()
3334 && !persistentSelection)
3337 emit q->editingFinished();
3341void QQuickTextEditPrivate::addCurrentTextNodeToRoot(QQuickTextNodeEngine *engine, QSGTransformNode *root, QSGInternalTextNode *node, TextNodeIterator &it,
int startPos)
3343 engine->addToSceneGraph(node, QQuickText::Normal, QColor());
3344 it = textNodeMap.insert(it, TextNode(startPos, node));
3346 root->appendChildNode(node);
3347 ++renderedBlockCount;
3350QSGInternalTextNode *QQuickTextEditPrivate::createTextNode()
3352 Q_Q(QQuickTextEdit);
3353 QSGInternalTextNode* node = sceneGraphContext()->createInternalTextNode(sceneGraphRenderContext());
3354 node->setRenderType(QSGTextNode::RenderType(renderType));
3355 node->setFiltering(q->smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
3359void QQuickTextEdit::q_canPasteChanged()
3361 Q_D(QQuickTextEdit);
3362 bool old = d->canPaste;
3363 d->canPaste = d->control->canPaste();
3364 bool changed = old!=d->canPaste || !d->canPasteValid;
3365 d->canPasteValid =
true;
3367 emit canPasteChanged();
3371
3372
3373
3374
3375
3376
3378QString QQuickTextEdit::getText(
int start,
int end)
const
3380 Q_D(
const QQuickTextEdit);
3381 start = qBound(0, start, d->document->characterCount() - 1);
3382 end = qBound(0, end, d->document->characterCount() - 1);
3383 QTextCursor cursor(d->document);
3384 cursor.setPosition(start, QTextCursor::MoveAnchor);
3385 cursor.setPosition(end, QTextCursor::KeepAnchor);
3386#if QT_CONFIG(texthtmlparser)
3387 return d->richText || d->markdownText
3388 ? cursor.selectedText()
3389 : cursor.selection().toPlainText();
3391 return cursor.selection().toPlainText();
3396
3397
3398
3399
3400
3401
3403QString QQuickTextEdit::getFormattedText(
int start,
int end)
const
3405 Q_D(
const QQuickTextEdit);
3407 start = qBound(0, start, d->document->characterCount() - 1);
3408 end = qBound(0, end, d->document->characterCount() - 1);
3410 QTextCursor cursor(d->document);
3411 cursor.setPosition(start, QTextCursor::MoveAnchor);
3412 cursor.setPosition(end, QTextCursor::KeepAnchor);
3415#if QT_CONFIG(texthtmlparser)
3416 return cursor.selection().toHtml();
3418 return cursor.selection().toPlainText();
3420 }
else if (d->markdownText) {
3421#if QT_CONFIG(textmarkdownwriter)
3422 return cursor.selection().toMarkdown();
3424 return cursor.selection().toPlainText();
3427 return cursor.selection().toPlainText();
3432
3433
3434
3435
3436void QQuickTextEdit::insert(
int position,
const QString &text)
3438 Q_D(QQuickTextEdit);
3439 if (position < 0 || position >= d->document->characterCount())
3441 QTextCursor cursor(d->document);
3442 cursor.setPosition(position);
3443 d->richText = d->richText || (d->format == AutoText && Qt::mightBeRichText(text));
3445#if QT_CONFIG(texthtmlparser)
3446 cursor.insertHtml(text);
3448 cursor.insertText(text);
3450 }
else if (d->markdownText) {
3451#if QT_CONFIG(textmarkdownreader)
3452 cursor.insertMarkdown(text);
3454 cursor.insertText(text);
3457 cursor.insertText(text);
3459 d->control->updateCursorRectangle(
false);
3463
3464
3465
3466
3468void QQuickTextEdit::remove(
int start,
int end)
3470 Q_D(QQuickTextEdit);
3471 start = qBound(0, start, d->document->characterCount() - 1);
3472 end = qBound(0, end, d->document->characterCount() - 1);
3473 QTextCursor cursor(d->document);
3474 cursor.setPosition(start, QTextCursor::MoveAnchor);
3475 cursor.setPosition(end, QTextCursor::KeepAnchor);
3476 cursor.removeSelectedText();
3477 d->control->updateCursorRectangle(
false);
3481
3482
3483
3484
3485
3486
3487
3488
3489
3490
3492QQuickTextDocument *QQuickTextEdit::textDocument()
3494 Q_D(QQuickTextEdit);
3495 if (!d->quickDocument) {
3496 d->quickDocument =
new QQuickTextDocument(
this);
3497 connect(d->quickDocument, &QQuickTextDocument::statusChanged, d->quickDocument,
3498 [d]() { d->onDocumentStatusChanged(); } );
3500 return d->quickDocument;
3503bool QQuickTextEditPrivate::isLinkHoveredConnected()
3505 Q_Q(QQuickTextEdit);
3506 IS_SIGNAL_CONNECTED(q, QQuickTextEdit, linkHovered, (
const QString &));
3509#if QT_CONFIG(cursor)
3510void QQuickTextEditPrivate::updateMouseCursorShape()
3512 Q_Q(QQuickTextEdit);
3513 q->setCursor(q->isReadOnly() && !q->selectByMouse() ? Qt::ArrowCursor : Qt::IBeamCursor);
3518
3519
3520
3521
3522
3523
3524
3525
3526
3529
3530
3531
3532
3533
3536
3537
3538
3539
3540
3541
3542
3543
3544
3546QString QQuickTextEdit::hoveredLink()
const
3548 Q_D(
const QQuickTextEdit);
3549 if (
const_cast<QQuickTextEditPrivate *>(d)->isLinkHoveredConnected()) {
3550 return d->control->hoveredLink();
3552#if QT_CONFIG(cursor)
3553 if (QQuickWindow *wnd = window()) {
3554 QPointF pos = QCursor::pos(wnd->screen()) - wnd->position() - mapToScene(QPointF(0, 0));
3555 return d->control->anchorAt(pos);
3562void QQuickTextEdit::hoverEnterEvent(QHoverEvent *event)
3564 Q_D(QQuickTextEdit);
3565 if (d->isLinkHoveredConnected())
3566 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
3570void QQuickTextEdit::hoverMoveEvent(QHoverEvent *event)
3572 Q_D(QQuickTextEdit);
3573 if (d->isLinkHoveredConnected())
3574 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
3578void QQuickTextEdit::hoverLeaveEvent(QHoverEvent *event)
3580 Q_D(QQuickTextEdit);
3581 if (d->isLinkHoveredConnected())
3582 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
3587
3588
3589
3590
3591
3592
3593
3594
3595void QQuickTextEdit::append(
const QString &text)
3597 Q_D(QQuickTextEdit);
3598 QTextCursor cursor(d->document);
3599 cursor.beginEditBlock();
3600 cursor.movePosition(QTextCursor::End);
3602 if (!d->document->isEmpty())
3603 cursor.insertBlock();
3605 if (d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(text))) {
3606#if QT_CONFIG(texthtmlparser)
3607 cursor.insertHtml(text);
3609 cursor.insertText(text);
3611 }
else if (d->format == MarkdownText) {
3612#if QT_CONFIG(textmarkdownreader)
3613 cursor.insertMarkdown(text);
3615 cursor.insertText(text);
3618 cursor.insertText(text);
3621 cursor.endEditBlock();
3622 d->control->updateCursorRectangle(
false);
3626
3627
3628
3629
3630
3631
3632
3633
3634QString QQuickTextEdit::linkAt(qreal x, qreal y)
const
3636 Q_D(
const QQuickTextEdit);
3637 return d->control->anchorAt(QPointF(x + topPadding(), y + leftPadding()));
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651qreal QQuickTextEdit::padding()
const
3653 Q_D(
const QQuickTextEdit);
3654 return d->padding();
3657void QQuickTextEdit::setPadding(qreal padding)
3659 Q_D(QQuickTextEdit);
3660 if (qFuzzyCompare(d->padding(), padding))
3663 d->extra.value().padding = padding;
3665 if (isComponentComplete()) {
3666 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
3669 emit paddingChanged();
3670 if (!d->extra.isAllocated() || !d->extra->explicitTopPadding)
3671 emit topPaddingChanged();
3672 if (!d->extra.isAllocated() || !d->extra->explicitLeftPadding)
3673 emit leftPaddingChanged();
3674 if (!d->extra.isAllocated() || !d->extra->explicitRightPadding)
3675 emit rightPaddingChanged();
3676 if (!d->extra.isAllocated() || !d->extra->explicitBottomPadding)
3677 emit bottomPaddingChanged();
3680void QQuickTextEdit::resetPadding()
3685qreal QQuickTextEdit::topPadding()
const
3687 Q_D(
const QQuickTextEdit);
3688 if (d->extra.isAllocated() && d->extra->explicitTopPadding)
3689 return d->extra->topPadding;
3690 return d->padding();
3693void QQuickTextEdit::setTopPadding(qreal padding)
3695 Q_D(QQuickTextEdit);
3696 d->setTopPadding(padding);
3699void QQuickTextEdit::resetTopPadding()
3701 Q_D(QQuickTextEdit);
3702 d->setTopPadding(0,
true);
3705qreal QQuickTextEdit::leftPadding()
const
3707 Q_D(
const QQuickTextEdit);
3708 if (d->extra.isAllocated() && d->extra->explicitLeftPadding)
3709 return d->extra->leftPadding;
3710 return d->padding();
3713void QQuickTextEdit::setLeftPadding(qreal padding)
3715 Q_D(QQuickTextEdit);
3716 d->setLeftPadding(padding);
3719void QQuickTextEdit::resetLeftPadding()
3721 Q_D(QQuickTextEdit);
3722 d->setLeftPadding(0,
true);
3725qreal QQuickTextEdit::rightPadding()
const
3727 Q_D(
const QQuickTextEdit);
3728 if (d->extra.isAllocated() && d->extra->explicitRightPadding)
3729 return d->extra->rightPadding;
3730 return d->padding();
3733void QQuickTextEdit::setRightPadding(qreal padding)
3735 Q_D(QQuickTextEdit);
3736 d->setRightPadding(padding);
3739void QQuickTextEdit::resetRightPadding()
3741 Q_D(QQuickTextEdit);
3742 d->setRightPadding(0,
true);
3745qreal QQuickTextEdit::bottomPadding()
const
3747 Q_D(
const QQuickTextEdit);
3748 if (d->extra.isAllocated() && d->extra->explicitBottomPadding)
3749 return d->extra->bottomPadding;
3750 return d->padding();
3753void QQuickTextEdit::setBottomPadding(qreal padding)
3755 Q_D(QQuickTextEdit);
3756 d->setBottomPadding(padding);
3759void QQuickTextEdit::resetBottomPadding()
3761 Q_D(QQuickTextEdit);
3762 d->setBottomPadding(0,
true);
3766
3767
3768
3769
3770
3771
3772
3773int QQuickTextEdit::tabStopDistance()
const
3775 Q_D(
const QQuickTextEdit);
3776 return d->document->defaultTextOption().tabStopDistance();
3779void QQuickTextEdit::setTabStopDistance(qreal distance)
3781 Q_D(QQuickTextEdit);
3782 QTextOption textOptions = d->document->defaultTextOption();
3783 if (textOptions.tabStopDistance() == distance)
3786 textOptions.setTabStopDistance(distance);
3787 d->document->setDefaultTextOption(textOptions);
3788 emit tabStopDistanceChanged(distance);
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802void QQuickTextEdit::clear()
3804 Q_D(QQuickTextEdit);
3805 d->resetInputMethod();
3806 d->control->clear();
3809#ifndef QT_NO_DEBUG_STREAM
3812 QDebugStateSaver saver(debug);
3814 debug <<
"Node(startPos:" << n.m_startPos <<
"dirty:" << n.m_dirty << n.m_node <<
')';