1 /****************************************************************************
2 **
3 ** Copyright (C) 2017 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the Qt Quick Templates 2 module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL3$
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 http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free
28 ** Software Foundation and appearing in the file LICENSE.GPL included in
29 ** the packaging of this file. Please review the following information to
30 ** ensure the GNU General Public License version 2.0 requirements will be
31 ** met: http://www.gnu.org/licenses/gpl-2.0.html.
32 **
33 ** $QT_END_LICENSE$
34 **
35 ****************************************************************************/
36 
37 #include "qquicklabel_p.h"
38 #include "qquicklabel_p_p.h"
39 #include "qquickcontrol_p.h"
40 #include "qquickcontrol_p_p.h"
41 #include "qquickdeferredexecute_p_p.h"
42 
43 #include <QtQuick/private/qquickitem_p.h>
44 #include <QtQuick/private/qquicktext_p.h>
45 
46 #if QT_CONFIG(accessibility)
47 #include <QtQuick/private/qquickaccessibleattached_p.h>
48 #endif
49 
50 QT_BEGIN_NAMESPACE
51 
52 /*!
53     \qmltype Label
54     \inherits Text
55 //!     \instantiates QQuickLabel
56     \inqmlmodule QtQuick.Controls
57     \since 5.7
58     \ingroup text
59     \brief Styled text label with inherited font.
60 
61     Label extends \l Text with styling and \l {Control::font}{font}
62     inheritance. The default colors and font are style specific. Label
63     can also have a visual \l background item.
64 
65     \image qtquickcontrols2-label.png
66 
67     \snippet qtquickcontrols2-label.qml 1
68 
69     You can use the properties of \l Text to change the appearance of the text as desired:
70 
71     \qml
72      Label {
73          text: "Hello world"
74          font.pixelSize: 22
75          font.italic: true
76      }
77     \endqml
78 
79     \sa {Customizing Label}
80 */
81 
QQuickLabelPrivate()82 QQuickLabelPrivate::QQuickLabelPrivate()
83 {
84 #if QT_CONFIG(accessibility)
85     QAccessible::installActivationObserver(this);
86 #endif
87 }
88 
~QQuickLabelPrivate()89 QQuickLabelPrivate::~QQuickLabelPrivate()
90 {
91 #if QT_CONFIG(accessibility)
92     QAccessible::removeActivationObserver(this);
93 #endif
94 }
95 
setTopInset(qreal value,bool reset)96 void QQuickLabelPrivate::setTopInset(qreal value, bool reset)
97 {
98     Q_Q(QQuickLabel);
99     const QMarginsF oldInset = getInset();
100     extra.value().topInset = value;
101     extra.value().hasTopInset = !reset;
102     if (!qFuzzyCompare(oldInset.top(), value)) {
103         emit q->topInsetChanged();
104         q->insetChange(getInset(), oldInset);
105     }
106 }
107 
setLeftInset(qreal value,bool reset)108 void QQuickLabelPrivate::setLeftInset(qreal value, bool reset)
109 {
110     Q_Q(QQuickLabel);
111     const QMarginsF oldInset = getInset();
112     extra.value().leftInset = value;
113     extra.value().hasLeftInset = !reset;
114     if (!qFuzzyCompare(oldInset.left(), value)) {
115         emit q->leftInsetChanged();
116         q->insetChange(getInset(), oldInset);
117     }
118 }
119 
setRightInset(qreal value,bool reset)120 void QQuickLabelPrivate::setRightInset(qreal value, bool reset)
121 {
122     Q_Q(QQuickLabel);
123     const QMarginsF oldInset = getInset();
124     extra.value().rightInset = value;
125     extra.value().hasRightInset = !reset;
126     if (!qFuzzyCompare(oldInset.right(), value)) {
127         emit q->rightInsetChanged();
128         q->insetChange(getInset(), oldInset);
129     }
130 }
131 
setBottomInset(qreal value,bool reset)132 void QQuickLabelPrivate::setBottomInset(qreal value, bool reset)
133 {
134     Q_Q(QQuickLabel);
135     const QMarginsF oldInset = getInset();
136     extra.value().bottomInset = value;
137     extra.value().hasBottomInset = !reset;
138     if (!qFuzzyCompare(oldInset.bottom(), value)) {
139         emit q->bottomInsetChanged();
140         q->insetChange(getInset(), oldInset);
141     }
142 }
143 
resizeBackground()144 void QQuickLabelPrivate::resizeBackground()
145 {
146     if (!background)
147         return;
148 
149     resizingBackground = true;
150 
151     QQuickItemPrivate *p = QQuickItemPrivate::get(background);
152     if (((!p->widthValid || !extra.isAllocated() || !extra->hasBackgroundWidth) && qFuzzyIsNull(background->x()))
153             || (extra.isAllocated() && (extra->hasLeftInset || extra->hasRightInset))) {
154         background->setX(getLeftInset());
155         background->setWidth(width - getLeftInset() - getRightInset());
156     }
157     if (((!p->heightValid || !extra.isAllocated() || !extra->hasBackgroundHeight) && qFuzzyIsNull(background->y()))
158             || (extra.isAllocated() && (extra->hasTopInset || extra->hasBottomInset))) {
159         background->setY(getTopInset());
160         background->setHeight(height - getTopInset() - getBottomInset());
161     }
162 
163     resizingBackground = false;
164 }
165 
166 /*!
167     \internal
168 
169     Determine which font is implicitly imposed on this control by its ancestors
170     and QGuiApplication::font, resolve this against its own font (attributes from
171     the implicit font are copied over). Then propagate this font to this
172     control's children.
173 */
resolveFont()174 void QQuickLabelPrivate::resolveFont()
175 {
176     Q_Q(QQuickLabel);
177     inheritFont(QQuickControlPrivate::parentFont(q));
178 }
179 
inheritFont(const QFont & font)180 void QQuickLabelPrivate::inheritFont(const QFont &font)
181 {
182     QFont parentFont = extra.isAllocated() ? extra->requestedFont.resolve(font) : font;
183     parentFont.resolve(extra.isAllocated() ? extra->requestedFont.resolve() | font.resolve() : font.resolve());
184 
185     const QFont defaultFont = QQuickTheme::font(QQuickTheme::Label);
186     QFont resolvedFont = parentFont.resolve(defaultFont);
187     // See comment in QQuickControlPrivate::inheritFont
188     if (defaultFont.families().isEmpty())
189         resolvedFont.setFamilies(QStringList());
190 
191     setFont_helper(resolvedFont);
192 }
193 
194 /*!
195     \internal
196 
197     Assign \a font to this control, and propagate it to all children.
198 */
updateFont(const QFont & font)199 void QQuickLabelPrivate::updateFont(const QFont &font)
200 {
201     Q_Q(QQuickLabel);
202     QFont oldFont = sourceFont;
203     q->QQuickText::setFont(font);
204 
205     QQuickControlPrivate::updateFontRecur(q, font);
206 
207     if (oldFont != font)
208         emit q->fontChanged();
209 }
210 
211 /*!
212     \internal
213 
214     Determine which palette is implicitly imposed on this control by its ancestors
215     and QGuiApplication::palette, resolve this against its own palette (attributes from
216     the implicit palette are copied over). Then propagate this palette to this
217     control's children.
218 */
resolvePalette()219 void QQuickLabelPrivate::resolvePalette()
220 {
221     Q_Q(QQuickLabel);
222     inheritPalette(QQuickControlPrivate::parentPalette(q));
223 }
224 
inheritPalette(const QPalette & palette)225 void QQuickLabelPrivate::inheritPalette(const QPalette &palette)
226 {
227     QPalette parentPalette = extra.isAllocated() ? extra->requestedPalette.resolve(palette) : palette;
228     parentPalette.resolve(extra.isAllocated() ? extra->requestedPalette.resolve() | palette.resolve() : palette.resolve());
229 
230     const QPalette defaultPalette = QQuickTheme::palette(QQuickTheme::Label);
231     const QPalette resolvedPalette = parentPalette.resolve(defaultPalette);
232 
233     setPalette_helper(resolvedPalette);
234 }
235 
updatePalette(const QPalette & palette)236 void QQuickLabelPrivate::updatePalette(const QPalette &palette)
237 {
238     Q_Q(QQuickLabel);
239     QPalette oldPalette = resolvedPalette;
240     resolvedPalette = palette;
241 
242     QQuickControlPrivate::updatePaletteRecur(q, palette);
243 
244     if (oldPalette != palette)
245         emit q->paletteChanged();
246 }
247 
textChanged(const QString & text)248 void QQuickLabelPrivate::textChanged(const QString &text)
249 {
250 #if QT_CONFIG(accessibility)
251     maybeSetAccessibleName(text);
252 #else
253     Q_UNUSED(text)
254 #endif
255 }
256 
257 #if QT_CONFIG(accessibility)
accessibilityActiveChanged(bool active)258 void QQuickLabelPrivate::accessibilityActiveChanged(bool active)
259 {
260     if (!active)
261         return;
262 
263     Q_Q(QQuickLabel);
264     QQuickAccessibleAttached *accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(qmlAttachedPropertiesObject<QQuickAccessibleAttached>(q, true));
265     Q_ASSERT(accessibleAttached);
266     accessibleAttached->setRole(accessibleRole());
267     maybeSetAccessibleName(text);
268 }
269 
accessibleRole() const270 QAccessible::Role QQuickLabelPrivate::accessibleRole() const
271 {
272     return QAccessible::StaticText;
273 }
274 
maybeSetAccessibleName(const QString & name)275 void QQuickLabelPrivate::maybeSetAccessibleName(const QString &name)
276 {
277     Q_Q(QQuickLabel);
278     auto accessibleAttached = qobject_cast<QQuickAccessibleAttached *>(
279         qmlAttachedPropertiesObject<QQuickAccessibleAttached>(q, true));
280     if (accessibleAttached) {
281         if (!accessibleAttached->wasNameExplicitlySet())
282             accessibleAttached->setNameImplicitly(name);
283     }
284 }
285 #endif
286 
backgroundName()287 static inline QString backgroundName() { return QStringLiteral("background"); }
288 
cancelBackground()289 void QQuickLabelPrivate::cancelBackground()
290 {
291     Q_Q(QQuickLabel);
292     quickCancelDeferred(q, backgroundName());
293 }
294 
executeBackground(bool complete)295 void QQuickLabelPrivate::executeBackground(bool complete)
296 {
297     Q_Q(QQuickLabel);
298     if (background.wasExecuted())
299         return;
300 
301     if (!background || complete)
302         quickBeginDeferred(q, backgroundName(), background);
303     if (complete)
304         quickCompleteDeferred(q, backgroundName(), background);
305 }
306 
itemGeometryChanged(QQuickItem * item,QQuickGeometryChange change,const QRectF & diff)307 void QQuickLabelPrivate::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &diff)
308 {
309     Q_UNUSED(diff);
310     if (resizingBackground || item != background || !change.sizeChange())
311         return;
312 
313     QQuickItemPrivate *p = QQuickItemPrivate::get(item);
314     extra.value().hasBackgroundWidth = p->widthValid;
315     extra.value().hasBackgroundHeight = p->heightValid;
316     resizeBackground();
317 }
318 
itemImplicitWidthChanged(QQuickItem * item)319 void QQuickLabelPrivate::itemImplicitWidthChanged(QQuickItem *item)
320 {
321     Q_Q(QQuickLabel);
322     if (item == background)
323         emit q->implicitBackgroundWidthChanged();
324 }
325 
itemImplicitHeightChanged(QQuickItem * item)326 void QQuickLabelPrivate::itemImplicitHeightChanged(QQuickItem *item)
327 {
328     Q_Q(QQuickLabel);
329     if (item == background)
330         emit q->implicitBackgroundHeightChanged();
331 }
332 
itemDestroyed(QQuickItem * item)333 void QQuickLabelPrivate::itemDestroyed(QQuickItem *item)
334 {
335     Q_Q(QQuickLabel);
336     if (item == background) {
337         background = nullptr;
338         emit q->implicitBackgroundWidthChanged();
339         emit q->implicitBackgroundHeightChanged();
340     }
341 }
342 
QQuickLabel(QQuickItem * parent)343 QQuickLabel::QQuickLabel(QQuickItem *parent)
344     : QQuickText(*(new QQuickLabelPrivate), parent)
345 {
346     Q_D(QQuickLabel);
347     QObjectPrivate::connect(this, &QQuickText::textChanged, d, &QQuickLabelPrivate::textChanged);
348 }
349 
~QQuickLabel()350 QQuickLabel::~QQuickLabel()
351 {
352     Q_D(QQuickLabel);
353     QQuickControlPrivate::removeImplicitSizeListener(d->background, d, QQuickControlPrivate::ImplicitSizeChanges | QQuickItemPrivate::Geometry);
354 }
355 
font() const356 QFont QQuickLabel::font() const
357 {
358     return QQuickText::font();
359 }
360 
setFont(const QFont & font)361 void QQuickLabel::setFont(const QFont &font)
362 {
363     Q_D(QQuickLabel);
364     if (d->extra.value().requestedFont.resolve() == font.resolve() && d->extra.value().requestedFont == font)
365         return;
366 
367     d->extra.value().requestedFont = font;
368     d->resolveFont();
369 }
370 
371 /*!
372     \qmlproperty Item QtQuick.Controls::Label::background
373 
374     This property holds the background item.
375 
376     \note If the background item has no explicit size specified, it automatically
377           follows the control's size. In most cases, there is no need to specify
378           width or height for a background item.
379 
380     \sa {Customizing Label}
381 */
background() const382 QQuickItem *QQuickLabel::background() const
383 {
384     QQuickLabelPrivate *d = const_cast<QQuickLabelPrivate *>(d_func());
385     if (!d->background)
386         d->executeBackground();
387     return d->background;
388 }
389 
setBackground(QQuickItem * background)390 void QQuickLabel::setBackground(QQuickItem *background)
391 {
392     Q_D(QQuickLabel);
393     if (d->background == background)
394         return;
395 
396     if (!d->background.isExecuting())
397         d->cancelBackground();
398 
399     const qreal oldImplicitBackgroundWidth = implicitBackgroundWidth();
400     const qreal oldImplicitBackgroundHeight = implicitBackgroundHeight();
401 
402     if (d->extra.isAllocated()) {
403         d->extra.value().hasBackgroundWidth = false;
404         d->extra.value().hasBackgroundHeight = false;
405     }
406 
407     QQuickControlPrivate::removeImplicitSizeListener(d->background, d, QQuickControlPrivate::ImplicitSizeChanges | QQuickItemPrivate::Geometry);
408     QQuickControlPrivate::hideOldItem(d->background);
409     d->background = background;
410 
411     if (background) {
412         background->setParentItem(this);
413         if (qFuzzyIsNull(background->z()))
414             background->setZ(-1);
415         QQuickItemPrivate *p = QQuickItemPrivate::get(background);
416         if (p->widthValid || p->heightValid) {
417             d->extra.value().hasBackgroundWidth = p->widthValid;
418             d->extra.value().hasBackgroundHeight = p->heightValid;
419         }
420         if (isComponentComplete())
421             d->resizeBackground();
422         QQuickControlPrivate::addImplicitSizeListener(background, d, QQuickControlPrivate::ImplicitSizeChanges | QQuickItemPrivate::Geometry);
423     }
424 
425     if (!qFuzzyCompare(oldImplicitBackgroundWidth, implicitBackgroundWidth()))
426         emit implicitBackgroundWidthChanged();
427     if (!qFuzzyCompare(oldImplicitBackgroundHeight, implicitBackgroundHeight()))
428         emit implicitBackgroundHeightChanged();
429     if (!d->background.isExecuting())
430         emit backgroundChanged();
431 }
432 
433 /*!
434     \since QtQuick.Controls 2.3 (Qt 5.10)
435     \qmlproperty palette QtQuick.Controls::Label::palette
436 
437     This property holds the palette currently set for the label.
438 
439     \sa Control::palette
440 */
palette() const441 QPalette QQuickLabel::palette() const
442 {
443     Q_D(const QQuickLabel);
444     QPalette palette = d->resolvedPalette;
445     if (!isEnabled())
446         palette.setCurrentColorGroup(QPalette::Disabled);
447     return palette;
448 }
449 
setPalette(const QPalette & palette)450 void QQuickLabel::setPalette(const QPalette &palette)
451 {
452     Q_D(QQuickLabel);
453     if (d->extra.value().requestedPalette.resolve() == palette.resolve() && d->extra.value().requestedPalette == palette)
454         return;
455 
456     d->extra.value().requestedPalette = palette;
457     d->resolvePalette();
458 }
459 
resetPalette()460 void QQuickLabel::resetPalette()
461 {
462     setPalette(QPalette());
463 }
464 
465 /*!
466     \since QtQuick.Controls 2.5 (Qt 5.12)
467     \qmlproperty real QtQuick.Controls::Label::implicitBackgroundWidth
468     \readonly
469 
470     This property holds the implicit background width.
471 
472     The value is equal to \c {background ? background.implicitWidth : 0}.
473 
474     \sa implicitBackgroundHeight
475 */
implicitBackgroundWidth() const476 qreal QQuickLabel::implicitBackgroundWidth() const
477 {
478     Q_D(const QQuickLabel);
479     if (!d->background)
480         return 0;
481     return d->background->implicitWidth();
482 }
483 
484 /*!
485     \since QtQuick.Controls 2.5 (Qt 5.12)
486     \qmlproperty real QtQuick.Controls::Label::implicitBackgroundHeight
487     \readonly
488 
489     This property holds the implicit background height.
490 
491     The value is equal to \c {background ? background.implicitHeight : 0}.
492 
493     \sa implicitBackgroundWidth
494 */
implicitBackgroundHeight() const495 qreal QQuickLabel::implicitBackgroundHeight() const
496 {
497     Q_D(const QQuickLabel);
498     if (!d->background)
499         return 0;
500     return d->background->implicitHeight();
501 }
502 
503 /*!
504     \since QtQuick.Controls 2.5 (Qt 5.12)
505     \qmlproperty real QtQuick.Controls::Label::topInset
506 
507     This property holds the top inset for the background.
508 
509     \sa {Control Layout}, bottomInset
510 */
topInset() const511 qreal QQuickLabel::topInset() const
512 {
513     Q_D(const QQuickLabel);
514     return d->getTopInset();
515 }
516 
setTopInset(qreal inset)517 void QQuickLabel::setTopInset(qreal inset)
518 {
519     Q_D(QQuickLabel);
520     d->setTopInset(inset);
521 }
522 
resetTopInset()523 void QQuickLabel::resetTopInset()
524 {
525     Q_D(QQuickLabel);
526     d->setTopInset(0, true);
527 }
528 
529 /*!
530     \since QtQuick.Controls 2.5 (Qt 5.12)
531     \qmlproperty real QtQuick.Controls::Label::leftInset
532 
533     This property holds the left inset for the background.
534 
535     \sa {Control Layout}, rightInset
536 */
leftInset() const537 qreal QQuickLabel::leftInset() const
538 {
539     Q_D(const QQuickLabel);
540     return d->getLeftInset();
541 }
542 
setLeftInset(qreal inset)543 void QQuickLabel::setLeftInset(qreal inset)
544 {
545     Q_D(QQuickLabel);
546     d->setLeftInset(inset);
547 }
548 
resetLeftInset()549 void QQuickLabel::resetLeftInset()
550 {
551     Q_D(QQuickLabel);
552     d->setLeftInset(0, true);
553 }
554 
555 /*!
556     \since QtQuick.Controls 2.5 (Qt 5.12)
557     \qmlproperty real QtQuick.Controls::Label::rightInset
558 
559     This property holds the right inset for the background.
560 
561     \sa {Control Layout}, leftInset
562 */
rightInset() const563 qreal QQuickLabel::rightInset() const
564 {
565     Q_D(const QQuickLabel);
566     return d->getRightInset();
567 }
568 
setRightInset(qreal inset)569 void QQuickLabel::setRightInset(qreal inset)
570 {
571     Q_D(QQuickLabel);
572     d->setRightInset(inset);
573 }
574 
resetRightInset()575 void QQuickLabel::resetRightInset()
576 {
577     Q_D(QQuickLabel);
578     d->setRightInset(0, true);
579 }
580 
581 /*!
582     \since QtQuick.Controls 2.5 (Qt 5.12)
583     \qmlproperty real QtQuick.Controls::Label::bottomInset
584 
585     This property holds the bottom inset for the background.
586 
587     \sa {Control Layout}, topInset
588 */
bottomInset() const589 qreal QQuickLabel::bottomInset() const
590 {
591     Q_D(const QQuickLabel);
592     return d->getBottomInset();
593 }
594 
setBottomInset(qreal inset)595 void QQuickLabel::setBottomInset(qreal inset)
596 {
597     Q_D(QQuickLabel);
598     d->setBottomInset(inset);
599 }
600 
resetBottomInset()601 void QQuickLabel::resetBottomInset()
602 {
603     Q_D(QQuickLabel);
604     d->setBottomInset(0, true);
605 }
606 
classBegin()607 void QQuickLabel::classBegin()
608 {
609     Q_D(QQuickLabel);
610     QQuickText::classBegin();
611     d->resolveFont();
612     d->resolvePalette();
613 }
614 
componentComplete()615 void QQuickLabel::componentComplete()
616 {
617     Q_D(QQuickLabel);
618     d->executeBackground(true);
619     QQuickText::componentComplete();
620     d->resizeBackground();
621 #if QT_CONFIG(accessibility)
622     if (QAccessible::isActive())
623         d->accessibilityActiveChanged(true);
624 #endif
625 }
626 
itemChange(QQuickItem::ItemChange change,const QQuickItem::ItemChangeData & value)627 void QQuickLabel::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
628 {
629     Q_D(QQuickLabel);
630     QQuickText::itemChange(change, value);
631     switch (change) {
632     case ItemEnabledHasChanged:
633         emit paletteChanged();
634         break;
635     case ItemSceneChange:
636     case ItemParentHasChanged:
637         if ((change == ItemParentHasChanged && value.item) || (change == ItemSceneChange && value.window)) {
638             d->resolveFont();
639             d->resolvePalette();
640         }
641         break;
642     default:
643         break;
644     }
645 }
646 
geometryChanged(const QRectF & newGeometry,const QRectF & oldGeometry)647 void QQuickLabel::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
648 {
649     Q_D(QQuickLabel);
650     QQuickText::geometryChanged(newGeometry, oldGeometry);
651     d->resizeBackground();
652 }
653 
insetChange(const QMarginsF & newInset,const QMarginsF & oldInset)654 void QQuickLabel::insetChange(const QMarginsF &newInset, const QMarginsF &oldInset)
655 {
656     Q_D(QQuickLabel);
657     Q_UNUSED(newInset);
658     Q_UNUSED(oldInset);
659     d->resizeBackground();
660 }
661 
662 QT_END_NAMESPACE
663