KDEGames

kgamethemeprovider.cpp
1/*
2 SPDX-FileCopyrightText: 2012 Stefan Majewsky <majewsky@gmx.net>
3
4 SPDX-License-Identifier: LGPL-2.0-only
5*/
6
7#include "kgamethemeprovider.h"
8#include "kgameimageprovider_p.h"
9
10// own
11#include <kdegames_logging.h>
12// KF
13#include <KConfig>
14#include <KConfigGroup>
15#include <KSharedConfig>
16// Qt
17#include <QDir>
18#include <QFileInfo>
19#include <QGuiApplication>
20#include <QQmlContext>
21#include <QStandardPaths>
22// Std
23#include <utility>
24
25class KGameThemeProviderPrivate
26{
27public:
28 KGameThemeProvider *const q;
29
30 QString m_name;
32 const QByteArray m_configKey;
33 mutable const KGameTheme *m_currentTheme = nullptr;
34 const KGameTheme *m_defaultTheme = nullptr;
35 // this stores the arguments which were passed to discoverThemes()
36 QString m_dtDirectory;
37 QString m_dtDefaultThemeName;
38 const QMetaObject *m_dtThemeClass = nullptr;
39 // this remembers which themes were already discovered
40 QStringList m_discoveredThemes;
41 // this disables the addTheme() lock during rediscoverThemes()
42 bool m_inRediscover = false;
43
44public:
45 KGameThemeProviderPrivate(KGameThemeProvider *parent, const QByteArray &key)
46 : q(parent)
47 , m_configKey(key)
48 {
49 }
50
51 void updateThemeName()
52 {
53 Q_EMIT q->currentThemeNameChanged(q->currentThemeName());
54 }
55};
56
58 : QObject(parent)
59 , d_ptr(new KGameThemeProviderPrivate(this, configKey))
60{
65 d->updateThemeName();
66 });
67}
68
70{
72
73 if (!d->m_themes.isEmpty()) {
74 // save current theme in config file (no sync() call here; this will most
75 // likely be called at application shutdown when others are also writing to
76 // KGlobal::config(); also KConfig's dtor will sync automatically)
77 // but do not save if there is no choice; this is esp. helpful for the
78 // KGameRenderer constructor overload that uses a single KGameTheme instance
79 if (d->m_themes.size() > 1 && !d->m_configKey.isEmpty()) {
80 KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("KgTheme"));
81 cg.writeEntry(d->m_configKey.data(), currentTheme()->identifier());
82 }
83 // cleanup
84 while (!d->m_themes.isEmpty()) {
85 delete const_cast<KGameTheme *>(d->m_themes.takeFirst());
86 }
87 }
88}
89
90QString KGameThemeProvider::name() const
91{
93
94 return d->m_name;
95}
96
98{
100
101 return d->m_themes;
102}
103
105{
107
108 // The intended use is to create the KGameThemeProvider object, add themes,
109 //*then* start to work with the currentLevel(). The first call to
110 // currentTheme() will load the previous selection from the config, and the
111 // level list will be considered immutable from this point.
112 Q_ASSERT_X(d->m_currentTheme == nullptr || d->m_inRediscover, "KGameThemeProvider::addTheme", "Only allowed before currentTheme() is called.");
113 // add theme
114 d->m_themes.append(theme);
115 theme->setParent(this);
116}
117
119{
120 Q_D(const KGameThemeProvider);
121
122 return d->m_defaultTheme;
123}
124
126{
128
129 if (d->m_currentTheme) {
130 qCDebug(KDEGAMES_LOG) << "You're calling setDefaultTheme after the current "
131 "theme has already been determined. That's not gonna work.";
132 return;
133 }
134 Q_ASSERT(d->m_themes.contains(theme));
135 d->m_defaultTheme = theme;
136}
137
138const KGameTheme *KGameThemeProvider::currentTheme() const
139{
140 Q_D(const KGameThemeProvider);
141
142 if (d->m_currentTheme) {
143 return d->m_currentTheme;
144 }
145 Q_ASSERT(!d->m_themes.isEmpty());
146 // check configuration file for saved theme
147 if (!d->m_configKey.isEmpty()) {
148 KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("KgTheme"));
149 const QByteArray id = cg.readEntry(d->m_configKey.data(), QByteArray());
150 // look for a theme with this id
151 for (const KGameTheme *theme : std::as_const(d->m_themes)) {
152 if (theme->identifier() == id) {
153 return d->m_currentTheme = theme;
154 }
155 }
156 }
157 // fall back to default theme (or first theme if no default specified)
158 return d->m_currentTheme = (d->m_defaultTheme ? d->m_defaultTheme : d->m_themes.first());
159}
160
162{
164
165 Q_ASSERT(d->m_themes.contains(theme));
166 if (d->m_currentTheme != theme) {
167 d->m_currentTheme = theme;
169 }
170}
171
172QString KGameThemeProvider::currentThemeName() const
173{
174 Q_D(const KGameThemeProvider);
175
176 return currentTheme()->name();
177}
178
179void KGameThemeProvider::discoverThemes(const QString &directory, const QString &defaultThemeName, const QMetaObject *themeClass)
180{
182
183 d->m_dtDirectory = directory;
184 d->m_dtDefaultThemeName = defaultThemeName;
185 d->m_dtThemeClass = themeClass;
187}
188
189static QStringList findSubdirectories(const QStringList &dirs)
190{
191 QStringList result;
192
193 for (const QString &dir : dirs) {
194 const QStringList subdirNames = QDir(dir).entryList(QDir::Dirs | QDir::NoDotAndDotDot);
195 result.reserve(result.size() + subdirNames.size());
196 for (const QString &subdirName : subdirNames) {
197 const QString subdir = dir + QLatin1Char('/') + subdirName;
198 result << subdir;
199 }
200 }
201 if (!result.isEmpty()) {
202 result += findSubdirectories(result);
203 }
204
205 return result;
206}
207
209{
211
212 if (d->m_dtDirectory.isEmpty()) {
213 return; // discoverThemes() was never called
214 }
215
216 KGameTheme *defaultTheme = nullptr;
217
218 d->m_inRediscover = true;
219 const QString defaultFileName = d->m_dtDefaultThemeName + QLatin1String(".desktop");
220
223 const QStringList allDirs = dirs + findSubdirectories(dirs);
224 for (const QString &dir : allDirs) {
225 const QStringList fileNames = QDir(dir).entryList({QStringLiteral("*.desktop")});
226 for (const QString &file : fileNames) {
227 if (!themePaths.contains(file)) {
228 themePaths.append(dir + QLatin1Char('/') + file);
229 }
230 }
231 }
232
233 // create themes from result, order default theme at the front (that's not
234 // needed by KGameThemeProvider, but nice for the theme selector)
236 themes.reserve(themePaths.size());
237 if (d->m_discoveredThemes.isEmpty()) {
238 d->m_discoveredThemes.reserve(themePaths.size());
239 }
240 for (const QString &themePath : std::as_const(themePaths)) {
241 const QFileInfo fi(themePath);
242 if (d->m_discoveredThemes.contains(fi.fileName())) {
243 continue;
244 }
245 d->m_discoveredThemes << fi.fileName();
246 // the identifier is constructed such that it is compatible with
247 // KGameTheme (e.g. "themes/default.desktop")
248 const QByteArray id = QString(d->m_dtDirectory + QLatin1Char('/') + fi.fileName()).toUtf8();
249
250 // create theme
251 KGameTheme *theme;
252 if (d->m_dtThemeClass) {
253 theme = qobject_cast<KGameTheme *>(d->m_dtThemeClass->newInstance(Q_ARG(QByteArray, id), Q_ARG(QObject *, this)));
254 Q_ASSERT_X(theme, "KGameThemeProvider::discoverThemes", "Could not create theme instance. Is your constructor Q_INVOKABLE?");
255 } else {
256 theme = new KGameTheme(id, this);
257 }
258 // silently discard invalid theme files
259 if (!theme->readFromDesktopFile(themePath)) {
260 delete theme;
261 continue;
262 }
263 // order default theme at the front (that's not necessarily needed by
264 // KGameThemeProvider, but nice for the theme selector)
265 if (fi.fileName() == defaultFileName) {
266 themes.prepend(theme);
267 defaultTheme = theme;
268 } else {
269 themes.append(theme);
270 }
271 }
272 // add themes in the determined order
273 for (KGameTheme *theme : std::as_const(themes)) {
274 addTheme(theme);
275 }
276
277 if (defaultTheme != nullptr) {
279 } else if (d->m_defaultTheme == nullptr && themes.count() != 0) {
281 }
282
283 d->m_inRediscover = false;
284}
285
287{
288 const qreal dpr = qApp->devicePixelRatio();
289 QPixmap pixmap = QPixmap(theme->previewPath()).scaled(size * dpr, Qt::KeepAspectRatio, Qt::SmoothTransformation);
290 pixmap.setDevicePixelRatio(dpr);
291 return pixmap;
292}
293
295{
297
298 if (d->m_name != name) { // prevent multiple declarations
299 d->m_name = name;
300 engine->addImageProvider(name, new KGameImageProvider(this));
301 engine->rootContext()->setContextProperty(name, this);
302 }
303}
304
305#include "moc_kgamethemeprovider.cpp"
A theme provider manages KGameTheme instances, and maintains a selection of the currentTheme().
QList< const KGameTheme * > themes() const
void currentThemeChanged(const KGameTheme *theme)
Emitted when the current theme changes.
~KGameThemeProvider() override
Destructor.
void rediscoverThemes()
After this provider has been set up with discoverThemes(), this method may be used to read additional...
void discoverThemes(const QString &directory, const QString &defaultThemeName=QStringLiteral("default"), const QMetaObject *themeClass=nullptr)
This method reads theme description files from a standard location.
virtual QPixmap generatePreview(const KGameTheme *theme, QSize size)
Generate a preview pixmap for the given theme.
void currentThemeNameChanged(const QString &themeName)
Emitted when the name of the current theme changes.
void setDefaultTheme(const KGameTheme *theme)
void setDeclarativeEngine(const QString &name, QQmlEngine *engine)
Registers this KGameThemeProvider with engine's root context with ID name and constructs a KGameImage...
void addTheme(KGameTheme *theme)
Adds a theme to this instance.
void setCurrentTheme(const KGameTheme *theme)
Select a new theme.
const KGameTheme * defaultTheme() const
KGameThemeProvider(const QByteArray &configKey=QByteArrayLiteral("Theme"), QObject *parent=nullptr)
Constructor.
A theme describes the visual appearance of a game.
Definition kgametheme.h:60
virtual bool readFromDesktopFile(const QString &path)
Initializes a KGameTheme instance by reading a description file.
static KSharedConfig::Ptr openConfig(const QString &fileName=QString(), OpenFlags mode=FullConfig, QStandardPaths::StandardLocation type=QStandardPaths::GenericConfigLocation)
QStringList entryList(Filters filters, SortFlags sort) const const
void append(QList< T > &&value)
qsizetype count() const const
bool isEmpty() const const
void prepend(parameter_type value)
void reserve(qsizetype size)
qsizetype size() const const
T value(qsizetype i) const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
T qobject_cast(QObject *object)
void setParent(QObject *parent)
QPixmap scaled(const QSize &size, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const const
void setDevicePixelRatio(qreal scaleFactor)
void setContextProperty(const QString &name, QObject *value)
void addImageProvider(const QString &providerId, QQmlImageProviderBase *provider)
QQmlContext * rootContext() const const
QStringList locateAll(StandardLocation type, const QString &fileName, LocateOptions options)
QByteArray toUtf8() const const
KeepAspectRatio
SmoothTransformation
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Apr 27 2024 22:10:38 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.