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