KSvg

framesvgitem.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 "framesvgitem.h"
9
10#include "imagetexturescache.h"
11#include "managedtexturenode.h"
12
13#include <QQuickWindow>
14#include <QSGGeometry>
15#include <QSGTexture>
16
17#include <QDebug>
18#include <QPainter>
19
20#include <ksvg/private/framesvg_helpers.h>
21#include <ksvg/private/framesvg_p.h>
22
23#include <cmath> //floor()
24
25#include <Kirigami/Platform/PlatformTheme>
26#include <debug_p.h>
27
28namespace KSvg
29{
30Q_GLOBAL_STATIC(ImageTexturesCache, s_cache)
31
32class FrameNode : public QSGNode
33{
34public:
35 FrameNode(const QString &prefix, FrameSvg *svg)
36 : QSGNode()
37 , leftWidth(0)
38 , rightWidth(0)
39 , topHeight(0)
40 , bottomHeight(0)
41 {
42 if (svg->enabledBorders() & FrameSvg::LeftBorder) {
43 leftWidth = svg->elementSize(prefix % QLatin1String("left")).width();
44 }
45 if (svg->enabledBorders() & FrameSvg::RightBorder) {
46 rightWidth = svg->elementSize(prefix % QLatin1String("right")).width();
47 }
48 if (svg->enabledBorders() & FrameSvg::TopBorder) {
49 topHeight = svg->elementSize(prefix % QLatin1String("top")).height();
50 }
51 if (svg->enabledBorders() & FrameSvg::BottomBorder) {
52 bottomHeight = svg->elementSize(prefix % QLatin1String("bottom")).height();
53 }
54 }
55
56 QRect contentsRect(const QSize &size) const
57 {
58 const QSize contentSize(size.width() - leftWidth - rightWidth, size.height() - topHeight - bottomHeight);
59
60 return QRect(QPoint(leftWidth, topHeight), contentSize);
61 }
62
63private:
64 int leftWidth;
65 int rightWidth;
66 int topHeight;
67 int bottomHeight;
68};
69
70class FrameItemNode : public ManagedTextureNode
71{
72public:
73 enum FitMode {
74 // render SVG at native resolution then stretch it in openGL
75 FastStretch,
76 // on resize re-render the part of the frame from the SVG
77 Stretch,
78 Tile,
79 };
80
81 FrameItemNode(FrameSvgItem *frameSvg, FrameSvg::EnabledBorders borders, FitMode fitMode, QSGNode *parent)
82 : ManagedTextureNode()
83 , m_frameSvg(frameSvg)
84 , m_border(borders)
85 , m_fitMode(fitMode)
86 {
88
89 if (m_fitMode == Tile) {
90 if (m_border == FrameSvg::TopBorder || m_border == FrameSvg::BottomBorder || m_border == FrameSvg::NoBorder) {
91 static_cast<QSGTextureMaterial *>(material())->setHorizontalWrapMode(QSGTexture::Repeat);
92 static_cast<QSGOpaqueTextureMaterial *>(opaqueMaterial())->setHorizontalWrapMode(QSGTexture::Repeat);
93 }
94 if (m_border == FrameSvg::LeftBorder || m_border == FrameSvg::RightBorder || m_border == FrameSvg::NoBorder) {
95 static_cast<QSGTextureMaterial *>(material())->setVerticalWrapMode(QSGTexture::Repeat);
96 static_cast<QSGOpaqueTextureMaterial *>(opaqueMaterial())->setVerticalWrapMode(QSGTexture::Repeat);
97 }
98 }
99
100 if (m_fitMode == Tile || m_fitMode == FastStretch) {
101 QString elementId = m_frameSvg->frameSvg()->actualPrefix() + FrameSvgHelpers::borderToElementId(m_border);
102 m_elementNativeSize = m_frameSvg->frameSvg()->elementSize(elementId).toSize();
103
104 if (m_elementNativeSize.isEmpty()) {
105 // if the default element is empty, we can avoid the slower tiling path
106 // this also avoids a divide by 0 error
107 m_fitMode = FastStretch;
108 }
109
110 updateTexture(m_elementNativeSize, elementId);
111 }
112 }
113
114 void updateTexture(const QSize &size, const QString &elementId)
115 {
117 if (m_fitMode != Tile) {
119 }
120 setTexture(s_cache->loadTexture(m_frameSvg->window(), m_frameSvg->frameSvg()->image(size, elementId), options));
121 }
122
123 void reposition(const QRect &frameGeometry, QSize &fullSize)
124 {
125 QRect nodeRect = FrameSvgHelpers::sectionRect(m_border, frameGeometry, fullSize);
126
127 // ensure we're not passing a weird rectangle to updateTexturedRectGeometry
128 if (!nodeRect.isValid() || nodeRect.isEmpty()) {
129 nodeRect = QRect();
130 }
131
132 // the position of the relevant texture within this texture ID.
133 // for atlas' this will only be a small part of the texture
134 QRectF textureRect;
135
136 if (m_fitMode == Tile) {
137 textureRect = QRectF(0, 0, 1, 1); // we can never be in an atlas for tiled images.
138
139 // if tiling horizontally
140 if (m_border == FrameSvg::TopBorder || m_border == FrameSvg::BottomBorder || m_border == FrameSvg::NoBorder) {
141 // cmp. CSS3's border-image-repeat: "repeat", though with first tile not centered, but aligned to left
142 textureRect.setWidth((qreal)nodeRect.width() / m_elementNativeSize.width());
143 }
144 // if tiling vertically
145 if (m_border == FrameSvg::LeftBorder || m_border == FrameSvg::RightBorder || m_border == FrameSvg::NoBorder) {
146 // cmp. CSS3's border-image-repeat: "repeat", though with first tile not centered, but aligned to top
147 textureRect.setHeight((qreal)nodeRect.height() / m_elementNativeSize.height());
148 }
149 } else if (m_fitMode == Stretch) {
150 QString prefix = m_frameSvg->frameSvg()->actualPrefix();
151
152 QString elementId = prefix + FrameSvgHelpers::borderToElementId(m_border);
153
154 // re-render the SVG at new size
155 updateTexture(nodeRect.size(), elementId);
156 textureRect = texture()->normalizedTextureSubRect();
157 } else if (texture()) { // for fast stretch.
158 textureRect = texture()->normalizedTextureSubRect();
159 }
160
161 QSGGeometry::updateTexturedRectGeometry(geometry(), nodeRect, textureRect);
163 }
164
165private:
166 FrameSvgItem *m_frameSvg;
168 QSize m_elementNativeSize;
169 FitMode m_fitMode;
170};
171
172FrameSvgItemMargins::FrameSvgItemMargins(KSvg::FrameSvg *frameSvg, QObject *parent)
173 : QObject(parent)
174 , m_frameSvg(frameSvg)
175 , m_fixed(false)
176 , m_inset(false)
177{
178 // qDebug() << "margins at: " << left() << top() << right() << bottom();
179}
180
181qreal FrameSvgItemMargins::left() const
182{
183 if (m_fixed) {
184 return m_frameSvg->fixedMarginSize(FrameSvg::LeftMargin);
185 } else if (m_inset) {
186 return m_frameSvg->insetSize(FrameSvg::LeftMargin);
187 } else {
188 return m_frameSvg->marginSize(FrameSvg::LeftMargin);
189 }
190}
191
192qreal FrameSvgItemMargins::top() const
193{
194 if (m_fixed) {
195 return m_frameSvg->fixedMarginSize(FrameSvg::TopMargin);
196 } else if (m_inset) {
197 return m_frameSvg->insetSize(FrameSvg::TopMargin);
198 } else {
199 return m_frameSvg->marginSize(FrameSvg::TopMargin);
200 }
201}
202
203qreal FrameSvgItemMargins::right() const
204{
205 if (m_fixed) {
206 return m_frameSvg->fixedMarginSize(FrameSvg::RightMargin);
207 } else if (m_inset) {
208 return m_frameSvg->insetSize(FrameSvg::RightMargin);
209 } else {
210 return m_frameSvg->marginSize(FrameSvg::RightMargin);
211 }
212}
213
214qreal FrameSvgItemMargins::bottom() const
215{
216 if (m_fixed) {
217 return m_frameSvg->fixedMarginSize(FrameSvg::BottomMargin);
218 } else if (m_inset) {
219 return m_frameSvg->insetSize(FrameSvg::BottomMargin);
220 } else {
221 return m_frameSvg->marginSize(FrameSvg::BottomMargin);
222 }
223}
224
226{
227 return left() + right();
228}
229
231{
232 return top() + bottom();
233}
234
236{
237 qreal left;
238 qreal top;
239 qreal right;
240 qreal bottom;
241 m_frameSvg->getMargins(left, top, right, bottom);
242 return {left, top, right, bottom};
243}
244
245void FrameSvgItemMargins::update()
246{
247 Q_EMIT marginsChanged();
248}
249
250void FrameSvgItemMargins::setFixed(bool fixed)
251{
252 if (fixed == m_fixed) {
253 return;
254 }
255
256 m_fixed = fixed;
257 Q_EMIT marginsChanged();
258}
259
260bool FrameSvgItemMargins::isFixed() const
261{
262 return m_fixed;
263}
264
265void FrameSvgItemMargins::setInset(bool inset)
266{
267 if (inset == m_inset) {
268 return;
269 }
270
271 m_inset = inset;
272 Q_EMIT marginsChanged();
273}
274
275bool FrameSvgItemMargins::isInset() const
276{
277 return m_inset;
278}
279
280FrameSvgItem::FrameSvgItem(QQuickItem *parent)
281 : QQuickItem(parent)
282 , m_margins(nullptr)
283 , m_fixedMargins(nullptr)
284 , m_insetMargins(nullptr)
285 , m_textureChanged(false)
286 , m_sizeChanged(false)
287 , m_fastPath(true)
288{
289 m_frameSvg = new KSvg::FrameSvg(this);
290
291 setFlag(QQuickItem::ItemHasContents, true);
292 setFlag(ItemHasContents, true);
293 connect(m_frameSvg, &FrameSvg::repaintNeeded, this, &FrameSvgItem::doUpdate);
294 connect(m_frameSvg, &Svg::fromCurrentImageSetChanged, this, &FrameSvgItem::fromCurrentImageSetChanged);
295 connect(m_frameSvg, &Svg::statusChanged, this, &FrameSvgItem::statusChanged);
296}
297
298FrameSvgItem::~FrameSvgItem()
299{
300}
301
302class CheckMarginsChange
303{
304public:
305 CheckMarginsChange(QList<qreal> &oldMargins, FrameSvgItemMargins *marginsObject)
306 : m_oldMargins(oldMargins)
307 , m_marginsObject(marginsObject)
308 {
309 }
310
311 ~CheckMarginsChange()
312 {
313 const QList<qreal> oldMarginsBefore = m_oldMargins;
314 m_oldMargins = m_marginsObject ? m_marginsObject->margins() : QList<qreal>();
315
316 if (m_marginsObject && oldMarginsBefore != m_oldMargins) {
317 m_marginsObject->update();
318 }
319 }
320
321private:
322 QList<qreal> &m_oldMargins;
323 FrameSvgItemMargins *const m_marginsObject;
324};
325
326void FrameSvgItem::setImagePath(const QString &path)
327{
328 if (m_frameSvg->imagePath() == path) {
329 return;
330 }
331
332 CheckMarginsChange checkMargins(m_oldMargins, m_margins);
333 CheckMarginsChange checkFixedMargins(m_oldFixedMargins, m_fixedMargins);
334 CheckMarginsChange checkInsetMargins(m_oldInsetMargins, m_insetMargins);
335
336 updateDevicePixelRatio();
337 m_frameSvg->setImagePath(path);
338
339 if (implicitWidth() <= 0) {
341 }
342
343 if (implicitHeight() <= 0) {
345 }
346
347 Q_EMIT imagePathChanged();
348
349 if (isComponentComplete()) {
350 applyPrefixes();
351
352 m_frameSvg->resizeFrame(size());
353 m_textureChanged = true;
354 update();
355 }
356}
357
359{
360 return m_frameSvg->imagePath();
361}
362
363void FrameSvgItem::setPrefix(const QVariant &prefixes)
364{
365 QStringList prefixList;
366 // is this a simple string?
367 if (prefixes.canConvert<QString>()) {
368 prefixList << prefixes.toString();
369 } else if (prefixes.canConvert<QStringList>()) {
370 prefixList = prefixes.toStringList();
371 }
372
373 if (m_prefixes == prefixList) {
374 return;
375 }
376
377 CheckMarginsChange checkMargins(m_oldMargins, m_margins);
378 CheckMarginsChange checkFixedMargins(m_oldFixedMargins, m_fixedMargins);
379 CheckMarginsChange checkInsetMargins(m_oldInsetMargins, m_insetMargins);
380
381 m_prefixes = prefixList;
382 applyPrefixes();
383
384 if (implicitWidth() <= 0) {
386 }
387
388 if (implicitHeight() <= 0) {
390 }
391
392 Q_EMIT prefixChanged();
393
394 if (isComponentComplete()) {
395 m_frameSvg->resizeFrame(QSizeF(width(), height()));
396 m_textureChanged = true;
397 update();
398 }
399}
400
402{
403 return m_prefixes;
404}
405
407{
408 return m_frameSvg->prefix();
409}
410
411FrameSvgItemMargins *FrameSvgItem::margins()
412{
413 if (!m_margins) {
414 m_margins = new FrameSvgItemMargins(m_frameSvg, this);
415 }
416 return m_margins;
417}
418
419FrameSvgItemMargins *FrameSvgItem::fixedMargins()
420{
421 if (!m_fixedMargins) {
422 m_fixedMargins = new FrameSvgItemMargins(m_frameSvg, this);
423 m_fixedMargins->setFixed(true);
424 }
425 return m_fixedMargins;
426}
427
428FrameSvgItemMargins *FrameSvgItem::inset()
429{
430 if (!m_insetMargins) {
431 m_insetMargins = new FrameSvgItemMargins(m_frameSvg, this);
432 m_insetMargins->setInset(true);
433 }
434 return m_insetMargins;
435}
436
438{
439 return m_frameSvg->fromCurrentImageSet();
440}
441
442void FrameSvgItem::setStatus(KSvg::Svg::Status status)
443{
444 m_frameSvg->setStatus(status);
445}
446
447KSvg::Svg::Status FrameSvgItem::status() const
448{
449 return m_frameSvg->status();
450}
451
452void FrameSvgItem::setEnabledBorders(const KSvg::FrameSvg::EnabledBorders borders)
453{
454 if (m_frameSvg->enabledBorders() == borders) {
455 return;
456 }
457
458 CheckMarginsChange checkMargins(m_oldMargins, m_margins);
459
460 m_frameSvg->setEnabledBorders(borders);
461 Q_EMIT enabledBordersChanged();
462 m_textureChanged = true;
463 update();
464}
465
467{
468 return m_frameSvg->enabledBorders();
469}
470
471void FrameSvgItem::setColorSet(KSvg::Svg::ColorSet colorSet)
472{
473 if (m_frameSvg->colorSet() == colorSet) {
474 return;
475 }
476
477 m_frameSvg->setColorSet(colorSet);
478 m_textureChanged = true;
479
480 update();
481}
482
483KSvg::Svg::ColorSet FrameSvgItem::colorSet() const
484{
485 return m_frameSvg->colorSet();
486}
487
489{
490 return m_frameSvg->hasElementPrefix(prefix);
491}
492
493bool FrameSvgItem::hasElement(const QString &elementName) const
494{
495 return m_frameSvg->hasElement(elementName);
496}
497
499{
500 return m_frameSvg->mask();
501}
502
504{
505 return m_frameSvg->minimumDrawingHeight();
506}
507
509{
510 return m_frameSvg->minimumDrawingWidth();
511}
512
513void FrameSvgItem::geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
514{
515 const bool isComponentComplete = this->isComponentComplete();
516 if (isComponentComplete) {
517 m_frameSvg->resizeFrame(newGeometry.size());
518 m_sizeChanged = true;
519 }
520
521 QQuickItem::geometryChange(newGeometry, oldGeometry);
522
523 // the above only triggers updatePaintNode, so we have to inform subscribers
524 // about the potential change of the mask explicitly here
526 Q_EMIT maskChanged();
527 }
528}
529
530void FrameSvgItem::doUpdate()
531{
532 if (m_frameSvg->isRepaintBlocked()) {
533 return;
534 }
535
536 CheckMarginsChange checkMargins(m_oldMargins, m_margins);
537 CheckMarginsChange checkFixedMargins(m_oldFixedMargins, m_fixedMargins);
538 CheckMarginsChange checkInsetMargins(m_oldInsetMargins, m_insetMargins);
539
540 // if the theme changed, the available prefix may have changed as well
541 applyPrefixes();
542
543 if (implicitWidth() <= 0) {
545 }
546
547 if (implicitHeight() <= 0) {
549 }
550
551 QString prefix = m_frameSvg->actualPrefix();
552 bool hasOverlay = (!prefix.startsWith(QLatin1String("mask-")) //
553 && m_frameSvg->hasElement(prefix % QLatin1String("overlay")));
554 bool hasComposeOverBorder = m_frameSvg->hasElement(prefix % QLatin1String("hint-compose-over-border"))
555 && m_frameSvg->hasElement(QLatin1String("mask-") % prefix % QLatin1String("center"));
556 m_fastPath = !hasOverlay && !hasComposeOverBorder;
557
558 // Software rendering (at time of writing Qt5.10) doesn't seem to like our
559 // tiling/stretching in the 9-tiles.
560 // Also when using QPainter it's arguably faster to create and cache pixmaps
561 // of the whole frame, which is what the slow path does
562 if (QQuickWindow::sceneGraphBackend() == QLatin1String("software")) {
563 m_fastPath = false;
564 }
565 m_textureChanged = true;
566
567 update();
568
569 Q_EMIT maskChanged();
570 Q_EMIT repaintNeeded();
571}
572
573KSvg::FrameSvg *FrameSvgItem::frameSvg() const
574{
575 return m_frameSvg;
576}
577
578QSGNode *FrameSvgItem::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
579{
580 if (!window() || !m_frameSvg //
581 || (!m_frameSvg->hasElementPrefix(m_frameSvg->actualPrefix()) //
582 && !m_frameSvg->hasElementPrefix(m_frameSvg->prefix()))) {
583 delete oldNode;
584 return nullptr;
585 }
586
588
589 if (m_fastPath) {
590 if (m_textureChanged) {
591 delete oldNode;
592 oldNode = nullptr;
593 }
594
595 if (!oldNode) {
596 QString prefix = m_frameSvg->actualPrefix();
597 oldNode = new FrameNode(prefix, m_frameSvg);
598
599 bool tileCenter = (m_frameSvg->hasElement(QStringLiteral("hint-tile-center")) //
600 || m_frameSvg->hasElement(prefix % QLatin1String("hint-tile-center")));
601 bool stretchBorders = (m_frameSvg->hasElement(QStringLiteral("hint-stretch-borders")) //
602 || m_frameSvg->hasElement(prefix % QLatin1String("hint-stretch-borders")));
603 FrameItemNode::FitMode borderFitMode = stretchBorders ? FrameItemNode::Stretch : FrameItemNode::Tile;
604 FrameItemNode::FitMode centerFitMode = tileCenter ? FrameItemNode::Tile : FrameItemNode::Stretch;
605
606 new FrameItemNode(this, FrameSvg::NoBorder, centerFitMode, oldNode);
607 if (enabledBorders() & (FrameSvg::TopBorder | FrameSvg::LeftBorder)) {
608 new FrameItemNode(this, FrameSvg::TopBorder | FrameSvg::LeftBorder, FrameItemNode::FastStretch, oldNode);
609 }
610 if (enabledBorders() & (FrameSvg::TopBorder | FrameSvg::RightBorder)) {
611 new FrameItemNode(this, FrameSvg::TopBorder | FrameSvg::RightBorder, FrameItemNode::FastStretch, oldNode);
612 }
613 if (enabledBorders() & FrameSvg::TopBorder) {
614 new FrameItemNode(this, FrameSvg::TopBorder, borderFitMode, oldNode);
615 }
616 if (enabledBorders() & FrameSvg::BottomBorder) {
617 new FrameItemNode(this, FrameSvg::BottomBorder, borderFitMode, oldNode);
618 }
619 if (enabledBorders() & (FrameSvg::BottomBorder | FrameSvg::LeftBorder)) {
620 new FrameItemNode(this, FrameSvg::BottomBorder | FrameSvg::LeftBorder, FrameItemNode::FastStretch, oldNode);
621 }
622 if (enabledBorders() & (FrameSvg::BottomBorder | FrameSvg::RightBorder)) {
623 new FrameItemNode(this, FrameSvg::BottomBorder | FrameSvg::RightBorder, FrameItemNode::FastStretch, oldNode);
624 }
625 if (enabledBorders() & FrameSvg::LeftBorder) {
626 new FrameItemNode(this, FrameSvg::LeftBorder, borderFitMode, oldNode);
627 }
628 if (enabledBorders() & FrameSvg::RightBorder) {
629 new FrameItemNode(this, FrameSvg::RightBorder, borderFitMode, oldNode);
630 }
631
632 m_sizeChanged = true;
633 m_textureChanged = false;
634 }
635
636 QSGNode *node = oldNode->firstChild();
637 while (node) {
638 static_cast<FrameItemNode *>(node)->setFiltering(filtering);
639 node = node->nextSibling();
640 }
641
642 if (m_sizeChanged) {
643 FrameNode *frameNode = static_cast<FrameNode *>(oldNode);
644 QSize frameSize(width(), height());
645 QRect geometry = frameNode->contentsRect(frameSize);
646 QSGNode *node = oldNode->firstChild();
647 while (node) {
648 static_cast<FrameItemNode *>(node)->reposition(geometry, frameSize);
649 node = node->nextSibling();
650 }
651
652 m_sizeChanged = false;
653 }
654 } else {
655 ManagedTextureNode *textureNode = dynamic_cast<ManagedTextureNode *>(oldNode);
656 if (!textureNode) {
657 delete oldNode;
658 textureNode = new ManagedTextureNode;
659 m_textureChanged = true; // force updating the texture on our newly created node
660 oldNode = textureNode;
661 }
662 textureNode->setFiltering(filtering);
663
664 if ((m_textureChanged || m_sizeChanged) || textureNode->texture()->textureSize() != m_frameSvg->size()) {
665 QImage image = m_frameSvg->framePixmap().toImage();
666 textureNode->setTexture(s_cache->loadTexture(window(), image));
667 textureNode->setRect(0, 0, width(), height());
668
669 m_textureChanged = false;
670 m_sizeChanged = false;
671 }
672 }
673
674 return oldNode;
675}
676
678{
680 m_frameSvg->setRepaintBlocked(true);
681}
682
684{
685 m_kirigamiTheme = qobject_cast<Kirigami::Platform::PlatformTheme *>(qmlAttachedPropertiesObject<Kirigami::Platform::PlatformTheme>(this, true));
686 if (!m_kirigamiTheme) {
687 qCWarning(LOG_KSVGQML) << "no theme!" << qmlAttachedPropertiesObject<Kirigami::Platform::PlatformTheme>(this, true) << this;
688 return;
689 }
690
691 auto checkApplyTheme = [this]() {
692 if (!m_frameSvg->imageSet()->filePath(QStringLiteral("colors")).isEmpty()) {
693 m_frameSvg->clearCache();
694 m_frameSvg->clearColorOverrides();
695 }
696 };
697 auto applyTheme = [this]() {
698 if (!m_frameSvg->imageSet()->filePath(QStringLiteral("colors")).isEmpty()) {
699 m_frameSvg->clearCache();
700 m_frameSvg->clearColorOverrides();
701
702 return;
703 }
704 m_frameSvg->setColor(Svg::Text, m_kirigamiTheme->textColor());
705 m_frameSvg->setColor(Svg::Background, m_kirigamiTheme->backgroundColor());
706 m_frameSvg->setColor(Svg::Highlight, m_kirigamiTheme->highlightColor());
707 m_frameSvg->setColor(Svg::HighlightedText, m_kirigamiTheme->highlightedTextColor());
708 m_frameSvg->setColor(Svg::PositiveText, m_kirigamiTheme->positiveTextColor());
709 m_frameSvg->setColor(Svg::NeutralText, m_kirigamiTheme->neutralTextColor());
710 m_frameSvg->setColor(Svg::NegativeText, m_kirigamiTheme->negativeTextColor());
711 };
712 applyTheme();
713 connect(m_kirigamiTheme, &Kirigami::Platform::PlatformTheme::colorsChanged, this, applyTheme);
714 connect(m_frameSvg->imageSet(), &ImageSet::imageSetChanged, this, checkApplyTheme);
715 connect(m_frameSvg, &Svg::imageSetChanged, this, checkApplyTheme);
716
717 CheckMarginsChange checkMargins(m_oldMargins, m_margins);
718 CheckMarginsChange checkFixedMargins(m_oldFixedMargins, m_fixedMargins);
719 CheckMarginsChange checkInsetMargins(m_oldInsetMargins, m_insetMargins);
720
722 m_frameSvg->resizeFrame(size());
723 m_frameSvg->setRepaintBlocked(false);
724 m_textureChanged = true;
725}
726
727void FrameSvgItem::updateDevicePixelRatio()
728{
729 // devicepixelratio is always set integer in the svg, so needs at least 192dpi to double up.
730 //(it needs to be integer to have lines contained inside a svg piece to keep being pixel aligned)
731 const auto newDevicePixelRatio = std::max<qreal>(1.0, floor(window() ? window()->devicePixelRatio() : qApp->devicePixelRatio()));
732
733 if (newDevicePixelRatio != m_frameSvg->devicePixelRatio()) {
734 m_frameSvg->setDevicePixelRatio(newDevicePixelRatio);
735 m_textureChanged = true;
736 }
737}
738
739void FrameSvgItem::applyPrefixes()
740{
741 if (m_frameSvg->imagePath().isEmpty()) {
742 return;
743 }
744
745 const QString oldPrefix = m_frameSvg->prefix();
746
747 if (m_prefixes.isEmpty()) {
748 m_frameSvg->setElementPrefix(QString());
749 if (oldPrefix != m_frameSvg->prefix()) {
750 Q_EMIT usedPrefixChanged();
751 }
752 return;
753 }
754
755 bool found = false;
756 for (const QString &prefix : std::as_const(m_prefixes)) {
757 if (m_frameSvg->hasElementPrefix(prefix)) {
758 m_frameSvg->setElementPrefix(prefix);
759 found = true;
760 break;
761 }
762 }
763 if (!found) {
764 // this setElementPrefix is done to keep the same behavior as before, when it was a simple string
765 m_frameSvg->setElementPrefix(m_prefixes.constLast());
766 }
767 if (oldPrefix != m_frameSvg->prefix()) {
768 Q_EMIT usedPrefixChanged();
769 }
770}
771
773{
774 if (change == ItemSceneChange && value.window) {
775 updateDevicePixelRatio();
776 } else if (change == QQuickItem::ItemDevicePixelRatioHasChanged) {
777 updateDevicePixelRatio();
778 }
779
780 QQuickItem::itemChange(change, value);
781}
782
783} // KSvg namespace
784
785#include "moc_framesvgitem.cpp"
Helps to manage textures by creating images and reference counts them.
qreal horizontal
This property holds the combined width of the left and right margins.
qreal vertical
This property holds the combined width of the top and bottom margins.
QList< qreal > margins() const
returns a vector with left, top, right, bottom
qreal right
This property holds the right margin's width in pixels.
qreal bottom
This property holds the bottom margin's width in pixels.
qreal left
This property holds the left margin's width in pixels.
qreal top
This property holds the top margin's width in pixels.
An SVG Item with borders.
Q_INVOKABLE bool hasElementPrefix(const QString &prefix) const
QString imagePath
This property specifies the relative path of the SVG in the theme.
KSvg::FrameSvg::EnabledBorders enabledBorders
This property specifies which borders are shown.
Q_INVOKABLE bool hasElement(const QString &elementName) const
QString usedPrefix
This property holds the actual prefix that was used, if a fallback chain array was set as "prefix".
bool fromCurrentImageSet
This property holds whether the current SVG is present in the currently-used theme and no fallback is...
KSvg::Svg::Status status
This property specifies the SVG's status.
int minimumDrawingWidth
This property holds the minimum width required to correctly draw this SVG.
QRegion mask
This property holds the mask that contains the SVG's painted areas.
QVariant prefix
This property holds the prefix for the SVG.
int minimumDrawingHeight
This property holds the minimum height required to correctly draw this SVG.
KSvg::FrameSvgItemMargins * margins
This property holds the frame's margins.
Provides an SVG with borders.
Definition framesvg.h:61
Q_INVOKABLE bool hasElementPrefix(const QString &prefix) const
This method returns whether the SVG has the necessary elements with the given prefix to draw a frame.
Definition framesvg.cpp:130
bool isRepaintBlocked() const
This method returns whether we are in a transaction of many changes at once.
Definition framesvg.cpp:999
Q_INVOKABLE void clearCache()
This method deletes the internal cache.
Definition framesvg.cpp:374
@ BottomMargin
The bottom margin.
Definition framesvg.h:93
@ RightMargin
The right margin.
Definition framesvg.h:95
@ LeftMargin
The left margin.
Definition framesvg.h:94
@ TopMargin
The top margin.
Definition framesvg.h:92
Q_INVOKABLE int minimumDrawingHeight()
This method returns the minimum height required to correctly draw this SVG.
Definition framesvg.cpp:419
Q_INVOKABLE int minimumDrawingWidth()
This method returns the minimum width required to correctly draw this SVG.
Definition framesvg.cpp:427
Q_INVOKABLE qreal marginSize(const FrameSvg::MarginEdge edge) const
This method returns the margin size for the given edge.
Definition framesvg.cpp:197
QString actualPrefix() const
This method returns the prefix that is actually being used (including a '-' at the end if not empty).
Definition framesvg.cpp:994
void setRepaintBlocked(bool blocked)
This method sets whether we should block rebuilding generated graphics for each change made.
Q_INVOKABLE QRegion mask() const
This method returns a mask that tightly contains the fully opaque areas of the SVG.
Definition framesvg.cpp:330
void setEnabledBorders(const EnabledBorders borders)
This method sets which borders should be painted.
Definition framesvg.cpp:70
Q_INVOKABLE qreal insetSize(const FrameSvg::MarginEdge edge) const
This method returns the insets margin size for the specified edge.
Definition framesvg.cpp:223
Q_INVOKABLE void getMargins(qreal &left, qreal &top, qreal &right, qreal &bottom) const
This is a convenience method that extracts the size of the four margins and saves their size into the...
Definition framesvg.cpp:275
Q_INVOKABLE QString prefix()
This method returns the prefix for SVG elements of the FrameSvg (including a '-' at the end if not em...
Definition framesvg.cpp:160
Q_INVOKABLE void setElementPrefix(KSvg::FrameSvg::LocationPrefix location)
This method sets the prefix (.
Definition framesvg.cpp:88
Q_INVOKABLE void setImagePath(const QString &path) override
Loads a new Svg.
Definition framesvg.cpp:55
Q_INVOKABLE QPixmap framePixmap()
This method returns a pixmap of the SVG represented by this object.
Definition framesvg.cpp:386
Q_INVOKABLE void resizeFrame(const QSizeF &size)
This method resizes the frame, maintaining the same border size.
Definition framesvg.cpp:165
Q_INVOKABLE qreal fixedMarginSize(const FrameSvg::MarginEdge edge) const
This method returns the margin size for the specified edge.
Definition framesvg.cpp:249
void imageSetChanged(const QString &basePath)
@brif This signal is emitted when the user makes changes to the theme.
QString filePath(const QString &name) const
This method returns the path for a generic file in the current theme.
Definition imageset.cpp:194
void statusChanged(KSvg::Svg::Status status)
This signal is emitted when the status has changed.
void setStatus(Svg::Status status)
This method sets the image in a selected status.
Definition svg.cpp:1115
void fromCurrentImageSetChanged(bool fromCurrentImageSet)
This signal is emitted when the value of fromCurrentImageSet() has changed.
void setColorSet(ColorSet colorSet)
This method sets a color set for the SVG.
Definition svg.cpp:1132
qreal devicePixelRatio() const
This method returns the device pixel ratio for this Svg.
Definition svg.cpp:913
void setDevicePixelRatio(qreal factor)
This method sets the device pixel ratio for the Svg.
Definition svg.cpp:895
ImageSet * imageSet() const
This method returns the KSvg::ImageSet used by this Svg object.
Definition svg.cpp:1110
void repaintNeeded()
This signal is emitted whenever the SVG data has changed in such a way that a repaint is required.
void imageSetChanged(ImageSet *imageSet)
This signal is emitted when the image set has changed.
Q_INVOKABLE bool hasElement(const QString &elementId) const
This method checks whether an element exists in the loaded SVG.
Definition svg.cpp:1024
Q_INVOKABLE QSizeF elementSize(const QString &elementId) const
This method returns the size of a given element.
Definition svg.cpp:1002
Q_SCRIPTABLE CaptureState status()
The KSvg namespace.
const T & constLast() const const
bool isEmpty() const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QImage toImage() const const
virtual void classBegin() override
virtual void componentComplete() override
virtual void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry)
bool isComponentComplete() const const
virtual void itemChange(ItemChange change, const ItemChangeData &value)
QSizeF size() const const
void update()
virtual QSGNode * updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData)
QQuickWindow * window() const const
typedef CreateTextureOptions
QString sceneGraphBackend()
int height() const const
bool isEmpty() const const
bool isValid() const const
QSize size() const const
int width() const const
void setHeight(qreal height)
void setWidth(qreal width)
QSizeF size() const const
QSGGeometry * geometry()
void updateTexturedRectGeometry(QSGGeometry *g, const QRectF &rect, const QRectF &textureRect)
QSGMaterial * material() const const
QSGMaterial * opaqueMaterial() const const
void appendChildNode(QSGNode *node)
QSGNode * firstChild() const const
void markDirty(DirtyState bits)
QSGNode * nextSibling() const const
QSGNode * parent() const const
void setFiltering(QSGTexture::Filtering filtering)
void setRect(const QRectF &r)
QSGTexture * texture() const const
virtual QRectF normalizedTextureSubRect() const const
virtual QSize textureSize() const const=0
int height() const const
bool isEmpty() const const
int width() const const
qreal height() const const
qreal width() const const
bool isEmpty() const const
QTextStream & fixed(QTextStream &stream)
bool canConvert() const const
QString toString() const const
QStringList toStringList() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri May 10 2024 11:47:10 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.