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

KDE's Doxygen guidelines are available online.