KDEGames

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

KDE's Doxygen guidelines are available online.