From d8a1a9eb084b19e552c789244267f7346e1b27a8 Mon Sep 17 00:00:00 2001 From: Marco Martin Date: Tue, 7 Feb 2017 13:05:57 +0100 Subject: [PATCH] don't regenerate frames when setting every property Summary: give frameSvg the concept of repaintBlocked(), that enables and disables the regeneration of the frame data when a property is set. the use case is when often, a lot of properties are set one after the other (such as prefix, enabled borders, size) collapse the formely similar, but a bit different logic of frame regeneration is a single function for better maintanability. QML FrameSvgItem sets repaintblocked when it starts and releases it just on oncomponentCompleted Test Plan: plasmashell still starts, autotests still work, all frames are rendered correctly the destruction of old frames is cutted by 50%. in the qml profiler the creation time of a framesvgitem slightly improved, on this machine from around 26 msecs to around 21, can still be improved, but at least the code is a bit simpler Reviewers: #plasma Subscribers: davidedmundson, plasma-devel, #frameworks Tags: #plasma, #frameworks Differential Revision: https://phabricator.kde.org/D4414 --- autotests/data/background.svgz | Bin 2703 -> 2879 bytes autotests/framesvgtest.cpp | 17 + autotests/framesvgtest.h | 1 + src/declarativeimports/core/framesvgitem.cpp | 7 + src/declarativeimports/core/framesvgitem.h | 3 + src/plasma/framesvg.cpp | 556 ++++++++----------- src/plasma/framesvg.h | 19 +- src/plasma/private/framesvg_p.h | 22 +- 8 files changed, 296 insertions(+), 329 deletions(-) diff --git a/autotests/data/background.svgz b/autotests/data/background.svgz index b226eba4ccdfbed491183c0d0f52dffe24965b24..70a668aa314b238a2997704e02c3e175f998c657 100644 GIT binary patch literal 2879 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 literal 2703 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, QRectF(26, 26, 48, 48)); } +void FrameSvgTest::repaintBlocked() +{ + //check the properties to be correct even if set during a repaint blocked transaction + m_frameSvg->setRepaintBlocked(true); + QVERIFY(m_frameSvg->isRepaintBlocked()); + + m_frameSvg->setElementPrefix("prefix"); + m_frameSvg->setEnabledBorders(Plasma::FrameSvg::TopBorder|Plasma::FrameSvg::LeftBorder); + m_frameSvg->resizeFrame(QSizeF(100,100)); + + m_frameSvg->setRepaintBlocked(false); + + QCOMPARE(m_frameSvg->prefix(), QString("prefix")); + QCOMPARE(m_frameSvg->enabledBorders(), Plasma::FrameSvg::TopBorder|Plasma::FrameSvg::LeftBorder); + QCOMPARE(m_frameSvg->frameSize(), QSizeF(100,100)); +} + void FrameSvgTest::setTheme() { // Should not crash diff --git a/autotests/framesvgtest.h b/autotests/framesvgtest.h index e201becf6..e84385a1f 100644 --- a/autotests/framesvgtest.h +++ b/autotests/framesvgtest.h @@ -38,6 +38,7 @@ private Q_SLOTS: void margins(); void contentsRect(); void setTheme(); + void repaintBlocked(); private: Plasma::FrameSvg *m_frameSvg; diff --git a/src/declarativeimports/core/framesvgitem.cpp b/src/declarativeimports/core/framesvgitem.cpp index 966f5b609..e61d51ed1 100644 --- a/src/declarativeimports/core/framesvgitem.cpp +++ b/src/declarativeimports/core/framesvgitem.cpp @@ -516,10 +516,17 @@ QSGNode *FrameSvgItem::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaint return oldNode; } +void FrameSvgItem::classBegin() +{ + QQuickItem::classBegin(); + m_frameSvg->setRepaintBlocked(true); +} + void FrameSvgItem::componentComplete() { QQuickItem::componentComplete(); m_frameSvg->resizeFrame(QSize(width(), height())); + m_frameSvg->setRepaintBlocked(false); m_textureChanged = true; } diff --git a/src/declarativeimports/core/framesvgitem.h b/src/declarativeimports/core/framesvgitem.h index e2a48e8e7..b32432364 100644 --- a/src/declarativeimports/core/framesvgitem.h +++ b/src/declarativeimports/core/framesvgitem.h @@ -22,6 +22,7 @@ #define FRAMESVGITEM_P #include +#include #include @@ -107,6 +108,7 @@ private: class FrameSvgItem : public QQuickItem { Q_OBJECT + Q_INTERFACES(QQmlParserStatus) /** * Theme relative path of the svg, like "widgets/background" @@ -213,6 +215,7 @@ public: protected: + void classBegin() Q_DECL_OVERRIDE; void componentComplete() Q_DECL_OVERRIDE; /// @endcond diff --git a/src/plasma/framesvg.cpp b/src/plasma/framesvg.cpp index f75252835..16d0e17c1 100644 --- a/src/plasma/framesvg.cpp +++ b/src/plasma/framesvg.cpp @@ -49,7 +49,9 @@ static const int MAX_FRAME_SIZE = 100000; FrameData::~FrameData() { for (auto it = references.constBegin(), end = references.constEnd(); it != end; ++it) { - it.key()->d->frames.remove(prefix); + if (it.key()->d->frame == this) { + it.key()->d->frame = nullptr; + } } } @@ -59,7 +61,7 @@ FrameSvg::FrameSvg(QObject *parent) d(new FrameSvgPrivate(this)) { connect(this, SIGNAL(repaintNeeded()), this, SLOT(updateNeeded())); - d->frames.insert(QString(), new FrameData(this, QString())); + d->frame = new FrameData(this, QString()); } FrameSvg::~FrameSvg() @@ -76,7 +78,7 @@ void FrameSvg::setImagePath(const QString &path) bool updateNeeded = true; clearCache(); - FrameData *fd = d->frames[d->prefix]; + FrameData *fd = d->frame; if (fd->refcount() == 1) { // we're the only user of it, let's remove it from the shared keys // we don't want to deref it, however, as we'll still be using it @@ -92,7 +94,7 @@ void FrameSvg::setImagePath(const QString &path) if (!fd) { // we need to replace our frame, start by looking in the frame cache - FrameData *oldFd = d->frames[d->prefix]; + FrameData *oldFd = d->frame; const QString key = d->cacheId(oldFd, d->prefix); fd = FrameSvgPrivate::s_sharedFrames[theme()->d].value(key); @@ -108,7 +110,7 @@ void FrameSvg::setImagePath(const QString &path) fd = new FrameData(*oldFd, this); } - d->frames.insert(d->prefix, fd); + d->frame = fd; } setContainsMultipleImages(true); @@ -127,67 +129,20 @@ void FrameSvg::setImagePath(const QString &path) void FrameSvg::setEnabledBorders(const EnabledBorders borders) { - if (borders == d->frames[d->prefix]->enabledBorders) { + if (borders == d->enabledBorders) { return; } - FrameData *fd = d->frames[d->prefix]; + d->enabledBorders = borders; - const QString oldKey = d->cacheId(fd, d->prefix); - const EnabledBorders oldBorders = fd->enabledBorders; - fd->enabledBorders = borders; - const QString newKey = d->cacheId(fd, d->prefix); - fd->enabledBorders = oldBorders; - - //qCDebug(LOG_PLASMA) << "looking for" << newKey; - FrameData *newFd = FrameSvgPrivate::s_sharedFrames[theme()->d].value(newKey); - if (newFd) { - //qCDebug(LOG_PLASMA) << "FOUND IT!" << newFd->refcount; - // we've found a math, so insert that new one and ref it .. - newFd->ref(this); - d->frames.insert(d->prefix, newFd); - - //.. then deref the old one and if it's no longer used, get rid of it - if (fd->deref(this)) { - //const QString oldKey = d->cacheId(fd, d->prefix); - //qCDebug(LOG_PLASMA) << "1. Removing it" << oldKey << fd->refcount; - FrameSvgPrivate::s_sharedFrames[fd->theme].remove(oldKey); - delete fd; - } - - return; + if (!d->repaintBlocked) { + d->updateFrameData(); } - - if (fd->refcount() == 1) { - // we're the only user of it, let's remove it from the shared keys - // we don't want to deref it, however, as we'll still be using it - FrameSvgPrivate::s_sharedFrames[fd->theme].remove(oldKey); - } else { - // others are using it, but we wish to change its size. so deref it, - // then create a copy of it (we're automatically ref'd via the ctor), - // then insert it into our frames. - fd->deref(this); - fd = new FrameData(*fd, this); - d->frames.insert(d->prefix, fd); - } - - fd->enabledBorders = borders; - d->updateAndSignalSizes(); } FrameSvg::EnabledBorders FrameSvg::enabledBorders() const { - if (d->frames.isEmpty()) { - return NoBorder; - } - - QHash::const_iterator it = d->frames.constFind(d->prefix); - - if (it != d->frames.constEnd()) { - return it.value()->enabledBorders; - } else { - return NoBorder; - } + return d->enabledBorders; } void FrameSvg::setElementPrefix(Plasma::Types::Location location) @@ -215,8 +170,6 @@ void FrameSvg::setElementPrefix(Plasma::Types::Location location) void FrameSvg::setElementPrefix(const QString &prefix) { - const QString oldPrefix(d->prefix); - if (!hasElement(prefix % QLatin1String("-center"))) { d->prefix.clear(); } else { @@ -227,64 +180,11 @@ void FrameSvg::setElementPrefix(const QString &prefix) } d->requestedPrefix = prefix; - FrameData *oldFrameData = d->frames.value(oldPrefix); - if (oldPrefix == d->prefix && oldFrameData) { - return; - } - - if (!d->frames.contains(d->prefix)) { - if (oldFrameData) { - FrameData *newFd = 0; - if (!oldFrameData->frameSize.isEmpty()) { - const QString key = d->cacheId(oldFrameData, d->prefix); - newFd = FrameSvgPrivate::s_sharedFrames[theme()->d].value(key); - if (newFd && newFd->devicePixelRatio != devicePixelRatio()) { - newFd = 0; - } - } - - // we need to put this in the cache if we didn't find it in the shared frames - // and we have a size; if we don't have a size, we'll catch it later - const bool cache = !newFd && !oldFrameData->frameSize.isEmpty(); - if (newFd) { - newFd->ref(this); - } else { - newFd = new FrameData(*oldFrameData, this); - } - - d->frames.insert(d->prefix, newFd); - - if (cache) { - // we have to cache after inserting the frame since the cacheId requires the - // frame to be in the frames collection already - const QString key = d->cacheId(oldFrameData, d->prefix); - //qCDebug(LOG_PLASMA) << this << " 1. inserting as" << key; - - FrameSvgPrivate::s_sharedFrames[theme()->d].insert(key, newFd); - newFd->theme = theme()->d; - } - } else { - // couldn't find anything useful, so we just create something here - // we don't have a size for it yet, so don't bother trying to share it just yet - FrameData *newFd = new FrameData(this, d->prefix); - d->frames.insert(d->prefix, newFd); - } - - d->updateSizes(); - } - - if (!d->cacheAll) { - d->frames.remove(oldPrefix); - if (oldFrameData) { - if (oldFrameData->deref(this)) { - const QString oldKey = d->cacheId(oldFrameData, oldPrefix); - FrameSvgPrivate::s_sharedFrames[oldFrameData->theme].remove(oldKey); - delete oldFrameData; - } - } - } - d->location = Types::Floating; + + if (!d->repaintBlocked) { + d->updateFrameData(); + } } bool FrameSvg::hasElementPrefix(const QString &prefix) const @@ -337,155 +237,108 @@ void FrameSvg::resizeFrame(const QSizeF &size) return; } - FrameData *fd = d->frames[d->prefix]; - if (size == fd->frameSize) { + if (size.toSize() == d->frame->frameSize) { return; } + d->pendingFrameSize = size.toSize(); - const QString oldKey = d->cacheId(fd, d->prefix); - const QSize currentSize = fd->frameSize; - fd->frameSize = size.toSize(); - const QString newKey = d->cacheId(fd, d->prefix); - fd->frameSize = currentSize; - - //qCDebug(LOG_PLASMA) << "looking for" << newKey; - FrameData *newFd = FrameSvgPrivate::s_sharedFrames[theme()->d].value(newKey); - if (newFd) { - //qCDebug(LOG_PLASMA) << "FOUND IT!" << newFd->refcount; - // we've found a math, so insert that new one and ref it .. - newFd->ref(this); - d->frames.insert(d->prefix, newFd); - - //.. then deref the old one and if it's no longer used, get rid of it - if (fd->deref(this)) { - //const QString oldKey = d->cacheId(fd, d->prefix); - //qCDebug(LOG_PLASMA) << "1. Removing it" << oldKey << fd->refcount; - FrameSvgPrivate::s_sharedFrames[fd->theme].remove(oldKey); - delete fd; - } - - return; + if (!d->repaintBlocked) { + d->updateFrameData(); } - - if (fd->refcount() == 1) { - // we're the only user of it, let's remove it from the shared keys - // we don't want to deref it, however, as we'll still be using it - FrameSvgPrivate::s_sharedFrames[fd->theme].remove(oldKey); - } else { - // others are using it, but we wish to change its size. so deref it, - // then create a copy of it (we're automatically ref'd via the ctor), - // then insert it into our frames. - fd->deref(this); - fd = new FrameData(*fd, this); - d->frames.insert(d->prefix, fd); - } - - d->updateSizes(); - fd->frameSize = size.toSize(); - // we know it isn't in s_sharedFrames due to the check above, so insert it now - FrameSvgPrivate::s_sharedFrames[theme()->d].insert(newKey, fd); - fd->theme = theme()->d; } QSizeF FrameSvg::frameSize() const { - QHash::const_iterator it = d->frames.constFind(d->prefix); - - if (it == d->frames.constEnd()) { + if (!d->frame) { return QSize(-1, -1); } else { - return d->frameSize(it.value()); + return d->frameSize(d->frame); } } qreal FrameSvg::marginSize(const Plasma::Types::MarginEdge edge) const { - if (d->frames[d->prefix]->noBorderPadding) { + if (d->frame->noBorderPadding) { return .0; } switch (edge) { case Plasma::Types::TopMargin: - return d->frames[d->prefix]->topMargin; + return d->frame->topMargin; break; case Plasma::Types::LeftMargin: - return d->frames[d->prefix]->leftMargin; + return d->frame->leftMargin; break; case Plasma::Types::RightMargin: - return d->frames[d->prefix]->rightMargin; + return d->frame->rightMargin; break; //Plasma::BottomMargin default: - return d->frames[d->prefix]->bottomMargin; + return d->frame->bottomMargin; break; } } qreal FrameSvg::fixedMarginSize(const Plasma::Types::MarginEdge edge) const { - if (d->frames[d->prefix]->noBorderPadding) { + if (d->frame->noBorderPadding) { return .0; } switch (edge) { case Plasma::Types::TopMargin: - return d->frames[d->prefix]->fixedTopMargin; + return d->frame->fixedTopMargin; break; case Plasma::Types::LeftMargin: - return d->frames[d->prefix]->fixedLeftMargin; + return d->frame->fixedLeftMargin; break; case Plasma::Types::RightMargin: - return d->frames[d->prefix]->fixedRightMargin; + return d->frame->fixedRightMargin; break; //Plasma::BottomMargin default: - return d->frames[d->prefix]->fixedBottomMargin; + return d->frame->fixedBottomMargin; break; } } void FrameSvg::getMargins(qreal &left, qreal &top, qreal &right, qreal &bottom) const { - FrameData *frame = d->frames[d->prefix]; - - if (frame->noBorderPadding) { + if (d->frame->noBorderPadding) { left = top = right = bottom = 0; return; } - top = frame->topMargin; - left = frame->leftMargin; - right = frame->rightMargin; - bottom = frame->bottomMargin; + top = d->frame->topMargin; + left = d->frame->leftMargin; + right = d->frame->rightMargin; + bottom = d->frame->bottomMargin; } void FrameSvg::getFixedMargins(qreal &left, qreal &top, qreal &right, qreal &bottom) const { - FrameData *frame = d->frames[d->prefix]; - - if (frame->noBorderPadding) { + if (d->frame->noBorderPadding) { left = top = right = bottom = 0; return; } - top = frame->fixedTopMargin; - left = frame->fixedLeftMargin; - right = frame->fixedRightMargin; - bottom = frame->fixedBottomMargin; + top = d->frame->fixedTopMargin; + left = d->frame->fixedLeftMargin; + right = d->frame->fixedRightMargin; + bottom = d->frame->fixedBottomMargin; } QRectF FrameSvg::contentsRect() const { - FrameData* frame = d->frames.value(d->prefix); - if (frame) { - QRectF rect(QPoint(0,0), frame->frameSize); - return rect.adjusted(frame->leftMargin, frame->topMargin, -frame->rightMargin, -frame->bottomMargin); + if (d->frame) { + QRectF rect(QPoint(0,0), d->frame->frameSize); + return rect.adjusted(d->frame->leftMargin, d->frame->topMargin, -d->frame->rightMargin, -d->frame->bottomMargin); } else { return QRectF(); } @@ -499,16 +352,15 @@ QPixmap FrameSvg::alphaMask() const QRegion FrameSvg::mask() const { - FrameData *frame = d->frames[d->prefix]; - QString id = d->cacheId(frame, QString()); + QString id = d->cacheId(d->frame, QString()); - QRegion* obj = frame->cachedMasks.object(id); + QRegion* obj = d->frame->cachedMasks.object(id); QRegion result; if (!obj) { obj = new QRegion(QBitmap(d->alphaMask().alphaChannel().createMaskFromColor(Qt::black))); result = *obj; - frame->cachedMasks.insert(id, obj); + d->frame->cachedMasks.insert(id, obj); } else { result = *obj; @@ -532,59 +384,45 @@ bool FrameSvg::cacheAllRenderedFrames() const void FrameSvg::clearCache() { - FrameData *frame = d->frames[d->prefix]; - - // delete all the frames that aren't this one - QMutableHashIterator it(d->frames); - while (it.hasNext()) { - FrameData *p = it.next().value(); - if (frame != p) { - //TODO: should we clear from the Theme pixmap cache as well? - if (p->deref(this)) { - const QString key = d->cacheId(p, it.key()); - FrameSvgPrivate::s_sharedFrames[p->theme].remove(key); - p->cachedBackground = QPixmap(); - } - - it.remove(); - } + if (d->frame) { + d->frame->cachedBackground = QPixmap(); + } + if (d->maskFrame) { + d->maskFrame->cachedBackground = QPixmap(); } } QPixmap FrameSvg::framePixmap() { - FrameData *frame = d->frames[d->prefix]; - if (frame->cachedBackground.isNull()) { - d->generateBackground(frame); + if (d->frame->cachedBackground.isNull()) { + d->generateBackground(d->frame); } - return frame->cachedBackground; + return d->frame->cachedBackground; } void FrameSvg::paintFrame(QPainter *painter, const QRectF &target, const QRectF &source) { - FrameData *frame = d->frames[d->prefix]; - if (frame->cachedBackground.isNull()) { - d->generateBackground(frame); - if (frame->cachedBackground.isNull()) { + if (d->frame->cachedBackground.isNull()) { + d->generateBackground(d->frame); + if (d->frame->cachedBackground.isNull()) { return; } } - painter->drawPixmap(target, frame->cachedBackground, source.isValid() ? source : target); + painter->drawPixmap(target, d->frame->cachedBackground, source.isValid() ? source : target); } void FrameSvg::paintFrame(QPainter *painter, const QPointF &pos) { - FrameData *frame = d->frames[d->prefix]; - if (frame->cachedBackground.isNull()) { - d->generateBackground(frame); - if (frame->cachedBackground.isNull()) { + if (d->frame->cachedBackground.isNull()) { + d->generateBackground(d->frame); + if (d->frame->cachedBackground.isNull()) { return; } } - painter->drawPixmap(pos, frame->cachedBackground); + painter->drawPixmap(pos, d->frame->cachedBackground); } //#define DEBUG_FRAMESVG_CACHE @@ -596,36 +434,27 @@ FrameSvgPrivate::~FrameSvgPrivate() #endif #endif - QHashIterator it(frames); - while (it.hasNext()) { - it.next(); - if (it.value()) { - // we remove all references from this widget to the frame, and delete it if we're the - // last user - if (it.value()->removeRefs(q)) { - const QString key = cacheId(it.value(), it.key()); + // we remove all references from this widget to the frame, and delete it if we're the + // last user + if (frame && frame->removeRefs(q)) { + const QString key = cacheId(frame, frame->prefix); #ifdef DEBUG_FRAMESVG_CACHE #ifndef NDEBUG - // qCDebug(LOG_PLASMA) << "2. Removing it" << key << it.value() << it.value()->refcount() << s_sharedFrames[theme()->d].contains(key); + // qCDebug(LOG_PLASMA) << "2. Removing it" << key << frame << frame->refcount() << s_sharedFrames[theme()->d].contains(key); #endif #endif - s_sharedFrames[it.value()->theme].remove(key); - delete it.value(); - } -#ifdef DEBUG_FRAMESVG_CACHE - else { -#ifndef NDEBUG - // qCDebug(LOG_PLASMA) << "still shared:" << cacheId(it.value(), it.key()) << it.value() << it.value()->refcount() << it.value()->isUsed(); -#endif - } - } else { -#ifndef NDEBUG - // qCDebug(LOG_PLASMA) << "lost our value for" << it.key(); -#endif -#endif - } + s_sharedFrames[frame->theme].remove(key); + delete frame; } + //same thing for maskFrame + if (maskFrame && maskFrame->removeRefs(q)) { + const QString key = cacheId(maskFrame, maskFrame->prefix); + s_sharedFrames[maskFrame->theme].remove(key); + delete maskFrame; + } + + #ifdef DEBUG_FRAMESVG_CACHE QHashIterator it2(s_sharedFrames[theme()->d]); int shares = 0; @@ -653,12 +482,12 @@ FrameSvgPrivate::~FrameSvgPrivate() #endif #endif - frames.clear(); + frame = nullptr; + maskFrame = nullptr; } QPixmap FrameSvgPrivate::alphaMask() { - FrameData *frame = frames[prefix]; QString maskPrefix; if (q->hasElement(QLatin1String("mask-") % prefix % QLatin1String("center"))) { @@ -675,38 +504,38 @@ QPixmap FrameSvgPrivate::alphaMask() return frame->cachedBackground; } else { - QString oldPrefix = prefix; - // We are setting the prefix only temporary to generate // the needed mask image - prefix = maskPrefix % oldPrefix; + const QString maskRequestedPrefix = maskPrefix % requestedPrefix; + maskPrefix = maskPrefix % prefix; - if (!frames.contains(prefix)) { - const QString key = cacheId(frame, prefix); + if (!maskFrame) { + const QString key = cacheId(frame, maskPrefix); // see if we can find a suitable candidate in the shared frames // if successful, ref and insert, otherwise create a new one // and insert that into both the shared frames and our frames. - FrameData *maskFrame = s_sharedFrames[q->theme()->d].value(key); + maskFrame = s_sharedFrames[q->theme()->d].value(key); if (maskFrame) { maskFrame->ref(q); } else { maskFrame = new FrameData(*frame, q); - s_sharedFrames[q->theme()->d].insert(key, maskFrame); + maskFrame->prefix = maskPrefix; + maskFrame->requestedPrefix = maskRequestedPrefix; maskFrame->theme = q->theme()->d; + s_sharedFrames[q->theme()->d].insert(key, maskFrame); } maskFrame->enabledBorders = frame->enabledBorders; - frames.insert(prefix, maskFrame); - updateSizes(); + updateSizes(maskFrame); } - FrameData *maskFrame = frames[prefix]; + // maskFrame = frame; maskFrame->enabledBorders = frame->enabledBorders; if (maskFrame->cachedBackground.isNull() || maskFrame->frameSize != frameSize(frame)) { - const QString oldKey = cacheId(maskFrame, prefix); + const QString oldKey = cacheId(maskFrame, maskPrefix); maskFrame->frameSize = frameSize(frame).toSize(); - const QString newKey = cacheId(maskFrame, prefix); + const QString newKey = cacheId(maskFrame, maskPrefix); if (s_sharedFrames[q->theme()->d].contains(oldKey)) { s_sharedFrames[q->theme()->d].remove(oldKey); s_sharedFrames[q->theme()->d].insert(newKey, maskFrame); @@ -715,27 +544,27 @@ QPixmap FrameSvgPrivate::alphaMask() maskFrame->cachedBackground = QPixmap(); generateBackground(maskFrame); + if (maskFrame->cachedBackground.isNull()) { return QPixmap(); } } - prefix = oldPrefix; return maskFrame->cachedBackground; } } void FrameSvgPrivate::generateBackground(FrameData *frame) { - if (!frame->cachedBackground.isNull() || !q->hasElementPrefix(q->prefix())) { + if (!frame->cachedBackground.isNull() || !q->hasElementPrefix(frame->requestedPrefix)) { return; } - const QString id = cacheId(frame, prefix); + const QString id = cacheId(frame, frame->prefix); bool frameCached = !frame->cachedBackground.isNull(); bool overlayCached = false; - const bool overlayAvailable = !prefix.startsWith(QLatin1String("mask-")) && q->hasElement(prefix % QLatin1String("overlay")); + const bool overlayAvailable = !frame->prefix.startsWith(QLatin1String("mask-")) && q->hasElement(frame->prefix % QLatin1String("overlay")); QPixmap overlay; if (q->isUsingRenderingCache()) { frameCached = q->theme()->findInCache(id, frame->cachedBackground) && !frame->cachedBackground.isNull(); @@ -753,20 +582,20 @@ void FrameSvgPrivate::generateBackground(FrameData *frame) QSize overlaySize; QPoint actualOverlayPos = QPoint(0, 0); if (overlayAvailable && !overlayCached) { - overlaySize = q->elementSize(prefix % QLatin1String("overlay")); + overlaySize = q->elementSize(frame->prefix % QLatin1String("overlay")); - if (q->hasElement(prefix % QLatin1String("hint-overlay-pos-right"))) { + if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-pos-right"))) { actualOverlayPos.setX(frame->frameSize.width() - overlaySize.width()); - } else if (q->hasElement(prefix % QLatin1String("hint-overlay-pos-bottom"))) { + } else if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-pos-bottom"))) { actualOverlayPos.setY(frame->frameSize.height() - overlaySize.height()); //Stretched or Tiled? - } else if (q->hasElement(prefix % QLatin1String("hint-overlay-stretch"))) { + } else if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-stretch"))) { overlaySize = frameSize(frame).toSize(); } else { - if (q->hasElement(prefix % QLatin1String("hint-overlay-tile-horizontal"))) { + if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-tile-horizontal"))) { overlaySize.setWidth(frameSize(frame).width()); } - if (q->hasElement(prefix % QLatin1String("hint-overlay-tile-vertical"))) { + if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-tile-vertical"))) { overlaySize.setHeight(frameSize(frame).height()); } } @@ -775,23 +604,23 @@ void FrameSvgPrivate::generateBackground(FrameData *frame) QPainter overlayPainter(&overlay); overlayPainter.setCompositionMode(QPainter::CompositionMode_SourceIn); //Tiling? - if (q->hasElement(prefix % QLatin1String("hint-overlay-tile-horizontal")) || - q->hasElement(prefix % QLatin1String("hint-overlay-tile-vertical"))) { + if (q->hasElement(frame->prefix % QLatin1String("hint-overlay-tile-horizontal")) || + q->hasElement(frame->prefix % QLatin1String("hint-overlay-tile-vertical"))) { QSize s = q->size(); - q->resize(q->elementSize(prefix % QLatin1String("overlay"))); + q->resize(q->elementSize(frame->prefix % QLatin1String("overlay"))); - overlayPainter.drawTiledPixmap(QRect(QPoint(0, 0), overlaySize), q->pixmap(prefix % QLatin1String("overlay"))); + overlayPainter.drawTiledPixmap(QRect(QPoint(0, 0), overlaySize), q->pixmap(frame->prefix % QLatin1String("overlay"))); q->resize(s); } else { - q->paint(&overlayPainter, QRect(actualOverlayPos, overlaySize), prefix % QLatin1String("overlay")); + q->paint(&overlayPainter, QRect(actualOverlayPos, overlaySize), frame->prefix % QLatin1String("overlay")); } overlayPainter.end(); } if (!frameCached) { - cacheFrame(prefix, frame->cachedBackground, overlayCached ? overlay : QPixmap()); + cacheFrame(frame->prefix, frame->cachedBackground, overlayCached ? overlay : QPixmap()); } if (!overlay.isNull()) { @@ -832,11 +661,11 @@ void FrameSvgPrivate::generateFrameBackground(FrameData *frame) paintCorner(p, frame, FrameSvg::RightBorder|FrameSvg::BottomBorder, contentRect); // Sides - const int leftHeight = q->elementSize(prefix % QLatin1String("left")).height(); + const int leftHeight = q->elementSize(frame->prefix % QLatin1String("left")).height(); paintBorder(p, frame, FrameSvg::LeftBorder, QSize(frame->leftWidth, leftHeight) * q->devicePixelRatio(), contentRect); paintBorder(p, frame, FrameSvg::RightBorder, QSize(frame->rightWidth, leftHeight) * q->devicePixelRatio(), contentRect); - const int topWidth = q->elementSize(prefix % QLatin1String("top")).width(); + const int topWidth = q->elementSize(frame->prefix % QLatin1String("top")).width(); paintBorder(p, frame, FrameSvg::TopBorder, QSize(topWidth, frame->topHeight) * q->devicePixelRatio(), contentRect); paintBorder(p, frame, FrameSvg::BottomBorder, QSize(topWidth, frame->bottomHeight) * q->devicePixelRatio(), contentRect); p.end(); @@ -849,21 +678,89 @@ QRect FrameSvgPrivate::contentGeometry(FrameData* frame, const QSize& size) cons const QSize contentSize(size.width() - frame->leftWidth * q->devicePixelRatio() - frame->rightWidth * q->devicePixelRatio(), size.height() - frame->topHeight * q->devicePixelRatio() - frame->bottomHeight * q->devicePixelRatio()); QRect contentRect(QPoint(0,0), contentSize); - if (frame->enabledBorders & FrameSvg::LeftBorder && q->hasElement(prefix % QLatin1String("left"))) { + if (frame->enabledBorders & FrameSvg::LeftBorder && q->hasElement(frame->prefix % QLatin1String("left"))) { contentRect.translate(frame->leftWidth * q->devicePixelRatio(), 0); } // Corners - if (frame->enabledBorders & FrameSvg::TopBorder && q->hasElement(prefix % QLatin1String("top"))) { + if (frame->enabledBorders & FrameSvg::TopBorder && q->hasElement(frame->prefix % QLatin1String("top"))) { contentRect.translate(0, frame->topHeight * q->devicePixelRatio()); } return contentRect; } +void FrameSvgPrivate::updateFrameData() +{ + FrameData *fd = frame; + + const QString oldKey = cacheId(fd, fd->prefix); + + const FrameSvg::EnabledBorders oldBorders = fd->enabledBorders; + const QSize currentSize = fd->frameSize; + + fd->enabledBorders = enabledBorders; + fd->frameSize = pendingFrameSize; + + const QString newKey = cacheId(fd, prefix); + + //reset frame to old values + fd->enabledBorders = oldBorders; + fd->frameSize = currentSize; + + //FIXME: something more efficient than string comparison? + if (oldKey == newKey) { + return; + } + + //qCDebug(LOG_PLASMA) << "looking for" << newKey; + FrameData *newFd = FrameSvgPrivate::s_sharedFrames[q->theme()->d].value(newKey); + if (newFd) { + //qCDebug(LOG_PLASMA) << "FOUND IT!" << newFd->refcount; + // we've found a math, so insert that new one and ref it .. + newFd->ref(q); + frame = newFd; + + //.. then deref the old one and if it's no longer used, get rid of it + if (fd->deref(q)) { + //const QString oldKey = cacheId(fd, prefix); + //qCDebug(LOG_PLASMA) << "1. Removing it" << oldKey << fd->refcount; + FrameSvgPrivate::s_sharedFrames[fd->theme].remove(oldKey); + + delete fd; + } + + return; + } + + if (fd->refcount() == 1) { + // we're the only user of it, let's remove it from the shared keys + // we don't want to deref it, however, as we'll still be using it + FrameSvgPrivate::s_sharedFrames[fd->theme].remove(oldKey); + } else { + // others are using it, but we wish to change its size. so deref it, + // then create a copy of it (we're automatically ref'd via the ctor), + // then insert it into our frames. + fd->deref(q); + fd = new FrameData(*fd, q); + } + + frame = fd; + fd->prefix = prefix; + fd->requestedPrefix = requestedPrefix; + //updateSizes(); + fd->enabledBorders = enabledBorders; + fd->frameSize = pendingFrameSize; + + // we know it isn't in s_sharedFrames due to the check above, so insert it now + FrameSvgPrivate::s_sharedFrames[q->theme()->d].insert(newKey, fd); + fd->theme = q->theme()->d; + updateAndSignalSizes(); +} + void FrameSvgPrivate::paintCenter(QPainter& p, FrameData* frame, const QRect& contentRect, const QSize& fullSize) { if (!contentRect.isEmpty()) { - const QString centerElementId = prefix % QLatin1String("center"); + const QString centerElementId = frame->prefix % QLatin1String("center"); if (frame->tileCenter) { QSize centerTileSize = q->elementSize(centerElementId); QPixmap center(centerTileSize); @@ -897,7 +794,7 @@ void FrameSvgPrivate::paintCenter(QPainter& p, FrameData* frame, const QRect& co void FrameSvgPrivate::paintBorder(QPainter& p, FrameData* frame, const FrameSvg::EnabledBorders borders, const QSize& size, const QRect& contentRect) const { - QString side = prefix % FrameSvgHelpers::borderToElementId(borders); + QString side = frame->prefix % FrameSvgHelpers::borderToElementId(borders); if (frame->enabledBorders & borders && q->hasElement(side) && !size.isEmpty()) { if (frame->stretchBorders) { q->paint(&p, FrameSvgHelpers::sectionRect(borders, contentRect, frame->frameSize * q->devicePixelRatio()), side); @@ -916,7 +813,7 @@ void FrameSvgPrivate::paintBorder(QPainter& p, FrameData* frame, const FrameSvg: void FrameSvgPrivate::paintCorner(QPainter& p, FrameData* frame, Plasma::FrameSvg::EnabledBorders border, const QRect& contentRect) const { - QString corner = prefix % FrameSvgHelpers::borderToElementId(border); + QString corner = frame->prefix % FrameSvgHelpers::borderToElementId(border); if (frame->enabledBorders & border && q->hasElement(corner)) { q->paint(&p, FrameSvgHelpers::sectionRect(border, contentRect, frame->frameSize * q->devicePixelRatio()), corner); } @@ -936,8 +833,6 @@ void FrameSvgPrivate::cacheFrame(const QString &prefixToSave, const QPixmap &bac } //insert background - FrameData *frame = frames.value(prefixToSave); - if (!frame) { return; } @@ -954,10 +849,9 @@ void FrameSvgPrivate::cacheFrame(const QString &prefixToSave, const QPixmap &bac } } -void FrameSvgPrivate::updateSizes() const +void FrameSvgPrivate::updateSizes(FrameData *frame) const { //qCDebug(LOG_PLASMA) << "!!!!!!!!!!!!!!!!!!!!!! updating sizes" << prefix; - FrameData *frame = frames[prefix]; Q_ASSERT(frame); QSize s = q->size(); @@ -965,20 +859,20 @@ void FrameSvgPrivate::updateSizes() const frame->cachedBackground = QPixmap(); //This has the same size regardless the border is enabled or not - frame->fixedTopHeight = q->elementSize(prefix % QLatin1String("top")).height(); + frame->fixedTopHeight = q->elementSize(frame->prefix % QLatin1String("top")).height(); - if (q->hasElement(prefix % QLatin1String("hint-top-margin"))) { - frame->fixedTopMargin = q->elementSize(prefix % QLatin1String("hint-top-margin")).height(); + if (q->hasElement(frame->prefix % QLatin1String("hint-top-margin"))) { + frame->fixedTopMargin = q->elementSize(frame->prefix % QLatin1String("hint-top-margin")).height(); } else { frame->fixedTopMargin = frame->fixedTopHeight; } //The same, but its size depends from the margin being enabled if (frame->enabledBorders & FrameSvg::TopBorder) { - frame->topHeight = q->elementSize(prefix % QLatin1String("top")).height(); + frame->topHeight = q->elementSize(frame->prefix % QLatin1String("top")).height(); - if (q->hasElement(prefix % QLatin1String("hint-top-margin"))) { - frame->topMargin = q->elementSize(prefix % QLatin1String("hint-top-margin")).height(); + if (q->hasElement(frame->prefix % QLatin1String("hint-top-margin"))) { + frame->topMargin = q->elementSize(frame->prefix % QLatin1String("hint-top-margin")).height(); } else { frame->topMargin = frame->topHeight; } @@ -986,19 +880,19 @@ void FrameSvgPrivate::updateSizes() const frame->topMargin = frame->topHeight = 0; } - frame->fixedLeftWidth = q->elementSize(prefix % QLatin1String("left")).width(); + frame->fixedLeftWidth = q->elementSize(frame->prefix % QLatin1String("left")).width(); - if (q->hasElement(prefix % QLatin1String("hint-left-margin"))) { - frame->fixedLeftMargin = q->elementSize(prefix % QLatin1String("hint-left-margin")).width(); + if (q->hasElement(frame->prefix % QLatin1String("hint-left-margin"))) { + frame->fixedLeftMargin = q->elementSize(frame->prefix % QLatin1String("hint-left-margin")).width(); } else { frame->fixedLeftMargin = frame->fixedLeftWidth; } if (frame->enabledBorders & FrameSvg::LeftBorder) { - frame->leftWidth = q->elementSize(prefix % QLatin1String("left")).width(); + frame->leftWidth = q->elementSize(frame->prefix % QLatin1String("left")).width(); - if (q->hasElement(prefix % QLatin1String("hint-left-margin"))) { - frame->leftMargin = q->elementSize(prefix % QLatin1String("hint-left-margin")).width(); + if (q->hasElement(frame->prefix % QLatin1String("hint-left-margin"))) { + frame->leftMargin = q->elementSize(frame->prefix % QLatin1String("hint-left-margin")).width(); } else { frame->leftMargin = frame->leftWidth; } @@ -1006,19 +900,19 @@ void FrameSvgPrivate::updateSizes() const frame->leftMargin = frame->leftWidth = 0; } - frame->fixedRightWidth = q->elementSize(prefix % QLatin1String("right")).width(); + frame->fixedRightWidth = q->elementSize(frame->prefix % QLatin1String("right")).width(); - if (q->hasElement(prefix % QLatin1String("hint-right-margin"))) { - frame->fixedRightMargin = q->elementSize(prefix % QLatin1String("hint-right-margin")).width(); + if (q->hasElement(frame->prefix % QLatin1String("hint-right-margin"))) { + frame->fixedRightMargin = q->elementSize(frame->prefix % QLatin1String("hint-right-margin")).width(); } else { frame->fixedRightMargin = frame->fixedRightWidth; } if (frame->enabledBorders & FrameSvg::RightBorder) { - frame->rightWidth = q->elementSize(prefix % QLatin1String("right")).width(); + frame->rightWidth = q->elementSize(frame->prefix % QLatin1String("right")).width(); - if (q->hasElement(prefix % QLatin1String("hint-right-margin"))) { - frame->rightMargin = q->elementSize(prefix % QLatin1String("hint-right-margin")).width(); + if (q->hasElement(frame->prefix % QLatin1String("hint-right-margin"))) { + frame->rightMargin = q->elementSize(frame->prefix % QLatin1String("hint-right-margin")).width(); } else { frame->rightMargin = frame->rightWidth; } @@ -1026,19 +920,19 @@ void FrameSvgPrivate::updateSizes() const frame->rightMargin = frame->rightWidth = 0; } - frame->fixedBottomHeight = q->elementSize(prefix % QLatin1String("bottom")).height(); + frame->fixedBottomHeight = q->elementSize(frame->prefix % QLatin1String("bottom")).height(); - if (q->hasElement(prefix % QLatin1String("hint-bottom-margin"))) { - frame->fixedBottomMargin = q->elementSize(prefix % QLatin1String("hint-bottom-margin")).height(); + if (q->hasElement(frame->prefix % QLatin1String("hint-bottom-margin"))) { + frame->fixedBottomMargin = q->elementSize(frame->prefix % QLatin1String("hint-bottom-margin")).height(); } else { frame->fixedBottomMargin = frame->fixedBottomHeight; } if (frame->enabledBorders & FrameSvg::BottomBorder) { - frame->bottomHeight = q->elementSize(prefix % QLatin1String("bottom")).height(); + frame->bottomHeight = q->elementSize(frame->prefix % QLatin1String("bottom")).height(); - if (q->hasElement(prefix % QLatin1String("hint-bottom-margin"))) { - frame->bottomMargin = q->elementSize(prefix % QLatin1String("hint-bottom-margin")).height(); + if (q->hasElement(frame->prefix % QLatin1String("hint-bottom-margin"))) { + frame->bottomMargin = q->elementSize(frame->prefix % QLatin1String("hint-bottom-margin")).height(); } else { frame->bottomMargin = frame->bottomHeight; } @@ -1046,14 +940,14 @@ void FrameSvgPrivate::updateSizes() const frame->bottomMargin = frame->bottomHeight = 0; } - frame->composeOverBorder = (q->hasElement(prefix % QLatin1String("hint-compose-over-border")) && - q->hasElement(QLatin1String("mask-") % prefix % QLatin1String("center"))); + frame->composeOverBorder = (q->hasElement(frame->prefix % QLatin1String("hint-compose-over-border")) && + q->hasElement(QLatin1String("mask-") % frame->prefix % QLatin1String("center"))); //since it's rectangular, topWidth and bottomWidth must be the same - //the ones that don't have a prefix is for retrocompatibility - frame->tileCenter = (q->hasElement(QStringLiteral("hint-tile-center")) || q->hasElement(prefix % QLatin1String("hint-tile-center"))); - frame->noBorderPadding = (q->hasElement(QStringLiteral("hint-no-border-padding")) || q->hasElement(prefix % QLatin1String("hint-no-border-padding"))); - frame->stretchBorders = (q->hasElement(QStringLiteral("hint-stretch-borders")) || q->hasElement(prefix % QLatin1String("hint-stretch-borders"))); + //the ones that don't have a frame->prefix is for retrocompatibility + frame->tileCenter = (q->hasElement(QStringLiteral("hint-tile-center")) || q->hasElement(frame->prefix % QLatin1String("hint-tile-center"))); + frame->noBorderPadding = (q->hasElement(QStringLiteral("hint-no-border-padding")) || q->hasElement(frame->prefix % QLatin1String("hint-no-border-padding"))); + frame->stretchBorders = (q->hasElement(QStringLiteral("hint-stretch-borders")) || q->hasElement(frame->prefix % QLatin1String("hint-stretch-borders"))); q->resize(s); } @@ -1061,19 +955,19 @@ void FrameSvgPrivate::updateNeeded() { q->setElementPrefix(requestedPrefix); q->clearCache(); - updateSizes(); + updateSizes(frame); } void FrameSvgPrivate::updateAndSignalSizes() { - updateSizes(); + updateSizes(frame); emit q->repaintNeeded(); } QSizeF FrameSvgPrivate::frameSize(FrameData *frame) const { if (!frame->frameSize.isValid()) { - updateSizes(); + updateSizes(frame); frame->frameSize = q->size(); } @@ -1118,6 +1012,20 @@ QString FrameSvg::actualPrefix() const return d->prefix; } +bool FrameSvg::isRepaintBlocked() const +{ + return d->repaintBlocked; +} + +void FrameSvg::setRepaintBlocked(bool blocked) +{ + d->repaintBlocked = blocked; + + if (!blocked) { + d->updateFrameData(); + } +} + } // Plasma namespace #include "moc_framesvg.cpp" diff --git a/src/plasma/framesvg.h b/src/plasma/framesvg.h index 139482acb..4eda0dc63 100644 --- a/src/plasma/framesvg.h +++ b/src/plasma/framesvg.h @@ -292,11 +292,28 @@ public: */ QString actualPrefix() const; + /** + * @returns true if we are in a transaction of many changes at once + * and we don't want to rebuild the generated graphics for each change yet + * @since 5.31 + */ + bool isRepaintBlocked() const; + + /** + * If we will do several changes at once in the frame properties, + * such as prefix, enabled borders and size, in order to not regenerate + * the graphics for each change, set this property to true, and set + * it to false again after applying all the changes needed. + * Note that any change will not be visible in the painted frame while this property is set to true. + * @since 5.31 + */ + void setRepaintBlocked(bool blocked); + private: FrameSvgPrivate *const d; friend class FrameData; - Q_PRIVATE_SLOT(d, void updateSizes()) + //Q_PRIVATE_SLOT(d, void updateSizes()) Q_PRIVATE_SLOT(d, void updateNeeded()) }; diff --git a/src/plasma/private/framesvg_p.h b/src/plasma/private/framesvg_p.h index e48bc56fa..c7082dc01 100644 --- a/src/plasma/private/framesvg_p.h +++ b/src/plasma/private/framesvg_p.h @@ -88,6 +88,7 @@ public: int refcount() const; QString prefix; + QString requestedPrefix; FrameSvg::EnabledBorders enabledBorders; QPixmap cachedBackground; QCache cachedMasks; @@ -137,8 +138,12 @@ class FrameSvgPrivate public: FrameSvgPrivate(FrameSvg *psvg) : q(psvg), + overlayPos(0, 0), + frame(nullptr), + maskFrame(nullptr), + enabledBorders(FrameSvg::AllBorders), cacheAll(false), - overlayPos(0, 0) + repaintBlocked(false) { } @@ -150,7 +155,7 @@ public: void generateFrameBackground(FrameData *frame); QString cacheId(FrameData *frame, const QString &prefixToUse) const; void cacheFrame(const QString &prefixToSave, const QPixmap &background, const QPixmap &overlay); - void updateSizes() const; + void updateSizes(FrameData *frame) const; void updateNeeded(); void updateAndSignalSizes(); QSizeF frameSize(FrameData *frame) const; @@ -158,6 +163,7 @@ public: void paintCorner(QPainter& p, FrameData* frame, Plasma::FrameSvg::EnabledBorders border, const QRect& output) const; void paintCenter(QPainter& p, FrameData* frame, const QRect& contentRect, const QSize& fullSize); QRect contentGeometry(FrameData* frame, const QSize& size) const; + void updateFrameData(); Types::Location location; QString prefix; @@ -167,12 +173,20 @@ public: FrameSvg *q; - bool cacheAll : 1; QPoint overlayPos; - QHash frames; + FrameData *frame; + FrameData *maskFrame; + + //those can differ from frame->enabledBorders if we are in a transition + FrameSvg::EnabledBorders enabledBorders; + //this can differ from frame->frameSize if we are in a transition + QSize pendingFrameSize; static QHash > s_sharedFrames; + + bool cacheAll : 1; + bool repaintBlocked : 1; }; }