mirror of
https://invent.kde.org/marcoa/a-la-karte.git
synced 2026-02-09 21:13:08 +00:00
328 lines
11 KiB
QML
328 lines
11 KiB
QML
// 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 QtQuick.Effects
|
|
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: -1
|
|
|
|
property int adaptiveCardSize: App.config.gridSize
|
|
property bool isTouchDevice: false
|
|
|
|
signal gameSelected(var game)
|
|
signal gameLaunched(var game)
|
|
|
|
readonly property int gameCount: proxyModel.count
|
|
property url focusedCoverUrl: ""
|
|
|
|
readonly property bool anyMenuOpen: searchHeader.anyMenuOpen
|
|
|
|
function closeCurrentMenu() {
|
|
searchHeader.closeCurrentMenu()
|
|
}
|
|
|
|
function focusSearch() {
|
|
searchField.forceActiveFocus()
|
|
}
|
|
|
|
function clearSearch() {
|
|
searchField.text = ""
|
|
proxyModel.filterText = ""
|
|
}
|
|
|
|
function restoreFocus() {
|
|
let w = applicationWindow()
|
|
if (w && w.hasOwnProperty("pendingSidebarOpen") && w.pendingSidebarOpen) {
|
|
w.pendingSidebarOpen = false
|
|
if (w.globalDrawer && typeof w.globalDrawer.open === "function") {
|
|
w.globalDrawer.open()
|
|
return
|
|
}
|
|
}
|
|
if (libraryRoot.searchActive) {
|
|
libraryRoot.focusSearch()
|
|
} else {
|
|
if (libraryRoot.focusedIndex >= 0 && libraryRoot.focusedIndex < proxyModel.count) {
|
|
gameGrid.currentIndex = libraryRoot.focusedIndex
|
|
}
|
|
gameGrid.forceActiveFocus()
|
|
}
|
|
}
|
|
|
|
function launchFocusedGame() {
|
|
if (!gameGrid || !proxyModel) return
|
|
if (gameGrid.currentIndex < 0 && proxyModel.count > 0) {
|
|
gameGrid.currentIndex = 0
|
|
libraryRoot.focusedIndex = 0
|
|
}
|
|
let game = proxyModel.get(gameGrid.currentIndex)
|
|
if (game) {
|
|
libraryRoot.gameLaunched(game)
|
|
}
|
|
}
|
|
|
|
function openDetailsForFocusedGame() {
|
|
if (!gameGrid || !proxyModel) return
|
|
if (gameGrid.currentIndex < 0 && proxyModel.count > 0) {
|
|
gameGrid.currentIndex = 0
|
|
libraryRoot.focusedIndex = 0
|
|
}
|
|
let game = proxyModel.get(gameGrid.currentIndex)
|
|
if (game) {
|
|
libraryRoot.gameSelected(game)
|
|
}
|
|
}
|
|
|
|
onSearchActiveChanged: {
|
|
if (!libraryRoot.searchActive) {
|
|
libraryRoot.clearSearch()
|
|
Qt.callLater(function() {
|
|
gameGrid.forceActiveFocus()
|
|
})
|
|
}
|
|
}
|
|
|
|
Item {
|
|
anchors.fill: parent
|
|
anchors.margins: 0
|
|
|
|
Item {
|
|
anchors.fill: parent
|
|
visible: libraryRoot.gameCount > 0
|
|
|
|
Image {
|
|
id: backgroundCoverA
|
|
anchors.fill: parent
|
|
source: ""
|
|
fillMode: Image.PreserveAspectCrop
|
|
asynchronous: true
|
|
visible: source.toString().length > 0
|
|
smooth: true
|
|
mipmap: App.config.highQualityImages
|
|
opacity: 0.0
|
|
|
|
Behavior on opacity {
|
|
NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutCubic }
|
|
}
|
|
|
|
layer.enabled: true
|
|
layer.effect: MultiEffect {
|
|
blurEnabled: true
|
|
blur: 0.9
|
|
blurMax: 64
|
|
}
|
|
}
|
|
|
|
Image {
|
|
id: backgroundCoverB
|
|
anchors.fill: parent
|
|
source: ""
|
|
fillMode: Image.PreserveAspectCrop
|
|
asynchronous: true
|
|
visible: source.toString().length > 0
|
|
smooth: true
|
|
mipmap: App.config.highQualityImages
|
|
opacity: 0.0
|
|
|
|
Behavior on opacity {
|
|
NumberAnimation { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutCubic }
|
|
}
|
|
|
|
layer.enabled: true
|
|
layer.effect: MultiEffect {
|
|
blurEnabled: true
|
|
blur: 0.9
|
|
blurMax: 64
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
color: Qt.rgba(0, 0, 0, 0.55)
|
|
}
|
|
}
|
|
|
|
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()
|
|
|
|
Connections {
|
|
target: GamepadManager
|
|
function onNavigateDown() {
|
|
let w = applicationWindow()
|
|
if (w && w.currentConfirmDialog && w.currentConfirmDialog()) return
|
|
if (!searchField.activeFocus) return
|
|
gameGrid.forceActiveFocus()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
GameGridView {
|
|
id: gameGrid
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
|
|
cardSize: libraryRoot.adaptiveCardSize
|
|
|
|
onCurrentIndexChanged: {
|
|
if (gameGrid.activeFocus) {
|
|
libraryRoot.focusedIndex = currentIndex
|
|
}
|
|
let game = proxyModel.get(currentIndex)
|
|
let url = (game && game.coverUrl) ? game.coverUrl : ""
|
|
if (url === libraryRoot.focusedCoverUrl) return
|
|
if (backgroundCoverA.opacity > 0.1) {
|
|
backgroundCoverB.source = url
|
|
backgroundCoverB.opacity = 0.0
|
|
backgroundCoverA.opacity = 0.0
|
|
Qt.callLater(function() { backgroundCoverB.opacity = 0.22 })
|
|
} else {
|
|
backgroundCoverA.source = url
|
|
backgroundCoverA.opacity = 0.0
|
|
backgroundCoverB.opacity = 0.0
|
|
Qt.callLater(function() { backgroundCoverA.opacity = 0.22 })
|
|
}
|
|
libraryRoot.focusedCoverUrl = url
|
|
}
|
|
|
|
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() {
|
|
gameGrid.currentIndex = index
|
|
libraryRoot.focusedIndex = index
|
|
card.clicked()
|
|
}
|
|
|
|
function play() {
|
|
gameGrid.currentIndex = index
|
|
libraryRoot.focusedIndex = index
|
|
card.playClicked()
|
|
}
|
|
|
|
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) {
|
|
if (currentIndex < 0 && proxyModel.count > 0) {
|
|
currentIndex = 0
|
|
libraryRoot.focusedIndex = 0
|
|
}
|
|
let game = proxyModel.get(currentIndex)
|
|
if (game) {
|
|
libraryRoot.gameSelected(game)
|
|
}
|
|
event.accepted = true
|
|
} else if (event.key === Qt.Key_Space) {
|
|
if (currentIndex < 0 && proxyModel.count > 0) {
|
|
currentIndex = 0
|
|
libraryRoot.focusedIndex = 0
|
|
}
|
|
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" : (libraryRoot.filterSource === "favorites" ? "bookmark-new" : (libraryRoot.filterSource === "hidden" ? "view-hidden" : "applications-games"))
|
|
title: proxyModel.filterText.length > 0 ?
|
|
i18n("No games found") : (libraryRoot.filterSource === "favorites" ? i18n("No favorites yet") : (libraryRoot.filterSource === "hidden" ? i18n("No hidden games") : i18n("Your library is empty")))
|
|
description: proxyModel.filterText.length > 0 ?
|
|
i18n("Try adjusting your search") : (libraryRoot.filterSource === "favorites" ? i18n("Mark games as favorites to see them here") : (libraryRoot.filterSource === "hidden" ? i18n("Hidden games will appear here") : i18n("Import games to get started")))
|
|
|
|
actionText: (proxyModel.filterText.length > 0 || libraryRoot.filterSource === "favorites" || libraryRoot.filterSource === "hidden") ? "" : i18n("Import Games")
|
|
onActionTriggered: App.importAllGames()
|
|
}
|
|
|
|
QQC2.BusyIndicator {
|
|
anchors.centerIn: parent
|
|
running: App.importing
|
|
visible: App.importing
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|