Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquickiconimage.cpp
Go to the documentation of this file.
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// Qt-Security score:significant reason:default
4
7
8#include <QtCore/qmath.h>
9#include <QtQuick/private/qquickimagebase_p_p.h>
10
12
13QQuickIconImagePrivate::~QQuickIconImagePrivate()
14{
15 icon.entries.clear();
16}
17
18bool QQuickIconImagePrivate::updateDevicePixelRatio(qreal targetDevicePixelRatio)
19{
20 if (isThemeIcon) {
21 devicePixelRatio = effectiveDevicePixelRatio();
22 return true;
23 }
24
25 return QQuickImagePrivate::updateDevicePixelRatio(targetDevicePixelRatio);
26}
27
28void QQuickIconImage::load()
29{
30 Q_D(QQuickIconImage);
31 // Both geometryChange() and QQuickImageBase::sourceSizeChanged()
32 // (which we connect to load() in the constructor) can be called as a result
33 // of load() changing the various sizes, so we must check that we're not recursing.
34 if (d->updatingIcon)
35 return;
36
37 d->updatingIcon = true;
38
39 QSize size = d->sourcesize;
40 // If no size is specified for theme icons, it will use the smallest available size.
41 if (size.width() <= 0)
42 size.setWidth(width());
43 if (size.height() <= 0)
44 size.setHeight(height());
45
46 const qreal dpr = d->effectiveDevicePixelRatio();
47
48 if (const auto *entry = QIconLoaderEngine::entryForSize(d->icon, size * dpr, qCeil(dpr))) {
49 // First, try to load an icon from the theme (index.theme).
50 QQmlContext *context = qmlContext(this);
51 const QUrl entryUrl = QUrl::fromLocalFile(entry->filename);
52 d->url = context ? context->resolvedUrl(entryUrl) : entryUrl;
53 d->isThemeIcon = true;
54 } else if (d->source.isEmpty()) {
55 // Then, try loading it through a platform icon engine.
56 std::unique_ptr<QIconEngine> iconEngine(QIconLoader::instance()->iconEngine(d->icon.iconName));
57 if (iconEngine && !iconEngine->isNull()) {
58 // ### TODO that's the best we can do for now to select different pixmaps based on the
59 // QuickItem's state. QQuickIconImage cannot know about the state of the control that
60 // uses it without adding more properties that are then synced up with the control.
61 const QIcon::Mode mode = isEnabled() ? QIcon::Normal : QIcon::Disabled;
62 const QImage image = iconEngine->scaledPixmap(size, mode, QIcon::Off, dpr).toImage();
63 d->devicePixelRatio = image.devicePixelRatio();
64 d->setImage(image);
65 }
66 } else {
67 // Either we failed to load a named icon or the source was set instead; use that.
68 d->url = d->source;
69 d->isThemeIcon = false;
70 }
71 if (!d->url.isEmpty())
72 QQuickImage::load();
73
74 d->updatingIcon = false;
75}
76
77void QQuickIconImagePrivate::updateFillMode()
78{
79 Q_Q(QQuickIconImage);
80 // If we start with a sourceSize of 28x28 and then set sourceSize.width to 24, the fillMode
81 // will change to PreserveAspectFit (because pixmapSize.width() > width()), which causes the
82 // pixmap to be reloaded at its original size of 28x28, which causes the fillMode to change
83 // to Pad (because pixmapSize.width() <= width()), and so on.
84 if (updatingFillMode)
85 return;
86
87 updatingFillMode = true;
88
89 const QSize pixmapSize = QSize(currentPix->width(), currentPix->height()) / effectiveDevicePixelRatio();
90 if (pixmapSize.width() > q->width() || pixmapSize.height() > q->height())
91 q->setFillMode(QQuickImage::PreserveAspectFit);
92 else
93 q->setFillMode(QQuickImage::Pad);
94
95 updatingFillMode = false;
96}
97
98QQuickIconImage::QQuickIconImage(QQuickItem *parent)
99 : QQuickImage(*(new QQuickIconImagePrivate), parent)
100{
101 setFillMode(Pad);
102}
103
104QString QQuickIconImage::name() const
105{
106 Q_D(const QQuickIconImage);
107 return d->icon.iconName;
108}
109
110void QQuickIconImage::setName(const QString &name)
111{
112 Q_D(QQuickIconImage);
113 if (d->icon.iconName == name)
114 return;
115
116 d->icon.entries.clear();
117 d->icon = QIconLoader::instance()->loadIcon(name);
118 if (d->icon.iconName.isEmpty())
119 d->icon.iconName = name;
120 if (isComponentComplete())
121 load();
122 emit nameChanged();
123}
124
125QColor QQuickIconImage::color() const
126{
127 Q_D(const QQuickIconImage);
128 return d->color;
129}
130
131void QQuickIconImage::setColor(const QColor &color)
132{
133 Q_D(QQuickIconImage);
134 if (d->color == color)
135 return;
136
137 d->color = color;
138 if (isComponentComplete())
139 load();
140 emit colorChanged();
141}
142
143void QQuickIconImage::setSource(const QUrl &source)
144{
145 Q_D(QQuickIconImage);
146 if (d->source == source)
147 return;
148
149 d->source = source;
150 if (isComponentComplete())
151 load();
152 emit sourceChanged(source);
153}
154
155void QQuickIconImage::snapPositionTo(QPointF pos)
156{
157 // Ensure that we are placed on an integer position relative to the owning control. We assume
158 // that there is exactly one intermediate parent item between us and the control (in practice,
159 // the contentItem). The idea is to enable the app, by placing the controls at integer
160 // positions, to avoid the image being smoothed over pixel boundaries in the basic, DPR=1 case.
161 QPointF offset;
162 if (parentItem())
163 offset = parentItem()->position();
164 QPointF offsetPos(std::round(offset.x() + pos.x()), std::round(offset.y() + pos.y()));
165 setPosition(offsetPos - offset);
166}
167
168void QQuickIconImage::componentComplete()
169{
170 QQuickImage::componentComplete();
171 load();
172}
173
174void QQuickIconImage::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
175{
176 QQuickImage::geometryChange(newGeometry, oldGeometry);
177 if (isComponentComplete() && newGeometry.size() != oldGeometry.size())
178 load();
179}
180
181void QQuickIconImage::pixmapChange()
182{
183 Q_D(QQuickIconImage);
184 QQuickImage::pixmapChange();
185 d->updateFillMode();
186
187 // Don't apply the color if we're recursing (updateFillMode() can cause us to recurse).
188 if (!d->updatingFillMode && d->color.alpha() > 0) {
189 QImage image = d->currentPix->image();
190 if (!image.isNull()) {
191 QPainter painter(&image);
192 painter.setCompositionMode(QPainter::CompositionMode_SourceIn);
193 painter.fillRect(image.rect(), d->color);
194 d->currentPix->setImage(image);
195 }
196 }
197}
198
199QT_END_NAMESPACE
200
201#include "moc_qquickiconimage_p.cpp"