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 "qpainter.h"
5#include "qevent.h"
6#include "qdrawutil.h"
7#include "qapplication.h"
8#if QT_CONFIG(abstractbutton)
9#include "qabstractbutton.h"
10#endif
11#include "qstyle.h"
12#include "qstyleoption.h"
13#include <limits.h>
14#include "qclipboard.h"
15#include <qdebug.h>
16#include <qurl.h>
17#include "qlabel_p.h"
18#include "private/qstylesheetstyle_p.h"
19#include <qmath.h>
20
21#if QT_CONFIG(accessibility)
22#include <qaccessible.h>
23#endif
24
25QT_BEGIN_NAMESPACE
26
27using namespace Qt::StringLiterals;
28
29QLabelPrivate::QLabelPrivate()
30 : QFramePrivate(),
31 sh(),
32 msh(),
33 text(),
34 pixmap(),
35 scaledpixmap(),
36 cachedimage(),
37#ifndef QT_NO_PICTURE
38 picture(),
39#endif
40#if QT_CONFIG(movie)
41 movie(),
42#endif
43 control(nullptr),
44 shortcutCursor(),
45#ifndef QT_NO_CURSOR
46 cursor(),
47#endif
48#ifndef QT_NO_SHORTCUT
49 buddy(),
50 shortcutId(0),
51#endif
52 textformat(Qt::AutoText),
53 effectiveTextFormat(Qt::PlainText),
54 textInteractionFlags(Qt::LinksAccessibleByMouse),
55 sizePolicy(),
56 margin(0),
57 align(Qt::AlignLeft | Qt::AlignVCenter | Qt::TextExpandTabs),
58 indent(-1),
59 valid_hints(false),
60 scaledcontents(false),
61 textLayoutDirty(false),
62 textDirty(false),
63 isTextLabel(false),
64 hasShortcut(/*???*/),
65#ifndef QT_NO_CURSOR
66 validCursor(false),
67 onAnchor(false),
68#endif
69 openExternalLinks(false),
70 resourceProvider(nullptr)
71{
72}
73
74QLabelPrivate::~QLabelPrivate()
75{
76}
77
78/*!
79 \class QLabel
80 \brief The QLabel widget provides a text or image display.
81
82 \ingroup basicwidgets
83 \inmodule QtWidgets
84
85 \image windows-label.png
86
87 QLabel is used for displaying text or an image. No user
88 interaction functionality is provided. The visual appearance of
89 the label can be configured in various ways, and it can be used
90 for specifying a focus mnemonic key for another widget.
91
92 A QLabel can contain any of the following content types:
93
94 \table
95 \header \li Content \li Setting
96 \row \li Plain text
97 \li Pass a QString to setText().
98 \row \li Rich text
99 \li Pass a QString that contains rich text to setText().
100 \row \li A pixmap
101 \li Pass a QPixmap to setPixmap().
102 \row \li A movie
103 \li Pass a QMovie to setMovie().
104 \row \li A number
105 \li Pass an \e int or a \e double to setNum(), which converts
106 the number to plain text.
107 \row \li Nothing
108 \li The same as an empty plain text. This is the default. Set
109 by clear().
110 \endtable
111
112 \warning When passing a QString to the constructor or calling setText(),
113 make sure to sanitize your input, as QLabel tries to guess whether it
114 displays the text as plain text or as rich text, a subset of HTML 4
115 markup. You may want to call
116 setTextFormat() explicitly, e.g. in case you expect the text to be in
117 plain format but cannot control the text source (for instance when
118 displaying data loaded from the Web).
119
120 When the content is changed using any of these functions, any
121 previous content is cleared.
122
123 By default, labels display \l{alignment}{left-aligned, vertically-centered}
124 text and images, where any tabs in the text to be displayed are
125 \l{Qt::TextExpandTabs}{automatically expanded}. However, the look
126 of a QLabel can be adjusted and fine-tuned in several ways.
127
128 The positioning of the content within the QLabel widget area can
129 be tuned with setAlignment() and setIndent(). Text content can
130 also wrap lines along word boundaries with setWordWrap(). For
131 example, this code sets up a sunken panel with a two-line text in
132 the bottom right corner (both lines being flush with the right
133 side of the label):
134
135 \snippet code/src_gui_widgets_qlabel.cpp 0
136
137 The properties and functions QLabel inherits from QFrame can also
138 be used to specify the widget frame to be used for any given label.
139
140 A QLabel is often used as a label for an interactive widget. For
141 this use QLabel provides a useful mechanism for adding an
142 mnemonic (see QKeySequence) that will set the keyboard focus to
143 the other widget (called the QLabel's "buddy"). For example:
144
145 \snippet code/src_gui_widgets_qlabel.cpp 1
146
147 In this example, keyboard focus is transferred to the label's
148 buddy (the QLineEdit) when the user presses Alt+P. If the buddy
149 was a button (inheriting from QAbstractButton), triggering the
150 mnemonic would emulate a button click.
151
152 \sa QLineEdit, QTextEdit, QPixmap, QMovie
153*/
154
155#ifndef QT_NO_PICTURE
156/*!
157 \fn QPicture QLabel::picture(Qt::ReturnByValueConstant) const
158 \deprecated Use the overload without argument instead.
159 \since 5.15
160
161 Returns the label's picture.
162
163 Previously, Qt provided a version of \c picture() which returned the picture
164 by-pointer. That version is now removed. This overload allowed to
165 explicitly differentiate between the by-pointer function and the by-value.
166*/
167
168/*!
169 \since 6.0
170
171 Returns the label's picture.
172*/
173QPicture QLabel::picture() const
174{
175 Q_D(const QLabel);
176 if (d->picture)
177 return *(d->picture);
178 return QPicture();
179}
180#endif // QT_NO_PICTURE
181
182
183/*!
184 Constructs an empty label.
185
186 The \a parent and widget flag \a f, arguments are passed
187 to the QFrame constructor.
188
189 \sa setAlignment(), setFrameStyle(), setIndent()
190*/
191QLabel::QLabel(QWidget *parent, Qt::WindowFlags f)
192 : QFrame(*new QLabelPrivate(), parent, f)
193{
194 Q_D(QLabel);
195 d->init();
196}
197
198/*!
199 Constructs a label that displays the text, \a text.
200
201 The \a parent and widget flag \a f, arguments are passed
202 to the QFrame constructor.
203
204 \sa setText(), setAlignment(), setFrameStyle(), setIndent()
205*/
206QLabel::QLabel(const QString &text, QWidget *parent, Qt::WindowFlags f)
207 : QLabel(parent, f)
208{
209 setText(text);
210}
211
212
213
214/*!
215 Destroys the label.
216*/
217
218QLabel::~QLabel()
219{
220 Q_D(QLabel);
221 d->clearContents();
222}
223
224void QLabelPrivate::init()
225{
226 Q_Q(QLabel);
227
228 q->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred,
229 QSizePolicy::Label));
230 setLayoutItemMargins(element: QStyle::SE_LabelLayoutItem);
231}
232
233
234/*!
235 \property QLabel::text
236 \brief the label's text
237
238 If no text has been set this will return an empty string. Setting
239 the text clears any previous content.
240
241 The text will be interpreted either as plain text or as rich
242 text, depending on the text format setting; see setTextFormat().
243 The default setting is Qt::AutoText; i.e. QLabel will try to
244 auto-detect the format of the text set.
245 See \l {Supported HTML Subset} for the definition of rich text.
246
247 If a buddy has been set, the buddy mnemonic key is updated
248 from the new text.
249
250 Note that QLabel is well-suited to display small rich text
251 documents, such as small documents that get their document
252 specific settings (font, text color, link color) from the label's
253 palette and font properties. For large documents, use QTextEdit
254 in read-only mode instead. QTextEdit can also provide a scroll bar
255 when necessary.
256
257 \note This function enables mouse tracking if \a text contains rich
258 text.
259
260 \sa setTextFormat(), setBuddy(), alignment
261*/
262
263void QLabel::setText(const QString &text)
264{
265 Q_D(QLabel);
266 if (d->text == text)
267 return;
268
269 QWidgetTextControl *oldControl = d->control;
270 d->control = nullptr;
271
272 d->clearContents();
273 d->text = text;
274 d->isTextLabel = true;
275 d->textDirty = true;
276 if (d->textformat == Qt::AutoText) {
277 if (Qt::mightBeRichText(d->text))
278 d->effectiveTextFormat = Qt::RichText;
279 else
280 d->effectiveTextFormat = Qt::PlainText;
281 } else {
282 d->effectiveTextFormat = d->textformat;
283 }
284
285 d->control = oldControl;
286
287 if (d->needTextControl()) {
288 d->ensureTextControl();
289 } else {
290 delete d->control;
291 d->control = nullptr;
292 }
293
294 if (d->effectiveTextFormat != Qt::PlainText) {
295 setMouseTracking(true);
296 } else {
297 // Note: mouse tracking not disabled intentionally
298 }
299
300#ifndef QT_NO_SHORTCUT
301 if (d->buddy)
302 d->updateShortcut();
303#endif
304
305 d->updateLabel();
306
307#if QT_CONFIG(accessibility)
308 if (accessibleName().isEmpty()) {
309 QAccessibleEvent event(this, QAccessible::NameChanged);
310 QAccessible::updateAccessibility(event: &event);
311 }
312#endif
313}
314
315QString QLabel::text() const
316{
317 Q_D(const QLabel);
318 return d->text;
319}
320
321/*!
322 Clears any label contents.
323*/
324
325void QLabel::clear()
326{
327 Q_D(QLabel);
328 d->clearContents();
329 d->updateLabel();
330}
331
332/*!
333 \property QLabel::pixmap
334 \brief the label's pixmap.
335
336 Setting the pixmap clears any previous content. The buddy
337 shortcut, if any, is disabled.
338*/
339void QLabel::setPixmap(const QPixmap &pixmap)
340{
341 Q_D(QLabel);
342 if (!d->pixmap || d->pixmap->cacheKey() != pixmap.cacheKey()) {
343 d->clearContents();
344 d->pixmap = pixmap;
345 }
346
347 d->updateLabel();
348}
349
350QPixmap QLabel::pixmap() const
351{
352 Q_D(const QLabel);
353 if (d->pixmap)
354 return *(d->pixmap);
355 return QPixmap();
356}
357
358/*!
359 \fn QPixmap QLabel::pixmap(Qt::ReturnByValueConstant) const
360
361 \deprecated Use the overload without argument instead.
362 \since 5.15
363
364 Returns the label's pixmap.
365
366 Previously, Qt provided a version of \c pixmap() which returned the pixmap
367 by-pointer. That version has now been removed. This overload allowed to
368 explicitly differentiate between the by-pointer function and the by-value.
369
370 \code
371 QPixmap pixmapVal = label->pixmap(Qt::ReturnByValue);
372 \endcode
373*/
374
375#ifndef QT_NO_PICTURE
376/*!
377 Sets the label contents to \a picture. Any previous content is
378 cleared.
379
380 The buddy shortcut, if any, is disabled.
381
382 \sa picture(), setBuddy()
383*/
384
385void QLabel::setPicture(const QPicture &picture)
386{
387 Q_D(QLabel);
388 d->clearContents();
389 d->picture = picture;
390
391 d->updateLabel();
392}
393#endif // QT_NO_PICTURE
394
395/*!
396 Sets the label contents to plain text containing the textual
397 representation of integer \a num. Any previous content is cleared.
398 Does nothing if the integer's string representation is the same as
399 the current contents of the label.
400
401 The buddy shortcut, if any, is disabled.
402
403 \sa setText(), QString::setNum(), setBuddy()
404*/
405
406void QLabel::setNum(int num)
407{
408 QString str;
409 str.setNum(n: num);
410 setText(str);
411}
412
413/*!
414 \overload
415
416 Sets the label contents to plain text containing the textual
417 representation of double \a num. Any previous content is cleared.
418 Does nothing if the double's string representation is the same as
419 the current contents of the label.
420
421 The buddy shortcut, if any, is disabled.
422
423 \sa setText(), QString::setNum(), setBuddy()
424*/
425
426void QLabel::setNum(double num)
427{
428 QString str;
429 str.setNum(num);
430 setText(str);
431}
432
433/*!
434 \property QLabel::alignment
435 \brief the alignment of the label's contents
436
437 By default, the contents of the label are left-aligned and vertically-centered.
438
439 \sa text
440*/
441
442void QLabel::setAlignment(Qt::Alignment alignment)
443{
444 Q_D(QLabel);
445 if (alignment == (d->align & (Qt::AlignVertical_Mask|Qt::AlignHorizontal_Mask)))
446 return;
447 d->align = (d->align & ~(Qt::AlignVertical_Mask|Qt::AlignHorizontal_Mask))
448 | (alignment & (Qt::AlignVertical_Mask|Qt::AlignHorizontal_Mask));
449
450 d->updateLabel();
451}
452
453
454Qt::Alignment QLabel::alignment() const
455{
456 Q_D(const QLabel);
457 return QFlag(d->align & (Qt::AlignVertical_Mask|Qt::AlignHorizontal_Mask));
458}
459
460
461/*!
462 \property QLabel::wordWrap
463 \brief the label's word-wrapping policy
464
465 If this property is \c true then label text is wrapped where
466 necessary at word-breaks; otherwise it is not wrapped at all.
467
468 By default, word wrap is disabled.
469
470 \sa text
471*/
472void QLabel::setWordWrap(bool on)
473{
474 Q_D(QLabel);
475 if (on)
476 d->align |= Qt::TextWordWrap;
477 else
478 d->align &= ~Qt::TextWordWrap;
479
480 d->updateLabel();
481}
482
483bool QLabel::wordWrap() const
484{
485 Q_D(const QLabel);
486 return d->align & Qt::TextWordWrap;
487}
488
489/*!
490 \property QLabel::indent
491 \brief the label's text indent in pixels
492
493 If a label displays text, the indent applies to the left edge if
494 alignment() is Qt::AlignLeft, to the right edge if alignment() is
495 Qt::AlignRight, to the top edge if alignment() is Qt::AlignTop, and
496 to the bottom edge if alignment() is Qt::AlignBottom.
497
498 If indent is negative, or if no indent has been set, the label
499 computes the effective indent as follows: If frameWidth() is 0,
500 the effective indent becomes 0. If frameWidth() is greater than 0,
501 the effective indent becomes half the width of the "x" character
502 of the widget's current font().
503
504 By default, the indent is -1, meaning that an effective indent is
505 calculating in the manner described above.
506
507 \sa alignment, margin, frameWidth(), font()
508*/
509
510void QLabel::setIndent(int indent)
511{
512 Q_D(QLabel);
513 d->indent = indent;
514 d->updateLabel();
515}
516
517int QLabel::indent() const
518{
519 Q_D(const QLabel);
520 return d->indent;
521}
522
523
524/*!
525 \property QLabel::margin
526 \brief the width of the margin
527
528 The margin is the distance between the innermost pixel of the
529 frame and the outermost pixel of contents.
530
531 The default margin is 0.
532
533 \sa indent
534*/
535int QLabel::margin() const
536{
537 Q_D(const QLabel);
538 return d->margin;
539}
540
541void QLabel::setMargin(int margin)
542{
543 Q_D(QLabel);
544 if (d->margin == margin)
545 return;
546 d->margin = margin;
547 d->updateLabel();
548}
549
550/*!
551 Returns the size that will be used if the width of the label is \a
552 w. If \a w is -1, the sizeHint() is returned. If \a w is 0 minimumSizeHint() is returned
553*/
554QSize QLabelPrivate::sizeForWidth(int w) const
555{
556 Q_Q(const QLabel);
557 if (q->minimumWidth() > 0)
558 w = qMax(a: w, b: q->minimumWidth());
559 QSize contentsMargin(leftmargin + rightmargin, topmargin + bottommargin);
560
561 QRect br;
562
563 int hextra = 2 * margin;
564 int vextra = hextra;
565 QFontMetrics fm = q->fontMetrics();
566
567 if (pixmap && !pixmap->isNull()) {
568 br = pixmap->rect();
569 br.setSize(pixmap->deviceIndependentSize().toSize());
570#ifndef QT_NO_PICTURE
571 } else if (picture && !picture->isNull()) {
572 br = picture->boundingRect();
573#endif
574#if QT_CONFIG(movie)
575 } else if (movie && !movie->currentPixmap().isNull()) {
576 br = movie->currentPixmap().rect();
577 br.setSize(movie->currentPixmap().deviceIndependentSize().toSize());
578#endif
579 } else if (isTextLabel) {
580 int align = QStyle::visualAlignment(direction: textDirection(), alignment: QFlag(this->align));
581 // Add indentation
582 int m = indent;
583
584 if (m < 0 && q->frameWidth()) // no indent, but we do have a frame
585 m = fm.horizontalAdvance(u'x') - margin*2;
586 if (m > 0) {
587 if ((align & Qt::AlignLeft) || (align & Qt::AlignRight))
588 hextra += m;
589 if ((align & Qt::AlignTop) || (align & Qt::AlignBottom))
590 vextra += m;
591 }
592
593 if (control) {
594 ensureTextLayouted();
595 const qreal oldTextWidth = control->textWidth();
596 // Calculate the length of document if w is the width
597 if (align & Qt::TextWordWrap) {
598 if (w >= 0) {
599 w = qMax(a: w-hextra-contentsMargin.width(), b: 0); // strip margin and indent
600 control->setTextWidth(w);
601 } else {
602 control->adjustSize();
603 }
604 } else {
605 control->setTextWidth(-1);
606 }
607
608 QSizeF controlSize = control->size();
609 br = QRect(QPoint(0, 0), QSize(qCeil(v: controlSize.width()), qCeil(v: controlSize.height())));
610
611 // restore state
612 control->setTextWidth(oldTextWidth);
613 } else {
614 // Turn off center alignment in order to avoid rounding errors for centering,
615 // since centering involves a division by 2. At the end, all we want is the size.
616 int flags = align & ~(Qt::AlignVCenter | Qt::AlignHCenter);
617 if (hasShortcut) {
618 flags |= Qt::TextShowMnemonic;
619 QStyleOption opt;
620 opt.initFrom(w: q);
621 if (!q->style()->styleHint(stylehint: QStyle::SH_UnderlineShortcut, opt: &opt, widget: q))
622 flags |= Qt::TextHideMnemonic;
623 }
624
625 bool tryWidth = (w < 0) && (align & Qt::TextWordWrap);
626 if (tryWidth)
627 w = qMin(a: fm.averageCharWidth() * 80, b: q->maximumSize().width());
628 else if (w < 0)
629 w = 2000;
630 w -= (hextra + contentsMargin.width());
631 br = fm.boundingRect(x: 0, y: 0, w ,h: 2000, flags, text);
632 if (tryWidth && br.height() < 4*fm.lineSpacing() && br.width() > w/2)
633 br = fm.boundingRect(x: 0, y: 0, w: w/2, h: 2000, flags, text);
634 if (tryWidth && br.height() < 2*fm.lineSpacing() && br.width() > w/4)
635 br = fm.boundingRect(x: 0, y: 0, w: w/4, h: 2000, flags, text);
636 }
637 } else {
638 br = QRect(QPoint(0, 0), QSize(fm.averageCharWidth(), fm.lineSpacing()));
639 }
640
641 const QSize contentsSize(br.width() + hextra, br.height() + vextra);
642 return (contentsSize + contentsMargin).expandedTo(otherSize: q->minimumSize());
643}
644
645
646/*!
647 \reimp
648*/
649
650int QLabel::heightForWidth(int w) const
651{
652 Q_D(const QLabel);
653 if (d->isTextLabel)
654 return d->sizeForWidth(w).height();
655 return QWidget::heightForWidth(w);
656}
657
658/*!
659 \property QLabel::openExternalLinks
660 \since 4.2
661
662 Specifies whether QLabel should automatically open links using
663 QDesktopServices::openUrl() instead of emitting the
664 linkActivated() signal.
665
666 \b{Note:} The textInteractionFlags set on the label need to include
667 either LinksAccessibleByMouse or LinksAccessibleByKeyboard.
668
669 The default value is false.
670
671 \sa textInteractionFlags()
672*/
673bool QLabel::openExternalLinks() const
674{
675 Q_D(const QLabel);
676 return d->openExternalLinks;
677}
678
679void QLabel::setOpenExternalLinks(bool open)
680{
681 Q_D(QLabel);
682 d->openExternalLinks = open;
683 if (d->control)
684 d->control->setOpenExternalLinks(open);
685}
686
687/*!
688 \property QLabel::textInteractionFlags
689 \since 4.2
690
691 Specifies how the label should interact with user input if it displays text.
692
693 If the flags contain Qt::LinksAccessibleByKeyboard the focus policy is also
694 automatically set to Qt::StrongFocus. If Qt::TextSelectableByKeyboard is set
695 then the focus policy is set to Qt::ClickFocus.
696
697 The default value is Qt::LinksAccessibleByMouse.
698*/
699void QLabel::setTextInteractionFlags(Qt::TextInteractionFlags flags)
700{
701 Q_D(QLabel);
702 if (d->textInteractionFlags == flags)
703 return;
704 d->textInteractionFlags = flags;
705 if (flags & Qt::LinksAccessibleByKeyboard)
706 setFocusPolicy(Qt::StrongFocus);
707 else if (flags & (Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse))
708 setFocusPolicy(Qt::ClickFocus);
709 else
710 setFocusPolicy(Qt::NoFocus);
711
712 if (d->needTextControl()) {
713 d->ensureTextControl();
714 } else {
715 delete d->control;
716 d->control = nullptr;
717 }
718
719 if (d->control)
720 d->control->setTextInteractionFlags(d->textInteractionFlags);
721}
722
723Qt::TextInteractionFlags QLabel::textInteractionFlags() const
724{
725 Q_D(const QLabel);
726 return d->textInteractionFlags;
727}
728
729/*!
730 Selects text from position \a start and for \a length characters.
731
732 \sa selectedText()
733
734 \b{Note:} The textInteractionFlags set on the label need to include
735 either TextSelectableByMouse or TextSelectableByKeyboard.
736
737 \since 4.7
738*/
739void QLabel::setSelection(int start, int length)
740{
741 Q_D(QLabel);
742 if (d->control) {
743 d->ensureTextPopulated();
744 QTextCursor cursor = d->control->textCursor();
745 cursor.setPosition(pos: start);
746 cursor.setPosition(pos: start + length, mode: QTextCursor::KeepAnchor);
747 d->control->setTextCursor(cursor);
748 }
749}
750
751/*!
752 \property QLabel::hasSelectedText
753 \brief whether there is any text selected
754
755 hasSelectedText() returns \c true if some or all of the text has been
756 selected by the user; otherwise returns \c false.
757
758 By default, this property is \c false.
759
760 \sa selectedText()
761
762 \b{Note:} The textInteractionFlags set on the label need to include
763 either TextSelectableByMouse or TextSelectableByKeyboard.
764
765 \since 4.7
766*/
767bool QLabel::hasSelectedText() const
768{
769 Q_D(const QLabel);
770 if (d->control)
771 return d->control->textCursor().hasSelection();
772 return false;
773}
774
775/*!
776 \property QLabel::selectedText
777 \brief the selected text
778
779 If there is no selected text this property's value is
780 an empty string.
781
782 By default, this property contains an empty string.
783
784 \sa hasSelectedText()
785
786 \b{Note:} The textInteractionFlags set on the label need to include
787 either TextSelectableByMouse or TextSelectableByKeyboard.
788
789 \since 4.7
790*/
791QString QLabel::selectedText() const
792{
793 Q_D(const QLabel);
794 if (d->control)
795 return d->control->textCursor().selectedText();
796 return QString();
797}
798
799/*!
800 selectionStart() returns the index of the first selected character in the
801 label or -1 if no text is selected.
802
803 \sa selectedText()
804
805 \b{Note:} The textInteractionFlags set on the label need to include
806 either TextSelectableByMouse or TextSelectableByKeyboard.
807
808 \since 4.7
809*/
810int QLabel::selectionStart() const
811{
812 Q_D(const QLabel);
813 if (d->control && d->control->textCursor().hasSelection())
814 return d->control->textCursor().selectionStart();
815 return -1;
816}
817
818/*!\reimp
819*/
820QSize QLabel::sizeHint() const
821{
822 Q_D(const QLabel);
823 if (!d->valid_hints)
824 (void) QLabel::minimumSizeHint();
825 return d->sh;
826}
827
828/*!
829 \reimp
830*/
831QSize QLabel::minimumSizeHint() const
832{
833 Q_D(const QLabel);
834 if (d->valid_hints) {
835 if (d->sizePolicy == sizePolicy())
836 return d->msh;
837 }
838
839 ensurePolished();
840 d->valid_hints = true;
841 d->sh = d->sizeForWidth(w: -1); // wrap ? golden ratio : min doc size
842 QSize msh(-1, -1);
843
844 if (!d->isTextLabel) {
845 msh = d->sh;
846 } else {
847 msh.rheight() = d->sizeForWidth(QWIDGETSIZE_MAX).height(); // height for one line
848 msh.rwidth() = d->sizeForWidth(w: 0).width(); // wrap ? size of biggest word : min doc size
849 if (d->sh.height() < msh.height())
850 msh.rheight() = d->sh.height();
851 }
852 d->msh = msh;
853 d->sizePolicy = sizePolicy();
854 return msh;
855}
856
857/*!\reimp
858*/
859void QLabel::mousePressEvent(QMouseEvent *ev)
860{
861 Q_D(QLabel);
862 d->sendControlEvent(e: ev);
863}
864
865/*!\reimp
866*/
867void QLabel::mouseMoveEvent(QMouseEvent *ev)
868{
869 Q_D(QLabel);
870 d->sendControlEvent(e: ev);
871}
872
873/*!\reimp
874*/
875void QLabel::mouseReleaseEvent(QMouseEvent *ev)
876{
877 Q_D(QLabel);
878 d->sendControlEvent(e: ev);
879}
880
881#ifndef QT_NO_CONTEXTMENU
882/*!\reimp
883*/
884void QLabel::contextMenuEvent(QContextMenuEvent *ev)
885{
886 Q_D(QLabel);
887 if (!d->isTextLabel) {
888 ev->ignore();
889 return;
890 }
891 QMenu *menu = d->createStandardContextMenu(pos: ev->pos());
892 if (!menu) {
893 ev->ignore();
894 return;
895 }
896 ev->accept();
897 menu->setAttribute(Qt::WA_DeleteOnClose);
898 menu->popup(pos: ev->globalPos());
899}
900#endif // QT_NO_CONTEXTMENU
901
902/*!
903 \reimp
904*/
905void QLabel::focusInEvent(QFocusEvent *ev)
906{
907 Q_D(QLabel);
908 if (d->isTextLabel) {
909 d->ensureTextControl();
910 d->sendControlEvent(e: ev);
911 }
912 QFrame::focusInEvent(event: ev);
913}
914
915/*!
916 \reimp
917*/
918void QLabel::focusOutEvent(QFocusEvent *ev)
919{
920 Q_D(QLabel);
921 if (d->control) {
922 d->sendControlEvent(e: ev);
923 QTextCursor cursor = d->control->textCursor();
924 Qt::FocusReason reason = ev->reason();
925 if (reason != Qt::ActiveWindowFocusReason
926 && reason != Qt::PopupFocusReason
927 && cursor.hasSelection()) {
928 cursor.clearSelection();
929 d->control->setTextCursor(cursor);
930 }
931 }
932
933 QFrame::focusOutEvent(event: ev);
934}
935
936/*!\reimp
937*/
938bool QLabel::focusNextPrevChild(bool next)
939{
940 Q_D(QLabel);
941 if (d->control && d->control->setFocusToNextOrPreviousAnchor(next))
942 return true;
943 return QFrame::focusNextPrevChild(next);
944}
945
946/*!\reimp
947*/
948void QLabel::keyPressEvent(QKeyEvent *ev)
949{
950 Q_D(QLabel);
951 d->sendControlEvent(e: ev);
952}
953
954/*!\reimp
955*/
956bool QLabel::event(QEvent *e)
957{
958 Q_D(QLabel);
959 QEvent::Type type = e->type();
960
961#ifndef QT_NO_SHORTCUT
962 if (type == QEvent::Shortcut) {
963 QShortcutEvent *se = static_cast<QShortcutEvent *>(e);
964 if (se->shortcutId() == d->shortcutId) {
965 QWidget *w = d->buddy;
966 if (!w)
967 return QFrame::event(e);
968 if (w->focusPolicy() != Qt::NoFocus)
969 w->setFocus(Qt::ShortcutFocusReason);
970#if QT_CONFIG(abstractbutton)
971 QAbstractButton *button = qobject_cast<QAbstractButton *>(object: w);
972 if (button && !se->isAmbiguous())
973 button->animateClick();
974 else
975#endif
976 window()->setAttribute(Qt::WA_KeyboardFocusChange);
977 return true;
978 }
979 } else
980#endif
981 if (type == QEvent::Resize) {
982 if (d->control)
983 d->textLayoutDirty = true;
984 } else if (e->type() == QEvent::StyleChange
985#ifdef Q_OS_MAC
986 || e->type() == QEvent::MacSizeChange
987#endif
988 ) {
989 d->setLayoutItemMargins(element: QStyle::SE_LabelLayoutItem);
990 d->updateLabel();
991 } else if (type == QEvent::Polish) {
992 if (d->needTextControl())
993 d->ensureTextControl();
994 }
995
996 return QFrame::event(e);
997}
998
999/*!\reimp
1000*/
1001void QLabel::paintEvent(QPaintEvent *)
1002{
1003 Q_D(QLabel);
1004 QStyle *style = QWidget::style();
1005 QPainter painter(this);
1006 drawFrame(&painter);
1007 QRect cr = contentsRect();
1008 cr.adjust(dx1: d->margin, dy1: d->margin, dx2: -d->margin, dy2: -d->margin);
1009 int align = QStyle::visualAlignment(direction: d->isTextLabel ? d->textDirection()
1010 : layoutDirection(), alignment: QFlag(d->align));
1011
1012#if QT_CONFIG(movie)
1013 if (d->movie && !d->movie->currentPixmap().isNull()) {
1014 if (d->scaledcontents)
1015 style->drawItemPixmap(painter: &painter, rect: cr, alignment: align, pixmap: d->movie->currentPixmap().scaled(s: cr.size()));
1016 else
1017 style->drawItemPixmap(painter: &painter, rect: cr, alignment: align, pixmap: d->movie->currentPixmap());
1018 }
1019 else
1020#endif
1021 if (d->isTextLabel) {
1022 QRectF lr = d->layoutRect().toAlignedRect();
1023 QStyleOption opt;
1024 opt.initFrom(w: this);
1025#ifndef QT_NO_STYLE_STYLESHEET
1026 if (QStyleSheetStyle* cssStyle = qt_styleSheet(style))
1027 cssStyle->styleSheetPalette(w: this, opt: &opt, pal: &opt.palette);
1028#endif
1029 if (d->control) {
1030#ifndef QT_NO_SHORTCUT
1031 const bool underline = static_cast<bool>(style->styleHint(stylehint: QStyle::SH_UnderlineShortcut,
1032 opt: nullptr, widget: this, returnData: nullptr));
1033 if (d->shortcutId != 0
1034 && underline != d->shortcutCursor.charFormat().fontUnderline()) {
1035 QTextCharFormat fmt;
1036 fmt.setFontUnderline(underline);
1037 d->shortcutCursor.mergeCharFormat(modifier: fmt);
1038 }
1039#endif
1040 d->ensureTextLayouted();
1041
1042 QAbstractTextDocumentLayout::PaintContext context;
1043 // Adjust the palette
1044 context.palette = opt.palette;
1045
1046 if (foregroundRole() != QPalette::Text && isEnabled())
1047 context.palette.setColor(acr: QPalette::Text, acolor: context.palette.color(cr: foregroundRole()));
1048
1049 painter.save();
1050 painter.translate(offset: lr.topLeft());
1051 painter.setClipRect(lr.translated(dx: -lr.x(), dy: -lr.y()));
1052 d->control->setPalette(context.palette);
1053 d->control->drawContents(painter: &painter, rect: QRectF(), widget: this);
1054 painter.restore();
1055 } else {
1056 int flags = align | (d->textDirection() == Qt::LeftToRight ? Qt::TextForceLeftToRight
1057 : Qt::TextForceRightToLeft);
1058 if (d->hasShortcut) {
1059 flags |= Qt::TextShowMnemonic;
1060 if (!style->styleHint(stylehint: QStyle::SH_UnderlineShortcut, opt: &opt, widget: this))
1061 flags |= Qt::TextHideMnemonic;
1062 }
1063 style->drawItemText(painter: &painter, rect: lr.toRect(), flags, pal: opt.palette, enabled: isEnabled(), text: d->text, textRole: foregroundRole());
1064 }
1065 } else
1066#ifndef QT_NO_PICTURE
1067 if (d->picture) {
1068 QRect br = d->picture->boundingRect();
1069 int rw = br.width();
1070 int rh = br.height();
1071 if (d->scaledcontents) {
1072 painter.save();
1073 painter.translate(dx: cr.x(), dy: cr.y());
1074 painter.scale(sx: (double)cr.width()/rw, sy: (double)cr.height()/rh);
1075 painter.drawPicture(x: -br.x(), y: -br.y(), p: *d->picture);
1076 painter.restore();
1077 } else {
1078 int xo = 0;
1079 int yo = 0;
1080 if (align & Qt::AlignVCenter)
1081 yo = (cr.height()-rh)/2;
1082 else if (align & Qt::AlignBottom)
1083 yo = cr.height()-rh;
1084 if (align & Qt::AlignRight)
1085 xo = cr.width()-rw;
1086 else if (align & Qt::AlignHCenter)
1087 xo = (cr.width()-rw)/2;
1088 painter.drawPicture(x: cr.x()+xo-br.x(), y: cr.y()+yo-br.y(), p: *d->picture);
1089 }
1090 } else
1091#endif
1092 if (d->pixmap && !d->pixmap->isNull()) {
1093 QPixmap pix;
1094 const qreal dpr = devicePixelRatio();
1095 if (d->scaledcontents || dpr != d->pixmap->devicePixelRatio()) {
1096 QSize scaledSize = d->scaledcontents ? (cr.size() * dpr)
1097 : (d->pixmap->size() * (dpr / d->pixmap->devicePixelRatio()));
1098 if (!d->scaledpixmap || d->scaledpixmap->size() != scaledSize) {
1099 if (!d->cachedimage)
1100 d->cachedimage = d->pixmap->toImage();
1101 d->scaledpixmap.reset();
1102 QImage scaledImage =
1103 d->cachedimage->scaled(s: scaledSize,
1104 aspectMode: Qt::IgnoreAspectRatio, mode: Qt::SmoothTransformation);
1105 d->scaledpixmap = QPixmap::fromImage(image: std::move(scaledImage));
1106 d->scaledpixmap->setDevicePixelRatio(dpr);
1107 }
1108 pix = *d->scaledpixmap;
1109 } else
1110 pix = *d->pixmap;
1111 QStyleOption opt;
1112 opt.initFrom(w: this);
1113 if (!isEnabled())
1114 pix = style->generatedIconPixmap(iconMode: QIcon::Disabled, pixmap: pix, opt: &opt);
1115 style->drawItemPixmap(painter: &painter, rect: cr, alignment: align, pixmap: pix);
1116 }
1117}
1118
1119
1120/*!
1121 Updates the label, but not the frame.
1122*/
1123
1124void QLabelPrivate::updateLabel()
1125{
1126 Q_Q(QLabel);
1127 valid_hints = false;
1128
1129 if (isTextLabel) {
1130 QSizePolicy policy = q->sizePolicy();
1131 const bool wrap = align & Qt::TextWordWrap;
1132 policy.setHeightForWidth(wrap);
1133 if (policy != q->sizePolicy()) // ### should be replaced by WA_WState_OwnSizePolicy idiom
1134 q->setSizePolicy(policy);
1135 textLayoutDirty = true;
1136 }
1137 q->updateGeometry();
1138 q->update(q->contentsRect());
1139}
1140
1141#ifndef QT_NO_SHORTCUT
1142/*!
1143 Sets this label's buddy to \a buddy.
1144
1145 When the user presses the shortcut key indicated by this label,
1146 the keyboard focus is transferred to the label's buddy widget.
1147
1148 The buddy mechanism is only available for QLabels that contain
1149 text in which one character is prefixed with an ampersand, '&'.
1150 This character is set as the shortcut key. See the \l
1151 QKeySequence::mnemonic() documentation for details (to display an
1152 actual ampersand, use '&&').
1153
1154 In a dialog, you might create two data entry widgets and a label
1155 for each, and set up the geometry layout so each label is just to
1156 the left of its data entry widget (its "buddy"), for example:
1157 \snippet code/src_gui_widgets_qlabel.cpp 2
1158
1159 With the code above, the focus jumps to the Name field when the
1160 user presses Alt+N, and to the Phone field when the user presses
1161 Alt+P.
1162
1163 To unset a previously set buddy, call this function with \a buddy
1164 set to nullptr.
1165
1166 \sa buddy(), setText(), QShortcut, setAlignment()
1167*/
1168
1169void QLabel::setBuddy(QWidget *buddy)
1170{
1171 Q_D(QLabel);
1172
1173 if (d->buddy)
1174 disconnect(sender: d->buddy, SIGNAL(destroyed()), receiver: this, SLOT(_q_buddyDeleted()));
1175
1176 d->buddy = buddy;
1177
1178 if (buddy)
1179 connect(sender: buddy, SIGNAL(destroyed()), receiver: this, SLOT(_q_buddyDeleted()));
1180
1181 if (d->isTextLabel) {
1182 if (d->shortcutId)
1183 releaseShortcut(id: d->shortcutId);
1184 d->shortcutId = 0;
1185 d->textDirty = true;
1186 if (buddy)
1187 d->updateShortcut(); // grab new shortcut
1188 d->updateLabel();
1189 }
1190}
1191
1192
1193/*!
1194 Returns this label's buddy, or nullptr if no buddy is currently set.
1195
1196 \sa setBuddy()
1197*/
1198
1199QWidget * QLabel::buddy() const
1200{
1201 Q_D(const QLabel);
1202 return d->buddy;
1203}
1204
1205void QLabelPrivate::updateShortcut()
1206{
1207 Q_Q(QLabel);
1208 Q_ASSERT(shortcutId == 0);
1209 // Introduce an extra boolean to indicate the presence of a shortcut in the
1210 // text. We cannot use the shortcutId itself because on the mac mnemonics are
1211 // off by default, so QKeySequence::mnemonic always returns an empty sequence.
1212 // But then we do want to hide the ampersands, so we can't use shortcutId.
1213 hasShortcut = false;
1214
1215 if (!text.contains(c: u'&'))
1216 return;
1217 hasShortcut = true;
1218 shortcutId = q->grabShortcut(key: QKeySequence::mnemonic(text));
1219}
1220
1221
1222void QLabelPrivate::_q_buddyDeleted()
1223{
1224 Q_Q(QLabel);
1225 q->setBuddy(nullptr);
1226}
1227
1228#endif // QT_NO_SHORTCUT
1229
1230#if QT_CONFIG(movie)
1231void QLabelPrivate::_q_movieUpdated(const QRect& rect)
1232{
1233 Q_Q(QLabel);
1234 if (movie && movie->isValid()) {
1235 QRect r;
1236 if (scaledcontents) {
1237 QRect cr = q->contentsRect();
1238 QRect pixmapRect(cr.topLeft(), movie->currentPixmap().size());
1239 if (pixmapRect.isEmpty())
1240 return;
1241 r.setRect(ax: cr.left(), ay: cr.top(),
1242 aw: (rect.width() * cr.width()) / pixmapRect.width(),
1243 ah: (rect.height() * cr.height()) / pixmapRect.height());
1244 } else {
1245 r = q->style()->itemPixmapRect(r: q->contentsRect(), flags: align, pixmap: movie->currentPixmap());
1246 r.translate(dx: rect.x(), dy: rect.y());
1247 r.setWidth(qMin(a: r.width(), b: rect.width()));
1248 r.setHeight(qMin(a: r.height(), b: rect.height()));
1249 }
1250 q->update(r);
1251 }
1252}
1253
1254void QLabelPrivate::_q_movieResized(const QSize& size)
1255{
1256 Q_Q(QLabel);
1257 q->update(); //we need to refresh the whole background in case the new size is smaller
1258 valid_hints = false;
1259 _q_movieUpdated(rect: QRect(QPoint(0,0), size));
1260 q->updateGeometry();
1261}
1262
1263/*!
1264 Sets the label contents to \a movie. Any previous content is
1265 cleared. The label does NOT take ownership of the movie.
1266
1267 The buddy shortcut, if any, is disabled.
1268
1269 \sa movie(), setBuddy()
1270*/
1271
1272void QLabel::setMovie(QMovie *movie)
1273{
1274 Q_D(QLabel);
1275 d->clearContents();
1276
1277 if (!movie)
1278 return;
1279
1280 d->movie = movie;
1281 connect(sender: movie, SIGNAL(resized(QSize)), receiver: this, SLOT(_q_movieResized(QSize)));
1282 connect(sender: movie, SIGNAL(updated(QRect)), receiver: this, SLOT(_q_movieUpdated(QRect)));
1283
1284 // Assume that if the movie is running,
1285 // resize/update signals will come soon enough
1286 if (movie->state() != QMovie::Running)
1287 d->updateLabel();
1288}
1289
1290#endif // QT_CONFIG(movie)
1291
1292/*!
1293 \internal
1294
1295 Clears any contents, without updating/repainting the label.
1296*/
1297
1298void QLabelPrivate::clearContents()
1299{
1300 delete control;
1301 control = nullptr;
1302 isTextLabel = false;
1303 hasShortcut = false;
1304
1305#ifndef QT_NO_PICTURE
1306 picture.reset();
1307#endif
1308 scaledpixmap.reset();
1309 cachedimage.reset();
1310 pixmap.reset();
1311
1312 text.clear();
1313 Q_Q(QLabel);
1314#ifndef QT_NO_SHORTCUT
1315 if (shortcutId)
1316 q->releaseShortcut(id: shortcutId);
1317 shortcutId = 0;
1318#endif
1319#if QT_CONFIG(movie)
1320 if (movie) {
1321 QObject::disconnect(sender: movie, SIGNAL(resized(QSize)), receiver: q, SLOT(_q_movieResized(QSize)));
1322 QObject::disconnect(sender: movie, SIGNAL(updated(QRect)), receiver: q, SLOT(_q_movieUpdated(QRect)));
1323 }
1324 movie = nullptr;
1325#endif
1326#ifndef QT_NO_CURSOR
1327 if (onAnchor) {
1328 if (validCursor)
1329 q->setCursor(cursor);
1330 else
1331 q->unsetCursor();
1332 }
1333 validCursor = false;
1334 onAnchor = false;
1335#endif
1336}
1337
1338
1339#if QT_CONFIG(movie)
1340
1341/*!
1342 Returns a pointer to the label's movie, or nullptr if no movie has been
1343 set.
1344
1345 \sa setMovie()
1346*/
1347
1348QMovie *QLabel::movie() const
1349{
1350 Q_D(const QLabel);
1351 return d->movie;
1352}
1353
1354#endif // QT_CONFIG(movie)
1355
1356/*!
1357 \property QLabel::textFormat
1358 \brief the label's text format
1359
1360 See the Qt::TextFormat enum for an explanation of the possible
1361 options.
1362
1363 The default format is Qt::AutoText.
1364
1365 \sa text()
1366*/
1367
1368Qt::TextFormat QLabel::textFormat() const
1369{
1370 Q_D(const QLabel);
1371 return d->textformat;
1372}
1373
1374void QLabel::setTextFormat(Qt::TextFormat format)
1375{
1376 Q_D(QLabel);
1377 if (format != d->textformat) {
1378 d->textformat = format;
1379 QString t = d->text;
1380 if (!t.isNull()) {
1381 d->text.clear();
1382 setText(t);
1383 }
1384 }
1385}
1386
1387/*!
1388 \since 6.1
1389
1390 Returns the resource provider for rich text of this label.
1391*/
1392QTextDocument::ResourceProvider QLabel::resourceProvider() const
1393{
1394 Q_D(const QLabel);
1395 return d->control ? d->control->document()->resourceProvider() : d->resourceProvider;
1396}
1397
1398/*!
1399 \since 6.1
1400
1401 Sets the \a provider of resources for rich text of this label.
1402
1403 \note The label \e{does not} take ownership of the \a provider.
1404*/
1405void QLabel::setResourceProvider(const QTextDocument::ResourceProvider &provider)
1406{
1407 Q_D(QLabel);
1408 d->resourceProvider = provider;
1409 if (d->control != nullptr)
1410 d->control->document()->setResourceProvider(provider);
1411}
1412
1413/*!
1414 \reimp
1415*/
1416void QLabel::changeEvent(QEvent *ev)
1417{
1418 Q_D(QLabel);
1419 if (ev->type() == QEvent::FontChange || ev->type() == QEvent::ApplicationFontChange) {
1420 if (d->isTextLabel) {
1421 if (d->control)
1422 d->control->document()->setDefaultFont(font());
1423 d->updateLabel();
1424 }
1425 } else if (ev->type() == QEvent::PaletteChange && d->control) {
1426 d->control->setPalette(palette());
1427 } else if (ev->type() == QEvent::ContentsRectChange) {
1428 d->updateLabel();
1429 }
1430 QFrame::changeEvent(ev);
1431}
1432
1433/*!
1434 \property QLabel::scaledContents
1435 \brief whether the label will scale its contents to fill all
1436 available space.
1437
1438 When enabled and the label shows a pixmap, it will scale the
1439 pixmap to fill the available space.
1440
1441 This property's default is false.
1442*/
1443bool QLabel::hasScaledContents() const
1444{
1445 Q_D(const QLabel);
1446 return d->scaledcontents;
1447}
1448
1449void QLabel::setScaledContents(bool enable)
1450{
1451 Q_D(QLabel);
1452 if ((bool)d->scaledcontents == enable)
1453 return;
1454 d->scaledcontents = enable;
1455 if (!enable) {
1456 d->scaledpixmap.reset();
1457 d->cachedimage.reset();
1458 }
1459 update(contentsRect());
1460}
1461
1462Qt::LayoutDirection QLabelPrivate::textDirection() const
1463{
1464 if (control) {
1465 QTextOption opt = control->document()->defaultTextOption();
1466 return opt.textDirection();
1467 }
1468
1469 return text.isRightToLeft() ? Qt::RightToLeft : Qt::LeftToRight;
1470}
1471
1472
1473// Returns the rect that is available for us to draw the document
1474QRect QLabelPrivate::documentRect() const
1475{
1476 Q_Q(const QLabel);
1477 Q_ASSERT_X(isTextLabel, "documentRect", "document rect called for label that is not a text label!");
1478 QRect cr = q->contentsRect();
1479 cr.adjust(dx1: margin, dy1: margin, dx2: -margin, dy2: -margin);
1480 const int align = QStyle::visualAlignment(direction: isTextLabel ? textDirection()
1481 : q->layoutDirection(), alignment: QFlag(this->align));
1482 int m = indent;
1483 if (m < 0 && q->frameWidth()) // no indent, but we do have a frame
1484 m = q->fontMetrics().horizontalAdvance(u'x') / 2 - margin;
1485 if (m > 0) {
1486 if (align & Qt::AlignLeft)
1487 cr.setLeft(cr.left() + m);
1488 if (align & Qt::AlignRight)
1489 cr.setRight(cr.right() - m);
1490 if (align & Qt::AlignTop)
1491 cr.setTop(cr.top() + m);
1492 if (align & Qt::AlignBottom)
1493 cr.setBottom(cr.bottom() - m);
1494 }
1495 return cr;
1496}
1497
1498void QLabelPrivate::ensureTextPopulated() const
1499{
1500 if (!textDirty)
1501 return;
1502 if (control) {
1503 QTextDocument *doc = control->document();
1504 if (textDirty) {
1505 if (effectiveTextFormat == Qt::PlainText) {
1506 doc->setPlainText(text);
1507#if QT_CONFIG(texthtmlparser)
1508 } else if (effectiveTextFormat == Qt::RichText) {
1509 doc->setHtml(text);
1510#endif
1511#if QT_CONFIG(textmarkdownreader)
1512 } else if (effectiveTextFormat == Qt::MarkdownText) {
1513 doc->setMarkdown(markdown: text);
1514#endif
1515 } else {
1516 doc->setPlainText(text);
1517 }
1518 doc->setUndoRedoEnabled(false);
1519
1520#ifndef QT_NO_SHORTCUT
1521 if (hasShortcut) {
1522 // Underline the first character that follows an ampersand (and remove the others ampersands)
1523 int from = 0;
1524 bool found = false;
1525 QTextCursor cursor;
1526 while (!(cursor = control->document()->find(subString: ("&"_L1), from)).isNull()) {
1527 cursor.deleteChar(); // remove the ampersand
1528 cursor.movePosition(op: QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
1529 from = cursor.position();
1530 if (!found && cursor.selectedText() != "&"_L1) { //not a second &
1531 found = true;
1532 shortcutCursor = cursor;
1533 }
1534 }
1535 }
1536#endif
1537 }
1538 }
1539 textDirty = false;
1540}
1541
1542void QLabelPrivate::ensureTextLayouted() const
1543{
1544 if (!textLayoutDirty)
1545 return;
1546 ensureTextPopulated();
1547 if (control) {
1548 QTextDocument *doc = control->document();
1549 QTextOption opt = doc->defaultTextOption();
1550
1551 opt.setAlignment(QFlag(this->align));
1552
1553 if (this->align & Qt::TextWordWrap)
1554 opt.setWrapMode(QTextOption::WordWrap);
1555 else
1556 opt.setWrapMode(QTextOption::ManualWrap);
1557
1558 doc->setDefaultTextOption(opt);
1559
1560 QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
1561 fmt.setMargin(0);
1562 doc->rootFrame()->setFrameFormat(fmt);
1563 doc->setTextWidth(documentRect().width());
1564 }
1565 textLayoutDirty = false;
1566}
1567
1568void QLabelPrivate::ensureTextControl() const
1569{
1570 Q_Q(const QLabel);
1571 if (!isTextLabel)
1572 return;
1573 if (!control) {
1574 control = new QWidgetTextControl(const_cast<QLabel *>(q));
1575 control->document()->setUndoRedoEnabled(false);
1576 control->document()->setDefaultFont(q->font());
1577 if (resourceProvider != nullptr)
1578 control->document()->setResourceProvider(resourceProvider);
1579 control->setTextInteractionFlags(textInteractionFlags);
1580 control->setOpenExternalLinks(openExternalLinks);
1581 control->setPalette(q->palette());
1582 control->setFocus(focus: q->hasFocus());
1583 QObject::connect(sender: control, SIGNAL(updateRequest(QRectF)),
1584 receiver: q, SLOT(update()));
1585 QObject::connect(sender: control, SIGNAL(linkHovered(QString)),
1586 receiver: q, SLOT(_q_linkHovered(QString)));
1587 QObject::connect(sender: control, SIGNAL(linkActivated(QString)),
1588 receiver: q, SIGNAL(linkActivated(QString)));
1589 textLayoutDirty = true;
1590 textDirty = true;
1591 }
1592}
1593
1594void QLabelPrivate::sendControlEvent(QEvent *e)
1595{
1596 Q_Q(QLabel);
1597 if (!isTextLabel || !control || textInteractionFlags == Qt::NoTextInteraction) {
1598 e->ignore();
1599 return;
1600 }
1601 control->processEvent(e, coordinateOffset: -layoutRect().topLeft(), contextWidget: q);
1602}
1603
1604void QLabelPrivate::_q_linkHovered(const QString &anchor)
1605{
1606 Q_Q(QLabel);
1607#ifndef QT_NO_CURSOR
1608 if (anchor.isEmpty()) { // restore cursor
1609 if (validCursor)
1610 q->setCursor(cursor);
1611 else
1612 q->unsetCursor();
1613 onAnchor = false;
1614 } else if (!onAnchor) {
1615 validCursor = q->testAttribute(attribute: Qt::WA_SetCursor);
1616 if (validCursor) {
1617 cursor = q->cursor();
1618 }
1619 q->setCursor(Qt::PointingHandCursor);
1620 onAnchor = true;
1621 }
1622#endif
1623 emit q->linkHovered(link: anchor);
1624}
1625
1626// Return the layout rect - this is the rect that is given to the layout painting code
1627// This may be different from the document rect since vertical alignment is not
1628// done by the text layout code
1629QRectF QLabelPrivate::layoutRect() const
1630{
1631 QRectF cr = documentRect();
1632 if (!control)
1633 return cr;
1634 ensureTextLayouted();
1635 // Calculate y position manually
1636 qreal rh = control->document()->documentLayout()->documentSize().height();
1637 qreal yo = 0;
1638 if (align & Qt::AlignVCenter)
1639 yo = qMax(a: (cr.height()-rh)/2, b: qreal(0));
1640 else if (align & Qt::AlignBottom)
1641 yo = qMax(a: cr.height()-rh, b: qreal(0));
1642 return QRectF(cr.x(), yo + cr.y(), cr.width(), cr.height());
1643}
1644
1645// Returns the point in the document rect adjusted with p
1646QPoint QLabelPrivate::layoutPoint(const QPoint& p) const
1647{
1648 QRect lr = layoutRect().toRect();
1649 return p - lr.topLeft();
1650}
1651
1652#ifndef QT_NO_CONTEXTMENU
1653QMenu *QLabelPrivate::createStandardContextMenu(const QPoint &pos)
1654{
1655 if (!control)
1656 return nullptr;
1657
1658 const QPoint p = layoutPoint(p: pos);
1659 return control->createStandardContextMenu(pos: p, parent: q_func());
1660}
1661#endif
1662
1663/*!
1664 \fn void QLabel::linkHovered(const QString &link)
1665 \since 4.2
1666
1667 This signal is emitted when the user hovers over a link. The URL
1668 referred to by the anchor is passed in \a link.
1669
1670 \sa linkActivated()
1671*/
1672
1673
1674/*!
1675 \fn void QLabel::linkActivated(const QString &link)
1676 \since 4.2
1677
1678 This signal is emitted when the user clicks a link. The URL
1679 referred to by the anchor is passed in \a link.
1680
1681 \sa linkHovered()
1682*/
1683
1684QT_END_NAMESPACE
1685
1686#include "moc_qlabel.cpp"
1687

source code of qtbase/src/widgets/widgets/qlabel.cpp