// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: 2024 A-La-Karte Contributors import QtQuick import QtQuick.Controls as QQC2 import QtQuick.Layouts import org.kde.kirigami as Kirigami import org.kde.alakarte import "components" FocusScope { id: libraryRoot property string filterSource: "all" property bool searchActive: false property int focusedIndex: 0 property int adaptiveCardSize: App.config.gridSize property bool isTouchDevice: false signal gameSelected(var game) signal gameLaunched(var game) function focusSearch() { searchField.forceActiveFocus() } function restoreFocus() { if (libraryRoot.searchActive) { libraryRoot.focusSearch() } else { gameGrid.forceActiveFocus() } } onSearchActiveChanged: { if (!libraryRoot.searchActive) { Qt.callLater(function() { gameGrid.forceActiveFocus() }) } } Item { anchors.fill: parent anchors.margins: 0 ColumnLayout { anchors.fill: parent spacing: Kirigami.Units.smallSpacing SearchHeader { id: searchHeader Layout.fillWidth: true visible: libraryRoot.searchActive searchField: searchField onSearchChanged: function(text) { proxyModel.filterText = text } onSortChanged: function(mode) { proxyModel.sortMode = mode } Kirigami.SearchField { id: searchField Layout.fillWidth: true placeholderText: i18n("Search games...") onTextChanged: proxyModel.filterText = text Keys.onEscapePressed: { text = "" let w = applicationWindow() if (w && w.hasOwnProperty("searchActive")) { w.searchActive = false } else { libraryRoot.searchActive = false } libraryRoot.restoreFocus() } Keys.onDownPressed: gameGrid.forceActiveFocus() } } GameGridView { id: gameGrid Layout.fillWidth: true Layout.fillHeight: true cardSize: libraryRoot.adaptiveCardSize model: 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 } } delegate: Item { width: gameGrid.cellWidth height: gameGrid.cellHeight function clicked() { card.clicked() } GameCard { id: card width: gameGrid.cardSize height: Math.round(gameGrid.cardSize * 1.4) anchors.centerIn: parent game: model.gameObject focused: gameGrid.currentIndex === index && gameGrid.activeFocus onClicked: libraryRoot.gameSelected(model.gameObject) onDoubleClicked: libraryRoot.gameLaunched(model.gameObject) onPlayClicked: libraryRoot.gameLaunched(model.gameObject) Keys.onReturnPressed: libraryRoot.gameSelected(model.gameObject) Keys.onEnterPressed: libraryRoot.gameSelected(model.gameObject) Keys.onSpacePressed: libraryRoot.gameLaunched(model.gameObject) } } Keys.onPressed: function(event) { if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { let game = proxyModel.get(currentIndex) if (game) { libraryRoot.gameSelected(game) } event.accepted = true } else if (event.key === Qt.Key_Space) { let game = proxyModel.get(currentIndex) if (game) { libraryRoot.gameLaunched(game) } event.accepted = true } } EmptyState { anchors.centerIn: parent visible: proxyModel.count === 0 && !App.importing icon: proxyModel.filterText.length > 0 ? "edit-find" : "applications-games" title: proxyModel.filterText.length > 0 ? i18n("No games found") : i18n("Your library is empty") description: proxyModel.filterText.length > 0 ? i18n("Try adjusting your search") : i18n("Import games to get started") actionText: proxyModel.filterText.length > 0 ? "" : i18n("Import Games") onActionTriggered: App.importAllGames() } QQC2.BusyIndicator { anchors.centerIn: parent running: App.importing visible: App.importing } } QQC2.ToolBar { Layout.fillWidth: true visible: proxyModel.count > 0 leftPadding: 0 rightPadding: 0 topPadding: Kirigami.Units.smallSpacing bottomPadding: Kirigami.Units.smallSpacing contentItem: RowLayout { spacing: Kirigami.Units.mediumSpacing QQC2.Label { text: i18np("%1 game", "%1 games", proxyModel.count) color: Kirigami.Theme.disabledTextColor font.pointSize: Kirigami.Theme.smallFont.pointSize Layout.alignment: Qt.AlignVCenter } Item { Layout.fillWidth: true } QQC2.Slider { id: sizeSlider from: 120 to: 280 stepSize: 20 value: App.config.gridSize Layout.preferredWidth: Kirigami.Units.gridUnit * 8 Layout.alignment: Qt.AlignVCenter onMoved: App.config.gridSize = value QQC2.ToolTip { parent: sizeSlider.handle visible: sizeSlider.pressed text: Math.round(sizeSlider.value) + " px" } } } } } } }