From 9f7e1677eb7247f7153fbbac45823739accb71d8 Mon Sep 17 00:00:00 2001 From: Marco Allegretti Date: Sat, 2 May 2026 09:41:07 +0200 Subject: [PATCH] Hide launcher tabs when not installed Steam, Lutris, and Heroic filter tabs in the Game Center are now only shown when those launchers are detected on the system. Detection checks the native executable, known Flatpak data directories, and standard data paths. The tab bar shrinks accordingly. --- .../gamelauncherprovider.cpp | 46 +++++++++++++++ .../gamingshellplugin/gamelauncherprovider.h | 12 ++++ .../folio/qml/gaming/GameCenterOverlay.qml | 59 +++++++++---------- 3 files changed, 86 insertions(+), 31 deletions(-) diff --git a/components/gamingshellplugin/gamelauncherprovider.cpp b/components/gamingshellplugin/gamelauncherprovider.cpp index 99bfd3ae..9bae988b 100644 --- a/components/gamingshellplugin/gamelauncherprovider.cpp +++ b/components/gamingshellplugin/gamelauncherprovider.cpp @@ -357,6 +357,21 @@ void GameLauncherProvider::refresh() m_allGames.clear(); + // Detect which third-party launchers are installed + const bool steamWas = m_steamAvailable; + const bool lutrisWas = m_lutrisAvailable; + const bool heroicWas = m_heroicAvailable; + m_steamAvailable = detectLauncher(QStringLiteral("steam"), + {QStringLiteral("com.valvesoftware.Steam")}, + {QDir::homePath() + QStringLiteral("/.steam/steam"), + QDir::homePath() + QStringLiteral("/.local/share/Steam"), + QDir::homePath() + QStringLiteral("/.var/app/com.valvesoftware.Steam/.local/share/Steam")}); + m_lutrisAvailable = + detectLauncher(QStringLiteral("lutris"), {QStringLiteral("net.lutris.Lutris")}, {QDir::homePath() + QStringLiteral("/.local/share/lutris")}); + m_heroicAvailable = + detectLauncher(QStringLiteral("heroic"), {QStringLiteral("com.heroicgameslauncher.hgl")}, {QDir::homePath() + QStringLiteral("/.config/heroic")}); + if (m_steamAvailable != steamWas || m_lutrisAvailable != lutrisWas || m_heroicAvailable != heroicWas) + Q_EMIT launcherAvailabilityChanged(); loadDesktopGames(); loadSteamGames(); loadLutrisGames(); @@ -904,6 +919,37 @@ bool GameLauncherProvider::mangohudAvailable() const return m_mangohudAvailable; } +bool GameLauncherProvider::steamAvailable() const +{ + return m_steamAvailable; +} + +bool GameLauncherProvider::lutrisAvailable() const +{ + return m_lutrisAvailable; +} + +bool GameLauncherProvider::heroicAvailable() const +{ + return m_heroicAvailable; +} + +// static +bool GameLauncherProvider::detectLauncher(const QString &executable, const QStringList &flatpakAppIds, const QStringList &dataDirs) +{ + if (!QStandardPaths::findExecutable(executable).isEmpty()) + return true; + for (const QString &appId : flatpakAppIds) { + if (QDir(QDir::homePath() + QStringLiteral("/.var/app/") + appId).exists()) + return true; + } + for (const QString &dir : dataDirs) { + if (QDir(dir).exists()) + return true; + } + return false; +} + int GameLauncherProvider::fpsLimit() const { return m_fpsLimit; diff --git a/components/gamingshellplugin/gamelauncherprovider.h b/components/gamingshellplugin/gamelauncherprovider.h index 32bf6fcd..99896efc 100644 --- a/components/gamingshellplugin/gamelauncherprovider.h +++ b/components/gamingshellplugin/gamelauncherprovider.h @@ -27,6 +27,9 @@ class GameLauncherProvider : public QAbstractListModel Q_PROPERTY(QString sourceFilter READ sourceFilter WRITE setSourceFilter NOTIFY sourceFilterChanged) Q_PROPERTY(bool overlayEnabled READ overlayEnabled WRITE setOverlayEnabled NOTIFY overlayEnabledChanged) Q_PROPERTY(bool mangohudAvailable READ mangohudAvailable NOTIFY mangohudAvailableChanged) + Q_PROPERTY(bool steamAvailable READ steamAvailable NOTIFY launcherAvailabilityChanged) + Q_PROPERTY(bool lutrisAvailable READ lutrisAvailable NOTIFY launcherAvailabilityChanged) + Q_PROPERTY(bool heroicAvailable READ heroicAvailable NOTIFY launcherAvailabilityChanged) Q_PROPERTY(int fpsLimit READ fpsLimit WRITE setFpsLimit NOTIFY fpsLimitChanged) Q_PROPERTY(bool launchPending READ launchPending NOTIFY launchPendingChanged) Q_PROPERTY(QString pendingLaunchName READ pendingLaunchName NOTIFY launchPendingChanged) @@ -61,6 +64,9 @@ public: bool overlayEnabled() const; void setOverlayEnabled(bool enabled); bool mangohudAvailable() const; + bool steamAvailable() const; + bool lutrisAvailable() const; + bool heroicAvailable() const; int fpsLimit() const; void setFpsLimit(int limit); bool launchPending() const; @@ -89,6 +95,7 @@ Q_SIGNALS: void sourceFilterChanged(); void overlayEnabledChanged(); void mangohudAvailableChanged(); + void launcherAvailabilityChanged(); void fpsLimitChanged(); void launchPendingChanged(); void lastLaunchErrorChanged(); @@ -137,6 +144,11 @@ private: int m_fpsLimit = 0; bool m_mangohudAvailable = false; QString m_mangohudPath; + bool m_steamAvailable = false; + bool m_lutrisAvailable = false; + bool m_heroicAvailable = false; + + static bool detectLauncher(const QString &executable, const QStringList &flatpakAppIds, const QStringList &dataDirs); QSet m_pinnedGames; bool m_launchPending = false; QString m_pendingLaunchName; diff --git a/containments/homescreens/folio/qml/gaming/GameCenterOverlay.qml b/containments/homescreens/folio/qml/gaming/GameCenterOverlay.qml index 5df0a6b8..7579c8c9 100644 --- a/containments/homescreens/folio/qml/gaming/GameCenterOverlay.qml +++ b/containments/homescreens/folio/qml/gaming/GameCenterOverlay.qml @@ -422,8 +422,14 @@ Window { } } - // Cycle through source filter tabs. - readonly property var _sourceFilters: ["", "steam", "desktop", "waydroid", "lutris", "heroic"] + // Cycle through source filter tabs (only includes installed launcher sources). + readonly property var _sourceFilters: { + var filters = ["", "desktop", "waydroid"] + if (GamingShell.GameLauncherProvider.steamAvailable) filters.splice(1, 0, "steam") + if (GamingShell.GameLauncherProvider.lutrisAvailable) filters.push("lutris") + if (GamingShell.GameLauncherProvider.heroicAvailable) filters.push("heroic") + return filters + } function cycleSourceFilter(direction) { var current = _sourceFilters.indexOf( GamingShell.GameLauncherProvider.sourceFilter) @@ -749,35 +755,26 @@ Window { id: sourceFilterBar Layout.alignment: Qt.AlignVCenter - QQC2.TabButton { - text: i18n("All") - width: implicitWidth - onClicked: GamingShell.GameLauncherProvider.sourceFilter = "" - } - QQC2.TabButton { - text: "Steam" - width: implicitWidth - onClicked: GamingShell.GameLauncherProvider.sourceFilter = "steam" - } - QQC2.TabButton { - text: i18n("Desktop") - width: implicitWidth - onClicked: GamingShell.GameLauncherProvider.sourceFilter = "desktop" - } - QQC2.TabButton { - text: i18n("Waydroid") - width: implicitWidth - onClicked: GamingShell.GameLauncherProvider.sourceFilter = "waydroid" - } - QQC2.TabButton { - text: "Lutris" - width: implicitWidth - onClicked: GamingShell.GameLauncherProvider.sourceFilter = "lutris" - } - QQC2.TabButton { - text: "Heroic" - width: implicitWidth - onClicked: GamingShell.GameLauncherProvider.sourceFilter = "heroic" + Repeater { + model: { + var tabs = [ + {label: i18n("All"), filter: ""}, + {label: i18n("Desktop"), filter: "desktop"}, + {label: i18n("Waydroid"),filter: "waydroid"} + ] + if (GamingShell.GameLauncherProvider.steamAvailable) + tabs.splice(1, 0, {label: "Steam", filter: "steam"}) + if (GamingShell.GameLauncherProvider.lutrisAvailable) + tabs.push({label: "Lutris", filter: "lutris"}) + if (GamingShell.GameLauncherProvider.heroicAvailable) + tabs.push({label: "Heroic", filter: "heroic"}) + return tabs + } + QQC2.TabButton { + text: modelData.label + width: implicitWidth + onClicked: GamingShell.GameLauncherProvider.sourceFilter = modelData.filter + } } } }