// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: 2026 A-La-Karte Contributors import QtQuick import QtQuick.Controls as QQC2 import QtQuick.Layouts import org.mauikit.controls as Maui import org.kde.alakarte import "components" FocusScope { id: libraryRoot property string filterSource: "all" property string filterText: "" signal gameSelected(var game) signal gameFocused(var game) signal gameLaunched(var game) signal headerFocusRequested() readonly property int gameCount: proxyModel.count function restoreFocus() { if (grid.flickable) { grid.flickable.forceActiveFocus() } else { grid.forceActiveFocus() } } function focusedGame() { if (grid.currentIndex < 0) return null return proxyModel.get(grid.currentIndex) } function selectFocused() { let g = focusedGame() if (g) libraryRoot.gameSelected(g) } function launchFocused() { let g = focusedGame() if (g) libraryRoot.gameLaunched(g) } GameSortFilterModel { id: proxyModel sourceModel: App.gameModel showHidden: libraryRoot.filterSource === "hidden" favoritesOnly: libraryRoot.filterSource === "favorites" filterSource: { if (libraryRoot.filterSource === "all") return "" if (libraryRoot.filterSource === "favorites") return "" if (libraryRoot.filterSource === "hidden") return "" return libraryRoot.filterSource } filterText: libraryRoot.filterText } Maui.GridBrowser { id: grid anchors.fill: parent focus: true model: proxyModel itemSize: Math.max(160, Math.min(App.config.gridSize, 280)) itemHeight: Math.round(grid.itemSize * 1.5) adaptContent: true enableLassoSelection: false topPadding: 16 bottomPadding: 24 leftPadding: 24 rightPadding: 24 holder.visible: proxyModel.count === 0 && !App.importing holder.emoji: libraryRoot.filterSource === "favorites" ? "starred-symbolic" : "applications-games" holder.title: libraryRoot.filterSource === "favorites" ? i18n("No favorites yet") : i18n("Your library is empty") holder.body: libraryRoot.filterSource === "favorites" ? i18n("Mark games as favorites to see them here") : i18n("Import games to get started") onCurrentIndexChanged: { let g = proxyModel.get(currentIndex) if (g) libraryRoot.gameFocused(g) } Component.onCompleted: { Qt.callLater(function() { let g = proxyModel.get(currentIndex) if (g) libraryRoot.gameFocused(g) }) } delegate: GameCard { game: model.gameObject gridActive: grid.flickable ? grid.flickable.activeFocus : grid.activeFocus onClicked: { grid.currentIndex = index libraryRoot.gameSelected(model.gameObject) } onPlayClicked: { grid.currentIndex = index libraryRoot.gameLaunched(model.gameObject) } } Keys.onReturnPressed: libraryRoot.selectFocused() Keys.onEnterPressed: libraryRoot.selectFocused() Keys.onSpacePressed: libraryRoot.selectFocused() QQC2.BusyIndicator { anchors.centerIn: parent running: App.importing visible: App.importing } Connections { target: GamepadManager function onSelectPressed() { if (!grid.flickable || !grid.flickable.activeFocus) return libraryRoot.selectFocused() } function onNavigateUp() { if (!grid.flickable || !grid.flickable.activeFocus) return let cols = Math.max(1, Math.floor(grid.width / grid.cellWidth)) if (grid.currentIndex < cols) { libraryRoot.headerFocusRequested() return } grid.currentIndex = Math.max(0, grid.currentIndex - cols) } function onNavigateDown() { if (!grid.flickable || !grid.flickable.activeFocus) return let cols = Math.max(1, Math.floor(grid.width / grid.cellWidth)) grid.currentIndex = Math.min(proxyModel.count - 1, grid.currentIndex + cols) } function onNavigateLeft() { if (!grid.flickable || !grid.flickable.activeFocus) return grid.currentIndex = Math.max(0, grid.currentIndex - 1) } function onNavigateRight() { if (!grid.flickable || !grid.flickable.activeFocus) return grid.currentIndex = Math.min(proxyModel.count - 1, grid.currentIndex + 1) } } } }