70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
93
94
95
96
97
98
104#if !defined(QQUICKTEXT_LARGETEXT_THRESHOLD)
105 #define QQUICKTEXT_LARGETEXT_THRESHOLD 10000
111 class RootNode :
public QSGTransformNode
114 RootNode() : cursorNode(
nullptr), frameDecorationsNode(
nullptr)
117 void resetFrameDecorations(QSGInternalTextNode* newNode)
119 if (frameDecorationsNode) {
120 removeChildNode(frameDecorationsNode);
121 delete frameDecorationsNode;
123 frameDecorationsNode = newNode;
124 newNode->setFlag(QSGNode::OwnedByParent);
127 void resetCursorNode(QSGInternalRectangleNode* newNode)
130 removeChildNode(cursorNode);
132 cursorNode = newNode;
134 appendChildNode(cursorNode);
135 cursorNode->setFlag(QSGNode::OwnedByParent);
139 QSGInternalRectangleNode *cursorNode;
140 QSGInternalTextNode* frameDecorationsNode;
145QQuickTextEdit::QQuickTextEdit(QQuickItem *parent)
146: QQuickImplicitSizeItem(*(
new QQuickTextEditPrivate), parent)
152QQuickTextEdit::~QQuickTextEdit()
155 qDeleteAll(d->pixmapsInProgress);
158QQuickTextEdit::QQuickTextEdit(QQuickTextEditPrivate &dd, QQuickItem *parent)
159: QQuickImplicitSizeItem(dd, parent)
165QString QQuickTextEdit::text()
const
167 Q_D(
const QQuickTextEdit);
168 if (!d->textCached && isComponentComplete()) {
169 QQuickTextEditPrivate *d =
const_cast<QQuickTextEditPrivate *>(d_func());
170#if QT_CONFIG(texthtmlparser)
172 d->text = d->control->toHtml();
175#if QT_CONFIG(textmarkdownwriter)
177 d->text = d->control->toMarkdown();
180 d->text = d->control->toPlainText();
181 d->textCached =
true;
187
188
189
190
191
192
193
194
197
198
199
200
201
202
203
204
208
209
210
211
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
235
236
237
238
241
242
243
244
247
248
249
250
253
254
255
256
259
260
261
262
263
264
265
266
269
270
271
272
273
274
275
278
279
280
281
282
283
284
285
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
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
333
336
337
338
339
340
341
342
343
344
345
346
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
366
367
368
369
370
373
374
375
376
377
380
381
382
383
384
387
388
389
390
391
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
421
422void QQuickTextEdit::setText(
const QString &text)
425 if (QQuickTextEdit::text() == text)
428 d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(text));
429 d->markdownText = d->format == MarkdownText;
430 if (!isComponentComplete()) {
432 }
else if (d->richText) {
433#if QT_CONFIG(texthtmlparser)
434 d->control->setHtml(text);
436 d->control->setPlainText(text);
438 }
else if (d->markdownText) {
439 d->control->setMarkdownText(text);
441 d->control->setPlainText(text);
443 setFlag(QQuickItem::ItemObservesViewport, text.size() > QQuickTextEditPrivate::largeTextSizeThreshold);
446void QQuickTextEdit::invalidate()
448 QMetaObject::invokeMethod(
this, &QQuickTextEdit::q_invalidate);
451void QQuickTextEdit::q_invalidate()
454 if (isComponentComplete()) {
455 if (d->document !=
nullptr)
456 d->document->markContentsDirty(0, d->document->characterCount());
457 invalidateFontCaches();
458 d->updateType = QQuickTextEditPrivate::UpdateAll;
464
465
466
467
468
469
470
471
472
473
474
475QString QQuickTextEdit::preeditText()
const
477 Q_D(
const QQuickTextEdit);
478 return d->control->preeditText();
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
534
535QQuickTextEdit::TextFormat QQuickTextEdit::textFormat()
const
537 Q_D(
const QQuickTextEdit);
541void QQuickTextEdit::setTextFormat(TextFormat format)
544 if (format == d->format)
547 auto mightBeRichText = [
this]() {
548 return Qt::mightBeRichText(text());
551 auto findSourceFormat = [d, mightBeRichText](Qt::TextFormat detectedFormat) {
552 if (d->format == PlainText)
554 if (d->richText)
return RichText;
555 if (d->markdownText)
return MarkdownText;
556 if (detectedFormat == Qt::AutoText && mightBeRichText())
561 auto findDestinationFormat = [format, mightBeRichText](Qt::TextFormat detectedFormat, TextFormat sourceFormat) {
562 if (format == AutoText) {
563 if (detectedFormat == Qt::MarkdownText || (detectedFormat == Qt::AutoText && sourceFormat == MarkdownText))
565 if (detectedFormat == Qt::RichText || (detectedFormat == Qt::AutoText && (sourceFormat == RichText || mightBeRichText())))
572 bool textCachedChanged =
false;
573 bool converted =
false;
575 if (isComponentComplete()) {
576 Qt::TextFormat detectedFormat = Qt::AutoText;
577 if (d->quickDocument) {
580 detectedFormat = QQuickTextDocumentPrivate::get(d->quickDocument)->detectedFormat;
583 const TextFormat sourceFormat = findSourceFormat(detectedFormat);
584 const TextFormat destinationFormat = findDestinationFormat(detectedFormat, sourceFormat);
586 d->richText = destinationFormat == RichText;
587 d->markdownText = destinationFormat == MarkdownText;
590 if (format != PlainText && (sourceFormat != destinationFormat)) {
591 d->textCached =
false;
592 textCachedChanged =
true;
595 switch (destinationFormat) {
597#if QT_CONFIG(texthtmlparser)
598 if (sourceFormat == RichText) {
602 d->control->setPlainText(d->textCached ? d->text : d->control->toHtml());
606#if QT_CONFIG(textmarkdownwriter) && QT_CONFIG(textmarkdownreader)
607 if (sourceFormat == MarkdownText) {
611 d->control->setPlainText(d->textCached ? d->text : d->control->toMarkdown());
617#if QT_CONFIG(texthtmlparser)
618 switch (sourceFormat) {
621 d->control->setHtml(d->control->toHtml());
627 d->control->setHtml(d->textCached ? d->text : d->control->toPlainText());
637#if QT_CONFIG(textmarkdownwriter) && QT_CONFIG(textmarkdownreader)
638 switch (sourceFormat) {
641 d->control->setMarkdownText(d->control->toMarkdown());
647 d->control->setMarkdownText(d->textCached ? d->text : d->control->toPlainText());
663 d->richText = format == RichText || (format == AutoText && (d->richText || mightBeRichText()));
664 d->markdownText = format == MarkdownText;
667 qCDebug(lcTextEdit) << d->format <<
"->" << format
668 <<
"rich?" << d->richText <<
"md?" << d->markdownText
669 <<
"converted?" << converted <<
"cache invalidated?" << textCachedChanged;
672 d->control->setAcceptRichText(d->format != PlainText);
673 emit textFormatChanged(d->format);
674 if (textCachedChanged)
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703QQuickTextEdit::RenderType QQuickTextEdit::renderType()
const
705 Q_D(
const QQuickTextEdit);
706 return d->renderType;
709void QQuickTextEdit::setRenderType(QQuickTextEdit::RenderType renderType)
712 if (d->renderType == renderType)
715 d->renderType = renderType;
716 emit renderTypeChanged();
717 d->updateDefaultTextOption();
719 if (isComponentComplete())
723QFont QQuickTextEdit::font()
const
725 Q_D(
const QQuickTextEdit);
726 return d->sourceFont;
729void QQuickTextEdit::setFont(
const QFont &font)
732 if (d->sourceFont == font)
735 d->sourceFont = font;
736 QFont oldFont = d->font;
738 if (d->font.pointSizeF() != -1) {
740 qreal size = qRound(d->font.pointSizeF()*2.0);
741 d->font.setPointSizeF(size/2.0);
744 if (oldFont != d->font) {
745 d->document->setDefaultFont(d->font);
747 d->cursorItem->setHeight(QFontMetrics(d->font).height());
748 moveCursorDelegate();
751 updateWholeDocument();
753 updateInputMethod(Qt::ImCursorRectangle | Qt::ImAnchorRectangle | Qt::ImFont);
756 emit fontChanged(d->sourceFont);
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774QColor QQuickTextEdit::color()
const
776 Q_D(
const QQuickTextEdit);
780void QQuickTextEdit::setColor(
const QColor &color)
783 if (d->color == color)
787 updateWholeDocument();
788 emit colorChanged(d->color);
792
793
794
795
796QColor QQuickTextEdit::selectionColor()
const
798 Q_D(
const QQuickTextEdit);
799 return d->selectionColor;
802void QQuickTextEdit::setSelectionColor(
const QColor &color)
805 if (d->selectionColor == color)
808 d->selectionColor = color;
809 updateWholeDocument();
810 emit selectionColorChanged(d->selectionColor);
814
815
816
817
818QColor QQuickTextEdit::selectedTextColor()
const
820 Q_D(
const QQuickTextEdit);
821 return d->selectedTextColor;
824void QQuickTextEdit::setSelectedTextColor(
const QColor &color)
827 if (d->selectedTextColor == color)
830 d->selectedTextColor = color;
831 updateWholeDocument();
832 emit selectedTextColorChanged(d->selectedTextColor);
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
866
867QQuickTextEdit::HAlignment QQuickTextEdit::hAlign()
const
869 Q_D(
const QQuickTextEdit);
873void QQuickTextEdit::setHAlign(HAlignment align)
877 if (d->setHAlign(align,
true) && isComponentComplete()) {
878 d->updateDefaultTextOption();
880 updateWholeDocument();
884void QQuickTextEdit::resetHAlign()
887 d->hAlignImplicit =
true;
888 if (d->determineHorizontalAlignment() && isComponentComplete()) {
889 d->updateDefaultTextOption();
894QQuickTextEdit::HAlignment QQuickTextEdit::effectiveHAlign()
const
896 Q_D(
const QQuickTextEdit);
897 QQuickTextEdit::HAlignment effectiveAlignment = d->hAlign;
898 if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
900 case QQuickTextEdit::AlignLeft:
901 effectiveAlignment = QQuickTextEdit::AlignRight;
903 case QQuickTextEdit::AlignRight:
904 effectiveAlignment = QQuickTextEdit::AlignLeft;
910 return effectiveAlignment;
913bool QQuickTextEditPrivate::setHAlign(QQuickTextEdit::HAlignment align,
bool forceAlign)
916 if (hAlign == align && !forceAlign)
919 const bool wasImplicit = hAlignImplicit;
920 const auto oldEffectiveHAlign = q->effectiveHAlign();
922 hAlignImplicit = !forceAlign;
923 if (hAlign != align) {
925 emit q->horizontalAlignmentChanged(align);
928 if (q->effectiveHAlign() != oldEffectiveHAlign) {
929 emit q->effectiveHorizontalAlignmentChanged();
933 if (forceAlign && wasImplicit) {
936 emit q->effectiveHorizontalAlignmentChanged();
941Qt::LayoutDirection QQuickTextEditPrivate::textDirection(
const QString &text)
const
943 const QChar *character = text.constData();
944 while (!character->isNull()) {
945 switch (character->direction()) {
947 return Qt::LeftToRight;
951 return Qt::RightToLeft;
957 return Qt::LayoutDirectionAuto;
960bool QQuickTextEditPrivate::determineHorizontalAlignment()
963 if (!hAlignImplicit || !q->isComponentComplete())
966 Qt::LayoutDirection direction = contentDirection;
968 if (direction == Qt::LayoutDirectionAuto) {
969 QTextBlock block = control->textCursor().block();
972 direction = textDirection(block.layout()->preeditAreaText());
974 if (direction == Qt::LayoutDirectionAuto)
975 direction = qGuiApp->inputMethod()->inputDirection();
978 const auto implicitHAlign = direction == Qt::RightToLeft ?
979 QQuickTextEdit::AlignRight : QQuickTextEdit::AlignLeft;
980 return setHAlign(implicitHAlign);
983void QQuickTextEditPrivate::mirrorChange()
986 if (q->isComponentComplete()) {
987 if (!hAlignImplicit && (hAlign == QQuickTextEdit::AlignRight || hAlign == QQuickTextEdit::AlignLeft)) {
988 updateDefaultTextOption();
990 q->updateWholeDocument();
991 emit q->effectiveHorizontalAlignmentChanged();
996bool QQuickTextEditPrivate::transformChanged(QQuickItem *transformedItem)
999 qCDebug(lcVP) << q <<
"sees that" << transformedItem <<
"moved in VP" << q->clipRect();
1004 if (flags & QQuickItem::ItemObservesViewport) {
1005 if (QQuickItem *viewport = q->viewportItem()) {
1006 QRectF vp = q->mapRectFromItem(viewport, viewport->clipRect());
1007 if (!(vp.top() > renderedRegion.top() && vp.bottom() < renderedRegion.bottom())) {
1008 qCDebug(lcVP) <<
"viewport" << vp <<
"now goes beyond rendered region" << renderedRegion <<
"; updating";
1009 q->updateWholeDocument();
1011 const bool textCursorVisible = cursorVisible && q->cursorRectangle().intersects(vp);
1013 cursorItem->setVisible(textCursorVisible);
1015 control->setCursorVisible(textCursorVisible);
1018 return QQuickImplicitSizeItemPrivate::transformChanged(transformedItem);
1022Qt::InputMethodHints QQuickTextEditPrivate::effectiveInputMethodHints()
const
1024 return inputMethodHints | Qt::ImhMultiLine;
1028#if QT_CONFIG(accessibility)
1029void QQuickTextEditPrivate::accessibilityActiveChanged(
bool active)
1034 Q_Q(QQuickTextEdit);
1035 if (QQuickAccessibleAttached *accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(
1036 qmlAttachedPropertiesObject<QQuickAccessibleAttached>(q,
true))) {
1037 accessibleAttached->setRole(effectiveAccessibleRole());
1038 accessibleAttached->set_readOnly(q->isReadOnly());
1042QAccessible::Role QQuickTextEditPrivate::accessibleRole()
const
1044 return QAccessible::EditableText;
1048void QQuickTextEditPrivate::setTopPadding(qreal value,
bool reset)
1050 Q_Q(QQuickTextEdit);
1051 qreal oldPadding = q->topPadding();
1052 if (!reset || extra.isAllocated()) {
1053 extra.value().topPadding = value;
1054 extra.value().explicitTopPadding = !reset;
1056 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
1058 q->updateWholeDocument();
1059 emit q->topPaddingChanged();
1063void QQuickTextEditPrivate::setLeftPadding(qreal value,
bool reset)
1065 Q_Q(QQuickTextEdit);
1066 qreal oldPadding = q->leftPadding();
1067 if (!reset || extra.isAllocated()) {
1068 extra.value().leftPadding = value;
1069 extra.value().explicitLeftPadding = !reset;
1071 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
1073 q->updateWholeDocument();
1074 emit q->leftPaddingChanged();
1078void QQuickTextEditPrivate::setRightPadding(qreal value,
bool reset)
1080 Q_Q(QQuickTextEdit);
1081 qreal oldPadding = q->rightPadding();
1082 if (!reset || extra.isAllocated()) {
1083 extra.value().rightPadding = value;
1084 extra.value().explicitRightPadding = !reset;
1086 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
1088 q->updateWholeDocument();
1089 emit q->rightPaddingChanged();
1093void QQuickTextEditPrivate::setBottomPadding(qreal value,
bool reset)
1095 Q_Q(QQuickTextEdit);
1096 qreal oldPadding = q->bottomPadding();
1097 if (!reset || extra.isAllocated()) {
1098 extra.value().bottomPadding = value;
1099 extra.value().explicitBottomPadding = !reset;
1101 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
1103 q->updateWholeDocument();
1104 emit q->bottomPaddingChanged();
1108bool QQuickTextEditPrivate::isImplicitResizeEnabled()
const
1110 return !extra.isAllocated() || extra->implicitResize;
1113void QQuickTextEditPrivate::setImplicitResizeEnabled(
bool enabled)
1116 extra.value().implicitResize =
false;
1117 else if (extra.isAllocated())
1118 extra->implicitResize =
true;
1121QQuickTextEdit::VAlignment QQuickTextEdit::vAlign()
const
1123 Q_D(
const QQuickTextEdit);
1127void QQuickTextEdit::setVAlign(QQuickTextEdit::VAlignment alignment)
1129 Q_D(QQuickTextEdit);
1130 if (alignment == d->vAlign)
1132 d->vAlign = alignment;
1133 d->updateDefaultTextOption();
1135 moveCursorDelegate();
1136 emit verticalAlignmentChanged(d->vAlign);
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159QQuickTextEdit::WrapMode QQuickTextEdit::wrapMode()
const
1161 Q_D(
const QQuickTextEdit);
1165void QQuickTextEdit::setWrapMode(WrapMode mode)
1167 Q_D(QQuickTextEdit);
1168 if (mode == d->wrapMode)
1171 d->updateDefaultTextOption();
1173 emit wrapModeChanged();
1177
1178
1179
1180
1181int QQuickTextEdit::lineCount()
const
1183 Q_D(
const QQuickTextEdit);
1184 return d->lineCount;
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1199int QQuickTextEdit::length()
const
1201 Q_D(
const QQuickTextEdit);
1203 return qMax(0, d->document->characterCount() - 1);
1207
1208
1209
1210
1211
1212qreal QQuickTextEdit::contentWidth()
const
1214 Q_D(
const QQuickTextEdit);
1215 return d->contentSize.width();
1219
1220
1221
1222
1223
1224qreal QQuickTextEdit::contentHeight()
const
1226 Q_D(
const QQuickTextEdit);
1227 return d->contentSize.height();
1231
1232
1233
1234
1235
1236
1237
1239QUrl QQuickTextEdit::baseUrl()
const
1241 Q_D(
const QQuickTextEdit);
1242 if (d->baseUrl.isEmpty()) {
1243 if (QQmlContext *context = qmlContext(
this))
1244 const_cast<QQuickTextEditPrivate *>(d)->baseUrl = context->baseUrl();
1249void QQuickTextEdit::setBaseUrl(
const QUrl &url)
1251 Q_D(QQuickTextEdit);
1252 if (baseUrl() != url) {
1255 d->document->setBaseUrl(url);
1256 emit baseUrlChanged();
1260void QQuickTextEdit::resetBaseUrl()
1262 if (QQmlContext *context = qmlContext(
this))
1263 setBaseUrl(context->baseUrl());
1269
1270
1271
1272
1273
1274
1275QRectF QQuickTextEdit::positionToRectangle(
int pos)
const
1277 Q_D(
const QQuickTextEdit);
1278 QTextCursor c(d->document);
1280 return d->control->cursorRect(c).translated(d->xoff, d->yoff);
1285
1286
1287
1288
1289
1290
1291
1292int QQuickTextEdit::positionAt(qreal x, qreal y)
const
1294 Q_D(
const QQuickTextEdit);
1298 int r = d->document->documentLayout()->hitTest(QPointF(x, y), Qt::FuzzyHit);
1300 QTextCursor cursor = d->control->textCursor();
1301 if (r > cursor.position()) {
1305 QTextLayout *layout = cursor.block().layout();
1306 const int preeditLength = layout
1307 ? layout->preeditAreaText().size()
1309 if (preeditLength > 0
1310 && d->document->documentLayout()->blockBoundingRect(cursor.block()).contains(x, y)) {
1311 r = r > cursor.position() + preeditLength
1313 : cursor.position();
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330QQuickTextSelection *QQuickTextEdit::cursorSelection()
const
1332 Q_D(
const QQuickTextEdit);
1333 if (!d->cursorSelection)
1334 d->cursorSelection =
new QQuickTextSelection(
const_cast<QQuickTextEdit *>(
this));
1335 return d->cursorSelection;
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
1373
1374void QQuickTextEdit::moveCursorSelection(
int pos)
1377 Q_D(QQuickTextEdit);
1378 QTextCursor cursor = d->control->textCursor();
1379 if (cursor.position() == pos)
1381 cursor.setPosition(pos, QTextCursor::KeepAnchor);
1382 d->control->setTextCursor(cursor);
1385void QQuickTextEdit::moveCursorSelection(
int pos, SelectionMode mode)
1387 Q_D(QQuickTextEdit);
1388 QTextCursor cursor = d->control->textCursor();
1389 if (cursor.position() == pos)
1391 if (mode == SelectCharacters) {
1392 cursor.setPosition(pos, QTextCursor::KeepAnchor);
1393 }
else if (cursor.anchor() < pos || (cursor.anchor() == pos && cursor.position() < pos)) {
1394 if (cursor.anchor() > cursor.position()) {
1395 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1396 cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
1397 if (cursor.position() == cursor.anchor())
1398 cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::MoveAnchor);
1400 cursor.setPosition(cursor.position(), QTextCursor::MoveAnchor);
1402 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1403 cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor);
1406 cursor.setPosition(pos, QTextCursor::KeepAnchor);
1407 cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
1408 if (cursor.position() != pos)
1409 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
1410 }
else if (cursor.anchor() > pos || (cursor.anchor() == pos && cursor.position() > pos)) {
1411 if (cursor.anchor() < cursor.position()) {
1412 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1413 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::MoveAnchor);
1415 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1416 cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
1417 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
1418 if (cursor.position() != cursor.anchor()) {
1419 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1420 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::MoveAnchor);
1424 cursor.setPosition(pos, QTextCursor::KeepAnchor);
1425 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
1426 if (cursor.position() != pos) {
1427 cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
1428 cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
1431 d->control->setTextCursor(cursor);
1435
1436
1437
1438
1439
1440
1441bool QQuickTextEdit::isCursorVisible()
const
1443 Q_D(
const QQuickTextEdit);
1444 return d->cursorVisible;
1447void QQuickTextEdit::setCursorVisible(
bool on)
1449 Q_D(QQuickTextEdit);
1450 if (d->cursorVisible == on)
1452 d->cursorVisible = on;
1453 if (on && isComponentComplete())
1454 QQuickTextUtil::createCursor(d);
1455 if (!on && !d->persistentSelection)
1456 d->control->setCursorIsFocusIndicator(
true);
1457 d->control->setCursorVisible(on);
1458 emit cursorVisibleChanged(d->cursorVisible);
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473int QQuickTextEdit::cursorPosition()
const
1475 Q_D(
const QQuickTextEdit);
1476 return d->control->textCursor().position();
1479void QQuickTextEdit::setCursorPosition(
int pos)
1481 Q_D(QQuickTextEdit);
1482 if (pos < 0 || pos >= d->document->characterCount())
1484 QTextCursor cursor = d->control->textCursor();
1485 if (cursor.position() == pos && cursor.anchor() == pos)
1487 cursor.setPosition(pos);
1488 d->control->setTextCursor(cursor);
1489 d->control->updateCursorRectangle(
true);
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505QQmlComponent* QQuickTextEdit::cursorDelegate()
const
1507 Q_D(
const QQuickTextEdit);
1508 return d->cursorComponent;
1511void QQuickTextEdit::setCursorDelegate(QQmlComponent* c)
1513 Q_D(QQuickTextEdit);
1514 QQuickTextUtil::setCursorDelegate(d, c);
1517void QQuickTextEdit::createCursor()
1519 Q_D(QQuickTextEdit);
1520 d->cursorPending =
true;
1521 QQuickTextUtil::createCursor(d);
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534int QQuickTextEdit::selectionStart()
const
1536 Q_D(
const QQuickTextEdit);
1537 return d->control->textCursor().selectionStart();
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550int QQuickTextEdit::selectionEnd()
const
1552 Q_D(
const QQuickTextEdit);
1553 return d->control->textCursor().selectionEnd();
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570QString QQuickTextEdit::selectedText()
const
1572 Q_D(
const QQuickTextEdit);
1573#if QT_CONFIG(texthtmlparser)
1574 return d->richText || d->markdownText
1575 ? d->control->textCursor().selectedText()
1576 : d->control->textCursor().selection().toPlainText();
1578 return d->control->textCursor().selection().toPlainText();
1583
1584
1585
1586
1587
1588bool QQuickTextEdit::focusOnPress()
const
1590 Q_D(
const QQuickTextEdit);
1591 return d->focusOnPress;
1594void QQuickTextEdit::setFocusOnPress(
bool on)
1596 Q_D(QQuickTextEdit);
1597 if (d->focusOnPress == on)
1599 d->focusOnPress = on;
1600 emit activeFocusOnPressChanged(d->focusOnPress);
1604
1605
1606
1607
1608
1609bool QQuickTextEdit::persistentSelection()
const
1611 Q_D(
const QQuickTextEdit);
1612 return d->persistentSelection;
1615void QQuickTextEdit::setPersistentSelection(
bool on)
1617 Q_D(QQuickTextEdit);
1618 if (d->persistentSelection == on)
1620 d->persistentSelection = on;
1621 emit persistentSelectionChanged(d->persistentSelection);
1625
1626
1627
1628
1629qreal QQuickTextEdit::textMargin()
const
1631 Q_D(
const QQuickTextEdit);
1632 return d->textMargin;
1635void QQuickTextEdit::setTextMargin(qreal margin)
1637 Q_D(QQuickTextEdit);
1638 if (d->textMargin == margin)
1640 d->textMargin = margin;
1641 d->document->setDocumentMargin(d->textMargin);
1642 emit textMarginChanged(d->textMargin);
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
1680
1682Qt::InputMethodHints QQuickTextEdit::inputMethodHints()
const
1687 Q_D(
const QQuickTextEdit);
1688 return d->inputMethodHints;
1692void QQuickTextEdit::setInputMethodHints(Qt::InputMethodHints hints)
1697 Q_D(QQuickTextEdit);
1699 if (hints == d->inputMethodHints)
1702 d->inputMethodHints = hints;
1703 updateInputMethod(Qt::ImHints);
1704 emit inputMethodHintsChanged();
1708void QQuickTextEdit::geometryChange(
const QRectF &newGeometry,
const QRectF &oldGeometry)
1710 Q_D(QQuickTextEdit);
1711 if (!d->inLayout && ((newGeometry.width() != oldGeometry.width())
1712 || (newGeometry.height() != oldGeometry.height()))) {
1714 updateWholeDocument();
1715 if (widthValid() || heightValid())
1716 moveCursorDelegate();
1718 QQuickImplicitSizeItem::geometryChange(newGeometry, oldGeometry);
1721void QQuickTextEdit::itemChange(ItemChange change,
const ItemChangeData &value)
1723 Q_D(QQuickTextEdit);
1726 case ItemDevicePixelRatioHasChanged:
1727 if (d->containsUnscalableGlyphs) {
1732 updateWholeDocument();
1739 QQuickImplicitSizeItem::itemChange(change, value);
1743
1744
1745
1746void QQuickTextEdit::componentComplete()
1748 Q_D(QQuickTextEdit);
1749 QQuickImplicitSizeItem::componentComplete();
1751 const QUrl url = baseUrl();
1752 const QQmlContext *context = qmlContext(
this);
1753 d->document->setBaseUrl(context ? context->resolvedUrl(url) : url);
1754 if (!d->text.isEmpty()) {
1755#if QT_CONFIG(texthtmlparser)
1757 d->control->setHtml(d->text);
1760#if QT_CONFIG(textmarkdownreader)
1761 if (d->markdownText)
1762 d->control->setMarkdownText(d->text);
1765 d->control->setPlainText(d->text);
1769 d->determineHorizontalAlignment();
1770 d->updateDefaultTextOption();
1774 if (d->cursorComponent && isCursorVisible())
1775 QQuickTextUtil::createCursor(d);
1778#if QT_CONFIG(accessibility)
1779 if (QAccessible::isActive())
1780 d->accessibilityActiveChanged(
true);
1784int QQuickTextEdit::resourcesLoading()
const
1786 Q_D(
const QQuickTextEdit);
1787 return d->pixmapsInProgress.size();
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804bool QQuickTextEdit::selectByKeyboard()
const
1806 Q_D(
const QQuickTextEdit);
1807 if (d->selectByKeyboardSet)
1808 return d->selectByKeyboard;
1809 return !isReadOnly();
1812void QQuickTextEdit::setSelectByKeyboard(
bool on)
1814 Q_D(QQuickTextEdit);
1815 bool was = selectByKeyboard();
1816 if (!d->selectByKeyboardSet || was != on) {
1817 d->selectByKeyboardSet =
true;
1818 d->selectByKeyboard = on;
1820 d->control->setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByKeyboard);
1822 d->control->setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByKeyboard);
1823 emit selectByKeyboardChanged(on);
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850bool QQuickTextEdit::selectByMouse()
const
1852 Q_D(
const QQuickTextEdit);
1853 return d->selectByMouse;
1856void QQuickTextEdit::setSelectByMouse(
bool on)
1858 Q_D(QQuickTextEdit);
1859 if (d->selectByMouse == on)
1862 d->selectByMouse = on;
1863 setKeepMouseGrab(on);
1865 d->control->setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByMouse);
1867 d->control->setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByMouse);
1869#if QT_CONFIG(cursor)
1870 d->updateMouseCursorShape();
1872 emit selectByMouseChanged(on);
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885QQuickTextEdit::SelectionMode QQuickTextEdit::mouseSelectionMode()
const
1887 Q_D(
const QQuickTextEdit);
1888 return d->mouseSelectionMode;
1891void QQuickTextEdit::setMouseSelectionMode(SelectionMode mode)
1893 Q_D(QQuickTextEdit);
1894 if (d->mouseSelectionMode != mode) {
1895 d->mouseSelectionMode = mode;
1896 d->control->setWordSelectionEnabled(mode == SelectWords);
1897 emit mouseSelectionModeChanged(mode);
1902
1903
1904
1905
1906
1907
1908
1909void QQuickTextEdit::setReadOnly(
bool r)
1911 Q_D(QQuickTextEdit);
1912 if (r == isReadOnly())
1916 setFlag(QQuickItem::ItemAcceptsInputMethod, !r);
1918 Qt::TextInteractionFlags flags = Qt::LinksAccessibleByMouse;
1919 if (d->selectByMouse)
1920 flags = flags | Qt::TextSelectableByMouse;
1921 if (d->selectByKeyboardSet && d->selectByKeyboard)
1922 flags = flags | Qt::TextSelectableByKeyboard;
1923 else if (!d->selectByKeyboardSet && !r)
1924 flags = flags | Qt::TextSelectableByKeyboard;
1926 flags = flags | Qt::TextEditable;
1927 d->control->setTextInteractionFlags(flags);
1928 d->control->moveCursor(QTextCursor::End);
1931 updateInputMethod(Qt::ImEnabled);
1933#if QT_CONFIG(cursor)
1934 d->updateMouseCursorShape();
1936 q_canPasteChanged();
1937 emit readOnlyChanged(r);
1938 if (!d->selectByKeyboardSet)
1939 emit selectByKeyboardChanged(!r);
1941 setCursorVisible(
false);
1942 }
else if (hasActiveFocus()) {
1943 setCursorVisible(
true);
1946#if QT_CONFIG(accessibility)
1947 if (QAccessible::isActive()) {
1948 if (QQuickAccessibleAttached *accessibleAttached = QQuickAccessibleAttached::attachedProperties(
this))
1949 accessibleAttached->set_readOnly(r);
1954bool QQuickTextEdit::isReadOnly()
const
1956 Q_D(
const QQuickTextEdit);
1957 return !(d->control->textInteractionFlags() & Qt::TextEditable);
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970QRectF QQuickTextEdit::cursorRectangle()
const
1972 Q_D(
const QQuickTextEdit);
1973 return d->control->cursorRect().translated(d->xoff, d->yoff);
1976bool QQuickTextEdit::event(QEvent *event)
1978 Q_D(QQuickTextEdit);
1979 bool state = QQuickImplicitSizeItem::event(event);
1980 if (event->type() == QEvent::ShortcutOverride && !event->isAccepted()) {
1981 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001bool QQuickTextEdit::overwriteMode()
const
2003 Q_D(
const QQuickTextEdit);
2004 return d->control->overwriteMode();
2007void QQuickTextEdit::setOverwriteMode(
bool overwrite)
2009 Q_D(QQuickTextEdit);
2010 d->control->setOverwriteMode(overwrite);
2014
2015
2016
2017void QQuickTextEdit::keyPressEvent(QKeyEvent *event)
2019 Q_D(QQuickTextEdit);
2020 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2021 if (!event->isAccepted())
2022 QQuickImplicitSizeItem::keyPressEvent(event);
2026
2027
2028
2029void QQuickTextEdit::keyReleaseEvent(QKeyEvent *event)
2031 Q_D(QQuickTextEdit);
2032 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2033 if (!event->isAccepted())
2034 QQuickImplicitSizeItem::keyReleaseEvent(event);
2038
2039
2040
2041
2042void QQuickTextEdit::deselect()
2044 Q_D(QQuickTextEdit);
2045 QTextCursor c = d->control->textCursor();
2047 d->control->setTextCursor(c);
2051
2052
2053
2054
2055void QQuickTextEdit::selectAll()
2057 Q_D(QQuickTextEdit);
2058 d->control->selectAll();
2062
2063
2064
2065
2066void QQuickTextEdit::selectWord()
2068 Q_D(QQuickTextEdit);
2069 QTextCursor c = d->control->textCursor();
2070 c.select(QTextCursor::WordUnderCursor);
2071 d->control->setTextCursor(c);
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087void QQuickTextEdit::select(
int start,
int end)
2089 Q_D(QQuickTextEdit);
2090 if (start < 0 || end < 0 || start >= d->document->characterCount() || end >= d->document->characterCount())
2092 QTextCursor cursor = d->control->textCursor();
2093 cursor.beginEditBlock();
2094 cursor.setPosition(start, QTextCursor::MoveAnchor);
2095 cursor.setPosition(end, QTextCursor::KeepAnchor);
2096 cursor.endEditBlock();
2097 d->control->setTextCursor(cursor);
2102 updateInputMethod();
2107
2108
2109
2110
2111
2112bool QQuickTextEdit::isRightToLeft(
int start,
int end)
2115 qmlWarning(
this) <<
"isRightToLeft(start, end) called with the end property being smaller than the start.";
2118 return getText(start, end).isRightToLeft();
2122#if QT_CONFIG(clipboard)
2124
2125
2126
2127
2128void QQuickTextEdit::cut()
2130 Q_D(QQuickTextEdit);
2135
2136
2137
2138
2139void QQuickTextEdit::copy()
2141 Q_D(QQuickTextEdit);
2146
2147
2148
2149
2150void QQuickTextEdit::paste()
2152 Q_D(QQuickTextEdit);
2153 d->control->paste();
2159
2160
2161
2162
2163
2164
2166void QQuickTextEdit::undo()
2168 Q_D(QQuickTextEdit);
2173
2174
2175
2176
2178void QQuickTextEdit::redo()
2180 Q_D(QQuickTextEdit);
2185
2186
2187
2188void QQuickTextEdit::mousePressEvent(QMouseEvent *event)
2190 Q_D(QQuickTextEdit);
2191 const bool isMouse = QQuickDeliveryAgentPrivate::isEventFromMouseOrTouchpad(event);
2192 setKeepMouseGrab(d->selectByMouse && isMouse);
2193 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2194 if (d->focusOnPress){
2195 bool hadActiveFocus = hasActiveFocus();
2196 forceActiveFocus(Qt::MouseFocusReason);
2199 if (hasActiveFocus() && hadActiveFocus && !isReadOnly())
2200 qGuiApp->inputMethod()->show();
2202 Q_UNUSED(hadActiveFocus);
2205 if (!event->isAccepted())
2206 QQuickImplicitSizeItem::mousePressEvent(event);
2210
2211
2212
2213void QQuickTextEdit::mouseReleaseEvent(QMouseEvent *event)
2215 Q_D(QQuickTextEdit);
2216 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2218 if (!event->isAccepted())
2219 QQuickImplicitSizeItem::mouseReleaseEvent(event);
2223
2224
2225
2226void QQuickTextEdit::mouseDoubleClickEvent(QMouseEvent *event)
2228 Q_D(QQuickTextEdit);
2229 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2230 if (!event->isAccepted())
2231 QQuickImplicitSizeItem::mouseDoubleClickEvent(event);
2235
2236
2237
2238void QQuickTextEdit::mouseMoveEvent(QMouseEvent *event)
2240 Q_D(QQuickTextEdit);
2241 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2242 if (!event->isAccepted())
2243 QQuickImplicitSizeItem::mouseMoveEvent(event);
2248
2249
2250
2251void QQuickTextEdit::inputMethodEvent(QInputMethodEvent *event)
2253 Q_D(QQuickTextEdit);
2254 const bool wasComposing = isInputMethodComposing();
2255 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2256 setCursorVisible(d->control->cursorVisible());
2257 if (wasComposing != isInputMethodComposing())
2258 emit inputMethodComposingChanged();
2262
2263
2264
2265QVariant QQuickTextEdit::inputMethodQuery(Qt::InputMethodQuery property, QVariant argument)
const
2267 Q_D(
const QQuickTextEdit);
2272 v = (
bool)(flags() & ItemAcceptsInputMethod);
2275 v = (
int)d->effectiveInputMethodHints();
2277 case Qt::ImInputItemClipRectangle:
2278 v = QQuickItem::inputMethodQuery(property);
2280 case Qt::ImReadOnly:
2284 if (property == Qt::ImCursorPosition && !argument.isNull())
2285 argument = QVariant(argument.toPointF() - QPointF(d->xoff, d->yoff));
2286 v = d->control->inputMethodQuery(property, argument);
2287 if (property == Qt::ImCursorRectangle || property == Qt::ImAnchorRectangle)
2288 v = QVariant(v.toRectF().translated(d->xoff, d->yoff));
2295
2296
2297
2298QVariant QQuickTextEdit::inputMethodQuery(Qt::InputMethodQuery property)
const
2300 return inputMethodQuery(property, QVariant());
2304void QQuickTextEdit::triggerPreprocess()
2306 Q_D(QQuickTextEdit);
2307 if (d->updateType == QQuickTextEditPrivate::UpdateNone)
2308 d->updateType = QQuickTextEditPrivate::UpdateOnlyPreprocess;
2314
2315
2316
2317
2318QVariant QQuickTextEdit::loadResource(
int type,
const QUrl &source)
2320 Q_D(QQuickTextEdit);
2321 const QUrl url = d->document->baseUrl().resolved(source);
2322 if (url.isLocalFile()) {
2324 QFileInfo fi(QQmlFile::urlToLocalFileOrQrc(url));
2326 qmlWarning(
this) <<
"Cannot open: " << url.toString();
2332 if (!url.scheme().compare(
"qrc"_L1, Qt::CaseInsensitive)) {
2334 QFile f(QQmlFile::urlToLocalFileOrQrc(url));
2335 if (f.open(QFile::ReadOnly)) {
2336 QByteArray buf = f.readAll();
2339 image.loadFromData(buf);
2340 if (!image.isNull())
2344 qmlWarning(
this) <<
"Cannot read resource: " << f.fileName();
2349 auto existingJobIter = std::find_if(
2350 d->pixmapsInProgress.cbegin(), d->pixmapsInProgress.cend(),
2351 [&url](
const auto *job) {
return job->url() == url; } );
2352 if (existingJobIter != d->pixmapsInProgress.cend()) {
2353 const QQuickPixmap *job = *existingJobIter;
2354 if (job->isError()) {
2355 qmlWarning(
this) << job->error();
2356 d->pixmapsInProgress.erase(existingJobIter);
2360 qCDebug(lcTextEdit) <<
"already downloading" << url;
2362 return job->isReady() ? job->image() : QVariant();
2367 qCDebug(lcTextEdit) <<
"loading" << source <<
"resolved" << url
2368 <<
"type" <<
static_cast<QTextDocument::ResourceType>(type);
2369 QQmlContext *context = qmlContext(
this);
2372 QQuickPixmap *p =
new QQuickPixmap(context->engine(), url, QQuickPixmap::Options{});
2373 p->connectFinished(
this, SLOT(resourceRequestFinished()));
2374 d->pixmapsInProgress.append(p);
2376 return p->isReady() ? p->image() : QVariant();
2380
2381
2382void QQuickTextEdit::resourceRequestFinished()
2384 Q_D(QQuickTextEdit);
2385 for (
auto it = d->pixmapsInProgress.cbegin(); it != d->pixmapsInProgress.cend(); ++it) {
2387 if (job->isError()) {
2389 qCDebug(lcTextEdit) <<
"failed to load (error)" << job->url();
2390 d->document->resource(QTextDocument::ImageResource, job->url());
2394 }
else if (job->isReady()) {
2396 auto res = d->document->resource(QTextDocument::ImageResource, job->url());
2398 qCDebug(lcTextEdit) << (res.isValid() ?
"done downloading" :
"failed to load") << job->url() << job->rect();
2399 d->pixmapsInProgress.erase(it);
2404 if (d->pixmapsInProgress.isEmpty()) {
2412using TextNodeIterator = QQuickTextEditPrivate::TextNodeIterator;
2416 return n1.startPos() < n2.startPos();
2421 QMatrix4x4 transformMatrix;
2422 transformMatrix.translate(topLeft.x(), topLeft.y());
2423 node->setMatrix(transformMatrix);
2427
2428
2429
2430
2431
2432void QQuickTextEdit::invalidateFontCaches()
2434 Q_D(QQuickTextEdit);
2435 if (d->document ==
nullptr)
2439 for (block = d->document->firstBlock(); block.isValid(); block = block.next()) {
2440 if (block.layout() !=
nullptr && block.layout()->engine() !=
nullptr)
2441 block.layout()->engine()->resetFontEngineCache();
2445QTextDocument *QQuickTextEdit::document()
const
2447 Q_D(
const QQuickTextEdit);
2451void QQuickTextEdit::setDocument(QTextDocument *doc)
2453 Q_D(QQuickTextEdit);
2455 std::unique_ptr<QTextDocument> cleanup(d->ownsDocument ? d->document :
nullptr);
2457 d->ownsDocument =
false;
2458 d->control->setDocument(doc);
2468 engine->setDevicePixelRatio(dpr);
2471QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData)
2473 Q_UNUSED(updatePaintNodeData);
2474 Q_D(QQuickTextEdit);
2476 if (d->updateType != QQuickTextEditPrivate::UpdatePaintNode
2477 && d->updateType != QQuickTextEditPrivate::UpdateAll
2478 && oldNode !=
nullptr) {
2480 d->updateType = QQuickTextEditPrivate::UpdateNone;
2484 d->containsUnscalableGlyphs =
false;
2485 if (!oldNode || d->updateType == QQuickTextEditPrivate::UpdateAll) {
2491 d->textNodeMap.clear();
2494 d->updateType = QQuickTextEditPrivate::UpdateNone;
2496 RootNode *rootNode =
static_cast<RootNode *>(oldNode);
2497 TextNodeIterator nodeIterator = d->textNodeMap.begin();
2498 std::optional<
int> firstPosAcrossAllNodes;
2499 if (nodeIterator != d->textNodeMap.end())
2500 firstPosAcrossAllNodes = nodeIterator->startPos();
2502 while (nodeIterator != d->textNodeMap.end() && !nodeIterator->dirty())
2505 const auto dpr = d->effectiveDevicePixelRatio();
2506 QQuickTextNodeEngine engine;
2507 engine.setDevicePixelRatio(dpr);
2508 QQuickTextNodeEngine frameDecorationsEngine;
2509 frameDecorationsEngine.setDevicePixelRatio(dpr);
2511 if (!oldNode || nodeIterator < d->textNodeMap.end() || d->textNodeMap.isEmpty()) {
2514 rootNode =
new RootNode;
2516 int firstDirtyPos = 0;
2517 if (nodeIterator != d->textNodeMap.end()) {
2518 firstDirtyPos = nodeIterator->startPos();
2521 QSGInternalTextNode *firstCleanNode =
nullptr;
2522 auto it = d->textNodeMap.constEnd();
2523 while (it != nodeIterator) {
2527 firstCleanNode = it->textNode();
2530 rootNode->removeChildNode(nodeIterator->textNode());
2531 delete nodeIterator->textNode();
2532 nodeIterator = d->textNodeMap.erase(nodeIterator);
2533 }
while (nodeIterator != d->textNodeMap.constEnd() && nodeIterator->textNode() != firstCleanNode);
2538 if (flags().testFlag(QQuickItem::ItemObservesViewport)) {
2539 viewport = clipRect();
2540 qCDebug(lcVP) <<
"text viewport" << viewport;
2544 rootNode->resetFrameDecorations(d->createTextNode());
2545 resetEngine(&frameDecorationsEngine, d->color, d->selectedTextColor, d->selectionColor, dpr);
2547 QSGInternalTextNode *node =
nullptr;
2549 int currentNodeSize = 0;
2550 int nodeStart = firstDirtyPos;
2551 QPointF basePosition(d->xoff, d->yoff);
2552 QMatrix4x4 basePositionMatrix;
2553 basePositionMatrix.translate(basePosition.x(), basePosition.y());
2554 rootNode->setMatrix(basePositionMatrix);
2557 const TextNode firstCleanNode = (nodeIterator != d->textNodeMap.end()) ? *nodeIterator
2560 QList<QTextFrame *> frames;
2561 frames.append(d->document->rootFrame());
2564 d->firstBlockInViewport = -1;
2565 d->firstBlockPastViewport = -1;
2566 int frameCount = -1;
2567 while (!frames.isEmpty()) {
2568 QTextFrame *textFrame = frames.takeFirst();
2572 qCDebug(lcVP) <<
"frame" << frameCount << textFrame
2573 <<
"from" << positionToRectangle(textFrame->firstPosition()).topLeft()
2574 <<
"to" << positionToRectangle(textFrame->lastPosition()).bottomRight();
2575 frames.append(textFrame->childFrames());
2576 frameDecorationsEngine.addFrameDecorations(d->document, textFrame);
2577 resetEngine(&engine, d->color, d->selectedTextColor, d->selectionColor, dpr);
2579 if (textFrame->firstPosition() > textFrame->lastPosition()
2580 && textFrame->frameFormat().position() != QTextFrameFormat::InFlow) {
2581 node = d->createTextNode();
2582 updateNodeTransform(node, d->document->documentLayout()->frameBoundingRect(textFrame).topLeft());
2583 const int pos = textFrame->firstPosition() - 1;
2584 auto *a =
static_cast<QtPrivate::ProtectedLayoutAccessor *>(d->document->documentLayout());
2585 QTextCharFormat format = a->formatAccessor(pos);
2586 QTextBlock block = textFrame->firstCursorPosition().block();
2587 nodeOffset = d->document->documentLayout()->blockBoundingRect(block).topLeft();
2589 if (!viewport.isNull() && block.layout()) {
2590 QRectF coveredRegion = block.layout()->boundingRect().adjusted(nodeOffset.x(), nodeOffset.y(), nodeOffset.x(), nodeOffset.y());
2591 inView = coveredRegion.bottom() >= viewport.top() && coveredRegion.top() <= viewport.bottom();
2592 qCDebug(lcVP) <<
"non-flow frame" << coveredRegion <<
"in viewport?" << inView;
2595 engine.setCurrentLine(block.layout()->lineForTextPosition(pos - block.position()));
2596 engine.addTextObject(block, QPointF(0, 0), format, QQuickTextNodeEngine::Unselected, d->document,
2597 pos, textFrame->frameFormat().position());
2602 QVarLengthArray<
int, 8> frameBoundaries;
2603 frameBoundaries.reserve(frames.size());
2604 for (QTextFrame *frame : std::as_const(frames))
2605 frameBoundaries.append(frame->firstPosition());
2606 std::sort(frameBoundaries.begin(), frameBoundaries.end());
2608 QTextFrame::iterator it = textFrame->begin();
2609 while (!it.atEnd()) {
2610 QTextBlock block = it.currentBlock();
2611 if (block.position() < firstDirtyPos) {
2616 if (!engine.hasContents())
2617 nodeOffset = d->document->documentLayout()->blockBoundingRect(block).topLeft();
2620 if (!viewport.isNull()) {
2621 QRectF coveredRegion;
2622 if (block.layout()) {
2623 coveredRegion = block.layout()->boundingRect().adjusted(nodeOffset.x(), nodeOffset.y(), nodeOffset.x(), nodeOffset.y());
2624 inView = coveredRegion.bottom() > viewport.top();
2626 const bool potentiallyScrollingBackwards = firstPosAcrossAllNodes && *firstPosAcrossAllNodes == firstDirtyPos;
2627 if (d->firstBlockInViewport < 0 && inView && potentiallyScrollingBackwards) {
2629 if (coveredRegion.top() > viewport.top() + 1) {
2630 qCDebug(lcVP) <<
"checking backwards from block" << block.blockNumber() <<
"@" << nodeOffset.y() << coveredRegion;
2631 while (it != textFrame->begin() && it.currentBlock().layout() &&
2632 it.currentBlock().layout()->boundingRect().top() + nodeOffset.y() > viewport.top()) {
2633 nodeOffset = d->document->documentLayout()->blockBoundingRect(it.currentBlock()).topLeft();
2636 if (!it.currentBlock().layout())
2638 if (Q_LIKELY(it.currentBlock().layout())) {
2639 block = it.currentBlock();
2640 coveredRegion = block.layout()->boundingRect().adjusted(nodeOffset.x(), nodeOffset.y(), nodeOffset.x(), nodeOffset.y());
2641 firstDirtyPos = it.currentBlock().position();
2643 qCWarning(lcVP) <<
"failed to find a text block with layout during back-scrolling";
2646 qCDebug(lcVP) <<
"first block in viewport" << block.blockNumber() <<
"@" << nodeOffset.y() << coveredRegion;
2648 d->renderedRegion = coveredRegion;
2650 if (nodeOffset.y() > viewport.bottom()) {
2652 if (d->firstBlockInViewport >= 0 && d->firstBlockPastViewport < 0) {
2653 qCDebug(lcVP) <<
"first block past viewport" << viewport << block.blockNumber()
2654 <<
"@" << nodeOffset.y() <<
"total region rendered" << d->renderedRegion;
2655 d->firstBlockPastViewport = block.blockNumber();
2659 if (inView && !block.text().isEmpty() && coveredRegion.isValid()) {
2660 d->renderedRegion = d->renderedRegion.united(coveredRegion);
2663 if (!frames.isEmpty())
2664 viewport = viewport.united(d->renderedRegion);
2667 if (inView && d->firstBlockInViewport < 0)
2668 d->firstBlockInViewport = block.blockNumber();
2671 bool createdNodeInView =
false;
2673 if (!engine.hasContents()) {
2675 d->containsUnscalableGlyphs = d->containsUnscalableGlyphs
2676 || node->containsUnscalableGlyphs();
2677 if (!node->parent())
2678 d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart);
2680 node = d->createTextNode();
2681 createdNodeInView =
true;
2682 updateNodeTransform(node, nodeOffset);
2683 nodeStart = block.position();
2685 engine.addTextBlock(d->document, block, -nodeOffset, d->color, QColor(), selectionStart(), selectionEnd() - 1);
2686 currentNodeSize += block.length();
2689 if ((it.atEnd()) || block.next().position() >= firstCleanNode.startPos())
2691 const auto lowerBound =
2692 std::lower_bound(frameBoundaries.constBegin(),
2693 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(),
3330 q->mapToScene(q->cursorRectangle().center()).toPoint(), event->globalPos(),
3331 event->modifiers());
3332 const bool eventProcessed = QQuickItemPrivate::handleContextMenuEvent(&mapped);
3333 event->setAccepted(mapped.isAccepted());
3334 return eventProcessed;
3337void QQuickTextEditPrivate::handleFocusEvent(QFocusEvent *event)
3339 Q_Q(QQuickTextEdit);
3340 bool focus = event->type() == QEvent::FocusIn;
3341 if (!q->isReadOnly())
3342 q->setCursorVisible(focus);
3343 control->processEvent(event, QPointF(-xoff, -yoff));
3345 q->q_updateAlignment();
3347 if (focusOnPress && !q->isReadOnly())
3348 qGuiApp->inputMethod()->show();
3349 q->connect(QGuiApplication::inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
3350 q, SLOT(q_updateAlignment()));
3354 q->disconnect(QGuiApplication::inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
3355 q, SLOT(q_updateAlignment()));
3357 if (event->reason() != Qt::ActiveWindowFocusReason
3358 && event->reason() != Qt::PopupFocusReason
3359 && control->textCursor().hasSelection()
3360 && !persistentSelection)
3363 emit q->editingFinished();
3367void QQuickTextEditPrivate::addCurrentTextNodeToRoot(QQuickTextNodeEngine *engine, QSGTransformNode *root, QSGInternalTextNode *node, TextNodeIterator &it,
int startPos)
3369 engine->addToSceneGraph(node, QQuickText::Normal, QColor());
3370 it = textNodeMap.insert(it, TextNode(startPos, node));
3372 root->appendChildNode(node);
3373 ++renderedBlockCount;
3376QSGInternalTextNode *QQuickTextEditPrivate::createTextNode()
3378 Q_Q(QQuickTextEdit);
3379 QSGInternalTextNode* node = sceneGraphContext()->createInternalTextNode(sceneGraphRenderContext());
3380 node->setRenderType(QSGTextNode::RenderType(renderType));
3381 node->setFiltering(q->smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
3385void QQuickTextEdit::q_canPasteChanged()
3387 Q_D(QQuickTextEdit);
3388 bool old = d->canPaste;
3389 d->canPaste = d->control->canPaste();
3390 bool changed = old!=d->canPaste || !d->canPasteValid;
3391 d->canPasteValid =
true;
3393 emit canPasteChanged();
3397
3398
3399
3400
3401
3402
3404QString QQuickTextEdit::getText(
int start,
int end)
const
3406 Q_D(
const QQuickTextEdit);
3407 start = qBound(0, start, d->document->characterCount() - 1);
3408 end = qBound(0, end, d->document->characterCount() - 1);
3409 QTextCursor cursor(d->document);
3410 cursor.setPosition(start, QTextCursor::MoveAnchor);
3411 cursor.setPosition(end, QTextCursor::KeepAnchor);
3412#if QT_CONFIG(texthtmlparser)
3413 return d->richText || d->markdownText
3414 ? cursor.selectedText()
3415 : cursor.selection().toPlainText();
3417 return cursor.selection().toPlainText();
3422
3423
3424
3425
3426
3427
3429QString QQuickTextEdit::getFormattedText(
int start,
int end)
const
3431 Q_D(
const QQuickTextEdit);
3433 start = qBound(0, start, d->document->characterCount() - 1);
3434 end = qBound(0, end, d->document->characterCount() - 1);
3436 QTextCursor cursor(d->document);
3437 cursor.setPosition(start, QTextCursor::MoveAnchor);
3438 cursor.setPosition(end, QTextCursor::KeepAnchor);
3441#if QT_CONFIG(texthtmlparser)
3442 return cursor.selection().toHtml();
3444 return cursor.selection().toPlainText();
3446 }
else if (d->markdownText) {
3447#if QT_CONFIG(textmarkdownwriter)
3448 return cursor.selection().toMarkdown();
3450 return cursor.selection().toPlainText();
3453 return cursor.selection().toPlainText();
3458
3459
3460
3461
3462void QQuickTextEdit::insert(
int position,
const QString &text)
3464 Q_D(QQuickTextEdit);
3465 if (position < 0 || position >= d->document->characterCount())
3467 QTextCursor cursor(d->document);
3468 cursor.setPosition(position);
3469 d->richText = d->richText || (d->format == AutoText && Qt::mightBeRichText(text));
3471#if QT_CONFIG(texthtmlparser)
3472 cursor.insertHtml(text);
3474 cursor.insertText(text);
3476 }
else if (d->markdownText) {
3477#if QT_CONFIG(textmarkdownreader)
3478 cursor.insertMarkdown(text);
3480 cursor.insertText(text);
3483 cursor.insertText(text);
3485 d->control->updateCursorRectangle(
false);
3489
3490
3491
3492
3494void QQuickTextEdit::remove(
int start,
int end)
3496 Q_D(QQuickTextEdit);
3497 start = qBound(0, start, d->document->characterCount() - 1);
3498 end = qBound(0, end, d->document->characterCount() - 1);
3499 QTextCursor cursor(d->document);
3500 cursor.setPosition(start, QTextCursor::MoveAnchor);
3501 cursor.setPosition(end, QTextCursor::KeepAnchor);
3502 cursor.removeSelectedText();
3503 d->control->updateCursorRectangle(
false);
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516
3518QQuickTextDocument *QQuickTextEdit::textDocument()
3520 Q_D(QQuickTextEdit);
3521 if (!d->quickDocument) {
3522 d->quickDocument =
new QQuickTextDocument(
this);
3523 connect(d->quickDocument, &QQuickTextDocument::statusChanged, d->quickDocument,
3524 [d]() { d->onDocumentStatusChanged(); } );
3526 return d->quickDocument;
3529bool QQuickTextEditPrivate::isLinkHoveredConnected()
3531 Q_Q(QQuickTextEdit);
3532 IS_SIGNAL_CONNECTED(q, QQuickTextEdit, linkHovered, (
const QString &));
3535#if QT_CONFIG(cursor)
3536void QQuickTextEditPrivate::updateMouseCursorShape()
3538 Q_Q(QQuickTextEdit);
3539 q->setCursor(q->isReadOnly() && !q->selectByMouse() ? Qt::ArrowCursor : Qt::IBeamCursor);
3544
3545
3546
3547
3548
3549
3550
3551
3552
3555
3556
3557
3558
3559
3562
3563
3564
3565
3566
3567
3568
3569
3570
3573
3574
3575
3576
3577
3578
3579
3581QString QQuickTextEdit::hoveredLink()
const
3583 Q_D(
const QQuickTextEdit);
3584 if (
const_cast<QQuickTextEditPrivate *>(d)->isLinkHoveredConnected()) {
3585 return d->control->hoveredLink();
3587#if QT_CONFIG(cursor)
3588 if (QQuickWindow *wnd = window()) {
3589 QPointF pos = QCursor::pos(wnd->screen()) - wnd->position() - mapToScene(QPointF(0, 0));
3590 return d->control->anchorAt(pos);
3597void QQuickTextEdit::hoverEnterEvent(QHoverEvent *event)
3599 Q_D(QQuickTextEdit);
3600 if (d->isLinkHoveredConnected())
3601 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
3605void QQuickTextEdit::hoverMoveEvent(QHoverEvent *event)
3607 Q_D(QQuickTextEdit);
3608 if (d->isLinkHoveredConnected())
3609 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
3613void QQuickTextEdit::hoverLeaveEvent(QHoverEvent *event)
3615 Q_D(QQuickTextEdit);
3616 if (d->isLinkHoveredConnected())
3617 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
3622
3623
3624
3625
3626
3627
3628
3629
3630void QQuickTextEdit::append(
const QString &text)
3632 Q_D(QQuickTextEdit);
3633 QTextCursor cursor(d->document);
3634 cursor.beginEditBlock();
3635 cursor.movePosition(QTextCursor::End);
3637 if (!d->document->isEmpty())
3638 cursor.insertBlock();
3640 if (d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(text))) {
3641#if QT_CONFIG(texthtmlparser)
3642 cursor.insertHtml(text);
3644 cursor.insertText(text);
3646 }
else if (d->format == MarkdownText) {
3647#if QT_CONFIG(textmarkdownreader)
3648 cursor.insertMarkdown(text);
3650 cursor.insertText(text);
3653 cursor.insertText(text);
3656 cursor.endEditBlock();
3657 d->control->updateCursorRectangle(
false);
3661
3662
3663
3664
3665
3666
3667
3668
3669QString QQuickTextEdit::linkAt(qreal x, qreal y)
const
3671 Q_D(
const QQuickTextEdit);
3672 return d->control->anchorAt(QPointF(x + topPadding(), y + leftPadding()));
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686qreal QQuickTextEdit::padding()
const
3688 Q_D(
const QQuickTextEdit);
3689 return d->padding();
3692void QQuickTextEdit::setPadding(qreal padding)
3694 Q_D(QQuickTextEdit);
3695 if (qFuzzyCompare(d->padding(), padding))
3698 d->extra.value().padding = padding;
3700 if (isComponentComplete()) {
3701 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
3704 emit paddingChanged();
3705 if (!d->extra.isAllocated() || !d->extra->explicitTopPadding)
3706 emit topPaddingChanged();
3707 if (!d->extra.isAllocated() || !d->extra->explicitLeftPadding)
3708 emit leftPaddingChanged();
3709 if (!d->extra.isAllocated() || !d->extra->explicitRightPadding)
3710 emit rightPaddingChanged();
3711 if (!d->extra.isAllocated() || !d->extra->explicitBottomPadding)
3712 emit bottomPaddingChanged();
3715void QQuickTextEdit::resetPadding()
3720qreal QQuickTextEdit::topPadding()
const
3722 Q_D(
const QQuickTextEdit);
3723 if (d->extra.isAllocated() && d->extra->explicitTopPadding)
3724 return d->extra->topPadding;
3725 return d->padding();
3728void QQuickTextEdit::setTopPadding(qreal padding)
3730 Q_D(QQuickTextEdit);
3731 d->setTopPadding(padding);
3734void QQuickTextEdit::resetTopPadding()
3736 Q_D(QQuickTextEdit);
3737 d->setTopPadding(0,
true);
3740qreal QQuickTextEdit::leftPadding()
const
3742 Q_D(
const QQuickTextEdit);
3743 if (d->extra.isAllocated() && d->extra->explicitLeftPadding)
3744 return d->extra->leftPadding;
3745 return d->padding();
3748void QQuickTextEdit::setLeftPadding(qreal padding)
3750 Q_D(QQuickTextEdit);
3751 d->setLeftPadding(padding);
3754void QQuickTextEdit::resetLeftPadding()
3756 Q_D(QQuickTextEdit);
3757 d->setLeftPadding(0,
true);
3760qreal QQuickTextEdit::rightPadding()
const
3762 Q_D(
const QQuickTextEdit);
3763 if (d->extra.isAllocated() && d->extra->explicitRightPadding)
3764 return d->extra->rightPadding;
3765 return d->padding();
3768void QQuickTextEdit::setRightPadding(qreal padding)
3770 Q_D(QQuickTextEdit);
3771 d->setRightPadding(padding);
3774void QQuickTextEdit::resetRightPadding()
3776 Q_D(QQuickTextEdit);
3777 d->setRightPadding(0,
true);
3780qreal QQuickTextEdit::bottomPadding()
const
3782 Q_D(
const QQuickTextEdit);
3783 if (d->extra.isAllocated() && d->extra->explicitBottomPadding)
3784 return d->extra->bottomPadding;
3785 return d->padding();
3788void QQuickTextEdit::setBottomPadding(qreal padding)
3790 Q_D(QQuickTextEdit);
3791 d->setBottomPadding(padding);
3794void QQuickTextEdit::resetBottomPadding()
3796 Q_D(QQuickTextEdit);
3797 d->setBottomPadding(0,
true);
3801
3802
3803
3804
3805
3806
3807
3808int QQuickTextEdit::tabStopDistance()
const
3810 Q_D(
const QQuickTextEdit);
3811 return d->document->defaultTextOption().tabStopDistance();
3814void QQuickTextEdit::setTabStopDistance(qreal distance)
3816 Q_D(QQuickTextEdit);
3817 QTextOption textOptions = d->document->defaultTextOption();
3818 if (textOptions.tabStopDistance() == distance)
3821 textOptions.setTabStopDistance(distance);
3822 d->document->setDefaultTextOption(textOptions);
3823 emit tabStopDistanceChanged(distance);
3827
3828
3829
3830
3831
3832
3833
3834
3835
3836
3837void QQuickTextEdit::clear()
3839 Q_D(QQuickTextEdit);
3840 d->resetInputMethod();
3841 d->control->clear();
3844#ifndef QT_NO_DEBUG_STREAM
3847 QDebugStateSaver saver(debug);
3849 debug <<
"Node(startPos:" << n.m_startPos <<
"dirty:" << n.m_dirty << n.m_node <<
')';