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"
16#include "qquicktextdocument_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
37QT_BEGIN_NAMESPACE
38
39Q_DECLARE_LOGGING_CATEGORY(lcHoverTrace)
40
41const QChar QQuickTextPrivate::elideChar = QChar(0x2026);
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
47const int QQuickTextPrivate::largeTextSizeThreshold = QQUICKTEXT_LARGETEXT_THRESHOLD;
48
49QQuickTextPrivate::QQuickTextPrivate()
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{
66 implicitAntialiasing = true;
67}
68
69QQuickTextPrivate::ExtraData::ExtraData()
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
92void QQuickTextPrivate::init()
93{
94 Q_Q(QQuickText);
95 q->setAcceptedMouseButtons(Qt::LeftButton);
96 q->setFlag(flag: QQuickItem::ItemHasContents);
97 q->setFlag(flag: QQuickItem::ItemObservesViewport); // default until size is known
98}
99
100QQuickTextPrivate::~QQuickTextPrivate()
101{
102 delete elideLayout;
103 delete textLine; textLine = nullptr;
104
105 if (extra.isAllocated()) {
106 qDeleteAll(c: extra->imgTags);
107 extra->imgTags.clear();
108 }
109}
110
111qreal QQuickTextPrivate::getImplicitWidth() const
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
123qreal QQuickTextPrivate::getImplicitHeight() const
124{
125 if (!requireImplicitSize) {
126 QQuickTextPrivate *me = const_cast<QQuickTextPrivate*>(this);
127 me->requireImplicitSize = true;
128 me->updateSize();
129 }
130 return implicitHeight;
131}
132
133qreal QQuickTextPrivate::availableWidth() const
134{
135 Q_Q(const QQuickText);
136 return q->width() - q->leftPadding() - q->rightPadding();
137}
138
139qreal QQuickTextPrivate::availableHeight() const
140{
141 Q_Q(const QQuickText);
142 return q->height() - q->topPadding() - q->bottomPadding();
143}
144
145void QQuickTextPrivate::setTopPadding(qreal value, bool reset)
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(p1: oldPadding, p2: value)) || (reset && !qFuzzyCompare(p1: oldPadding, p2: padding()))) {
154 updateSize();
155 emit q->topPaddingChanged();
156 }
157}
158
159void QQuickTextPrivate::setLeftPadding(qreal value, bool reset)
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(p1: oldPadding, p2: value)) || (reset && !qFuzzyCompare(p1: oldPadding, p2: padding()))) {
168 updateSize();
169 emit q->leftPaddingChanged();
170 }
171}
172
173void QQuickTextPrivate::setRightPadding(qreal value, bool reset)
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(p1: oldPadding, p2: value)) || (reset && !qFuzzyCompare(p1: oldPadding, p2: padding()))) {
182 updateSize();
183 emit q->rightPaddingChanged();
184 }
185}
186
187void QQuickTextPrivate::setBottomPadding(qreal value, bool reset)
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(p1: oldPadding, p2: value)) || (reset && !qFuzzyCompare(p1: oldPadding, p2: padding()))) {
196 updateSize();
197 emit q->bottomPaddingChanged();
198 }
199}
200
201/*!
202 \qmlproperty bool QtQuick::Text::antialiasing
203
204 Used to decide if the Text should use antialiasing or not. Only Text
205 with renderType of Text.NativeRendering can disable antialiasing.
206
207 The default is true.
208*/
209
210void QQuickText::q_updateLayout()
211{
212 Q_D(QQuickText);
213 d->updateLayout();
214}
215
216void QQuickTextPrivate::updateLayout()
217{
218 Q_Q(QQuickText);
219 if (!q->isComponentComplete()) {
220 updateOnComponentComplete = true;
221 return;
222 }
223 updateOnComponentComplete = false;
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()) {
234 layout.setFont(font);
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(string: text, layout, imgTags&: someImgTags, baseUrl: q->baseUrl(), context: qmlContext(q), preloadImages: !maximumLineCountValid, fontSizeModified: &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(c: QLatin1Char('\x9c'));
246 if (multilengthEos != -1)
247 tmp = tmp.mid(position: 0, n: multilengthEos);
248 tmp.replace(before: QLatin1Char('\n'), after: QChar::LineSeparator);
249 layout.setText(tmp);
250 }
251 textHasChanged = false;
252 }
253 } else if (extra.isAllocated() && extra->lineHeightValid) {
254 ensureDoc();
255 QTextBlockFormat::LineHeightTypes type;
256 type = lineHeightMode() == QQuickText::FixedHeight ? QTextBlockFormat::FixedHeight : QTextBlockFormat::ProportionalHeight;
257 QTextBlockFormat blockFormat;
258 blockFormat.setLineHeight(height: (lineHeightMode() == QQuickText::FixedHeight ? lineHeight() : lineHeight() * 100), heightType: type);
259 for (QTextBlock it = extra->doc->begin(); it != extra->doc->end(); it = it.next()) {
260 QTextCursor cursor(it);
261 cursor.mergeBlockFormat(modifier: 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(t&: 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 {
299 d->updateType = QQuickTextPrivate::UpdatePaintNode;
300 update();
301 }
302 }
303}
304
305void QQuickTextPrivate::updateBaseline(qreal baseline, qreal dy)
306{
307 Q_Q(QQuickText);
308
309 qreal yoff = 0;
310
311 if (q->heightValid()) {
312 if (vAlign == QQuickText::AlignBottom)
313 yoff = dy;
314 else if (vAlign == QQuickText::AlignVCenter)
315 yoff = dy/2;
316 }
317
318 q->setBaselineOffset(baseline + yoff + q->topPadding());
319}
320
321void QQuickTextPrivate::signalSizeChange(const QSizeF &previousSize)
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(contentWidth: contentSize.width());
330 if (contentSize.height() != previousSize.height())
331 emit q->contentHeightChanged(contentHeight: contentSize.height());
332 }
333}
334
335void QQuickTextPrivate::updateSize()
336{
337 Q_Q(QQuickText);
338
339 if (!q->isComponentComplete()) {
340 updateOnComponentComplete = true;
341 return;
342 }
343
344 if (!requireImplicitSize) {
345 implicitWidthChanged();
346 implicitHeightChanged();
347 // if the implicitWidth is used, then updateSize() has already been called (recursively)
348 if (requireImplicitSize)
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
357 if (text.isEmpty() && !isLineLaidOutConnected() && fontSizeMode() == QQuickText::FixedSize) {
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.
362 QFontMetricsF fm(font);
363 qreal fontHeight = qCeil(v: fm.height()); // QScriptLine and therefore QTextLine rounds up
364 if (!richText) { // line height, so we will as well.
365 fontHeight = lineHeightMode() == QQuickText::FixedHeight
366 ? lineHeight()
367 : fontHeight * lineHeight();
368 }
369 updateBaseline(baseline: fm.ascent(), dy: q->height() - fontHeight - vPadding);
370 q->setImplicitSize(hPadding, fontHeight + qMax(a: lineHeightOffset(), b: 0) + vPadding);
371 layedOutTextRect = QRectF(0, 0, 0, fontHeight);
372 advance = QSizeF();
373 signalSizeChange(previousSize);
374 lineCount = 1;
375 emit q->lineCountChanged();
376 updateType = UpdatePaintNode;
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;
386 QRectF textRect = setupTextLayout(&baseline);
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
391 layedOutTextRect = textRect;
392 size = textRect.size();
393 updateBaseline(baseline, dy: 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 }
406 QTextOption option;
407 option.setAlignment((Qt::Alignment)int(horizontalAlignment | vAlign));
408 option.setWrapMode(QTextOption::WrapMode(wrapMode));
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 }
420 if (internalWidthUpdate)
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
433 QFontMetricsF fm(font);
434 updateBaseline(baseline: fm.ascent(), dy: 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(a: lineHeightOffset(), b: 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(p1: q->width(), p2: oldWidth) && !updateSizeRecursionGuard) {
452 updateSizeRecursionGuard = true;
453 updateSize();
454 updateSizeRecursionGuard = false;
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(i: 0);
469 QTextLine lastLine = lastBlock.layout()->lineAt(i: 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);
479 updateType = UpdatePaintNode;
480 q->update();
481}
482
483QQuickTextLine::QQuickTextLine()
484 : QObject(), m_line(nullptr), m_height(0), m_lineOffset(0)
485{
486}
487
488void QQuickTextLine::setLine(QTextLine *line)
489{
490 m_line = line;
491}
492
493void QQuickTextLine::setLineOffset(int offset)
494{
495 m_lineOffset = offset;
496}
497
498void QQuickTextLine::setFullLayoutTextLength(int length)
499{
500 m_fullLayoutTextLength = length;
501}
502
503int QQuickTextLine::number() const
504{
505 if (m_line)
506 return m_line->lineNumber() + m_lineOffset;
507 return 0;
508}
509
510qreal QQuickTextLine::implicitWidth() const
511{
512 if (m_line)
513 return m_line->naturalTextWidth();
514 return 0;
515}
516
517bool QQuickTextLine::isLast() const
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
528qreal QQuickTextLine::width() const
529{
530 if (m_line)
531 return m_line->width();
532 return 0;
533}
534
535void QQuickTextLine::setWidth(qreal width)
536{
537 if (m_line)
538 m_line->setLineWidth(width);
539}
540
541qreal QQuickTextLine::height() const
542{
543 if (m_height)
544 return m_height;
545 if (m_line)
546 return m_line->height();
547 return 0;
548}
549
550void QQuickTextLine::setHeight(qreal height)
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
557qreal QQuickTextLine::x() const
558{
559 if (m_line)
560 return m_line->x();
561 return 0;
562}
563
564void QQuickTextLine::setX(qreal x)
565{
566 if (m_line)
567 m_line->setPosition(QPointF(x, m_line->y()));
568}
569
570qreal QQuickTextLine::y() const
571{
572 if (m_line)
573 return m_line->y();
574 return 0;
575}
576
577void QQuickTextLine::setY(qreal y)
578{
579 if (m_line)
580 m_line->setPosition(QPointF(m_line->x(), y));
581}
582
583bool QQuickTextPrivate::isLineLaidOutConnected()
584{
585 Q_Q(QQuickText);
586 IS_SIGNAL_CONNECTED(q, QQuickText, lineLaidOut, (QQuickTextLine *));
587}
588
589void QQuickTextPrivate::setupCustomLineGeometry(QTextLine &line, qreal &height, int fullLayoutTextLength, int lineOffset)
590{
591 Q_Q(QQuickText);
592
593 if (!textLine)
594 textLine = new QQuickTextLine;
595 textLine->setFullLayoutTextLength(fullLayoutTextLength);
596 textLine->setLine(&line);
597 textLine->setY(height);
598 textLine->setHeight(0);
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))
604 textLine->setWidth(availableWidth());
605 else
606 textLine->setWidth(INT_MAX);
607 if (lineHeight() != 1.0)
608 textLine->setHeight((lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : line.height() * lineHeight());
609
610 emit q->lineLaidOut(line: textLine);
611
612 height += textLine->height();
613}
614
615void QQuickTextPrivate::elideFormats(
616 const int start, const int length, int offset, QVector<QTextLayout::FormatRange> *elidedFormats)
617{
618 const int end = start + length;
619 const QVector<QTextLayout::FormatRange> formats = layout.formats();
620 for (int i = 0; i < formats.size(); ++i) {
621 QTextLayout::FormatRange format = formats.at(i);
622 const int formatLength = qMin(a: format.start + format.length, b: end) - qMax(a: format.start, b: start);
623 if (formatLength > 0) {
624 format.start = qMax(a: offset, b: format.start - start + offset);
625 format.length = formatLength;
626 elidedFormats->append(t: 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(
635 mode: Qt::TextElideMode(elideMode),
636 width: QFixed::fromReal(r: lineWidth),
637 flags: 0,
638 from: line.textStart(),
639 count: line.textLength() + nextLine->textLength());
640 } else {
641 QString elideText = layout.text().mid(position: line.textStart(), n: 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.
647 QFontMetricsF metrics(layout.font());
648 if (metrics.horizontalAdvance(elideChar) + line.naturalTextWidth() >= lineWidth)
649 elideText = metrics.elidedText(text: elideText, mode: Qt::TextElideMode(elideMode), width: lineWidth);
650 }
651 return elideText;
652 }
653}
654
655void QQuickTextPrivate::clearFormats()
656{
657 layout.clearFormats();
658 if (elideLayout)
659 elideLayout->clearFormats();
660}
661
662/*!
663 Lays out the QQuickTextPrivate::layout QTextLayout in the constraints of the QQuickText.
664
665 Returns the size of the final text. This can be used to position the text vertically (the text is
666 already absolutely positioned horizontally).
667*/
668
669QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline)
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
678 if ((!requireImplicitSize || (implicitWidthValid && implicitHeightValid))
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(flag: QQuickItem::ItemObservesViewport, enabled: false);
692 emit q->lineCountChanged();
693 }
694
695 if (qFuzzyIsNull(d: q->width())) {
696 layout.setText(QString());
697 textHasChanged = true;
698 }
699
700 QFontMetricsF fm(font);
701 qreal height = (lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : qCeil(v: fm.height()) * lineHeight();
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();
709 layout.setCacheEnabled(true);
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()));
715 textOption.setWrapMode(QTextOption::WrapMode(wrapMode));
716 textOption.setUseDesignMetrics(shouldUseDesignMetrics);
717 layout.setTextOption(textOption);
718 }
719 if (layout.font() != font)
720 layout.setFont(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())
741 : minimumPointSize();
742 qreal largeFont = pixelSize ? font.pixelSize() : font.pointSizeF();
743 qreal smallFont = fontSizeMode() != QQuickText::FixedSize
744 ? qMin<qreal>(a: minimumSize, b: 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
781 layout.beginLayout();
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;
795 QTextLine line;
796 for (visibleCount = 1; ; ++visibleCount) {
797 line = layout.createLine();
798
799 if (noBreakLastLine && visibleCount == maxLineCount)
800 layout.engine()->option.setWrapMode(QTextOption::WrapAnywhere);
801 if (customLayout) {
802 setupCustomLineGeometry(line, height&: naturalHeight, fullLayoutTextLength: layoutText.size());
803 } else {
804 setLineGeometry(line, lineWidth, height&: naturalHeight);
805 }
806 if (noBreakLastLine && visibleCount == maxLineCount)
807 layout.engine()->option.setWrapMode(QTextOption::WrapMode(wrapMode));
808
809 unelidedRect = br.united(r: 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(i: visibleCount - 1);
825 elideText = layoutText.at(i: line.textStart() - 1) != QChar::LineSeparator
826 ? elidedText(lineWidth: line.width(), line: previousLine, nextLine: &line)
827 : elidedText(lineWidth: line.width(), line: 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(
846 mode: Qt::TextElideMode(elideMode),
847 width: QFixed::fromReal(r: line.width()),
848 flags: 0,
849 from: line.textStart(),
850 count: 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(i: 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(lineWidth: line.width(), line, nextLine: &nextLine)
878 : elidedText(lineWidth: 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) {
904 line = layout.createLine();
905 if (!line.isValid())
906 break;
907 if (layoutText.at(i: line.textStart() - 1) == QChar::LineSeparator)
908 ++unwrappedLineCount;
909 setLineGeometry(line, lineWidth, height&: 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(i: eol) != QChar::LineSeparator)
918 line = layout.createLine();
919 for (; line.isValid() && unwrappedLineCount <= maxLineCount; ++unwrappedLineCount)
920 line = layout.createLine();
921 }
922 layout.endLayout();
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(a: lineHeightOffset(), b: 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(p1: lineWidth, p2: oldWidth) || (widthExceeded && lineWidth > oldWidth))
954 && (singlelineElide || multilineElide || canWrap || horizontalFit
955 || q->effectiveHAlign() != QQuickText::AlignLeft)) {
956 widthChanged = true;
957 widthExceeded = lineWidth >= qMin(a: oldWidth, b: 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(a: oldHeight, b: 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) {
982 line = layout.createLine();
983 if (!line.isValid())
984 break;
985 setLineGeometry(line, lineWidth, height&: naturalHeight);
986 }
987 }
988 layout.endLayout();
989
990 bool wasInLayout = internalWidthUpdate;
991 internalWidthUpdate = true;
992 q->setImplicitHeight(naturalHeight + qMax(a: lineHeightOffset(), b: 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(a: oldHeight, b: 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(c: QLatin1Char('\x9c'), from: start);
1020 layoutText = text.mid(position: start, n: eos != -1 ? eos - start : -1);
1021 layoutText.replace(before: QLatin1Char('\n'), after: QChar::LineSeparator);
1022 layout.setText(layoutText);
1023 textHasChanged = true;
1024 continue;
1025 }
1026
1027 br.moveTop(pos: 0);
1028
1029 // Find the advance of the text layout
1030 if (layout.lineCount() > 0) {
1031 QTextLine firstLine = layout.lineAt(i: 0);
1032 QTextLine lastLine = layout.lineAt(i: 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(p1: smallFont, p2: 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(p1: fontInfo.pointSizeF(), p2: 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
1102 assignedFont = QFontInfo(font).family();
1103
1104 if (elide) {
1105 if (!elideLayout) {
1106 elideLayout = new QTextLayout;
1107 elideLayout->setCacheEnabled(true);
1108 }
1109 QTextEngine *engine = layout.engine();
1110 if (engine && engine->hasFormats()) {
1111 QVector<QTextLayout::FormatRange> formats;
1112 switch (elideMode) {
1113 case QQuickText::ElideRight:
1114 elideFormats(start: elideStart, length: elideText.size() - 1, offset: 0, elidedFormats: &formats);
1115 break;
1116 case QQuickText::ElideLeft:
1117 elideFormats(start: elideEnd - elideText.size() + 1, length: elideText.size() - 1, offset: 1, elidedFormats: &formats);
1118 break;
1119 case QQuickText::ElideMiddle: {
1120 const int index = elideText.indexOf(c: elideChar);
1121 if (index != -1) {
1122 elideFormats(start: elideStart, length: index, offset: 0, elidedFormats: &formats);
1123 elideFormats(
1124 start: elideEnd - elideText.size() + index + 1,
1125 length: elideText.size() - index - 1,
1126 offset: index + 1,
1127 elidedFormats: &formats);
1128 }
1129 break;
1130 }
1131 default:
1132 break;
1133 }
1134 elideLayout->setFormats(formats);
1135 }
1136
1137 elideLayout->setFont(layout.font());
1138 elideLayout->setTextOption(layout.textOption());
1139 elideLayout->setText(elideText);
1140 elideLayout->beginLayout();
1141
1142 QTextLine elidedLine = elideLayout->createLine();
1143 elidedLine.setPosition(QPointF(0, height));
1144 if (customLayout) {
1145 setupCustomLineGeometry(line&: elidedLine, height, fullLayoutTextLength: elideText.size(), lineOffset: visibleCount - 1);
1146 } else {
1147 setLineGeometry(line&: elidedLine, lineWidth, height);
1148 }
1149 elideLayout->endLayout();
1150
1151 br = br.united(r: elidedLine.naturalTextRect());
1152
1153 if (visibleCount == 1)
1154 layout.clearLayout();
1155 } else {
1156 delete elideLayout;
1157 elideLayout = nullptr;
1158 }
1159
1160 QTextLine firstLine = visibleCount == 1 && elideLayout
1161 ? elideLayout->lineAt(i: 0)
1162 : layout.lineAt(i: 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
1181void QQuickTextPrivate::setLineGeometry(QTextLine &line, qreal lineWidth, qreal &height)
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));
1188 height += (lineHeightMode() == QQuickText::FixedHeight) ? lineHeight() : line.height() * lineHeight();
1189 return;
1190 }
1191
1192 qreal textTop = 0;
1193 qreal textHeight = line.height();
1194 qreal totalLineHeight = textHeight;
1195
1196 QList<QQuickStyledTextImgTag *> imagesInLine;
1197
1198 if (extra.isAllocated()) {
1199 for (QQuickStyledTextImgTag *image : std::as_const(t&: 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(relative: 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(me: 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(a: textTop, b: qAbs(t: image->pos.y()));
1233 }
1234 }
1235 }
1236
1237 for (QQuickStyledTextImgTag *image : std::as_const(t&: imagesInLine)) {
1238 totalLineHeight = qMax(a: totalLineHeight, b: textTop + image->pos.y() + image->size.height());
1239 const int leadX = line.cursorToX(cursorPos: image->position);
1240 const int trailX = line.cursorToX(cursorPos: image->position, edge: 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
1251/*!
1252 Returns the y offset when aligning text with a non-1.0 lineHeight
1253*/
1254int QQuickTextPrivate::lineHeightOffset() const
1255{
1256 QFontMetricsF fm(font);
1257 qreal fontHeight = qCeil(v: fm.height()); // QScriptLine and therefore QTextLine rounds up
1258 return lineHeightMode() == QQuickText::FixedHeight ? fontHeight - lineHeight()
1259 : (1.0 - lineHeight()) * fontHeight;
1260}
1261
1262/*!
1263 Ensures the QQuickTextPrivate::doc variable is set to a valid text document
1264*/
1265void QQuickTextPrivate::ensureDoc()
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());
1274 qmlobject_connect(extra->doc, QQuickTextDocumentWithImageResources, SIGNAL(imagesLoaded()),
1275 q, QQuickText, SLOT(q_updateLayout()));
1276 }
1277}
1278
1279void QQuickTextPrivate::updateDocumentText()
1280{
1281 ensureDoc();
1282#if QT_CONFIG(textmarkdownreader)
1283 if (markdownText)
1284 extra->doc->setMarkdown(markdown: 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
1296/*!
1297 \qmltype Text
1298 \instantiates QQuickText
1299 \inqmlmodule QtQuick
1300 \ingroup qtquick-visual
1301 \inherits Item
1302 \brief Specifies how to add formatted text to a scene.
1303
1304 Text items can display both plain and rich text. For example, red text with
1305 a specific font and size can be defined like this:
1306
1307 \qml
1308 Text {
1309 text: "Hello World!"
1310 font.family: "Helvetica"
1311 font.pointSize: 24
1312 color: "red"
1313 }
1314 \endqml
1315
1316 Rich text is defined using HTML-style markup:
1317
1318 \qml
1319 Text {
1320 text: "<b>Hello</b> <i>World!</i>"
1321 }
1322 \endqml
1323
1324 \image declarative-text.png
1325
1326 If height and width are not explicitly set, Text will attempt to determine how
1327 much room is needed and set it accordingly. Unless \l wrapMode is set, it will always
1328 prefer width to height (all text will be placed on a single line).
1329
1330 The \l elide property can alternatively be used to fit a single line of
1331 plain text to a set width.
1332
1333 Note that the \l{Supported HTML Subset} is limited. Also, if the text contains
1334 HTML img tags that load remote images, the text is reloaded.
1335
1336 Text provides read-only text. For editable text, see \l TextEdit.
1337
1338 \sa {Qt Quick Examples - Text#Fonts}{Fonts example}
1339*/
1340QQuickText::QQuickText(QQuickItem *parent)
1341: QQuickImplicitSizeItem(*(new QQuickTextPrivate), parent)
1342{
1343 Q_D(QQuickText);
1344 d->init();
1345}
1346
1347QQuickText::QQuickText(QQuickTextPrivate &dd, QQuickItem *parent)
1348: QQuickImplicitSizeItem(dd, parent)
1349{
1350 Q_D(QQuickText);
1351 d->init();
1352}
1353
1354QQuickText::~QQuickText()
1355{
1356}
1357
1358/*!
1359 \qmlproperty bool QtQuick::Text::clip
1360 This property holds whether the text is clipped.
1361
1362 Note that if the text does not fit in the bounding rectangle it will be abruptly chopped.
1363
1364 If you want to display potentially long text in a limited space, you probably want to use \c elide instead.
1365*/
1366
1367/*!
1368 \qmlsignal QtQuick::Text::lineLaidOut(object line)
1369
1370 This signal is emitted for each line of text that is laid out during the layout
1371 process in plain text or styled text mode. It is not emitted in rich text mode.
1372 The specified \a line object provides more details about the line that
1373 is currently being laid out.
1374
1375 This gives the opportunity to position and resize a line as it is being laid out.
1376 It can for example be used to create columns or lay out text around objects.
1377
1378 The properties of the specified \a line object are:
1379
1380 \table
1381 \header
1382 \li Property name
1383 \li Description
1384 \row
1385 \li number (read-only)
1386 \li Line number, starts with zero.
1387 \row
1388 \li x
1389 \li Specifies the line's x position inside the \c Text element.
1390 \row
1391 \li y
1392 \li Specifies the line's y position inside the \c Text element.
1393 \row
1394 \li width
1395 \li Specifies the width of the line.
1396 \row
1397 \li height
1398 \li Specifies the height of the line.
1399 \row
1400 \li implicitWidth (read-only)
1401 \li The width that the line would naturally occupy based on its contents,
1402 not taking into account any modifications made to \e width.
1403 \row
1404 \li isLast (read-only)
1405 \li Whether the line is the last. This property can change if you
1406 set the \e width property to a different value.
1407 \endtable
1408
1409 For example, this will move the first 5 lines of a Text item by 100 pixels to the right:
1410 \code
1411 onLineLaidOut: (line)=> {
1412 if (line.number < 5) {
1413 line.x = line.x + 100
1414 line.width = line.width - 100
1415 }
1416 }
1417 \endcode
1418
1419 The following example will allow you to position an item at the end of the last line:
1420 \code
1421 onLineLaidOut: (line)=> {
1422 if (line.isLast) {
1423 lastLineMarker.x = line.x + line.implicitWidth
1424 lastLineMarker.y = line.y + (line.height - lastLineMarker.height) / 2
1425 }
1426 }
1427 \endcode
1428*/
1429
1430/*!
1431 \qmlsignal QtQuick::Text::linkActivated(string link)
1432
1433 This signal is emitted when the user clicks on a link embedded in the text.
1434 The link must be in rich text or HTML format and the
1435 \a link string provides access to the particular link.
1436
1437 \snippet qml/text/onLinkActivated.qml 0
1438
1439 The example code will display the text
1440 "See the \l{http://qt-project.org}{Qt Project website}."
1441
1442 Clicking on the highlighted link will output
1443 \tt{http://qt-project.org link activated} to the console.
1444*/
1445
1446/*!
1447 \qmlproperty string QtQuick::Text::font.family
1448
1449 Sets the family name of the font.
1450
1451 The family name is case insensitive and may optionally include a foundry name, e.g. "Helvetica [Cronyx]".
1452 If the family is available from more than one foundry and the foundry isn't specified, an arbitrary foundry is chosen.
1453 If the family isn't available a family will be set using the font matching algorithm.
1454*/
1455
1456/*!
1457 \qmlproperty string QtQuick::Text::font.styleName
1458 \since 5.6
1459
1460 Sets the style name of the font.
1461
1462 The style name is case insensitive. If set, the font will be matched against style name instead
1463 of the font properties \l font.weight, \l font.bold and \l font.italic.
1464*/
1465
1466/*!
1467 \qmlproperty bool QtQuick::Text::font.bold
1468
1469 Sets whether the font weight is bold.
1470*/
1471
1472/*!
1473 \qmlproperty int QtQuick::Text::font.weight
1474
1475 The requested weight of the font. The weight requested must be an integer
1476 between 1 and 1000, or one of the predefined values:
1477
1478 \value Font.Thin 100
1479 \value Font.ExtraLight 200
1480 \value Font.Light 300
1481 \value Font.Normal 400 (default)
1482 \value Font.Medium 500
1483 \value Font.DemiBold 600
1484 \value Font.Bold 700
1485 \value Font.ExtraBold 800
1486 \value Font.Black 900
1487
1488 \qml
1489 Text { text: "Hello"; font.weight: Font.DemiBold }
1490 \endqml
1491*/
1492
1493/*!
1494 \qmlproperty bool QtQuick::Text::font.italic
1495
1496 Sets whether the font has an italic style.
1497*/
1498
1499/*!
1500 \qmlproperty bool QtQuick::Text::font.underline
1501
1502 Sets whether the text is underlined.
1503*/
1504
1505/*!
1506 \qmlproperty bool QtQuick::Text::font.strikeout
1507
1508 Sets whether the font has a strikeout style.
1509*/
1510
1511/*!
1512 \qmlproperty real QtQuick::Text::font.pointSize
1513
1514 Sets the font size in points. The point size must be greater than zero.
1515*/
1516
1517/*!
1518 \qmlproperty int QtQuick::Text::font.pixelSize
1519
1520 Sets the font size in pixels.
1521
1522 Using this function makes the font device dependent.
1523 Use \c pointSize to set the size of the font in a device independent manner.
1524*/
1525
1526/*!
1527 \qmlproperty real QtQuick::Text::font.letterSpacing
1528
1529 Sets the letter spacing for the font.
1530
1531 Letter spacing changes the default spacing between individual letters in the font.
1532 A positive value increases the letter spacing by the corresponding pixels; a negative value decreases the spacing.
1533*/
1534
1535/*!
1536 \qmlproperty real QtQuick::Text::font.wordSpacing
1537
1538 Sets the word spacing for the font.
1539
1540 Word spacing changes the default spacing between individual words.
1541 A positive value increases the word spacing by a corresponding amount of pixels,
1542 while a negative value decreases the inter-word spacing accordingly.
1543*/
1544
1545/*!
1546 \qmlproperty enumeration QtQuick::Text::font.capitalization
1547
1548 Sets the capitalization for the text.
1549
1550 \value Font.MixedCase the normal case: no capitalization change is applied
1551 \value Font.AllUppercase alters the text to be rendered in all uppercase type
1552 \value Font.AllLowercase alters the text to be rendered in all lowercase type
1553 \value Font.SmallCaps alters the text to be rendered in small-caps type
1554 \value Font.Capitalize alters the text to be rendered with the first character of
1555 each word as an uppercase character
1556
1557 \qml
1558 Text { text: "Hello"; font.capitalization: Font.AllLowercase }
1559 \endqml
1560*/
1561
1562/*!
1563 \qmlproperty enumeration QtQuick::Text::font.hintingPreference
1564 \since 5.8
1565
1566 Sets the preferred hinting on the text. This is a hint to the underlying text rendering system
1567 to use a certain level of hinting, and has varying support across platforms. See the table in
1568 the documentation for QFont::HintingPreference for more details.
1569
1570 \note This property only has an effect when used together with render type Text.NativeRendering.
1571
1572 \value Font.PreferDefaultHinting Use the default hinting level for the target platform.
1573 \value Font.PreferNoHinting If possible, render text without hinting the outlines
1574 of the glyphs. The text layout will be typographically accurate, using the same metrics
1575 as are used e.g. when printing.
1576 \value Font.PreferVerticalHinting If possible, render text with no horizontal hinting,
1577 but align glyphs to the pixel grid in the vertical direction. The text will appear
1578 crisper on displays where the density is too low to give an accurate rendering
1579 of the glyphs. But since the horizontal metrics of the glyphs are unhinted, the text's
1580 layout will be scalable to higher density devices (such as printers) without impacting
1581 details such as line breaks.
1582 \value Font.PreferFullHinting If possible, render text with hinting in both horizontal and
1583 vertical directions. The text will be altered to optimize legibility on the target
1584 device, but since the metrics will depend on the target size of the text, the positions
1585 of glyphs, line breaks, and other typographical detail will not scale, meaning that a
1586 text layout may look different on devices with different pixel densities.
1587
1588 \qml
1589 Text { text: "Hello"; renderType: Text.NativeRendering; font.hintingPreference: Font.PreferVerticalHinting }
1590 \endqml
1591*/
1592
1593/*!
1594 \qmlproperty bool QtQuick::Text::font.kerning
1595 \since 5.10
1596
1597 Enables or disables the kerning OpenType feature when shaping the text. Disabling this may
1598 improve performance when creating or changing the text, at the expense of some cosmetic
1599 features. The default value is true.
1600
1601 \qml
1602 Text { text: "OATS FLAVOUR WAY"; font.kerning: false }
1603 \endqml
1604*/
1605
1606/*!
1607 \qmlproperty bool QtQuick::Text::font.preferShaping
1608 \since 5.10
1609
1610 Sometimes, a font will apply complex rules to a set of characters in order to
1611 display them correctly. In some writing systems, such as Brahmic scripts, this is
1612 required in order for the text to be legible, but in e.g. Latin script, it is merely
1613 a cosmetic feature. Setting the \c preferShaping property to false will disable all
1614 such features when they are not required, which will improve performance in most cases.
1615
1616 The default value is true.
1617
1618 \qml
1619 Text { text: "Some text"; font.preferShaping: false }
1620 \endqml
1621*/
1622
1623/*!
1624 \qmlproperty object QtQuick::Text::font.features
1625 \since 6.6
1626
1627//! [qml-font-features]
1628 Applies integer values to specific OpenType features when shaping the text based on the contents
1629 in \a features. This provides advanced access to the font shaping process, and can be used
1630 to support font features that are otherwise not covered in the API.
1631
1632 The font features are represented by a map from four-letter tags to integer values. This integer
1633 value passed along with the tag in most cases represents a boolean value: A zero value means the
1634 feature is disabled, and a non-zero value means it is enabled. For certain font features,
1635 however, it may have other intepretations. For example, when applied to the \c salt feature, the
1636 value is an index that specifies the stylistic alternative to use.
1637
1638 For example, the \c frac font feature will convert diagonal fractions separated with a slash
1639 (such as \c 1/2) with a different representation. Typically this will involve baking the full
1640 fraction into a single character width (such as \c ½).
1641
1642 If a font supports the \c frac feature, then it can be enabled in the shaper as in the following
1643 code:
1644
1645 \qml
1646 Text {
1647 text: "One divided by two is 1/2"
1648 font.family: "MyFractionFont"
1649 font.features: { "frac": 1 }
1650 }
1651 \endqml
1652
1653 Multiple features can be assigned values in the same mapping. For instance, if we would like
1654 to also disable kerning for the font, we can explicitly disable this as follows:
1655
1656 \qml
1657 Text {
1658 text: "One divided by two is 1/2"
1659 font.family: "MyFractionFont"
1660 font.features: { "frac": 1, "kern": 0 }
1661 }
1662 \endqml
1663
1664 You can also collect the font properties in an object:
1665
1666 \qml
1667 Text {
1668 text: "One divided by two is 1/2"
1669 font: {
1670 family: "MyFractionFont"
1671 features: { "frac": 1, "kern": 0 }
1672 }
1673 }
1674 \endqml
1675
1676 \note By default, Qt will enable and disable certain font features based on other font
1677 properties. In particular, the \c kern feature will be enabled/disabled depending on the
1678 \l font.kerning property of the QFont. In addition, all ligature features (\c liga, \c clig,
1679 \c dlig, \c hlig) will be disabled if a \l font.letterSpacing is set, but only for writing
1680 systems where the use of ligature is cosmetic. For writing systems where ligatures are required,
1681 the features will remain in their default state. The values set using \c font.features will
1682 override the default behavior. If, for instance, \c{"kern"} is set to 1, then kerning will
1683 always be enabled, egardless of whether the \l font.kerning property is set to false. Similarly,
1684 if it is set to 0, then it will always be disabled.
1685
1686 \sa QFont::setFeature()
1687//! [qml-font-features]
1688*/
1689QFont QQuickText::font() const
1690{
1691 Q_D(const QQuickText);
1692 return d->sourceFont;
1693}
1694
1695void QQuickText::setFont(const QFont &font)
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: 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(font: d->sourceFont);
1725}
1726
1727void QQuickText::itemChange(ItemChange change, const ItemChangeData &value)
1728{
1729 Q_D(QQuickText);
1730 Q_UNUSED(value);
1731 switch (change) {
1732 case ItemAntialiasingHasChanged:
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
1742 case ItemDevicePixelRatioHasChanged:
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 }
1757 QQuickItem::itemChange(change, value);
1758}
1759
1760/*!
1761 \qmlproperty string QtQuick::Text::text
1762
1763 The text to display. Text supports both plain and rich text strings.
1764
1765 The item will try to automatically determine whether the text should
1766 be treated as styled text. This determination is made using Qt::mightBeRichText().
1767 However, detection of Markdown is not automatic.
1768
1769 \sa textFormat
1770*/
1771QString QQuickText::text() const
1772{
1773 Q_D(const QQuickText);
1774 return d->text;
1775}
1776
1777void QQuickText::setText(const QString &n)
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(c: d->extra->imgTags);
1802 d->extra->imgTags.clear();
1803 }
1804 setFlag(flag: QQuickItem::ItemObservesViewport, enabled: n.size() > QQuickTextPrivate::largeTextSizeThreshold);
1805 d->updateLayout();
1806 setAcceptHoverEvents(d->richText || d->styledText);
1807 emit textChanged(text: d->text);
1808}
1809
1810/*!
1811 \qmlproperty color QtQuick::Text::color
1812
1813 The text color.
1814
1815 An example of green text defined using hexadecimal notation:
1816 \qml
1817 Text {
1818 color: "#00FF00"
1819 text: "green text"
1820 }
1821 \endqml
1822
1823 An example of steel blue text defined using an SVG color name:
1824 \qml
1825 Text {
1826 color: "steelblue"
1827 text: "blue text"
1828 }
1829 \endqml
1830*/
1831QColor QQuickText::color() const
1832{
1833 Q_D(const QQuickText);
1834 return QColor::fromRgba(rgba: d->color);
1835}
1836
1837void QQuickText::setColor(const QColor &color)
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()) {
1846 d->updateType = QQuickTextPrivate::UpdatePaintNode;
1847 update();
1848 }
1849 emit colorChanged();
1850}
1851
1852/*!
1853 \qmlproperty color QtQuick::Text::linkColor
1854
1855 The color of links in the text.
1856
1857 This property works with the StyledText \l textFormat, but not with RichText.
1858 Link color in RichText can be specified by including CSS style tags in the
1859 text.
1860*/
1861
1862QColor QQuickText::linkColor() const
1863{
1864 Q_D(const QQuickText);
1865 return QColor::fromRgba(rgba: d->linkColor);
1866}
1867
1868void QQuickText::setLinkColor(const QColor &color)
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()) {
1877 d->updateType = QQuickTextPrivate::UpdatePaintNode;
1878 update();
1879 }
1880 emit linkColorChanged();
1881}
1882
1883/*!
1884 \qmlproperty enumeration QtQuick::Text::style
1885
1886 Set an additional text style.
1887
1888 Supported text styles are:
1889
1890 \value Text.Normal - the default
1891 \value Text.Outline
1892 \value Text.Raised
1893 \value Text.Sunken
1894
1895 \qml
1896 Row {
1897 Text { font.pointSize: 24; text: "Normal" }
1898 Text { font.pointSize: 24; text: "Raised"; style: Text.Raised; styleColor: "#AAAAAA" }
1899 Text { font.pointSize: 24; text: "Outline";style: Text.Outline; styleColor: "red" }
1900 Text { font.pointSize: 24; text: "Sunken"; style: Text.Sunken; styleColor: "#AAAAAA" }
1901 }
1902 \endqml
1903
1904 \image declarative-textstyle.png
1905*/
1906QQuickText::TextStyle QQuickText::style() const
1907{
1908 Q_D(const QQuickText);
1909 return d->style;
1910}
1911
1912void QQuickText::setStyle(QQuickText::TextStyle style)
1913{
1914 Q_D(QQuickText);
1915 if (d->style == style)
1916 return;
1917
1918 d->style = style;
1919 if (isComponentComplete()) {
1920 d->updateType = QQuickTextPrivate::UpdatePaintNode;
1921 update();
1922 }
1923 emit styleChanged(style: d->style);
1924}
1925
1926/*!
1927 \qmlproperty color QtQuick::Text::styleColor
1928
1929 Defines the secondary color used by text styles.
1930
1931 \c styleColor is used as the outline color for outlined text, and as the
1932 shadow color for raised or sunken text. If no style has been set, it is not
1933 used at all.
1934
1935 \qml
1936 Text { font.pointSize: 18; text: "hello"; style: Text.Raised; styleColor: "gray" }
1937 \endqml
1938
1939 \sa style
1940 */
1941QColor QQuickText::styleColor() const
1942{
1943 Q_D(const QQuickText);
1944 return QColor::fromRgba(rgba: d->styleColor);
1945}
1946
1947void QQuickText::setStyleColor(const QColor &color)
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()) {
1956 d->updateType = QQuickTextPrivate::UpdatePaintNode;
1957 update();
1958 }
1959 emit styleColorChanged();
1960}
1961
1962/*!
1963 \qmlproperty enumeration QtQuick::Text::horizontalAlignment
1964 \qmlproperty enumeration QtQuick::Text::verticalAlignment
1965 \qmlproperty enumeration QtQuick::Text::effectiveHorizontalAlignment
1966
1967 Sets the horizontal and vertical alignment of the text within the Text items
1968 width and height. By default, the text is vertically aligned to the top. Horizontal
1969 alignment follows the natural alignment of the text, for example text that is read
1970 from left to right will be aligned to the left.
1971
1972 The valid values for \c horizontalAlignment are \c Text.AlignLeft, \c Text.AlignRight, \c Text.AlignHCenter and
1973 \c Text.AlignJustify. The valid values for \c verticalAlignment are \c Text.AlignTop, \c Text.AlignBottom
1974 and \c Text.AlignVCenter.
1975
1976 Note that for a single line of text, the size of the text is the area of the text. In this common case,
1977 all alignments are equivalent. If you want the text to be, say, centered in its parent, then you will
1978 need to either modify the Item::anchors, or set horizontalAlignment to Text.AlignHCenter and bind the width to
1979 that of the parent.
1980
1981 When using the attached property LayoutMirroring::enabled to mirror application
1982 layouts, the horizontal alignment of text will also be mirrored. However, the property
1983 \c horizontalAlignment will remain unchanged. To query the effective horizontal alignment
1984 of Text, use the read-only property \c effectiveHorizontalAlignment.
1985*/
1986QQuickText::HAlignment QQuickText::hAlign() const
1987{
1988 Q_D(const QQuickText);
1989 return d->hAlign;
1990}
1991
1992void QQuickText::setHAlign(HAlignment align)
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
2001void QQuickText::resetHAlign()
2002{
2003 Q_D(QQuickText);
2004 d->hAlignImplicit = true;
2005 if (isComponentComplete() && d->determineHorizontalAlignment())
2006 d->updateLayout();
2007}
2008
2009QQuickText::HAlignment QQuickText::effectiveHAlign() const
2010{
2011 Q_D(const QQuickText);
2012 QQuickText::HAlignment effectiveAlignment = d->hAlign;
2013 if (!d->hAlignImplicit && d->effectiveLayoutMirror) {
2014 switch (d->hAlign) {
2015 case QQuickText::AlignLeft:
2016 effectiveAlignment = QQuickText::AlignRight;
2017 break;
2018 case QQuickText::AlignRight:
2019 effectiveAlignment = QQuickText::AlignLeft;
2020 break;
2021 default:
2022 break;
2023 }
2024 }
2025 return effectiveAlignment;
2026}
2027
2028bool QQuickTextPrivate::setHAlign(QQuickText::HAlignment alignment, bool forceAlign)
2029{
2030 Q_Q(QQuickText);
2031 if (hAlign != alignment || forceAlign) {
2032 QQuickText::HAlignment oldEffectiveHAlign = q->effectiveHAlign();
2033 hAlign = alignment;
2034
2035 emit q->horizontalAlignmentChanged(alignment: hAlign);
2036 if (oldEffectiveHAlign != q->effectiveHAlign())
2037 emit q->effectiveHorizontalAlignmentChanged();
2038 return true;
2039 }
2040 return false;
2041}
2042
2043bool QQuickTextPrivate::determineHorizontalAlignment()
2044{
2045 if (hAlignImplicit) {
2046#if QT_CONFIG(im)
2047 bool alignToRight = text.isEmpty() ? QGuiApplication::inputMethod()->inputDirection() == Qt::RightToLeft : rightToLeftText;
2048#else
2049 bool alignToRight = rightToLeftText;
2050#endif
2051 return setHAlign(alignment: alignToRight ? QQuickText::AlignRight : QQuickText::AlignLeft);
2052 }
2053 return false;
2054}
2055
2056void QQuickTextPrivate::mirrorChange()
2057{
2058 Q_Q(QQuickText);
2059 if (q->isComponentComplete()) {
2060 if (!hAlignImplicit && (hAlign == QQuickText::AlignRight || hAlign == QQuickText::AlignLeft)) {
2061 updateLayout();
2062 emit q->effectiveHorizontalAlignmentChanged();
2063 }
2064 }
2065}
2066
2067QQuickText::VAlignment QQuickText::vAlign() const
2068{
2069 Q_D(const QQuickText);
2070 return d->vAlign;
2071}
2072
2073void QQuickText::setVAlign(VAlignment align)
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
2084 emit verticalAlignmentChanged(alignment: align);
2085}
2086
2087/*!
2088 \qmlproperty enumeration QtQuick::Text::wrapMode
2089
2090 Set this property to wrap the text to the Text item's width. The text will only
2091 wrap if an explicit width has been set. wrapMode can be one of:
2092
2093 \value Text.NoWrap
2094 (default) no wrapping will be performed. If the text contains
2095 insufficient newlines, then \l contentWidth will exceed a set width.
2096 \value Text.WordWrap
2097 wrapping is done on word boundaries only. If a word is too long,
2098 \l contentWidth will exceed a set width.
2099 \value Text.WrapAnywhere
2100 wrapping is done at any point on a line, even if it occurs in the middle of a word.
2101 \value Text.Wrap
2102 if possible, wrapping occurs at a word boundary; otherwise it will occur
2103 at the appropriate point on the line, even in the middle of a word.
2104*/
2105QQuickText::WrapMode QQuickText::wrapMode() const
2106{
2107 Q_D(const QQuickText);
2108 return d->wrapMode;
2109}
2110
2111void QQuickText::setWrapMode(WrapMode mode)
2112{
2113 Q_D(QQuickText);
2114 if (mode == d->wrapMode)
2115 return;
2116
2117 d->wrapMode = mode;
2118 d->updateLayout();
2119
2120 emit wrapModeChanged();
2121}
2122
2123/*!
2124 \qmlproperty int QtQuick::Text::lineCount
2125
2126 Returns the number of lines visible in the text item.
2127
2128 This property is not supported for rich text.
2129
2130 \sa maximumLineCount
2131*/
2132int QQuickText::lineCount() const
2133{
2134 Q_D(const QQuickText);
2135 return d->lineCount;
2136}
2137
2138/*!
2139 \qmlproperty bool QtQuick::Text::truncated
2140
2141 Returns true if the text has been truncated due to \l maximumLineCount
2142 or \l elide.
2143
2144 This property is not supported for rich text.
2145
2146 \sa maximumLineCount, elide
2147*/
2148bool QQuickText::truncated() const
2149{
2150 Q_D(const QQuickText);
2151 return d->truncated;
2152}
2153
2154/*!
2155 \qmlproperty int QtQuick::Text::maximumLineCount
2156
2157 Set this property to limit the number of lines that the text item will show.
2158 If elide is set to Text.ElideRight, the text will be elided appropriately.
2159 By default, this is the value of the largest possible integer.
2160
2161 This property is not supported for rich text.
2162
2163 \sa lineCount, elide
2164*/
2165int QQuickText::maximumLineCount() const
2166{
2167 Q_D(const QQuickText);
2168 return d->maximumLineCount();
2169}
2170
2171void QQuickText::setMaximumLineCount(int lines)
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();
2180 emit maximumLineCountChanged();
2181 }
2182}
2183
2184void QQuickText::resetMaximumLineCount()
2185{
2186 Q_D(QQuickText);
2187 setMaximumLineCount(INT_MAX);
2188 if (d->truncated != false) {
2189 d->truncated = false;
2190 emit truncatedChanged();
2191 }
2192}
2193
2194/*!
2195 \qmlproperty enumeration QtQuick::Text::textFormat
2196
2197 The way the \l text property should be displayed.
2198
2199 Supported text formats are:
2200
2201 \value Text.AutoText (default) detected via the Qt::mightBeRichText() heuristic
2202 \value Text.PlainText all styling tags are treated as plain text
2203 \value Text.StyledText optimized basic rich text as in HTML 3.2
2204 \value Text.RichText \l {Supported HTML Subset} {a subset of HTML 4}
2205 \value Text.MarkdownText \l {https://commonmark.org/help/}{CommonMark} plus the
2206 \l {https://guides.github.com/features/mastering-markdown/}{GitHub}
2207 extensions for tables and task lists (since 5.14)
2208
2209 If the text format is \c Text.AutoText the Text item
2210 will automatically determine whether the text should be treated as
2211 styled text. This determination is made using Qt::mightBeRichText(),
2212 which can detect the presence of an HTML tag on the first line of text,
2213 but cannot distinguish Markdown from plain text.
2214
2215 \c Text.StyledText is an optimized format supporting some basic text
2216 styling markup, in the style of HTML 3.2:
2217
2218 \code
2219 <b></b> - bold
2220 <del></del> - strike out (removed content)
2221 <s></s> - strike out (no longer accurate or no longer relevant content)
2222 <strong></strong> - bold
2223 <i></i> - italic
2224 <br> - new line
2225 <p> - paragraph
2226 <u> - underlined text
2227 <font color="color_name" size="1-7"></font>
2228 <h1> to <h6> - headers
2229 <a href=""> - anchor
2230 <img src="" align="top,middle,bottom" width="" height=""> - inline images
2231 <ol type="">, <ul type=""> and <li> - ordered and unordered lists
2232 <pre></pre> - preformatted
2233 All entities
2234 \endcode
2235
2236 \c Text.StyledText parser is strict, requiring tags to be correctly nested.
2237
2238 \table
2239 \row
2240 \li
2241 \snippet qml/text/textFormats.qml 0
2242 \li \image declarative-textformat.png
2243 \endtable
2244
2245 \c Text.RichText supports a larger subset of HTML 4, as described on the
2246 \l {Supported HTML Subset} page. You should prefer using \c Text.PlainText,
2247 \c Text.StyledText or \c Text.MarkdownText instead, as they offer better performance.
2248
2249 \note With \c Text.MarkdownText, and with the supported subset of HTML,
2250 some decorative elements are not rendered as they would be in a web browser:
2251 \list
2252 \li code blocks use the \l {QFontDatabase::FixedFont}{default monospace font} but without a surrounding highlight box
2253 \li block quotes are indented, but there is no vertical line alongside the quote
2254 \li horizontal rules are not rendered
2255 \endlist
2256*/
2257QQuickText::TextFormat QQuickText::textFormat() const
2258{
2259 Q_D(const QQuickText);
2260 return d->format;
2261}
2262
2263void QQuickText::setTextFormat(TextFormat format)
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(textFormat: d->format);
2289}
2290
2291/*!
2292 \qmlproperty enumeration QtQuick::Text::elide
2293
2294 Set this property to elide parts of the text fit to the Text item's width.
2295 The text will only elide if an explicit width has been set.
2296
2297 This property cannot be used with rich text.
2298
2299 Eliding can be:
2300
2301 \value Text.ElideNone - the default
2302 \value Text.ElideLeft
2303 \value Text.ElideMiddle
2304 \value Text.ElideRight
2305
2306 If this property is set to Text.ElideRight, it can be used with \l {wrapMode}{wrapped}
2307 text. The text will only elide if \c maximumLineCount, or \c height has been set.
2308 If both \c maximumLineCount and \c height are set, \c maximumLineCount will
2309 apply unless the lines do not fit in the height allowed.
2310
2311 If the text is a multi-length string, and the mode is not \c Text.ElideNone,
2312 the first string that fits will be used, otherwise the last will be elided.
2313
2314 Multi-length strings are ordered from longest to shortest, separated by the
2315 Unicode "String Terminator" character \c U009C (write this in QML with \c{"\u009C"} or \c{"\x9C"}).
2316*/
2317QQuickText::TextElideMode QQuickText::elideMode() const
2318{
2319 Q_D(const QQuickText);
2320 return d->elideMode;
2321}
2322
2323void QQuickText::setElideMode(QQuickText::TextElideMode mode)
2324{
2325 Q_D(QQuickText);
2326 if (mode == d->elideMode)
2327 return;
2328
2329 d->elideMode = mode;
2330 d->updateLayout();
2331
2332 emit elideModeChanged(mode);
2333}
2334
2335/*!
2336 \qmlproperty url QtQuick::Text::baseUrl
2337
2338 This property specifies a base URL which is used to resolve relative URLs
2339 within the text.
2340
2341 Urls are resolved to be within the same directory as the target of the base
2342 URL meaning any portion of the path after the last '/' will be ignored.
2343
2344 \table
2345 \header \li Base URL \li Relative URL \li Resolved URL
2346 \row \li http://qt-project.org/ \li images/logo.png \li http://qt-project.org/images/logo.png
2347 \row \li http://qt-project.org/index.html \li images/logo.png \li http://qt-project.org/images/logo.png
2348 \row \li http://qt-project.org/content \li images/logo.png \li http://qt-project.org/content/images/logo.png
2349 \row \li http://qt-project.org/content/ \li images/logo.png \li http://qt-project.org/content/images/logo.png
2350 \row \li http://qt-project.org/content/index.html \li images/logo.png \li http://qt-project.org/content/images/logo.png
2351 \row \li http://qt-project.org/content/index.html \li ../images/logo.png \li http://qt-project.org/images/logo.png
2352 \row \li http://qt-project.org/content/index.html \li /images/logo.png \li http://qt-project.org/images/logo.png
2353 \endtable
2354
2355 The default value is the url of the QML file instantiating the Text item.
2356*/
2357
2358QUrl QQuickText::baseUrl() const
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
2371void QQuickText::setBaseUrl(const QUrl &url)
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(c: d->extra->imgTags);
2385 d->extra->imgTags.clear();
2386 }
2387 d->updateLayout();
2388 }
2389 emit baseUrlChanged();
2390 }
2391}
2392
2393void QQuickText::resetBaseUrl()
2394{
2395 if (QQmlContext *context = qmlContext(this))
2396 setBaseUrl(context->baseUrl());
2397 else
2398 setBaseUrl(QUrl());
2399}
2400
2401/*!
2402 Returns the extents of the text after layout.
2403 If the \l style() is not \c Text.Normal, a margin is added to ensure
2404 that the rendering effect will fit within this rectangle.
2405
2406 \sa contentWidth(), contentHeight(), clipRect()
2407*/
2408QRectF QQuickText::boundingRect() const
2409{
2410 Q_D(const QQuickText);
2411
2412 QRectF rect = d->layedOutTextRect;
2413 rect.moveLeft(pos: QQuickTextUtil::alignedX(textWidth: rect.width(), itemWidth: width(), alignment: effectiveHAlign()));
2414 rect.moveTop(pos: QQuickTextUtil::alignedY(textHeight: rect.height() + d->lineHeightOffset(), itemHeight: height(), alignment: d->vAlign));
2415
2416 if (d->style != Normal)
2417 rect.adjust(xp1: -1, yp1: 0, xp2: 1, yp2: 2);
2418 // Could include font max left/right bearings to either side of rectangle.
2419
2420 return rect;
2421}
2422
2423/*!
2424 Returns a rectangular area slightly larger than what is currently visible
2425 in \l viewportItem(); otherwise, the rectangle \c (0, 0, width, height).
2426 The text will be clipped to fit if \l clip is \c true.
2427
2428 \note If the \l style is not \c Text.Normal, the clip rectangle is adjusted
2429 to be slightly larger, to limit clipping of the outline effect at the edges.
2430 But it still looks better to set \l clip to \c false in that case.
2431
2432 \sa contentWidth(), contentHeight(), boundingRect()
2433*/
2434QRectF QQuickText::clipRect() const
2435{
2436 Q_D(const QQuickText);
2437
2438 QRectF rect = QQuickImplicitSizeItem::clipRect();
2439 if (d->style != Normal)
2440 rect.adjust(xp1: -1, yp1: 0, xp2: 1, yp2: 2);
2441 return rect;
2442}
2443
2444/*! \internal */
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.
2471 d->updateType = QQuickTextPrivate::UpdatePaintNode;
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(d: 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(d: 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)
2530 d->updateType = QQuickTextPrivate::UpdatePreprocess;
2531 update();
2532}
2533
2534QSGNode *QQuickText::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *data)
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(textHeight: d->layedOutTextRect.height() + d->lineHeightOffset(), itemHeight: d->availableHeight(), alignment: 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(rgba: d->color);
2566 const QColor styleColor = QColor::fromRgba(rgba: d->styleColor);
2567 const QColor linkColor = QColor::fromRgba(rgba: d->linkColor);
2568
2569 if (d->richText) {
2570 const qreal dx = QQuickTextUtil::alignedX(textWidth: d->layedOutTextRect.width(), itemWidth: d->availableWidth(), alignment: effectiveHAlign()) + leftPadding();
2571 d->ensureDoc();
2572 node->addTextDocument(position: QPointF(dx, dy), textDocument: d->extra->doc, color, style: d->style, styleColor, anchorColor: linkColor);
2573 } else if (d->layedOutTextRect.width() > 0) {
2574 const qreal dx = QQuickTextUtil::alignedX(textWidth: d->lineWidth, itemWidth: d->availableWidth(), alignment: effectiveHAlign()) + leftPadding();
2575 int unelidedLineCount = d->lineCount;
2576 if (d->elideLayout)
2577 unelidedLineCount -= 1;
2578 if (unelidedLineCount > 0) {
2579 node->addTextLayout(
2580 position: QPointF(dx, dy),
2581 textLayout: &d->layout,
2582 color, style: d->style, styleColor, anchorColor: linkColor,
2583 selectionColor: QColor(), selectedTextColor: QColor(), selectionStart: -1, selectionEnd: -1,
2584 lineStart: 0, lineCount: unelidedLineCount);
2585 }
2586 if (d->elideLayout)
2587 node->addTextLayout(position: QPointF(dx, dy), textLayout: d->elideLayout, color, style: d->style, styleColor, anchorColor: linkColor);
2588
2589 if (d->extra.isAllocated()) {
2590 for (QQuickStyledTextImgTag *img : std::as_const(t&: d->extra->visibleImgTags)) {
2591 QQuickPixmap *pix = img->pix;
2592 if (pix && pix->isReady())
2593 node->addImage(rect: QRectF(img->pos.x() + dx, img->pos.y() + dy, pix->width(), pix->height()), image: 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.
2600 invalidateFontCaches();
2601
2602 return node;
2603}
2604
2605void QQuickText::updatePolish()
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 }
2623 invalidateFontCaches();
2624}
2625
2626/*!
2627 \qmlproperty real QtQuick::Text::contentWidth
2628
2629 Returns the width of the text, including width past the width
2630 which is covered due to insufficient wrapping if WrapMode is set.
2631*/
2632qreal QQuickText::contentWidth() const
2633{
2634 Q_D(const QQuickText);
2635 return d->layedOutTextRect.width();
2636}
2637
2638/*!
2639 \qmlproperty real QtQuick::Text::contentHeight
2640
2641 Returns the height of the text, including height past the height
2642 which is covered due to there being more text than fits in the set height.
2643*/
2644qreal QQuickText::contentHeight() const
2645{
2646 Q_D(const QQuickText);
2647 return d->layedOutTextRect.height() + qMax(a: d->lineHeightOffset(), b: 0);
2648}
2649
2650/*!
2651 \qmlproperty real QtQuick::Text::lineHeight
2652
2653 Sets the line height for the text.
2654 The value can be in pixels or a multiplier depending on lineHeightMode.
2655
2656 The default value is a multiplier of 1.0.
2657 The line height must be a positive value.
2658*/
2659qreal QQuickText::lineHeight() const
2660{
2661 Q_D(const QQuickText);
2662 return d->lineHeight();
2663}
2664
2665void QQuickText::setLineHeight(qreal lineHeight)
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();
2676 emit lineHeightChanged(lineHeight);
2677}
2678
2679/*!
2680 \qmlproperty enumeration QtQuick::Text::lineHeightMode
2681
2682 This property determines how the line height is specified.
2683 The possible values are:
2684
2685 \value Text.ProportionalHeight (default) sets the spacing proportional to the line
2686 (as a multiplier). For example, set to 2 for double spacing.
2687 \value Text.FixedHeight sets the line height to a fixed line height (in pixels).
2688*/
2689QQuickText::LineHeightMode QQuickText::lineHeightMode() const
2690{
2691 Q_D(const QQuickText);
2692 return d->lineHeightMode();
2693}
2694
2695void QQuickText::setLineHeightMode(LineHeightMode mode)
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
2706 emit lineHeightModeChanged(mode);
2707}
2708
2709/*!
2710 \qmlproperty enumeration QtQuick::Text::fontSizeMode
2711
2712 This property specifies how the font size of the displayed text is determined.
2713 The possible values are:
2714
2715 \value Text.FixedSize
2716 (default) The size specified by \l font.pixelSize or \l font.pointSize is used.
2717 \value Text.HorizontalFit
2718 The largest size up to the size specified that fits within the width of the item
2719 without wrapping is used.
2720 \value Text.VerticalFit
2721 The largest size up to the size specified that fits the height of the item is used.
2722 \value Text.Fit
2723 The largest size up to the size specified that fits within the width and height
2724 of the item is used.
2725
2726 The font size of fitted text has a minimum bound specified by the
2727 minimumPointSize or minimumPixelSize property and maximum bound specified
2728 by either the \l font.pointSize or \l font.pixelSize properties.
2729
2730 \qml
2731 Text { text: "Hello"; fontSizeMode: Text.Fit; minimumPixelSize: 10; font.pixelSize: 72 }
2732 \endqml
2733
2734 If the text does not fit within the item bounds with the minimum font size
2735 the text will be elided as per the \l elide property.
2736
2737 If the \l textFormat property is set to \c Text.RichText, this will have no effect at all as the
2738 property will be ignored completely. If \l textFormat is set to \c Text.StyledText, then the
2739 property will be respected provided there is no font size tags inside the text. If there are
2740 font size tags, the property will still respect those. This can cause it to not fully comply with
2741 the fontSizeMode setting.
2742*/
2743
2744QQuickText::FontSizeMode QQuickText::fontSizeMode() const
2745{
2746 Q_D(const QQuickText);
2747 return d->fontSizeMode();
2748}
2749
2750void QQuickText::setFontSizeMode(FontSizeMode mode)
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;
2760 emit fontSizeModeChanged();
2761}
2762
2763/*!
2764 \qmlproperty int QtQuick::Text::minimumPixelSize
2765
2766 This property specifies the minimum font pixel size of text scaled by the
2767 fontSizeMode property.
2768
2769 If the fontSizeMode is Text.FixedSize or the \l font.pixelSize is -1 this
2770 property is ignored.
2771*/
2772
2773int QQuickText::minimumPixelSize() const
2774{
2775 Q_D(const QQuickText);
2776 return d->minimumPixelSize();
2777}
2778
2779void QQuickText::setMinimumPixelSize(int size)
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;
2790 emit minimumPixelSizeChanged();
2791}
2792
2793/*!
2794 \qmlproperty int QtQuick::Text::minimumPointSize
2795
2796 This property specifies the minimum font point \l size of text scaled by
2797 the fontSizeMode property.
2798
2799 If the fontSizeMode is Text.FixedSize or the \l font.pointSize is -1 this
2800 property is ignored.
2801*/
2802
2803int QQuickText::minimumPointSize() const
2804{
2805 Q_D(const QQuickText);
2806 return d->minimumPointSize();
2807}
2808
2809void QQuickText::setMinimumPointSize(int size)
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;
2820 emit minimumPointSizeChanged();
2821}
2822
2823/*!
2824 Returns the number of resources (images) that are being loaded asynchronously.
2825*/
2826int QQuickText::resourcesLoading() const
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
2834/*! \internal */
2835void QQuickText::componentComplete()
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 }
2846 QQuickItem::componentComplete();
2847 if (d->updateOnComponentComplete)
2848 d->updateLayout();
2849}
2850
2851QString QQuickTextPrivate::anchorAt(const QTextLayout *layout, const QPointF &mousePos)
2852{
2853 for (int i = 0; i < layout->lineCount(); ++i) {
2854 QTextLine line = layout->lineAt(i);
2855 if (line.naturalTextRect().contains(p: mousePos)) {
2856 int charPos = line.xToCursor(x: 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
2871QString QQuickTextPrivate::anchorAt(const QPointF &mousePos) const
2872{
2873 Q_Q(const QQuickText);
2874 QPointF translatedMousePos = mousePos;
2875 translatedMousePos.rx() -= q->leftPadding();
2876 translatedMousePos.ry() -= q->topPadding() + QQuickTextUtil::alignedY(textHeight: layedOutTextRect.height() + lineHeightOffset(), itemHeight: availableHeight(), alignment: vAlign);
2877 if (styledText) {
2878 QString link = anchorAt(layout: &layout, mousePos: translatedMousePos);
2879 if (link.isEmpty() && elideLayout)
2880 link = anchorAt(layout: elideLayout, mousePos: translatedMousePos);
2881 return link;
2882 } else if (richText && extra.isAllocated() && extra->doc) {
2883 translatedMousePos.rx() -= QQuickTextUtil::alignedX(textWidth: layedOutTextRect.width(), itemWidth: availableWidth(), alignment: q->effectiveHAlign());
2884 return extra->doc->documentLayout()->anchorAt(pos: translatedMousePos);
2885 }
2886 return QString();
2887}
2888
2889bool QQuickTextPrivate::isLinkActivatedConnected()
2890{
2891 Q_Q(QQuickText);
2892 IS_SIGNAL_CONNECTED(q, QQuickText, linkActivated, (const QString &));
2893}
2894
2895/*! \internal */
2896void QQuickText::mousePressEvent(QMouseEvent *event)
2897{
2898 Q_D(QQuickText);
2899
2900 QString link;
2901 if (d->isLinkActivatedConnected())
2902 link = d->anchorAt(mousePos: 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())
2913 QQuickItem::mousePressEvent(event);
2914}
2915
2916
2917/*! \internal */
2918void QQuickText::mouseReleaseEvent(QMouseEvent *event)
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(mousePos: event->position());
2927
2928 if (!link.isEmpty() && d->extra.isAllocated() && d->extra->activeLink == link)
2929 emit linkActivated(link: d->extra->activeLink);
2930 else
2931 event->setAccepted(false);
2932
2933 if (!event->isAccepted())
2934 QQuickItem::mouseReleaseEvent(event);
2935}
2936
2937bool QQuickTextPrivate::isLinkHoveredConnected()
2938{
2939 Q_Q(QQuickText);
2940 IS_SIGNAL_CONNECTED(q, QQuickText, linkHovered, (const QString &));
2941}
2942
2943static void getLinks_helper(const QTextLayout *layout, QVector<QQuickTextPrivate::LinkDesc> *links)
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(pos: start);
2950 QRectF r;
2951 r.setTop(line.y());
2952 r.setLeft(line.cursorToX(cursorPos: start, edge: QTextLine::Leading));
2953 r.setHeight(line.height());
2954 r.setRight(line.cursorToX(cursorPos: start + len, edge: 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(position: start, n: len);
2959 const QString anchorHref = formatRange.format.anchorHref();
2960 if (anchorName.isEmpty())
2961 anchorName = anchorHref;
2962 links->append( t: { .m_anchor: anchorName, .m_anchorTarget: anchorHref, .m_startIndex: start, .m_endIndex: start + len, .rect: r.toRect()} );
2963 }
2964 }
2965}
2966
2967QVector<QQuickTextPrivate::LinkDesc> QQuickTextPrivate::getLinks() const
2968{
2969 QVector<QQuickTextPrivate::LinkDesc> links;
2970 getLinks_helper(layout: &layout, links: &links);
2971 return links;
2972}
2973
2974
2975/*!
2976 \qmlsignal QtQuick::Text::linkHovered(string link)
2977 \since 5.2
2978
2979 This signal is emitted when the user hovers a link embedded in the
2980 text. The link must be in rich text or HTML format and the \a link
2981 string provides access to the particular link.
2982
2983 \sa hoveredLink, linkAt()
2984*/
2985
2986/*!
2987 \qmlproperty string QtQuick::Text::hoveredLink
2988 \since 5.2
2989
2990 This property contains the link string when the user hovers a link
2991 embedded in the text. The link must be in rich text or HTML format
2992 and the \a hoveredLink string provides access to the particular link.
2993
2994 \sa linkHovered, linkAt()
2995*/
2996
2997QString QQuickText::hoveredLink() const
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(screen: wnd->screen()) - wnd->position() - mapToScene(point: QPointF(0, 0));
3007 return d->anchorAt(mousePos: pos);
3008 }
3009#endif // cursor
3010 }
3011 return QString();
3012}
3013
3014void QQuickTextPrivate::processHoverEvent(QHoverEvent *event)
3015{
3016 Q_Q(QQuickText);
3017 qCDebug(lcHoverTrace) << q;
3018 QString link;
3019 if (isLinkHoveredConnected()) {
3020 if (event->type() != QEvent::HoverLeave)
3021 link = anchorAt(mousePos: event->position());
3022
3023 if ((!extra.isAllocated() && !link.isEmpty()) || (extra.isAllocated() && extra->hoveredLink != link)) {
3024 extra.value().hoveredLink = link;
3025 emit q->linkHovered(link: extra->hoveredLink);
3026 }
3027 }
3028 event->ignore();
3029}
3030
3031void QQuickText::hoverEnterEvent(QHoverEvent *event)
3032{
3033 Q_D(QQuickText);
3034 d->processHoverEvent(event);
3035}
3036
3037void QQuickText::hoverMoveEvent(QHoverEvent *event)
3038{
3039 Q_D(QQuickText);
3040 d->processHoverEvent(event);
3041}
3042
3043void QQuickText::hoverLeaveEvent(QHoverEvent *event)
3044{
3045 Q_D(QQuickText);
3046 d->processHoverEvent(event);
3047}
3048
3049void QQuickText::invalidate()
3050{
3051 Q_D(QQuickText);
3052 d->textHasChanged = true;
3053 d->updateLayout();
3054}
3055
3056bool QQuickTextPrivate::transformChanged(QQuickItem *transformedItem)
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
3060 if (flags & QQuickItem::ItemObservesViewport) {
3061 updateType = UpdatePaintNode;
3062 dirty(QQuickItemPrivate::Content);
3063 }
3064 return QQuickImplicitSizeItemPrivate::transformChanged(transformedItem);
3065}
3066
3067/*!
3068 \qmlproperty int QtQuick::Text::renderTypeQuality
3069 \since 6.0
3070
3071 Override the default rendering type quality for this component. This is a low-level
3072 customization which can be ignored in most cases. It currently only has an effect
3073 when \l renderType is \c Text.QtRendering.
3074
3075 The rasterization algorithm used by Text.QtRendering may give artifacts at
3076 large text sizes, such as sharp corners looking rounder than they should. If
3077 this is an issue for specific text items, increase the \c renderTypeQuality to
3078 improve rendering quality, at the expense of memory consumption.
3079
3080 The \c renderTypeQuality may be any integer over 0, or one of the following
3081 predefined values
3082
3083 \value Text.DefaultRenderTypeQuality -1 (default)
3084 \value Text.LowRenderTypeQuality 26
3085 \value Text.NormalRenderTypeQuality 52
3086 \value Text.HighRenderTypeQuality 104
3087 \value Text.VeryHighRenderTypeQuality 208
3088*/
3089int QQuickText::renderTypeQuality() const
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()) {
3103 d->updateType = QQuickTextPrivate::UpdatePaintNode;
3104 update();
3105 }
3106
3107 emit renderTypeQualityChanged();
3108}
3109
3110/*!
3111 \qmlproperty enumeration QtQuick::Text::renderType
3112
3113 Override the default rendering type for this component.
3114
3115 Supported render types are:
3116
3117 \value Text.QtRendering Text is rendered using a scalable distance field for each glyph.
3118 \value Text.NativeRendering Text is rendered using a platform-specific technique.
3119
3120 Select \c Text.NativeRendering if you prefer text to look native on the target platform and do
3121 not require advanced features such as transformation of the text. Using such features in
3122 combination with the NativeRendering render type will lend poor and sometimes pixelated
3123 results.
3124
3125 The default rendering type is determined by \l QQuickWindow::textRenderType().
3126*/
3127QQuickText::RenderType QQuickText::renderType() const
3128{
3129 Q_D(const QQuickText);
3130 return d->renderType;
3131}
3132
3133void QQuickText::setRenderType(QQuickText::RenderType renderType)
3134{
3135 Q_D(QQuickText);
3136 if (d->renderType == renderType)
3137 return;
3138
3139 d->renderType = renderType;
3140 emit renderTypeChanged();
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)
3148/*!
3149 \qmlmethod QtQuick::Text::doLayout()
3150 \deprecated
3151
3152 Use \l forceLayout() instead.
3153*/
3154void QQuickText::doLayout()
3155{
3156 forceLayout();
3157}
3158
3159#endif
3160#endif
3161/*!
3162 \qmlmethod QtQuick::Text::forceLayout()
3163 \since 5.9
3164
3165 Triggers a re-layout of the displayed text.
3166*/
3167void QQuickText::forceLayout()
3168{
3169 Q_D(QQuickText);
3170 d->updateSize();
3171}
3172
3173/*!
3174 \qmlmethod QtQuick::Text::linkAt(real x, real y)
3175 \since 5.3
3176
3177 Returns the link string at point \a x, \a y in content coordinates,
3178 or an empty string if no link exists at that point.
3179
3180 \sa hoveredLink
3181*/
3182QString QQuickText::linkAt(qreal x, qreal y) const
3183{
3184 Q_D(const QQuickText);
3185 return d->anchorAt(mousePos: QPointF(x, y));
3186}
3187
3188/*!
3189 * \internal
3190 *
3191 * Invalidates font caches owned by the text objects owned by the element
3192 * to work around the fact that text objects cannot be used from multiple threads.
3193 */
3194void QQuickText::invalidateFontCaches()
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
3210/*!
3211 \since 5.6
3212 \qmlproperty real QtQuick::Text::padding
3213 \qmlproperty real QtQuick::Text::topPadding
3214 \qmlproperty real QtQuick::Text::leftPadding
3215 \qmlproperty real QtQuick::Text::bottomPadding
3216 \qmlproperty real QtQuick::Text::rightPadding
3217
3218 These properties hold the padding around the content. This space is reserved
3219 in addition to the contentWidth and contentHeight.
3220*/
3221qreal QQuickText::padding() const
3222{
3223 Q_D(const QQuickText);
3224 return d->padding();
3225}
3226
3227void QQuickText::setPadding(qreal padding)
3228{
3229 Q_D(QQuickText);
3230 if (qFuzzyCompare(p1: d->padding(), p2: 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
3246void QQuickText::resetPadding()
3247{
3248 setPadding(0);
3249}
3250
3251qreal QQuickText::topPadding() const
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
3259void QQuickText::setTopPadding(qreal padding)
3260{
3261 Q_D(QQuickText);
3262 d->setTopPadding(value: padding);
3263}
3264
3265void QQuickText::resetTopPadding()
3266{
3267 Q_D(QQuickText);
3268 d->setTopPadding(value: 0, reset: true);
3269}
3270
3271qreal QQuickText::leftPadding() const
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
3279void QQuickText::setLeftPadding(qreal padding)
3280{
3281 Q_D(QQuickText);
3282 d->setLeftPadding(value: padding);
3283}
3284
3285void QQuickText::resetLeftPadding()
3286{
3287 Q_D(QQuickText);
3288 d->setLeftPadding(value: 0, reset: true);
3289}
3290
3291qreal QQuickText::rightPadding() const
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
3299void QQuickText::setRightPadding(qreal padding)
3300{
3301 Q_D(QQuickText);
3302 d->setRightPadding(value: padding);
3303}
3304
3305void QQuickText::resetRightPadding()
3306{
3307 Q_D(QQuickText);
3308 d->setRightPadding(value: 0, reset: true);
3309}
3310
3311qreal QQuickText::bottomPadding() const
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
3319void QQuickText::setBottomPadding(qreal padding)
3320{
3321 Q_D(QQuickText);
3322 d->setBottomPadding(value: padding);
3323}
3324
3325void QQuickText::resetBottomPadding()
3326{
3327 Q_D(QQuickText);
3328 d->setBottomPadding(value: 0, reset: true);
3329}
3330
3331/*!
3332 \qmlproperty string QtQuick::Text::fontInfo.family
3333 \since 5.9
3334
3335 The family name of the font that has been resolved for the current font
3336 and fontSizeMode.
3337*/
3338
3339/*!
3340 \qmlproperty string QtQuick::Text::fontInfo.styleName
3341 \since 5.9
3342
3343 The style name of the font info that has been resolved for the current font
3344 and fontSizeMode.
3345*/
3346
3347/*!
3348 \qmlproperty bool QtQuick::Text::fontInfo.bold
3349 \since 5.9
3350
3351 The bold state of the font info that has been resolved for the current font
3352 and fontSizeMode. This is true if the weight of the resolved font is bold or higher.
3353*/
3354
3355/*!
3356 \qmlproperty int QtQuick::Text::fontInfo.weight
3357 \since 5.9
3358
3359 The weight of the font info that has been resolved for the current font
3360 and fontSizeMode.
3361*/
3362
3363/*!
3364 \qmlproperty bool QtQuick::Text::fontInfo.italic
3365 \since 5.9
3366
3367 The italic state of the font info that has been resolved for the current font
3368 and fontSizeMode.
3369*/
3370
3371/*!
3372 \qmlproperty real QtQuick::Text::fontInfo.pointSize
3373 \since 5.9
3374
3375 The pointSize of the font info that has been resolved for the current font
3376 and fontSizeMode.
3377*/
3378
3379/*!
3380 \qmlproperty string QtQuick::Text::fontInfo.pixelSize
3381 \since 5.9
3382
3383 The pixel size of the font info that has been resolved for the current font
3384 and fontSizeMode.
3385*/
3386QJSValue QQuickText::fontInfo() const
3387{
3388 Q_D(const QQuickText);
3389
3390 QJSEngine *engine = qjsEngine(this);
3391 if (!engine) {
3392 qmlWarning(me: this) << "fontInfo: item has no JS engine";
3393 return QJSValue();
3394 }
3395
3396 QJSValue value = engine->newObject();
3397 value.setProperty(QStringLiteral("family"), value: d->fontInfo.family());
3398 value.setProperty(QStringLiteral("styleName"), value: d->fontInfo.styleName());
3399 value.setProperty(QStringLiteral("bold"), value: d->fontInfo.bold());
3400 value.setProperty(QStringLiteral("weight"), value: d->fontInfo.weight());
3401 value.setProperty(QStringLiteral("italic"), value: d->fontInfo.italic());
3402 value.setProperty(QStringLiteral("pointSize"), value: d->fontInfo.pointSizeF());
3403 value.setProperty(QStringLiteral("pixelSize"), value: d->fontInfo.pixelSize());
3404 return value;
3405}
3406
3407/*!
3408 \qmlproperty size QtQuick::Text::advance
3409 \since 5.10
3410
3411 The distance, in pixels, from the baseline origin of the first
3412 character of the text item, to the baseline origin of the first
3413 character in a text item occurring directly after this one
3414 in a text flow.
3415
3416 Note that the advance can be negative if the text flows from
3417 the right to the left.
3418*/
3419QSizeF QQuickText::advance() const
3420{
3421 Q_D(const QQuickText);
3422 return d->advance;
3423}
3424
3425QT_END_NAMESPACE
3426
3427#include "moc_qquicktext_p.cpp"
3428

source code of qtdeclarative/src/quick/items/qquicktext.cpp