/**************************************************************************** ** ** Copyright (C) 2017 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL3$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see http://www.qt.io/terms-conditions. For further ** information use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 3 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPLv3 included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 3 requirements ** will be met: https://www.gnu.org/licenses/lgpl.html. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 2.0 or later as published by the Free ** Software Foundation and appearing in the file LICENSE.GPL included in ** the packaging of this file. Please review the following information to ** ensure the GNU General Public License version 2.0 requirements will be ** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qquickiconlabel_p.h" #include "qquickiconlabel_p_p.h" #include "qquickiconimage_p.h" #include "qquickmnemoniclabel_p.h" #include #include #include QT_BEGIN_NAMESPACE static void beginClass(QQuickItem *item) { if (QQmlParserStatus *parserStatus = qobject_cast(item)) parserStatus->classBegin(); } static void completeComponent(QQuickItem *item) { if (QQmlParserStatus *parserStatus = qobject_cast(item)) parserStatus->componentComplete(); } bool QQuickIconLabelPrivate::hasIcon() const { return display != QQuickIconLabel::TextOnly && !icon.isEmpty(); } bool QQuickIconLabelPrivate::hasText() const { return display != QQuickIconLabel::IconOnly && !text.isEmpty(); } bool QQuickIconLabelPrivate::createImage() { Q_Q(QQuickIconLabel); if (image) return false; image = new QQuickIconImage(q); watchChanges(image); beginClass(image); image->setObjectName(QStringLiteral("image")); image->setName(icon.name()); image->setSource(icon.source()); image->setSourceSize(QSize(icon.width(), icon.height())); image->setColor(icon.color()); image->setCache(icon.cache()); QQmlEngine::setContextForObject(image, qmlContext(q)); if (componentComplete) completeComponent(image); return true; } bool QQuickIconLabelPrivate::destroyImage() { if (!image) return false; unwatchChanges(image); delete image; image = nullptr; return true; } bool QQuickIconLabelPrivate::updateImage() { if (!hasIcon()) return destroyImage(); return createImage(); } void QQuickIconLabelPrivate::syncImage() { if (!image || icon.isEmpty()) return; image->setName(icon.name()); image->setSource(icon.source()); image->setSourceSize(QSize(icon.width(), icon.height())); image->setColor(icon.color()); image->setCache(icon.cache()); const int valign = alignment & Qt::AlignVertical_Mask; image->setVerticalAlignment(static_cast(valign)); const int halign = alignment & Qt::AlignHorizontal_Mask; image->setHorizontalAlignment(static_cast(halign)); } void QQuickIconLabelPrivate::updateOrSyncImage() { if (updateImage()) { if (componentComplete) { updateImplicitSize(); layout(); } } else { syncImage(); } } bool QQuickIconLabelPrivate::createLabel() { Q_Q(QQuickIconLabel); if (label) return false; label = new QQuickMnemonicLabel(q); watchChanges(label); beginClass(label); label->setObjectName(QStringLiteral("label")); label->setFont(font); label->setColor(color); label->setElideMode(QQuickText::ElideRight); const int valign = alignment & Qt::AlignVertical_Mask; label->setVAlign(static_cast(valign)); const int halign = alignment & Qt::AlignHorizontal_Mask; label->setHAlign(static_cast(halign)); label->setText(text); if (componentComplete) completeComponent(label); return true; } bool QQuickIconLabelPrivate::destroyLabel() { if (!label) return false; unwatchChanges(label); delete label; label = nullptr; return true; } bool QQuickIconLabelPrivate::updateLabel() { if (!hasText()) return destroyLabel(); return createLabel(); } void QQuickIconLabelPrivate::syncLabel() { if (!label) return; label->setText(text); } void QQuickIconLabelPrivate::updateOrSyncLabel() { if (updateLabel()) { if (componentComplete) { updateImplicitSize(); layout(); } } else { syncLabel(); } } void QQuickIconLabelPrivate::updateImplicitSize() { Q_Q(QQuickIconLabel); const bool showIcon = image && hasIcon(); const bool showText = label && hasText(); const qreal horizontalPadding = leftPadding + rightPadding; const qreal verticalPadding = topPadding + bottomPadding; const qreal iconImplicitWidth = showIcon ? image->implicitWidth() : 0; const qreal iconImplicitHeight = showIcon ? image->implicitHeight() : 0; const qreal textImplicitWidth = showText ? label->implicitWidth() : 0; const qreal textImplicitHeight = showText ? label->implicitHeight() : 0; const qreal effectiveSpacing = showText && showIcon && image->implicitWidth() > 0 ? spacing : 0; const qreal implicitWidth = display == QQuickIconLabel::TextBesideIcon ? iconImplicitWidth + textImplicitWidth + effectiveSpacing : qMax(iconImplicitWidth, textImplicitWidth); const qreal implicitHeight = display == QQuickIconLabel::TextUnderIcon ? iconImplicitHeight + textImplicitHeight + effectiveSpacing : qMax(iconImplicitHeight, textImplicitHeight); q->setImplicitSize(implicitWidth + horizontalPadding, implicitHeight + verticalPadding); } // adapted from QStyle::alignedRect() static QRectF alignedRect(bool mirrored, Qt::Alignment alignment, const QSizeF &size, const QRectF &rectangle) { alignment = QGuiApplicationPrivate::visualAlignment(mirrored ? Qt::RightToLeft : Qt::LeftToRight, alignment); qreal x = rectangle.x(); qreal y = rectangle.y(); const qreal w = size.width(); const qreal h = size.height(); if ((alignment & Qt::AlignVCenter) == Qt::AlignVCenter) y += rectangle.height() / 2 - h / 2; else if ((alignment & Qt::AlignBottom) == Qt::AlignBottom) y += rectangle.height() - h; if ((alignment & Qt::AlignRight) == Qt::AlignRight) x += rectangle.width() - w; else if ((alignment & Qt::AlignHCenter) == Qt::AlignHCenter) x += rectangle.width() / 2 - w / 2; return QRectF(x, y, w, h); } void QQuickIconLabelPrivate::layout() { Q_Q(QQuickIconLabel); if (!componentComplete) return; const qreal availableWidth = width - leftPadding - rightPadding; const qreal availableHeight = height - topPadding - bottomPadding; switch (display) { case QQuickIconLabel::IconOnly: if (image) { const QRectF iconRect = alignedRect(mirrored, alignment, QSizeF(qMin(image->implicitWidth(), availableWidth), qMin(image->implicitHeight(), availableHeight)), QRectF(leftPadding, topPadding, availableWidth, availableHeight)); image->setSize(iconRect.size()); image->setPosition(iconRect.topLeft()); } break; case QQuickIconLabel::TextOnly: if (label) { const QRectF textRect = alignedRect(mirrored, alignment, QSizeF(qMin(label->implicitWidth(), availableWidth), qMin(label->implicitHeight(), availableHeight)), QRectF(leftPadding, topPadding, availableWidth, availableHeight)); label->setSize(textRect.size()); label->setPosition(textRect.topLeft()); } break; case QQuickIconLabel::TextUnderIcon: { // Work out the sizes first, as the positions depend on them. QSizeF iconSize; QSizeF textSize; if (image) { iconSize.setWidth(qMin(image->implicitWidth(), availableWidth)); iconSize.setHeight(qMin(image->implicitHeight(), availableHeight)); } qreal effectiveSpacing = 0; if (label) { if (!iconSize.isEmpty()) effectiveSpacing = spacing; textSize.setWidth(qMin(label->implicitWidth(), availableWidth)); textSize.setHeight(qMin(label->implicitHeight(), availableHeight - iconSize.height() - effectiveSpacing)); } QRectF combinedRect = alignedRect(mirrored, alignment, QSizeF(qMax(iconSize.width(), textSize.width()), iconSize.height() + effectiveSpacing + textSize.height()), QRectF(leftPadding, topPadding, availableWidth, availableHeight)); if (image) { QRectF iconRect = alignedRect(mirrored, Qt::AlignHCenter | Qt::AlignTop, iconSize, combinedRect); image->setSize(iconRect.size()); image->setPosition(iconRect.topLeft()); } if (label) { QRectF textRect = alignedRect(mirrored, Qt::AlignHCenter | Qt::AlignBottom, textSize, combinedRect); label->setSize(textRect.size()); label->setPosition(textRect.topLeft()); } break; } case QQuickIconLabel::TextBesideIcon: default: // Work out the sizes first, as the positions depend on them. QSizeF iconSize(0, 0); QSizeF textSize(0, 0); if (image) { iconSize.setWidth(qMin(image->implicitWidth(), availableWidth)); iconSize.setHeight(qMin(image->implicitHeight(), availableHeight)); } qreal effectiveSpacing = 0; if (label) { if (!iconSize.isEmpty()) effectiveSpacing = spacing; textSize.setWidth(qMin(label->implicitWidth(), availableWidth - iconSize.width() - effectiveSpacing)); textSize.setHeight(qMin(label->implicitHeight(), availableHeight)); } const QRectF combinedRect = alignedRect(mirrored, alignment, QSizeF(iconSize.width() + effectiveSpacing + textSize.width(), qMax(iconSize.height(), textSize.height())), QRectF(leftPadding, topPadding, availableWidth, availableHeight)); if (image) { const QRectF iconRect = alignedRect(mirrored, Qt::AlignLeft | Qt::AlignVCenter, iconSize, combinedRect); image->setSize(iconRect.size()); image->setPosition(iconRect.topLeft()); } if (label) { const QRectF textRect = alignedRect(mirrored, Qt::AlignRight | Qt::AlignVCenter, textSize, combinedRect); label->setSize(textRect.size()); label->setPosition(textRect.topLeft()); } break; } q->setBaselineOffset(label ? label->y() + label->baselineOffset() : 0); } static const QQuickItemPrivate::ChangeTypes itemChangeTypes = QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight | QQuickItemPrivate::Destroyed; void QQuickIconLabelPrivate::watchChanges(QQuickItem *item) { QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); itemPrivate->addItemChangeListener(this, itemChangeTypes); } void QQuickIconLabelPrivate::unwatchChanges(QQuickItem* item) { QQuickItemPrivate *itemPrivate = QQuickItemPrivate::get(item); itemPrivate->removeItemChangeListener(this, itemChangeTypes); } void QQuickIconLabelPrivate::itemImplicitWidthChanged(QQuickItem *) { updateImplicitSize(); layout(); } void QQuickIconLabelPrivate::itemImplicitHeightChanged(QQuickItem *) { updateImplicitSize(); layout(); } void QQuickIconLabelPrivate::itemDestroyed(QQuickItem *item) { unwatchChanges(item); if (item == image) image = nullptr; else if (item == label) label = nullptr; } QQuickIconLabel::QQuickIconLabel(QQuickItem *parent) : QQuickItem(*(new QQuickIconLabelPrivate), parent) { } QQuickIconLabel::~QQuickIconLabel() { Q_D(QQuickIconLabel); if (d->image) d->unwatchChanges(d->image); if (d->label) d->unwatchChanges(d->label); } QQuickIcon QQuickIconLabel::icon() const { Q_D(const QQuickIconLabel); return d->icon; } void QQuickIconLabel::setIcon(const QQuickIcon &icon) { Q_D(QQuickIconLabel); if (d->icon == icon) return; d->icon = icon; d->updateOrSyncImage(); } QString QQuickIconLabel::text() const { Q_D(const QQuickIconLabel); return d->text; } void QQuickIconLabel::setText(const QString &text) { Q_D(QQuickIconLabel); if (d->text == text) return; d->text = text; d->updateOrSyncLabel(); } QFont QQuickIconLabel::font() const { Q_D(const QQuickIconLabel); return d->font; } void QQuickIconLabel::setFont(const QFont &font) { Q_D(QQuickIconLabel); if (d->font == font) return; d->font = font; if (d->label) d->label->setFont(font); } QColor QQuickIconLabel::color() const { Q_D(const QQuickIconLabel); return d->color; } void QQuickIconLabel::setColor(const QColor &color) { Q_D(QQuickIconLabel); if (d->color == color) return; d->color = color; if (d->label) d->label->setColor(color); } QQuickIconLabel::Display QQuickIconLabel::display() const { Q_D(const QQuickIconLabel); return d->display; } void QQuickIconLabel::setDisplay(Display display) { Q_D(QQuickIconLabel); if (d->display == display) return; d->display = display; d->updateImage(); d->updateLabel(); d->updateImplicitSize(); d->layout(); } qreal QQuickIconLabel::spacing() const { Q_D(const QQuickIconLabel); return d->spacing; } void QQuickIconLabel::setSpacing(qreal spacing) { Q_D(QQuickIconLabel); if (qFuzzyCompare(d->spacing, spacing)) return; d->spacing = spacing; if (d->image && d->label) { d->updateImplicitSize(); d->layout(); } } bool QQuickIconLabel::isMirrored() const { Q_D(const QQuickIconLabel); return d->mirrored; } void QQuickIconLabel::setMirrored(bool mirrored) { Q_D(QQuickIconLabel); if (d->mirrored == mirrored) return; d->mirrored = mirrored; d->layout(); } Qt::Alignment QQuickIconLabel::alignment() const { Q_D(const QQuickIconLabel); return d->alignment; } void QQuickIconLabel::setAlignment(Qt::Alignment alignment) { Q_D(QQuickIconLabel); const int valign = alignment & Qt::AlignVertical_Mask; const int halign = alignment & Qt::AlignHorizontal_Mask; const uint align = (valign ? valign : Qt::AlignVCenter) | (halign ? halign : Qt::AlignHCenter); if (d->alignment == align) return; d->alignment = static_cast(align); if (d->label) { d->label->setVAlign(static_cast(valign)); d->label->setHAlign(static_cast(halign)); } if (d->image) { d->image->setVerticalAlignment(static_cast(valign)); d->image->setHorizontalAlignment(static_cast(halign)); } d->layout(); } qreal QQuickIconLabel::topPadding() const { Q_D(const QQuickIconLabel); return d->topPadding; } void QQuickIconLabel::setTopPadding(qreal padding) { Q_D(QQuickIconLabel); if (qFuzzyCompare(d->topPadding, padding)) return; d->topPadding = padding; d->updateImplicitSize(); d->layout(); } void QQuickIconLabel::resetTopPadding() { setTopPadding(0); } qreal QQuickIconLabel::leftPadding() const { Q_D(const QQuickIconLabel); return d->leftPadding; } void QQuickIconLabel::setLeftPadding(qreal padding) { Q_D(QQuickIconLabel); if (qFuzzyCompare(d->leftPadding, padding)) return; d->leftPadding = padding; d->updateImplicitSize(); d->layout(); } void QQuickIconLabel::resetLeftPadding() { setLeftPadding(0); } qreal QQuickIconLabel::rightPadding() const { Q_D(const QQuickIconLabel); return d->rightPadding; } void QQuickIconLabel::setRightPadding(qreal padding) { Q_D(QQuickIconLabel); if (qFuzzyCompare(d->rightPadding, padding)) return; d->rightPadding = padding; d->updateImplicitSize(); d->layout(); } void QQuickIconLabel::resetRightPadding() { setRightPadding(0); } qreal QQuickIconLabel::bottomPadding() const { Q_D(const QQuickIconLabel); return d->bottomPadding; } void QQuickIconLabel::setBottomPadding(qreal padding) { Q_D(QQuickIconLabel); if (qFuzzyCompare(d->bottomPadding, padding)) return; d->bottomPadding = padding; d->updateImplicitSize(); d->layout(); } void QQuickIconLabel::resetBottomPadding() { setBottomPadding(0); } void QQuickIconLabel::componentComplete() { Q_D(QQuickIconLabel); if (d->image) completeComponent(d->image); if (d->label) completeComponent(d->label); QQuickItem::componentComplete(); d->layout(); } void QQuickIconLabel::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) { Q_D(QQuickIconLabel); QQuickItem::geometryChange(newGeometry, oldGeometry); d->layout(); } QT_END_NAMESPACE