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

source code of qtdeclarative/src/quickcontrolsimpl/qquickiconlabel.cpp