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 "framesvgitem.h"
9
10 #include <QQuickWindow>
11 #include <QSGGeometry>
12 #include <QSGTexture>
13
14 #include <QDebug>
15 #include <QPainter>
16
17 #include <plasma/private/framesvg_helpers.h>
18 #include <plasma/private/framesvg_p.h>
19
20 #include <QuickAddons/ImageTexturesCache>
21 #include <QuickAddons/ManagedTextureNode>
22
23 #include <cmath> //floor()
24
25 namespace Plasma
26 {
27 Q_GLOBAL_STATIC(ImageTexturesCache, s_cache)
28
29 class FrameNode : public QSGNode
30 {
31 public:
FrameNode(const QString & prefix,FrameSvg * svg)32 FrameNode(const QString &prefix, FrameSvg *svg)
33 : QSGNode()
34 , leftWidth(0)
35 , rightWidth(0)
36 , topHeight(0)
37 , bottomHeight(0)
38 {
39 if (svg->enabledBorders() & FrameSvg::LeftBorder) {
40 leftWidth = svg->elementSize(prefix % QLatin1String("left")).width();
41 }
42 if (svg->enabledBorders() & FrameSvg::RightBorder) {
43 rightWidth = svg->elementSize(prefix % QLatin1String("right")).width();
44 }
45 if (svg->enabledBorders() & FrameSvg::TopBorder) {
46 topHeight = svg->elementSize(prefix % QLatin1String("top")).height();
47 }
48 if (svg->enabledBorders() & FrameSvg::BottomBorder) {
49 bottomHeight = svg->elementSize(prefix % QLatin1String("bottom")).height();
50 }
51 }
52
contentsRect(const QSize & size) const53 QRect contentsRect(const QSize &size) const
54 {
55 const QSize contentSize(size.width() - leftWidth - rightWidth, size.height() - topHeight - bottomHeight);
56
57 return QRect(QPoint(leftWidth, topHeight), contentSize);
58 }
59
60 private:
61 int leftWidth;
62 int rightWidth;
63 int topHeight;
64 int bottomHeight;
65 };
66
67 class FrameItemNode : public ManagedTextureNode
68 {
69 public:
70 enum FitMode {
71 // render SVG at native resolution then stretch it in openGL
72 FastStretch,
73 // on resize re-render the part of the frame from the SVG
74 Stretch,
75 Tile,
76 };
77
FrameItemNode(FrameSvgItem * frameSvg,FrameSvg::EnabledBorders borders,FitMode fitMode,QSGNode * parent)78 FrameItemNode(FrameSvgItem *frameSvg, FrameSvg::EnabledBorders borders, FitMode fitMode, QSGNode *parent)
79 : ManagedTextureNode()
80 , m_frameSvg(frameSvg)
81 , m_border(borders)
82 , m_lastParent(parent)
83 , m_fitMode(fitMode)
84 {
85 m_lastParent->appendChildNode(this);
86
87 if (m_fitMode == Tile) {
88 if (m_border == FrameSvg::TopBorder || m_border == FrameSvg::BottomBorder || m_border == FrameSvg::NoBorder) {
89 static_cast<QSGTextureMaterial *>(material())->setHorizontalWrapMode(QSGTexture::Repeat);
90 static_cast<QSGOpaqueTextureMaterial *>(opaqueMaterial())->setHorizontalWrapMode(QSGTexture::Repeat);
91 }
92 if (m_border == FrameSvg::LeftBorder || m_border == FrameSvg::RightBorder || m_border == FrameSvg::NoBorder) {
93 static_cast<QSGTextureMaterial *>(material())->setVerticalWrapMode(QSGTexture::Repeat);
94 static_cast<QSGOpaqueTextureMaterial *>(opaqueMaterial())->setVerticalWrapMode(QSGTexture::Repeat);
95 }
96 }
97
98 if (m_fitMode == Tile || m_fitMode == FastStretch) {
99 QString elementId = m_frameSvg->frameSvg()->actualPrefix() + FrameSvgHelpers::borderToElementId(m_border);
100 m_elementNativeSize = m_frameSvg->frameSvg()->elementSize(elementId);
101
102 if (m_elementNativeSize.isEmpty()) {
103 // if the default element is empty, we can avoid the slower tiling path
104 // this also avoids a divide by 0 error
105 m_fitMode = FastStretch;
106 }
107
108 updateTexture(m_elementNativeSize, elementId);
109 }
110 }
111
updateTexture(const QSize & size,const QString & elementId)112 void updateTexture(const QSize &size, const QString &elementId)
113 {
114 QQuickWindow::CreateTextureOptions options;
115 if (m_fitMode != Tile) {
116 options = QQuickWindow::TextureCanUseAtlas;
117 }
118 setTexture(s_cache->loadTexture(m_frameSvg->window(), m_frameSvg->frameSvg()->image(size, elementId), options));
119 }
120
reposition(const QRect & frameGeometry,QSize & fullSize)121 void reposition(const QRect &frameGeometry, QSize &fullSize)
122 {
123 QRect nodeRect = FrameSvgHelpers::sectionRect(m_border, frameGeometry, fullSize);
124
125 // ensure we're not passing a weird rectangle to updateTexturedRectGeometry
126 if (!nodeRect.isValid() || nodeRect.isEmpty()) {
127 nodeRect = QRect();
128 }
129
130 // the position of the relevant texture within this texture ID.
131 // for atlas' this will only be a small part of the texture
132 QRectF textureRect;
133
134 if (m_fitMode == Tile) {
135 textureRect = QRectF(0, 0, 1, 1); // we can never be in an atlas for tiled images.
136
137 // if tiling horizontally
138 if (m_border == FrameSvg::TopBorder || m_border == FrameSvg::BottomBorder || m_border == FrameSvg::NoBorder) {
139 // cmp. CSS3's border-image-repeat: "repeat", though with first tile not centered, but aligned to left
140 textureRect.setWidth((qreal)nodeRect.width() / m_elementNativeSize.width());
141 }
142 // if tiling vertically
143 if (m_border == FrameSvg::LeftBorder || m_border == FrameSvg::RightBorder || m_border == FrameSvg::NoBorder) {
144 // cmp. CSS3's border-image-repeat: "repeat", though with first tile not centered, but aligned to top
145 textureRect.setHeight((qreal)nodeRect.height() / m_elementNativeSize.height());
146 }
147 } else if (m_fitMode == Stretch) {
148 QString prefix = m_frameSvg->frameSvg()->actualPrefix();
149
150 QString elementId = prefix + FrameSvgHelpers::borderToElementId(m_border);
151
152 // re-render the SVG at new size
153 updateTexture(nodeRect.size(), elementId);
154 textureRect = texture()->normalizedTextureSubRect();
155 } else if (texture()) { // for fast stretch.
156 textureRect = texture()->normalizedTextureSubRect();
157 }
158
159 QSGGeometry::updateTexturedRectGeometry(geometry(), nodeRect, textureRect);
160 markDirty(QSGNode::DirtyGeometry);
161 }
162
163 private:
164 FrameSvgItem *m_frameSvg;
165 FrameSvg::EnabledBorders m_border;
166 QSGNode *m_lastParent;
167 QSize m_elementNativeSize;
168 FitMode m_fitMode;
169 };
170
FrameSvgItemMargins(Plasma::FrameSvg * frameSvg,QObject * parent)171 FrameSvgItemMargins::FrameSvgItemMargins(Plasma::FrameSvg *frameSvg, QObject *parent)
172 : QObject(parent)
173 , m_frameSvg(frameSvg)
174 , m_fixed(false)
175 , m_inset(false)
176 {
177 // qDebug() << "margins at: " << left() << top() << right() << bottom();
178 }
179
left() const180 qreal FrameSvgItemMargins::left() const
181 {
182 if (m_fixed) {
183 return m_frameSvg->fixedMarginSize(Types::LeftMargin);
184 } else if (m_inset) {
185 return m_frameSvg->insetSize(Types::LeftMargin);
186 } else {
187 return m_frameSvg->marginSize(Types::LeftMargin);
188 }
189 }
190
top() const191 qreal FrameSvgItemMargins::top() const
192 {
193 if (m_fixed) {
194 return m_frameSvg->fixedMarginSize(Types::TopMargin);
195 } else if (m_inset) {
196 return m_frameSvg->insetSize(Types::TopMargin);
197 } else {
198 return m_frameSvg->marginSize(Types::TopMargin);
199 }
200 }
201
right() const202 qreal FrameSvgItemMargins::right() const
203 {
204 if (m_fixed) {
205 return m_frameSvg->fixedMarginSize(Types::RightMargin);
206 } else if (m_inset) {
207 return m_frameSvg->insetSize(Types::RightMargin);
208 } else {
209 return m_frameSvg->marginSize(Types::RightMargin);
210 }
211 }
212
bottom() const213 qreal FrameSvgItemMargins::bottom() const
214 {
215 if (m_fixed) {
216 return m_frameSvg->fixedMarginSize(Types::BottomMargin);
217 } else if (m_inset) {
218 return m_frameSvg->insetSize(Types::BottomMargin);
219 } else {
220 return m_frameSvg->marginSize(Types::BottomMargin);
221 }
222 }
223
horizontal() const224 qreal FrameSvgItemMargins::horizontal() const
225 {
226 return left() + right();
227 }
228
vertical() const229 qreal FrameSvgItemMargins::vertical() const
230 {
231 return top() + bottom();
232 }
233
margins() const234 QVector<qreal> FrameSvgItemMargins::margins() const
235 {
236 qreal left;
237 qreal top;
238 qreal right;
239 qreal bottom;
240 m_frameSvg->getMargins(left, top, right, bottom);
241 return {left, top, right, bottom};
242 }
243
update()244 void FrameSvgItemMargins::update()
245 {
246 Q_EMIT marginsChanged();
247 }
248
setFixed(bool fixed)249 void FrameSvgItemMargins::setFixed(bool fixed)
250 {
251 if (fixed == m_fixed) {
252 return;
253 }
254
255 m_fixed = fixed;
256 Q_EMIT marginsChanged();
257 }
258
isFixed() const259 bool FrameSvgItemMargins::isFixed() const
260 {
261 return m_fixed;
262 }
263
setInset(bool inset)264 void FrameSvgItemMargins::setInset(bool inset)
265 {
266 if (inset == m_inset) {
267 return;
268 }
269
270 m_inset = inset;
271 Q_EMIT marginsChanged();
272 }
273
isInset() const274 bool FrameSvgItemMargins::isInset() const
275 {
276 return m_inset;
277 }
278
FrameSvgItem(QQuickItem * parent)279 FrameSvgItem::FrameSvgItem(QQuickItem *parent)
280 : QQuickItem(parent)
281 , m_margins(nullptr)
282 , m_fixedMargins(nullptr)
283 , m_insetMargins(nullptr)
284 , m_textureChanged(false)
285 , m_sizeChanged(false)
286 , m_fastPath(true)
287 {
288 m_frameSvg = new Plasma::FrameSvg(this);
289 setFlag(ItemHasContents, true);
290 connect(m_frameSvg, &FrameSvg::repaintNeeded, this, &FrameSvgItem::doUpdate);
291 connect(&Units::instance(), &Units::devicePixelRatioChanged, this, &FrameSvgItem::updateDevicePixelRatio);
292 connect(m_frameSvg, &Svg::fromCurrentThemeChanged, this, &FrameSvgItem::fromCurrentThemeChanged);
293 connect(m_frameSvg, &Svg::statusChanged, this, &FrameSvgItem::statusChanged);
294 }
295
~FrameSvgItem()296 FrameSvgItem::~FrameSvgItem()
297 {
298 }
299
300 class CheckMarginsChange
301 {
302 public:
CheckMarginsChange(QVector<qreal> & oldMargins,FrameSvgItemMargins * marginsObject)303 CheckMarginsChange(QVector<qreal> &oldMargins, FrameSvgItemMargins *marginsObject)
304 : m_oldMargins(oldMargins)
305 , m_marginsObject(marginsObject)
306 {
307 }
308
~CheckMarginsChange()309 ~CheckMarginsChange()
310 {
311 const QVector<qreal> oldMarginsBefore = m_oldMargins;
312 m_oldMargins = m_marginsObject ? m_marginsObject->margins() : QVector<qreal>();
313
314 if (oldMarginsBefore != m_oldMargins) {
315 m_marginsObject->update();
316 }
317 }
318
319 private:
320 QVector<qreal> &m_oldMargins;
321 FrameSvgItemMargins *const m_marginsObject;
322 };
323
setImagePath(const QString & path)324 void FrameSvgItem::setImagePath(const QString &path)
325 {
326 if (m_frameSvg->imagePath() == path) {
327 return;
328 }
329
330 CheckMarginsChange checkMargins(m_oldMargins, m_margins);
331 CheckMarginsChange checkFixedMargins(m_oldFixedMargins, m_fixedMargins);
332 CheckMarginsChange checkInsetMargins(m_oldInsetMargins, m_insetMargins);
333
334 updateDevicePixelRatio();
335 m_frameSvg->setImagePath(path);
336
337 if (implicitWidth() <= 0) {
338 setImplicitWidth(m_frameSvg->marginSize(Plasma::Types::LeftMargin) + m_frameSvg->marginSize(Plasma::Types::RightMargin));
339 }
340
341 if (implicitHeight() <= 0) {
342 setImplicitHeight(m_frameSvg->marginSize(Plasma::Types::TopMargin) + m_frameSvg->marginSize(Plasma::Types::BottomMargin));
343 }
344
345 Q_EMIT imagePathChanged();
346
347 if (isComponentComplete()) {
348 applyPrefixes();
349
350 m_frameSvg->resizeFrame(QSizeF(width(), height()));
351 m_textureChanged = true;
352 update();
353 }
354 }
355
imagePath() const356 QString FrameSvgItem::imagePath() const
357 {
358 return m_frameSvg->imagePath();
359 }
360
setPrefix(const QVariant & prefixes)361 void FrameSvgItem::setPrefix(const QVariant &prefixes)
362 {
363 QStringList prefixList;
364 // is this a simple string?
365 if (prefixes.canConvert<QString>()) {
366 prefixList << prefixes.toString();
367 } else if (prefixes.canConvert<QStringList>()) {
368 prefixList = prefixes.toStringList();
369 }
370
371 if (m_prefixes == prefixList) {
372 return;
373 }
374
375 CheckMarginsChange checkMargins(m_oldMargins, m_margins);
376 CheckMarginsChange checkFixedMargins(m_oldFixedMargins, m_fixedMargins);
377 CheckMarginsChange checkInsetMargins(m_oldInsetMargins, m_insetMargins);
378
379 m_prefixes = prefixList;
380 applyPrefixes();
381
382 if (implicitWidth() <= 0) {
383 setImplicitWidth(m_frameSvg->marginSize(Plasma::Types::LeftMargin) + m_frameSvg->marginSize(Plasma::Types::RightMargin));
384 }
385
386 if (implicitHeight() <= 0) {
387 setImplicitHeight(m_frameSvg->marginSize(Plasma::Types::TopMargin) + m_frameSvg->marginSize(Plasma::Types::BottomMargin));
388 }
389
390 Q_EMIT prefixChanged();
391
392 if (isComponentComplete()) {
393 m_frameSvg->resizeFrame(QSizeF(width(), height()));
394 m_textureChanged = true;
395 update();
396 }
397 }
398
prefix() const399 QVariant FrameSvgItem::prefix() const
400 {
401 return m_prefixes;
402 }
403
usedPrefix() const404 QString FrameSvgItem::usedPrefix() const
405 {
406 return m_frameSvg->prefix();
407 }
408
margins()409 FrameSvgItemMargins *FrameSvgItem::margins()
410 {
411 if (!m_margins) {
412 m_margins = new FrameSvgItemMargins(m_frameSvg, this);
413 }
414 return m_margins;
415 }
416
fixedMargins()417 FrameSvgItemMargins *FrameSvgItem::fixedMargins()
418 {
419 if (!m_fixedMargins) {
420 m_fixedMargins = new FrameSvgItemMargins(m_frameSvg, this);
421 m_fixedMargins->setFixed(true);
422 }
423 return m_fixedMargins;
424 }
425
inset()426 FrameSvgItemMargins *FrameSvgItem::inset()
427 {
428 if (!m_insetMargins) {
429 m_insetMargins = new FrameSvgItemMargins(m_frameSvg, this);
430 m_insetMargins->setInset(true);
431 }
432 return m_insetMargins;
433 }
434
setColorGroup(Plasma::Theme::ColorGroup group)435 void FrameSvgItem::setColorGroup(Plasma::Theme::ColorGroup group)
436 {
437 if (m_frameSvg->colorGroup() == group) {
438 return;
439 }
440
441 m_frameSvg->setColorGroup(group);
442
443 Q_EMIT colorGroupChanged();
444 }
445
colorGroup() const446 Plasma::Theme::ColorGroup FrameSvgItem::colorGroup() const
447 {
448 return m_frameSvg->colorGroup();
449 }
450
fromCurrentTheme() const451 bool FrameSvgItem::fromCurrentTheme() const
452 {
453 return m_frameSvg->fromCurrentTheme();
454 }
455
setStatus(Plasma::Svg::Status status)456 void FrameSvgItem::setStatus(Plasma::Svg::Status status)
457 {
458 m_frameSvg->setStatus(status);
459 }
460
status() const461 Plasma::Svg::Status FrameSvgItem::status() const
462 {
463 return m_frameSvg->status();
464 }
465
setEnabledBorders(const Plasma::FrameSvg::EnabledBorders borders)466 void FrameSvgItem::setEnabledBorders(const Plasma::FrameSvg::EnabledBorders borders)
467 {
468 if (m_frameSvg->enabledBorders() == borders) {
469 return;
470 }
471
472 CheckMarginsChange checkMargins(m_oldMargins, m_margins);
473
474 m_frameSvg->setEnabledBorders(borders);
475 Q_EMIT enabledBordersChanged();
476 m_textureChanged = true;
477 update();
478 }
479
enabledBorders() const480 Plasma::FrameSvg::EnabledBorders FrameSvgItem::enabledBorders() const
481 {
482 return m_frameSvg->enabledBorders();
483 }
484
hasElementPrefix(const QString & prefix) const485 bool FrameSvgItem::hasElementPrefix(const QString &prefix) const
486 {
487 return m_frameSvg->hasElementPrefix(prefix);
488 }
489
mask() const490 QRegion FrameSvgItem::mask() const
491 {
492 return m_frameSvg->mask();
493 }
494
geometryChanged(const QRectF & newGeometry,const QRectF & oldGeometry)495 void FrameSvgItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
496 {
497 const bool isComponentComplete = this->isComponentComplete();
498 if (isComponentComplete) {
499 m_frameSvg->resizeFrame(newGeometry.size());
500 m_sizeChanged = true;
501 }
502
503 QQuickItem::geometryChanged(newGeometry, oldGeometry);
504
505 // the above only triggers updatePaintNode, so we have to inform subscribers
506 // about the potential change of the mask explicitly here
507 if (isComponentComplete) {
508 Q_EMIT maskChanged();
509 }
510 }
511
doUpdate()512 void FrameSvgItem::doUpdate()
513 {
514 if (m_frameSvg->isRepaintBlocked()) {
515 return;
516 }
517
518 CheckMarginsChange checkMargins(m_oldMargins, m_margins);
519 CheckMarginsChange checkFixedMargins(m_oldFixedMargins, m_fixedMargins);
520 CheckMarginsChange checkInsetMargins(m_oldInsetMargins, m_insetMargins);
521
522 // if the theme changed, the available prefix may have changed as well
523 applyPrefixes();
524
525 if (implicitWidth() <= 0) {
526 setImplicitWidth(m_frameSvg->marginSize(Plasma::Types::LeftMargin) + m_frameSvg->marginSize(Plasma::Types::RightMargin));
527 }
528
529 if (implicitHeight() <= 0) {
530 setImplicitHeight(m_frameSvg->marginSize(Plasma::Types::TopMargin) + m_frameSvg->marginSize(Plasma::Types::BottomMargin));
531 }
532
533 QString prefix = m_frameSvg->actualPrefix();
534 bool hasOverlay = (!prefix.startsWith(QLatin1String("mask-")) //
535 && m_frameSvg->hasElement(prefix % QLatin1String("overlay")));
536 bool hasComposeOverBorder = m_frameSvg->hasElement(prefix % QLatin1String("hint-compose-over-border"))
537 && m_frameSvg->hasElement(QLatin1String("mask-") % prefix % QLatin1String("center"));
538 m_fastPath = !hasOverlay && !hasComposeOverBorder;
539
540 // Software rendering (at time of writing Qt5.10) doesn't seem to like our
541 // tiling/stretching in the 9-tiles.
542 // Also when using QPainter it's arguably faster to create and cache pixmaps
543 // of the whole frame, which is what the slow path does
544 if (QQuickWindow::sceneGraphBackend() == QLatin1String("software")) {
545 m_fastPath = false;
546 }
547 m_textureChanged = true;
548
549 update();
550
551 Q_EMIT maskChanged();
552 Q_EMIT repaintNeeded();
553 }
554
frameSvg() const555 Plasma::FrameSvg *FrameSvgItem::frameSvg() const
556 {
557 return m_frameSvg;
558 }
559
updatePaintNode(QSGNode * oldNode,QQuickItem::UpdatePaintNodeData *)560 QSGNode *FrameSvgItem::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
561 {
562 if (!window() || !m_frameSvg //
563 || (!m_frameSvg->hasElementPrefix(m_frameSvg->actualPrefix()) //
564 && !m_frameSvg->hasElementPrefix(m_frameSvg->prefix()))) {
565 delete oldNode;
566 return nullptr;
567 }
568
569 const QSGTexture::Filtering filtering = smooth() ? QSGTexture::Linear : QSGTexture::Nearest;
570
571 if (m_fastPath) {
572 if (m_textureChanged) {
573 delete oldNode;
574 oldNode = nullptr;
575 }
576
577 if (!oldNode) {
578 QString prefix = m_frameSvg->actualPrefix();
579 oldNode = new FrameNode(prefix, m_frameSvg);
580
581 bool tileCenter = (m_frameSvg->hasElement(QStringLiteral("hint-tile-center")) //
582 || m_frameSvg->hasElement(prefix % QLatin1String("hint-tile-center")));
583 bool stretchBorders = (m_frameSvg->hasElement(QStringLiteral("hint-stretch-borders")) //
584 || m_frameSvg->hasElement(prefix % QLatin1String("hint-stretch-borders")));
585 FrameItemNode::FitMode borderFitMode = stretchBorders ? FrameItemNode::Stretch : FrameItemNode::Tile;
586 FrameItemNode::FitMode centerFitMode = tileCenter ? FrameItemNode::Tile : FrameItemNode::Stretch;
587
588 new FrameItemNode(this, FrameSvg::NoBorder, centerFitMode, oldNode);
589 if (enabledBorders() & (FrameSvg::TopBorder | FrameSvg::LeftBorder)) {
590 new FrameItemNode(this, FrameSvg::TopBorder | FrameSvg::LeftBorder, FrameItemNode::FastStretch, oldNode);
591 }
592 if (enabledBorders() & (FrameSvg::TopBorder | FrameSvg::RightBorder)) {
593 new FrameItemNode(this, FrameSvg::TopBorder | FrameSvg::RightBorder, FrameItemNode::FastStretch, oldNode);
594 }
595 if (enabledBorders() & FrameSvg::TopBorder) {
596 new FrameItemNode(this, FrameSvg::TopBorder, borderFitMode, oldNode);
597 }
598 if (enabledBorders() & FrameSvg::BottomBorder) {
599 new FrameItemNode(this, FrameSvg::BottomBorder, borderFitMode, oldNode);
600 }
601 if (enabledBorders() & (FrameSvg::BottomBorder | FrameSvg::LeftBorder)) {
602 new FrameItemNode(this, FrameSvg::BottomBorder | FrameSvg::LeftBorder, FrameItemNode::FastStretch, oldNode);
603 }
604 if (enabledBorders() & (FrameSvg::BottomBorder | FrameSvg::RightBorder)) {
605 new FrameItemNode(this, FrameSvg::BottomBorder | FrameSvg::RightBorder, FrameItemNode::FastStretch, oldNode);
606 }
607 if (enabledBorders() & FrameSvg::LeftBorder) {
608 new FrameItemNode(this, FrameSvg::LeftBorder, borderFitMode, oldNode);
609 }
610 if (enabledBorders() & FrameSvg::RightBorder) {
611 new FrameItemNode(this, FrameSvg::RightBorder, borderFitMode, oldNode);
612 }
613
614 m_sizeChanged = true;
615 m_textureChanged = false;
616 }
617
618 QSGNode *node = oldNode->firstChild();
619 while (node) {
620 static_cast<FrameItemNode *>(node)->setFiltering(filtering);
621 node = node->nextSibling();
622 }
623
624 if (m_sizeChanged) {
625 FrameNode *frameNode = static_cast<FrameNode *>(oldNode);
626 QSize frameSize(width(), height());
627 QRect geometry = frameNode->contentsRect(frameSize);
628 QSGNode *node = oldNode->firstChild();
629 while (node) {
630 static_cast<FrameItemNode *>(node)->reposition(geometry, frameSize);
631 node = node->nextSibling();
632 }
633
634 m_sizeChanged = false;
635 }
636 } else {
637 ManagedTextureNode *textureNode = dynamic_cast<ManagedTextureNode *>(oldNode);
638 if (!textureNode) {
639 delete oldNode;
640 textureNode = new ManagedTextureNode;
641 m_textureChanged = true; // force updating the texture on our newly created node
642 oldNode = textureNode;
643 }
644 textureNode->setFiltering(filtering);
645
646 if ((m_textureChanged || m_sizeChanged) || textureNode->texture()->textureSize() != m_frameSvg->size()) {
647 QImage image = m_frameSvg->framePixmap().toImage();
648 textureNode->setTexture(s_cache->loadTexture(window(), image));
649 textureNode->setRect(0, 0, width(), height());
650
651 m_textureChanged = false;
652 m_sizeChanged = false;
653 }
654 }
655
656 return oldNode;
657 }
658
classBegin()659 void FrameSvgItem::classBegin()
660 {
661 QQuickItem::classBegin();
662 m_frameSvg->setRepaintBlocked(true);
663 }
664
componentComplete()665 void FrameSvgItem::componentComplete()
666 {
667 CheckMarginsChange checkMargins(m_oldMargins, m_margins);
668 CheckMarginsChange checkFixedMargins(m_oldFixedMargins, m_fixedMargins);
669 CheckMarginsChange checkInsetMargins(m_oldInsetMargins, m_insetMargins);
670
671 QQuickItem::componentComplete();
672 m_frameSvg->resizeFrame(QSize(width(), height()));
673 m_frameSvg->setRepaintBlocked(false);
674 m_textureChanged = true;
675 }
676
updateDevicePixelRatio()677 void FrameSvgItem::updateDevicePixelRatio()
678 {
679 m_frameSvg->setScaleFactor(qMax<qreal>(1.0, floor(Units::instance().devicePixelRatio())));
680
681 // devicepixelratio is always set integer in the svg, so needs at least 192dpi to double up.
682 //(it needs to be integer to have lines contained inside a svg piece to keep being pixel aligned)
683 const auto newDevicePixelRation = qMax<qreal>(1.0, floor(window() ? window()->devicePixelRatio() : qApp->devicePixelRatio()));
684
685 if (newDevicePixelRation != m_frameSvg->devicePixelRatio()) {
686 m_frameSvg->setDevicePixelRatio(qMax<qreal>(1.0, newDevicePixelRation));
687 m_textureChanged = true;
688 }
689 }
690
applyPrefixes()691 void FrameSvgItem::applyPrefixes()
692 {
693 if (m_frameSvg->imagePath().isEmpty()) {
694 return;
695 }
696
697 const QString oldPrefix = m_frameSvg->prefix();
698
699 if (m_prefixes.isEmpty()) {
700 m_frameSvg->setElementPrefix(QString());
701 if (oldPrefix != m_frameSvg->prefix()) {
702 Q_EMIT usedPrefixChanged();
703 }
704 return;
705 }
706
707 bool found = false;
708 for (const QString &prefix : std::as_const(m_prefixes)) {
709 if (m_frameSvg->hasElementPrefix(prefix)) {
710 m_frameSvg->setElementPrefix(prefix);
711 found = true;
712 break;
713 }
714 }
715 if (!found) {
716 // this setElementPrefix is done to keep the same behavior as before, when it was a simple string
717 m_frameSvg->setElementPrefix(m_prefixes.constLast());
718 }
719 if (oldPrefix != m_frameSvg->prefix()) {
720 Q_EMIT usedPrefixChanged();
721 }
722 }
723
itemChange(QQuickItem::ItemChange change,const QQuickItem::ItemChangeData & value)724 void FrameSvgItem::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
725 {
726 if (change == ItemSceneChange && value.window) {
727 updateDevicePixelRatio();
728 }
729
730 QQuickItem::itemChange(change, value);
731 }
732
733 } // Plasma namespace
734