blob: 536b3c1b0a025b6bb9c48da2e44de0c3d2fe8a01 [file] [log] [blame]
/****************************************************************************
**
** 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 <QtGui/private/qguiapplication_p.h>
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qquicktext_p.h>
QT_BEGIN_NAMESPACE
static void beginClass(QQuickItem *item)
{
if (QQmlParserStatus *parserStatus = qobject_cast<QQmlParserStatus *>(item))
parserStatus->classBegin();
}
static void completeComponent(QQuickItem *item)
{
if (QQmlParserStatus *parserStatus = qobject_cast<QQmlParserStatus *>(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<QQuickImage::VAlignment>(valign));
const int halign = alignment & Qt::AlignHorizontal_Mask;
image->setHorizontalAlignment(static_cast<QQuickImage::HAlignment>(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<QQuickText::VAlignment>(valign));
const int halign = alignment & Qt::AlignHorizontal_Mask;
label->setHAlign(static_cast<QQuickText::HAlignment>(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<Qt::Alignment>(align);
if (d->label) {
d->label->setVAlign(static_cast<QQuickText::VAlignment>(valign));
d->label->setHAlign(static_cast<QQuickText::HAlignment>(halign));
}
if (d->image) {
d->image->setVerticalAlignment(static_cast<QQuickImage::VAlignment>(valign));
d->image->setHorizontalAlignment(static_cast<QQuickImage::HAlignment>(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::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
{
Q_D(QQuickIconLabel);
QQuickItem::geometryChanged(newGeometry, oldGeometry);
d->layout();
}
QT_END_NAMESPACE