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
935 switch (e->type()) {
936#ifndef QT_NO_SHORTCUT
937 case 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 break;
955 }
956#endif
957 case QEvent::Resize:
958 if (d->control)
959 d->textLayoutDirty = true;
960 break;
961 case QEvent::StyleChange:
962#ifdef Q_OS_MAC
963 case QEvent::MacSizeChange:
964#endif
965 d->setLayoutItemMargins(QStyle::SE_LabelLayoutItem);
966 d->updateLabel();
967 break;
968 case QEvent::Polish:
969 if (d->needTextControl())
970 d->ensureTextControl();
971 break;
972 case QEvent::Enter:
973 case QEvent::Leave:
974 d->sendControlEvent(e);
975 break;
976 default:
977 break;
978 }
979
980 return QFrame::event(e);
981}
982
983/*!\reimp
984*/
985void QLabel::paintEvent(QPaintEvent *)
986{
987 Q_D(QLabel);
988 QStyle *style = QWidget::style();
989 QPainter painter(this);
990 drawFrame(&painter);
991 QRect cr = contentsRect();
992 cr.adjust(d->margin, d->margin, -d->margin, -d->margin);
993 int align = QStyle::visualAlignment(d->isTextLabel ? d->textDirection()
994 : layoutDirection(), QFlag(d->align));
995
996#if QT_CONFIG(movie)
997 if (d->movie && !d->movie->currentPixmap().isNull()) {
998 if (d->scaledcontents)
999 style->drawItemPixmap(&painter, cr, align, d->movie->currentPixmap().scaled(cr.size()));
1000 else
1001 style->drawItemPixmap(&painter, cr, align, d->movie->currentPixmap());
1002 }
1003 else
1004#endif
1005 if (d->isTextLabel) {
1006 QRectF lr = d->layoutRect().toAlignedRect();
1007 QStyleOption opt;
1008 opt.initFrom(this);
1009#if QT_CONFIG(style_stylesheet)
1010 if (QStyleSheetStyle* cssStyle = qt_styleSheet(style))
1011 cssStyle->styleSheetPalette(this, &opt, &opt.palette);
1012#endif
1013 if (d->control) {
1014#ifndef QT_NO_SHORTCUT
1015 const bool underline = static_cast<bool>(style->styleHint(QStyle::SH_UnderlineShortcut,
1016 nullptr, this, nullptr));
1017 if (d->shortcutId != 0
1018 && underline != d->shortcutCursor.charFormat().fontUnderline()) {
1019 QTextCharFormat fmt;
1020 fmt.setFontUnderline(underline);
1021 d->shortcutCursor.mergeCharFormat(fmt);
1022 }
1023#endif
1024 d->ensureTextLayouted();
1025
1026 QAbstractTextDocumentLayout::PaintContext context;
1027 // Adjust the palette
1028 context.palette = opt.palette;
1029
1030 if (foregroundRole() != QPalette::Text && isEnabled())
1031 context.palette.setColor(QPalette::Text, context.palette.color(foregroundRole()));
1032
1033 painter.save();
1034 painter.translate(lr.topLeft());
1035 painter.setClipRect(lr.translated(-lr.x(), -lr.y()));
1036 d->control->setPalette(context.palette);
1037 d->control->drawContents(&painter, QRectF(), this);
1038 painter.restore();
1039 } else {
1040 int flags = align | (d->textDirection() == Qt::LeftToRight ? Qt::TextForceLeftToRight
1041 : Qt::TextForceRightToLeft);
1042 if (d->hasShortcut) {
1043 flags |= Qt::TextShowMnemonic;
1044 if (!style->styleHint(QStyle::SH_UnderlineShortcut, &opt, this))
1045 flags |= Qt::TextHideMnemonic;
1046 }
1047 style->drawItemText(&painter, lr.toRect(), flags, opt.palette, isEnabled(), d->text, foregroundRole());
1048 }
1049 } else
1050#ifndef QT_NO_PICTURE
1051 if (d->picture) {
1052 QRect br = d->picture->boundingRect();
1053 int rw = br.width();
1054 int rh = br.height();
1055 if (d->scaledcontents) {
1056 painter.save();
1057 painter.translate(cr.x(), cr.y());
1058 painter.scale((double)cr.width()/rw, (double)cr.height()/rh);
1059 painter.drawPicture(-br.x(), -br.y(), *d->picture);
1060 painter.restore();
1061 } else {
1062 int xo = 0;
1063 int yo = 0;
1064 if (align & Qt::AlignVCenter)
1065 yo = (cr.height()-rh)/2;
1066 else if (align & Qt::AlignBottom)
1067 yo = cr.height()-rh;
1068 if (align & Qt::AlignRight)
1069 xo = cr.width()-rw;
1070 else if (align & Qt::AlignHCenter)
1071 xo = (cr.width()-rw)/2;
1072 painter.drawPicture(cr.x()+xo-br.x(), cr.y()+yo-br.y(), *d->picture);
1073 }
1074 } else
1075#endif
1076 if (d->icon && !d->icon->isNull()) {
1077 const qreal dpr = devicePixelRatio();
1078 const QSize size = d->scaledcontents ? cr.size() : d->pixmapSize;
1079 const auto mode = isEnabled() ? QIcon::Normal : QIcon::Disabled;
1080 QPixmap pix = d->icon->pixmap(size, dpr, mode);
1081 // the size of the returned pixmap might not match when
1082 // - scaledContents is enabled
1083 // - the dpr does not match the one from the pixmap in QIcon
1084 // since QStyle::drawItemPixmap() stretches without Qt::SmoothTransformation
1085 // we do it here
1086 if (pix.size() != size * dpr) {
1087 const QString key = "qt_label_"_L1 % HexString<quint64>(pix.cacheKey())
1088 % HexString<quint8>(mode)
1089 % HexString<uint>(size.width())
1090 % HexString<uint>(size.height())
1091 % HexString<quint16>(qRound(dpr * 1000));
1092 if (!QPixmapCache::find(key, &pix)) {
1093 pix = pix.scaled(size * dpr, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
1094 pix.setDevicePixelRatio(dpr);
1095 // using QIcon to cache the newly create pixmap is not possible
1096 // because QIcon does not clear this cache (so we grow indefinitely)
1097 // and also uses the newly added pixmap as starting point for new
1098 // scaled pixmap which makes it very blurry.
1099 // Therefore use QPixmapCache here.
1100 QPixmapCache::insert(key, pix);
1101 }
1102 }
1103 QStyleOption opt;
1104 opt.initFrom(this);
1105 style->drawItemPixmap(&painter, cr, align, pix);
1106 }
1107}
1108
1109
1110/*!
1111 Updates the label, but not the frame.
1112*/
1113
1114void QLabelPrivate::updateLabel()
1115{
1116 Q_Q(QLabel);
1117 valid_hints = false;
1118
1119 if (isTextLabel) {
1120 QSizePolicy policy = q->sizePolicy();
1121 const bool wrap = align & Qt::TextWordWrap;
1122 policy.setHeightForWidth(wrap);
1123 if (policy != q->sizePolicy()) // ### should be replaced by WA_WState_OwnSizePolicy idiom
1124 q->setSizePolicy(policy);
1125 textLayoutDirty = true;
1126 }
1127 q->updateGeometry();
1128 q->update(q->contentsRect());
1129}
1130
1131#ifndef QT_NO_SHORTCUT
1132/*!
1133 Sets this label's buddy to \a buddy.
1134
1135 When the user presses the shortcut key indicated by this label,
1136 the keyboard focus is transferred to the label's buddy widget.
1137
1138 The buddy mechanism is only available for QLabels that contain
1139 text in which one character is prefixed with an ampersand, '&'.
1140 This character is set as the shortcut key. See the \l
1141 QKeySequence::mnemonic() documentation for details (to display an
1142 actual ampersand, use '&&').
1143
1144 In a dialog, you might create two data entry widgets and a label
1145 for each, and set up the geometry layout so each label is just to
1146 the left of its data entry widget (its "buddy"), for example:
1147 \snippet code/src_gui_widgets_qlabel.cpp 2
1148
1149 With the code above, the focus jumps to the Name field when the
1150 user presses Alt+N, and to the Phone field when the user presses
1151 Alt+P.
1152
1153 To unset a previously set buddy, call this function with \a buddy
1154 set to nullptr.
1155
1156 \sa buddy(), setText(), QShortcut, setAlignment()
1157*/
1158
1159void QLabel::setBuddy(QWidget *buddy)
1160{
1161 Q_D(QLabel);
1162
1163 if (d->buddy) {
1164 QObjectPrivate::disconnect(d->buddy, &QObject::destroyed,
1165 d, &QLabelPrivate::buddyDeleted);
1166 d->buddy->d_func()->labels.removeAll(this);
1167 }
1168
1169 d->buddy = buddy;
1170
1171 if (buddy) {
1172 buddy->d_func()->labels.append(this);
1173 QObjectPrivate::connect(buddy, &QObject::destroyed,
1174 d, &QLabelPrivate::buddyDeleted);
1175 }
1176
1177 if (d->isTextLabel) {
1178 if (d->shortcutId)
1179 releaseShortcut(d->shortcutId);
1180 d->shortcutId = 0;
1181 d->textDirty = true;
1182 if (buddy)
1183 d->updateShortcut(); // grab new shortcut
1184 d->updateLabel();
1185 }
1186}
1187
1188
1189/*!
1190 Returns this label's buddy, or nullptr if no buddy is currently set.
1191
1192 \sa setBuddy()
1193*/
1194
1195QWidget * QLabel::buddy() const
1196{
1197 Q_D(const QLabel);
1198 return d->buddy;
1199}
1200
1201void QLabelPrivate::updateShortcut()
1202{
1203 Q_Q(QLabel);
1204 Q_ASSERT(shortcutId == 0);
1205 // Introduce an extra boolean to indicate the presence of a shortcut in the
1206 // text. We cannot use the shortcutId itself because on the mac mnemonics are
1207 // off by default, so QKeySequence::mnemonic always returns an empty sequence.
1208 // But then we do want to hide the ampersands, so we can't use shortcutId.
1209 hasShortcut = false;
1210
1211 if (!text.contains(u'&'))
1212 return;
1213 hasShortcut = true;
1214 shortcutId = q->grabShortcut(QKeySequence::mnemonic(text));
1215}
1216
1217
1218void QLabelPrivate::buddyDeleted()
1219{
1220 Q_Q(QLabel);
1221 q->setBuddy(nullptr);
1222}
1223
1224#endif // QT_NO_SHORTCUT
1225
1226#if QT_CONFIG(movie)
1227void QLabelPrivate::movieUpdated(const QRect &rect)
1228{
1229 Q_Q(QLabel);
1230 if (movie && movie->isValid()) {
1231 QRect r;
1232 if (scaledcontents) {
1233 QRect cr = q->contentsRect();
1234 QRect pixmapRect(cr.topLeft(), movie->currentPixmap().size());
1235 if (pixmapRect.isEmpty())
1236 return;
1237 r.setRect(cr.left(), cr.top(),
1238 (rect.width() * cr.width()) / pixmapRect.width(),
1239 (rect.height() * cr.height()) / pixmapRect.height());
1240 } else {
1241 r = q->style()->itemPixmapRect(q->contentsRect(), align, movie->currentPixmap());
1242 r.translate(rect.x(), rect.y());
1243 r.setWidth(qMin(r.width(), rect.width()));
1244 r.setHeight(qMin(r.height(), rect.height()));
1245 }
1246 q->update(r);
1247 }
1248}
1249
1250void QLabelPrivate::movieResized(const QSize &size)
1251{
1252 Q_Q(QLabel);
1253 q->update(); //we need to refresh the whole background in case the new size is smaller
1254 valid_hints = false;
1255 movieUpdated(QRect(QPoint(0,0), size));
1256 q->updateGeometry();
1257}
1258
1259/*!
1260 Sets the label contents to \a movie. Any previous content is
1261 cleared. The label does NOT take ownership of the movie.
1262
1263 The buddy shortcut, if any, is disabled.
1264
1265 \sa movie(), setBuddy()
1266*/
1267
1268void QLabel::setMovie(QMovie *movie)
1269{
1270 Q_D(QLabel);
1271 d->clearContents();
1272
1273 if (!movie)
1274 return;
1275
1276 d->movie = movie;
1277 d->movieConnections = {
1278 QObjectPrivate::connect(movie, &QMovie::resized, d, &QLabelPrivate::movieResized),
1279 QObjectPrivate::connect(movie, &QMovie::updated, d, &QLabelPrivate::movieUpdated),
1280 };
1281
1282 // Assume that if the movie is running,
1283 // resize/update signals will come soon enough
1284 if (movie->state() != QMovie::Running)
1285 d->updateLabel();
1286}
1287
1288#endif // QT_CONFIG(movie)
1289
1290/*!
1291 \internal
1292
1293 Clears any contents, without updating/repainting the label.
1294*/
1295
1296void QLabelPrivate::clearContents()
1297{
1298 delete control;
1299 control = nullptr;
1300 isTextLabel = false;
1301 hasShortcut = false;
1302
1303#ifndef QT_NO_PICTURE
1304 picture.reset();
1305#endif
1306 icon.reset();
1307 pixmapSize = QSize();
1308
1309 text.clear();
1310 Q_Q(QLabel);
1311#ifndef QT_NO_SHORTCUT
1312 if (shortcutId)
1313 q->releaseShortcut(shortcutId);
1314 shortcutId = 0;
1315#endif
1316#if QT_CONFIG(movie)
1317 for (auto &conn : movieConnections)
1318 QObject::disconnect(conn);
1319 movie = nullptr;
1320#endif
1321#ifndef QT_NO_CURSOR
1322 if (onAnchor) {
1323 if (validCursor)
1324 q->setCursor(cursor);
1325 else
1326 q->unsetCursor();
1327 }
1328 validCursor = false;
1329 onAnchor = false;
1330#endif
1331}
1332
1333
1334#if QT_CONFIG(movie)
1335
1336/*!
1337 Returns a pointer to the label's movie, or nullptr if no movie has been
1338 set.
1339
1340 \sa setMovie()
1341*/
1342
1343QMovie *QLabel::movie() const
1344{
1345 Q_D(const QLabel);
1346 return d->movie;
1347}
1348
1349#endif // QT_CONFIG(movie)
1350
1351/*!
1352 \property QLabel::textFormat
1353 \brief the label's text format
1354
1355 See the Qt::TextFormat enum for an explanation of the possible
1356 options.
1357
1358 The default format is Qt::AutoText.
1359
1360 \sa text()
1361*/
1362
1363Qt::TextFormat QLabel::textFormat() const
1364{
1365 Q_D(const QLabel);
1366 return d->textformat;
1367}
1368
1369void QLabel::setTextFormat(Qt::TextFormat format)
1370{
1371 Q_D(QLabel);
1372 if (format != d->textformat) {
1373 d->textformat = format;
1374 QString t = d->text;
1375 if (!t.isNull()) {
1376 d->text.clear();
1377 setText(t);
1378 }
1379 }
1380}
1381
1382/*!
1383 \since 6.1
1384
1385 Returns the resource provider for rich text of this label.
1386*/
1387QTextDocument::ResourceProvider QLabel::resourceProvider() const
1388{
1389 Q_D(const QLabel);
1390 return d->control ? d->control->document()->resourceProvider() : d->resourceProvider;
1391}
1392
1393/*!
1394 \since 6.1
1395
1396 Sets the \a provider of resources for rich text of this label.
1397
1398 \note The label \e{does not} take ownership of the \a provider.
1399*/
1400void QLabel::setResourceProvider(const QTextDocument::ResourceProvider &provider)
1401{
1402 Q_D(QLabel);
1403 d->resourceProvider = provider;
1404 if (d->control != nullptr)
1405 d->control->document()->setResourceProvider(provider);
1406}
1407
1408/*!
1409 \reimp
1410*/
1411void QLabel::changeEvent(QEvent *ev)
1412{
1413 Q_D(QLabel);
1414 if (ev->type() == QEvent::FontChange || ev->type() == QEvent::ApplicationFontChange) {
1415 if (d->isTextLabel) {
1416 if (d->control)
1417 d->control->document()->setDefaultFont(font());
1418 d->updateLabel();
1419 }
1420 } else if (ev->type() == QEvent::PaletteChange && d->control) {
1421 d->control->setPalette(palette());
1422 } else if (ev->type() == QEvent::ContentsRectChange) {
1423 d->updateLabel();
1424 }
1425 QFrame::changeEvent(ev);
1426}
1427
1428/*!
1429 \property QLabel::scaledContents
1430 \brief whether the label will scale its contents to fill all
1431 available space.
1432
1433 When enabled and the label shows a pixmap, it will scale the
1434 pixmap to fill the available space.
1435
1436 This property's default is false.
1437*/
1438bool QLabel::hasScaledContents() const
1439{
1440 Q_D(const QLabel);
1441 return d->scaledcontents;
1442}
1443
1444void QLabel::setScaledContents(bool enable)
1445{
1446 Q_D(QLabel);
1447 if ((bool)d->scaledcontents == enable)
1448 return;
1449 d->scaledcontents = enable;
1450 update(contentsRect());
1451}
1452
1453Qt::LayoutDirection QLabelPrivate::textDirection() const
1454{
1455 if (control) {
1456 QTextOption opt = control->document()->defaultTextOption();
1457 return opt.textDirection();
1458 }
1459
1460 return text.isRightToLeft() ? Qt::RightToLeft : Qt::LeftToRight;
1461}
1462
1463
1464// Returns the rect that is available for us to draw the document
1465QRect QLabelPrivate::documentRect() const
1466{
1467 Q_Q(const QLabel);
1468 Q_ASSERT_X(isTextLabel, "documentRect", "document rect called for label that is not a text label!");
1469 QRect cr = q->contentsRect();
1470 cr.adjust(margin, margin, -margin, -margin);
1471 const int align = QStyle::visualAlignment(isTextLabel ? textDirection()
1472 : q->layoutDirection(), QFlag(this->align));
1473 int m = indent;
1474 if (m < 0 && q->frameWidth()) // no indent, but we do have a frame
1475 m = q->fontMetrics().horizontalAdvance(u'x') / 2 - margin;
1476 if (m > 0) {
1477 if (align & Qt::AlignLeft)
1478 cr.setLeft(cr.left() + m);
1479 if (align & Qt::AlignRight)
1480 cr.setRight(cr.right() - m);
1481 if (align & Qt::AlignTop)
1482 cr.setTop(cr.top() + m);
1483 if (align & Qt::AlignBottom)
1484 cr.setBottom(cr.bottom() - m);
1485 }
1486 return cr;
1487}
1488
1489void QLabelPrivate::ensureTextPopulated() const
1490{
1491 if (!textDirty)
1492 return;
1493 if (control) {
1494 QTextDocument *doc = control->document();
1495 if (textDirty) {
1496 if (effectiveTextFormat == Qt::PlainText) {
1497 doc->setPlainText(text);
1498#if QT_CONFIG(texthtmlparser)
1499 } else if (effectiveTextFormat == Qt::RichText) {
1500 doc->setHtml(text);
1501#endif
1502#if QT_CONFIG(textmarkdownreader)
1503 } else if (effectiveTextFormat == Qt::MarkdownText) {
1504 doc->setMarkdown(text);
1505#endif
1506 } else {
1507 doc->setPlainText(text);
1508 }
1509 doc->setUndoRedoEnabled(false);
1510
1511#ifndef QT_NO_SHORTCUT
1512 if (hasShortcut) {
1513 // Underline the first character that follows an ampersand (and remove the others ampersands)
1514 int from = 0;
1515 bool found = false;
1516 QTextCursor cursor;
1517 while (!(cursor = control->document()->find(("&"_L1), from)).isNull()) {
1518 cursor.deleteChar(); // remove the ampersand
1519 cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
1520 from = cursor.position();
1521 if (!found && cursor.selectedText() != "&"_L1) { //not a second &
1522 found = true;
1523 shortcutCursor = cursor;
1524 }
1525 }
1526 }
1527#endif
1528 }
1529 }
1530 textDirty = false;
1531}
1532
1533void QLabelPrivate::ensureTextLayouted() const
1534{
1535 if (!textLayoutDirty)
1536 return;
1537 ensureTextPopulated();
1538 if (control) {
1539 QTextDocument *doc = control->document();
1540 QTextOption opt = doc->defaultTextOption();
1541
1542 opt.setAlignment(QFlag(this->align));
1543
1544 if (this->align & Qt::TextWordWrap)
1545 opt.setWrapMode(QTextOption::WordWrap);
1546 else
1547 opt.setWrapMode(QTextOption::ManualWrap);
1548
1549 doc->setDefaultTextOption(opt);
1550
1551 QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
1552 fmt.setMargin(0);
1553 doc->rootFrame()->setFrameFormat(fmt);
1554 doc->setTextWidth(documentRect().width());
1555 }
1556 textLayoutDirty = false;
1557}
1558
1559void QLabelPrivate::ensureTextControl() const
1560{
1561 Q_Q(const QLabel);
1562 if (!isTextLabel)
1563 return;
1564 if (!control) {
1565 control = new QWidgetTextControl(const_cast<QLabel *>(q));
1566 control->document()->setUndoRedoEnabled(false);
1567 control->document()->setDefaultFont(q->font());
1568 if (resourceProvider != nullptr)
1569 control->document()->setResourceProvider(resourceProvider);
1570 control->setTextInteractionFlags(textInteractionFlags);
1571 control->setOpenExternalLinks(openExternalLinks);
1572 control->setPalette(q->palette());
1573 control->setFocus(q->hasFocus());
1574 QObject::connect(control, &QWidgetTextControl::updateRequest,
1575 q, qOverload<>(&QLabel::update));
1576 QObject::connect(control, &QWidgetTextControl::linkActivated,
1577 q, &QLabel::linkActivated);
1578 QObjectPrivate::connect(control, &QWidgetTextControl::linkHovered,
1579 this, &QLabelPrivate::linkHovered);
1580 textLayoutDirty = true;
1581 textDirty = true;
1582 }
1583}
1584
1585void QLabelPrivate::sendControlEvent(QEvent *e)
1586{
1587 Q_Q(QLabel);
1588 if (!isTextLabel || !control || textInteractionFlags == Qt::NoTextInteraction) {
1589 e->ignore();
1590 return;
1591 }
1592 control->processEvent(e, -layoutRect().topLeft(), q);
1593}
1594
1595void QLabelPrivate::linkHovered(const QString &anchor)
1596{
1597 Q_Q(QLabel);
1598#ifndef QT_NO_CURSOR
1599 if (anchor.isEmpty()) { // restore cursor
1600 if (validCursor)
1601 q->setCursor(cursor);
1602 else
1603 q->unsetCursor();
1604 onAnchor = false;
1605 } else if (!onAnchor) {
1606 validCursor = q->testAttribute(Qt::WA_SetCursor);
1607 if (validCursor) {
1608 cursor = q->cursor();
1609 }
1610 q->setCursor(Qt::PointingHandCursor);
1611 onAnchor = true;
1612 }
1613#endif
1614 emit q->linkHovered(anchor);
1615}
1616
1617// Return the layout rect - this is the rect that is given to the layout painting code
1618// This may be different from the document rect since vertical alignment is not
1619// done by the text layout code
1620QRectF QLabelPrivate::layoutRect() const
1621{
1622 QRectF cr = documentRect();
1623 if (!control)
1624 return cr;
1625 ensureTextLayouted();
1626 // Calculate y position manually
1627 qreal rh = control->document()->documentLayout()->documentSize().height();
1628 qreal yo = 0;
1629 if (align & Qt::AlignVCenter)
1630 yo = qMax((cr.height()-rh)/2, qreal(0));
1631 else if (align & Qt::AlignBottom)
1632 yo = qMax(cr.height()-rh, qreal(0));
1633 return QRectF(cr.x(), yo + cr.y(), cr.width(), cr.height());
1634}
1635
1636// Returns the point in the document rect adjusted with p
1637QPoint QLabelPrivate::layoutPoint(const QPoint& p) const
1638{
1639 QRect lr = layoutRect().toRect();
1640 return p - lr.topLeft();
1641}
1642
1643#ifndef QT_NO_CONTEXTMENU
1644QMenu *QLabelPrivate::createStandardContextMenu(const QPoint &pos)
1645{
1646 if (!control)
1647 return nullptr;
1648
1649 const QPoint p = layoutPoint(pos);
1650 return control->createStandardContextMenu(p, q_func());
1651}
1652#endif
1653
1654/*!
1655 \fn void QLabel::linkHovered(const QString &link)
1656 \since 4.2
1657
1658 This signal is emitted when the user hovers over a link. The URL
1659 referred to by the anchor is passed in \a link.
1660
1661 \sa linkActivated()
1662*/
1663
1664
1665/*!
1666 \fn void QLabel::linkActivated(const QString &link)
1667 \since 4.2
1668
1669 This signal is emitted when the user clicks a link. The URL
1670 referred to by the anchor is passed in \a link.
1671
1672 \sa linkHovered()
1673*/
1674
1675QT_END_NAMESPACE
1676
1677#include "moc_qlabel.cpp"
Combined button and popup list for selecting options.