mirror of
https://invent.kde.org/marcoa/a-la-karte.git
synced 2026-03-27 01:03:09 +00:00
162 lines
5 KiB
QML
162 lines
5 KiB
QML
|
|
// 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
|
||
|
|
|
||
|
|
Item {
|
||
|
|
id: root
|
||
|
|
|
||
|
|
property string currentCategory: "all"
|
||
|
|
|
||
|
|
signal categorySelected(string categoryId)
|
||
|
|
|
||
|
|
function selectNext() {
|
||
|
|
if (rail.currentIndex < categoryModel.count - 1) {
|
||
|
|
rail.currentIndex++
|
||
|
|
categorySelected(categoryModel.get(rail.currentIndex).categoryId)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function selectPrevious() {
|
||
|
|
if (rail.currentIndex > 0) {
|
||
|
|
rail.currentIndex--
|
||
|
|
categorySelected(categoryModel.get(rail.currentIndex).categoryId)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function _platformIcon(name) {
|
||
|
|
let p = (name || "").toLowerCase()
|
||
|
|
if (p.includes("steam")) return ":/icons/brand/steam-symbolic.svg"
|
||
|
|
if (p.includes("itch")) return ":/icons/brand/itchdotio-symbolic.svg"
|
||
|
|
if (p.includes("retroarch")) return ":/icons/brand/retroarch-symbolic.svg"
|
||
|
|
if (p.includes("lutris")) return "applications-games"
|
||
|
|
if (p.includes("heroic")) return "applications-games"
|
||
|
|
if (p.includes("bottles")) return "application-x-executable"
|
||
|
|
if (p.includes("flatpak")) return "applications-games"
|
||
|
|
if (p.includes("desktop")) return "computer"
|
||
|
|
return "applications-games"
|
||
|
|
}
|
||
|
|
|
||
|
|
GameSortFilterModel {
|
||
|
|
id: allGames
|
||
|
|
sourceModel: App.gameModel
|
||
|
|
showHidden: false
|
||
|
|
}
|
||
|
|
|
||
|
|
ListModel {
|
||
|
|
id: categoryModel
|
||
|
|
|
||
|
|
Component.onCompleted: rebuild()
|
||
|
|
|
||
|
|
function rebuild() {
|
||
|
|
clear()
|
||
|
|
append({ label: i18n("All"), categoryId: "all", iconSource: "view-list-icons" })
|
||
|
|
append({ label: i18n("Favorites"), categoryId: "favorites", iconSource: "starred-symbolic" })
|
||
|
|
|
||
|
|
let seen = {}
|
||
|
|
for (let i = 0; i < allGames.count; ++i) {
|
||
|
|
let g = allGames.get(i)
|
||
|
|
if (!g) continue
|
||
|
|
let p = g.platform || ""
|
||
|
|
if (p && !seen[p]) {
|
||
|
|
seen[p] = true
|
||
|
|
append({ label: p, categoryId: p, iconSource: root._platformIcon(p) })
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
for (let j = 0; j < count; ++j) {
|
||
|
|
if (get(j).categoryId === root.currentCategory) {
|
||
|
|
rail.currentIndex = j
|
||
|
|
break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Connections {
|
||
|
|
target: App.gameModel
|
||
|
|
function onCountChanged() { categoryModel.rebuild() }
|
||
|
|
}
|
||
|
|
|
||
|
|
onCurrentCategoryChanged: {
|
||
|
|
for (let j = 0; j < categoryModel.count; ++j) {
|
||
|
|
if (categoryModel.get(j).categoryId === currentCategory) {
|
||
|
|
rail.currentIndex = j
|
||
|
|
return
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Rectangle {
|
||
|
|
anchors.fill: parent
|
||
|
|
color: Qt.rgba(0, 0, 0, 0.55)
|
||
|
|
|
||
|
|
ListView {
|
||
|
|
id: rail
|
||
|
|
anchors {
|
||
|
|
fill: parent
|
||
|
|
leftMargin: 24
|
||
|
|
rightMargin: 24
|
||
|
|
}
|
||
|
|
orientation: ListView.Horizontal
|
||
|
|
spacing: 8
|
||
|
|
clip: true
|
||
|
|
model: categoryModel
|
||
|
|
keyNavigationEnabled: true
|
||
|
|
highlightMoveDuration: 200
|
||
|
|
|
||
|
|
delegate: QQC2.ItemDelegate {
|
||
|
|
id: pill
|
||
|
|
width: implicitContentWidth + 32
|
||
|
|
height: rail.height
|
||
|
|
topPadding: 0
|
||
|
|
bottomPadding: 0
|
||
|
|
leftPadding: 16
|
||
|
|
rightPadding: 16
|
||
|
|
|
||
|
|
readonly property bool isCurrent: ListView.isCurrentItem
|
||
|
|
|
||
|
|
background: Rectangle {
|
||
|
|
radius: height / 2
|
||
|
|
color: pill.isCurrent
|
||
|
|
? Maui.Theme.highlightColor
|
||
|
|
: (pill.hovered ? Qt.rgba(1, 1, 1, 0.12) : Qt.rgba(1, 1, 1, 0.06))
|
||
|
|
border.color: pill.isCurrent ? Maui.Theme.highlightColor : Qt.rgba(1, 1, 1, 0.18)
|
||
|
|
border.width: 1
|
||
|
|
|
||
|
|
Behavior on color { ColorAnimation { duration: 120 } }
|
||
|
|
}
|
||
|
|
|
||
|
|
contentItem: RowLayout {
|
||
|
|
spacing: 6
|
||
|
|
|
||
|
|
Maui.Icon {
|
||
|
|
source: model.iconSource
|
||
|
|
Layout.preferredWidth: 16
|
||
|
|
Layout.preferredHeight: 16
|
||
|
|
color: "white"
|
||
|
|
opacity: pill.isCurrent ? 1.0 : 0.75
|
||
|
|
}
|
||
|
|
|
||
|
|
QQC2.Label {
|
||
|
|
text: model.label
|
||
|
|
color: "white"
|
||
|
|
opacity: pill.isCurrent ? 1.0 : 0.75
|
||
|
|
font.pixelSize: 14
|
||
|
|
font.weight: pill.isCurrent ? Font.DemiBold : Font.Normal
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
onClicked: {
|
||
|
|
rail.currentIndex = index
|
||
|
|
root.categorySelected(model.categoryId)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|