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

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