diff --git a/cockatrice/src/interface/theme_icon.cpp b/cockatrice/src/interface/theme_icon.cpp index 161c436ca..038a2fba0 100644 --- a/cockatrice/src/interface/theme_icon.cpp +++ b/cockatrice/src/interface/theme_icon.cpp @@ -85,9 +85,9 @@ bool hasSentinel(const QByteArray &svg) return !svg.isEmpty() && QString::fromUtf8(svg).contains(QStringLiteral("currentColor"), Qt::CaseInsensitive); } -QString cacheKey(const QString &path, const QByteArray &variant, const QSize &size, qreal devicePixelRatio) +QString cacheKey(const QString &path, const QByteArray &color, const QSize &size, qreal devicePixelRatio) { - return path + QLatin1Char('|') + QString::fromLatin1(variant) + QLatin1Char('|') + QString::number(size.width()) + + return path + QLatin1Char('|') + QString::fromLatin1(color) + QLatin1Char('|') + QString::number(size.width()) + QLatin1Char('x') + QString::number(size.height()) + QLatin1Char('@') + QString::number(devicePixelRatio); } @@ -97,11 +97,9 @@ QString cacheKey(const QString &path, const QByteArray &variant, const QSize &si * Qt's SVG renderer has no ambient color, so the sentinel is substituted from * the current palette on every pixmap request. Asset resolution, the file bytes * and the intrinsic size are cached until the theme changes: a repaint then - * does no file I/O at all. The render cache is keyed by resolved path and pixel - * size plus a variant: the palette color for sentinel SVGs, the icon mode for - * assets that carry their own colors. A palette change therefore produces new - * keys instead of reusing art built under the previous palette, and the - * non-sentinel path (raster overrides, plain SVGs) is cached like any other. + * does no file I/O at all. The render cache is keyed by resolved path, palette + * color, pixel size and DPR, so a palette change produces new keys instead of + * reusing art built under the previous palette. */ class ThemeSvgIconEngine final : public QObject, public QIconEngine { @@ -233,26 +231,24 @@ private: QPixmap render(const QSize &pixelSize, qreal devicePixelRatio, QIcon::Mode mode) const { ensureResolved(); + if (!resolvedHasSentinel) { + // Authored art carries its own colors: use Qt's normal loader so + // raster overrides and non-SVG assets keep working. + QPixmap pixmap = fromAsset(pixelSize, mode); + if (!pixmap.isNull()) { + pixmap.setDevicePixelRatio(devicePixelRatio); + } + return pixmap; + } - // Same cache for both paths: sentinel art varies by palette color, art - // with its own colors varies by mode (Disabled is generated). A color - // variant is never a bare digit, so the two key spaces cannot collide. - const QByteArray variant = - resolvedHasSentinel ? sentinelColor(mode, role) : QByteArray::number(static_cast(mode)); - const QString key = cacheKey(resolvedPath, variant, pixelSize, devicePixelRatio); + const QByteArray color = sentinelColor(mode, role); + const QString key = cacheKey(resolvedPath, color, pixelSize, devicePixelRatio); const auto cached = cache.constFind(key); if (cached != cache.constEnd()) { return cached.value(); } - QPixmap pixmap; - if (resolvedHasSentinel) { - pixmap = renderSvg(resolveSvgColorSentinel(resolvedSvg, variant), pixelSize, devicePixelRatio); - } else { - // Authored art carries its own colors: use Qt's normal loader so - // raster overrides and non-SVG assets keep working. - pixmap = fromAsset(pixelSize, mode); - } + QPixmap pixmap = renderSvg(resolveSvgColorSentinel(resolvedSvg, color), pixelSize, devicePixelRatio); if (!pixmap.isNull()) { // Set the ratio before caching: assigning it to a cached pixmap // afterwards detaches (deep-copies) it on every paint. diff --git a/cockatrice/src/interface/widgets/server/games_model.cpp b/cockatrice/src/interface/widgets/server/games_model.cpp index 95ea97bd2..217a62abc 100644 --- a/cockatrice/src/interface/widgets/server/games_model.cpp +++ b/cockatrice/src/interface/widgets/server/games_model.cpp @@ -86,8 +86,7 @@ const QString GamesModel::getGameCreatedString(const int secs) } GamesModel::GamesModel(const QMap &_rooms, const QMap &_gameTypes, QObject *parent) - : QAbstractTableModel(parent), rooms(_rooms), gameTypes(_gameTypes), - lockIcon(loadThemedIcon(QStringLiteral("icons/lock"))) + : QAbstractTableModel(parent), rooms(_rooms), gameTypes(_gameTypes) { } @@ -190,9 +189,10 @@ QVariant GamesModel::data(const QModelIndex &index, int role) const return result.join(", "); } case Qt::DecorationRole: { - // One engine per model: it is palette- and theme-aware and - // invalidates itself, so a repaint must not build a new one. - return gameentry.with_password() ? lockIcon : QVariant(); + // The themed icon follows the palette, so the lock also + // switches to HighlightedText on a selected row, and the + // item view paints it at the device pixel ratio. + return gameentry.with_password() ? loadThemedIcon(QStringLiteral("icons/lock")) : QVariant(); case Qt::TextAlignmentRole: return Qt::AlignLeft; default: diff --git a/cockatrice/src/interface/widgets/server/games_model.h b/cockatrice/src/interface/widgets/server/games_model.h index 62862417d..9bf54b83a 100644 --- a/cockatrice/src/interface/widgets/server/games_model.h +++ b/cockatrice/src/interface/widgets/server/games_model.h @@ -5,7 +5,6 @@ #include "game_type_map.h" #include -#include #include #include #include @@ -33,7 +32,6 @@ private: QList gameList; /**< List of games currently displayed. */ QMap rooms; /**< Map of room IDs to room names. */ QMap gameTypes; /**< Map of room IDs to available game types. */ - QIcon lockIcon; /**< Themed lock for password-protected games. */ static const int NUM_COLS = 8; /**< Number of columns in the table. */ diff --git a/libcockatrice_utility/libcockatrice/utility/svg_recolor.cpp b/libcockatrice_utility/libcockatrice/utility/svg_recolor.cpp index 041e289fe..e25a31ba5 100644 --- a/libcockatrice_utility/libcockatrice/utility/svg_recolor.cpp +++ b/libcockatrice_utility/libcockatrice/utility/svg_recolor.cpp @@ -11,11 +11,8 @@ constexpr QByteArrayView SENTINEL = "currentColor"; /// its char overload cannot match a word. qsizetype findSentinel(const QByteArray &bytes, qsizetype from) { - // QByteArrayView::sliced() is a view, so the scan does not allocate a copy - // at every byte position (QByteArray::sliced() would). - const QByteArrayView view(bytes); - for (qsizetype i = from; i + SENTINEL.size() <= view.size(); ++i) { - if (view.sliced(i, SENTINEL.size()).compare(SENTINEL, Qt::CaseInsensitive) == 0) { + for (qsizetype i = from; i + SENTINEL.size() <= bytes.size(); ++i) { + if (bytes.sliced(i, SENTINEL.size()).compare(SENTINEL, Qt::CaseInsensitive) == 0) { return i; } }