diff --git a/src/kiconengine.h b/src/kiconengine.h --- a/src/kiconengine.h +++ b/src/kiconengine.h @@ -84,6 +84,7 @@ private: //TODO KF6: move those into the d-pointer + QPixmap createPixmap(const QSize &size, qreal scale, QIcon::Mode mode, QIcon::State state); QString mIconName; QStringList mOverlays; QPointer mIconLoader; diff --git a/src/kiconengine.cpp b/src/kiconengine.cpp --- a/src/kiconengine.cpp +++ b/src/kiconengine.cpp @@ -75,38 +75,50 @@ painter->drawPixmap(rect, pixmap(rect.size() * dpr, mode, state)); } -QPixmap KIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) +QPixmap KIconEngine::createPixmap(const QSize &size, qreal scale, QIcon::Mode mode, QIcon::State state) { Q_UNUSED(state) + if (scale < 1) { + scale = 1; + } + if (size.isEmpty()) { return QPixmap(); } if (!mIconLoader) { QPixmap pm(size); + pm.setDevicePixelRatio(scale); pm.fill(Qt::transparent); return pm; } + const QSize scaledSize = size / scale; const int kstate = qIconModeToKIconState(mode); - const int iconSize = qMin(size.width(), size.height()); - QPixmap pix = mIconLoader.data()->loadIcon(mIconName, KIconLoader::Desktop, iconSize, kstate, mOverlays); + const int iconSize = qMin(scaledSize.width(), scaledSize.height()); + QPixmap pix = mIconLoader.data()->loadScaledIcon(mIconName, KIconLoader::Desktop, scale, iconSize, kstate, mOverlays); if (pix.size() == size) { return pix; } - QPixmap pix2(size); + QPixmap pix2(size * scale); + pix2.setDevicePixelRatio(scale); pix2.fill(QColor(0, 0, 0, 0)); QPainter painter(&pix2); painter.drawPixmap(QPoint((pix2.width() - pix.width()) / 2, (pix2.height() - pix.height()) / 2), pix); return pix2; } +QPixmap KIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) +{ + return createPixmap(size, 1 /*scale*/, mode, state); +} + QString KIconEngine::iconName() const { if (!mIconLoader || !mIconLoader->hasIcon(mIconName)) { @@ -157,5 +169,12 @@ if (id == QIconEngine::IsNullHook) { *reinterpret_cast(data) = !mIconLoader || !mIconLoader->hasIcon(mIconName); } +#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) + if (id == QIconEngine::ScaledPixmapHook) { + auto *info = reinterpret_cast(data); + info->pixmap = createPixmap(info->size, info->scale, info->mode, info->state); + return; + } +#endif QIconEngine::virtual_hook(id, data); } diff --git a/src/kiconloader.h b/src/kiconloader.h --- a/src/kiconloader.h +++ b/src/kiconloader.h @@ -244,6 +244,44 @@ QString *path_store = nullptr, bool canReturnNull = false) const; + /** + * Loads an icon. It will try very hard to find an icon which is + * suitable. If no exact match is found, a close match is searched. + * If neither an exact nor a close match is found, a null pixmap or + * the "unknown" pixmap is returned, depending on the value of the + * @p canReturnNull parameter. + * + * @param name The name of the icon, without extension. + * @param group The icon group. This will specify the size of and effects to + * be applied to the icon. + * @param scale The scale of the icon group to use. If no icon exists in the + * scaled group, a 1x icon with its size multiplied by the scale will be + * loaded instead. + * @param size If nonzero, this overrides the size specified by @p group. + * See KIconLoader::StdSizes. + * @param state The icon state: @p DefaultState, @p ActiveState or + * @p DisabledState. Depending on the user's preferences, the iconloader + * may apply a visual effect to hint about its state. + * @param overlays a list of emblem icons to overlay, by name + * @see drawOverlays + * @param path_store If not null, the path of the icon is stored here, + * if the icon was found. If the icon was not found @p path_store + * is unaltered even if the "unknown" pixmap was returned. + * @param canReturnNull Can return a null pixmap? If false, the + * "unknown" pixmap is returned when no appropriate icon has been + * found. Note: a null pixmap can still be returned in the + * event of invalid parameters, such as empty names, negative sizes, + * and etc. + * @return the QPixmap. Can be null when not found, depending on + * @p canReturnNull. + * @since 5.48 + */ + // TODO KF6 merge loadIcon() and loadScaledIcon() + QPixmap loadScaledIcon(const QString &name, KIconLoader::Group group, qreal scale, int size = 0, + int state = KIconLoader::DefaultState, const QStringList &overlays = QStringList(), + QString *path_store = nullptr, + bool canReturnNull = false) const; + /** * Loads an icon for a mimetype. * This is basically like loadIcon except that extra desktop themes are loaded if necessary. @@ -314,6 +352,26 @@ QString iconPath(const QString &name, int group_or_size, bool canReturnNull = false) const; + /** + * Returns the path of an icon. + * @param name The name of the icon, without extension. If an absolute + * path is supplied for this parameter, iconPath will return it + * directly. + * @param group_or_size If positive, search icons whose size is + * specified by the icon group @p group_or_size. If negative, search + * icons whose size is - @p group_or_size. + * See KIconLoader::Group and KIconLoader::StdSizes + * @param canReturnNull Can return a null string? If not, a path to the + * "unknown" icon will be returned. + * @param scale The scale of the icon group. + * @return the path of an icon, can be null or the "unknown" icon when + * not found, depending on @p canReturnNull. + * @since 5.48 + */ + // TODO KF6 merge iconPath() with and without "scale" and move that argument after "group_or_size" + QString iconPath(const QString &name, int group_or_size, + bool canReturnNull, qreal scale) const; + /** * Loads an animated icon. * @param name The name of the icon. diff --git a/src/kiconloader.cpp b/src/kiconloader.cpp --- a/src/kiconloader.cpp +++ b/src/kiconloader.cpp @@ -250,15 +250,15 @@ * tries to find an icon with the name. It tries some extension and * match strategies */ - QString findMatchingIcon(const QString &name, int size) const; + QString findMatchingIcon(const QString &name, int size, qreal scale) const; /** * @internal * tries to find an icon with the name. * This is one layer above findMatchingIcon -- it also implements generic fallbacks * such as generic icons for mimetypes. */ - QString findMatchingIconWithGenericFallbacks(const QString &name, int size) const; + QString findMatchingIconWithGenericFallbacks(const QString &name, int size, qreal scale) const; /** * @internal @@ -298,7 +298,7 @@ * @internal * return the path for the unknown icon in that size */ - QString unknownIconPath(int size) const; + QString unknownIconPath(int size, qreal scale) const; /** * Checks if name ends in one of the supported icon formats (i.e. .png) @@ -320,7 +320,7 @@ * icon metadata. Ensure the metadata is normalized first. */ QString makeCacheKey(const QString &name, KIconLoader::Group group, const QStringList &overlays, - int size, int state) const; + int size, qreal scale, int state) const; /** * @internal @@ -337,7 +337,7 @@ * @p size is only used for scalable images, but if non-zero non-scalable * images will be resized anyways. */ - QImage createIconImage(const QString &path, int size = 0, KIconLoader::States state = KIconLoader::DefaultState); + QImage createIconImage(const QString &path, int size = 0, qreal scale = 1.0, KIconLoader::States state = KIconLoader::DefaultState); /** * @internal @@ -857,7 +857,7 @@ } QString KIconLoaderPrivate::makeCacheKey(const QString &name, KIconLoader::Group group, - const QStringList &overlays, int size, int state) const + const QStringList &overlays, int size, qreal scale, int state) const { // The KSharedDataCache is shared so add some namespacing. The following code // uses QStringBuilder (new in Qt 4.6) @@ -868,6 +868,8 @@ % name % QLatin1Char('_') % QString::number(size) + % QLatin1Char('@') + % QString::number(scale, 'f', 1) % QLatin1Char('_') % overlays.join(QStringLiteral("_")) % (group >= 0 ? mpEffect.fingerprint(group, state) @@ -927,7 +929,7 @@ return processedContents; } -QImage KIconLoaderPrivate::createIconImage(const QString &path, int size, KIconLoader::States state) +QImage KIconLoaderPrivate::createIconImage(const QString &path, int size, qreal scale, KIconLoader::States state) { //TODO: metadata in the theme to make it do this only if explicitly supported? QScopedPointer reader; @@ -945,7 +947,7 @@ } if (size != 0) { - reader->setScaledSize(QSize(size, size)); + reader->setScaledSize(QSize(size * scale, size * scale)); } return reader->read(); @@ -1037,21 +1039,21 @@ return false; } -QString KIconLoaderPrivate::findMatchingIconWithGenericFallbacks(const QString &name, int size) const +QString KIconLoaderPrivate::findMatchingIconWithGenericFallbacks(const QString &name, int size, qreal scale) const { - QString path = findMatchingIcon(name, size); + QString path = findMatchingIcon(name, size, scale); if (!path.isEmpty()) { return path; } const QString genericIcon = s_globalData()->genericIconFor(name); if (!genericIcon.isEmpty()) { - path = findMatchingIcon(genericIcon, size); + path = findMatchingIcon(genericIcon, size, scale); } return path; } -QString KIconLoaderPrivate::findMatchingIcon(const QString &name, int size) const +QString KIconLoaderPrivate::findMatchingIcon(const QString &name, int size, qreal scale) const { const_cast(this)->initIconThemes(); @@ -1071,7 +1073,7 @@ // from this method. foreach (KIconThemeNode *themeNode, links) { - const QString path = themeNode->theme->iconPathByName(name, size, KIconLoader::MatchBest); + const QString path = themeNode->theme->iconPathByName(name, size, KIconLoader::MatchBest, scale); if (!path.isEmpty()) { return path; } @@ -1121,20 +1123,20 @@ } //qCDebug(KICONTHEMES) << "Looking up" << currentName; - path = themeNode->theme->iconPathByName(currentName, size, KIconLoader::MatchBest); + path = themeNode->theme->iconPathByName(currentName, size, KIconLoader::MatchBest, scale); if (!path.isEmpty()) { return path; } } } return path; } -inline QString KIconLoaderPrivate::unknownIconPath(int size) const +inline QString KIconLoaderPrivate::unknownIconPath(int size, qreal scale) const { - QString path = findMatchingIcon(QStringLiteral("unknown"), size); + QString path = findMatchingIcon(QStringLiteral("unknown"), size, scale); if (path.isEmpty()) { - qCDebug(KICONTHEMES) << "Warning: could not find \"unknown\" icon for size" << size; + qCDebug(KICONTHEMES) << "Warning: could not find \"unknown\" icon for size" << size << "at scale" << scale; return QString(); } return path; @@ -1162,6 +1164,12 @@ QString KIconLoader::iconPath(const QString &_name, int group_or_size, bool canReturnNull) const +{ + return iconPath(_name, group_or_size, canReturnNull, 1 /*scale*/); +} + +QString KIconLoader::iconPath(const QString &_name, int group_or_size, + bool canReturnNull, qreal scale) const { if (!d->initIconThemes()) { return QString(); @@ -1205,20 +1213,20 @@ if (canReturnNull) { return QString(); } else { - return d->unknownIconPath(size); + return d->unknownIconPath(size, scale); } } - path = d->findMatchingIconWithGenericFallbacks(name, size); + path = d->findMatchingIconWithGenericFallbacks(name, size, scale); if (path.isEmpty()) { // Try "User" group too. path = iconPath(name, KIconLoader::User, true); if (!path.isEmpty() || canReturnNull) { return path; } - return d->unknownIconPath(size); + return d->unknownIconPath(size, scale); } return path; } @@ -1247,9 +1255,18 @@ return pixmap; } + + QPixmap KIconLoader::loadIcon(const QString &_name, KIconLoader::Group group, int size, int state, const QStringList &overlays, QString *path_store, bool canReturnNull) const +{ +return loadScaledIcon(_name, group, 1.0 /*scale*/, size, state, overlays, path_store, canReturnNull); +} + +QPixmap KIconLoader::loadScaledIcon(const QString &_name, KIconLoader::Group group, qreal scale, + int size, int state, const QStringList &overlays, + QString *path_store, bool canReturnNull) const { QString name = _name; bool favIconOverlay = false; @@ -1289,12 +1306,15 @@ d->normalizeIconMetadata(group, size, state); // See if the image is already cached. - QString key = d->makeCacheKey(name, group, overlays, size, state); + QString key = d->makeCacheKey(name, group, overlays, size, scale, state); QPixmap pix; + bool iconWasUnknown = false; QString path; if (d->findCachedPixmapWithPath(key, pix, path)) { + pix.setDevicePixelRatio(scale); + if (path_store) { *path_store = path; } @@ -1323,7 +1343,7 @@ if (absolutePath && !favIconOverlay) { path = name; } else { - path = d->findMatchingIconWithGenericFallbacks(favIconOverlay ? QStringLiteral("text-html") : name, size); + path = d->findMatchingIconWithGenericFallbacks(favIconOverlay ? QStringLiteral("text-html") : name, size, scale); } } @@ -1336,16 +1356,16 @@ // Still can't find it? Use "unknown" if we can't return null. // We keep going in the function so we can ensure this result gets cached. if (path.isEmpty() && !canReturnNull) { - path = d->unknownIconPath(size); + path = d->unknownIconPath(size, scale); iconWasUnknown = true; } QImage img; if (!path.isEmpty()) { - img = d->createIconImage(path, size, static_cast(state)); + img = d->createIconImage(path, size, scale, static_cast(state)); } - if (group >= 0 && group < KIconLoader::LastGroup) { + if (group >= 0) { img = d->mpEffect.apply(img, group, state); } @@ -1484,7 +1504,7 @@ if (size == 0) { size = d->mpGroups[group].size; } - file = d->findMatchingIcon(file, size); + file = d->findMatchingIcon(file, size, 1); // FIXME scale } if (file.isEmpty()) { return lst; diff --git a/src/kicontheme.h b/src/kicontheme.h --- a/src/kicontheme.h +++ b/src/kicontheme.h @@ -162,6 +162,21 @@ */ QString iconPath(const QString &name, int size, KIconLoader::MatchType match) const; + /** + * Lookup an icon in the theme. + * @param name The name of the icon, with extension. + * @param size The desired size of the icon. + * @param match The matching mode. KIconLoader::MatchExact returns an icon + * only if matches exactly. KIconLoader::MatchBest returns the best matching + * icon. + * @param scale The scale of the icon group. + * @return An absolute path to the file of the icon if it's found, QString() otherwise. + * @see KIconLoader::isValid will return true, and false otherwise. + * @since 5.48 + */ + // TODO KF6 merge iconPath() with and without "scale" and move that argument after "size" + QString iconPath(const QString &name, int size, KIconLoader::MatchType match, qreal scale) const; + /** * Lookup an icon in the theme. * @param name The name of the icon, without extension. @@ -176,6 +191,22 @@ */ QString iconPathByName(const QString &name, int size, KIconLoader::MatchType match) const; + /** + * Lookup an icon in the theme. + * @param name The name of the icon, without extension. + * @param size The desired size of the icon. + * @param match The matching mode. KIconLoader::MatchExact returns an icon + * only if matches exactly. KIconLoader::MatchBest returns the best matching + * icon. + * @param scale The scale of the icon group. + * @return An absolute path to the file of the icon if it's found, QString() otherwise. + * @see KIconLoader::isValid will return true, and false otherwise. + * + * @since 5.48 + */ + // TODO KF6 merge iconPathByName() with and without "scale" and move that argument after "size" + QString iconPathByName(const QString &name, int size, KIconLoader::MatchType match, qreal scale) const; + /** * Returns true if the theme has any icons for the given context. */ diff --git a/src/kicontheme.cpp b/src/kicontheme.cpp --- a/src/kicontheme.cpp +++ b/src/kicontheme.cpp @@ -85,7 +85,11 @@ QStringList mInherits; QStringList mExtensions; QVector mDirs; + QVector mScaledDirs; bool followsColorScheme : 1; + + /// Searches the given dirs vector for a matching icon + QString iconPath(const QVector &dirs, const QString &name, int size, qreal scale, KIconLoader::MatchType match) const; }; Q_GLOBAL_STATIC(QString, _theme) Q_GLOBAL_STATIC(QStringList, _theme_list) @@ -121,6 +125,10 @@ { return mSize; } + int scale() const + { + return mScale; + } int minSize() const { return mMinSize; @@ -138,14 +146,107 @@ bool mbValid; KIconLoader::Type mType; KIconLoader::Context mContext; - int mSize, mMinSize, mMaxSize; + int mSize, mScale, mMinSize, mMaxSize; int mThreshold; const QString mBaseDir; const QString mThemeDir; }; +QString KIconTheme::KIconThemePrivate::iconPath(const QVector &dirs, const QString &name, int size, qreal scale, KIconLoader::MatchType match) const +{ + QString path; + QString tempPath; // used to cache icon path if it exists + + int delta = -INT_MAX; // current icon size delta of 'icon' + int dw = INT_MAX; // icon size delta of current directory + + // Rather downsample than upsample + int integerScale = 1; + if (scale > 1.1) { + integerScale = 2; + } else if (scale > 2.1) { + integerScale = 3; + } else if (scale > 3.1) { + integerScale = 4; + } + + // Search the directory that contains the icon which matches best to the requested + // size. If there is no directory which matches exactly to the requested size, the + // following criterias get applied: + // - Take a directory having icons with a minimum difference to the requested size. + // - Prefer directories that allow a downscaling even if the difference to + // the requested size is bigger than a directory where an upscaling is required. + for (KIconThemeDir *dir : dirs) { + if (dir->scale() != integerScale) { + continue; + } + + if (match == KIconLoader::MatchExact) { + if ((dir->type() == KIconLoader::Fixed) && (dir->size() != size)) { + continue; + } + if ((dir->type() == KIconLoader::Scalable) && + ((size < dir->minSize()) || (size > dir->maxSize()))) { + continue; + } + if ((dir->type() == KIconLoader::Threshold) && + (abs(dir->size() - size) > dir->threshold())) { + continue; + } + } else { + // dw < 0 means need to scale up to get an icon of the requested size. + // Upscaling should only be done if no larger icon is available. + if (dir->type() == KIconLoader::Fixed) { + dw = dir->size() - size; + } else if (dir->type() == KIconLoader::Scalable) { + if (size < dir->minSize()) { + dw = dir->minSize() - size; + } else if (size > dir->maxSize()) { + dw = dir->maxSize() - size; + } else { + dw = 0; + } + } else if (dir->type() == KIconLoader::Threshold) { + if (size < dir->size() - dir->threshold()) { + dw = dir->size() - dir->threshold() - size; + } else if (size > dir->size() + dir->threshold()) { + dw = dir->size() + dir->threshold() - size; + } else { + dw = 0; + } + } + // Usually if the delta (= 'dw') of the current directory is + // not smaller than the delta (= 'delta') of the currently best + // matching icon, this candidate can be skipped. But skipping + // the candidate may only be done, if this does not imply + // in an upscaling of the icon (it is OK to use a directory with + // smaller icons that what we've already found, however). + if ((abs(dw) >= abs(delta)) && ((dw < 0) || (delta > 0))) { + continue; + } + } + + // cache the result of iconPath() call which checks if file exists + tempPath = dir->iconPath(name); + + if (tempPath.isEmpty()) { + continue; + } + + path = tempPath; + // if we got in MatchExact that far, we find no better + if (match == KIconLoader::MatchExact) { + return path; + } + delta = dw; + if (delta == 0) { + return path; // We won't find a better match anyway + } + } + return path; +} KIconTheme::KIconTheme(const QString &name, const QString &appName, const QString &basePathHint) : d(new KIconThemePrivate) @@ -234,16 +335,21 @@ d->screenshot = cfg.readPathEntry("ScreenShot", QString()); d->mExtensions = cfg.readEntry("KDE-Extensions", QStringList{ QStringLiteral(".png"), QStringLiteral(".svgz"), QStringLiteral(".svg"), QStringLiteral(".xpm") }); - const QStringList dirs = cfg.readPathEntry("Directories", QStringList()); + const QStringList dirs = cfg.readPathEntry("Directories", QStringList()) + + cfg.readPathEntry("ScaledDirectories", QStringList()); for (QStringList::ConstIterator it = dirs.begin(); it != dirs.end(); ++it) { KConfigGroup cg(d->sharedConfig, *it); for (QStringList::ConstIterator itDir = themeDirs.constBegin(); itDir != themeDirs.constEnd(); ++itDir) { const QString currentDir(*itDir + *it + QLatin1Char('/')); if (!addedDirs.contains(currentDir) && QDir(currentDir).exists()) { addedDirs.insert(currentDir); KIconThemeDir *dir = new KIconThemeDir(*itDir, *it, cg); if (dir->isValid()) { - d->mDirs.append(dir); + if (dir->scale() > 1) { + d->mScaledDirs.append(dir); + } else { + d->mDirs.append(dir); + } } else { delete dir; } @@ -270,6 +376,7 @@ KIconTheme::~KIconTheme() { qDeleteAll(d->mDirs); + qDeleteAll(d->mScaledDirs); delete d; } @@ -310,7 +417,7 @@ bool KIconTheme::isValid() const { - return !d->mDirs.isEmpty(); + return !d->mDirs.isEmpty() || !d->mScaledDirs.isEmpty(); } bool KIconTheme::isHidden() const @@ -345,7 +452,7 @@ { // Try to find exact match QStringList result; - foreach (KIconThemeDir* dir, d->mDirs) { + foreach (KIconThemeDir* dir, d->mDirs + d->mScaledDirs) { if ((context != KIconLoader::Any) && (context != dir->context())) { continue; } @@ -402,7 +509,7 @@ // 26 (48-22) and 32 (48-16) will be used, but who knows if someone // will make icon themes with different icon sizes. - foreach (KIconThemeDir *dir, d->mDirs) { + foreach (KIconThemeDir *dir, d->mDirs + d->mScaledDirs) { if ((context != KIconLoader::Any) && (context != dir->context())) { continue; } @@ -420,18 +527,23 @@ bool KIconTheme::hasContext(KIconLoader::Context context) const { - foreach (KIconThemeDir *dir, d->mDirs) { + foreach (KIconThemeDir *dir, d->mDirs + d->mScaledDirs) { if ((context == KIconLoader::Any) || (context == dir->context())) { return true; } } return false; } QString KIconTheme::iconPathByName(const QString &iconName, int size, KIconLoader::MatchType match) const +{ + return iconPathByName(iconName, size, match, 1 /*scale*/); +} + +QString KIconTheme::iconPathByName(const QString &iconName, int size, KIconLoader::MatchType match, qreal scale) const { foreach(const QString ¤t, d->mExtensions) { - const QString path = iconPath(iconName + current, size, match); + const QString path = iconPath(iconName + current, size, match, scale); if (!path.isEmpty()) return path; } @@ -445,79 +557,17 @@ QString KIconTheme::iconPath(const QString &name, int size, KIconLoader::MatchType match) const { - QString path; - QString tempPath; // used to cache icon path if it exists - int delta = -INT_MAX; // current icon size delta of 'icon' - int dw = INT_MAX; // icon size delta of current directory - - // Search the directory that contains the icon which matches best to the requested - // size. If there is no directory which matches exactly to the requested size, the - // following criterias get applied: - // - Take a directory having icons with a minimum difference to the requested size. - // - Prefer directories that allow a downscaling even if the difference to - // the requested size is bigger than a directory where an upscaling is required. - foreach (KIconThemeDir *dir, d->mDirs) { - if (match == KIconLoader::MatchExact) { - if ((dir->type() == KIconLoader::Fixed) && (dir->size() != size)) { - continue; - } - if ((dir->type() == KIconLoader::Scalable) && - ((size < dir->minSize()) || (size > dir->maxSize()))) { - continue; - } - if ((dir->type() == KIconLoader::Threshold) && - (abs(dir->size() - size) > dir->threshold())) { - continue; - } - } else { - // dw < 0 means need to scale up to get an icon of the requested size. - // Upscaling should only be done if no larger icon is available. - if (dir->type() == KIconLoader::Fixed) { - dw = dir->size() - size; - } else if (dir->type() == KIconLoader::Scalable) { - if (size < dir->minSize()) { - dw = dir->minSize() - size; - } else if (size > dir->maxSize()) { - dw = dir->maxSize() - size; - } else { - dw = 0; - } - } else if (dir->type() == KIconLoader::Threshold) { - if (size < dir->size() - dir->threshold()) { - dw = dir->size() - dir->threshold() - size; - } else if (size > dir->size() + dir->threshold()) { - dw = dir->size() + dir->threshold() - size; - } else { - dw = 0; - } - } - // Usually if the delta (= 'dw') of the current directory is - // not smaller than the delta (= 'delta') of the currently best - // matching icon, this candidate can be skipped. But skipping - // the candidate may only be done, if this does not imply - // in an upscaling of the icon (it is OK to use a directory with - // smaller icons that what we've already found, however). - if ((abs(dw) >= abs(delta)) && ((dw < 0) || (delta > 0))) { - continue; - } - } - - // cache the result of iconPath() call which checks if file exists - tempPath = dir->iconPath(name); + return iconPath(name, size, match, 1 /*scale*/); +} - if (tempPath.isEmpty()) { - continue; - } - path = tempPath; +QString KIconTheme::iconPath(const QString &name, int size, KIconLoader::MatchType match, qreal scale) const +{ + // first look for a scaled image at exactly the requested size + QString path = d->iconPath(d->mScaledDirs, name, size, scale, KIconLoader::MatchExact); - // if we got in MatchExact that far, we find no better - if (match == KIconLoader::MatchExact) { - return path; - } - delta = dw; - if (delta == 0) { - return path; // We won't find a better match anyway - } + // then look for an unscaled one but request it at larger size so it doesn't become blurry + if (path.isEmpty()) { + path = d->iconPath(d->mDirs, name, size * scale, 1, match); } return path; } @@ -658,6 +708,7 @@ : mbValid(false) , mType(KIconLoader::Fixed) , mSize(config.readEntry("Size", 0)) + , mScale(config.readEntry("Scale", 1)) , mMinSize(1) // just set the variables to something , mMaxSize(50) // meaningful in case someone calls minSize or maxSize , mThreshold(2)