Do not require an exact match for size hinted elements, but search for the smallest size hinted element that is bigger than the display size. Thanks to mommertz, who outlined and mentored this patch, element ids are cached both locally and in the theme, so no additional calls to createRenderer are required.
This was discussed in <http://svn.reviewboard.kde.org/r/5689/>. svn path=/trunk/KDE/kdelibs/; revision=1191192
This commit is contained in:
parent
c251a29564
commit
e049ecb34d
@ -37,11 +37,23 @@ class SharedSvgRenderer : public QSvgRenderer, public QSharedData
|
|||||||
typedef KSharedPtr<SharedSvgRenderer> Ptr;
|
typedef KSharedPtr<SharedSvgRenderer> Ptr;
|
||||||
|
|
||||||
SharedSvgRenderer(QObject *parent = 0);
|
SharedSvgRenderer(QObject *parent = 0);
|
||||||
SharedSvgRenderer(const QString &filename, const QString &styleSheet, QObject *parent = 0);
|
SharedSvgRenderer(
|
||||||
SharedSvgRenderer(const QByteArray &contents, const QString &styleSheet, QObject *parent = 0);
|
const QString &filename,
|
||||||
|
const QString &styleSheet,
|
||||||
|
QHash<QString, QSize> &elementsWithSizeHints,
|
||||||
|
QObject *parent = 0);
|
||||||
|
|
||||||
|
SharedSvgRenderer(
|
||||||
|
const QByteArray &contents,
|
||||||
|
const QString &styleSheet,
|
||||||
|
QHash<QString, QSize> &elementsWithSizeHints,
|
||||||
|
QObject *parent = 0);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool load(const QByteArray &contents, const QString &styleSheet);
|
bool load(
|
||||||
|
const QByteArray &contents,
|
||||||
|
const QString &styleSheet,
|
||||||
|
QHash<QString, QSize> &elementsWithSizeHints);
|
||||||
};
|
};
|
||||||
|
|
||||||
class SvgPrivate
|
class SvgPrivate
|
||||||
@ -86,6 +98,7 @@ public:
|
|||||||
Svg *q;
|
Svg *q;
|
||||||
QWeakPointer<Theme> theme;
|
QWeakPointer<Theme> theme;
|
||||||
QHash<QString, QRectF> localRectCache;
|
QHash<QString, QRectF> localRectCache;
|
||||||
|
QHash<QString, QSize> elementsWithSizeHints;
|
||||||
SharedSvgRenderer::Ptr renderer;
|
SharedSvgRenderer::Ptr renderer;
|
||||||
QString themePath;
|
QString themePath;
|
||||||
QString path;
|
QString path;
|
||||||
|
131
svg.cpp
131
svg.cpp
@ -49,7 +49,11 @@ SharedSvgRenderer::SharedSvgRenderer(QObject *parent)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
SharedSvgRenderer::SharedSvgRenderer(const QString &filename, const QString &styleSheet, QObject *parent)
|
SharedSvgRenderer::SharedSvgRenderer(
|
||||||
|
const QString &filename,
|
||||||
|
const QString &styleSheet,
|
||||||
|
QHash<QString, QSize> &elementsWithSizeHints,
|
||||||
|
QObject *parent)
|
||||||
: QSvgRenderer(parent)
|
: QSvgRenderer(parent)
|
||||||
{
|
{
|
||||||
QIODevice *file = KFilterDev::deviceForFile(filename, "application/x-gzip");
|
QIODevice *file = KFilterDev::deviceForFile(filename, "application/x-gzip");
|
||||||
@ -57,25 +61,53 @@ SharedSvgRenderer::SharedSvgRenderer(const QString &filename, const QString &sty
|
|||||||
delete file;
|
delete file;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
load(file->readAll(), styleSheet);
|
load(file->readAll(), styleSheet, elementsWithSizeHints);
|
||||||
delete file;
|
delete file;
|
||||||
}
|
}
|
||||||
|
|
||||||
SharedSvgRenderer::SharedSvgRenderer(const QByteArray &contents, const QString &styleSheet, QObject *parent)
|
SharedSvgRenderer::SharedSvgRenderer(
|
||||||
|
const QByteArray &contents,
|
||||||
|
const QString &styleSheet,
|
||||||
|
QHash<QString, QSize> &elementsWithSizeHints,
|
||||||
|
QObject *parent)
|
||||||
: QSvgRenderer(parent)
|
: QSvgRenderer(parent)
|
||||||
{
|
{
|
||||||
load(contents, styleSheet);
|
load(contents, styleSheet, elementsWithSizeHints);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SharedSvgRenderer::load(const QByteArray &contents, const QString &styleSheet)
|
bool SharedSvgRenderer::load(
|
||||||
|
const QByteArray &contents,
|
||||||
|
const QString &styleSheet,
|
||||||
|
QHash<QString, QSize> &elementsWithSizeHints)
|
||||||
{
|
{
|
||||||
|
{ // Search the SVG to find and store all ids that contain size hints.
|
||||||
|
const QString contentsAsString(QString::fromLatin1(contents));
|
||||||
|
QRegExp idExpr("id\\s*=\\s*(['\"])(\\d+)-(\\d+)-(.+)\\1");
|
||||||
|
idExpr.setMinimal(true);
|
||||||
|
|
||||||
|
int pos = 0;
|
||||||
|
while ((pos = idExpr.indexIn(contentsAsString, pos)) != -1) {
|
||||||
|
QString elementId = idExpr.cap(4);
|
||||||
|
QSize sizeHint(idExpr.cap(2).toInt(), idExpr.cap(3).toInt());
|
||||||
|
|
||||||
|
if (sizeHint.isValid()) {
|
||||||
|
elementsWithSizeHints.insertMulti(elementId, sizeHint);
|
||||||
|
}
|
||||||
|
|
||||||
|
pos += idExpr.matchedLength();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply the style sheet.
|
||||||
if (styleSheet.isEmpty() || ! contents.contains("current-color-scheme")) {
|
if (styleSheet.isEmpty() || ! contents.contains("current-color-scheme")) {
|
||||||
return QSvgRenderer::load(contents);
|
return QSvgRenderer::load(contents);
|
||||||
}
|
}
|
||||||
|
|
||||||
QDomDocument svg;
|
QDomDocument svg;
|
||||||
if (!svg.setContent(contents)) {
|
if (!svg.setContent(contents)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QDomNode defs = svg.elementsByTagName("defs").item(0);
|
QDomNode defs = svg.elementsByTagName("defs").item(0);
|
||||||
|
|
||||||
for (QDomElement style = defs.firstChildElement("style"); !style.isNull();
|
for (QDomElement style = defs.firstChildElement("style"); !style.isNull();
|
||||||
@ -96,6 +128,8 @@ bool SharedSvgRenderer::load(const QByteArray &contents, const QString &styleShe
|
|||||||
|
|
||||||
#define QLSEP QLatin1Char('_')
|
#define QLSEP QLatin1Char('_')
|
||||||
#define CACHE_ID_WITH_SIZE(size, id) QString::number(int(size.width())) % QString::number(int(size.height())) % QLSEP % id
|
#define CACHE_ID_WITH_SIZE(size, id) QString::number(int(size.width())) % QString::number(int(size.height())) % QLSEP % id
|
||||||
|
#define CACHE_ID_NATURAL_SIZE(id) QLatin1Literal("Natural") % QLSEP % id
|
||||||
|
|
||||||
SvgPrivate::SvgPrivate(Svg *svg)
|
SvgPrivate::SvgPrivate(Svg *svg)
|
||||||
: q(svg),
|
: q(svg),
|
||||||
renderer(0),
|
renderer(0),
|
||||||
@ -121,7 +155,7 @@ QString SvgPrivate::cacheId(const QString &elementId)
|
|||||||
if (size.isValid() && size != naturalSize) {
|
if (size.isValid() && size != naturalSize) {
|
||||||
return CACHE_ID_WITH_SIZE(size, elementId);
|
return CACHE_ID_WITH_SIZE(size, elementId);
|
||||||
} else {
|
} else {
|
||||||
return QLatin1Literal("Natural") % QLSEP % elementId;
|
return CACHE_ID_NATURAL_SIZE(elementId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,6 +191,7 @@ bool SvgPrivate::setImagePath(const QString &imagePath)
|
|||||||
path.clear();
|
path.clear();
|
||||||
themePath.clear();
|
themePath.clear();
|
||||||
localRectCache.clear();
|
localRectCache.clear();
|
||||||
|
elementsWithSizeHints.clear();
|
||||||
|
|
||||||
if (themed) {
|
if (themed) {
|
||||||
themePath = imagePath;
|
themePath = imagePath;
|
||||||
@ -216,7 +251,60 @@ Theme *SvgPrivate::actualTheme()
|
|||||||
QPixmap SvgPrivate::findInCache(const QString &elementId, const QSizeF &s)
|
QPixmap SvgPrivate::findInCache(const QString &elementId, const QSizeF &s)
|
||||||
{
|
{
|
||||||
QSize size;
|
QSize size;
|
||||||
QString actualElementId(QString::number(int(s.width())) % "-" % QString::number(int(s.height())) % "-" % elementId);
|
QString actualElementId;
|
||||||
|
|
||||||
|
if (elementsWithSizeHints.isEmpty()) {
|
||||||
|
// Fetch all size hinted element ids from the theme's rect cache
|
||||||
|
// and store them locally.
|
||||||
|
QRegExp sizeHintedKeyExpr(CACHE_ID_NATURAL_SIZE("(\\d+)-(\\d+)-(.+)"));
|
||||||
|
|
||||||
|
Q_FOREACH(const QString &key, actualTheme()->listCachedRectKeys(path)) {
|
||||||
|
|
||||||
|
if (sizeHintedKeyExpr.exactMatch(key)) {
|
||||||
|
QString baseElementId = sizeHintedKeyExpr.cap(3);
|
||||||
|
QSize sizeHint(
|
||||||
|
sizeHintedKeyExpr.cap(1).toInt(),
|
||||||
|
sizeHintedKeyExpr.cap(2).toInt());
|
||||||
|
|
||||||
|
if (sizeHint.isValid()) {
|
||||||
|
elementsWithSizeHints.insertMulti(baseElementId, sizeHint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elementsWithSizeHints.isEmpty()) {
|
||||||
|
// Make sure we won't query the theme unnecessarily.
|
||||||
|
elementsWithSizeHints.insert(QString(), QSize());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look at the size hinted elements and try to find the smallest one with an
|
||||||
|
// identical aspect ratio.
|
||||||
|
if (s.isValid() && !elementId.isEmpty()) {
|
||||||
|
|
||||||
|
QList<QSize> elementSizeHints = elementsWithSizeHints.values(elementId);
|
||||||
|
|
||||||
|
if (!elementSizeHints.isEmpty()) {
|
||||||
|
QSize bestFit(-1, -1);
|
||||||
|
|
||||||
|
Q_FOREACH(const QSize &hint, elementSizeHints) {
|
||||||
|
|
||||||
|
if (hint.width() >= s.width() && hint.height() >= s.height() &&
|
||||||
|
(!bestFit.isValid() ||
|
||||||
|
bestFit.width() * bestFit.height() >
|
||||||
|
hint.width() * hint.height())) {
|
||||||
|
|
||||||
|
bestFit = hint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bestFit.isValid()) {
|
||||||
|
actualElementId =
|
||||||
|
QString::number(bestFit.width()) % "-" %
|
||||||
|
QString::number(bestFit.height()) % "-" % elementId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (elementId.isEmpty() || !q->hasElement(actualElementId)) {
|
if (elementId.isEmpty() || !q->hasElement(actualElementId)) {
|
||||||
actualElementId = elementId;
|
actualElementId = elementId;
|
||||||
@ -329,7 +417,33 @@ void SvgPrivate::createRenderer()
|
|||||||
if (path.isEmpty()) {
|
if (path.isEmpty()) {
|
||||||
renderer = new SharedSvgRenderer();
|
renderer = new SharedSvgRenderer();
|
||||||
} else {
|
} else {
|
||||||
renderer = new SharedSvgRenderer(path, actualTheme()->styleSheet("SVG"));
|
renderer = new SharedSvgRenderer(
|
||||||
|
path, actualTheme()->styleSheet("SVG"), elementsWithSizeHints);
|
||||||
|
|
||||||
|
// Add size hinted elements to the theme's rect cache.
|
||||||
|
QHashIterator<QString, QSize> i(elementsWithSizeHints);
|
||||||
|
|
||||||
|
while (i.hasNext()) {
|
||||||
|
i.next();
|
||||||
|
const QString &baseElementId = i.key();
|
||||||
|
const QSize &hintedSize = i.value();
|
||||||
|
QString fullElementId =
|
||||||
|
QString::number(hintedSize.width()) % '-' %
|
||||||
|
QString::number(hintedSize.height()) % '-' %
|
||||||
|
baseElementId;
|
||||||
|
|
||||||
|
QString cacheId = CACHE_ID_NATURAL_SIZE(fullElementId);
|
||||||
|
QRectF elementRect = renderer->boundsOnElement(fullElementId);
|
||||||
|
if (elementRect.isValid()) {
|
||||||
|
actualTheme()->insertIntoRectsCache(
|
||||||
|
path, cacheId, elementRect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elementsWithSizeHints.isEmpty()) {
|
||||||
|
// Make sure we won't query the theme unnecessarily.
|
||||||
|
elementsWithSizeHints.insert(QString(), QSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
s_renderers[styleCrc + path] = renderer;
|
s_renderers[styleCrc + path] = renderer;
|
||||||
@ -354,6 +468,7 @@ void SvgPrivate::eraseRenderer()
|
|||||||
renderer = 0;
|
renderer = 0;
|
||||||
styleCrc = 0;
|
styleCrc = 0;
|
||||||
localRectCache.clear();
|
localRectCache.clear();
|
||||||
|
elementsWithSizeHints.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
QRectF SvgPrivate::elementRect(const QString &elementId)
|
QRectF SvgPrivate::elementRect(const QString &elementId)
|
||||||
|
19
theme.cpp
19
theme.cpp
@ -22,6 +22,7 @@
|
|||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
#include <QMutableListIterator>
|
||||||
#include <QPair>
|
#include <QPair>
|
||||||
#include <QStringBuilder>
|
#include <QStringBuilder>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
@ -960,6 +961,24 @@ bool Theme::findInRectsCache(const QString &image, const QString &element, QRect
|
|||||||
return invalid;
|
return invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QStringList Theme::listCachedRectKeys(const QString &image) const
|
||||||
|
{
|
||||||
|
KConfigGroup imageGroup(d->svgElementsCache, image);
|
||||||
|
QStringList keys = imageGroup.keyList();
|
||||||
|
|
||||||
|
QMutableListIterator<QString> i(keys);
|
||||||
|
while (i.hasNext()) {
|
||||||
|
const QString &key = i.next();
|
||||||
|
if (key.endsWith("Size")) {
|
||||||
|
// The actual cache id used from outside doesn't end on "Size".
|
||||||
|
i.setValue(key.resize(key.size() - 4));
|
||||||
|
} else {
|
||||||
|
i.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return keys;
|
||||||
|
}
|
||||||
|
|
||||||
void Theme::insertIntoRectsCache(const QString& image, const QString &element, const QRectF &rect)
|
void Theme::insertIntoRectsCache(const QString& image, const QString &element, const QRectF &rect)
|
||||||
{
|
{
|
||||||
if (!d->pixmapCache) {
|
if (!d->pixmapCache) {
|
||||||
|
13
theme.h
13
theme.h
@ -330,12 +330,23 @@ class PLASMA_EXPORT Theme : public QObject
|
|||||||
*
|
*
|
||||||
* @arg image path of the image we want to check
|
* @arg image path of the image we want to check
|
||||||
* @arg element sub element we want to retrieve
|
* @arg element sub element we want to retrieve
|
||||||
* @arg rect output parameter of the element rect found in cache
|
* @arg rect output parameter of the element rect found in cache
|
||||||
* if not found or if we are sure it doesn't exist it will be QRect()
|
* if not found or if we are sure it doesn't exist it will be QRect()
|
||||||
* @return true if the element was found in cache or if we are sure the element doesn't exist
|
* @return true if the element was found in cache or if we are sure the element doesn't exist
|
||||||
**/
|
**/
|
||||||
bool findInRectsCache(const QString &image, const QString &element, QRectF &rect) const;
|
bool findInRectsCache(const QString &image, const QString &element, QRectF &rect) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of all keys of cached rects for the given image.
|
||||||
|
*
|
||||||
|
* @arg image path of the image for which the keys should be returned
|
||||||
|
*
|
||||||
|
* @return a QStringList whose elements are the entry keys in the rects cache
|
||||||
|
*
|
||||||
|
* @since 4.6
|
||||||
|
*/
|
||||||
|
QStringList listCachedRectKeys(const QString &image) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inserts a rectangle of a sub element of an image into a disk cache
|
* Inserts a rectangle of a sub element of an image into a disk cache
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user