a-la-karte/src/qml/ConsoleCategoryRail.qml

162 lines
5 KiB
QML
Raw Normal View History

// 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)
}
}
}
}
}