KSvg

svgitem.cpp
1/*
2 SPDX-FileCopyrightText: 2010 Marco Martin <mart@kde.org>
3 SPDX-FileCopyrightText: 2014 David Edmundson <davidedmundson@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "svgitem.h"
9
10#include <QDebug>
11#include <QQuickWindow>
12#include <QRectF>
13#include <QSGTexture>
14
15#include "ksvg/svg.h"
16
17#include "managedtexturenode.h"
18
19#include <Kirigami/Platform/PlatformTheme>
20#include <debug_p.h>
21
22namespace KSvg
23{
24SvgItem::SvgItem(QQuickItem *parent)
25 : QQuickItem(parent)
26 , m_textureChanged(false)
27{
28 m_svg = new KSvg::Svg(this);
29 setFlag(QQuickItem::ItemHasContents, true);
30
31 connect(m_svg, &Svg::repaintNeeded, this, &SvgItem::updateNeeded);
32 connect(m_svg, &Svg::repaintNeeded, this, &SvgItem::naturalSizeChanged);
33 connect(m_svg, &Svg::sizeChanged, this, &SvgItem::naturalSizeChanged);
34 connect(m_svg, &Svg::repaintNeeded, this, &SvgItem::elementRectChanged);
35 connect(m_svg, &Svg::sizeChanged, this, &SvgItem::elementRectChanged);
36}
37
38SvgItem::~SvgItem()
39{
40 // Make sure to not call anything on m_svg when this is shutting down
41 // Kirigami::PlatformTheme will lose its window at that point so will
42 // emit colorschanged, which we shouldn't react to during destructor
43 disconnect(m_kirigamiTheme, nullptr, this, nullptr);
44}
45
46void SvgItem::componentComplete()
47{
48 m_kirigamiTheme = qobject_cast<Kirigami::Platform::PlatformTheme *>(qmlAttachedPropertiesObject<Kirigami::Platform::PlatformTheme>(this, true));
49 if (!m_kirigamiTheme) {
50 qCWarning(LOG_KSVGQML) << "No theme!" << qmlAttachedPropertiesObject<Kirigami::Platform::PlatformTheme>(this, true) << this;
51 return;
52 }
53
54 auto checkApplyTheme = [this]() {
55 if (!m_svg->imageSet()->filePath(QStringLiteral("colors")).isEmpty()) {
56 m_svg->clearColorOverrides();
57 }
58 };
59 auto applyTheme = [this]() {
60 if (!m_svg) {
61 return;
62 }
63 if (!m_svg->imageSet()->filePath(QStringLiteral("colors")).isEmpty()) {
64 m_svg->clearColorOverrides();
65 return;
66 }
67 m_svg->setColor(Svg::Text, m_kirigamiTheme->textColor());
68 m_svg->setColor(Svg::Background, m_kirigamiTheme->backgroundColor());
69 m_svg->setColor(Svg::Highlight, m_kirigamiTheme->highlightColor());
70 m_svg->setColor(Svg::HighlightedText, m_kirigamiTheme->highlightedTextColor());
71 m_svg->setColor(Svg::PositiveText, m_kirigamiTheme->positiveTextColor());
72 m_svg->setColor(Svg::NeutralText, m_kirigamiTheme->neutralTextColor());
73 m_svg->setColor(Svg::NegativeText, m_kirigamiTheme->negativeTextColor());
74 };
75 applyTheme();
76 connect(m_kirigamiTheme, &Kirigami::Platform::PlatformTheme::colorsChanged, this, applyTheme);
77 connect(m_svg->imageSet(), &ImageSet::imageSetChanged, this, checkApplyTheme);
78 connect(m_svg, &Svg::imageSetChanged, this, checkApplyTheme);
79
81}
82
83void SvgItem::setImagePath(const QString &path)
84{
85 if (!m_svg || m_svg->imagePath() == path) {
86 return;
87 }
88
89 updateDevicePixelRatio();
90 m_svg->setImagePath(path);
91
92 Q_EMIT imagePathChanged();
93
94 if (isComponentComplete()) {
95 update();
96 }
97}
98
99QString SvgItem::imagePath() const
100{
101 return m_svg->imagePath();
102}
103
104void SvgItem::setElementId(const QString &elementID)
105{
106 if (elementID == m_elementID) {
107 return;
108 }
109
110 if (implicitWidth() <= 0) {
111 setImplicitWidth(naturalSize().width());
112 }
113 if (implicitHeight() <= 0) {
114 setImplicitHeight(naturalSize().height());
115 }
116
117 m_elementID = elementID;
118 Q_EMIT elementIdChanged();
119 Q_EMIT naturalSizeChanged();
120 Q_EMIT elementRectChanged();
121
122 scheduleImageUpdate();
123}
124
125QString SvgItem::elementId() const
126{
127 return m_elementID;
128}
129
130void SvgItem::setSvg(KSvg::Svg *svg)
131{
132 if (m_svg) {
133 disconnect(m_svg.data(), nullptr, this, nullptr);
134 }
135 m_svg = svg;
136
137 if (svg) {
138 connect(svg, &Svg::repaintNeeded, this, &SvgItem::updateNeeded);
139 connect(svg, &Svg::repaintNeeded, this, &SvgItem::naturalSizeChanged);
140 connect(svg, &Svg::repaintNeeded, this, &SvgItem::elementRectChanged);
141 connect(svg, &Svg::sizeChanged, this, &SvgItem::naturalSizeChanged);
142 connect(svg, &Svg::sizeChanged, this, &SvgItem::elementRectChanged);
143 }
144
145 if (implicitWidth() <= 0) {
146 setImplicitWidth(naturalSize().width());
147 }
148 if (implicitHeight() <= 0) {
149 setImplicitHeight(naturalSize().height());
150 }
151
152 scheduleImageUpdate();
153
154 Q_EMIT svgChanged();
155 Q_EMIT naturalSizeChanged();
156 Q_EMIT elementRectChanged();
157 Q_EMIT imagePathChanged();
158}
159
160KSvg::Svg *SvgItem::svg() const
161{
162 return m_svg.data();
163}
164
165QSizeF SvgItem::naturalSize() const
166{
167 if (!m_svg) {
168 return QSizeF();
169 } else if (!m_elementID.isEmpty()) {
170 return m_svg->elementSize(m_elementID);
171 }
172
173 return m_svg->size();
174}
175
176QRectF SvgItem::elementRect() const
177{
178 if (!m_svg) {
179 return QRectF();
180 } else if (!m_elementID.isEmpty()) {
181 return m_svg->elementRect(m_elementID);
182 }
183
184 return QRectF(QPointF(0, 0), m_svg->size());
185}
186
187QSGNode *SvgItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData)
188{
189 Q_UNUSED(updatePaintNodeData);
190 if (!window() || !m_svg) {
191 delete oldNode;
192 return nullptr;
193 }
194
195 // this is more than just an optimization, uploading a null image to QSGAtlasTexture causes a crash
196 if (width() == 0.0 || height() == 0.0) {
197 delete oldNode;
198 return nullptr;
199 }
200
201 ManagedTextureNode *textureNode = static_cast<ManagedTextureNode *>(oldNode);
202 if (!textureNode) {
203 textureNode = new ManagedTextureNode;
204 m_textureChanged = true;
205 }
206
207 // TODO use a heuristic to work out when to redraw
208 // if !m_smooth and size is approximate simply change the textureNode.rect without
209 // updating the material
210
211 if (m_textureChanged || textureNode->texture()->textureSize() != QSize(width(), height())) {
212 // despite having a valid size sometimes we still get a null QImage from KSvg::Svg
213 // loading a null texture to an atlas fatals
214 // Dave E fixed this in Qt in 5.3.something onwards but we need this for now
215 if (m_image.isNull()) {
216 delete textureNode;
217 return nullptr;
218 }
219
220 QSharedPointer<QSGTexture> texture(window()->createTextureFromImage(m_image, QQuickWindow::TextureCanUseAtlas));
221 textureNode->setTexture(texture);
222 m_textureChanged = false;
223
224 textureNode->setRect(0, 0, width(), height());
225 }
226
227 textureNode->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest);
228
229 return textureNode;
230}
231
232void SvgItem::updateNeeded()
233{
234 if (implicitWidth() <= 0) {
235 setImplicitWidth(naturalSize().width());
236 }
237 if (implicitHeight() <= 0) {
238 setImplicitHeight(naturalSize().height());
239 }
240 scheduleImageUpdate();
241}
242
243void SvgItem::scheduleImageUpdate()
244{
245 polish();
246 update();
247}
248
249void SvgItem::updatePolish()
250{
252
253 if (m_svg) {
254 // setContainsMultipleImages has to be done there since m_svg can be shared with somebody else
255 m_textureChanged = true;
256 m_svg->setContainsMultipleImages(!m_elementID.isEmpty());
257 m_image = m_svg->image(QSize(width(), height()), m_elementID);
258 }
259}
260
261void SvgItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
262{
263 if (newGeometry.size() != oldGeometry.size() && newGeometry.isValid()) {
264 scheduleImageUpdate();
265 }
266
267 QQuickItem::geometryChange(newGeometry, oldGeometry);
268}
269
270void SvgItem::updateDevicePixelRatio()
271{
272 const auto newDevicePixelRatio = std::max<qreal>(1.0, (window() ? window()->devicePixelRatio() : qApp->devicePixelRatio()));
273 if (newDevicePixelRatio != m_svg->devicePixelRatio()) {
274 m_svg->setDevicePixelRatio(newDevicePixelRatio);
275 m_textureChanged = true;
276 }
277}
278
279void SvgItem::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
280{
281 if (change == ItemSceneChange && value.window) {
282 updateDevicePixelRatio();
283 } else if (change == QQuickItem::ItemDevicePixelRatioHasChanged) {
284 updateDevicePixelRatio();
285 }
286
287 QQuickItem::itemChange(change, value);
288}
289
290} // KSvg namespace
291
292#include "moc_svgitem.cpp"
A theme aware image-centric SVG class.
Definition svg.h:46
void update(Part *part, const QByteArray &data, qint64 dataSize)
KGUIADDONS_EXPORT QWindow * window(QObject *job)
The KSvg namespace.
virtual void componentComplete() override
virtual void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
virtual void itemChange(ItemChange change, const ItemChangeData &value)
virtual void updatePolish()
bool isValid() const const
QSizeF size() const const
void setFiltering(QSGTexture::Filtering filtering)
void setRect(const QRectF &r)
QSGTexture * texture() const const
virtual QSize textureSize() const const=0
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
qreal devicePixelRatio() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 17:00:08 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.