utils: Load Xcursor cursors on demand

Usually, only a small subset of cursors would be used, for example the
default shape, the pointer shape, the text shape, etc.

Another reason why this change makes the KXcursorTheme load cursors on
demand is to prepare the cursor loading machinery to SVG cursors.
This commit is contained in:
Vlad Zahorodnii 2024-06-24 14:41:36 +03:00
parent 4f12792c2f
commit 2296f1ac3b

View file

@ -29,20 +29,31 @@ public:
std::chrono::milliseconds delay; std::chrono::milliseconds delay;
}; };
class KXcursorThemeEntry
{
public:
explicit KXcursorThemeEntry(const QString &filePath);
void load(int size, qreal devicePixelRatio);
QString filePath;
QList<KXcursorSprite> sprites;
};
class KXcursorThemePrivate : public QSharedData class KXcursorThemePrivate : public QSharedData
{ {
public: public:
KXcursorThemePrivate(); KXcursorThemePrivate();
KXcursorThemePrivate(const QString &themeName, int size, qreal devicePixelRatio); KXcursorThemePrivate(const QString &themeName, int size, qreal devicePixelRatio);
void load(const QStringList &searchPaths); void discover(const QStringList &searchPaths);
void loadCursors(const QString &packagePath); void discoverCursors(const QString &packagePath);
QString name; QString name;
int size = 0; int size = 0;
qreal devicePixelRatio = 0; qreal devicePixelRatio = 0;
QHash<QByteArray, QList<KXcursorSprite>> registry; QHash<QByteArray, std::shared_ptr<KXcursorThemeEntry>> registry;
}; };
KXcursorSprite::KXcursorSprite() KXcursorSprite::KXcursorSprite()
@ -146,7 +157,19 @@ static QList<KXcursorSprite> loadCursor(const QString &filePath, int desiredSize
return sprites; return sprites;
} }
void KXcursorThemePrivate::loadCursors(const QString &packagePath) KXcursorThemeEntry::KXcursorThemeEntry(const QString &filePath)
: filePath(filePath)
{
}
void KXcursorThemeEntry::load(int size, qreal devicePixelRatio)
{
if (sprites.isEmpty()) {
sprites = loadCursor(filePath, size, devicePixelRatio);
}
}
void KXcursorThemePrivate::discoverCursors(const QString &packagePath)
{ {
const QDir dir(packagePath); const QDir dir(packagePath);
QFileInfoList entries = dir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot); QFileInfoList entries = dir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot);
@ -162,17 +185,13 @@ void KXcursorThemePrivate::loadCursors(const QString &packagePath)
if (entry.isSymLink()) { if (entry.isSymLink()) {
const QFileInfo symLinkInfo(entry.symLinkTarget()); const QFileInfo symLinkInfo(entry.symLinkTarget());
if (symLinkInfo.absolutePath() == entry.absolutePath()) { if (symLinkInfo.absolutePath() == entry.absolutePath()) {
const auto sprites = registry.value(QFile::encodeName(symLinkInfo.fileName())); if (auto alias = registry.value(QFile::encodeName(symLinkInfo.fileName()))) {
if (!sprites.isEmpty()) { registry.insert(shape, alias);
registry.insert(shape, sprites);
continue; continue;
} }
} }
} }
const QList<KXcursorSprite> sprites = loadCursor(entry.absoluteFilePath(), size, devicePixelRatio); registry.insert(shape, std::make_shared<KXcursorThemeEntry>(entry.absoluteFilePath()));
if (!sprites.isEmpty()) {
registry.insert(shape, sprites);
}
} }
} }
@ -199,7 +218,7 @@ static QStringList defaultSearchPaths()
return paths; return paths;
} }
void KXcursorThemePrivate::load(const QStringList &searchPaths) void KXcursorThemePrivate::discover(const QStringList &searchPaths)
{ {
const QStringList paths = !searchPaths.isEmpty() ? searchPaths : defaultSearchPaths(); const QStringList paths = !searchPaths.isEmpty() ? searchPaths : defaultSearchPaths();
@ -221,7 +240,7 @@ void KXcursorThemePrivate::load(const QStringList &searchPaths)
if (!dir.exists()) { if (!dir.exists()) {
continue; continue;
} }
loadCursors(dir.filePath(QStringLiteral("cursors"))); discoverCursors(dir.filePath(QStringLiteral("cursors")));
if (inherits.isEmpty()) { if (inherits.isEmpty()) {
const KConfig config(dir.filePath(QStringLiteral("index.theme")), KConfig::NoGlobals); const KConfig config(dir.filePath(QStringLiteral("index.theme")), KConfig::NoGlobals);
inherits << KConfigGroup(&config, QStringLiteral("Icon Theme")).readEntry("Inherits", QStringList()); inherits << KConfigGroup(&config, QStringLiteral("Icon Theme")).readEntry("Inherits", QStringList());
@ -243,7 +262,7 @@ KXcursorTheme::KXcursorTheme()
KXcursorTheme::KXcursorTheme(const QString &themeName, int size, qreal devicePixelRatio, const QStringList &searchPaths) KXcursorTheme::KXcursorTheme(const QString &themeName, int size, qreal devicePixelRatio, const QStringList &searchPaths)
: d(new KXcursorThemePrivate(themeName, size, devicePixelRatio)) : d(new KXcursorThemePrivate(themeName, size, devicePixelRatio))
{ {
d->load(searchPaths); d->discover(searchPaths);
} }
KXcursorTheme::KXcursorTheme(const KXcursorTheme &other) KXcursorTheme::KXcursorTheme(const KXcursorTheme &other)
@ -293,7 +312,11 @@ bool KXcursorTheme::isEmpty() const
QList<KXcursorSprite> KXcursorTheme::shape(const QByteArray &name) const QList<KXcursorSprite> KXcursorTheme::shape(const QByteArray &name) const
{ {
return d->registry.value(name); if (auto entry = d->registry.value(name)) {
entry->load(d->size, d->devicePixelRatio);
return entry->sprites;
}
return QList<KXcursorSprite>();
} }
} // namespace KWin } // namespace KWin