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