441void QQuickTextPrivate::updateSize()
445 if (!q->isComponentComplete()) {
446 updateOnComponentComplete =
true;
450 if (!requireImplicitSize) {
451 implicitWidthChanged();
452 implicitHeightChanged();
454 if (requireImplicitSize)
458 qreal hPadding = q->leftPadding() + q->rightPadding();
459 qreal vPadding = q->topPadding() + q->bottomPadding();
461 const QSizeF previousSize(q->contentWidth(), q->contentHeight());
463 if (text.isEmpty() && !isLineLaidOutConnected() && fontSizeMode() == QQuickText::FixedSize) {
468 QFontMetricsF fm(font);
469 qreal fontHeight = qCeil(fm.height());
471 fontHeight = lineHeightMode() == QQuickText::FixedHeight
473 : fontHeight * lineHeight();
475 updateBaseline(fm.ascent(), q->height() - fontHeight - vPadding);
476 q->setImplicitSize(hPadding, fontHeight + qMax(lineHeightOffset(), 0) + vPadding);
477 layedOutTextRect = QRectF(0, 0, 0, fontHeight);
479 signalSizeChange(previousSize);
481 emit q->lineCountChanged();
484 emit q->truncatedChanged();
486 scheduleUpdatePaintNode();
495 QRectF textRect = setupTextLayout(&baseline);
497 if (internalWidthUpdate)
500 layedOutTextRect = textRect;
501 size = textRect.size();
502 updateBaseline(baseline, q->height() - size.height() - vPadding);
504 widthExceeded =
true;
505 heightExceeded =
false;
507 extra->doc->setDefaultFont(font);
508 QQuickText::HAlignment horizontalAlignment = q->effectiveHAlign();
509 if (rightToLeftText) {
510 if (horizontalAlignment == QQuickText::AlignLeft)
511 horizontalAlignment = QQuickText::AlignRight;
512 else if (horizontalAlignment == QQuickText::AlignRight)
513 horizontalAlignment = QQuickText::AlignLeft;
516 option.setAlignment((Qt::Alignment)
int(horizontalAlignment | vAlign));
517 option.setWrapMode(QTextOption::WrapMode(wrapMode));
518 option.setUseDesignMetrics(renderType != QQuickText::NativeRendering);
519 extra->doc->setDefaultTextOption(option);
520 qreal naturalWidth = 0;
521 if (requireImplicitSize) {
522 extra->doc->setTextWidth(-1);
523 naturalWidth = extra->doc->idealWidth();
524 const bool wasInLayout = internalWidthUpdate;
525 internalWidthUpdate =
true;
526 q->setImplicitWidth(naturalWidth + hPadding);
527 internalWidthUpdate = wasInLayout;
529 if (internalWidthUpdate)
532 extra->doc->setPageSize(QSizeF(q->width(), -1));
533 if (q->widthValid() && (wrapMode != QQuickText::NoWrap || extra->doc->idealWidth() < availableWidth()))
534 extra->doc->setTextWidth(availableWidth());
536 extra->doc->setTextWidth(extra->doc->idealWidth());
538 QSizeF dsize = extra->doc->size();
539 layedOutTextRect = QRectF(QPointF(0,0), dsize);
540 size = QSizeF(extra->doc->idealWidth(),dsize.height());
543 qreal baseline = QFontMetricsF(font).ascent();
544 QTextBlock firstBlock = extra->doc->firstBlock();
545 if (firstBlock.isValid() && firstBlock.layout() !=
nullptr && firstBlock.lineCount() > 0)
546 baseline = firstBlock.layout()->lineAt(0).ascent();
548 updateBaseline(baseline, q->height() - size.height() - vPadding);
551 internalWidthUpdate =
true;
552 qreal oldWidth = q->width();
554 if (!q->widthValid())
555 iWidth = size.width();
557 q->setImplicitSize(iWidth + hPadding, size.height() + qMax(lineHeightOffset(), 0) + vPadding);
558 internalWidthUpdate =
false;
565 if (!qFuzzyCompare(q->width(), oldWidth) && !updateSizeRecursionGuard) {
566 updateSizeRecursionGuard =
true;
568 updateSizeRecursionGuard =
false;
571 q->setImplicitHeight(size.height() + lineHeightOffset() + vPadding);
573 QTextBlock firstBlock = extra->doc->firstBlock();
574 while (firstBlock.layout()->lineCount() == 0)
575 firstBlock = firstBlock.next();
577 QTextBlock lastBlock = extra->doc->lastBlock();
578 while (lastBlock.layout()->lineCount() == 0)
579 lastBlock = lastBlock.previous();
581 if (firstBlock.lineCount() > 0 && lastBlock.lineCount() > 0) {
582 QTextLine firstLine = firstBlock.layout()->lineAt(0);
583 QTextLine lastLine = lastBlock.layout()->lineAt(lastBlock.layout()->lineCount() - 1);
584 advance = QSizeF(lastLine.horizontalAdvance(),
585 (lastLine.y() + lastBlock.layout()->position().y() + lastLine.ascent()) - (firstLine.y() + firstBlock.layout()->position().y() + firstLine.ascent()));
592 signalSizeChange(previousSize);
593 scheduleUpdatePaintNode();
797QRectF QQuickTextPrivate::setupTextLayout(qreal *
const baseline)
801 bool singlelineElide = elideMode != QQuickText::ElideNone && q->widthValid();
802 bool multilineElide = elideMode == QQuickText::ElideRight
804 && (q->heightValid() || maximumLineCountValid);
806 if ((!requireImplicitSize || (implicitWidthValid && implicitHeightValid))
807 && ((singlelineElide && availableWidth() <= 0.)
808 || (multilineElide && q->heightValid() && availableHeight() <= 0.))) {
810 widthExceeded = q->widthValid() && availableWidth() <= 0.;
811 heightExceeded = q->heightValid() && availableHeight() <= 0.;
815 emit q->truncatedChanged();
819 q->setFlag(QQuickItem::ItemObservesViewport,
false);
820 emit q->lineCountChanged();
823 if (qFuzzyIsNull(q->width())) {
824 layout.setText(QString());
825 textHasChanged =
true;
828 QFontMetricsF fm(font);
829 qreal height = (lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : qCeil(fm.height()) * lineHeight();
830 *baseline = fm.ascent();
831 return QRectF(0, 0, 0, height);
834 bool shouldUseDesignMetrics = renderType != QQuickText::NativeRendering;
835 layout.setCacheEnabled(
true);
836 QTextOption textOption = layout.textOption();
837 if (textOption.alignment() != q->effectiveHAlign()
838 || textOption.wrapMode() != QTextOption::WrapMode(wrapMode)
839 || textOption.useDesignMetrics() != shouldUseDesignMetrics) {
840 textOption.setAlignment(Qt::Alignment(q->effectiveHAlign()));
841 textOption.setWrapMode(QTextOption::WrapMode(wrapMode));
842 textOption.setUseDesignMetrics(shouldUseDesignMetrics);
843 layout.setTextOption(textOption);
845 if (layout.font() != font)
846 layout.setFont(font);
848 lineWidth = (q->widthValid() || implicitWidthValid) && q->width() > 0
851 qreal maxHeight = q->heightValid() ? availableHeight() : FLT_MAX;
853 const bool customLayout = isLineLaidOutConnected();
854 const bool wasTruncated = truncated;
856 bool canWrap = wrapMode != QQuickText::NoWrap && q->widthValid();
858 bool horizontalFit = fontSizeMode() & QQuickText::HorizontalFit && q->widthValid();
859 bool verticalFit = fontSizeMode() & QQuickText::VerticalFit
860 && (q->heightValid() || (maximumLineCountValid && canWrap));
862 const bool pixelSize = font.pixelSize() != -1;
863 QString layoutText = layout.text();
865 const qreal minimumSize = pixelSize
866 ?
static_cast<qreal>(minimumPixelSize())
867 : minimumPointSize();
868 qreal largeFont = pixelSize ? font.pixelSize() : font.pointSizeF();
869 qreal smallFont = fontSizeMode() != QQuickText::FixedSize
870 ? qMin<qreal>(minimumSize, largeFont)
872 qreal scaledFontSize = largeFont;
873 const qreal sizeFittingThreshold(0.01);
875 bool widthChanged =
false;
876 widthExceeded = availableWidth() <= 0 && (singlelineElide || canWrap || horizontalFit);
877 heightExceeded = availableHeight() <= 0 && (multilineElide || verticalFit);
881 QFont scaledFont = font;
883 int visibleCount = 0;
890 bool noBreakLastLine = multilineElide && (wrapMode == QQuickText::Wrap || wrapMode == QQuickText::WordWrap);
892 int eos = multilengthEos;
900 scaledFont.setPixelSize(scaledFontSize);
902 scaledFont.setPointSizeF(scaledFontSize);
903 if (layout.font() != scaledFont)
904 layout.setFont(scaledFont);
907 layout.beginLayout();
909 bool wrapped =
false;
910 bool truncateHeight =
false;
913 int unwrappedLineCount = 1;
914 const int maxLineCount = maximumLineCount();
916 qreal naturalHeight = 0;
917 qreal previousHeight = 0;
922 for (visibleCount = 1; ; ++visibleCount) {
923 line = layout.createLine();
925 if (noBreakLastLine && visibleCount == maxLineCount)
926 layout.engine()->option.setWrapMode(QTextOption::WrapAnywhere);
928 setupCustomLineGeometry(line, naturalHeight, layoutText.size());
930 setLineGeometry(line, lineWidth, naturalHeight);
932 if (noBreakLastLine && visibleCount == maxLineCount)
933 layout.engine()->option.setWrapMode(QTextOption::WrapMode(wrapMode));
935 unelidedRect = br.united(line.naturalTextRect());
939 if (multilineElide && naturalHeight > maxHeight && visibleCount > 1) {
941 heightExceeded =
true;
946 truncateHeight =
true;
950 const QTextLine previousLine = layout.lineAt(visibleCount - 1);
951 elideText = elidedText(line.width(), previousLine);
952 elideStart = previousLine.textStart();
953 elideEnd = line.textStart() + line.textLength();
955 height = previousHeight;
959 const bool isLastLine = line.textStart() + line.textLength() >= layoutText.size();
961 if (singlelineElide && visibleCount == 1 && line.naturalTextWidth() > line.width()) {
964 widthExceeded =
true;
969 elideText = layout.engine()->elidedText(
970 Qt::TextElideMode(elideMode),
971 QFixed::fromReal(line.width()),
975 elideStart = line.textStart();
976 elideEnd = elideStart + line.textLength();
979 height = naturalHeight;
983 const bool wrappedLine = layoutText.at(line.textStart() + line.textLength() - 1) != QChar::LineSeparator;
984 wrapped |= wrappedLine;
987 ++unwrappedLineCount;
990 if (visibleCount == maxLineCount) {
992 heightExceeded |= wrapped;
994 if (multilineElide) {
999 elideText = elidedText(line.width(), line);
1000 elideStart = line.textStart();
1001 elideEnd = elideStart + line.textLength();
1004 height = naturalHeight;
1010 previousHeight = height;
1011 height = naturalHeight;
1013 widthExceeded |= wrapped;
1021 if ((requireImplicitSize) && line.isValid() && unwrappedLineCount < maxLineCount) {
1024 for (
int lineCount = layout.lineCount(); lineCount < maxLineCount; ++lineCount) {
1025 line = layout.createLine();
1026 if (!line.isValid())
1028 if (layoutText.at(line.textStart() - 1) == QChar::LineSeparator)
1029 ++unwrappedLineCount;
1030 setLineGeometry(line, lineWidth, naturalHeight);
1035 const int eol = line.isValid()
1036 ? line.textStart() + line.textLength()
1037 : layoutText.size();
1038 if (eol < layoutText.size() && layoutText.at(eol) != QChar::LineSeparator)
1039 line = layout.createLine();
1040 for (; line.isValid() && unwrappedLineCount < maxLineCount; ++unwrappedLineCount)
1041 line = layout.createLine();
1046 const qreal naturalWidth = layout.maximumWidth();
1048 bool wasInLayout = internalWidthUpdate;
1049 internalWidthUpdate =
true;
1050 q->setImplicitSize(naturalWidth + q->leftPadding() + q->rightPadding(), naturalHeight + qMax(lineHeightOffset(), 0) + q->topPadding() + q->bottomPadding());
1051 internalWidthUpdate = wasInLayout;
1054 singlelineElide = elideMode != QQuickText::ElideNone && q->widthValid();
1055 multilineElide = elideMode == QQuickText::ElideRight
1057 && (q->heightValid() || maximumLineCountValid);
1058 canWrap = wrapMode != QQuickText::NoWrap && q->widthValid();
1060 horizontalFit = fontSizeMode() & QQuickText::HorizontalFit && q->widthValid();
1061 verticalFit = fontSizeMode() & QQuickText::VerticalFit
1062 && (q->heightValid() || (maximumLineCountValid && canWrap));
1064 const qreal oldWidth = lineWidth;
1065 const qreal oldHeight = maxHeight;
1067 const qreal availWidth = availableWidth();
1068 const qreal availHeight = availableHeight();
1070 lineWidth = q->widthValid() && q->width() > 0 ? availWidth : naturalWidth;
1071 maxHeight = q->heightValid() ? availHeight : FLT_MAX;
1075 if ((!qFuzzyCompare(lineWidth, oldWidth) || (widthExceeded && lineWidth > oldWidth))
1076 && (singlelineElide || multilineElide || canWrap || horizontalFit
1077 || q->effectiveHAlign() != QQuickText::AlignLeft)) {
1078 widthChanged =
true;
1079 widthExceeded = lineWidth >= qMin(oldWidth, naturalWidth);
1080 heightExceeded =
false;
1086 if ((maxHeight < qMin(oldHeight, naturalHeight) || (heightExceeded && maxHeight > oldHeight))
1087 && (multilineElide || (canWrap && maximumLineCountValid))) {
1088 widthExceeded =
false;
1089 heightExceeded =
false;
1095 if (!q->widthValid() && !implicitWidthValid && unwrappedLineCount > 1 && q->effectiveHAlign() != QQuickText::AlignLeft) {
1096 widthExceeded =
false;
1097 heightExceeded =
false;
1100 }
else if (widthChanged) {
1101 widthChanged =
false;
1102 if (line.isValid()) {
1103 for (
int lineCount = layout.lineCount(); lineCount < maxLineCount; ++lineCount) {
1104 line = layout.createLine();
1105 if (!line.isValid())
1107 setLineGeometry(line, lineWidth, naturalHeight);
1112 bool wasInLayout = internalWidthUpdate;
1113 internalWidthUpdate =
true;
1114 q->setImplicitHeight(naturalHeight + qMax(lineHeightOffset(), 0) + q->topPadding() + q->bottomPadding());
1115 internalWidthUpdate = wasInLayout;
1117 multilineElide = elideMode == QQuickText::ElideRight
1119 && (q->heightValid() || maximumLineCountValid);
1120 verticalFit = fontSizeMode() & QQuickText::VerticalFit
1121 && (q->heightValid() || (maximumLineCountValid && canWrap));
1123 const qreal oldHeight = maxHeight;
1124 maxHeight = q->heightValid() ? availableHeight() : FLT_MAX;
1127 if ((maxHeight < qMin(oldHeight, naturalHeight) || (heightExceeded && maxHeight > oldHeight))
1128 && (multilineElide || (canWrap && maximumLineCountValid))) {
1129 widthExceeded =
false;
1130 heightExceeded =
false;
1139 if (eos != -1 && elide) {
1140 int start = eos + 1;
1141 eos = text.indexOf(QLatin1Char(
'\x9c'), start);
1142 layoutText = text.mid(start, eos != -1 ? eos - start : -1);
1143 layoutText.replace(QLatin1Char(
'\n'), QChar::LineSeparator);
1144 layout.setText(layoutText);
1145 textHasChanged =
true;
1152 if (layout.lineCount() > 0) {
1153 QTextLine firstLine = layout.lineAt(0);
1154 QTextLine lastLine = layout.lineAt(layout.lineCount() - 1);
1155 advance = QSizeF(lastLine.horizontalAdvance(),
1156 lastLine.y() - firstLine.y());
1161 if (!horizontalFit && !verticalFit)
1165 if (qFuzzyCompare(smallFont, largeFont))
1169 if (horizontalFit) {
1170 if (unelidedRect.width() > lineWidth || (!verticalFit && wrapped)) {
1171 widthExceeded =
true;
1172 largeFont = scaledFontSize;
1174 scaledFontSize = (smallFont + largeFont) / 2;
1177 }
else if (!verticalFit) {
1178 smallFont = scaledFontSize;
1181 if ((largeFont - smallFont) < sizeFittingThreshold)
1184 scaledFontSize = (smallFont + largeFont) / 2;
1189 if (truncateHeight || unelidedRect.height() > maxHeight) {
1190 heightExceeded =
true;
1191 largeFont = scaledFontSize;
1193 scaledFontSize = (smallFont + largeFont) / 2;
1196 smallFont = scaledFontSize;
1199 if ((largeFont - smallFont) < sizeFittingThreshold)
1202 scaledFontSize = (smallFont + largeFont) / 2;
1207 implicitWidthValid =
true;
1208 implicitHeightValid =
true;
1210 QFontInfo scaledFontInfo(scaledFont);
1211 if (fontInfo.weight() != scaledFontInfo.weight()
1212 || fontInfo.pixelSize() != scaledFontInfo.pixelSize()
1213 || fontInfo.italic() != scaledFontInfo.italic()
1214 || !qFuzzyCompare(fontInfo.pointSizeF(), scaledFontInfo.pointSizeF())
1215 || fontInfo.family() != scaledFontInfo.family()
1216 || fontInfo.styleName() != scaledFontInfo.styleName()) {
1217 fontInfo = scaledFontInfo;
1218 emit q->fontInfoChanged();
1221 if (eos != multilengthEos)
1224 assignedFont = QFontInfo(font).family();
1228 elideLayout.reset(
new QTextLayout);
1229 elideLayout->setCacheEnabled(
true);
1231 QTextEngine *engine = layout.engine();
1232 if (engine && engine->hasFormats()) {
1233 QList<QTextLayout::FormatRange> formats;
1234 switch (elideMode) {
1235 case QQuickText::ElideRight:
1236 elideFormats(elideStart, elideText.size() - 1, 0, &formats);
1238 case QQuickText::ElideLeft:
1239 elideFormats(elideEnd - elideText.size() + 1, elideText.size() - 1, 1, &formats);
1241 case QQuickText::ElideMiddle: {
1242 const int index = elideText.indexOf(elideChar);
1244 elideFormats(elideStart, index, 0, &formats);
1246 elideEnd - elideText.size() + index + 1,
1247 elideText.size() - index - 1,
1256 elideLayout->setFormats(formats);
1259 elideLayout->setFont(layout.font());
1260 elideLayout->setTextOption(layout.textOption());
1261 elideLayout->setText(elideText);
1262 elideLayout->beginLayout();
1264 QTextLine elidedLine = elideLayout->createLine();
1265 elidedLine.setPosition(QPointF(0, height));
1267 setupCustomLineGeometry(elidedLine, height, elideText.size(), visibleCount - 1);
1269 setLineGeometry(elidedLine, lineWidth, height);
1271 elideLayout->endLayout();
1273 br = br.united(elidedLine.naturalTextRect());
1275 if (visibleCount == 1)
1276 layout.clearLayout();
1278 elideLayout.reset();
1283 if (extra.isAllocated()) {
1284 extra->visibleImgTags.clear();
1285 const int mainLineCount = elide ? visibleCount - 1 : visibleCount;
1286 for (
int i = 0; i < mainLineCount; ++i)
1287 positionInlineImages(layout.lineAt(i), layout.formats());
1289 positionInlineImages(elideLayout->lineAt(0), elideLayout->formats());
1292 QTextLine firstLine = visibleCount == 1 && elideLayout
1293 ? elideLayout->lineAt(0)
1295 if (firstLine.isValid())
1296 *baseline = firstLine.y() + firstLine.ascent();
1299 br.setHeight(height);
1302 if (lineCount != visibleCount) {
1303 lineCount = visibleCount;
1304 emit q->lineCountChanged();
1307 if (truncated != wasTruncated)
1308 emit q->truncatedChanged();
2594QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
2599 if (d->text.isEmpty()) {
2600 d->containsUnscalableGlyphs =
false;
2605 if (d->updateType != QQuickTextPrivate::UpdatePaintNode && oldNode !=
nullptr) {
2607 d->updateType = QQuickTextPrivate::UpdateNone;
2611 d->updateType = QQuickTextPrivate::UpdateNone;
2613 const qreal dy = QQuickTextUtil::alignedY(d->layedOutTextRect.height() + d->lineHeightOffset(), d->availableHeight(), d->vAlign) + topPadding();
2615 QSGInternalTextNode *node =
nullptr;
2617 node = d->sceneGraphContext()->createInternalTextNode(d->sceneGraphRenderContext());
2619 node =
static_cast<QSGInternalTextNode *>(oldNode);
2621 node->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
2623 node->setTextStyle(QSGTextNode::TextStyle(d->style));
2624 node->setRenderType(QSGTextNode::RenderType(d->renderType));
2625 node->setRenderTypeQuality(d->renderTypeQuality());
2627 QSGInternalTextNode::RecycleBin recycleBin;
2628 node->recycle(&recycleBin);
2629 node->setMatrix(QMatrix4x4());
2631 node->setColor(QColor::fromRgba(d->color));
2632 node->setStyleColor(QColor::fromRgba(d->styleColor));
2633 node->setLinkColor(QColor::fromRgba(d->linkColor));
2635 node->setDevicePixelRatio(d->effectiveDevicePixelRatio());
2638 node->setViewport(clipRect());
2639 const qreal dx = QQuickTextUtil::alignedX(d->layedOutTextRect.width(), d->availableWidth(), effectiveHAlign()) + leftPadding();
2641 node->addTextDocument(QPointF(dx, dy), d->extra->doc, &recycleBin);
2642 }
else if (d->layedOutTextRect.width() > 0) {
2643 if (flags().testFlag(ItemObservesViewport))
2644 node->setViewport(clipRect());
2646 node->setViewport(QRectF{});
2647 const qreal dx = QQuickTextUtil::alignedX(d->lineWidth, d->availableWidth(), effectiveHAlign()) + leftPadding();
2648 int unelidedLineCount = d->lineCount;
2650 unelidedLineCount -= 1;
2651 if (unelidedLineCount > 0)
2652 node->addTextLayout(QPointF(dx, dy), &d->layout, &recycleBin, -1, -1,0, unelidedLineCount);
2655 node->addTextLayout(QPointF(dx, dy), d->elideLayout.get(), &recycleBin);
2657 if (d->extra.isAllocated()) {
2658 for (QQuickStyledTextImgTag *img : std::as_const(d->extra->visibleImgTags)) {
2659 if (img->pix && img->pix->isReady()) {
2660 node->addImage(QRectF(img->pos.x() + dx,
2663 img->size.height()),
2671 d->containsUnscalableGlyphs = node->containsUnscalableGlyphs();
2675 invalidateFontCaches();
2677 node->discardUnusedNodes(&recycleBin);