Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qquicktext.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qquicktext_p.h"
5#include "qquicktext_p_p.h"
6
7#include <private/qqmldebugserviceinterfaces_p.h>
8#include <private/qqmldebugconnector_p.h>
9
10#include <QtQuick/private/qsgcontext_p.h>
11#include <private/qqmlglobal_p.h>
12#include <private/qsgadaptationlayer_p.h>
13#include "qquicktextnode_p.h"
14#include "qquickimage_p_p.h"
15#include "qquicktextutil_p.h"
17
18#include <QtQuick/private/qsgtexture_p.h>
19
20#include <QtQml/qqmlinfo.h>
21#include <QtGui/qevent.h>
22#include <QtGui/qabstracttextdocumentlayout.h>
23#include <QtGui/qpainter.h>
24#include <QtGui/qtextdocument.h>
25#include <QtGui/qtextobject.h>
26#include <QtGui/qtextcursor.h>
27#include <QtGui/qguiapplication.h>
28#include <QtGui/qinputmethod.h>
29
30#include <private/qtextengine_p.h>
31#include <private/qquickstyledtext_p.h>
32#include <QtQuick/private/qquickpixmapcache_p.h>
33
34#include <qmath.h>
35#include <limits.h>
36
38
40
42
43#if !defined(QQUICKTEXT_LARGETEXT_THRESHOLD)
44 #define QQUICKTEXT_LARGETEXT_THRESHOLD 10000
45#endif
46// if QString::size() > largeTextSizeThreshold, we render more often, but only visible lines
48
50 : fontInfo(font), elideLayout(nullptr), textLine(nullptr), lineWidth(0)
51 , color(0xFF000000), linkColor(0xFF0000FF), styleColor(0xFF000000)
52 , lineCount(1), multilengthEos(-1)
53 , elideMode(QQuickText::ElideNone), hAlign(QQuickText::AlignLeft), vAlign(QQuickText::AlignTop)
54 , format(QQuickText::AutoText), wrapMode(QQuickText::NoWrap)
55 , style(QQuickText::Normal)
56 , renderType(QQuickTextUtil::textRenderType<QQuickText>())
57 , updateType(UpdatePaintNode)
58 , maximumLineCountValid(false), updateOnComponentComplete(true), richText(false)
59 , styledText(false), widthExceeded(false), heightExceeded(false), internalWidthUpdate(false)
60 , requireImplicitSize(false), implicitWidthValid(false), implicitHeightValid(false)
61 , truncated(false), hAlignImplicit(true), rightToLeftText(false)
62 , layoutTextElided(false), textHasChanged(true), needToUpdateLayout(false), formatModifiesFontSize(false)
63 , polishSize(false)
64 , updateSizeRecursionGuard(false)
65{
67}
68
70 : padding(0)
71 , topPadding(0)
72 , leftPadding(0)
73 , rightPadding(0)
74 , bottomPadding(0)
75 , explicitTopPadding(false)
76 , explicitLeftPadding(false)
77 , explicitRightPadding(false)
78 , explicitBottomPadding(false)
79 , lineHeight(1.0)
80 , doc(nullptr)
81 , minimumPixelSize(12)
82 , minimumPointSize(12)
83 , nbActiveDownloads(0)
84 , maximumLineCount(INT_MAX)
85 , renderTypeQuality(QQuickText::DefaultRenderTypeQuality)
86 , lineHeightValid(false)
87 , lineHeightMode(QQuickText::ProportionalHeight)
88 , fontSizeMode(QQuickText::FixedSize)
89{
90}
91
93{
94 Q_Q(QQuickText);
95 q->setAcceptedMouseButtons(Qt::LeftButton);
97 q->setFlag(QQuickItem::ItemObservesViewport); // default until size is known
98}
99
101{
102 delete elideLayout;
103 delete textLine; textLine = nullptr;
104
105 if (extra.isAllocated()) {
106 qDeleteAll(extra->imgTags);
107 extra->imgTags.clear();
108 }
109}
110
112{
113 if (!requireImplicitSize) {
114 // We don't calculate implicitWidth unless it is required.
115 // We need to force a size update now to ensure implicitWidth is calculated
116 QQuickTextPrivate *me = const_cast<QQuickTextPrivate*>(this);
117 me->requireImplicitSize = true;
118 me->updateSize();
119 }
120 return implicitWidth;
121}
122
124{
125 if (!requireImplicitSize) {
126 QQuickTextPrivate *me = const_cast<QQuickTextPrivate*>(this);
127 me->requireImplicitSize = true;
128 me->updateSize();
129 }
130 return implicitHeight;
131}
132
134{
135 Q_Q(const QQuickText);
136 return q->width() - q->leftPadding() - q->rightPadding();
137}
138
140{
141 Q_Q(const QQuickText);
142 return q->height() - q->topPadding() - q->bottomPadding();
143}
144
146{
147 Q_Q(QQuickText);
148 qreal oldPadding = q->topPadding();
149 if (!reset || extra.isAllocated()) {
150 extra.value().topPadding = value;
151 extra.value().explicitTopPadding = !reset;
152 }
153 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
154 updateSize();
155 emit q->topPaddingChanged();
156 }
157}
158
160{
161 Q_Q(QQuickText);
162 qreal oldPadding = q->leftPadding();
163 if (!reset || extra.isAllocated()) {
164 extra.value().leftPadding = value;
165 extra.value().explicitLeftPadding = !reset;
166 }
167 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
168 updateSize();
169 emit q->leftPaddingChanged();
170 }
171}
172
174{
175 Q_Q(QQuickText);
176 qreal oldPadding = q->rightPadding();
177 if (!reset || extra.isAllocated()) {
178 extra.value().rightPadding = value;
179 extra.value().explicitRightPadding = !reset;
180 }
181 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
182 updateSize();
183 emit q->rightPaddingChanged();
184 }
185}
186
188{
189 Q_Q(QQuickText);
190 qreal oldPadding = q->bottomPadding();
191 if (!reset || extra.isAllocated()) {
192 extra.value().bottomPadding = value;
193 extra.value().explicitBottomPadding = !reset;
194 }
195 if ((!reset && !qFuzzyCompare(oldPadding, value)) || (reset && !qFuzzyCompare(oldPadding, padding()))) {
196 updateSize();
197 emit q->bottomPaddingChanged();
198 }
199}
200
210void QQuickText::q_updateLayout()
211{
212 Q_D(QQuickText);
213 d->updateLayout();
214}
215
217{
218 Q_Q(QQuickText);
219 if (!q->isComponentComplete()) {
221 return;
222 }
224 layoutTextElided = false;
225
226 if (extra.isAllocated())
227 extra->visibleImgTags.clear();
228 needToUpdateLayout = false;
229
230 // Setup instance of QTextLayout for all cases other than richtext
231 if (!richText) {
232 if (textHasChanged) {
233 if (styledText && !text.isEmpty()) {
235 // needs temporary bool because formatModifiesFontSize is in a bit-field
236 bool fontSizeModified = false;
237 QList<QQuickStyledTextImgTag*> someImgTags = extra.isAllocated() ? extra->imgTags : QList<QQuickStyledTextImgTag*>();
238 QQuickStyledText::parse(text, layout, someImgTags, q->baseUrl(), qmlContext(q), !maximumLineCountValid, &fontSizeModified);
239 if (someImgTags.size() || extra.isAllocated())
240 extra.value().imgTags = someImgTags;
241 formatModifiesFontSize = fontSizeModified;
242 multilengthEos = -1;
243 } else {
244 QString tmp = text;
245 multilengthEos = tmp.indexOf(QLatin1Char('\x9c'));
246 if (multilengthEos != -1)
247 tmp = tmp.mid(0, multilengthEos);
249 layout.setText(tmp);
250 }
251 textHasChanged = false;
252 }
253 } else if (extra.isAllocated() && extra->lineHeightValid) {
254 ensureDoc();
257 QTextBlockFormat blockFormat;
259 for (QTextBlock it = extra->doc->begin(); it != extra->doc->end(); it = it.next()) {
261 cursor.mergeBlockFormat(blockFormat);
262 }
263 }
264
265 updateSize();
266
267 if (needToUpdateLayout) {
268 needToUpdateLayout = false;
269 textHasChanged = true;
270 updateLayout();
271 }
272
273 q->polish();
274}
275
276void QQuickText::imageDownloadFinished()
277{
278 Q_D(QQuickText);
279
280 (d->extra->nbActiveDownloads)--;
281
282 // when all the remote images have been downloaded,
283 // if one of the sizes was not specified at parsing time
284 // we use the implicit size from pixmapcache and re-layout.
285
286 if (d->extra.isAllocated() && d->extra->nbActiveDownloads == 0) {
287 bool needToUpdateLayout = false;
288 for (QQuickStyledTextImgTag *img : std::as_const(d->extra->visibleImgTags)) {
289 if (!img->size.isValid()) {
290 img->size = img->pix->implicitSize();
291 needToUpdateLayout = true;
292 }
293 }
294
295 if (needToUpdateLayout) {
296 d->textHasChanged = true;
297 d->updateLayout();
298 } else {
300 update();
301 }
302 }
303}
304
306{
307 Q_Q(QQuickText);
308
309 qreal yoff = 0;
310
311 if (q->heightValid()) {
313 yoff = dy;
315 yoff = dy/2;
316 }
317
318 q->setBaselineOffset(baseline + yoff + q->topPadding());
319}
320
322{
323 Q_Q(QQuickText);
324 const QSizeF contentSize(q->contentWidth(), q->contentHeight());
325
326 if (contentSize != previousSize) {
327 emit q->contentSizeChanged();
328 if (contentSize.width() != previousSize.width())
329 emit q->contentWidthChanged(contentSize.width());
330 if (contentSize.height() != previousSize.height())
331 emit q->contentHeightChanged(contentSize.height());
332 }
333}
334
336{
337 Q_Q(QQuickText);
338
339 if (!q->isComponentComplete()) {
341 return;
342 }
343
344 if (!requireImplicitSize) {
347 // if the implicitWidth is used, then updateSize() has already been called (recursively)
349 return;
350 }
351
352 qreal hPadding = q->leftPadding() + q->rightPadding();
353 qreal vPadding = q->topPadding() + q->bottomPadding();
354
355 const QSizeF previousSize(q->contentWidth(), q->contentHeight());
356
358 // How much more expensive is it to just do a full layout on an empty string here?
359 // There may be subtle differences in the height and baseline calculations between
360 // QTextLayout and QFontMetrics and the number of variables that can affect the size
361 // and position of a line is increasing.
363 qreal fontHeight = qCeil(fm.height()); // QScriptLine and therefore QTextLine rounds up
364 if (!richText) { // line height, so we will as well.
366 ? lineHeight()
367 : fontHeight * lineHeight();
368 }
369 updateBaseline(fm.ascent(), q->height() - fontHeight - vPadding);
370 q->setImplicitSize(hPadding, fontHeight + qMax(lineHeightOffset(), 0) + vPadding);
371 layedOutTextRect = QRectF(0, 0, 0, fontHeight);
372 advance = QSizeF();
373 signalSizeChange(previousSize);
374 lineCount = 1;
375 emit q->lineCountChanged();
377 q->update();
378 return;
379 }
380
381 QSizeF size(0, 0);
382
383 //setup instance of QTextLayout for all cases other than richtext
384 if (!richText) {
385 qreal baseline = 0;
387
388 if (internalWidthUpdate) // probably the result of a binding loop, but by letting it
389 return; // get this far we'll get a warning to that effect if it is.
390
392 size = textRect.size();
393 updateBaseline(baseline, q->height() - size.height() - vPadding);
394 } else {
395 widthExceeded = true; // always relayout rich text on width changes..
396 heightExceeded = false; // rich text layout isn't affected by height changes.
397 ensureDoc();
398 extra->doc->setDefaultFont(font);
399 QQuickText::HAlignment horizontalAlignment = q->effectiveHAlign();
400 if (rightToLeftText) {
401 if (horizontalAlignment == QQuickText::AlignLeft)
402 horizontalAlignment = QQuickText::AlignRight;
403 else if (horizontalAlignment == QQuickText::AlignRight)
404 horizontalAlignment = QQuickText::AlignLeft;
405 }
407 option.setAlignment((Qt::Alignment)int(horizontalAlignment | vAlign));
409 option.setUseDesignMetrics(renderType != QQuickText::NativeRendering);
410 extra->doc->setDefaultTextOption(option);
411 qreal naturalWidth = 0;
412 if (requireImplicitSize && q->widthValid()) {
413 extra->doc->setTextWidth(-1);
414 naturalWidth = extra->doc->idealWidth();
415 const bool wasInLayout = internalWidthUpdate;
416 internalWidthUpdate = true;
417 q->setImplicitWidth(naturalWidth + hPadding);
418 internalWidthUpdate = wasInLayout;
419 }
421 return;
422
423 extra->doc->setPageSize(QSizeF(q->width(), -1));
424 if (q->widthValid() && (wrapMode != QQuickText::NoWrap || extra->doc->idealWidth() < availableWidth()))
425 extra->doc->setTextWidth(availableWidth());
426 else
427 extra->doc->setTextWidth(extra->doc->idealWidth()); // ### Text does not align if width is not set (QTextDoc bug)
428
429 QSizeF dsize = extra->doc->size();
430 layedOutTextRect = QRectF(QPointF(0,0), dsize);
431 size = QSizeF(extra->doc->idealWidth(),dsize.height());
432
434 updateBaseline(fm.ascent(), q->height() - size.height() - vPadding);
435
436 //### need to confirm cost of always setting these for richText
437 internalWidthUpdate = true;
438 qreal oldWidth = q->width();
439 qreal iWidth = -1;
440 if (!q->widthValid())
441 iWidth = size.width();
442 if (iWidth > -1)
443 q->setImplicitSize(iWidth + hPadding, size.height() + qMax(lineHeightOffset(), 0) + vPadding);
444 internalWidthUpdate = false;
445
446 // If the implicit width update caused a recursive change of the width,
447 // we will have skipped integral parts of the layout due to the
448 // internalWidthUpdate recursion guard. To make sure everything is up
449 // to date, we need to run a second pass over the layout when updateSize()
450 // is done.
451 if (!qFuzzyCompare(q->width(), oldWidth) && !updateSizeRecursionGuard) {
453 updateSize();
455 } else {
456 if (iWidth == -1)
457 q->setImplicitHeight(size.height() + lineHeightOffset() + vPadding);
458
459 QTextBlock firstBlock = extra->doc->firstBlock();
460 while (firstBlock.layout()->lineCount() == 0)
461 firstBlock = firstBlock.next();
462
463 QTextBlock lastBlock = extra->doc->lastBlock();
464 while (lastBlock.layout()->lineCount() == 0)
465 lastBlock = lastBlock.previous();
466
467 if (firstBlock.lineCount() > 0 && lastBlock.lineCount() > 0) {
468 QTextLine firstLine = firstBlock.layout()->lineAt(0);
469 QTextLine lastLine = lastBlock.layout()->lineAt(lastBlock.layout()->lineCount() - 1);
470 advance = QSizeF(lastLine.horizontalAdvance(),
471 (lastLine.y() + lastBlock.layout()->position().y()) - (firstLine.y() + firstBlock.layout()->position().y()));
472 } else {
473 advance = QSizeF();
474 }
475 }
476 }
477
478 signalSizeChange(previousSize);
480 q->update();
481}
482
484 : QObject(), m_line(nullptr), m_height(0), m_lineOffset(0)
485{
486}
487
489{
490 m_line = line;
491}
492
494{
495 m_lineOffset = offset;
496}
497
499{
500 m_fullLayoutTextLength = length;
501}
502
504{
505 if (m_line)
506 return m_line->lineNumber() + m_lineOffset;
507 return 0;
508}
509
511{
512 if (m_line)
513 return m_line->naturalTextWidth();
514 return 0;
515}
516
518{
519 if (m_line && (m_line->textStart() + m_line->textLength()) == m_fullLayoutTextLength) {
520 // Ensure that isLast will change if the user reduced the width of the line
521 // so that the text no longer fits.
522 return m_line->width() >= m_line->naturalTextWidth();
523 }
524
525 return false;
526}
527
529{
530 if (m_line)
531 return m_line->width();
532 return 0;
533}
534
536{
537 if (m_line)
538 m_line->setLineWidth(width);
539}
540
542{
543 if (m_height)
544 return m_height;
545 if (m_line)
546 return m_line->height();
547 return 0;
548}
549
551{
552 if (m_line)
553 m_line->setPosition(QPointF(m_line->x(), m_line->y() - m_line->height() + height));
554 m_height = height;
555}
556
558{
559 if (m_line)
560 return m_line->x();
561 return 0;
562}
563
565{
566 if (m_line)
567 m_line->setPosition(QPointF(x, m_line->y()));
568}
569
571{
572 if (m_line)
573 return m_line->y();
574 return 0;
575}
576
578{
579 if (m_line)
580 m_line->setPosition(QPointF(m_line->x(), y));
581}
582
584{
585 Q_Q(QQuickText);
587}
588
589void QQuickTextPrivate::setupCustomLineGeometry(QTextLine &line, qreal &height, int fullLayoutTextLength, int lineOffset)
590{
591 Q_Q(QQuickText);
592
593 if (!textLine)
595 textLine->setFullLayoutTextLength(fullLayoutTextLength);
599 textLine->setLineOffset(lineOffset);
600
601 // use the text item's width by default if it has one and wrap is on or text must be aligned
602 if (q->widthValid() && (q->wrapMode() != QQuickText::NoWrap ||
603 q->effectiveHAlign() != QQuickText::AlignLeft))
605 else
606 textLine->setWidth(INT_MAX);
607 if (lineHeight() != 1.0)
609
610 emit q->lineLaidOut(textLine);
611
612 height += textLine->height();
613}
614
616 const int start, const int length, int offset, QVector<QTextLayout::FormatRange> *elidedFormats)
617{
618 const int end = start + length;
620 for (int i = 0; i < formats.size(); ++i) {
622 const int formatLength = qMin(format.start + format.length, end) - qMax(format.start, start);
623 if (formatLength > 0) {
624 format.start = qMax(offset, format.start - start + offset);
625 format.length = formatLength;
626 elidedFormats->append(format);
627 }
628 }
629}
630
631QString QQuickTextPrivate::elidedText(qreal lineWidth, const QTextLine &line, const QTextLine *nextLine) const
632{
633 if (nextLine) {
634 return layout.engine()->elidedText(
637 0,
638 line.textStart(),
639 line.textLength() + nextLine->textLength());
640 } else {
641 QString elideText = layout.text().mid(line.textStart(), line.textLength());
642 if (!styledText) {
643 // QFontMetrics won't help eliding styled text.
644 elideText[elideText.size() - 1] = elideChar;
645 // Appending the elide character may push the line over the maximum width
646 // in which case the elided text will need to be elided.
648 if (metrics.horizontalAdvance(elideChar) + line.naturalTextWidth() >= lineWidth)
649 elideText = metrics.elidedText(elideText, Qt::TextElideMode(elideMode), lineWidth);
650 }
651 return elideText;
652 }
653}
654
656{
658 if (elideLayout)
660}
661
670{
671 Q_Q(QQuickText);
672
673 bool singlelineElide = elideMode != QQuickText::ElideNone && q->widthValid();
674 bool multilineElide = elideMode == QQuickText::ElideRight
675 && q->widthValid()
676 && (q->heightValid() || maximumLineCountValid);
677
679 && ((singlelineElide && availableWidth() <= 0.)
680 || (multilineElide && q->heightValid() && availableHeight() <= 0.))) {
681 // we are elided and we have a zero width or height
682 widthExceeded = q->widthValid() && availableWidth() <= 0.;
683 heightExceeded = q->heightValid() && availableHeight() <= 0.;
684
685 if (!truncated) {
686 truncated = true;
687 emit q->truncatedChanged();
688 }
689 if (lineCount) {
690 lineCount = 0;
691 q->setFlag(QQuickItem::ItemObservesViewport, false);
692 emit q->lineCountChanged();
693 }
694
695 if (qFuzzyIsNull(q->width())) {
697 textHasChanged = true;
698 }
699
702 *baseline = fm.ascent();
703 return QRectF(0, 0, 0, height);
704 }
705
706 bool shouldUseDesignMetrics = renderType != QQuickText::NativeRendering;
707 if (extra.isAllocated())
708 extra->visibleImgTags.clear();
710 QTextOption textOption = layout.textOption();
711 if (textOption.alignment() != q->effectiveHAlign()
712 || textOption.wrapMode() != QTextOption::WrapMode(wrapMode)
713 || textOption.useDesignMetrics() != shouldUseDesignMetrics) {
714 textOption.setAlignment(Qt::Alignment(q->effectiveHAlign()));
716 textOption.setUseDesignMetrics(shouldUseDesignMetrics);
717 layout.setTextOption(textOption);
718 }
719 if (layout.font() != font)
721
722 lineWidth = (q->widthValid() || implicitWidthValid) && q->width() > 0
723 ? q->width()
724 : FLT_MAX;
725 qreal maxHeight = q->heightValid() ? availableHeight() : FLT_MAX;
726
727 const bool customLayout = isLineLaidOutConnected();
728 const bool wasTruncated = truncated;
729
730 bool canWrap = wrapMode != QQuickText::NoWrap && q->widthValid();
731
732 bool horizontalFit = fontSizeMode() & QQuickText::HorizontalFit && q->widthValid();
733 bool verticalFit = fontSizeMode() & QQuickText::VerticalFit
734 && (q->heightValid() || (maximumLineCountValid && canWrap));
735
736 const bool pixelSize = font.pixelSize() != -1;
737 QString layoutText = layout.text();
738
739 const qreal minimumSize = pixelSize
740 ? static_cast<qreal>(minimumPixelSize())
742 qreal largeFont = pixelSize ? font.pixelSize() : font.pointSizeF();
744 ? qMin<qreal>(minimumSize, largeFont)
745 : largeFont;
746 qreal scaledFontSize = largeFont;
747 const qreal sizeFittingThreshold(0.01);
748
749 bool widthChanged = false;
750 widthExceeded = availableWidth() <= 0 && (singlelineElide || canWrap || horizontalFit);
751 heightExceeded = availableHeight() <= 0 && (multilineElide || verticalFit);
752
753 QRectF br;
754
755 QFont scaledFont = font;
756
757 int visibleCount = 0;
758 bool elide;
759 qreal height = 0;
760 QString elideText;
761 bool once = true;
762 int elideStart = 0;
763 int elideEnd = 0;
764 bool noBreakLastLine = multilineElide && (wrapMode == QQuickText::Wrap || wrapMode == QQuickText::WordWrap);
765
766 int eos = multilengthEos;
767
768 // Repeated layouts with reduced font sizes or abbreviated strings may be required if the text
769 // doesn't fit within the item dimensions, or a binding to implicitWidth/Height changes
770 // the item dimensions.
771 for (;;) {
772 if (!once) {
773 if (pixelSize)
774 scaledFont.setPixelSize(scaledFontSize);
775 else
776 scaledFont.setPointSizeF(scaledFontSize);
777 if (layout.font() != scaledFont)
778 layout.setFont(scaledFont);
779 }
780
782
783 bool wrapped = false;
784 bool truncateHeight = false;
785 truncated = false;
786 elide = false;
787 int unwrappedLineCount = 1;
788 const int maxLineCount = maximumLineCount();
789 height = 0;
790 qreal naturalHeight = 0;
791 qreal previousHeight = 0;
792 br = QRectF();
793
794 QRectF unelidedRect;
796 for (visibleCount = 1; ; ++visibleCount) {
798
799 if (noBreakLastLine && visibleCount == maxLineCount)
801 if (customLayout) {
802 setupCustomLineGeometry(line, naturalHeight, layoutText.size());
803 } else {
804 setLineGeometry(line, lineWidth, naturalHeight);
805 }
806 if (noBreakLastLine && visibleCount == maxLineCount)
808
809 unelidedRect = br.united(line.naturalTextRect());
810
811 // Elide the previous line if the accumulated height of the text exceeds the height
812 // of the element.
813 if (multilineElide && naturalHeight > maxHeight && visibleCount > 1) {
814 elide = true;
815 heightExceeded = true;
816 if (eos != -1) // There's an abbreviated string available, skip the rest as it's
817 break; // all going to be discarded.
818
819 truncated = true;
820 truncateHeight = true;
821
822 visibleCount -= 1;
823
824 const QTextLine previousLine = layout.lineAt(visibleCount - 1);
825 elideText = layoutText.at(line.textStart() - 1) != QChar::LineSeparator
826 ? elidedText(line.width(), previousLine, &line)
827 : elidedText(line.width(), previousLine);
828 elideStart = previousLine.textStart();
829 // elideEnd isn't required for right eliding.
830
831 height = previousHeight;
832 break;
833 }
834
835 const bool isLastLine = line.textStart() + line.textLength() >= layoutText.size();
836 if (isLastLine) {
837 if (singlelineElide && visibleCount == 1 && line.naturalTextWidth() > line.width()) {
838 // Elide a single previousLine of text if its width exceeds the element width.
839 elide = true;
840 widthExceeded = true;
841 if (eos != -1) // There's an abbreviated string available.
842 break;
843
844 truncated = true;
845 elideText = layout.engine()->elidedText(
847 QFixed::fromReal(line.width()),
848 0,
849 line.textStart(),
850 line.textLength());
851 elideStart = line.textStart();
852 elideEnd = elideStart + line.textLength();
853 } else {
854 br = unelidedRect;
855 height = naturalHeight;
856 }
857 break;
858 } else {
859 const bool wrappedLine = layoutText.at(line.textStart() + line.textLength() - 1) != QChar::LineSeparator;
860 wrapped |= wrappedLine;
861
862 if (!wrappedLine)
863 ++unwrappedLineCount;
864
865 // Stop if the maximum number of lines has been reached
866 if (visibleCount == maxLineCount) {
867 truncated = true;
868 heightExceeded |= wrapped;
869
870 if (multilineElide) {
871 elide = true;
872 if (eos != -1) // There's an abbreviated string available
873 break;
874
875 const QTextLine nextLine = layout.createLine();
876 elideText = wrappedLine
877 ? elidedText(line.width(), line, &nextLine)
878 : elidedText(line.width(), line);
879 elideStart = line.textStart();
880 // elideEnd isn't required for right eliding.
881 } else {
882 br = unelidedRect;
883 height = naturalHeight;
884 }
885 break;
886 }
887 }
888 br = unelidedRect;
889 previousHeight = height;
890 height = naturalHeight;
891 }
892 widthExceeded |= wrapped;
893
894 // Save the implicit size of the text on the first layout only.
895 if (once) {
896 once = false;
897
898 // If implicit sizes are required layout any additional lines up to the maximum line
899 // count.
900 if ((requireImplicitSize) && line.isValid() && unwrappedLineCount < maxLineCount) {
901 // Layout the remainder of the wrapped lines up to maxLineCount to get the implicit
902 // height.
903 for (int lineCount = layout.lineCount(); lineCount < maxLineCount; ++lineCount) {
905 if (!line.isValid())
906 break;
907 if (layoutText.at(line.textStart() - 1) == QChar::LineSeparator)
908 ++unwrappedLineCount;
909 setLineGeometry(line, lineWidth, naturalHeight);
910 }
911
912 // Create the remainder of the unwrapped lines up to maxLineCount to get the
913 // implicit width.
914 const int eol = line.isValid()
915 ? line.textStart() + line.textLength()
916 : layoutText.size();
917 if (eol < layoutText.size() && layoutText.at(eol) != QChar::LineSeparator)
919 for (; line.isValid() && unwrappedLineCount <= maxLineCount; ++unwrappedLineCount)
921 }
923
924 const qreal naturalWidth = layout.maximumWidth();
925
926 bool wasInLayout = internalWidthUpdate;
927 internalWidthUpdate = true;
928 q->setImplicitSize(naturalWidth + q->leftPadding() + q->rightPadding(), naturalHeight + qMax(lineHeightOffset(), 0) + q->topPadding() + q->bottomPadding());
929 internalWidthUpdate = wasInLayout;
930
931 // Update any variables that are dependent on the validity of the width or height.
932 singlelineElide = elideMode != QQuickText::ElideNone && q->widthValid();
933 multilineElide = elideMode == QQuickText::ElideRight
934 && q->widthValid()
935 && (q->heightValid() || maximumLineCountValid);
936 canWrap = wrapMode != QQuickText::NoWrap && q->widthValid();
937
938 horizontalFit = fontSizeMode() & QQuickText::HorizontalFit && q->widthValid();
939 verticalFit = fontSizeMode() & QQuickText::VerticalFit
940 && (q->heightValid() || (maximumLineCountValid && canWrap));
941
942 const qreal oldWidth = lineWidth;
943 const qreal oldHeight = maxHeight;
944
945 const qreal availWidth = availableWidth();
946 const qreal availHeight = availableHeight();
947
948 lineWidth = q->widthValid() && q->width() > 0 ? availWidth : naturalWidth;
949 maxHeight = q->heightValid() ? availHeight : FLT_MAX;
950
951 // If the width of the item has changed and it's possible the result of wrapping,
952 // eliding, scaling has changed, or the text is not left aligned do another layout.
953 if ((!qFuzzyCompare(lineWidth, oldWidth) || (widthExceeded && lineWidth > oldWidth))
954 && (singlelineElide || multilineElide || canWrap || horizontalFit
955 || q->effectiveHAlign() != QQuickText::AlignLeft)) {
956 widthChanged = true;
957 widthExceeded = lineWidth >= qMin(oldWidth, naturalWidth);
958 heightExceeded = false;
959 continue;
960 }
961
962 // If the height of the item has changed and it's possible the result of eliding,
963 // line count truncation or scaling has changed, do another layout.
964 if ((maxHeight < qMin(oldHeight, naturalHeight) || (heightExceeded && maxHeight > oldHeight))
965 && (multilineElide || (canWrap && maximumLineCountValid))) {
966 widthExceeded = false;
967 heightExceeded = false;
968 continue;
969 }
970
971 // If the horizontal alignment is not left and the width was not valid we need to relayout
972 // now that we know the maximum line width.
973 if (!q->widthValid() && !implicitWidthValid && unwrappedLineCount > 1 && q->effectiveHAlign() != QQuickText::AlignLeft) {
974 widthExceeded = false;
975 heightExceeded = false;
976 continue;
977 }
978 } else if (widthChanged) {
979 widthChanged = false;
980 if (line.isValid()) {
981 for (int lineCount = layout.lineCount(); lineCount < maxLineCount; ++lineCount) {
983 if (!line.isValid())
984 break;
985 setLineGeometry(line, lineWidth, naturalHeight);
986 }
987 }
989
990 bool wasInLayout = internalWidthUpdate;
991 internalWidthUpdate = true;
992 q->setImplicitHeight(naturalHeight + qMax(lineHeightOffset(), 0) + q->topPadding() + q->bottomPadding());
993 internalWidthUpdate = wasInLayout;
994
995 multilineElide = elideMode == QQuickText::ElideRight
996 && q->widthValid()
997 && (q->heightValid() || maximumLineCountValid);
998 verticalFit = fontSizeMode() & QQuickText::VerticalFit
999 && (q->heightValid() || (maximumLineCountValid && canWrap));
1000
1001 const qreal oldHeight = maxHeight;
1002 maxHeight = q->heightValid() ? availableHeight() : FLT_MAX;
1003 // If the height of the item has changed and it's possible the result of eliding,
1004 // line count truncation or scaling has changed, do another layout.
1005 if ((maxHeight < qMin(oldHeight, naturalHeight) || (heightExceeded && maxHeight > oldHeight))
1006 && (multilineElide || (canWrap && maximumLineCountValid))) {
1007 widthExceeded = false;
1008 heightExceeded = false;
1009 continue;
1010 }
1011 } else {
1012 layout.endLayout();
1013 }
1014
1015 // If the next needs to be elided and there's an abbreviated string available
1016 // go back and do another layout with the abbreviated string.
1017 if (eos != -1 && elide) {
1018 int start = eos + 1;
1019 eos = text.indexOf(QLatin1Char('\x9c'), start);
1020 layoutText = text.mid(start, eos != -1 ? eos - start : -1);
1021 layoutText.replace(QLatin1Char('\n'), QChar::LineSeparator);
1022 layout.setText(layoutText);
1023 textHasChanged = true;
1024 continue;
1025 }
1026
1027 br.moveTop(0);
1028
1029 // Find the advance of the text layout
1030 if (layout.lineCount() > 0) {
1031 QTextLine firstLine = layout.lineAt(0);
1032 QTextLine lastLine = layout.lineAt(layout.lineCount() - 1);
1033 advance = QSizeF(lastLine.horizontalAdvance(),
1034 lastLine.y() - firstLine.y());
1035 } else {
1036 advance = QSizeF();
1037 }
1038
1039 if (!horizontalFit && !verticalFit)
1040 break;
1041
1042 // Can't find a better fit
1043 if (qFuzzyCompare(smallFont, largeFont))
1044 break;
1045
1046 // Try and find a font size that better fits the dimensions of the element.
1047 if (horizontalFit) {
1048 if (unelidedRect.width() > lineWidth || (!verticalFit && wrapped)) {
1049 widthExceeded = true;
1050 largeFont = scaledFontSize;
1051
1052 scaledFontSize = (smallFont + largeFont) / 2;
1053
1054 continue;
1055 } else if (!verticalFit) {
1056 smallFont = scaledFontSize;
1057
1058 // Check to see if the current scaledFontSize is acceptable
1059 if ((largeFont - smallFont) < sizeFittingThreshold)
1060 break;
1061
1062 scaledFontSize = (smallFont + largeFont) / 2;
1063 }
1064 }
1065
1066 if (verticalFit) {
1067 if (truncateHeight || unelidedRect.height() > maxHeight) {
1068 heightExceeded = true;
1069 largeFont = scaledFontSize;
1070
1071 scaledFontSize = (smallFont + largeFont) / 2;
1072
1073 } else {
1074 smallFont = scaledFontSize;
1075
1076 // Check to see if the current scaledFontSize is acceptable
1077 if ((largeFont - smallFont) < sizeFittingThreshold)
1078 break;
1079
1080 scaledFontSize = (smallFont + largeFont) / 2;
1081 }
1082 }
1083 }
1084
1085 implicitWidthValid = true;
1086 implicitHeightValid = true;
1087
1088 QFontInfo scaledFontInfo(scaledFont);
1089 if (fontInfo.weight() != scaledFontInfo.weight()
1090 || fontInfo.pixelSize() != scaledFontInfo.pixelSize()
1091 || fontInfo.italic() != scaledFontInfo.italic()
1092 || !qFuzzyCompare(fontInfo.pointSizeF(), scaledFontInfo.pointSizeF())
1093 || fontInfo.family() != scaledFontInfo.family()
1094 || fontInfo.styleName() != scaledFontInfo.styleName()) {
1095 fontInfo = scaledFontInfo;
1096 emit q->fontInfoChanged();
1097 }
1098
1099 if (eos != multilengthEos)
1100 truncated = true;
1101
1103
1104 if (elide) {
1105 if (!elideLayout) {
1108 }
1110 if (engine && engine->hasFormats()) {
1112 switch (elideMode) {
1114 elideFormats(elideStart, elideText.size() - 1, 0, &formats);
1115 break;
1117 elideFormats(elideEnd - elideText.size() + 1, elideText.size() - 1, 1, &formats);
1118 break;
1120 const int index = elideText.indexOf(elideChar);
1121 if (index != -1) {
1122 elideFormats(elideStart, index, 0, &formats);
1124 elideEnd - elideText.size() + index + 1,
1125 elideText.size() - index - 1,
1126 index + 1,
1127 &formats);
1128 }
1129 break;
1130 }
1131 default:
1132 break;
1133 }
1135 }
1136
1139 elideLayout->setText(elideText);
1141
1142 QTextLine elidedLine = elideLayout->createLine();
1143 elidedLine.setPosition(QPointF(0, height));
1144 if (customLayout) {
1145 setupCustomLineGeometry(elidedLine, height, elideText.size(), visibleCount - 1);
1146 } else {
1147 setLineGeometry(elidedLine, lineWidth, height);
1148 }
1150
1151 br = br.united(elidedLine.naturalTextRect());
1152
1153 if (visibleCount == 1)
1155 } else {
1156 delete elideLayout;
1157 elideLayout = nullptr;
1158 }
1159
1160 QTextLine firstLine = visibleCount == 1 && elideLayout
1161 ? elideLayout->lineAt(0)
1162 : layout.lineAt(0);
1163 if (firstLine.isValid())
1164 *baseline = firstLine.y() + firstLine.ascent();
1165
1166 if (!customLayout)
1167 br.setHeight(height);
1168
1169 //Update the number of visible lines
1170 if (lineCount != visibleCount) {
1171 lineCount = visibleCount;
1172 emit q->lineCountChanged();
1173 }
1174
1175 if (truncated != wasTruncated)
1176 emit q->truncatedChanged();
1177
1178 return br;
1179}
1180
1182{
1183 Q_Q(QQuickText);
1184 line.setLineWidth(lineWidth);
1185
1186 if (extra.isAllocated() && extra->imgTags.isEmpty()) {
1187 line.setPosition(QPointF(line.position().x(), height));
1189 return;
1190 }
1191
1192 qreal textTop = 0;
1193 qreal textHeight = line.height();
1194 qreal totalLineHeight = textHeight;
1195
1197
1198 if (extra.isAllocated()) {
1199 for (QQuickStyledTextImgTag *image : std::as_const(extra->imgTags)) {
1200 if (image->position >= line.textStart() &&
1201 image->position < line.textStart() + line.textLength()) {
1202
1203 if (!image->pix) {
1204 const QQmlContext *context = qmlContext(q);
1205 const QUrl url = context->resolvedUrl(q->baseUrl()).resolved(image->url);
1206 image->pix = new QQuickPixmap(context->engine(), url, QRect(), image->size);
1207 if (image->pix->isLoading()) {
1208 image->pix->connectFinished(q, SLOT(imageDownloadFinished()));
1209 if (!extra.isAllocated() || !extra->nbActiveDownloads)
1210 extra.value().nbActiveDownloads = 0;
1211 extra->nbActiveDownloads++;
1212 } else if (image->pix->isReady()) {
1213 if (!image->size.isValid()) {
1214 image->size = image->pix->implicitSize();
1215 // if the size of the image was not explicitly set, we need to
1216 // call updateLayout() once again.
1217 needToUpdateLayout = true;
1218 }
1219 } else if (image->pix->isError()) {
1220 qmlWarning(q) << image->pix->error();
1221 }
1222 }
1223
1224 qreal ih = qreal(image->size.height());
1225 if (image->align == QQuickStyledTextImgTag::Top)
1226 image->pos.setY(0);
1227 else if (image->align == QQuickStyledTextImgTag::Middle)
1228 image->pos.setY((textHeight / 2.0) - (ih / 2.0));
1229 else
1230 image->pos.setY(textHeight - ih);
1231 imagesInLine << image;
1232 textTop = qMax(textTop, qAbs(image->pos.y()));
1233 }
1234 }
1235 }
1236
1237 for (QQuickStyledTextImgTag *image : std::as_const(imagesInLine)) {
1238 totalLineHeight = qMax(totalLineHeight, textTop + image->pos.y() + image->size.height());
1239 const int leadX = line.cursorToX(image->position);
1240 const int trailX = line.cursorToX(image->position, QTextLine::Trailing);
1241 const bool rtl = trailX < leadX;
1242 image->pos.setX(leadX + (rtl ? (-image->offset - image->size.width()) : image->offset));
1243 image->pos.setY(image->pos.y() + height + textTop);
1244 extra->visibleImgTags << image;
1245 }
1246
1247 line.setPosition(QPointF(line.position().x(), height + textTop));
1248 height += (lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : totalLineHeight * lineHeight();
1249}
1250
1255{
1256 QFontMetricsF fm(font);
1257 qreal fontHeight = qCeil(fm.height()); // QScriptLine and therefore QTextLine rounds up
1258 return lineHeightMode() == QQuickText::FixedHeight ? fontHeight - lineHeight()
1259 : (1.0 - lineHeight()) * fontHeight;
1260}
1261
1266{
1267 if (!extra.isAllocated() || !extra->doc) {
1268 Q_Q(QQuickText);
1269 extra.value().doc = new QQuickTextDocumentWithImageResources(q);
1270 extra->doc->setPageSize(QSizeF(0, 0));
1271 extra->doc->setDocumentMargin(0);
1272 const QQmlContext *context = qmlContext(q);
1273 extra->doc->setBaseUrl(context ? context->resolvedUrl(q->baseUrl()) : q->baseUrl());
1275 q, QQuickText, SLOT(q_updateLayout()));
1276 }
1277}
1278
1280{
1281 ensureDoc();
1282#if QT_CONFIG(textmarkdownreader)
1283 if (markdownText)
1284 extra->doc->setMarkdown(text);
1285 else
1286#endif
1287#if QT_CONFIG(texthtmlparser)
1288 extra->doc->setHtml(text);
1289#else
1290 extra->doc->setPlainText(text);
1291#endif
1292 extra->doc->clearResources();
1293 rightToLeftText = extra->doc->toPlainText().isRightToLeft();
1294}
1295
1342{
1343 Q_D(QQuickText);
1344 d->init();
1345}
1346
1349{
1350 Q_D(QQuickText);
1351 d->init();
1352}
1353
1355{
1356}
1357
1628
1688
1690{
1691 Q_D(const QQuickText);
1692 return d->sourceFont;
1693}
1694
1696{
1697 Q_D(QQuickText);
1698 if (d->sourceFont == font)
1699 return;
1700
1701 d->sourceFont = font;
1702 QFont oldFont = d->font;
1703 d->font = font;
1704
1705 if (!antialiasing())
1706 d->font.setStyleStrategy(QFont::NoAntialias);
1707
1708 if (d->font.pointSizeF() != -1) {
1709 // 0.5pt resolution
1710 qreal size = qRound(d->font.pointSizeF()*2.0);
1711 d->font.setPointSizeF(size/2.0);
1712 }
1713
1714 if (oldFont != d->font) {
1715 // if the format changes the size of the text
1716 // with headings or <font> tag, we need to re-parse
1717 if (d->formatModifiesFontSize)
1718 d->textHasChanged = true;
1719 d->implicitWidthValid = false;
1720 d->implicitHeightValid = false;
1721 d->updateLayout();
1722 }
1723
1724 emit fontChanged(d->sourceFont);
1725}
1726
1728{
1729 Q_D(QQuickText);
1730 Q_UNUSED(value);
1731 switch (change) {
1733 if (!antialiasing())
1734 d->font.setStyleStrategy(QFont::NoAntialias);
1735 else
1736 d->font.setStyleStrategy(QFont::PreferAntialias);
1737 d->implicitWidthValid = false;
1738 d->implicitHeightValid = false;
1739 d->updateLayout();
1740 break;
1741
1743 if (d->renderType == NativeRendering) {
1744 // Native rendering optimizes for a given pixel grid, so its results must not be scaled.
1745 // Text layout code respects the current device pixel ratio automatically, we only need
1746 // to rerun layout after the ratio changed.
1747 // Changes of implicit size should be minimal; they are hard to avoid.
1748 d->implicitWidthValid = false;
1749 d->implicitHeightValid = false;
1750 d->updateLayout();
1751 }
1752 break;
1753
1754 default:
1755 break;
1756 }
1758}
1759
1772{
1773 Q_D(const QQuickText);
1774 return d->text;
1775}
1776
1778{
1779 Q_D(QQuickText);
1780 if (d->text == n)
1781 return;
1782
1783 d->markdownText = d->format == MarkdownText;
1784 d->richText = d->format == RichText || d->markdownText;
1785 d->styledText = d->format == StyledText || (d->format == AutoText && Qt::mightBeRichText(n));
1786 d->text = n;
1787 if (isComponentComplete()) {
1788 if (d->richText) {
1789 d->updateDocumentText();
1790 } else {
1791 d->clearFormats();
1792 d->rightToLeftText = d->text.isRightToLeft();
1793 }
1794 d->determineHorizontalAlignment();
1795 }
1796 d->textHasChanged = true;
1797 d->implicitWidthValid = false;
1798 d->implicitHeightValid = false;
1799
1800 if (d->extra.isAllocated()) {
1801 qDeleteAll(d->extra->imgTags);
1802 d->extra->imgTags.clear();
1803 }
1805 d->updateLayout();
1806 setAcceptHoverEvents(d->richText || d->styledText);
1807 emit textChanged(d->text);
1808}
1809
1832{
1833 Q_D(const QQuickText);
1834 return QColor::fromRgba(d->color);
1835}
1836
1838{
1839 Q_D(QQuickText);
1840 QRgb rgb = color.rgba();
1841 if (d->color == rgb)
1842 return;
1843
1844 d->color = rgb;
1845 if (isComponentComplete()) {
1847 update();
1848 }
1850}
1851
1863{
1864 Q_D(const QQuickText);
1865 return QColor::fromRgba(d->linkColor);
1866}
1867
1869{
1870 Q_D(QQuickText);
1871 QRgb rgb = color.rgba();
1872 if (d->linkColor == rgb)
1873 return;
1874
1875 d->linkColor = rgb;
1876 if (isComponentComplete()) {
1878 update();
1879 }
1881}
1882
1907{
1908 Q_D(const QQuickText);
1909 return d->style;
1910}
1911
1913{
1914 Q_D(QQuickText);
1915 if (d->style == style)
1916 return;
1917
1918 d->style = style;
1919 if (isComponentComplete()) {
1921 update();
1922 }
1923 emit styleChanged(d->style);
1924}
1925
1942{
1943 Q_D(const QQuickText);
1944 return QColor::fromRgba(d->styleColor);
1945}
1946
1948{
1949 Q_D(QQuickText);
1950 QRgb rgb = color.rgba();
1951 if (d->styleColor == rgb)
1952 return;
1953
1954 d->styleColor = rgb;
1955 if (isComponentComplete()) {
1957 update();
1958 }
1960}
1961
1987{
1988 Q_D(const QQuickText);
1989 return d->hAlign;
1990}
1991
1993{
1994 Q_D(QQuickText);
1995 bool forceAlign = d->hAlignImplicit && d->effectiveLayoutMirror;
1996 d->hAlignImplicit = false;
1997 if (d->setHAlign(align, forceAlign) && isComponentComplete())
1998 d->updateLayout();
1999}
2000
2002{
2003 Q_D(QQuickText);
2004 d->hAlignImplicit = true;
2005 if (isComponentComplete() && d->determineHorizontalAlignment())
2006 d->updateLayout();
2007}
2008
2010{
2011 Q_D(const QQuickText);
2012 QQuickText::HAlignment effectiveAlignment = d->hAlign;
2013 if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
2014 switch (d->hAlign) {
2016 effectiveAlignment = QQuickText::AlignRight;
2017 break;
2019 effectiveAlignment = QQuickText::AlignLeft;
2020 break;
2021 default:
2022 break;
2023 }
2024 }
2025 return effectiveAlignment;
2026}
2027
2029{
2030 Q_Q(QQuickText);
2031 if (hAlign != alignment || forceAlign) {
2032 QQuickText::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
2033 hAlign = alignment;
2034
2035 emit q->horizontalAlignmentChanged(hAlign);
2036 if (oldEffectiveHAlign != q->effectiveHAlign())
2037 emit q->effectiveHorizontalAlignmentChanged();
2038 return true;
2039 }
2040 return false;
2041}
2042
2044{
2045 if (hAlignImplicit) {
2046#if QT_CONFIG(im)
2048#else
2049 bool alignToRight = rightToLeftText;
2050#endif
2052 }
2053 return false;
2054}
2055
2057{
2058 Q_Q(QQuickText);
2059 if (q->isComponentComplete()) {
2061 updateLayout();
2062 emit q->effectiveHorizontalAlignmentChanged();
2063 }
2064 }
2065}
2066
2068{
2069 Q_D(const QQuickText);
2070 return d->vAlign;
2071}
2072
2074{
2075 Q_D(QQuickText);
2076 if (d->vAlign == align)
2077 return;
2078
2079 d->vAlign = align;
2080
2081 if (isComponentComplete())
2082 d->updateLayout();
2083
2085}
2086
2106{
2107 Q_D(const QQuickText);
2108 return d->wrapMode;
2109}
2110
2112{
2113 Q_D(QQuickText);
2114 if (mode == d->wrapMode)
2115 return;
2116
2117 d->wrapMode = mode;
2118 d->updateLayout();
2119
2121}
2122
2133{
2134 Q_D(const QQuickText);
2135 return d->lineCount;
2136}
2137
2149{
2150 Q_D(const QQuickText);
2151 return d->truncated;
2152}
2153
2166{
2167 Q_D(const QQuickText);
2168 return d->maximumLineCount();
2169}
2170
2172{
2173 Q_D(QQuickText);
2174
2175 d->maximumLineCountValid = lines==INT_MAX ? false : true;
2176 if (d->maximumLineCount() != lines) {
2177 d->extra.value().maximumLineCount = lines;
2178 d->implicitHeightValid = false;
2179 d->updateLayout();
2181 }
2182}
2183
2185{
2186 Q_D(QQuickText);
2187 setMaximumLineCount(INT_MAX);
2188 if (d->truncated != false) {
2189 d->truncated = false;
2191 }
2192}
2193
2258{
2259 Q_D(const QQuickText);
2260 return d->format;
2261}
2262
2264{
2265 Q_D(QQuickText);
2266 if (format == d->format)
2267 return;
2268 d->format = format;
2269 bool wasRich = d->richText;
2270 d->markdownText = format == MarkdownText;
2271 d->richText = format == RichText || d->markdownText;
2272 d->styledText = format == StyledText || (format == AutoText && Qt::mightBeRichText(d->text));
2273
2274 if (isComponentComplete()) {
2275 if (!wasRich && d->richText) {
2276 d->updateDocumentText();
2277 } else {
2278 d->clearFormats();
2279 d->rightToLeftText = d->text.isRightToLeft();
2280 d->textHasChanged = true;
2281 }
2282 d->determineHorizontalAlignment();
2283 }
2284 d->updateLayout();
2285 setAcceptHoverEvents(d->richText || d->styledText);
2286 setAcceptedMouseButtons(d->richText || d->styledText ? Qt::LeftButton : Qt::NoButton);
2287
2288 emit textFormatChanged(d->format);
2289}
2290
2318{
2319 Q_D(const QQuickText);
2320 return d->elideMode;
2321}
2322
2324{
2325 Q_D(QQuickText);
2326 if (mode == d->elideMode)
2327 return;
2328
2329 d->elideMode = mode;
2330 d->updateLayout();
2331
2333}
2334
2359{
2360 Q_D(const QQuickText);
2361 if (!d->extra.isAllocated() || d->extra->baseUrl.isEmpty()) {
2362 if (QQmlContext *context = qmlContext(this))
2363 return context->baseUrl();
2364 else
2365 return QUrl();
2366 } else {
2367 return d->extra->baseUrl;
2368 }
2369}
2370
2372{
2373 Q_D(QQuickText);
2374 if (baseUrl() != url) {
2375 d->extra.value().baseUrl = url;
2376
2377 if (d->richText) {
2378 d->ensureDoc();
2379 d->extra->doc->setBaseUrl(url);
2380 }
2381 if (d->styledText) {
2382 d->textHasChanged = true;
2383 if (d->extra.isAllocated()) {
2384 qDeleteAll(d->extra->imgTags);
2385 d->extra->imgTags.clear();
2386 }
2387 d->updateLayout();
2388 }
2390 }
2391}
2392
2394{
2395 if (QQmlContext *context = qmlContext(this))
2396 setBaseUrl(context->baseUrl());
2397 else
2398 setBaseUrl(QUrl());
2399}
2400
2409{
2410 Q_D(const QQuickText);
2411
2412 QRectF rect = d->layedOutTextRect;
2413 rect.moveLeft(QQuickTextUtil::alignedX(rect.width(), width(), effectiveHAlign()));
2414 rect.moveTop(QQuickTextUtil::alignedY(rect.height() + d->lineHeightOffset(), height(), d->vAlign));
2415
2416 if (d->style != Normal)
2417 rect.adjust(-1, 0, 1, 2);
2418 // Could include font max left/right bearings to either side of rectangle.
2419
2420 return rect;
2421}
2422
2435{
2436 Q_D(const QQuickText);
2437
2439 if (d->style != Normal)
2440 rect.adjust(-1, 0, 1, 2);
2441 return rect;
2442}
2443
2445void QQuickText::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
2446{
2447 Q_D(QQuickText);
2448 if (d->text.isEmpty()) {
2449 QQuickItem::geometryChange(newGeometry, oldGeometry);
2450 return;
2451 }
2452
2453 bool widthChanged = newGeometry.width() != oldGeometry.width();
2454 bool heightChanged = newGeometry.height() != oldGeometry.height();
2455 bool wrapped = d->wrapMode != QQuickText::NoWrap;
2456 bool elide = d->elideMode != QQuickText::ElideNone;
2457 bool scaleFont = d->fontSizeMode() != QQuickText::FixedSize && (widthValid() || heightValid());
2458 bool verticalScale = (d->fontSizeMode() & QQuickText::VerticalFit) && heightValid();
2459
2460 bool widthMaximum = newGeometry.width() >= oldGeometry.width() && !d->widthExceeded;
2461 bool heightMaximum = newGeometry.height() >= oldGeometry.height() && !d->heightExceeded;
2462
2463 bool verticalPositionChanged = heightChanged && d->vAlign != AlignTop;
2464
2465 if ((!widthChanged && !heightChanged) || d->internalWidthUpdate)
2466 goto geomChangeDone;
2467
2468 if ((effectiveHAlign() != QQuickText::AlignLeft && widthChanged) || verticalPositionChanged) {
2469 // If the width has changed and we're not left aligned do an update so the text is
2470 // repositioned even if a full layout isn't required. And the same for vertical.
2472 update();
2473 }
2474
2475 if (!wrapped && !elide && !scaleFont && !verticalPositionChanged)
2476 goto geomChangeDone; // left aligned unwrapped text without eliding never needs relayout
2477
2478 if (elide // eliding and dimensions were and remain invalid;
2479 && ((widthValid() && oldGeometry.width() <= 0 && newGeometry.width() <= 0)
2480 || (heightValid() && oldGeometry.height() <= 0 && newGeometry.height() <= 0))) {
2481 goto geomChangeDone;
2482 }
2483
2484 if (widthMaximum && heightMaximum && !d->isLineLaidOutConnected() && !verticalPositionChanged) // Size is sufficient and growing.
2485 goto geomChangeDone;
2486
2487 if (!(widthChanged || widthMaximum) && !d->isLineLaidOutConnected()) { // only height has changed
2488 if (!verticalPositionChanged) {
2489 if (newGeometry.height() > oldGeometry.height()) {
2490 if (!d->heightExceeded && !qFuzzyIsNull(oldGeometry.height())) {
2491 // Height is adequate and growing, and it wasn't 0 previously.
2492 goto geomChangeDone;
2493 }
2494 if (d->lineCount == d->maximumLineCount()) // Reached maximum line and height is growing.
2495 goto geomChangeDone;
2496 } else if (newGeometry.height() < oldGeometry.height()) {
2497 if (d->lineCount < 2 && !verticalScale && newGeometry.height() > 0) // A single line won't be truncated until the text is 0 height.
2498 goto geomChangeDone;
2499
2500 if (!verticalScale // no scaling, no eliding, and either unwrapped, or no maximum line count.
2501 && d->elideMode != QQuickText::ElideRight
2502 && !(d->maximumLineCountValid && d->widthExceeded)) {
2503 goto geomChangeDone;
2504 }
2505 }
2506 }
2507 } else if (!heightChanged && widthMaximum) {
2508 if (!qFuzzyIsNull(oldGeometry.width())) {
2509 // no change to height, width is adequate and wasn't 0 before
2510 goto geomChangeDone;
2511 }
2512 }
2513
2514 if (d->updateOnComponentComplete || d->textHasChanged) {
2515 // We need to re-elide
2516 d->updateLayout();
2517 } else {
2518 // We just need to re-layout
2519 d->updateSize();
2520 }
2521
2522geomChangeDone:
2523 QQuickItem::geometryChange(newGeometry, oldGeometry);
2524}
2525
2526void QQuickText::triggerPreprocess()
2527{
2528 Q_D(QQuickText);
2529 if (d->updateType == QQuickTextPrivate::UpdateNone)
2531 update();
2532}
2533
2535{
2536 Q_UNUSED(data);
2537 Q_D(QQuickText);
2538
2539 if (d->text.isEmpty()) {
2540 delete oldNode;
2541 return nullptr;
2542 }
2543
2544 if (d->updateType != QQuickTextPrivate::UpdatePaintNode && oldNode != nullptr) {
2545 // Update done in preprocess() in the nodes
2546 d->updateType = QQuickTextPrivate::UpdateNone;
2547 return oldNode;
2548 }
2549
2550 d->updateType = QQuickTextPrivate::UpdateNone;
2551
2552 const qreal dy = QQuickTextUtil::alignedY(d->layedOutTextRect.height() + d->lineHeightOffset(), d->availableHeight(), d->vAlign) + topPadding();
2553
2554 QQuickTextNode *node = nullptr;
2555 if (!oldNode)
2556 node = new QQuickTextNode(this);
2557 else
2558 node = static_cast<QQuickTextNode *>(oldNode);
2559
2560 node->setUseNativeRenderer(d->renderType == NativeRendering);
2561 node->setRenderTypeQuality(d->renderTypeQuality());
2562 node->deleteContent();
2563 node->setMatrix(QMatrix4x4());
2564
2565 const QColor color = QColor::fromRgba(d->color);
2566 const QColor styleColor = QColor::fromRgba(d->styleColor);
2567 const QColor linkColor = QColor::fromRgba(d->linkColor);
2568
2569 if (d->richText) {
2570 const qreal dx = QQuickTextUtil::alignedX(d->layedOutTextRect.width(), d->availableWidth(), effectiveHAlign()) + leftPadding();
2571 d->ensureDoc();
2572 node->addTextDocument(QPointF(dx, dy), d->extra->doc, color, d->style, styleColor, linkColor);
2573 } else if (d->layedOutTextRect.width() > 0) {
2574 const qreal dx = QQuickTextUtil::alignedX(d->lineWidth, d->availableWidth(), effectiveHAlign()) + leftPadding();
2575 int unelidedLineCount = d->lineCount;
2576 if (d->elideLayout)
2577 unelidedLineCount -= 1;
2578 if (unelidedLineCount > 0) {
2579 node->addTextLayout(
2580 QPointF(dx, dy),
2581 &d->layout,
2582 color, d->style, styleColor, linkColor,
2583 QColor(), QColor(), -1, -1,
2584 0, unelidedLineCount);
2585 }
2586 if (d->elideLayout)
2587 node->addTextLayout(QPointF(dx, dy), d->elideLayout, color, d->style, styleColor, linkColor);
2588
2589 if (d->extra.isAllocated()) {
2590 for (QQuickStyledTextImgTag *img : std::as_const(d->extra->visibleImgTags)) {
2591 QQuickPixmap *pix = img->pix;
2592 if (pix && pix->isReady())
2593 node->addImage(QRectF(img->pos.x() + dx, img->pos.y() + dy, pix->width(), pix->height()), pix->image());
2594 }
2595 }
2596 }
2597
2598 // The font caches have now been initialized on the render thread, so they have to be
2599 // invalidated before we can use them from the main thread again.
2601
2602 return node;
2603}
2604
2606{
2607 Q_D(QQuickText);
2608 const bool clipNodeChanged =
2609 d->componentComplete && d->clipNode() && d->clipNode()->rect() != clipRect();
2610 if (clipNodeChanged)
2611 d->dirty(QQuickItemPrivate::Clip);
2612
2613 // If the fonts used for rendering are different from the ones used in the GUI thread,
2614 // it means we will get warnings and corrupted text. If this case is detected, we need
2615 // to update the text layout before creating the scenegraph nodes.
2616 if (!d->assignedFont.isEmpty() && QFontInfo(d->font).family() != d->assignedFont)
2617 d->polishSize = true;
2618
2619 if (d->polishSize) {
2620 d->updateSize();
2621 d->polishSize = false;
2622 }
2624}
2625
2633{
2634 Q_D(const QQuickText);
2635 return d->layedOutTextRect.width();
2636}
2637
2645{
2646 Q_D(const QQuickText);
2647 return d->layedOutTextRect.height() + qMax(d->lineHeightOffset(), 0);
2648}
2649
2660{
2661 Q_D(const QQuickText);
2662 return d->lineHeight();
2663}
2664
2666{
2667 Q_D(QQuickText);
2668
2669 if ((d->lineHeight() == lineHeight) || (lineHeight < 0.0))
2670 return;
2671
2672 d->extra.value().lineHeightValid = true;
2673 d->extra.value().lineHeight = lineHeight;
2674 d->implicitHeightValid = false;
2675 d->updateLayout();
2677}
2678
2690{
2691 Q_D(const QQuickText);
2692 return d->lineHeightMode();
2693}
2694
2696{
2697 Q_D(QQuickText);
2698 if (mode == d->lineHeightMode())
2699 return;
2700
2701 d->implicitHeightValid = false;
2702 d->extra.value().lineHeightValid = true;
2703 d->extra.value().lineHeightMode = mode;
2704 d->updateLayout();
2705
2707}
2708
2745{
2746 Q_D(const QQuickText);
2747 return d->fontSizeMode();
2748}
2749
2751{
2752 Q_D(QQuickText);
2753 if (d->fontSizeMode() == mode)
2754 return;
2755
2756 d->polishSize = true;
2757 polish();
2758
2759 d->extra.value().fontSizeMode = mode;
2761}
2762
2774{
2775 Q_D(const QQuickText);
2776 return d->minimumPixelSize();
2777}
2778
2780{
2781 Q_D(QQuickText);
2782 if (d->minimumPixelSize() == size)
2783 return;
2784
2785 if (d->fontSizeMode() != FixedSize && (widthValid() || heightValid())) {
2786 d->polishSize = true;
2787 polish();
2788 }
2789 d->extra.value().minimumPixelSize = size;
2791}
2792
2804{
2805 Q_D(const QQuickText);
2806 return d->minimumPointSize();
2807}
2808
2810{
2811 Q_D(QQuickText);
2812 if (d->minimumPointSize() == size)
2813 return;
2814
2815 if (d->fontSizeMode() != FixedSize && (widthValid() || heightValid())) {
2816 d->polishSize = true;
2817 polish();
2818 }
2819 d->extra.value().minimumPointSize = size;
2821}
2822
2827{
2828 Q_D(const QQuickText);
2829 if (d->richText && d->extra.isAllocated() && d->extra->doc)
2830 return d->extra->doc->resourcesLoading();
2831 return 0;
2832}
2833
2836{
2837 Q_D(QQuickText);
2838 if (d->updateOnComponentComplete) {
2839 if (d->richText) {
2840 d->updateDocumentText();
2841 } else {
2842 d->rightToLeftText = d->text.isRightToLeft();
2843 }
2844 d->determineHorizontalAlignment();
2845 }
2847 if (d->updateOnComponentComplete)
2848 d->updateLayout();
2849}
2850
2852{
2853 for (int i = 0; i < layout->lineCount(); ++i) {
2855 if (line.naturalTextRect().contains(mousePos)) {
2856 int charPos = line.xToCursor(mousePos.x(), QTextLine::CursorOnCharacter);
2857 const auto formats = layout->formats();
2858 for (const QTextLayout::FormatRange &formatRange : formats) {
2859 if (formatRange.format.isAnchor()
2860 && charPos >= formatRange.start
2861 && charPos < formatRange.start + formatRange.length) {
2862 return formatRange.format.anchorHref();
2863 }
2864 }
2865 break;
2866 }
2867 }
2868 return QString();
2869}
2870
2872{
2873 Q_Q(const QQuickText);
2874 QPointF translatedMousePos = mousePos;
2875 translatedMousePos.rx() -= q->leftPadding();
2876 translatedMousePos.ry() -= q->topPadding() + QQuickTextUtil::alignedY(layedOutTextRect.height() + lineHeightOffset(), availableHeight(), vAlign);
2877 if (styledText) {
2878 QString link = anchorAt(&layout, translatedMousePos);
2879 if (link.isEmpty() && elideLayout)
2880 link = anchorAt(elideLayout, translatedMousePos);
2881 return link;
2882 } else if (richText && extra.isAllocated() && extra->doc) {
2883 translatedMousePos.rx() -= QQuickTextUtil::alignedX(layedOutTextRect.width(), availableWidth(), q->effectiveHAlign());
2884 return extra->doc->documentLayout()->anchorAt(translatedMousePos);
2885 }
2886 return QString();
2887}
2888
2890{
2891 Q_Q(QQuickText);
2892 IS_SIGNAL_CONNECTED(q, QQuickText, linkActivated, (const QString &));
2893}
2894
2897{
2898 Q_D(QQuickText);
2899
2900 QString link;
2901 if (d->isLinkActivatedConnected())
2902 link = d->anchorAt(event->position());
2903
2904 if (link.isEmpty()) {
2905 event->setAccepted(false);
2906 } else {
2907 d->extra.value().activeLink = link;
2908 }
2909
2910 // ### may malfunction if two of the same links are clicked & dragged onto each other)
2911
2912 if (!event->isAccepted())
2914}
2915
2916
2919{
2920 Q_D(QQuickText);
2921
2922 // ### confirm the link, and send a signal out
2923
2924 QString link;
2925 if (d->isLinkActivatedConnected())
2926 link = d->anchorAt(event->position());
2927
2928 if (!link.isEmpty() && d->extra.isAllocated() && d->extra->activeLink == link)
2929 emit linkActivated(d->extra->activeLink);
2930 else
2931 event->setAccepted(false);
2932
2933 if (!event->isAccepted())
2935}
2936
2938{
2939 Q_Q(QQuickText);
2940 IS_SIGNAL_CONNECTED(q, QQuickText, linkHovered, (const QString &));
2941}
2942
2944{
2945 for (const QTextLayout::FormatRange &formatRange : layout->formats()) {
2946 if (formatRange.format.isAnchor()) {
2947 const int start = formatRange.start;
2948 const int len = formatRange.length;
2949 QTextLine line = layout->lineForTextPosition(start);
2950 QRectF r;
2951 r.setTop(line.y());
2952 r.setLeft(line.cursorToX(start, QTextLine::Leading));
2953 r.setHeight(line.height());
2954 r.setRight(line.cursorToX(start + len, QTextLine::Trailing));
2955 // ### anchorNames() is empty?! Not sure why this doesn't work
2956 // QString anchorName = formatRange.format.anchorNames().value(0); //### pick the first?
2957 // Therefore, we resort to QString::mid()
2958 QString anchorName = layout->text().mid(start, len);
2959 const QString anchorHref = formatRange.format.anchorHref();
2960 if (anchorName.isEmpty())
2961 anchorName = anchorHref;
2962 links->append( { anchorName, anchorHref, start, start + len, r.toRect()} );
2963 }
2964 }
2965}
2966
2968{
2971 return links;
2972}
2973
2974
2998{
2999 Q_D(const QQuickText);
3000 if (const_cast<QQuickTextPrivate *>(d)->isLinkHoveredConnected()) {
3001 if (d->extra.isAllocated())
3002 return d->extra->hoveredLink;
3003 } else {
3004#if QT_CONFIG(cursor)
3005 if (QQuickWindow *wnd = window()) {
3006 QPointF pos = QCursor::pos(wnd->screen()) - wnd->position() - mapToScene(QPointF(0, 0));
3007 return d->anchorAt(pos);
3008 }
3009#endif // cursor
3010 }
3011 return QString();
3012}
3013
3015{
3016 Q_Q(QQuickText);
3017 qCDebug(lcHoverTrace) << q;
3018 QString link;
3019 if (isLinkHoveredConnected()) {
3020 if (event->type() != QEvent::HoverLeave)
3021 link = anchorAt(event->position());
3022
3023 if ((!extra.isAllocated() && !link.isEmpty()) || (extra.isAllocated() && extra->hoveredLink != link)) {
3024 extra.value().hoveredLink = link;
3025 emit q->linkHovered(extra->hoveredLink);
3026 }
3027 }
3028 event->ignore();
3029}
3030
3032{
3033 Q_D(QQuickText);
3034 d->processHoverEvent(event);
3035}
3036
3038{
3039 Q_D(QQuickText);
3040 d->processHoverEvent(event);
3041}
3042
3044{
3045 Q_D(QQuickText);
3046 d->processHoverEvent(event);
3047}
3048
3050{
3051 Q_D(QQuickText);
3052 d->textHasChanged = true;
3053 d->updateLayout();
3054}
3055
3057{
3058 // If there's a lot of text, we may need QQuickText::updatePaintNode() to call
3059 // QQuickTextNode::addTextLayout() again to populate a different range of lines
3063 }
3065}
3066
3090{
3091 Q_D(const QQuickText);
3092 return d->renderTypeQuality();
3093}
3094
3095void QQuickText::setRenderTypeQuality(int renderTypeQuality)
3096{
3097 Q_D(QQuickText);
3098 if (renderTypeQuality == d->renderTypeQuality())
3099 return;
3100 d->extra.value().renderTypeQuality = renderTypeQuality;
3101
3102 if (isComponentComplete()) {
3104 update();
3105 }
3106
3107 emit renderTypeQualityChanged();
3108}
3109
3128{
3129 Q_D(const QQuickText);
3130 return d->renderType;
3131}
3132
3134{
3135 Q_D(QQuickText);
3136 if (d->renderType == renderType)
3137 return;
3138
3139 d->renderType = renderType;
3141
3142 if (isComponentComplete())
3143 d->updateLayout();
3144}
3145
3146#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
3147#if QT_DEPRECATED_SINCE(5, 15)
3154void QQuickText::doLayout()
3155{
3156 forceLayout();
3157}
3158
3159#endif
3160#endif
3167void QQuickText::forceLayout()
3168{
3169 Q_D(QQuickText);
3170 d->updateSize();
3171}
3172
3182QString QQuickText::linkAt(qreal x, qreal y) const
3183{
3184 Q_D(const QQuickText);
3185 return d->anchorAt(QPointF(x, y));
3186}
3187
3195{
3196 Q_D(QQuickText);
3197
3198 if (d->richText && d->extra.isAllocated() && d->extra->doc != nullptr) {
3199 QTextBlock block;
3200 for (block = d->extra->doc->firstBlock(); block.isValid(); block = block.next()) {
3201 if (block.layout() != nullptr && block.layout()->engine() != nullptr)
3202 block.layout()->engine()->resetFontEngineCache();
3203 }
3204 } else {
3205 if (d->layout.engine() != nullptr)
3206 d->layout.engine()->resetFontEngineCache();
3207 }
3208}
3209
3222{
3223 Q_D(const QQuickText);
3224 return d->padding();
3225}
3226
3228{
3229 Q_D(QQuickText);
3230 if (qFuzzyCompare(d->padding(), padding))
3231 return;
3232
3233 d->extra.value().padding = padding;
3234 d->updateSize();
3235 emit paddingChanged();
3236 if (!d->extra.isAllocated() || !d->extra->explicitTopPadding)
3237 emit topPaddingChanged();
3238 if (!d->extra.isAllocated() || !d->extra->explicitLeftPadding)
3239 emit leftPaddingChanged();
3240 if (!d->extra.isAllocated() || !d->extra->explicitRightPadding)
3241 emit rightPaddingChanged();
3242 if (!d->extra.isAllocated() || !d->extra->explicitBottomPadding)
3243 emit bottomPaddingChanged();
3244}
3245
3247{
3248 setPadding(0);
3249}
3250
3252{
3253 Q_D(const QQuickText);
3254 if (d->extra.isAllocated() && d->extra->explicitTopPadding)
3255 return d->extra->topPadding;
3256 return d->padding();
3257}
3258
3260{
3261 Q_D(QQuickText);
3262 d->setTopPadding(padding);
3263}
3264
3266{
3267 Q_D(QQuickText);
3268 d->setTopPadding(0, true);
3269}
3270
3272{
3273 Q_D(const QQuickText);
3274 if (d->extra.isAllocated() && d->extra->explicitLeftPadding)
3275 return d->extra->leftPadding;
3276 return d->padding();
3277}
3278
3280{
3281 Q_D(QQuickText);
3282 d->setLeftPadding(padding);
3283}
3284
3286{
3287 Q_D(QQuickText);
3288 d->setLeftPadding(0, true);
3289}
3290
3292{
3293 Q_D(const QQuickText);
3294 if (d->extra.isAllocated() && d->extra->explicitRightPadding)
3295 return d->extra->rightPadding;
3296 return d->padding();
3297}
3298
3300{
3301 Q_D(QQuickText);
3302 d->setRightPadding(padding);
3303}
3304
3306{
3307 Q_D(QQuickText);
3308 d->setRightPadding(0, true);
3309}
3310
3312{
3313 Q_D(const QQuickText);
3314 if (d->extra.isAllocated() && d->extra->explicitBottomPadding)
3315 return d->extra->bottomPadding;
3316 return d->padding();
3317}
3318
3320{
3321 Q_D(QQuickText);
3322 d->setBottomPadding(padding);
3323}
3324
3326{
3327 Q_D(QQuickText);
3328 d->setBottomPadding(0, true);
3329}
3330
3387{
3388 Q_D(const QQuickText);
3389
3390 QJSEngine *engine = qjsEngine(this);
3391 if (!engine) {
3392 qmlWarning(this) << "fontInfo: item has no JS engine";
3393 return QJSValue();
3394 }
3395
3397 value.setProperty(QStringLiteral("family"), d->fontInfo.family());
3398 value.setProperty(QStringLiteral("styleName"), d->fontInfo.styleName());
3399 value.setProperty(QStringLiteral("bold"), d->fontInfo.bold());
3400 value.setProperty(QStringLiteral("weight"), d->fontInfo.weight());
3401 value.setProperty(QStringLiteral("italic"), d->fontInfo.italic());
3402 value.setProperty(QStringLiteral("pointSize"), d->fontInfo.pointSizeF());
3403 value.setProperty(QStringLiteral("pixelSize"), d->fontInfo.pixelSize());
3404 return value;
3405}
3406
3420{
3421 Q_D(const QQuickText);
3422 return d->advance;
3423}
3424
3426
3427#include "moc_qquicktext_p.cpp"
\inmodule QtCore
Definition qchar.h:48
@ LineSeparator
Definition qchar.h:64
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
static QColor fromRgba(QRgb rgba) noexcept
Static convenience function that returns a QColor constructed from the given QRgb value rgba.
Definition qcolor.cpp:2385
static QPoint pos()
Returns the position of the cursor (hot spot) of the primary screen in global screen coordinates.
Definition qcursor.cpp:188
@ HoverLeave
Definition qcoreevent.h:176
\reentrant
Definition qfontinfo.h:14
qreal pointSizeF() const
Returns the point size of the matched window system font.
Definition qfont.cpp:2847
int pixelSize() const
Returns the pixel size of the matched window system font.
Definition qfont.cpp:2859
QString family() const
Returns the family name of the matched window system font.
Definition qfont.cpp:2808
bool italic() const
Returns the italic value of the matched window system font.
Definition qfont.cpp:2871
QString styleName() const
Definition qfont.cpp:2823
int weight() const
Returns the weight of the matched window system font.
Definition qfont.cpp:2917
\reentrant \inmodule QtGui
qreal height() const
Returns the height of the font.
qreal ascent() const
Returns the ascent of the font.
\reentrant
Definition qfont.h:20
int pixelSize() const
Returns the pixel size of the font if it was set with setPixelSize().
Definition qfont.cpp:1059
void setPixelSize(int)
Sets the font size to pixelSize pixels, with a maxiumum size of an unsigned 16-bit integer.
Definition qfont.cpp:1034
qreal pointSizeF() const
Returns the point size of the font.
Definition qfont.cpp:1019
@ NoAntialias
Definition qfont.h:45
@ PreferAntialias
Definition qfont.h:44
void setPointSizeF(qreal)
Sets the point size to pointSize.
Definition qfont.cpp:995
static QInputMethod * inputMethod()
returns the input method.
\inmodule QtGui
Definition qevent.h:245
Qt::LayoutDirection inputDirection
Current input direction.
The QJSEngine class provides an environment for evaluating JavaScript code.
Definition qjsengine.h:26
QJSValue newObject()
Creates a JavaScript object of class Object.
The QJSValue class acts as a container for Qt/JavaScript data types.
Definition qjsvalue.h:31
void setProperty(const QString &name, const QJSValue &value)
Sets the value of this QJSValue's property with the given name to the given value.
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
void append(parameter_type t)
Definition qlist.h:441
The QMatrix4x4 class represents a 4x4 transformation matrix in 3D space.
Definition qmatrix4x4.h:25
\inmodule QtGui
Definition qevent.h:195
\inmodule QtCore
Definition qobject.h:90
int height() const
Returns the height of the pixmap.
Definition qpixmap.cpp:484
int width() const
Returns the width of the pixmap.
Definition qpixmap.cpp:472
\inmodule QtCore\reentrant
Definition qpoint.h:214
constexpr qreal & ry() noexcept
Returns a reference to the y coordinate of this point.
Definition qpoint.h:358
constexpr qreal x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:333
constexpr qreal y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:338
constexpr qreal & rx() noexcept
Returns a reference to the x coordinate of this point.
Definition qpoint.h:353
The QQmlContext class defines a context within a QML engine.
Definition qqmlcontext.h:25
virtual bool transformChanged(QQuickItem *transformedItem)
virtual void implicitHeightChanged()
void dirty(DirtyType)
quint32 implicitAntialiasing
virtual void implicitWidthChanged()
QQuickAnchorLine baseline() const
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:64
QPointF mapToScene(const QPointF &point) const
Maps the given point in this item's coordinate system to the equivalent point within the scene's coor...
virtual void mouseReleaseEvent(QMouseEvent *event)
This event handler can be reimplemented in a subclass to receive mouse release events for an item.
void heightChanged()
void setFlag(Flag flag, bool enabled=true)
Enables the specified flag for this item if enabled is true; if enabled is false, the flag is disable...
virtual void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
void widthChanged()
void componentComplete() override
\reimp Derived classes should call the base class method before adding their own actions to perform a...
void setAcceptHoverEvents(bool enabled)
If enabled is true, this sets the item to accept hover events; otherwise, hover events are not accept...
void setAcceptedMouseButtons(Qt::MouseButtons buttons)
Sets the mouse buttons accepted by this item to buttons.
QSizeF size() const
QQuickWindow * window() const
Returns the window in which this item is rendered.
virtual void mousePressEvent(QMouseEvent *event)
This event handler can be reimplemented in a subclass to receive mouse press events for an item.
virtual void itemChange(ItemChange, const ItemChangeData &)
Called when change occurs for this item.
bool isComponentComplete() const
Returns true if construction of the QML component is complete; otherwise returns false.
bool heightValid() const
Returns whether the height property has been set explicitly.
bool antialiasing
\qmlproperty bool QtQuick::Item::antialiasing
Definition qquickitem.h:112
bool widthValid() const
Returns whether the width property has been set explicitly.
virtual QRectF clipRect() const
Returns the rectangular area within this item that is currently visible in \l viewportItem(),...
ItemChange
Used in conjunction with QQuickItem::itemChange() to notify the item about certain types of changes.
Definition qquickitem.h:143
@ ItemAntialiasingHasChanged
Definition qquickitem.h:152
@ ItemDevicePixelRatioHasChanged
Definition qquickitem.h:153
@ ItemObservesViewport
Definition qquickitem.h:137
void update()
Schedules a call to updatePaintNode() for this item.
void polish()
Schedules a polish event for this item.
static void parse(const QString &string, QTextLayout &layout, QList< QQuickStyledTextImgTag * > &imgTags, const QUrl &baseUrl, QQmlContext *context, bool preloadImages, bool *fontSizeModified)
void setWidth(qreal width)
void setFullLayoutTextLength(int length)
void setY(qreal y)
void setLine(QTextLine *line)
FINALbool isLast
void setLineOffset(int offset)
void setHeight(qreal height)
void setX(qreal x)
void addImage(const QRectF &rect, const QImage &image)
void setUseNativeRenderer(bool on)
void addTextLayout(const QPointF &position, QTextLayout *textLayout, const QColor &color=QColor(), QQuickText::TextStyle style=QQuickText::Normal, const QColor &styleColor=QColor(), const QColor &anchorColor=QColor(), const QColor &selectionColor=QColor(), const QColor &selectedTextColor=QColor(), int selectionStart=-1, int selectionEnd=-1, int lineStart=0, int lineCount=-1)
void setRenderTypeQuality(int renderTypeQuality)
void addTextDocument(const QPointF &position, QTextDocument *textDocument, const QColor &color=QColor(), QQuickText::TextStyle style=QQuickText::Normal, const QColor &styleColor=QColor(), const QColor &anchorColor=QColor(), const QColor &selectionColor=QColor(), const QColor &selectedTextColor=QColor(), int selectionStart=-1, int selectionEnd=-1)
QQuickText::LineHeightMode lineHeightMode() const
qreal getImplicitHeight() const override
QVector< LinkDesc > getLinks() const
int minimumPixelSize() const
qreal padding() const
QLazilyAllocated< ExtraData > extra
qreal availableHeight() const
void setRightPadding(qreal value, bool reset=false)
void elideFormats(int start, int length, int offset, QVector< QTextLayout::FormatRange > *elidedFormats)
bool isLinkActivatedConnected()
bool transformChanged(QQuickItem *transformedItem) override
int minimumPointSize() const
void processHoverEvent(QHoverEvent *event)
int maximumLineCount() const
void ensureDoc()
Ensures the QQuickTextPrivate::doc variable is set to a valid text document.
QQuickText::RenderType renderType
qreal getImplicitWidth() const override
QQuickText::TextElideMode elideMode
QQuickText::WrapMode wrapMode
void setBottomPadding(qreal value, bool reset=false)
bool setHAlign(QQuickText::HAlignment, bool forceAlign=false)
qreal availableWidth() const
int lineHeightOffset() const
Returns the y offset when aligning text with a non-1.0 lineHeight.
QQuickTextLine * textLine
void setTopPadding(qreal value, bool reset=false)
void mirrorChange() override
bool determineHorizontalAlignment()
QQuickText::VAlignment vAlign
static const int largeTextSizeThreshold
bool isLineLaidOutConnected()
static const QChar elideChar
QString elidedText(qreal lineWidth, const QTextLine &line, const QTextLine *nextLine=nullptr) const
void setLeftPadding(qreal value, bool reset=false)
bool isLinkHoveredConnected()
QQuickText::FontSizeMode fontSizeMode() const
void updateBaseline(qreal baseline, qreal dy)
void signalSizeChange(const QSizeF &previousSize)
void setLineGeometry(QTextLine &line, qreal lineWidth, qreal &height)
void setupCustomLineGeometry(QTextLine &line, qreal &height, int fullLayoutTextLength, int lineOffset=0)
qreal lineHeight() const
QStringList links() const
QTextLayout * elideLayout
QQuickText::HAlignment hAlign
static QString anchorAt(const QTextLayout *layout, const QPointF &mousePos)
QRectF setupTextLayout(qreal *const baseline)
Lays out the QQuickTextPrivate::layout QTextLayout in the constraints of the QQuickText.
static qreal alignedX(qreal textWidth, qreal itemWidth, int alignment)
static qreal alignedY(qreal textHeight, qreal itemHeight, int alignment)
void resetPadding()
QQuickText(QQuickItem *parent=nullptr)
\qmltype Text \instantiates QQuickText \inqmlmodule QtQuick\inherits Item
void verticalAlignmentChanged(QQuickText::VAlignment alignment)
void minimumPointSizeChanged()
void resetTopPadding()
void setBaseUrl(const QUrl &url)
void setRenderType(RenderType renderType)
FINALQJSValue fontInfo
void invalidate() override
void updatePolish() override
This function should perform any layout as required for this item.
QRectF boundingRect() const override
Returns the extents of the text after layout.
void setPadding(qreal padding)
int resourcesLoading() const
Returns the number of resources (images) that are being loaded asynchronously.
FINALqreal bottomPadding
FINALqreal topPadding
int minimumPixelSize
TextStyle style
void minimumPixelSizeChanged()
void setText(const QString &)
void setStyle(TextStyle style)
void setFont(const QFont &font)
void wrapModeChanged()
HAlignment effectiveHAlign() const
void textChanged(const QString &text)
void elideModeChanged(QQuickText::TextElideMode mode)
void setLeftPadding(qreal padding)
QRectF clipRect() const override
Returns a rectangular area slightly larger than what is currently visible in \l viewportItem(); other...
void setMinimumPixelSize(int size)
FontSizeMode fontSizeMode
qreal lineHeight
void setMaximumLineCount(int lines)
void lineHeightModeChanged(LineHeightMode mode)
TextFormat textFormat
void renderTypeChanged()
QColor linkColor
void mouseReleaseEvent(QMouseEvent *event) override
HAlignment hAlign() const
\qmlproperty enumeration QtQuick::Text::horizontalAlignment \qmlproperty enumeration QtQuick::Text::v...
void resetBaseUrl()
void fontSizeModeChanged()
void fontChanged(const QFont &font)
void setElideMode(TextElideMode)
WrapMode wrapMode
VAlignment vAlign() const
void setLineHeightMode(LineHeightMode)
void setLinkColor(const QColor &color)
void componentComplete() override
void setLineHeight(qreal lineHeight)
void setVAlign(VAlignment align)
void truncatedChanged()
void hoverLeaveEvent(QHoverEvent *event) override
This event handler can be reimplemented in a subclass to receive hover-leave events for an item.
void setHAlign(HAlignment align)
void mousePressEvent(QMouseEvent *event) override
void setFontSizeMode(FontSizeMode mode)
void styleChanged(QQuickText::TextStyle style)
void styleColorChanged()
void setTopPadding(qreal padding)
void baseUrlChanged()
TextElideMode elide
void linkColorChanged()
FINALqreal padding
void textFormatChanged(QQuickText::TextFormat textFormat)
void setTextFormat(TextFormat format)
void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override
void resetRightPadding()
TextElideMode elideMode() const
\qmlproperty enumeration QtQuick::Text::elide
void linkActivated(const QString &link)
FINALint renderTypeQuality
int minimumPointSize
QSGNode * updatePaintNode(QSGNode *, UpdatePaintNodeData *) override
Called on the render thread when it is time to sync the state of the item with the scene graph.
qreal contentHeight
void maximumLineCountChanged()
void setColor(const QColor &c)
void lineHeightChanged(qreal lineHeight)
void hoverMoveEvent(QHoverEvent *event) override
This event handler can be reimplemented in a subclass to receive hover-move events for an item.
void setRightPadding(qreal padding)
LineHeightMode lineHeightMode
FINALqreal rightPadding
QString hoveredLink
void setWrapMode(WrapMode w)
void invalidateFontCaches()
QString text
void resetHAlign()
void setBottomPadding(qreal padding)
QColor styleColor
void setMinimumPointSize(int size)
FINALqreal leftPadding
void setRenderTypeQuality(int renderTypeQuality)
void itemChange(ItemChange change, const ItemChangeData &value) override
Called when change occurs for this item.
QColor color
void setStyleColor(const QColor &c)
int maximumLineCount
void resetMaximumLineCount()
void resetLeftPadding()
void colorChanged()
RenderType renderType
qreal contentWidth
~QQuickText() override
void hoverEnterEvent(QHoverEvent *event) override
This event handler can be reimplemented in a subclass to receive hover-enter events for an item.
FINALQSizeF advance
void resetBottomPadding()
\qmltype Window \instantiates QQuickWindow \inqmlmodule QtQuick
\inmodule QtCore\reentrant
Definition qrect.h:483
constexpr qreal height() const noexcept
Returns the height of the rectangle.
Definition qrect.h:718
constexpr qreal width() const noexcept
Returns the width of the rectangle.
Definition qrect.h:715
constexpr void setHeight(qreal h) noexcept
Sets the height of the rectangle to the given finite height.
Definition qrect.h:807
constexpr void moveTop(qreal pos) noexcept
Moves the rectangle vertically, leaving the rectangle's top line at the given finite y coordinate.
Definition qrect.h:691
QRectF united(const QRectF &other) const noexcept
Definition qrect.h:838
\inmodule QtCore\reentrant
Definition qrect.h:30
constexpr QSize size() const noexcept
Returns the size of the rectangle.
Definition qrect.h:241
\group qtquick-scenegraph-nodes \title Qt Quick Scene Graph Node classes
Definition qsgnode.h:37
void setMatrix(const QMatrix4x4 &matrix)
Sets this transform node's matrix to matrix.
Definition qsgnode.cpp:1160
\inmodule QtCore
Definition qsize.h:207
constexpr qreal width() const noexcept
Returns the width.
Definition qsize.h:321
constexpr qreal height() const noexcept
Returns the height.
Definition qsize.h:324
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
QString & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3794
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
QString mid(qsizetype position, qsizetype n=-1) const
Returns a string that contains n characters of this string, starting at the specified position index.
Definition qstring.cpp:5204
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1079
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
bool contains(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.h:1217
static QString static QString qsizetype indexOf(QChar c, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4420
void setLineHeight(qreal height, int heightType)
\reentrant
int lineCount() const
bool isValid() const
Returns true if this text block is valid; otherwise returns false.
QTextBlock next() const
Returns the text block in the document after this block, or an empty text block if this is the last o...
QTextLayout * layout() const
Returns the QTextLayout that is used to lay out and display the block's contents.
QTextBlock previous() const
Returns the text block in the document before this block, or an empty text block if this is the first...
\reentrant \inmodule QtGui
Definition qtextcursor.h:30
QTextOption option
void resetFontEngineCache()
QString elidedText(Qt::TextElideMode mode, QFixed width, int flags=0, int from=0, int count=-1) const
\reentrant
Definition qtextlayout.h:70
const QTextOption & textOption() const
Returns the current text option used to control the layout process.
void setFont(const QFont &f)
Sets the layout's font to the given font.
QTextLine createLine()
Returns a new text line to be laid out if there is text to be inserted into the layout; otherwise ret...
void beginLayout()
Begins the layout process.
QTextEngine * engine() const
void setCacheEnabled(bool enable)
Enables caching of the complete layout information if enable is true; otherwise disables layout cachi...
void setFormats(const QList< FormatRange > &overrides)
void setText(const QString &string)
Sets the layout's text to the given string.
QList< FormatRange > formats() const
QString text() const
Returns the layout's text.
void clearFormats()
int lineCount() const
Returns the number of lines in this text layout.
qreal maximumWidth() const
The maximum width the layout could expand to; this is essentially the width of the entire text.
QTextLine lineAt(int i) const
Returns the {i}-th line of text in this text layout.
void setTextOption(const QTextOption &option)
Sets the text option structure that controls the layout process to the given option.
QFont font() const
Returns the current font that is used for the layout, or a default font if none is set.
void endLayout()
Ends the layout process.
void clearLayout()
QPointF position() const
\reentrant
int textStart() const
Returns the start of the line from the beginning of the string passed to the QTextLayout.
QRectF naturalTextRect() const
Returns the rectangle covered by the line.
qreal height() const
Returns the line's height.
qreal naturalTextWidth() const
Returns the width of the line that is occupied by text.
qreal y() const
Returns the line's y position.
qreal width() const
Returns the line's width as specified by the layout() function.
void setPosition(const QPointF &pos)
Moves the line to position pos.
@ CursorOnCharacter
bool isValid() const
Returns true if this text line is valid; otherwise returns false.
void setLineWidth(qreal width)
Lays out the line with the given width.
qreal ascent() const
Returns the line's ascent.
int lineNumber() const
Returns the position of the line in the text engine.
qreal horizontalAdvance() const
qreal x() const
Returns the line's x position.
int textLength() const
Returns the length of the text in the line.
\reentrant
Definition qtextoption.h:18
WrapMode wrapMode() const
Returns the text wrap mode defined by the option.
Definition qtextoption.h:68
Qt::Alignment alignment() const
Returns the text alignment defined by the option.
Definition qtextoption.h:55
void setWrapMode(WrapMode wrap)
Sets the option's text wrap mode to the given mode.
Definition qtextoption.h:67
void setUseDesignMetrics(bool b)
If enable is true then the layout will use design metrics; otherwise it will use the metrics of the p...
Definition qtextoption.h:91
void setAlignment(Qt::Alignment alignment)
Sets the option's text alignment to the specified alignment.
bool useDesignMetrics() const
Returns true if the layout uses design rather than device metrics; otherwise returns false.
Definition qtextoption.h:92
WrapMode
This enum describes how text is wrapped in a document.
Definition qtextoption.h:60
\inmodule QtCore
Definition qurl.h:94
QCursor cursor
qDeleteAll(list.begin(), list.end())
QSet< QString >::iterator it
rect
[4]
QPixmap pix
uint alignment
QRect textRect
EGLint EGLint * formats
Combined button and popup list for selecting options.
@ LeftButton
Definition qnamespace.h:57
@ NoButton
Definition qnamespace.h:56
@ RightToLeft
Q_GUI_EXPORT bool mightBeRichText(const QString &)
Returns true if the string text is likely to be rich text; otherwise returns false.
TextElideMode
Definition qnamespace.h:187
Definition image.cpp:4
static void * context
#define rgb(r, g, b)
Definition qcolor.cpp:124
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition qfloat16.h:287
bool qFuzzyIsNull(qfloat16 f) noexcept
Definition qfloat16.h:303
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:281
Q_QML_EXPORT QJSEngine * qjsEngine(const QObject *)
#define qCDebug(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
int qCeil(T v)
Definition qmath.h:36
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
#define SLOT(a)
Definition qobjectdefs.h:51
#define SIGNAL(a)
Definition qobjectdefs.h:52
GLint GLint GLint GLint GLint x
[0]
GLenum mode
GLint GLsizei GLsizei height
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLboolean r
[2]
GLuint GLuint end
GLenum GLuint GLenum GLsizei length
GLsizei GLenum const void GLuint GLsizei GLfloat * metrics
GLint GLsizei width
GLenum type
GLbitfield flags
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint start
GLenum GLuint GLintptr offset
GLfloat n
GLint GLsizei GLsizei GLenum format
GLint y
struct _cl_event * event
GLboolean reset
GLint void * img
Definition qopenglext.h:233
GLenum GLsizei len
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLuint GLenum option
QQmlContext * qmlContext(const QObject *obj)
Definition qqml.cpp:71
#define qmlobject_connect(Sender, SenderType, Signal, Receiver, ReceiverType, Method)
Connect Signal of Sender to Method of Receiver.
#define IS_SIGNAL_CONNECTED(Sender, SenderType, Name, Arguments)
Q_QML_EXPORT QQmlInfo qmlWarning(const QObject *me)
#define QQUICKTEXT_LARGETEXT_THRESHOLD
static void getLinks_helper(const QTextLayout *layout, QVector< QQuickTextPrivate::LinkDesc > *links)
QT_BEGIN_NAMESPACE typedef unsigned int QRgb
Definition qrgb.h:13
#define QStringLiteral(str)
#define emit
#define Q_UNUSED(x)
double qreal
Definition qtypes.h:92
QUrl url("example.com")
[constructor-url-reference]
QObject::connect nullptr
QVBoxLayout * layout
QJSEngine engine
[0]
static constexpr QFixed fromReal(qreal r)
Definition qfixed_p.h:35
\inmodule QtCore \reentrant
Definition qchar.h:17
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent
\inmodule QtQuick
Definition qquickitem.h:158