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
195
196
197
198
199
200
201
202
206
207
208
209
212
213
214
215
218
219
220
221
224
225
226
227
230
231
232
233
236
237
238
239
242
243
244
245
246
247
248
249
252
253
254
255
256
257
260
261
262
263
264
265
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
285
286
287
288
289
290
291
294
295
296
297
298
301
302
303
304
305
308
309
310
311
312
315
316
317
318
319
322
323
324
325
326
329
330
331
332
333
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364void QQuickTextEdit::setText(
const QString &text)
367 if (QQuickTextEdit::text() == text)
370 d->richText = d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(text));
371 d->markdownText = d->format == MarkdownText;
372 if (!isComponentComplete()) {
374 }
else if (d->richText) {
375#if QT_CONFIG(texthtmlparser)
376 d->control->setHtml(text);
378 d->control->setPlainText(text);
380 }
else if (d->markdownText) {
381 d->control->setMarkdownText(text);
383 d->control->setPlainText(text);
385 setFlag(QQuickItem::ItemObservesViewport, text.size() > QQuickTextEditPrivate::largeTextSizeThreshold);
388void QQuickTextEdit::invalidate()
390 QMetaObject::invokeMethod(
this, &QQuickTextEdit::q_invalidate);
393void QQuickTextEdit::q_invalidate()
396 if (isComponentComplete()) {
397 if (d->document !=
nullptr)
398 d->document->markContentsDirty(0, d->document->characterCount());
399 invalidateFontCaches();
400 d->updateType = QQuickTextEditPrivate::UpdateAll;
406
407
408
409
410
411
412
413
414
415
416
417QString QQuickTextEdit::preeditText()
const
419 Q_D(
const QQuickTextEdit);
420 return d->control->preeditText();
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477QQuickTextEdit::TextFormat QQuickTextEdit::textFormat()
const
479 Q_D(
const QQuickTextEdit);
483void QQuickTextEdit::setTextFormat(TextFormat format)
486 if (format == d->format)
489 auto mightBeRichText = [
this]() {
490 return Qt::mightBeRichText(text());
493 auto findSourceFormat = [d, mightBeRichText](Qt::TextFormat detectedFormat) {
494 if (d->format == PlainText)
496 if (d->richText)
return RichText;
497 if (d->markdownText)
return MarkdownText;
498 if (detectedFormat == Qt::AutoText && mightBeRichText())
503 auto findDestinationFormat = [format, mightBeRichText](Qt::TextFormat detectedFormat, TextFormat sourceFormat) {
504 if (format == AutoText) {
505 if (detectedFormat == Qt::MarkdownText || (detectedFormat == Qt::AutoText && sourceFormat == MarkdownText))
507 if (detectedFormat == Qt::RichText || (detectedFormat == Qt::AutoText && (sourceFormat == RichText || mightBeRichText())))
514 bool textCachedChanged =
false;
515 bool converted =
false;
517 if (isComponentComplete()) {
518 Qt::TextFormat detectedFormat = Qt::AutoText;
519 if (d->quickDocument) {
522 detectedFormat = QQuickTextDocumentPrivate::get(d->quickDocument)->detectedFormat;
525 const TextFormat sourceFormat = findSourceFormat(detectedFormat);
526 const TextFormat destinationFormat = findDestinationFormat(detectedFormat, sourceFormat);
528 d->richText = destinationFormat == RichText;
529 d->markdownText = destinationFormat == MarkdownText;
532 if (format != PlainText && (sourceFormat != destinationFormat)) {
533 d->textCached =
false;
534 textCachedChanged =
true;
537 switch (destinationFormat) {
539#if QT_CONFIG(texthtmlparser)
540 if (sourceFormat == RichText) {
544 d->control->setPlainText(d->textCached ? d->text : d->control->toHtml());
548#if QT_CONFIG(textmarkdownwriter) && QT_CONFIG(textmarkdownreader)
549 if (sourceFormat == MarkdownText) {
553 d->control->setPlainText(d->textCached ? d->text : d->control->toMarkdown());
559#if QT_CONFIG(texthtmlparser)
560 switch (sourceFormat) {
563 d->control->setHtml(d->control->toHtml());
569 d->control->setHtml(d->textCached ? d->text : d->control->toPlainText());
579#if QT_CONFIG(textmarkdownwriter) && QT_CONFIG(textmarkdownreader)
580 switch (sourceFormat) {
583 d->control->setMarkdownText(d->control->toMarkdown());
589 d->control->setMarkdownText(d->textCached ? d->text : d->control->toPlainText());
605 d->richText = format == RichText || (format == AutoText && (d->richText || mightBeRichText()));
606 d->markdownText = format == MarkdownText;
609 qCDebug(lcTextEdit) << d->format <<
"->" << format
610 <<
"rich?" << d->richText <<
"md?" << d->markdownText
611 <<
"converted?" << converted <<
"cache invalidated?" << textCachedChanged;
614 d->control->setAcceptRichText(d->format != PlainText);
615 emit textFormatChanged(d->format);
616 if (textCachedChanged)
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645QQuickTextEdit::RenderType QQuickTextEdit::renderType()
const
647 Q_D(
const QQuickTextEdit);
648 return d->renderType;
651void QQuickTextEdit::setRenderType(QQuickTextEdit::RenderType renderType)
654 if (d->renderType == renderType)
657 d->renderType = renderType;
658 emit renderTypeChanged();
659 d->updateDefaultTextOption();
661 if (isComponentComplete())
665QFont QQuickTextEdit::font()
const
667 Q_D(
const QQuickTextEdit);
668 return d->sourceFont;
671void QQuickTextEdit::setFont(
const QFont &font)
674 if (d->sourceFont == font)
677 d->sourceFont = font;
678 QFont oldFont = d->font;
680 if (d->font.pointSizeF() != -1) {
682 qreal size = qRound(d->font.pointSizeF()*2.0);
683 d->font.setPointSizeF(size/2.0);
686 if (oldFont != d->font) {
687 d->document->setDefaultFont(d->font);
689 d->cursorItem->setHeight(QFontMetrics(d->font).height());
690 moveCursorDelegate();
693 updateWholeDocument();
695 updateInputMethod(Qt::ImCursorRectangle | Qt::ImAnchorRectangle | Qt::ImFont);
698 emit fontChanged(d->sourceFont);
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716QColor QQuickTextEdit::color()
const
718 Q_D(
const QQuickTextEdit);
722void QQuickTextEdit::setColor(
const QColor &color)
725 if (d->color == color)
729 updateWholeDocument();
730 emit colorChanged(d->color);
734
735
736
737
738QColor QQuickTextEdit::selectionColor()
const
740 Q_D(
const QQuickTextEdit);
741 return d->selectionColor;
744void QQuickTextEdit::setSelectionColor(
const QColor &color)
747 if (d->selectionColor == color)
750 d->selectionColor = color;
751 updateWholeDocument();
752 emit selectionColorChanged(d->selectionColor);
756
757
758
759
760QColor QQuickTextEdit::selectedTextColor()
const
762 Q_D(
const QQuickTextEdit);
763 return d->selectedTextColor;
766void QQuickTextEdit::setSelectedTextColor(
const QColor &color)
769 if (d->selectedTextColor == color)
772 d->selectedTextColor = color;
773 updateWholeDocument();
774 emit selectedTextColorChanged(d->selectedTextColor);
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809QQuickTextEdit::HAlignment QQuickTextEdit::hAlign()
const
811 Q_D(
const QQuickTextEdit);
815void QQuickTextEdit::setHAlign(HAlignment align)
819 if (d->setHAlign(align,
true) && isComponentComplete()) {
820 d->updateDefaultTextOption();
822 updateWholeDocument();
823 moveCursorDelegate();
827void QQuickTextEdit::resetHAlign()
830 d->hAlignImplicit =
true;
831 if (d->determineHorizontalAlignment() && isComponentComplete()) {
832 d->updateDefaultTextOption();
834 updateWholeDocument();
835 moveCursorDelegate();
839QQuickTextEdit::HAlignment QQuickTextEdit::effectiveHAlign()
const
841 Q_D(
const QQuickTextEdit);
842 QQuickTextEdit::HAlignment effectiveAlignment = d->hAlign;
843 if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
845 case QQuickTextEdit::AlignLeft:
846 effectiveAlignment = QQuickTextEdit::AlignRight;
848 case QQuickTextEdit::AlignRight:
849 effectiveAlignment = QQuickTextEdit::AlignLeft;
855 return effectiveAlignment;
858bool QQuickTextEditPrivate::setHAlign(QQuickTextEdit::HAlignment align,
bool forceAlign)
861 if (hAlign == align && !forceAlign)
864 const bool wasImplicit = hAlignImplicit;
865 const auto oldEffectiveHAlign = q->effectiveHAlign();
867 hAlignImplicit = !forceAlign;
868 if (hAlign != align) {
870 emit q->horizontalAlignmentChanged(align);
873 if (q->effectiveHAlign() != oldEffectiveHAlign) {
874 emit q->effectiveHorizontalAlignmentChanged();
878 if (forceAlign && wasImplicit) {
881 emit q->effectiveHorizontalAlignmentChanged();
886Qt::LayoutDirection QQuickTextEditPrivate::textDirection(
const QString &text)
const
888 const QChar *character = text.constData();
889 while (!character->isNull()) {
890 switch (character->direction()) {
892 return Qt::LeftToRight;
896 return Qt::RightToLeft;
902 return Qt::LayoutDirectionAuto;
905bool QQuickTextEditPrivate::determineHorizontalAlignment()
908 if (!hAlignImplicit || !q->isComponentComplete())
911 Qt::LayoutDirection direction = contentDirection;
913 if (direction == Qt::LayoutDirectionAuto) {
914 QTextBlock block = control->textCursor().block();
917 direction = textDirection(block.layout()->preeditAreaText());
919 if (direction == Qt::LayoutDirectionAuto)
920 direction = qGuiApp->inputMethod()->inputDirection();
923 const auto implicitHAlign = direction == Qt::RightToLeft ?
924 QQuickTextEdit::AlignRight : QQuickTextEdit::AlignLeft;
925 return setHAlign(implicitHAlign);
928void QQuickTextEditPrivate::mirrorChange()
931 if (q->isComponentComplete()) {
932 if (!hAlignImplicit && (hAlign == QQuickTextEdit::AlignRight || hAlign == QQuickTextEdit::AlignLeft)) {
933 updateDefaultTextOption();
935 q->updateWholeDocument();
936 emit q->effectiveHorizontalAlignmentChanged();
941bool QQuickTextEditPrivate::transformChanged(QQuickItem *transformedItem)
944 qCDebug(lcVP) << q <<
"sees that" << transformedItem <<
"moved in VP" << q->clipRect();
949 if (flags & QQuickItem::ItemObservesViewport) {
950 if (QQuickItem *viewport = q->viewportItem()) {
951 QRectF vp = q->mapRectFromItem(viewport, viewport->clipRect());
952 if (!(vp.top() > renderedRegion.top() && vp.bottom() < renderedRegion.bottom())) {
953 qCDebug(lcVP) <<
"viewport" << vp <<
"now goes beyond rendered region" << renderedRegion <<
"; updating";
954 q->updateWholeDocument();
956 const bool textCursorVisible = cursorVisible && q->cursorRectangle().intersects(vp);
958 cursorItem->setVisible(textCursorVisible);
960 control->setCursorVisible(textCursorVisible);
963 return QQuickImplicitSizeItemPrivate::transformChanged(transformedItem);
967Qt::InputMethodHints QQuickTextEditPrivate::effectiveInputMethodHints()
const
969 return inputMethodHints | Qt::ImhMultiLine;
973#if QT_CONFIG(accessibility)
974void QQuickTextEditPrivate::accessibilityActiveChanged(
bool active)
980 if (QQuickAccessibleAttached *accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(
981 qmlAttachedPropertiesObject<QQuickAccessibleAttached>(q,
true))) {
982 accessibleAttached->setRole(effectiveAccessibleRole());
983 accessibleAttached->set_readOnly(q->isReadOnly());
987QAccessible::Role QQuickTextEditPrivate::accessibleRole()
const
989 return QAccessible::EditableText;
993void QQuickTextEditPrivate::setTopPadding(qreal value,
bool reset)
996 qreal oldPadding = q->topPadding();
997 if (!reset || extra.isAllocated()) {
998 extra.value().topPadding = value;
999 extra.value().explicitTopPadding = !reset;
1001 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
1003 q->updateWholeDocument();
1004 emit q->topPaddingChanged();
1008void QQuickTextEditPrivate::setLeftPadding(qreal value,
bool reset)
1010 Q_Q(QQuickTextEdit);
1011 qreal oldPadding = q->leftPadding();
1012 if (!reset || extra.isAllocated()) {
1013 extra.value().leftPadding = value;
1014 extra.value().explicitLeftPadding = !reset;
1016 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
1018 q->updateWholeDocument();
1019 emit q->leftPaddingChanged();
1023void QQuickTextEditPrivate::setRightPadding(qreal value,
bool reset)
1025 Q_Q(QQuickTextEdit);
1026 qreal oldPadding = q->rightPadding();
1027 if (!reset || extra.isAllocated()) {
1028 extra.value().rightPadding = value;
1029 extra.value().explicitRightPadding = !reset;
1031 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
1033 q->updateWholeDocument();
1034 emit q->rightPaddingChanged();
1038void QQuickTextEditPrivate::setBottomPadding(qreal value,
bool reset)
1040 Q_Q(QQuickTextEdit);
1041 qreal oldPadding = q->bottomPadding();
1042 if (!reset || extra.isAllocated()) {
1043 extra.value().bottomPadding = value;
1044 extra.value().explicitBottomPadding = !reset;
1046 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
1048 q->updateWholeDocument();
1049 emit q->bottomPaddingChanged();
1053bool QQuickTextEditPrivate::isImplicitResizeEnabled()
const
1055 return !extra.isAllocated() || extra->implicitResize;
1058void QQuickTextEditPrivate::setImplicitResizeEnabled(
bool enabled)
1061 extra.value().implicitResize =
false;
1062 else if (extra.isAllocated())
1063 extra->implicitResize =
true;
1066QQuickTextEdit::VAlignment QQuickTextEdit::vAlign()
const
1068 Q_D(
const QQuickTextEdit);
1072void QQuickTextEdit::setVAlign(QQuickTextEdit::VAlignment alignment)
1074 Q_D(QQuickTextEdit);
1075 if (alignment == d->vAlign)
1077 d->vAlign = alignment;
1078 d->updateDefaultTextOption();
1080 updateWholeDocument();
1081 moveCursorDelegate();
1082 emit verticalAlignmentChanged(d->vAlign);
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105QQuickTextEdit::WrapMode QQuickTextEdit::wrapMode()
const
1107 Q_D(
const QQuickTextEdit);
1111void QQuickTextEdit::setWrapMode(WrapMode mode)
1113 Q_D(QQuickTextEdit);
1114 if (mode == d->wrapMode)
1117 d->updateDefaultTextOption();
1119 emit wrapModeChanged();
1123
1124
1125
1126
1127int QQuickTextEdit::lineCount()
const
1129 Q_D(
const QQuickTextEdit);
1130 return d->lineCount;
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1145int QQuickTextEdit::length()
const
1147 Q_D(
const QQuickTextEdit);
1149 return qMax(0, d->document->characterCount() - 1);
1153
1154
1155
1156
1157
1158qreal QQuickTextEdit::contentWidth()
const
1160 Q_D(
const QQuickTextEdit);
1161 return d->contentSize.width();
1165
1166
1167
1168
1169
1170qreal QQuickTextEdit::contentHeight()
const
1172 Q_D(
const QQuickTextEdit);
1173 return d->contentSize.height();
1177
1178
1179
1180
1181
1182
1183
1185QUrl QQuickTextEdit::baseUrl()
const
1187 Q_D(
const QQuickTextEdit);
1188 if (d->baseUrl.isEmpty()) {
1189 if (QQmlContext *context = qmlContext(
this))
1190 const_cast<QQuickTextEditPrivate *>(d)->baseUrl = context->baseUrl();
1195void QQuickTextEdit::setBaseUrl(
const QUrl &url)
1197 Q_D(QQuickTextEdit);
1198 if (baseUrl() != url) {
1201 d->document->setBaseUrl(url);
1202 emit baseUrlChanged();
1206void QQuickTextEdit::resetBaseUrl()
1208 if (QQmlContext *context = qmlContext(
this))
1209 setBaseUrl(context->baseUrl());
1215
1216
1217
1218
1219
1220
1221QRectF QQuickTextEdit::positionToRectangle(
int pos)
const
1223 Q_D(
const QQuickTextEdit);
1224 QTextCursor c(d->document);
1226 return d->control->cursorRect(c).translated(d->xoff, d->yoff);
1231
1232
1233
1234
1235
1236
1237
1238int QQuickTextEdit::positionAt(qreal x, qreal y)
const
1240 Q_D(
const QQuickTextEdit);
1244 int r = d->document->documentLayout()->hitTest(QPointF(x, y), Qt::FuzzyHit);
1246 QTextCursor cursor = d->control->textCursor();
1247 if (r > cursor.position()) {
1251 QTextLayout *layout = cursor.block().layout();
1252 const int preeditLength = layout
1253 ? layout->preeditAreaText().size()
1255 if (preeditLength > 0
1256 && d->document->documentLayout()->blockBoundingRect(cursor.block()).contains(x, y)) {
1257 r = r > cursor.position() + preeditLength
1259 : cursor.position();
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276QQuickTextSelection *QQuickTextEdit::cursorSelection()
const
1278 Q_D(
const QQuickTextEdit);
1279 if (!d->cursorSelection)
1280 d->cursorSelection =
new QQuickTextSelection(
const_cast<QQuickTextEdit *>(
this));
1281 return d->cursorSelection;
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320void QQuickTextEdit::moveCursorSelection(
int pos)
1323 Q_D(QQuickTextEdit);
1324 QTextCursor cursor = d->control->textCursor();
1325 if (cursor.position() == pos)
1327 cursor.setPosition(pos, QTextCursor::KeepAnchor);
1328 d->control->setTextCursor(cursor);
1331void QQuickTextEdit::moveCursorSelection(
int pos, SelectionMode mode)
1333 Q_D(QQuickTextEdit);
1334 QTextCursor cursor = d->control->textCursor();
1335 if (cursor.position() == pos)
1337 if (mode == SelectCharacters) {
1338 cursor.setPosition(pos, QTextCursor::KeepAnchor);
1339 }
else if (cursor.anchor() < pos || (cursor.anchor() == pos && cursor.position() < pos)) {
1340 if (cursor.anchor() > cursor.position()) {
1341 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1342 cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
1343 if (cursor.position() == cursor.anchor())
1344 cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::MoveAnchor);
1346 cursor.setPosition(cursor.position(), QTextCursor::MoveAnchor);
1348 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1349 cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor);
1352 cursor.setPosition(pos, QTextCursor::KeepAnchor);
1353 cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
1354 if (cursor.position() != pos)
1355 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
1356 }
else if (cursor.anchor() > pos || (cursor.anchor() == pos && cursor.position() > pos)) {
1357 if (cursor.anchor() < cursor.position()) {
1358 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1359 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::MoveAnchor);
1361 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1362 cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
1363 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
1364 if (cursor.position() != cursor.anchor()) {
1365 cursor.setPosition(cursor.anchor(), QTextCursor::MoveAnchor);
1366 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::MoveAnchor);
1370 cursor.setPosition(pos, QTextCursor::KeepAnchor);
1371 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
1372 if (cursor.position() != pos) {
1373 cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
1374 cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
1377 d->control->setTextCursor(cursor);
1381
1382
1383
1384
1385
1386
1387bool QQuickTextEdit::isCursorVisible()
const
1389 Q_D(
const QQuickTextEdit);
1390 return d->cursorVisible;
1393void QQuickTextEdit::setCursorVisible(
bool on)
1395 Q_D(QQuickTextEdit);
1396 if (d->cursorVisible == on)
1398 d->cursorVisible = on;
1399 if (on && isComponentComplete())
1400 QQuickTextUtil::createCursor(d);
1401 if (!on && !d->persistentSelection)
1402 d->control->setCursorIsFocusIndicator(
true);
1403 d->control->setCursorVisible(on);
1404 emit cursorVisibleChanged(d->cursorVisible);
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419int QQuickTextEdit::cursorPosition()
const
1421 Q_D(
const QQuickTextEdit);
1422 return d->control->textCursor().position();
1425void QQuickTextEdit::setCursorPosition(
int pos)
1427 Q_D(QQuickTextEdit);
1428 if (pos < 0 || pos >= d->document->characterCount())
1430 QTextCursor cursor = d->control->textCursor();
1431 if (cursor.position() == pos && cursor.anchor() == pos)
1433 cursor.setPosition(pos);
1434 d->control->setTextCursor(cursor);
1435 d->control->updateCursorRectangle(
true);
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451QQmlComponent* QQuickTextEdit::cursorDelegate()
const
1453 Q_D(
const QQuickTextEdit);
1454 return d->cursorComponent;
1457void QQuickTextEdit::setCursorDelegate(QQmlComponent* c)
1459 Q_D(QQuickTextEdit);
1460 QQuickTextUtil::setCursorDelegate(d, c);
1463void QQuickTextEdit::createCursor()
1465 Q_D(QQuickTextEdit);
1466 d->cursorPending =
true;
1467 QQuickTextUtil::createCursor(d);
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480int QQuickTextEdit::selectionStart()
const
1482 Q_D(
const QQuickTextEdit);
1483 return d->control->textCursor().selectionStart();
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496int QQuickTextEdit::selectionEnd()
const
1498 Q_D(
const QQuickTextEdit);
1499 return d->control->textCursor().selectionEnd();
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516QString QQuickTextEdit::selectedText()
const
1518 Q_D(
const QQuickTextEdit);
1519#if QT_CONFIG(texthtmlparser)
1520 return d->richText || d->markdownText
1521 ? d->control->textCursor().selectedText()
1522 : d->control->textCursor().selection().toPlainText();
1524 return d->control->textCursor().selection().toPlainText();
1529
1530
1531
1532
1533
1534bool QQuickTextEdit::focusOnPress()
const
1536 Q_D(
const QQuickTextEdit);
1537 return d->focusOnPress;
1540void QQuickTextEdit::setFocusOnPress(
bool on)
1542 Q_D(QQuickTextEdit);
1543 if (d->focusOnPress == on)
1545 d->focusOnPress = on;
1546 emit activeFocusOnPressChanged(d->focusOnPress);
1550
1551
1552
1553
1554
1555bool QQuickTextEdit::persistentSelection()
const
1557 Q_D(
const QQuickTextEdit);
1558 return d->persistentSelection;
1561void QQuickTextEdit::setPersistentSelection(
bool on)
1563 Q_D(QQuickTextEdit);
1564 if (d->persistentSelection == on)
1566 d->persistentSelection = on;
1567 emit persistentSelectionChanged(d->persistentSelection);
1571
1572
1573
1574
1575qreal QQuickTextEdit::textMargin()
const
1577 Q_D(
const QQuickTextEdit);
1578 return d->textMargin;
1581void QQuickTextEdit::setTextMargin(qreal margin)
1583 Q_D(QQuickTextEdit);
1584 if (d->textMargin == margin)
1586 d->textMargin = margin;
1587 d->document->setDocumentMargin(d->textMargin);
1588 emit textMarginChanged(d->textMargin);
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1628Qt::InputMethodHints QQuickTextEdit::inputMethodHints()
const
1633 Q_D(
const QQuickTextEdit);
1634 return d->inputMethodHints;
1638void QQuickTextEdit::setInputMethodHints(Qt::InputMethodHints hints)
1643 Q_D(QQuickTextEdit);
1645 if (hints == d->inputMethodHints)
1648 d->inputMethodHints = hints;
1649 updateInputMethod(Qt::ImHints);
1650 emit inputMethodHintsChanged();
1654void QQuickTextEdit::geometryChange(
const QRectF &newGeometry,
const QRectF &oldGeometry)
1656 Q_D(QQuickTextEdit);
1657 if (!d->inLayout && ((newGeometry.width() != oldGeometry.width())
1658 || (newGeometry.height() != oldGeometry.height()))) {
1660 updateWholeDocument();
1661 if (widthValid() || heightValid())
1662 moveCursorDelegate();
1664 QQuickImplicitSizeItem::geometryChange(newGeometry, oldGeometry);
1667void QQuickTextEdit::itemChange(ItemChange change,
const ItemChangeData &value)
1669 Q_D(QQuickTextEdit);
1672 case ItemDevicePixelRatioHasChanged:
1673 if (d->containsUnscalableGlyphs) {
1678 updateWholeDocument();
1685 QQuickImplicitSizeItem::itemChange(change, value);
1689
1690
1691
1692void QQuickTextEdit::componentComplete()
1694 Q_D(QQuickTextEdit);
1695 QQuickImplicitSizeItem::componentComplete();
1697 const QUrl url = baseUrl();
1698 const QQmlContext *context = qmlContext(
this);
1699 d->document->setBaseUrl(context ? context->resolvedUrl(url) : url);
1700 if (!d->text.isEmpty()) {
1701#if QT_CONFIG(texthtmlparser)
1703 d->control->setHtml(d->text);
1706#if QT_CONFIG(textmarkdownreader)
1707 if (d->markdownText)
1708 d->control->setMarkdownText(d->text);
1711 d->control->setPlainText(d->text);
1715 d->determineHorizontalAlignment();
1716 d->updateDefaultTextOption();
1720 if (d->cursorComponent && isCursorVisible())
1721 QQuickTextUtil::createCursor(d);
1724#if QT_CONFIG(accessibility)
1725 if (QAccessible::isActive())
1726 d->accessibilityActiveChanged(
true);
1730int QQuickTextEdit::resourcesLoading()
const
1732 Q_D(
const QQuickTextEdit);
1733 return d->pixmapsInProgress.size();
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750bool QQuickTextEdit::selectByKeyboard()
const
1752 Q_D(
const QQuickTextEdit);
1753 if (d->selectByKeyboardSet)
1754 return d->selectByKeyboard;
1755 return !isReadOnly();
1758void QQuickTextEdit::setSelectByKeyboard(
bool on)
1760 Q_D(QQuickTextEdit);
1761 bool was = selectByKeyboard();
1762 if (!d->selectByKeyboardSet || was != on) {
1763 d->selectByKeyboardSet =
true;
1764 d->selectByKeyboard = on;
1766 d->control->setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByKeyboard);
1768 d->control->setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByKeyboard);
1769 emit selectByKeyboardChanged(on);
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796bool QQuickTextEdit::selectByMouse()
const
1798 Q_D(
const QQuickTextEdit);
1799 return d->selectByMouse;
1802void QQuickTextEdit::setSelectByMouse(
bool on)
1804 Q_D(QQuickTextEdit);
1805 if (d->selectByMouse == on)
1808 d->selectByMouse = on;
1809 setKeepMouseGrab(on);
1811 d->control->setTextInteractionFlags(d->control->textInteractionFlags() | Qt::TextSelectableByMouse);
1813 d->control->setTextInteractionFlags(d->control->textInteractionFlags() & ~Qt::TextSelectableByMouse);
1815#if QT_CONFIG(cursor)
1816 d->updateMouseCursorShape();
1818 emit selectByMouseChanged(on);
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831QQuickTextEdit::SelectionMode QQuickTextEdit::mouseSelectionMode()
const
1833 Q_D(
const QQuickTextEdit);
1834 return d->mouseSelectionMode;
1837void QQuickTextEdit::setMouseSelectionMode(SelectionMode mode)
1839 Q_D(QQuickTextEdit);
1840 if (d->mouseSelectionMode != mode) {
1841 d->mouseSelectionMode = mode;
1842 d->control->setWordSelectionEnabled(mode == SelectWords);
1843 emit mouseSelectionModeChanged(mode);
1848
1849
1850
1851
1852
1853
1854
1855void QQuickTextEdit::setReadOnly(
bool r)
1857 Q_D(QQuickTextEdit);
1858 if (r == isReadOnly())
1862 setFlag(QQuickItem::ItemAcceptsInputMethod, !r);
1864 Qt::TextInteractionFlags flags = Qt::LinksAccessibleByMouse;
1865 if (d->selectByMouse)
1866 flags = flags | Qt::TextSelectableByMouse;
1867 if (d->selectByKeyboardSet && d->selectByKeyboard)
1868 flags = flags | Qt::TextSelectableByKeyboard;
1869 else if (!d->selectByKeyboardSet && !r)
1870 flags = flags | Qt::TextSelectableByKeyboard;
1872 flags = flags | Qt::TextEditable;
1873 d->control->setTextInteractionFlags(flags);
1874 d->control->moveCursor(QTextCursor::End);
1877 updateInputMethod(Qt::ImEnabled);
1879#if QT_CONFIG(cursor)
1880 d->updateMouseCursorShape();
1882 q_canPasteChanged();
1883 emit readOnlyChanged(r);
1884 if (!d->selectByKeyboardSet)
1885 emit selectByKeyboardChanged(!r);
1887 setCursorVisible(
false);
1888 }
else if (hasActiveFocus()) {
1889 setCursorVisible(
true);
1892#if QT_CONFIG(accessibility)
1893 if (QAccessible::isActive()) {
1894 if (QQuickAccessibleAttached *accessibleAttached = QQuickAccessibleAttached::attachedProperties(
this))
1895 accessibleAttached->set_readOnly(r);
1900bool QQuickTextEdit::isReadOnly()
const
1902 Q_D(
const QQuickTextEdit);
1903 return !(d->control->textInteractionFlags() & Qt::TextEditable);
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916QRectF QQuickTextEdit::cursorRectangle()
const
1918 Q_D(
const QQuickTextEdit);
1919 return d->control->cursorRect().translated(d->xoff, d->yoff);
1922bool QQuickTextEdit::event(QEvent *event)
1924 Q_D(QQuickTextEdit);
1925 bool state = QQuickImplicitSizeItem::event(event);
1926 if (event->type() == QEvent::ShortcutOverride && !event->isAccepted()) {
1927 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947bool QQuickTextEdit::overwriteMode()
const
1949 Q_D(
const QQuickTextEdit);
1950 return d->control->overwriteMode();
1953void QQuickTextEdit::setOverwriteMode(
bool overwrite)
1955 Q_D(QQuickTextEdit);
1956 d->control->setOverwriteMode(overwrite);
1960
1961
1962
1963void QQuickTextEdit::keyPressEvent(QKeyEvent *event)
1965 Q_D(QQuickTextEdit);
1966 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
1967 if (!event->isAccepted())
1968 QQuickImplicitSizeItem::keyPressEvent(event);
1972
1973
1974
1975void QQuickTextEdit::keyReleaseEvent(QKeyEvent *event)
1977 Q_D(QQuickTextEdit);
1978 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
1979 if (!event->isAccepted())
1980 QQuickImplicitSizeItem::keyReleaseEvent(event);
1984
1985
1986
1987
1988void QQuickTextEdit::deselect()
1990 Q_D(QQuickTextEdit);
1991 QTextCursor c = d->control->textCursor();
1993 d->control->setTextCursor(c);
1997
1998
1999
2000
2001void QQuickTextEdit::selectAll()
2003 Q_D(QQuickTextEdit);
2004 d->control->selectAll();
2008
2009
2010
2011
2012void QQuickTextEdit::selectWord()
2014 Q_D(QQuickTextEdit);
2015 QTextCursor c = d->control->textCursor();
2016 c.select(QTextCursor::WordUnderCursor);
2017 d->control->setTextCursor(c);
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033void QQuickTextEdit::select(
int start,
int end)
2035 Q_D(QQuickTextEdit);
2036 if (start < 0 || end < 0 || start >= d->document->characterCount() || end >= d->document->characterCount())
2038 QTextCursor cursor = d->control->textCursor();
2039 cursor.beginEditBlock();
2040 cursor.setPosition(start, QTextCursor::MoveAnchor);
2041 cursor.setPosition(end, QTextCursor::KeepAnchor);
2042 cursor.endEditBlock();
2043 d->control->setTextCursor(cursor);
2048 updateInputMethod();
2053
2054
2055
2056
2057
2058bool QQuickTextEdit::isRightToLeft(
int start,
int end)
2061 qmlWarning(
this) <<
"isRightToLeft(start, end) called with the end property being smaller than the start.";
2064 return getText(start, end).isRightToLeft();
2068#if QT_CONFIG(clipboard)
2070
2071
2072
2073
2074void QQuickTextEdit::cut()
2076 Q_D(QQuickTextEdit);
2081
2082
2083
2084
2085void QQuickTextEdit::copy()
2087 Q_D(QQuickTextEdit);
2092
2093
2094
2095
2096void QQuickTextEdit::paste()
2098 Q_D(QQuickTextEdit);
2099 d->control->paste();
2105
2106
2107
2108
2109
2110
2112void QQuickTextEdit::undo()
2114 Q_D(QQuickTextEdit);
2119
2120
2121
2122
2124void QQuickTextEdit::redo()
2126 Q_D(QQuickTextEdit);
2131
2132
2133
2134void QQuickTextEdit::mousePressEvent(QMouseEvent *event)
2136 Q_D(QQuickTextEdit);
2137 const bool isMouse = QQuickDeliveryAgentPrivate::isEventFromMouseOrTouchpad(event);
2138 setKeepMouseGrab(d->selectByMouse && isMouse);
2139 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2140 if (d->focusOnPress){
2141 bool hadActiveFocus = hasActiveFocus();
2142 forceActiveFocus(Qt::MouseFocusReason);
2145 if (hasActiveFocus() && hadActiveFocus && !isReadOnly())
2146 qGuiApp->inputMethod()->show();
2148 Q_UNUSED(hadActiveFocus);
2151 if (!event->isAccepted())
2152 QQuickImplicitSizeItem::mousePressEvent(event);
2156
2157
2158
2159void QQuickTextEdit::mouseReleaseEvent(QMouseEvent *event)
2161 Q_D(QQuickTextEdit);
2162 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2164 if (!event->isAccepted())
2165 QQuickImplicitSizeItem::mouseReleaseEvent(event);
2169
2170
2171
2172void QQuickTextEdit::mouseDoubleClickEvent(QMouseEvent *event)
2174 Q_D(QQuickTextEdit);
2175 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2176 if (!event->isAccepted())
2177 QQuickImplicitSizeItem::mouseDoubleClickEvent(event);
2181
2182
2183
2184void QQuickTextEdit::mouseMoveEvent(QMouseEvent *event)
2186 Q_D(QQuickTextEdit);
2187 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2188 if (!event->isAccepted())
2189 QQuickImplicitSizeItem::mouseMoveEvent(event);
2194
2195
2196
2197void QQuickTextEdit::inputMethodEvent(QInputMethodEvent *event)
2199 Q_D(QQuickTextEdit);
2200 const bool wasComposing = isInputMethodComposing();
2201 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
2202 setCursorVisible(d->control->cursorVisible());
2203 if (wasComposing != isInputMethodComposing())
2204 emit inputMethodComposingChanged();
2208
2209
2210
2211QVariant QQuickTextEdit::inputMethodQuery(Qt::InputMethodQuery property, QVariant argument)
const
2213 Q_D(
const QQuickTextEdit);
2218 v = (
bool)(flags() & ItemAcceptsInputMethod);
2221 v = (
int)d->effectiveInputMethodHints();
2223 case Qt::ImInputItemClipRectangle:
2224 v = QQuickItem::inputMethodQuery(property);
2226 case Qt::ImReadOnly:
2230 if (property == Qt::ImCursorPosition && !argument.isNull())
2231 argument = QVariant(argument.toPointF() - QPointF(d->xoff, d->yoff));
2232 v = d->control->inputMethodQuery(property, argument);
2233 if (property == Qt::ImCursorRectangle || property == Qt::ImAnchorRectangle)
2234 v = QVariant(v.toRectF().translated(d->xoff, d->yoff));
2241
2242
2243
2244QVariant QQuickTextEdit::inputMethodQuery(Qt::InputMethodQuery property)
const
2246 return inputMethodQuery(property, QVariant());
2250void QQuickTextEdit::triggerPreprocess()
2252 Q_D(QQuickTextEdit);
2253 if (d->updateType == QQuickTextEditPrivate::UpdateNone)
2254 d->updateType = QQuickTextEditPrivate::UpdateOnlyPreprocess;
2260
2261
2262
2263
2264QVariant QQuickTextEdit::loadResource(
int type,
const QUrl &source)
2266 Q_D(QQuickTextEdit);
2267 const QUrl url = d->document->baseUrl().resolved(source);
2268 if (url.isLocalFile()) {
2270 QFileInfo fi(QQmlFile::urlToLocalFileOrQrc(url));
2272 qmlWarning(
this) <<
"Cannot open: " << url.toString();
2278 if (!url.scheme().compare(
"qrc"_L1, Qt::CaseInsensitive)) {
2280 QFile f(QQmlFile::urlToLocalFileOrQrc(url));
2281 if (f.open(QFile::ReadOnly)) {
2282 QByteArray buf = f.readAll();
2285 image.loadFromData(buf);
2286 if (!image.isNull())
2290 qmlWarning(
this) <<
"Cannot read resource: " << f.fileName();
2295 auto existingJobIter = std::find_if(
2296 d->pixmapsInProgress.cbegin(), d->pixmapsInProgress.cend(),
2297 [&url](
const auto *job) {
return job->url() == url; } );
2298 if (existingJobIter != d->pixmapsInProgress.cend()) {
2299 const QQuickPixmap *job = *existingJobIter;
2300 if (job->isError()) {
2301 qmlWarning(
this) << job->error();
2302 d->pixmapsInProgress.erase(existingJobIter);
2306 qCDebug(lcTextEdit) <<
"already downloading" << url;
2308 return job->isReady() ? job->image() : QVariant();
2313 qCDebug(lcTextEdit) <<
"loading" << source <<
"resolved" << url
2314 <<
"type" <<
static_cast<QTextDocument::ResourceType>(type);
2315 QQmlContext *context = qmlContext(
this);
2318 QQuickPixmap *p =
new QQuickPixmap(context->engine(), url, QQuickPixmap::Options{});
2319 p->connectFinished(
this, SLOT(resourceRequestFinished()));
2320 d->pixmapsInProgress.append(p);
2322 return p->isReady() ? p->image() : QVariant();
2326
2327
2328void QQuickTextEdit::resourceRequestFinished()
2330 Q_D(QQuickTextEdit);
2331 for (
auto it = d->pixmapsInProgress.cbegin(); it != d->pixmapsInProgress.cend(); ++it) {
2333 if (job->isError()) {
2335 qCDebug(lcTextEdit) <<
"failed to load (error)" << job->url();
2336 d->document->resource(QTextDocument::ImageResource, job->url());
2340 }
else if (job->isReady()) {
2342 auto res = d->document->resource(QTextDocument::ImageResource, job->url());
2344 qCDebug(lcTextEdit) << (res.isValid() ?
"done downloading" :
"failed to load") << job->url() << job->rect();
2345 d->pixmapsInProgress.erase(it);
2350 if (d->pixmapsInProgress.isEmpty()) {
2358using TextNodeIterator = QQuickTextEditPrivate::TextNodeIterator;
2362 return n1.startPos() < n2.startPos();
2367 QMatrix4x4 transformMatrix;
2368 transformMatrix.translate(topLeft.x(), topLeft.y());
2369 node->setMatrix(transformMatrix);
2373
2374
2375
2376
2377
2378void QQuickTextEdit::invalidateFontCaches()
2380 Q_D(QQuickTextEdit);
2381 if (d->document ==
nullptr)
2385 for (block = d->document->firstBlock(); block.isValid(); block = block.next()) {
2386 if (block.layout() !=
nullptr && block.layout()->engine() !=
nullptr)
2387 block.layout()->engine()->resetFontEngineCache();
2391QTextDocument *QQuickTextEdit::document()
const
2393 Q_D(
const QQuickTextEdit);
2397void QQuickTextEdit::setDocument(QTextDocument *doc)
2399 Q_D(QQuickTextEdit);
2401 std::unique_ptr<QTextDocument> cleanup(d->ownsDocument ? d->document :
nullptr);
2403 d->ownsDocument =
false;
2404 d->control->setDocument(doc);
2414 engine->setDevicePixelRatio(dpr);
2417QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData)
2419 Q_UNUSED(updatePaintNodeData);
2420 Q_D(QQuickTextEdit);
2422 if (d->updateType != QQuickTextEditPrivate::UpdatePaintNode
2423 && d->updateType != QQuickTextEditPrivate::UpdateAll
2424 && oldNode !=
nullptr) {
2426 d->updateType = QQuickTextEditPrivate::UpdateNone;
2430 d->containsUnscalableGlyphs =
false;
2431 if (!oldNode || d->updateType == QQuickTextEditPrivate::UpdateAll) {
2437 d->textNodeMap.clear();
2440 d->updateType = QQuickTextEditPrivate::UpdateNone;
2442 RootNode *rootNode =
static_cast<RootNode *>(oldNode);
2443 TextNodeIterator nodeIterator = d->textNodeMap.begin();
2444 std::optional<
int> firstPosAcrossAllNodes;
2445 if (nodeIterator != d->textNodeMap.end())
2446 firstPosAcrossAllNodes = nodeIterator->startPos();
2448 while (nodeIterator != d->textNodeMap.end() && !nodeIterator->dirty())
2451 const auto dpr = d->effectiveDevicePixelRatio();
2452 QQuickTextNodeEngine engine;
2453 engine.setDevicePixelRatio(dpr);
2454 QQuickTextNodeEngine frameDecorationsEngine;
2455 frameDecorationsEngine.setDevicePixelRatio(dpr);
2457 if (!oldNode || nodeIterator < d->textNodeMap.end() || d->textNodeMap.isEmpty()) {
2460 rootNode =
new RootNode;
2462 int firstDirtyPos = 0;
2463 if (nodeIterator != d->textNodeMap.end()) {
2464 firstDirtyPos = nodeIterator->startPos();
2467 QSGInternalTextNode *firstCleanNode =
nullptr;
2468 auto it = d->textNodeMap.constEnd();
2469 while (it != nodeIterator) {
2473 firstCleanNode = it->textNode();
2476 rootNode->removeChildNode(nodeIterator->textNode());
2477 delete nodeIterator->textNode();
2478 nodeIterator = d->textNodeMap.erase(nodeIterator);
2479 }
while (nodeIterator != d->textNodeMap.constEnd() && nodeIterator->textNode() != firstCleanNode);
2484 if (flags().testFlag(QQuickItem::ItemObservesViewport)) {
2485 viewport = clipRect();
2486 qCDebug(lcVP) <<
"text viewport" << viewport;
2490 rootNode->resetFrameDecorations(d->createTextNode());
2491 resetEngine(&frameDecorationsEngine, d->color, d->selectedTextColor, d->selectionColor, dpr);
2493 QSGInternalTextNode *node =
nullptr;
2495 int currentNodeSize = 0;
2496 int nodeStart = firstDirtyPos;
2497 QPointF basePosition(d->xoff, d->yoff);
2498 QMatrix4x4 basePositionMatrix;
2499 basePositionMatrix.translate(basePosition.x(), basePosition.y());
2500 rootNode->setMatrix(basePositionMatrix);
2503 const TextNode firstCleanNode = (nodeIterator != d->textNodeMap.end()) ? *nodeIterator
2506 QList<QTextFrame *> frames;
2507 frames.append(d->document->rootFrame());
2510 d->firstBlockInViewport = -1;
2511 d->firstBlockPastViewport = -1;
2512 int frameCount = -1;
2513 while (!frames.isEmpty()) {
2514 QTextFrame *textFrame = frames.takeFirst();
2518 qCDebug(lcVP) <<
"frame" << frameCount << textFrame
2519 <<
"from" << positionToRectangle(textFrame->firstPosition()).topLeft()
2520 <<
"to" << positionToRectangle(textFrame->lastPosition()).bottomRight();
2521 frames.append(textFrame->childFrames());
2522 frameDecorationsEngine.addFrameDecorations(d->document, textFrame);
2523 resetEngine(&engine, d->color, d->selectedTextColor, d->selectionColor, dpr);
2525 if (textFrame->firstPosition() > textFrame->lastPosition()
2526 && textFrame->frameFormat().position() != QTextFrameFormat::InFlow) {
2527 node = d->createTextNode();
2528 updateNodeTransform(node, d->document->documentLayout()->frameBoundingRect(textFrame).topLeft());
2529 const int pos = textFrame->firstPosition() - 1;
2530 auto *a =
static_cast<QtPrivate::ProtectedLayoutAccessor *>(d->document->documentLayout());
2531 QTextCharFormat format = a->formatAccessor(pos);
2532 QTextBlock block = textFrame->firstCursorPosition().block();
2533 nodeOffset = d->document->documentLayout()->blockBoundingRect(block).topLeft();
2535 if (!viewport.isNull() && block.layout()) {
2536 QRectF coveredRegion = block.layout()->boundingRect().adjusted(nodeOffset.x(), nodeOffset.y(), nodeOffset.x(), nodeOffset.y());
2537 inView = coveredRegion.bottom() >= viewport.top() && coveredRegion.top() <= viewport.bottom();
2538 qCDebug(lcVP) <<
"non-flow frame" << coveredRegion <<
"in viewport?" << inView;
2541 engine.setCurrentLine(block.layout()->lineForTextPosition(pos - block.position()));
2542 engine.addTextObject(block, QPointF(0, 0), format, QQuickTextNodeEngine::Unselected, d->document,
2543 pos, textFrame->frameFormat().position());
2548 QVarLengthArray<
int, 8> frameBoundaries;
2549 frameBoundaries.reserve(frames.size());
2550 for (QTextFrame *frame : std::as_const(frames))
2551 frameBoundaries.append(frame->firstPosition());
2552 std::sort(frameBoundaries.begin(), frameBoundaries.end());
2554 QTextFrame::iterator it = textFrame->begin();
2555 while (!it.atEnd()) {
2556 QTextBlock block = it.currentBlock();
2557 if (block.position() < firstDirtyPos) {
2562 if (!engine.hasContents())
2563 nodeOffset = d->document->documentLayout()->blockBoundingRect(block).topLeft();
2566 if (!viewport.isNull()) {
2567 QRectF coveredRegion;
2568 if (block.layout()) {
2569 coveredRegion = block.layout()->boundingRect().adjusted(nodeOffset.x(), nodeOffset.y(), nodeOffset.x(), nodeOffset.y());
2570 inView = coveredRegion.bottom() > viewport.top();
2572 const bool potentiallyScrollingBackwards = firstPosAcrossAllNodes && *firstPosAcrossAllNodes == firstDirtyPos;
2573 if (d->firstBlockInViewport < 0 && inView && potentiallyScrollingBackwards) {
2575 if (coveredRegion.top() > viewport.top() + 1) {
2576 qCDebug(lcVP) <<
"checking backwards from block" << block.blockNumber() <<
"@" << nodeOffset.y() << coveredRegion;
2577 while (it != textFrame->begin() && it.currentBlock().layout() &&
2578 it.currentBlock().layout()->boundingRect().top() + nodeOffset.y() > viewport.top()) {
2579 nodeOffset = d->document->documentLayout()->blockBoundingRect(it.currentBlock()).topLeft();
2582 if (!it.currentBlock().layout())
2584 if (Q_LIKELY(it.currentBlock().layout())) {
2585 block = it.currentBlock();
2586 coveredRegion = block.layout()->boundingRect().adjusted(nodeOffset.x(), nodeOffset.y(), nodeOffset.x(), nodeOffset.y());
2587 firstDirtyPos = it.currentBlock().position();
2589 qCWarning(lcVP) <<
"failed to find a text block with layout during back-scrolling";
2592 qCDebug(lcVP) <<
"first block in viewport" << block.blockNumber() <<
"@" << nodeOffset.y() << coveredRegion;
2594 d->renderedRegion = coveredRegion;
2596 if (nodeOffset.y() > viewport.bottom()) {
2598 if (d->firstBlockInViewport >= 0 && d->firstBlockPastViewport < 0) {
2599 qCDebug(lcVP) <<
"first block past viewport" << viewport << block.blockNumber()
2600 <<
"@" << nodeOffset.y() <<
"total region rendered" << d->renderedRegion;
2601 d->firstBlockPastViewport = block.blockNumber();
2605 if (inView && !block.text().isEmpty() && coveredRegion.isValid()) {
2606 d->renderedRegion = d->renderedRegion.united(coveredRegion);
2609 if (!frames.isEmpty())
2610 viewport = viewport.united(d->renderedRegion);
2613 if (inView && d->firstBlockInViewport < 0)
2614 d->firstBlockInViewport = block.blockNumber();
2617 bool createdNodeInView =
false;
2619 if (!engine.hasContents()) {
2621 d->containsUnscalableGlyphs = d->containsUnscalableGlyphs
2622 || node->containsUnscalableGlyphs();
2623 if (!node->parent())
2624 d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart);
2626 node = d->createTextNode();
2627 createdNodeInView =
true;
2628 updateNodeTransform(node, nodeOffset);
2629 nodeStart = block.position();
2631 engine.addTextBlock(d->document, block, -nodeOffset, d->color, QColor(), selectionStart(), selectionEnd() - 1);
2632 currentNodeSize += block.length();
2635 if ((it.atEnd()) || block.next().position() >= firstCleanNode.startPos())
2637 const auto lowerBound =
2638 std::lower_bound(frameBoundaries.constBegin(),
2639 frameBoundaries.constEnd(), block.next().position());
2640 if (node && (currentNodeSize > nodeBreakingSize || lowerBound == frameBoundaries.constEnd() || *lowerBound > nodeStart)) {
2641 currentNodeSize = 0;
2642 d->containsUnscalableGlyphs = d->containsUnscalableGlyphs
2643 || node->containsUnscalableGlyphs();
2644 if (!node->parent())
2645 d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart);
2646 if (!createdNodeInView)
2647 node = d->createTextNode();
2648 resetEngine(&engine, d->color, d->selectedTextColor, d->selectionColor, dpr);
2649 nodeStart = block.next().position();
2654 if (Q_LIKELY(node)) {
2655 d->containsUnscalableGlyphs = d->containsUnscalableGlyphs
2656 || node->containsUnscalableGlyphs();
2657 if (Q_LIKELY(!node->parent()))
2658 d->addCurrentTextNodeToRoot(&engine, rootNode, node, nodeIterator, nodeStart);
2661 frameDecorationsEngine.addToSceneGraph(rootNode->frameDecorationsNode, QQuickText::Normal, QColor());
2663 rootNode->prependChildNode(rootNode->frameDecorationsNode);
2665 Q_ASSERT(nodeIterator == d->textNodeMap.end()
2666 || (nodeIterator->textNode() == firstCleanNode.textNode()
2667 && nodeIterator->startPos() == firstCleanNode.startPos()));
2669 if (firstCleanNode.textNode() !=
nullptr) {
2670 QPointF oldOffset = firstCleanNode.textNode()->matrix().map(QPointF(0,0));
2671 QPointF currentOffset = d->document->documentLayout()->blockBoundingRect(
2672 d->document->findBlock(firstCleanNode.startPos())).topLeft();
2673 QPointF delta = currentOffset - oldOffset;
2674 while (nodeIterator != d->textNodeMap.end()) {
2675 QMatrix4x4 transformMatrix = nodeIterator->textNode()->matrix();
2676 transformMatrix.translate(delta.x(), delta.y());
2677 nodeIterator->textNode()->setMatrix(transformMatrix);
2685 std::sort(d->textNodeMap.begin(), d->textNodeMap.end());
2688 if (d->cursorComponent ==
nullptr) {
2689 QSGInternalRectangleNode* cursor =
nullptr;
2690 if (!isReadOnly() && d->cursorVisible && d->control->cursorOn() && d->control->cursorVisible())
2691 cursor = d->sceneGraphContext()->createInternalRectangleNode(d->control->cursorRect(), d->color);
2692 rootNode->resetCursorNode(cursor);
2695 invalidateFontCaches();
2700void QQuickTextEdit::updatePolish()
2702 invalidateFontCaches();
2706
2707
2708
2709
2710
2711bool QQuickTextEdit::canPaste()
const
2713 Q_D(
const QQuickTextEdit);
2714 if (!d->canPasteValid) {
2715 const_cast<QQuickTextEditPrivate *>(d)->canPaste = d->control->canPaste();
2716 const_cast<QQuickTextEditPrivate *>(d)->canPasteValid =
true;
2722
2723
2724
2725
2726
2728bool QQuickTextEdit::canUndo()
const
2730 Q_D(
const QQuickTextEdit);
2731 return d->document->isUndoAvailable();
2735
2736
2737
2738
2739
2741bool QQuickTextEdit::canRedo()
const
2743 Q_D(
const QQuickTextEdit);
2744 return d->document->isRedoAvailable();
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759bool QQuickTextEdit::isInputMethodComposing()
const
2764 Q_D(
const QQuickTextEdit);
2765 return d->control->hasImState();
2769QQuickTextEditPrivate::ExtraData::ExtraData()
2770 : explicitTopPadding(
false)
2771 , explicitLeftPadding(
false)
2772 , explicitRightPadding(
false)
2773 , explicitBottomPadding(
false)
2774 , implicitResize(
true)
2778void QQuickTextEditPrivate::init()
2780 Q_Q(QQuickTextEdit);
2782#if QT_CONFIG(clipboard)
2783 if (QGuiApplication::clipboard()->supportsSelection())
2784 q->setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton);
2787 q->setAcceptedMouseButtons(Qt::LeftButton);
2790 q->setFlag(QQuickItem::ItemAcceptsInputMethod);
2792 q->setFlag(QQuickItem::ItemHasContents);
2794 q->setAcceptHoverEvents(
true);
2796 document =
new QTextDocument(q);
2797 ownsDocument =
true;
2798 auto *imageHandler =
new QQuickTextImageHandler(document);
2799 document->documentLayout()->registerHandler(QTextFormat::ImageObject, imageHandler);
2801 control =
new QQuickTextControl(document, q);
2802 control->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard | Qt::TextEditable);
2803 control->setAcceptRichText(
false);
2804 control->setCursorIsFocusIndicator(
true);
2805 q->setKeepMouseGrab(
true);
2807 qmlobject_connect(control, QQuickTextControl, SIGNAL(updateCursorRequest()), q, QQuickTextEdit, SLOT(updateCursor()));
2808 qmlobject_connect(control, QQuickTextControl, SIGNAL(selectionChanged()), q, QQuickTextEdit, SIGNAL(selectedTextChanged()));
2809 qmlobject_connect(control, QQuickTextControl, SIGNAL(selectionChanged()), q, QQuickTextEdit, SLOT(updateSelection()));
2810 qmlobject_connect(control, QQuickTextControl, SIGNAL(cursorPositionChanged()), q, QQuickTextEdit, SLOT(updateSelection()));
2811 qmlobject_connect(control, QQuickTextControl, SIGNAL(cursorPositionChanged()), q, QQuickTextEdit, SIGNAL(cursorPositionChanged()));
2812 qmlobject_connect(control, QQuickTextControl, SIGNAL(cursorRectangleChanged()), q, QQuickTextEdit, SLOT(moveCursorDelegate()));
2813 qmlobject_connect(control, QQuickTextControl, SIGNAL(linkActivated(QString)), q, QQuickTextEdit, SIGNAL(linkActivated(QString)));
2814 qmlobject_connect(control, QQuickTextControl, SIGNAL(overwriteModeChanged(
bool)), q, QQuickTextEdit, SIGNAL(overwriteModeChanged(
bool)));
2815 qmlobject_connect(control, QQuickTextControl, SIGNAL(textChanged()), q, QQuickTextEdit, SLOT(q_textChanged()));
2816 qmlobject_connect(control, QQuickTextControl, SIGNAL(preeditTextChanged()), q, QQuickTextEdit, SIGNAL(preeditTextChanged()));
2817#if QT_CONFIG(clipboard)
2818 qmlobject_connect(QGuiApplication::clipboard(), QClipboard, SIGNAL(dataChanged()), q, QQuickTextEdit, SLOT(q_canPasteChanged()));
2820 qmlobject_connect(document, QTextDocument, SIGNAL(undoAvailable(
bool)), q, QQuickTextEdit, SIGNAL(canUndoChanged()));
2821 qmlobject_connect(document, QTextDocument, SIGNAL(redoAvailable(
bool)), q, QQuickTextEdit, SIGNAL(canRedoChanged()));
2822 QObject::connect(document, &QTextDocument::contentsChange, q, &QQuickTextEdit::q_contentsChange);
2823 QObject::connect(document->documentLayout(), &QAbstractTextDocumentLayout::updateBlock, q, &QQuickTextEdit::invalidateBlock);
2824 QObject::connect(control, &QQuickTextControl::linkHovered, q, &QQuickTextEdit::q_linkHovered);
2825 QObject::connect(control, &QQuickTextControl::markerHovered, q, &QQuickTextEdit::q_markerHovered);
2827 document->setPageSize(QSizeF(0, 0));
2828 document->setDefaultFont(font);
2829 document->setDocumentMargin(textMargin);
2830 document->setUndoRedoEnabled(
false);
2831 document->setUndoRedoEnabled(
true);
2832 updateDefaultTextOption();
2833 document->setModified(
false);
2835#if QT_CONFIG(cursor)
2836 updateMouseCursorShape();
2838 setSizePolicy(QLayoutPolicy::Expanding, QLayoutPolicy::Expanding);
2841void QQuickTextEditPrivate::resetInputMethod()
2843 Q_Q(QQuickTextEdit);
2844 if (!q->isReadOnly() && q->hasActiveFocus() && qGuiApp)
2845 QGuiApplication::inputMethod()->reset();
2848void QQuickTextEdit::q_textChanged()
2850 Q_D(QQuickTextEdit);
2851 d->textCached =
false;
2852 for (QTextBlock it = d->document->begin(); it != d->document->end(); it = it.next()) {
2853 d->contentDirection = d->textDirection(it.text());
2854 if (d->contentDirection != Qt::LayoutDirectionAuto)
2857 d->determineHorizontalAlignment();
2858 d->updateDefaultTextOption();
2861 markDirtyNodesForRange(0, d->document->characterCount(), 0);
2862 if (isComponentComplete()) {
2864 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
2869 if (d->control->isBeingEdited())
2873void QQuickTextEdit::markDirtyNodesForRange(
int start,
int end,
int charDelta)
2875 Q_D(QQuickTextEdit);
2879 TextNode dummyNode(start);
2881 const TextNodeIterator textNodeMapBegin = d->textNodeMap.begin();
2882 const TextNodeIterator textNodeMapEnd = d->textNodeMap.end();
2884 TextNodeIterator it = std::lower_bound(textNodeMapBegin, textNodeMapEnd, dummyNode);
2887 if (it != textNodeMapBegin) {
2889 TextNode otherDummy(it->startPos());
2890 it = std::lower_bound(textNodeMapBegin, textNodeMapEnd, otherDummy);
2894 while (it != textNodeMapEnd) {
2895 if (it->startPos() <= end)
2898 it->moveStartPos(charDelta);
2905void QQuickTextEdit::q_contentsChange(
int pos,
int charsRemoved,
int charsAdded)
2907 Q_D(QQuickTextEdit);
2909 const int editRange = pos + qMax(charsAdded, charsRemoved);
2910 const int delta = charsAdded - charsRemoved;
2912 markDirtyNodesForRange(pos, editRange, delta);
2914 if (isComponentComplete()) {
2916 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
2921void QQuickTextEdit::moveCursorDelegate()
2923 Q_D(QQuickTextEdit);
2925 updateInputMethod();
2927 emit cursorRectangleChanged();
2930 QRectF cursorRect = cursorRectangle();
2931 d->cursorItem->setX(cursorRect.x());
2932 d->cursorItem->setY(cursorRect.y());
2933 d->cursorItem->setHeight(cursorRect.height());
2936void QQuickTextEdit::updateSelection()
2938 Q_D(QQuickTextEdit);
2941 if (d->control->textCursor().hasSelection() || d->hadSelection) {
2942 markDirtyNodesForRange(qMin(d->lastSelectionStart, d->control->textCursor().selectionStart()), qMax(d->control->textCursor().selectionEnd(), d->lastSelectionEnd), 0);
2943 if (isComponentComplete()) {
2945 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
2950 d->hadSelection = d->control->textCursor().hasSelection();
2952 if (d->lastSelectionStart != d->control->textCursor().selectionStart()) {
2953 d->lastSelectionStart = d->control->textCursor().selectionStart();
2954 emit selectionStartChanged();
2956 if (d->lastSelectionEnd != d->control->textCursor().selectionEnd()) {
2957 d->lastSelectionEnd = d->control->textCursor().selectionEnd();
2958 emit selectionEndChanged();
2962QRectF QQuickTextEdit::boundingRect()
const
2964 Q_D(
const QQuickTextEdit);
2966 QQuickTextUtil::alignedX(d->contentSize.width(), width(), effectiveHAlign()),
2968 d->contentSize.width(),
2969 d->contentSize.height());
2971 int cursorWidth = 1;
2974 else if (!d->document->isEmpty())
2978 r.setRight(r.right() + cursorWidth);
2983QRectF QQuickTextEdit::clipRect()
const
2985 Q_D(
const QQuickTextEdit);
2986 QRectF r = QQuickImplicitSizeItem::clipRect();
2987 int cursorWidth = 1;
2989 cursorWidth = d->cursorItem->width();
2990 if (!d->document->isEmpty())
2995 r.setRight(r.right() + cursorWidth);
2999qreal QQuickTextEditPrivate::getImplicitWidth()
const
3001 Q_Q(
const QQuickTextEdit);
3002 if (!requireImplicitWidth) {
3005 const_cast<QQuickTextEditPrivate*>(
this)->requireImplicitWidth =
true;
3006 const_cast<QQuickTextEdit*>(q)->updateSize();
3008 return implicitWidth;
3013void QQuickTextEdit::updateSize()
3015 Q_D(QQuickTextEdit);
3016 if (!isComponentComplete()) {
3024 if (!d->requireImplicitWidth) {
3025 emit implicitWidthChanged();
3027 if (d->requireImplicitWidth)
3030 if (d->requireImplicitWidth) {
3031 d->document->setTextWidth(-1);
3032 const qreal naturalWidth = d->document->idealWidth();
3033 const bool wasInLayout = d->inLayout;
3035 if (d->isImplicitResizeEnabled())
3036 setImplicitWidth(naturalWidth + leftPadding() + rightPadding());
3037 d->inLayout = wasInLayout;
3041 const qreal newTextWidth = width() - leftPadding() - rightPadding();
3042 if (d->document->textWidth() != newTextWidth)
3043 d->document->setTextWidth(newTextWidth);
3044 }
else if (d->wrapMode == NoWrap) {
3048 const qreal newTextWidth = d->document->idealWidth();
3049 if (d->document->textWidth() != newTextWidth)
3050 d->document->setTextWidth(newTextWidth);
3052 d->document->setTextWidth(-1);
3055 QFontMetricsF fm(d->font);
3056 const qreal newHeight = d->document->isEmpty() ? qCeil(fm.height()) : d->document->size().height();
3057 const qreal newWidth = d->document->idealWidth();
3059 if (d->isImplicitResizeEnabled()) {
3062 setImplicitSize(newWidth + leftPadding() + rightPadding(), newHeight + topPadding() + bottomPadding());
3064 setImplicitHeight(newHeight + topPadding() + bottomPadding());
3067 d->xoff = leftPadding() + qMax(qreal(0), QQuickTextUtil::alignedX(d->document->size().width(), width() - leftPadding() - rightPadding(), effectiveHAlign()));
3068 d->yoff = topPadding() + QQuickTextUtil::alignedY(d->document->size().height(), height() - topPadding() - bottomPadding(), d->vAlign);
3070 qreal baseline = fm.ascent();
3071 QTextBlock firstBlock = d->document->firstBlock();
3072 if (firstBlock.isValid() && firstBlock.layout() !=
nullptr && firstBlock.lineCount() > 0) {
3073 QTextLine firstLine = firstBlock.layout()->lineAt(0);
3074 if (firstLine.isValid())
3075 baseline = firstLine.ascent();
3078 setBaselineOffset(baseline + d->yoff + d->textMargin);
3080 QSizeF size(newWidth, newHeight);
3081 if (d->contentSize != size) {
3082 d->contentSize = size;
3084 const bool wasInResize = d->inResize;
3087 emit contentSizeChanged();
3088 d->inResize = wasInResize;
3093void QQuickTextEdit::updateWholeDocument()
3095 Q_D(QQuickTextEdit);
3096 if (!d->textNodeMap.isEmpty()) {
3097 for (TextNode &node : d->textNodeMap)
3101 if (isComponentComplete()) {
3103 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
3108void QQuickTextEdit::invalidateBlock(
const QTextBlock &block)
3110 Q_D(QQuickTextEdit);
3111 markDirtyNodesForRange(block.position(), block.position() + block.length(), 0);
3113 if (isComponentComplete()) {
3115 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
3120void QQuickTextEdit::updateCursor()
3122 Q_D(QQuickTextEdit);
3123 if (isComponentComplete() && isVisible()) {
3125 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
3130void QQuickTextEdit::q_linkHovered(
const QString &link)
3132 Q_D(QQuickTextEdit);
3133 emit linkHovered(link);
3134#if QT_CONFIG(cursor)
3135 if (link.isEmpty()) {
3136 d->updateMouseCursorShape();
3137 }
else if (cursor().shape() != Qt::PointingHandCursor) {
3138 setCursor(Qt::PointingHandCursor);
3143void QQuickTextEdit::q_markerHovered(
bool hovered)
3145 Q_D(QQuickTextEdit);
3146#if QT_CONFIG(cursor)
3148 d->updateMouseCursorShape();
3149 }
else if (cursor().shape() != Qt::PointingHandCursor) {
3150 setCursor(Qt::PointingHandCursor);
3155void QQuickTextEdit::q_updateAlignment()
3157 Q_D(QQuickTextEdit);
3158 if (d->determineHorizontalAlignment()) {
3159 d->updateDefaultTextOption();
3160 d->xoff = qMax(qreal(0), QQuickTextUtil::alignedX(d->document->size().width(), width(), effectiveHAlign()));
3161 moveCursorDelegate();
3162 updateWholeDocument();
3166void QQuickTextEdit::updateTotalLines()
3168 Q_D(QQuickTextEdit);
3172 for (QTextBlock it = d->document->begin(); it != d->document->end(); it = it.next()) {
3173 QTextLayout *layout = it.layout();
3176 subLines += layout->lineCount()-1;
3179 int newTotalLines = d->document->lineCount() + subLines;
3180 if (d->lineCount != newTotalLines) {
3181 d->lineCount = newTotalLines;
3182 emit lineCountChanged();
3186void QQuickTextEditPrivate::updateDefaultTextOption()
3188 Q_Q(QQuickTextEdit);
3189 QTextOption opt = document->defaultTextOption();
3190 const Qt::Alignment oldAlignment = opt.alignment();
3191 Qt::LayoutDirection oldTextDirection = opt.textDirection();
3193 QQuickTextEdit::HAlignment horizontalAlignment = q->effectiveHAlign();
3194 if (contentDirection == Qt::RightToLeft) {
3195 if (horizontalAlignment == QQuickTextEdit::AlignLeft)
3196 horizontalAlignment = QQuickTextEdit::AlignRight;
3197 else if (horizontalAlignment == QQuickTextEdit::AlignRight)
3198 horizontalAlignment = QQuickTextEdit::AlignLeft;
3200 if (!hAlignImplicit)
3201 opt.setAlignment((Qt::Alignment)(
int)(horizontalAlignment | vAlign));
3203 opt.setAlignment(Qt::Alignment(vAlign));
3206 if (contentDirection == Qt::LayoutDirectionAuto) {
3207 opt.setTextDirection(qGuiApp->inputMethod()->inputDirection());
3211 opt.setTextDirection(contentDirection);
3214 QTextOption::WrapMode oldWrapMode = opt.wrapMode();
3215 opt.setWrapMode(QTextOption::WrapMode(wrapMode));
3217 bool oldUseDesignMetrics = opt.useDesignMetrics();
3218 opt.setUseDesignMetrics(renderType != QQuickTextEdit::NativeRendering);
3220 if (oldWrapMode != opt.wrapMode() || oldAlignment != opt.alignment()
3221 || oldTextDirection != opt.textDirection()
3222 || oldUseDesignMetrics != opt.useDesignMetrics()) {
3223 document->setDefaultTextOption(opt);
3227void QQuickTextEditPrivate::onDocumentStatusChanged()
3229 Q_ASSERT(quickDocument);
3230 switch (quickDocument->status()) {
3231 case QQuickTextDocument::Status::Loaded:
3232 case QQuickTextDocument::Status::Saved:
3233 switch (QQuickTextDocumentPrivate::get(quickDocument)->detectedFormat) {
3235 richText = (format == QQuickTextEdit::RichText || format == QQuickTextEdit::AutoText);
3236 markdownText =
false;
3238 case Qt::MarkdownText:
3240 markdownText = (format == QQuickTextEdit::MarkdownText || format == QQuickTextEdit::AutoText);
3244 markdownText =
false;
3255void QQuickTextEdit::focusInEvent(QFocusEvent *event)
3257 Q_D(QQuickTextEdit);
3258 d->handleFocusEvent(event);
3259 QQuickImplicitSizeItem::focusInEvent(event);
3262void QQuickTextEdit::focusOutEvent(QFocusEvent *event)
3264 Q_D(QQuickTextEdit);
3265 d->handleFocusEvent(event);
3266 QQuickImplicitSizeItem::focusOutEvent(event);
3269#if QT_VERSION < QT_VERSION_CHECK(7
, 0
, 0
)
3270bool QQuickTextEditPrivate::handleContextMenuEvent(QContextMenuEvent *event)
3272bool QQuickTextEdit::contextMenuEvent(QContextMenuEvent *event)
3275 Q_Q(QQuickTextEdit);
3276 QContextMenuEvent mapped(event->reason(),
3277 q->mapToScene(q->cursorRectangle().center()).toPoint(), event->globalPos(),
3278 event->modifiers());
3279 const bool eventProcessed = QQuickItemPrivate::handleContextMenuEvent(&mapped);
3280 event->setAccepted(mapped.isAccepted());
3281 return eventProcessed;
3284void QQuickTextEditPrivate::handleFocusEvent(QFocusEvent *event)
3286 Q_Q(QQuickTextEdit);
3287 bool focus = event->type() == QEvent::FocusIn;
3288 if (!q->isReadOnly())
3289 q->setCursorVisible(focus);
3290 control->processEvent(event, QPointF(-xoff, -yoff));
3292 q->q_updateAlignment();
3294 if (focusOnPress && !q->isReadOnly())
3295 qGuiApp->inputMethod()->show();
3296 q->connect(QGuiApplication::inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
3297 q, SLOT(q_updateAlignment()));
3301 q->disconnect(QGuiApplication::inputMethod(), SIGNAL(inputDirectionChanged(Qt::LayoutDirection)),
3302 q, SLOT(q_updateAlignment()));
3304 if (event->reason() != Qt::ActiveWindowFocusReason
3305 && event->reason() != Qt::PopupFocusReason
3306 && control->textCursor().hasSelection()
3307 && !persistentSelection)
3310 emit q->editingFinished();
3314void QQuickTextEditPrivate::addCurrentTextNodeToRoot(QQuickTextNodeEngine *engine, QSGTransformNode *root, QSGInternalTextNode *node, TextNodeIterator &it,
int startPos)
3316 engine->addToSceneGraph(node, QQuickText::Normal, QColor());
3317 it = textNodeMap.insert(it, TextNode(startPos, node));
3319 root->appendChildNode(node);
3320 ++renderedBlockCount;
3323QSGInternalTextNode *QQuickTextEditPrivate::createTextNode()
3325 Q_Q(QQuickTextEdit);
3326 QSGInternalTextNode* node = sceneGraphContext()->createInternalTextNode(sceneGraphRenderContext());
3327 node->setRenderType(QSGTextNode::RenderType(renderType));
3328 node->setFiltering(q->smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
3332void QQuickTextEdit::q_canPasteChanged()
3334 Q_D(QQuickTextEdit);
3335 bool old = d->canPaste;
3336 d->canPaste = d->control->canPaste();
3337 bool changed = old!=d->canPaste || !d->canPasteValid;
3338 d->canPasteValid =
true;
3340 emit canPasteChanged();
3344
3345
3346
3347
3348
3349
3351QString QQuickTextEdit::getText(
int start,
int end)
const
3353 Q_D(
const QQuickTextEdit);
3354 start = qBound(0, start, d->document->characterCount() - 1);
3355 end = qBound(0, end, d->document->characterCount() - 1);
3356 QTextCursor cursor(d->document);
3357 cursor.setPosition(start, QTextCursor::MoveAnchor);
3358 cursor.setPosition(end, QTextCursor::KeepAnchor);
3359#if QT_CONFIG(texthtmlparser)
3360 return d->richText || d->markdownText
3361 ? cursor.selectedText()
3362 : cursor.selection().toPlainText();
3364 return cursor.selection().toPlainText();
3369
3370
3371
3372
3373
3374
3376QString QQuickTextEdit::getFormattedText(
int start,
int end)
const
3378 Q_D(
const QQuickTextEdit);
3380 start = qBound(0, start, d->document->characterCount() - 1);
3381 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);
3388#if QT_CONFIG(texthtmlparser)
3389 return cursor.selection().toHtml();
3391 return cursor.selection().toPlainText();
3393 }
else if (d->markdownText) {
3394#if QT_CONFIG(textmarkdownwriter)
3395 return cursor.selection().toMarkdown();
3397 return cursor.selection().toPlainText();
3400 return cursor.selection().toPlainText();
3405
3406
3407
3408
3409void QQuickTextEdit::insert(
int position,
const QString &text)
3411 Q_D(QQuickTextEdit);
3412 if (position < 0 || position >= d->document->characterCount())
3414 QTextCursor cursor(d->document);
3415 cursor.setPosition(position);
3416 d->richText = d->richText || (d->format == AutoText && Qt::mightBeRichText(text));
3418#if QT_CONFIG(texthtmlparser)
3419 cursor.insertHtml(text);
3421 cursor.insertText(text);
3423 }
else if (d->markdownText) {
3424#if QT_CONFIG(textmarkdownreader)
3425 cursor.insertMarkdown(text);
3427 cursor.insertText(text);
3430 cursor.insertText(text);
3432 d->control->updateCursorRectangle(
false);
3436
3437
3438
3439
3441void QQuickTextEdit::remove(
int start,
int end)
3443 Q_D(QQuickTextEdit);
3444 start = qBound(0, start, d->document->characterCount() - 1);
3445 end = qBound(0, end, d->document->characterCount() - 1);
3446 QTextCursor cursor(d->document);
3447 cursor.setPosition(start, QTextCursor::MoveAnchor);
3448 cursor.setPosition(end, QTextCursor::KeepAnchor);
3449 cursor.removeSelectedText();
3450 d->control->updateCursorRectangle(
false);
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3465QQuickTextDocument *QQuickTextEdit::textDocument()
3467 Q_D(QQuickTextEdit);
3468 if (!d->quickDocument) {
3469 d->quickDocument =
new QQuickTextDocument(
this);
3470 connect(d->quickDocument, &QQuickTextDocument::statusChanged, d->quickDocument,
3471 [d]() { d->onDocumentStatusChanged(); } );
3473 return d->quickDocument;
3476bool QQuickTextEditPrivate::isLinkHoveredConnected()
3478 Q_Q(QQuickTextEdit);
3479 IS_SIGNAL_CONNECTED(q, QQuickTextEdit, linkHovered, (
const QString &));
3482#if QT_CONFIG(cursor)
3483void QQuickTextEditPrivate::updateMouseCursorShape()
3485 Q_Q(QQuickTextEdit);
3486 q->setCursor(q->isReadOnly() && !q->selectByMouse() ? Qt::ArrowCursor : Qt::IBeamCursor);
3491
3492
3493
3494
3495
3496
3497
3498
3499
3502
3503
3504
3505
3506
3509
3510
3511
3512
3513
3514
3515
3516
3517
3520
3521
3522
3523
3524
3525
3526
3528QString QQuickTextEdit::hoveredLink()
const
3530 Q_D(
const QQuickTextEdit);
3531 if (
const_cast<QQuickTextEditPrivate *>(d)->isLinkHoveredConnected()) {
3532 return d->control->hoveredLink();
3534#if QT_CONFIG(cursor)
3535 if (QQuickWindow *wnd = window()) {
3536 QPointF pos = QCursor::pos(wnd->screen()) - wnd->position() - mapToScene(QPointF(0, 0));
3537 return d->control->anchorAt(pos);
3544void QQuickTextEdit::hoverEnterEvent(QHoverEvent *event)
3546 Q_D(QQuickTextEdit);
3547 if (d->isLinkHoveredConnected())
3548 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
3552void QQuickTextEdit::hoverMoveEvent(QHoverEvent *event)
3554 Q_D(QQuickTextEdit);
3555 if (d->isLinkHoveredConnected())
3556 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
3560void QQuickTextEdit::hoverLeaveEvent(QHoverEvent *event)
3562 Q_D(QQuickTextEdit);
3563 if (d->isLinkHoveredConnected())
3564 d->control->processEvent(event, QPointF(-d->xoff, -d->yoff));
3569
3570
3571
3572
3573
3574
3575
3576
3577void QQuickTextEdit::append(
const QString &text)
3579 Q_D(QQuickTextEdit);
3580 QTextCursor cursor(d->document);
3581 cursor.beginEditBlock();
3582 cursor.movePosition(QTextCursor::End);
3584 if (!d->document->isEmpty())
3585 cursor.insertBlock();
3587 if (d->format == RichText || (d->format == AutoText && Qt::mightBeRichText(text))) {
3588#if QT_CONFIG(texthtmlparser)
3589 cursor.insertHtml(text);
3591 cursor.insertText(text);
3593 }
else if (d->format == MarkdownText) {
3594#if QT_CONFIG(textmarkdownreader)
3595 cursor.insertMarkdown(text);
3597 cursor.insertText(text);
3600 cursor.insertText(text);
3603 cursor.endEditBlock();
3604 d->control->updateCursorRectangle(
false);
3608
3609
3610
3611
3612
3613
3614
3615
3616QString QQuickTextEdit::linkAt(qreal x, qreal y)
const
3618 Q_D(
const QQuickTextEdit);
3619 return d->control->anchorAt(QPointF(x + topPadding(), y + leftPadding()));
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633qreal QQuickTextEdit::padding()
const
3635 Q_D(
const QQuickTextEdit);
3636 return d->padding();
3639void QQuickTextEdit::setPadding(qreal padding)
3641 Q_D(QQuickTextEdit);
3642 if (qFuzzyCompare(d->padding(), padding))
3645 d->extra.value().padding = padding;
3647 if (isComponentComplete()) {
3648 d->updateType = QQuickTextEditPrivate::UpdatePaintNode;
3651 emit paddingChanged();
3652 if (!d->extra.isAllocated() || !d->extra->explicitTopPadding)
3653 emit topPaddingChanged();
3654 if (!d->extra.isAllocated() || !d->extra->explicitLeftPadding)
3655 emit leftPaddingChanged();
3656 if (!d->extra.isAllocated() || !d->extra->explicitRightPadding)
3657 emit rightPaddingChanged();
3658 if (!d->extra.isAllocated() || !d->extra->explicitBottomPadding)
3659 emit bottomPaddingChanged();
3662void QQuickTextEdit::resetPadding()
3667qreal QQuickTextEdit::topPadding()
const
3669 Q_D(
const QQuickTextEdit);
3670 if (d->extra.isAllocated() && d->extra->explicitTopPadding)
3671 return d->extra->topPadding;
3672 return d->padding();
3675void QQuickTextEdit::setTopPadding(qreal padding)
3677 Q_D(QQuickTextEdit);
3678 d->setTopPadding(padding);
3681void QQuickTextEdit::resetTopPadding()
3683 Q_D(QQuickTextEdit);
3684 d->setTopPadding(0,
true);
3687qreal QQuickTextEdit::leftPadding()
const
3689 Q_D(
const QQuickTextEdit);
3690 if (d->extra.isAllocated() && d->extra->explicitLeftPadding)
3691 return d->extra->leftPadding;
3692 return d->padding();
3695void QQuickTextEdit::setLeftPadding(qreal padding)
3697 Q_D(QQuickTextEdit);
3698 d->setLeftPadding(padding);
3701void QQuickTextEdit::resetLeftPadding()
3703 Q_D(QQuickTextEdit);
3704 d->setLeftPadding(0,
true);
3707qreal QQuickTextEdit::rightPadding()
const
3709 Q_D(
const QQuickTextEdit);
3710 if (d->extra.isAllocated() && d->extra->explicitRightPadding)
3711 return d->extra->rightPadding;
3712 return d->padding();
3715void QQuickTextEdit::setRightPadding(qreal padding)
3717 Q_D(QQuickTextEdit);
3718 d->setRightPadding(padding);
3721void QQuickTextEdit::resetRightPadding()
3723 Q_D(QQuickTextEdit);
3724 d->setRightPadding(0,
true);
3727qreal QQuickTextEdit::bottomPadding()
const
3729 Q_D(
const QQuickTextEdit);
3730 if (d->extra.isAllocated() && d->extra->explicitBottomPadding)
3731 return d->extra->bottomPadding;
3732 return d->padding();
3735void QQuickTextEdit::setBottomPadding(qreal padding)
3737 Q_D(QQuickTextEdit);
3738 d->setBottomPadding(padding);
3741void QQuickTextEdit::resetBottomPadding()
3743 Q_D(QQuickTextEdit);
3744 d->setBottomPadding(0,
true);
3748
3749
3750
3751
3752
3753
3754
3755int QQuickTextEdit::tabStopDistance()
const
3757 Q_D(
const QQuickTextEdit);
3758 return d->document->defaultTextOption().tabStopDistance();
3761void QQuickTextEdit::setTabStopDistance(qreal distance)
3763 Q_D(QQuickTextEdit);
3764 QTextOption textOptions = d->document->defaultTextOption();
3765 if (textOptions.tabStopDistance() == distance)
3768 textOptions.setTabStopDistance(distance);
3769 d->document->setDefaultTextOption(textOptions);
3770 emit tabStopDistanceChanged(distance);
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784void QQuickTextEdit::clear()
3786 Q_D(QQuickTextEdit);
3787 d->resetInputMethod();
3788 d->control->clear();
3791#ifndef QT_NO_DEBUG_STREAM
3794 QDebugStateSaver saver(debug);
3796 debug <<
"Node(startPos:" << n.m_startPos <<
"dirty:" << n.m_dirty << n.m_node <<
')';