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 Controls 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 "qquickiconlabel_p.h"
38#include "qquickiconlabel_p_p.h"
39#include "qquickiconimage_p.h"
40#include "qquickmnemoniclabel_p.h"
41
42#include <QtGui/private/qguiapplication_p.h>
43#include <QtQuick/private/qquickitem_p.h>
44#include <QtQuick/private/qquicktext_p.h>
45
46QT_BEGIN_NAMESPACE
47
48static void beginClass(QQuickItem *item)
49{
50 if (QQmlParserStatus *parserStatus = qobject_cast<QQmlParserStatus *>(object: item))
51 parserStatus->classBegin();
52}
53
54static void completeComponent(QQuickItem *item)
55{
56 if (QQmlParserStatus *parserStatus = qobject_cast<QQmlParserStatus *>(object: item))
57 parserStatus->componentComplete();
58}
59
60bool QQuickIconLabelPrivate::hasIcon() const
61{
62 return display != QQuickIconLabel::TextOnly && !icon.isEmpty();
63}
64
65bool QQuickIconLabelPrivate::hasText() const
66{
67 return display != QQuickIconLabel::IconOnly && !text.isEmpty();
68}
69
70bool QQuickIconLabelPrivate::createImage()
71{
72 Q_Q(QQuickIconLabel);
73 if (image)
74 return false;
75
76 image = new QQuickIconImage(q);
77 watchChanges(item: image);
78 beginClass(item: image);
79 image->setObjectName(QStringLiteral("image"));
80 image->setName(icon.name());
81 image->setSource(icon.source());
82 image->setSourceSize(QSize(icon.width(), icon.height()));
83 image->setColor(icon.color());
84 image->setCache(icon.cache());
85 QQmlEngine::setContextForObject(image, qmlContext(q));
86 if (componentComplete)
87 completeComponent(item: image);
88 return true;
89}
90
91bool QQuickIconLabelPrivate::destroyImage()
92{
93 if (!image)
94 return false;
95
96 unwatchChanges(item: image);
97 delete image;
98 image = nullptr;
99 return true;
100}
101
102bool QQuickIconLabelPrivate::updateImage()
103{
104 if (!hasIcon())
105 return destroyImage();
106 return createImage();
107}
108
109void QQuickIconLabelPrivate::syncImage()
110{
111 if (!image || icon.isEmpty())
112 return;
113
114 image->setName(icon.name());
115 image->setSource(icon.source());
116 image->setSourceSize(QSize(icon.width(), icon.height()));
117 image->setColor(icon.color());
118 image->setCache(icon.cache());
119 const int valign = alignment & Qt::AlignVertical_Mask;
120 image->setVerticalAlignment(static_cast<QQuickImage::VAlignment>(valign));
121 const int halign = alignment & Qt::AlignHorizontal_Mask;
122 image->setHorizontalAlignment(static_cast<QQuickImage::HAlignment>(halign));
123}
124
125void QQuickIconLabelPrivate::updateOrSyncImage()
126{
127 if (updateImage()) {
128 if (componentComplete) {
129 updateImplicitSize();
130 layout();
131 }
132 } else {
133 syncImage();
134 }
135}
136
137bool QQuickIconLabelPrivate::createLabel()
138{
139 Q_Q(QQuickIconLabel);
140 if (label)
141 return false;
142
143 label = new QQuickMnemonicLabel(q);
144 watchChanges(item: label);
145 beginClass(item: label);
146 label->setObjectName(QStringLiteral("label"));
147 label->setFont(font);
148 label->setColor(color);
149 label->setElideMode(QQuickText::ElideRight);
150 const int valign = alignment & Qt::AlignVertical_Mask;
151 label->setVAlign(static_cast<QQuickText::VAlignment>(valign));
152 const int halign = alignment & Qt::AlignHorizontal_Mask;
153 label->setHAlign(static_cast<QQuickText::HAlignment>(halign));
154 label->setText(text);
155 if (componentComplete)
156 completeComponent(item: label);
157 return true;
158}
159
160bool QQuickIconLabelPrivate::destroyLabel()
161{
162 if (!label)
163 return false;
164
165 unwatchChanges(item: label);
166 delete label;
167 label = nullptr;
168 return true;
169}
170
171bool QQuickIconLabelPrivate::updateLabel()
172{
173 if (!hasText())
174 return destroyLabel();
175 return createLabel();
176}
177
178void QQuickIconLabelPrivate::syncLabel()
179{
180 if (!label)
181 return;
182
183 label->setText(text);
184}
185
186void QQuickIconLabelPrivate::updateOrSyncLabel()
187{
188 if (updateLabel()) {
189 if (componentComplete) {
190 updateImplicitSize();
191 layout();
192 }
193 } else {
194 syncLabel();
195 }
196}
197
198void QQuickIconLabelPrivate::updateImplicitSize()
199{
200 Q_Q(QQuickIconLabel);
201 const bool showIcon = image && hasIcon();
202 const bool showText = label && hasText();
203 const qreal horizontalPadding = leftPadding + rightPadding;
204 const qreal verticalPadding = topPadding + bottomPadding;
205 const qreal iconImplicitWidth = showIcon ? image->implicitWidth() : 0;
206 const qreal iconImplicitHeight = showIcon ? image->implicitHeight() : 0;
207 const qreal textImplicitWidth = showText ? label->implicitWidth() : 0;
208 const qreal textImplicitHeight = showText ? label->implicitHeight() : 0;
209 const qreal effectiveSpacing = showText && showIcon && image->implicitWidth() > 0 ? spacing : 0;
210 const qreal implicitWidth = display == QQuickIconLabel::TextBesideIcon ? iconImplicitWidth + textImplicitWidth + effectiveSpacing
211 : qMax(a: iconImplicitWidth, b: textImplicitWidth);
212 const qreal implicitHeight = display == QQuickIconLabel::TextUnderIcon ? iconImplicitHeight + textImplicitHeight + effectiveSpacing
213 : qMax(a: iconImplicitHeight, b: textImplicitHeight);
214 q->setImplicitSize(implicitWidth + horizontalPadding, implicitHeight + verticalPadding);
215}
216
217// adapted from QStyle::alignedRect()
218static QRectF alignedRect(bool mirrored, Qt::Alignment alignment, const QSizeF &size, const QRectF &rectangle)
219{
220 alignment = QGuiApplicationPrivate::visualAlignment(direction: mirrored ? Qt::RightToLeft : Qt::LeftToRight, alignment);
221 qreal x = rectangle.x();
222 qreal y = rectangle.y();
223 const qreal w = size.width();
224 const qreal h = size.height();
225 if ((alignment & Qt::AlignVCenter) == Qt::AlignVCenter)
226 y += rectangle.height() / 2 - h / 2;
227 else if ((alignment & Qt::AlignBottom) == Qt::AlignBottom)
228 y += rectangle.height() - h;
229 if ((alignment & Qt::AlignRight) == Qt::AlignRight)
230 x += rectangle.width() - w;
231 else if ((alignment & Qt::AlignHCenter) == Qt::AlignHCenter)
232 x += rectangle.width() / 2 - w / 2;
233 return QRectF(x, y, w, h);
234}
235
236void QQuickIconLabelPrivate::layout()
237{
238 Q_Q(QQuickIconLabel);
239 if (!componentComplete)
240 return;
241
242 const qreal availableWidth = width - leftPadding - rightPadding;
243 const qreal availableHeight = height - topPadding - bottomPadding;
244
245 switch (display) {
246 case QQuickIconLabel::IconOnly:
247 if (image) {
248 const QRectF iconRect = alignedRect(mirrored, alignment,
249 size: QSizeF(qMin(a: image->implicitWidth(), b: availableWidth),
250 qMin(a: image->implicitHeight(), b: availableHeight)),
251 rectangle: QRectF(leftPadding, topPadding, availableWidth, availableHeight));
252 image->setSize(iconRect.size());
253 image->setPosition(iconRect.topLeft());
254 }
255 break;
256 case QQuickIconLabel::TextOnly:
257 if (label) {
258 const QRectF textRect = alignedRect(mirrored, alignment,
259 size: QSizeF(qMin(a: label->implicitWidth(), b: availableWidth),
260 qMin(a: label->implicitHeight(), b: availableHeight)),
261 rectangle: QRectF(leftPadding, topPadding, availableWidth, availableHeight));
262 label->setSize(textRect.size());
263 label->setPosition(textRect.topLeft());
264 }
265 break;
266
267 case QQuickIconLabel::TextUnderIcon: {
268 // Work out the sizes first, as the positions depend on them.
269 QSizeF iconSize;
270 QSizeF textSize;
271 if (image) {
272 iconSize.setWidth(qMin(a: image->implicitWidth(), b: availableWidth));
273 iconSize.setHeight(qMin(a: image->implicitHeight(), b: availableHeight));
274 }
275 qreal effectiveSpacing = 0;
276 if (label) {
277 if (!iconSize.isEmpty())
278 effectiveSpacing = spacing;
279 textSize.setWidth(qMin(a: label->implicitWidth(), b: availableWidth));
280 textSize.setHeight(qMin(a: label->implicitHeight(), b: availableHeight - iconSize.height() - effectiveSpacing));
281 }
282
283 QRectF combinedRect = alignedRect(mirrored, alignment,
284 size: QSizeF(qMax(a: iconSize.width(), b: textSize.width()),
285 iconSize.height() + effectiveSpacing + textSize.height()),
286 rectangle: QRectF(leftPadding, topPadding, availableWidth, availableHeight));
287 if (image) {
288 QRectF iconRect = alignedRect(mirrored, alignment: Qt::AlignHCenter | Qt::AlignTop, size: iconSize, rectangle: combinedRect);
289 image->setSize(iconRect.size());
290 image->setPosition(iconRect.topLeft());
291 }
292 if (label) {
293 QRectF textRect = alignedRect(mirrored, alignment: Qt::AlignHCenter | Qt::AlignBottom, size: textSize, rectangle: combinedRect);
294 label->setSize(textRect.size());
295 label->setPosition(textRect.topLeft());
296 }
297 break;
298 }
299
300 case QQuickIconLabel::TextBesideIcon:
301 default:
302 // Work out the sizes first, as the positions depend on them.
303 QSizeF iconSize(0, 0);
304 QSizeF textSize(0, 0);
305 if (image) {
306 iconSize.setWidth(qMin(a: image->implicitWidth(), b: availableWidth));
307 iconSize.setHeight(qMin(a: image->implicitHeight(), b: availableHeight));
308 }
309 qreal effectiveSpacing = 0;
310 if (label) {
311 if (!iconSize.isEmpty())
312 effectiveSpacing = spacing;
313 textSize.setWidth(qMin(a: label->implicitWidth(), b: availableWidth - iconSize.width() - effectiveSpacing));
314 textSize.setHeight(qMin(a: label->implicitHeight(), b: availableHeight));
315 }
316
317 const QRectF combinedRect = alignedRect(mirrored, alignment,
318 size: QSizeF(iconSize.width() + effectiveSpacing + textSize.width(),
319 qMax(a: iconSize.height(), b: textSize.height())),
320 rectangle: QRectF(leftPadding, topPadding, availableWidth, availableHeight));
321 if (image) {
322 const QRectF iconRect = alignedRect(mirrored, alignment: Qt::AlignLeft | Qt::AlignVCenter, size: iconSize, rectangle: combinedRect);
323 image->setSize(iconRect.size());
324 image->setPosition(iconRect.topLeft());
325 }
326 if (label) {
327 const QRectF textRect = alignedRect(mirrored, alignment: Qt::AlignRight | Qt::AlignVCenter, size: textSize, rectangle: combinedRect);
328 label->setSize(textRect.size());
329 label->setPosition(textRect.topLeft());
330 }
331 break;
332 }
333
334 q->setBaselineOffset(label ? label->y() + label->baselineOffset() : 0);
335}
336
337static const QQuickItemPrivate::ChangeTypes itemChangeTypes =
338 QQuickItemPrivate::ImplicitWidth
339 | QQuickItemPrivate::ImplicitHeight
340 | QQuickItemPrivate::Destroyed;
341
342void QQuickIconLabelPrivate::watchChanges(QQuickItem *item)
343{
344 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
345 itemPrivate->addItemChangeListener(listener: this, types: itemChangeTypes);
346}
347
348void QQuickIconLabelPrivate::unwatchChanges(QQuickItem* item)
349{
350 QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item);
351 itemPrivate->removeItemChangeListener(this, types: itemChangeTypes);
352}
353
354void QQuickIconLabelPrivate::itemImplicitWidthChanged(QQuickItem *)
355{
356 updateImplicitSize();
357 layout();
358}
359
360void QQuickIconLabelPrivate::itemImplicitHeightChanged(QQuickItem *)
361{
362 updateImplicitSize();
363 layout();
364}
365
366void QQuickIconLabelPrivate::itemDestroyed(QQuickItem *item)
367{
368 unwatchChanges(item);
369 if (item == image)
370 image = nullptr;
371 else if (item == label)
372 label = nullptr;
373}
374
375QQuickIconLabel::QQuickIconLabel(QQuickItem *parent)
376 : QQuickItem(*(new QQuickIconLabelPrivate), parent)
377{
378}
379
380QQuickIconLabel::~QQuickIconLabel()
381{
382 Q_D(QQuickIconLabel);
383 if (d->image)
384 d->unwatchChanges(item: d->image);
385 if (d->label)
386 d->unwatchChanges(item: d->label);
387}
388
389QQuickIcon QQuickIconLabel::icon() const
390{
391 Q_D(const QQuickIconLabel);
392 return d->icon;
393}
394
395void QQuickIconLabel::setIcon(const QQuickIcon &icon)
396{
397 Q_D(QQuickIconLabel);
398 if (d->icon == icon)
399 return;
400
401 d->icon = icon;
402 d->updateOrSyncImage();
403}
404
405QString QQuickIconLabel::text() const
406{
407 Q_D(const QQuickIconLabel);
408 return d->text;
409}
410
411void QQuickIconLabel::setText(const QString &text)
412{
413 Q_D(QQuickIconLabel);
414 if (d->text == text)
415 return;
416
417 d->text = text;
418 d->updateOrSyncLabel();
419}
420
421QFont QQuickIconLabel::font() const
422{
423 Q_D(const QQuickIconLabel);
424 return d->font;
425}
426
427void QQuickIconLabel::setFont(const QFont &font)
428{
429 Q_D(QQuickIconLabel);
430 if (d->font == font)
431 return;
432
433 d->font = font;
434 if (d->label)
435 d->label->setFont(font);
436}
437
438QColor QQuickIconLabel::color() const
439{
440 Q_D(const QQuickIconLabel);
441 return d->color;
442}
443
444void QQuickIconLabel::setColor(const QColor &color)
445{
446 Q_D(QQuickIconLabel);
447 if (d->color == color)
448 return;
449
450 d->color = color;
451 if (d->label)
452 d->label->setColor(color);
453}
454
455QQuickIconLabel::Display QQuickIconLabel::display() const
456{
457 Q_D(const QQuickIconLabel);
458 return d->display;
459}
460
461void QQuickIconLabel::setDisplay(Display display)
462{
463 Q_D(QQuickIconLabel);
464 if (d->display == display)
465 return;
466
467 d->display = display;
468 d->updateImage();
469 d->updateLabel();
470 d->updateImplicitSize();
471 d->layout();
472}
473
474qreal QQuickIconLabel::spacing() const
475{
476 Q_D(const QQuickIconLabel);
477 return d->spacing;
478}
479
480void QQuickIconLabel::setSpacing(qreal spacing)
481{
482 Q_D(QQuickIconLabel);
483 if (qFuzzyCompare(p1: d->spacing, p2: spacing))
484 return;
485
486 d->spacing = spacing;
487 if (d->image && d->label) {
488 d->updateImplicitSize();
489 d->layout();
490 }
491}
492
493bool QQuickIconLabel::isMirrored() const
494{
495 Q_D(const QQuickIconLabel);
496 return d->mirrored;
497}
498
499void QQuickIconLabel::setMirrored(bool mirrored)
500{
501 Q_D(QQuickIconLabel);
502 if (d->mirrored == mirrored)
503 return;
504
505 d->mirrored = mirrored;
506 d->layout();
507}
508
509Qt::Alignment QQuickIconLabel::alignment() const
510{
511 Q_D(const QQuickIconLabel);
512 return d->alignment;
513}
514
515void QQuickIconLabel::setAlignment(Qt::Alignment alignment)
516{
517 Q_D(QQuickIconLabel);
518 const int valign = alignment & Qt::AlignVertical_Mask;
519 const int halign = alignment & Qt::AlignHorizontal_Mask;
520 const uint align = (valign ? valign : Qt::AlignVCenter) | (halign ? halign : Qt::AlignHCenter);
521 if (d->alignment == align)
522 return;
523
524 d->alignment = static_cast<Qt::Alignment>(align);
525 if (d->label) {
526 d->label->setVAlign(static_cast<QQuickText::VAlignment>(valign));
527 d->label->setHAlign(static_cast<QQuickText::HAlignment>(halign));
528 }
529 if (d->image) {
530 d->image->setVerticalAlignment(static_cast<QQuickImage::VAlignment>(valign));
531 d->image->setHorizontalAlignment(static_cast<QQuickImage::HAlignment>(halign));
532 }
533 d->layout();
534}
535
536qreal QQuickIconLabel::topPadding() const
537{
538 Q_D(const QQuickIconLabel);
539 return d->topPadding;
540}
541
542void QQuickIconLabel::setTopPadding(qreal padding)
543{
544 Q_D(QQuickIconLabel);
545 if (qFuzzyCompare(p1: d->topPadding, p2: padding))
546 return;
547
548 d->topPadding = padding;
549 d->updateImplicitSize();
550 d->layout();
551}
552
553void QQuickIconLabel::resetTopPadding()
554{
555 setTopPadding(0);
556}
557
558qreal QQuickIconLabel::leftPadding() const
559{
560 Q_D(const QQuickIconLabel);
561 return d->leftPadding;
562}
563
564void QQuickIconLabel::setLeftPadding(qreal padding)
565{
566 Q_D(QQuickIconLabel);
567 if (qFuzzyCompare(p1: d->leftPadding, p2: padding))
568 return;
569
570 d->leftPadding = padding;
571 d->updateImplicitSize();
572 d->layout();
573}
574
575void QQuickIconLabel::resetLeftPadding()
576{
577 setLeftPadding(0);
578}
579
580qreal QQuickIconLabel::rightPadding() const
581{
582 Q_D(const QQuickIconLabel);
583 return d->rightPadding;
584}
585
586void QQuickIconLabel::setRightPadding(qreal padding)
587{
588 Q_D(QQuickIconLabel);
589 if (qFuzzyCompare(p1: d->rightPadding, p2: padding))
590 return;
591
592 d->rightPadding = padding;
593 d->updateImplicitSize();
594 d->layout();
595}
596
597void QQuickIconLabel::resetRightPadding()
598{
599 setRightPadding(0);
600}
601
602qreal QQuickIconLabel::bottomPadding() const
603{
604 Q_D(const QQuickIconLabel);
605 return d->bottomPadding;
606}
607
608void QQuickIconLabel::setBottomPadding(qreal padding)
609{
610 Q_D(QQuickIconLabel);
611 if (qFuzzyCompare(p1: d->bottomPadding, p2: padding))
612 return;
613
614 d->bottomPadding = padding;
615 d->updateImplicitSize();
616 d->layout();
617}
618
619void QQuickIconLabel::resetBottomPadding()
620{
621 setBottomPadding(0);
622}
623
624void QQuickIconLabel::componentComplete()
625{
626 Q_D(QQuickIconLabel);
627 if (d->image)
628 completeComponent(item: d->image);
629 if (d->label)
630 completeComponent(item: d->label);
631 QQuickItem::componentComplete();
632 d->layout();
633}
634
635void QQuickIconLabel::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
636{
637 Q_D(QQuickIconLabel);
638 QQuickItem::geometryChanged(newGeometry, oldGeometry);
639 d->layout();
640}
641
642QT_END_NAMESPACE
643

source code of qtquickcontrols2/src/quickcontrols2/qquickiconlabel.cpp