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 \class QLabelPrivate
522 \inmodule QtWidgets
523 \internal
524*/
525
526/*!
527 Returns the size that will be used if the width of the label is \a
528 w. If \a w is -1, the sizeHint() is returned. If \a w is 0 minimumSizeHint() is returned
529*/
530QSize QLabelPrivate::sizeForWidth(int w) const
531{
532 Q_Q(const QLabel);
533 if (q->minimumWidth() > 0)
534 w = qMax(w, q->minimumWidth());
535 QSize contentsMargin(leftmargin + rightmargin, topmargin + bottommargin);
536
537 QRect br;
538
539 int hextra = 2 * margin;
540 int vextra = hextra;
541 QFontMetrics fm = q->fontMetrics();
542
543 if (icon && !icon->isNull()) {
544 br = QRect(QPoint(0, 0), pixmapSize);
545#ifndef QT_NO_PICTURE
546 } else if (picture && !picture->isNull()) {
547 br = picture->boundingRect();
548#endif
549#if QT_CONFIG(movie)
550 } else if (movie && !movie->currentPixmap().isNull()) {
551 br = movie->currentPixmap().rect();
552 br.setSize(movie->currentPixmap().deviceIndependentSize().toSize());
553#endif
554 } else if (isTextLabel) {
555 int align = QStyle::visualAlignment(textDirection(), QFlag(this->align));
556 // Add indentation
557 int m = indent;
558
559 if (m < 0 && q->frameWidth()) // no indent, but we do have a frame
560 m = fm.horizontalAdvance(u'x') - margin*2;
561 if (m > 0) {
562 if ((align & Qt::AlignLeft) || (align & Qt::AlignRight))
563 hextra += m;
564 if ((align & Qt::AlignTop) || (align & Qt::AlignBottom))
565 vextra += m;
566 }
567
568 if (control) {
569 ensureTextLayouted();
570 const qreal oldTextWidth = control->textWidth();
571 // Calculate the length of document if w is the width
572 if (align & Qt::TextWordWrap) {
573 if (w >= 0) {
574 w = qMax(w-hextra-contentsMargin.width(), 0); // strip margin and indent
575 control->setTextWidth(w);
576 } else {
577 control->adjustSize();
578 }
579 } else {
580 control->setTextWidth(-1);
581 }
582
583 QSizeF controlSize = control->size();
584 br = QRect(QPoint(0, 0), QSize(qCeil(controlSize.width()), qCeil(controlSize.height())));
585
586 // restore state
587 control->setTextWidth(oldTextWidth);
588 } else {
589 // Turn off center alignment in order to avoid rounding errors for centering,
590 // since centering involves a division by 2. At the end, all we want is the size.
591 int flags = align & ~(Qt::AlignVCenter | Qt::AlignHCenter);
592 if (hasShortcut) {
593 flags |= Qt::TextShowMnemonic;
594 QStyleOption opt;
595 opt.initFrom(q);
596 if (!q->style()->styleHint(QStyle::SH_UnderlineShortcut, &opt, q))
597 flags |= Qt::TextHideMnemonic;
598 }
599
600 bool tryWidth = (w < 0) && (align & Qt::TextWordWrap);
601 if (tryWidth)
602 w = qMin(fm.averageCharWidth() * 80, q->maximumSize().width());
603 else if (w < 0)
604 w = 2000;
605 w -= (hextra + contentsMargin.width());
606 br = fm.boundingRect(0, 0, w ,2000, flags, text);
607 if (tryWidth && br.height() < 4*fm.lineSpacing() && br.width() > w/2)
608 br = fm.boundingRect(0, 0, w/2, 2000, flags, text);
609 if (tryWidth && br.height() < 2*fm.lineSpacing() && br.width() > w/4)
610 br = fm.boundingRect(0, 0, w/4, 2000, flags, text);
611 }
612 } else {
613 br = QRect(QPoint(0, 0), QSize(fm.averageCharWidth(), fm.lineSpacing()));
614 }
615
616 const QSize contentsSize(br.width() + hextra, br.height() + vextra);
617 return (contentsSize + contentsMargin).expandedTo(q->minimumSize());
618}
619
620
621/*!
622 \reimp
623*/
624
625int QLabel::heightForWidth(int w) const
626{
627 Q_D(const QLabel);
628 if (d->isTextLabel)
629 return d->sizeForWidth(w).height();
630 return QWidget::heightForWidth(w);
631}
632
633/*!
634 \property QLabel::openExternalLinks
635 \since 4.2
636
637 Specifies whether QLabel should automatically open links using
638 QDesktopServices::openUrl() instead of emitting the
639 linkActivated() signal.
640
641 \b{Note:} The textInteractionFlags set on the label need to include
642 either LinksAccessibleByMouse or LinksAccessibleByKeyboard.
643
644 The default value is false.
645
646 \sa textInteractionFlags()
647*/
648bool QLabel::openExternalLinks() const
649{
650 Q_D(const QLabel);
651 return d->openExternalLinks;
652}
653
654void QLabel::setOpenExternalLinks(bool open)
655{
656 Q_D(QLabel);
657 d->openExternalLinks = open;
658 if (d->control)
659 d->control->setOpenExternalLinks(open);
660}
661
662/*!
663 \property QLabel::textInteractionFlags
664 \since 4.2
665
666 Specifies how the label should interact with user input if it displays text.
667
668 If the flags contain Qt::LinksAccessibleByKeyboard the focus policy is also
669 automatically set to Qt::StrongFocus. If Qt::TextSelectableByKeyboard is set
670 then the focus policy is set to Qt::ClickFocus.
671
672 The default value is Qt::LinksAccessibleByMouse.
673*/
674void QLabel::setTextInteractionFlags(Qt::TextInteractionFlags flags)
675{
676 Q_D(QLabel);
677 if (d->textInteractionFlags == flags)
678 return;
679 d->textInteractionFlags = flags;
680 if (flags & Qt::LinksAccessibleByKeyboard)
681 setFocusPolicy(Qt::StrongFocus);
682 else if (flags & (Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse))
683 setFocusPolicy(Qt::ClickFocus);
684 else
685 setFocusPolicy(Qt::NoFocus);
686
687 if (d->needTextControl()) {
688 d->ensureTextControl();
689 } else {
690 delete d->control;
691 d->control = nullptr;
692 }
693
694 if (d->control)
695 d->control->setTextInteractionFlags(d->textInteractionFlags);
696}
697
698Qt::TextInteractionFlags QLabel::textInteractionFlags() const
699{
700 Q_D(const QLabel);
701 return d->textInteractionFlags;
702}
703
704/*!
705 Selects text from position \a start and for \a length characters.
706
707 \sa selectedText()
708
709 \b{Note:} The textInteractionFlags set on the label need to include
710 either TextSelectableByMouse or TextSelectableByKeyboard.
711
712 \since 4.7
713*/
714void QLabel::setSelection(int start, int length)
715{
716 Q_D(QLabel);
717 if (d->control) {
718 d->ensureTextPopulated();
719 QTextCursor cursor = d->control->textCursor();
720 cursor.setPosition(start);
721 cursor.setPosition(start + length, QTextCursor::KeepAnchor);
722 d->control->setTextCursor(cursor);
723 }
724}
725
726/*!
727 \property QLabel::hasSelectedText
728 \brief whether there is any text selected
729
730 hasSelectedText() returns \c true if some or all of the text has been
731 selected by the user; otherwise returns \c false.
732
733 By default, this property is \c false.
734
735 \sa selectedText()
736
737 \b{Note:} The textInteractionFlags set on the label need to include
738 either TextSelectableByMouse or TextSelectableByKeyboard.
739
740 \since 4.7
741*/
742bool QLabel::hasSelectedText() const
743{
744 Q_D(const QLabel);
745 if (d->control)
746 return d->control->textCursor().hasSelection();
747 return false;
748}
749
750/*!
751 \property QLabel::selectedText
752 \brief the selected text
753
754 If there is no selected text this property's value is
755 an empty string.
756
757 By default, this property contains an empty string.
758
759 \sa hasSelectedText()
760
761 \b{Note:} The textInteractionFlags set on the label need to include
762 either TextSelectableByMouse or TextSelectableByKeyboard.
763
764 \since 4.7
765*/
766QString QLabel::selectedText() const
767{
768 Q_D(const QLabel);
769 if (d->control)
770 return d->control->textCursor().selectedText();
771 return QString();
772}
773
774/*!
775 selectionStart() returns the index of the first selected character in the
776 label or -1 if no text is selected.
777
778 \sa selectedText()
779
780 \b{Note:} The textInteractionFlags set on the label need to include
781 either TextSelectableByMouse or TextSelectableByKeyboard.
782
783 \since 4.7
784*/
785int QLabel::selectionStart() const
786{
787 Q_D(const QLabel);
788 if (d->control && d->control->textCursor().hasSelection())
789 return d->control->textCursor().selectionStart();
790 return -1;
791}
792
793/*!\reimp
794*/
795QSize QLabel::sizeHint() const
796{
797 Q_D(const QLabel);
798 if (!d->valid_hints)
799 (void) QLabel::minimumSizeHint();
800 return d->sh;
801}
802
803/*!
804 \reimp
805*/
806QSize QLabel::minimumSizeHint() const
807{
808 Q_D(const QLabel);
809 if (d->valid_hints) {
810 if (d->sizePolicy == sizePolicy())
811 return d->msh;
812 }
813
814 ensurePolished();
815 d->valid_hints = true;
816 d->sh = d->sizeForWidth(-1); // wrap ? golden ratio : min doc size
817 QSize msh(-1, -1);
818
819 if (!d->isTextLabel) {
820 msh = d->sh;
821 } else {
822 msh.rheight() = d->sizeForWidth(QWIDGETSIZE_MAX).height(); // height for one line
823 msh.rwidth() = d->sizeForWidth(0).width(); // wrap ? size of biggest word : min doc size
824 if (d->sh.height() < msh.height())
825 msh.rheight() = d->sh.height();
826 }
827 d->msh = msh;
828 d->sizePolicy = sizePolicy();
829 return msh;
830}
831
832/*!\reimp
833*/
834void QLabel::mousePressEvent(QMouseEvent *ev)
835{
836 Q_D(QLabel);
837 d->sendControlEvent(ev);
838}
839
840/*!\reimp
841*/
842void QLabel::mouseMoveEvent(QMouseEvent *ev)
843{
844 Q_D(QLabel);
845 d->sendControlEvent(ev);
846}
847
848/*!\reimp
849*/
850void QLabel::mouseReleaseEvent(QMouseEvent *ev)
851{
852 Q_D(QLabel);
853 d->sendControlEvent(ev);
854}
855
856#ifndef QT_NO_CONTEXTMENU
857/*!\reimp
858*/
859void QLabel::contextMenuEvent(QContextMenuEvent *ev)
860{
861 Q_D(QLabel);
862 if (!d->isTextLabel) {
863 ev->ignore();
864 return;
865 }
866 QMenu *menu = d->createStandardContextMenu(ev->pos());
867 if (!menu) {
868 ev->ignore();
869 return;
870 }
871 ev->accept();
872 menu->setAttribute(Qt::WA_DeleteOnClose);
873 menu->popup(ev->globalPos());
874}
875#endif // QT_NO_CONTEXTMENU
876
877/*!
878 \reimp
879*/
880void QLabel::focusInEvent(QFocusEvent *ev)
881{
882 Q_D(QLabel);
883 if (d->isTextLabel) {
884 d->ensureTextControl();
885 d->sendControlEvent(ev);
886 }
887 QFrame::focusInEvent(ev);
888}
889
890/*!
891 \reimp
892*/
893void QLabel::focusOutEvent(QFocusEvent *ev)
894{
895 Q_D(QLabel);
896 if (d->control) {
897 d->sendControlEvent(ev);
898 QTextCursor cursor = d->control->textCursor();
899 Qt::FocusReason reason = ev->reason();
900 if (reason != Qt::ActiveWindowFocusReason
901 && reason != Qt::PopupFocusReason
902 && cursor.hasSelection()) {
903 cursor.clearSelection();
904 d->control->setTextCursor(cursor);
905 }
906 }
907
908 QFrame::focusOutEvent(ev);
909}
910
911/*!\reimp
912*/
913bool QLabel::focusNextPrevChild(bool next)
914{
915 Q_D(QLabel);
916 if (d->control && d->control->setFocusToNextOrPreviousAnchor(next))
917 return true;
918 return QFrame::focusNextPrevChild(next);
919}
920
921/*!\reimp
922*/
923void QLabel::keyPressEvent(QKeyEvent *ev)
924{
925 Q_D(QLabel);
926 d->sendControlEvent(ev);
927}
928
929/*!\reimp
930*/
931bool QLabel::event(QEvent *e)
932{
933 Q_D(QLabel);
934 QEvent::Type type = e->type();
935
936#ifndef QT_NO_SHORTCUT
937 if (type == QEvent::Shortcut) {
938 QShortcutEvent *se = static_cast<QShortcutEvent *>(e);
939 if (se->shortcutId() == d->shortcutId) {
940 QWidget *w = d->buddy;
941 if (!w)
942 return QFrame::event(e);
943 if (w->focusPolicy() != Qt::NoFocus)
944 w->setFocus(Qt::ShortcutFocusReason);
945#if QT_CONFIG(abstractbutton)
946 QAbstractButton *button = qobject_cast<QAbstractButton *>(w);
947 if (button && !se->isAmbiguous())
948 button->animateClick();
949 else
950#endif
951 window()->setAttribute(Qt::WA_KeyboardFocusChange);
952 return true;
953 }
954 } else
955#endif
956 if (type == QEvent::Resize) {
957 if (d->control)
958 d->textLayoutDirty = true;
959 } else if (e->type() == QEvent::StyleChange
960#ifdef Q_OS_MAC
961 || e->type() == QEvent::MacSizeChange
962#endif
963 ) {
964 d->setLayoutItemMargins(QStyle::SE_LabelLayoutItem);
965 d->updateLabel();
966 } else if (type == QEvent::Polish) {
967 if (d->needTextControl())
968 d->ensureTextControl();
969 }
970
971 return QFrame::event(e);
972}
973
974/*!\reimp
975*/
976void QLabel::paintEvent(QPaintEvent *)
977{
978 Q_D(QLabel);
979 QStyle *style = QWidget::style();
980 QPainter painter(this);
981 drawFrame(&painter);
982 QRect cr = contentsRect();
983 cr.adjust(d->margin, d->margin, -d->margin, -d->margin);
984 int align = QStyle::visualAlignment(d->isTextLabel ? d->textDirection()
985 : layoutDirection(), QFlag(d->align));
986
987#if QT_CONFIG(movie)
988 if (d->movie && !d->movie->currentPixmap().isNull()) {
989 if (d->scaledcontents)
990 style->drawItemPixmap(&painter, cr, align, d->movie->currentPixmap().scaled(cr.size()));
991 else
992 style->drawItemPixmap(&painter, cr, align, d->movie->currentPixmap());
993 }
994 else
995#endif
996 if (d->isTextLabel) {
997 QRectF lr = d->layoutRect().toAlignedRect();
998 QStyleOption opt;
999 opt.initFrom(this);
1000#if QT_CONFIG(style_stylesheet)
1001 if (QStyleSheetStyle* cssStyle = qt_styleSheet(style))
1002 cssStyle->styleSheetPalette(this, &opt, &opt.palette);
1003#endif
1004 if (d->control) {
1005#ifndef QT_NO_SHORTCUT
1006 const bool underline = static_cast<bool>(style->styleHint(QStyle::SH_UnderlineShortcut,
1007 nullptr, this, nullptr));
1008 if (d->shortcutId != 0
1009 && underline != d->shortcutCursor.charFormat().fontUnderline()) {
1010 QTextCharFormat fmt;
1011 fmt.setFontUnderline(underline);
1012 d->shortcutCursor.mergeCharFormat(fmt);
1013 }
1014#endif
1015 d->ensureTextLayouted();
1016
1017 QAbstractTextDocumentLayout::PaintContext context;
1018 // Adjust the palette
1019 context.palette = opt.palette;
1020
1021 if (foregroundRole() != QPalette::Text && isEnabled())
1022 context.palette.setColor(QPalette::Text, context.palette.color(foregroundRole()));
1023
1024 painter.save();
1025 painter.translate(lr.topLeft());
1026 painter.setClipRect(lr.translated(-lr.x(), -lr.y()));
1027 d->control->setPalette(context.palette);
1028 d->control->drawContents(&painter, QRectF(), this);
1029 painter.restore();
1030 } else {
1031 int flags = align | (d->textDirection() == Qt::LeftToRight ? Qt::TextForceLeftToRight
1032 : Qt::TextForceRightToLeft);
1033 if (d->hasShortcut) {
1034 flags |= Qt::TextShowMnemonic;
1035 if (!style->styleHint(QStyle::SH_UnderlineShortcut, &opt, this))
1036 flags |= Qt::TextHideMnemonic;
1037 }
1038 style->drawItemText(&painter, lr.toRect(), flags, opt.palette, isEnabled(), d->text, foregroundRole());
1039 }
1040 } else
1041#ifndef QT_NO_PICTURE
1042 if (d->picture) {
1043 QRect br = d->picture->boundingRect();
1044 int rw = br.width();
1045 int rh = br.height();
1046 if (d->scaledcontents) {
1047 painter.save();
1048 painter.translate(cr.x(), cr.y());
1049 painter.scale((double)cr.width()/rw, (double)cr.height()/rh);
1050 painter.drawPicture(-br.x(), -br.y(), *d->picture);
1051 painter.restore();
1052 } else {
1053 int xo = 0;
1054 int yo = 0;
1055 if (align & Qt::AlignVCenter)
1056 yo = (cr.height()-rh)/2;
1057 else if (align & Qt::AlignBottom)
1058 yo = cr.height()-rh;
1059 if (align & Qt::AlignRight)
1060 xo = cr.width()-rw;
1061 else if (align & Qt::AlignHCenter)
1062 xo = (cr.width()-rw)/2;
1063 painter.drawPicture(cr.x()+xo-br.x(), cr.y()+yo-br.y(), *d->picture);
1064 }
1065 } else
1066#endif
1067 if (d->icon && !d->icon->isNull()) {
1068 const qreal dpr = devicePixelRatio();
1069 const QSize size = d->scaledcontents ? cr.size() : d->pixmapSize;
1070 const auto mode = isEnabled() ? QIcon::Normal : QIcon::Disabled;
1071 QPixmap pix = d->icon->pixmap(size, dpr, mode);
1072 // the size of the returned pixmap might not match when
1073 // - scaledContents is enabled
1074 // - the dpr does not match the one from the pixmap in QIcon
1075 // since QStyle::drawItemPixmap() stretches without Qt::SmoothTransformation
1076 // we do it here
1077 if (pix.size() != size * dpr) {
1078 const QString key = "qt_label_"_L1 % HexString<quint64>(pix.cacheKey())
1079 % HexString<quint8>(mode)
1080 % HexString<uint>(size.width())
1081 % HexString<uint>(size.height())
1082 % HexString<quint16>(qRound(dpr * 1000));
1083 if (!QPixmapCache::find(key, &pix)) {
1084 pix = pix.scaled(size * dpr, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
1085 pix.setDevicePixelRatio(dpr);
1086 // using QIcon to cache the newly create pixmap is not possible
1087 // because QIcon does not clear this cache (so we grow indefinitely)
1088 // and also uses the newly added pixmap as starting point for new
1089 // scaled pixmap which makes it very blurry.
1090 // Therefore use QPixmapCache here.
1091 QPixmapCache::insert(key, pix);
1092 }
1093 }
1094 QStyleOption opt;
1095 opt.initFrom(this);
1096 style->drawItemPixmap(&painter, cr, align, pix);
1097 }
1098}
1099
1100
1101/*!
1102 Updates the label, but not the frame.
1103*/
1104
1105void QLabelPrivate::updateLabel()
1106{
1107 Q_Q(QLabel);
1108 valid_hints = false;
1109
1110 if (isTextLabel) {
1111 QSizePolicy policy = q->sizePolicy();
1112 const bool wrap = align & Qt::TextWordWrap;
1113 policy.setHeightForWidth(wrap);
1114 if (policy != q->sizePolicy()) // ### should be replaced by WA_WState_OwnSizePolicy idiom
1115 q->setSizePolicy(policy);
1116 textLayoutDirty = true;
1117 }
1118 q->updateGeometry();
1119 q->update(q->contentsRect());
1120}
1121
1122#ifndef QT_NO_SHORTCUT
1123/*!
1124 Sets this label's buddy to \a buddy.
1125
1126 When the user presses the shortcut key indicated by this label,
1127 the keyboard focus is transferred to the label's buddy widget.
1128
1129 The buddy mechanism is only available for QLabels that contain
1130 text in which one character is prefixed with an ampersand, '&'.
1131 This character is set as the shortcut key. See the \l
1132 QKeySequence::mnemonic() documentation for details (to display an
1133 actual ampersand, use '&&').
1134
1135 In a dialog, you might create two data entry widgets and a label
1136 for each, and set up the geometry layout so each label is just to
1137 the left of its data entry widget (its "buddy"), for example:
1138 \snippet code/src_gui_widgets_qlabel.cpp 2
1139
1140 With the code above, the focus jumps to the Name field when the
1141 user presses Alt+N, and to the Phone field when the user presses
1142 Alt+P.
1143
1144 To unset a previously set buddy, call this function with \a buddy
1145 set to nullptr.
1146
1147 \sa buddy(), setText(), QShortcut, setAlignment()
1148*/
1149
1150void QLabel::setBuddy(QWidget *buddy)
1151{
1152 Q_D(QLabel);
1153
1154 if (d->buddy) {
1155 QObjectPrivate::disconnect(d->buddy, &QObject::destroyed,
1156 d, &QLabelPrivate::buddyDeleted);
1157 d->buddy->d_func()->labels.removeAll(this);
1158 }
1159
1160 d->buddy = buddy;
1161
1162 if (buddy) {
1163 buddy->d_func()->labels.append(this);
1164 QObjectPrivate::connect(buddy, &QObject::destroyed,
1165 d, &QLabelPrivate::buddyDeleted);
1166 }
1167
1168 if (d->isTextLabel) {
1169 if (d->shortcutId)
1170 releaseShortcut(d->shortcutId);
1171 d->shortcutId = 0;
1172 d->textDirty = true;
1173 if (buddy)
1174 d->updateShortcut(); // grab new shortcut
1175 d->updateLabel();
1176 }
1177}
1178
1179
1180/*!
1181 Returns this label's buddy, or nullptr if no buddy is currently set.
1182
1183 \sa setBuddy()
1184*/
1185
1186QWidget * QLabel::buddy() const
1187{
1188 Q_D(const QLabel);
1189 return d->buddy;
1190}
1191
1192void QLabelPrivate::updateShortcut()
1193{
1194 Q_Q(QLabel);
1195 Q_ASSERT(shortcutId == 0);
1196 // Introduce an extra boolean to indicate the presence of a shortcut in the
1197 // text. We cannot use the shortcutId itself because on the mac mnemonics are
1198 // off by default, so QKeySequence::mnemonic always returns an empty sequence.
1199 // But then we do want to hide the ampersands, so we can't use shortcutId.
1200 hasShortcut = false;
1201
1202 if (!text.contains(u'&'))
1203 return;
1204 hasShortcut = true;
1205 shortcutId = q->grabShortcut(QKeySequence::mnemonic(text));
1206}
1207
1208
1209void QLabelPrivate::buddyDeleted()
1210{
1211 Q_Q(QLabel);
1212 q->setBuddy(nullptr);
1213}
1214
1215#endif // QT_NO_SHORTCUT
1216
1217#if QT_CONFIG(movie)
1218void QLabelPrivate::movieUpdated(const QRect &rect)
1219{
1220 Q_Q(QLabel);
1221 if (movie && movie->isValid()) {
1222 QRect r;
1223 if (scaledcontents) {
1224 QRect cr = q->contentsRect();
1225 QRect pixmapRect(cr.topLeft(), movie->currentPixmap().size());
1226 if (pixmapRect.isEmpty())
1227 return;
1228 r.setRect(cr.left(), cr.top(),
1229 (rect.width() * cr.width()) / pixmapRect.width(),
1230 (rect.height() * cr.height()) / pixmapRect.height());
1231 } else {
1232 r = q->style()->itemPixmapRect(q->contentsRect(), align, movie->currentPixmap());
1233 r.translate(rect.x(), rect.y());
1234 r.setWidth(qMin(r.width(), rect.width()));
1235 r.setHeight(qMin(r.height(), rect.height()));
1236 }
1237 q->update(r);
1238 }
1239}
1240
1241void QLabelPrivate::movieResized(const QSize &size)
1242{
1243 Q_Q(QLabel);
1244 q->update(); //we need to refresh the whole background in case the new size is smaller
1245 valid_hints = false;
1246 movieUpdated(QRect(QPoint(0,0), size));
1247 q->updateGeometry();
1248}
1249
1250/*!
1251 Sets the label contents to \a movie. Any previous content is
1252 cleared. The label does NOT take ownership of the movie.
1253
1254 The buddy shortcut, if any, is disabled.
1255
1256 \sa movie(), setBuddy()
1257*/
1258
1259void QLabel::setMovie(QMovie *movie)
1260{
1261 Q_D(QLabel);
1262 d->clearContents();
1263
1264 if (!movie)
1265 return;
1266
1267 d->movie = movie;
1268 d->movieConnections = {
1269 QObjectPrivate::connect(movie, &QMovie::resized, d, &QLabelPrivate::movieResized),
1270 QObjectPrivate::connect(movie, &QMovie::updated, d, &QLabelPrivate::movieUpdated),
1271 };
1272
1273 // Assume that if the movie is running,
1274 // resize/update signals will come soon enough
1275 if (movie->state() != QMovie::Running)
1276 d->updateLabel();
1277}
1278
1279#endif // QT_CONFIG(movie)
1280
1281/*!
1282 \internal
1283
1284 Clears any contents, without updating/repainting the label.
1285*/
1286
1287void QLabelPrivate::clearContents()
1288{
1289 delete control;
1290 control = nullptr;
1291 isTextLabel = false;
1292 hasShortcut = false;
1293
1294#ifndef QT_NO_PICTURE
1295 picture.reset();
1296#endif
1297 icon.reset();
1298 pixmapSize = QSize();
1299
1300 text.clear();
1301 Q_Q(QLabel);
1302#ifndef QT_NO_SHORTCUT
1303 if (shortcutId)
1304 q->releaseShortcut(shortcutId);
1305 shortcutId = 0;
1306#endif
1307#if QT_CONFIG(movie)
1308 for (const auto &conn : std::as_const(movieConnections))
1309 QObject::disconnect(conn);
1310 movie = nullptr;
1311#endif
1312#ifndef QT_NO_CURSOR
1313 if (onAnchor) {
1314 if (validCursor)
1315 q->setCursor(cursor);
1316 else
1317 q->unsetCursor();
1318 }
1319 validCursor = false;
1320 onAnchor = false;
1321#endif
1322}
1323
1324
1325#if QT_CONFIG(movie)
1326
1327/*!
1328 Returns a pointer to the label's movie, or nullptr if no movie has been
1329 set.
1330
1331 \sa setMovie()
1332*/
1333
1334QMovie *QLabel::movie() const
1335{
1336 Q_D(const QLabel);
1337 return d->movie;
1338}
1339
1340#endif // QT_CONFIG(movie)
1341
1342/*!
1343 \property QLabel::textFormat
1344 \brief the label's text format
1345
1346 See the Qt::TextFormat enum for an explanation of the possible
1347 options.
1348
1349 The default format is Qt::AutoText.
1350
1351 \sa text()
1352*/
1353
1354Qt::TextFormat QLabel::textFormat() const
1355{
1356 Q_D(const QLabel);
1357 return d->textformat;
1358}
1359
1360void QLabel::setTextFormat(Qt::TextFormat format)
1361{
1362 Q_D(QLabel);
1363 if (format != d->textformat) {
1364 d->textformat = format;
1365 QString t = d->text;
1366 if (!t.isNull()) {
1367 d->text.clear();
1368 setText(t);
1369 }
1370 }
1371}
1372
1373/*!
1374 \since 6.1
1375
1376 Returns the resource provider for rich text of this label.
1377*/
1378QTextDocument::ResourceProvider QLabel::resourceProvider() const
1379{
1380 Q_D(const QLabel);
1381 return d->control ? d->control->document()->resourceProvider() : d->resourceProvider;
1382}
1383
1384/*!
1385 \since 6.1
1386
1387 Sets the \a provider of resources for rich text of this label.
1388
1389 \note The label \e{does not} take ownership of the \a provider.
1390*/
1391void QLabel::setResourceProvider(const QTextDocument::ResourceProvider &provider)
1392{
1393 Q_D(QLabel);
1394 d->resourceProvider = provider;
1395 if (d->control != nullptr)
1396 d->control->document()->setResourceProvider(provider);
1397}
1398
1399/*!
1400 \reimp
1401*/
1402void QLabel::changeEvent(QEvent *ev)
1403{
1404 Q_D(QLabel);
1405 if (ev->type() == QEvent::FontChange || ev->type() == QEvent::ApplicationFontChange) {
1406 if (d->isTextLabel) {
1407 if (d->control)
1408 d->control->document()->setDefaultFont(font());
1409 d->updateLabel();
1410 }
1411 } else if (ev->type() == QEvent::PaletteChange && d->control) {
1412 d->control->setPalette(palette());
1413 } else if (ev->type() == QEvent::ContentsRectChange) {
1414 d->updateLabel();
1415 }
1416 QFrame::changeEvent(ev);
1417}
1418
1419/*!
1420 \property QLabel::scaledContents
1421 \brief whether the label will scale its contents to fill all
1422 available space.
1423
1424 When enabled and the label shows a pixmap, it will scale the
1425 pixmap to fill the available space.
1426
1427 This property's default is false.
1428*/
1429bool QLabel::hasScaledContents() const
1430{
1431 Q_D(const QLabel);
1432 return d->scaledcontents;
1433}
1434
1435void QLabel::setScaledContents(bool enable)
1436{
1437 Q_D(QLabel);
1438 if ((bool)d->scaledcontents == enable)
1439 return;
1440 d->scaledcontents = enable;
1441 update(contentsRect());
1442}
1443
1444Qt::LayoutDirection QLabelPrivate::textDirection() const
1445{
1446 if (control) {
1447 QTextOption opt = control->document()->defaultTextOption();
1448 return opt.textDirection();
1449 }
1450
1451 return text.isRightToLeft() ? Qt::RightToLeft : Qt::LeftToRight;
1452}
1453
1454
1455// Returns the rect that is available for us to draw the document
1456QRect QLabelPrivate::documentRect() const
1457{
1458 Q_Q(const QLabel);
1459 Q_ASSERT_X(isTextLabel, "documentRect", "document rect called for label that is not a text label!");
1460 QRect cr = q->contentsRect();
1461 cr.adjust(margin, margin, -margin, -margin);
1462 const int align = QStyle::visualAlignment(isTextLabel ? textDirection()
1463 : q->layoutDirection(), QFlag(this->align));
1464 int m = indent;
1465 if (m < 0 && q->frameWidth()) // no indent, but we do have a frame
1466 m = q->fontMetrics().horizontalAdvance(u'x') / 2 - margin;
1467 if (m > 0) {
1468 if (align & Qt::AlignLeft)
1469 cr.setLeft(cr.left() + m);
1470 if (align & Qt::AlignRight)
1471 cr.setRight(cr.right() - m);
1472 if (align & Qt::AlignTop)
1473 cr.setTop(cr.top() + m);
1474 if (align & Qt::AlignBottom)
1475 cr.setBottom(cr.bottom() - m);
1476 }
1477 return cr;
1478}
1479
1480void QLabelPrivate::ensureTextPopulated() const
1481{
1482 if (!textDirty)
1483 return;
1484 if (control) {
1485 QTextDocument *doc = control->document();
1486 if (textDirty) {
1487 if (effectiveTextFormat == Qt::PlainText) {
1488 doc->setPlainText(text);
1489#if QT_CONFIG(texthtmlparser)
1490 } else if (effectiveTextFormat == Qt::RichText) {
1491 doc->setHtml(text);
1492#endif
1493#if QT_CONFIG(textmarkdownreader)
1494 } else if (effectiveTextFormat == Qt::MarkdownText) {
1495 doc->setMarkdown(text);
1496#endif
1497 } else {
1498 doc->setPlainText(text);
1499 }
1500 doc->setUndoRedoEnabled(false);
1501
1502#ifndef QT_NO_SHORTCUT
1503 if (hasShortcut) {
1504 // Underline the first character that follows an ampersand (and remove the others ampersands)
1505 int from = 0;
1506 bool found = false;
1507 QTextCursor cursor;
1508 while (!(cursor = control->document()->find(("&"_L1), from)).isNull()) {
1509 cursor.deleteChar(); // remove the ampersand
1510 cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
1511 from = cursor.position();
1512 if (!found && cursor.selectedText() != "&"_L1) { //not a second &
1513 found = true;
1514 shortcutCursor = cursor;
1515 }
1516 }
1517 }
1518#endif
1519 }
1520 }
1521 textDirty = false;
1522}
1523
1524void QLabelPrivate::ensureTextLayouted() const
1525{
1526 if (!textLayoutDirty)
1527 return;
1528 ensureTextPopulated();
1529 if (control) {
1530 QTextDocument *doc = control->document();
1531 QTextOption opt = doc->defaultTextOption();
1532
1533 opt.setAlignment(QFlag(this->align));
1534
1535 if (this->align & Qt::TextWordWrap)
1536 opt.setWrapMode(QTextOption::WordWrap);
1537 else
1538 opt.setWrapMode(QTextOption::ManualWrap);
1539
1540 doc->setDefaultTextOption(opt);
1541
1542 QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
1543 fmt.setMargin(0);
1544 doc->rootFrame()->setFrameFormat(fmt);
1545 doc->setTextWidth(documentRect().width());
1546 }
1547 textLayoutDirty = false;
1548}
1549
1550void QLabelPrivate::ensureTextControl() const
1551{
1552 Q_Q(const QLabel);
1553 if (!isTextLabel)
1554 return;
1555 if (!control) {
1556 control = new QWidgetTextControl(const_cast<QLabel *>(q));
1557 control->document()->setUndoRedoEnabled(false);
1558 control->document()->setDefaultFont(q->font());
1559 if (resourceProvider != nullptr)
1560 control->document()->setResourceProvider(resourceProvider);
1561 control->setTextInteractionFlags(textInteractionFlags);
1562 control->setOpenExternalLinks(openExternalLinks);
1563 control->setPalette(q->palette());
1564 control->setFocus(q->hasFocus());
1565 QObject::connect(control, &QWidgetTextControl::updateRequest,
1566 q, qOverload<>(&QLabel::update));
1567 QObject::connect(control, &QWidgetTextControl::linkActivated,
1568 q, &QLabel::linkActivated);
1569 QObjectPrivate::connect(control, &QWidgetTextControl::linkHovered,
1570 this, &QLabelPrivate::linkHovered);
1571 textLayoutDirty = true;
1572 textDirty = true;
1573 }
1574}
1575
1576void QLabelPrivate::sendControlEvent(QEvent *e)
1577{
1578 Q_Q(QLabel);
1579 if (!isTextLabel || !control || textInteractionFlags == Qt::NoTextInteraction) {
1580 e->ignore();
1581 return;
1582 }
1583 control->processEvent(e, -layoutRect().topLeft(), q);
1584}
1585
1586void QLabelPrivate::linkHovered(const QString &anchor)
1587{
1588 Q_Q(QLabel);
1589#ifndef QT_NO_CURSOR
1590 if (anchor.isEmpty()) { // restore cursor
1591 if (validCursor)
1592 q->setCursor(cursor);
1593 else
1594 q->unsetCursor();
1595 onAnchor = false;
1596 } else if (!onAnchor) {
1597 validCursor = q->testAttribute(Qt::WA_SetCursor);
1598 if (validCursor) {
1599 cursor = q->cursor();
1600 }
1601 q->setCursor(Qt::PointingHandCursor);
1602 onAnchor = true;
1603 }
1604#endif
1605 emit q->linkHovered(anchor);
1606}
1607
1608// Return the layout rect - this is the rect that is given to the layout painting code
1609// This may be different from the document rect since vertical alignment is not
1610// done by the text layout code
1611QRectF QLabelPrivate::layoutRect() const
1612{
1613 QRectF cr = documentRect();
1614 if (!control)
1615 return cr;
1616 ensureTextLayouted();
1617 // Calculate y position manually
1618 qreal rh = control->document()->documentLayout()->documentSize().height();
1619 qreal yo = 0;
1620 if (align & Qt::AlignVCenter)
1621 yo = qMax((cr.height()-rh)/2, qreal(0));
1622 else if (align & Qt::AlignBottom)
1623 yo = qMax(cr.height()-rh, qreal(0));
1624 return QRectF(cr.x(), yo + cr.y(), cr.width(), cr.height());
1625}
1626
1627// Returns the point in the document rect adjusted with p
1628QPoint QLabelPrivate::layoutPoint(const QPoint& p) const
1629{
1630 QRect lr = layoutRect().toRect();
1631 return p - lr.topLeft();
1632}
1633
1634#ifndef QT_NO_CONTEXTMENU
1635QMenu *QLabelPrivate::createStandardContextMenu(const QPoint &pos)
1636{
1637 if (!control)
1638 return nullptr;
1639
1640 const QPoint p = layoutPoint(pos);
1641 return control->createStandardContextMenu(p, q_func());
1642}
1643#endif
1644
1645/*!
1646 \fn void QLabel::linkHovered(const QString &link)
1647 \since 4.2
1648
1649 This signal is emitted when the user hovers over a link. The URL
1650 referred to by the anchor is passed in \a link.
1651
1652 \sa linkActivated()
1653*/
1654
1655
1656/*!
1657 \fn void QLabel::linkActivated(const QString &link)
1658 \since 4.2
1659
1660 This signal is emitted when the user clicks a link. The URL
1661 referred to by the anchor is passed in \a link.
1662
1663 \sa linkHovered()
1664*/
1665
1666QT_END_NAMESPACE
1667
1668#include "moc_qlabel.cpp"
Combined button and popup list for selecting options.