mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-04-29 15:03:09 +00:00
Record recent launches only after the launcher reports success instead of optimistically on every attempt. For desktop entries, wait for the ApplicationLauncherJob result; for detached commands, use startDetached's return value and show an inline error when startup fails. Parse Steam libraryfolders and app manifests as KeyValues instead of matching individual lines. This makes Steam discovery less brittle when the files contain comments, nested blocks, or different formatting. Keep Continue Playing visible alongside Running and show a temporary launching state so the overlay does not pretend a task already exists. Rename the GameMode status text to say what the shell actually knows.
116 lines
3.9 KiB
C++
116 lines
3.9 KiB
C++
// SPDX-FileCopyrightText: 2026 Marco Allegretti
|
|
// SPDX-License-Identifier: EUPL-1.2
|
|
|
|
#pragma once
|
|
|
|
#include <QAbstractListModel>
|
|
#include <QDateTime>
|
|
#include <QList>
|
|
#include <QString>
|
|
#include <QTimer>
|
|
#include <qqmlregistration.h>
|
|
|
|
#include <KSharedConfig>
|
|
|
|
class GameLauncherProvider : public QAbstractListModel
|
|
{
|
|
Q_OBJECT
|
|
QML_ELEMENT
|
|
QML_SINGLETON
|
|
|
|
Q_PROPERTY(int count READ count NOTIFY countChanged)
|
|
Q_PROPERTY(bool loading READ loading NOTIFY loadingChanged)
|
|
Q_PROPERTY(QString filterString READ filterString WRITE setFilterString NOTIFY filterStringChanged)
|
|
Q_PROPERTY(QString sourceFilter READ sourceFilter WRITE setSourceFilter NOTIFY sourceFilterChanged)
|
|
Q_PROPERTY(bool overlayEnabled READ overlayEnabled WRITE setOverlayEnabled NOTIFY overlayEnabledChanged)
|
|
Q_PROPERTY(bool launchPending READ launchPending NOTIFY launchPendingChanged)
|
|
Q_PROPERTY(QString pendingLaunchName READ pendingLaunchName NOTIFY launchPendingChanged)
|
|
Q_PROPERTY(QString lastLaunchError READ lastLaunchError NOTIFY lastLaunchErrorChanged)
|
|
|
|
public:
|
|
explicit GameLauncherProvider(QObject *parent = nullptr);
|
|
|
|
enum Roles {
|
|
NameRole = Qt::UserRole + 1,
|
|
IconRole,
|
|
SourceRole, // "desktop", "steam", "flatpak"
|
|
StorageIdRole, // .desktop file name or launch URI
|
|
LaunchCommandRole,
|
|
ArtworkRole, // path to banner/grid image (empty if none)
|
|
InstalledRole,
|
|
};
|
|
Q_ENUM(Roles)
|
|
|
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
|
QVariant data(const QModelIndex &index, int role) const override;
|
|
QHash<int, QByteArray> roleNames() const override;
|
|
|
|
int count() const;
|
|
bool loading() const;
|
|
QString filterString() const;
|
|
void setFilterString(const QString &filter);
|
|
QString sourceFilter() const;
|
|
void setSourceFilter(const QString &source);
|
|
bool overlayEnabled() const;
|
|
void setOverlayEnabled(bool enabled);
|
|
bool launchPending() const;
|
|
QString pendingLaunchName() const;
|
|
QString lastLaunchError() const;
|
|
|
|
Q_INVOKABLE void refresh();
|
|
Q_INVOKABLE void launch(int index);
|
|
Q_INVOKABLE void launchByStorageId(const QString &storageId);
|
|
Q_INVOKABLE QVariantList recentGames(int limit = 5) const;
|
|
Q_INVOKABLE void clearPendingLaunch();
|
|
Q_INVOKABLE void clearLastLaunchError();
|
|
|
|
Q_SIGNALS:
|
|
void countChanged();
|
|
void loadingChanged();
|
|
void filterStringChanged();
|
|
void sourceFilterChanged();
|
|
void overlayEnabledChanged();
|
|
void launchPendingChanged();
|
|
void lastLaunchErrorChanged();
|
|
void gameLaunched(const QString &name);
|
|
void gameLaunchFailed(const QString &name, const QString &error);
|
|
|
|
private:
|
|
struct GameEntry {
|
|
QString name;
|
|
QString icon;
|
|
QString source;
|
|
QString storageId;
|
|
QString launchCommand;
|
|
QString artwork;
|
|
QDateTime lastPlayed;
|
|
bool installed = true;
|
|
};
|
|
|
|
void loadDesktopGames();
|
|
void loadSteamGames();
|
|
void loadFlatpakGames();
|
|
void loadLutrisGames();
|
|
void loadHeroicGames();
|
|
void deduplicateGames();
|
|
void loadRecentTimestamps();
|
|
void saveRecentTimestamp(const QString &storageId, const QDateTime &when);
|
|
void applyFilter();
|
|
void launchEntry(GameEntry &entry);
|
|
GameEntry *findEntryByStorageId(const QString &storageId);
|
|
void markLaunchSucceeded(const QString &storageId, const QString &name);
|
|
void markLaunchFailed(const QString &name, const QString &error);
|
|
void setPendingLaunch(const QString &name);
|
|
|
|
QList<GameEntry> m_allGames;
|
|
QList<GameEntry> m_games; // filtered view
|
|
QString m_filterString;
|
|
QString m_sourceFilter; // empty = all, or "desktop"/"steam"/"flatpak"
|
|
KSharedConfigPtr m_config;
|
|
bool m_loading = false;
|
|
bool m_overlayEnabled = false;
|
|
bool m_launchPending = false;
|
|
QString m_pendingLaunchName;
|
|
QString m_lastLaunchError;
|
|
QTimer m_pendingLaunchTimer;
|
|
};
|