Rework Game Center into console-style capsule rails

Restructure the overlay around landscape 16:9 media capsules, clearer focus borders, tighter search/filter rails, and stronger handheld vs big-screen spacing hierarchy.
This commit is contained in:
Marco Allegretti 2026-06-01 15:49:44 +02:00
parent 434f46403c
commit 75a0f7a21e

View file

@ -41,6 +41,15 @@ Window {
readonly property int shortAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsFast)
readonly property int longAnimationDuration: MobileShell.Motion.duration(MobileShell.Motion.EffectsDefault)
readonly property int launchFadeDuration: MobileShell.Motion.duration(MobileShell.Motion.StandardAccel)
readonly property int shortestSide: Math.min(width, height)
readonly property bool compactMode: !ShellSettings.Settings.convergenceModeEnabled && shortestSide <= Kirigami.Units.gridUnit * 50
readonly property bool bigScreenMode: !compactMode
readonly property int horizontalPadding: compactMode ? Kirigami.Units.largeSpacing : Kirigami.Units.largeSpacing * 2
readonly property int verticalPadding: compactMode ? Kirigami.Units.largeSpacing : Kirigami.Units.largeSpacing * 2
readonly property real gridMinCellSize: compactMode ? Kirigami.Units.gridUnit * 6.8 : Kirigami.Units.gridUnit * 8.8
// Steam library assets heavily favor wide capsules and 16:9 media surfaces.
// Keep game tiles landscape-first to avoid mobile-style portrait cards.
readonly property real capsuleArtAspect: 16 / 9
function controlLegendText() {
if (GamingShell.GamepadManager.hasGamepad) {
@ -508,7 +517,7 @@ Window {
anchors.fill: parent
Kirigami.Theme.inherit: false
Kirigami.Theme.colorSet: Kirigami.Theme.Window
color: MobileShell.SurfaceColors.withAlpha(MobileShell.SurfaceColors.accentSurface(Kirigami.Theme.backgroundColor, 0.24, 0.12), 0.92)
color: MobileShell.SurfaceColors.withAlpha(MobileShell.SurfaceColors.accentSurface(Kirigami.Theme.backgroundColor, 0.24, 0.12), root.bigScreenMode ? 0.94 : 0.9)
}
FocusScope {
@ -521,8 +530,11 @@ Window {
ColumnLayout {
anchors.fill: parent
anchors.margins: Kirigami.Units.largeSpacing * 2
spacing: Kirigami.Units.largeSpacing
anchors.leftMargin: root.horizontalPadding
anchors.rightMargin: root.horizontalPadding
anchors.topMargin: root.verticalPadding
anchors.bottomMargin: root.verticalPadding
spacing: root.compactMode ? Kirigami.Units.smallSpacing : Kirigami.Units.largeSpacing
// ---- header ----
RowLayout {
@ -531,7 +543,7 @@ Window {
Kirigami.Heading {
text: i18n("Game Center")
level: 1
level: root.compactMode ? 2 : 1
}
Item { Layout.fillWidth: true }
@ -586,7 +598,7 @@ Window {
QQC2.ToolButton {
icon.name: "window-close"
text: i18n("Exit Gaming Mode")
text: root.compactMode ? i18n("Exit") : i18n("Exit Gaming Mode")
display: QQC2.AbstractButton.TextBesideIcon
Keys.onReturnPressed: clicked()
Keys.onEnterPressed: clicked()
@ -598,6 +610,7 @@ Window {
RunningGamesView {
id: runningGames
Layout.fillWidth: true
compactMode: root.compactMode
onTaskActivated: {
GamingShell.GameLauncherProvider.clearPendingLaunch()
root.gameStarted()
@ -652,7 +665,9 @@ Window {
ListView {
id: recentList
Layout.fillWidth: true
Layout.preferredHeight: Kirigami.Units.gridUnit * 5
readonly property int cardWidth: root.compactMode ? Kirigami.Units.gridUnit * 8 : Kirigami.Units.gridUnit * 10
readonly property int artHeight: Math.round(cardWidth / root.capsuleArtAspect)
Layout.preferredHeight: artHeight + Kirigami.Units.gridUnit * 1.7
orientation: ListView.Horizontal
spacing: Kirigami.Units.largeSpacing
clip: true
@ -690,7 +705,7 @@ Window {
Keys.onDownPressed: grid.forceActiveFocus()
delegate: QQC2.ItemDelegate {
width: Kirigami.Units.gridUnit * 7
width: recentList.cardWidth
height: recentList.height
required property var modelData
@ -704,16 +719,26 @@ Window {
background: Rectangle {
radius: Kirigami.Units.cornerRadius
color: parent.isCurrent
? Kirigami.Theme.highlightColor
: (parent.hovered ? Kirigami.Theme.hoverColor : "transparent")
? Qt.rgba(Kirigami.Theme.highlightColor.r, Kirigami.Theme.highlightColor.g, Kirigami.Theme.highlightColor.b, 0.22)
: (parent.hovered
? Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.08)
: "transparent")
border.color: parent.isCurrent ? Kirigami.Theme.highlightColor : "transparent"
border.width: parent.isCurrent ? 2 : 0
}
contentItem: ColumnLayout {
spacing: Kirigami.Units.smallSpacing
Image {
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredHeight: recentList.artHeight
radius: Kirigami.Units.cornerRadius
clip: true
color: Qt.rgba(Kirigami.Theme.alternateBackgroundColor.r, Kirigami.Theme.alternateBackgroundColor.g, Kirigami.Theme.alternateBackgroundColor.b, 0.8)
Image {
anchors.fill: parent
source: hasArt ? "file://" + modelData.artwork : ""
fillMode: Image.PreserveAspectCrop
visible: hasArt
@ -721,19 +746,20 @@ Window {
}
Kirigami.Icon {
Layout.alignment: Qt.AlignHCenter
anchors.centerIn: parent
implicitWidth: Kirigami.Units.iconSizes.large
implicitHeight: Kirigami.Units.iconSizes.large
source: modelData.icon
visible: !hasArt
}
}
PC3.Label {
Layout.fillWidth: true
text: modelData.name
maximumLineCount: 1
elide: Text.ElideRight
horizontalAlignment: Text.AlignHCenter
horizontalAlignment: Text.AlignLeft
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.85
color: parent.parent.isCurrent
? Kirigami.Theme.highlightedTextColor
@ -761,9 +787,14 @@ Window {
}
// ---- search + filter ----
RowLayout {
Item {
Layout.fillWidth: true
spacing: Kirigami.Units.largeSpacing
implicitHeight: searchFilterStack.implicitHeight
ColumnLayout {
id: searchFilterStack
anchors.fill: parent
spacing: Kirigami.Units.smallSpacing
Kirigami.SearchField {
id: searchField
@ -783,6 +814,7 @@ Window {
QQC2.TabBar {
id: sourceFilterBar
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
Repeater {
@ -808,6 +840,7 @@ Window {
}
}
}
}
// ---- game grid ----
@ -819,11 +852,12 @@ Window {
model: GamingShell.GameLauncherProvider
readonly property real minCellSize: Kirigami.Units.gridUnit * 8
readonly property real minCellSize: root.gridMinCellSize
readonly property int columns: Math.max(2, Math.floor(width / minCellSize))
cellWidth: Math.floor(width / columns)
cellHeight: Math.floor(cellWidth * 1.5) + Kirigami.Units.gridUnit * 2
readonly property int artHeight: Math.round(cellWidth / root.capsuleArtAspect)
cellHeight: artHeight + (root.compactMode ? Kirigami.Units.gridUnit * 1.9 : Kirigami.Units.gridUnit * 2.2)
keyNavigationEnabled: true
highlightMoveDuration: 0
@ -898,7 +932,7 @@ Window {
QQC2.ItemDelegate {
anchors.fill: parent
anchors.margins: Kirigami.Units.smallSpacing
anchors.margins: root.compactMode ? 0 : Kirigami.Units.smallSpacing
padding: 0
readonly property bool isCurrent: GridView.isCurrentItem && grid.activeFocus
@ -906,24 +940,30 @@ Window {
background: Rectangle {
Kirigami.Theme.colorSet: Kirigami.Theme.Button
color: parent.isCurrent
? Kirigami.Theme.highlightColor
: (parent.hovered ? Kirigami.Theme.hoverColor : "transparent")
? Qt.rgba(Kirigami.Theme.highlightColor.r, Kirigami.Theme.highlightColor.g,
Kirigami.Theme.highlightColor.b, 0.22)
: (parent.hovered
? Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g,
Kirigami.Theme.textColor.b, 0.08)
: "transparent")
radius: Kirigami.Units.cornerRadius
border.color: parent.isCurrent ? Kirigami.Theme.highlightColor : "transparent"
border.width: parent.isCurrent ? 2 : 0
}
contentItem: Item {
// ---- cover art tile ----
ColumnLayout {
anchors.fill: parent
spacing: 0
visible: hasArt
spacing: Kirigami.Units.smallSpacing
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredHeight: grid.artHeight
radius: Kirigami.Units.cornerRadius
clip: true
color: "transparent"
color: Qt.rgba(Kirigami.Theme.alternateBackgroundColor.r,
Kirigami.Theme.alternateBackgroundColor.g,
Kirigami.Theme.alternateBackgroundColor.b, 0.85)
Image {
anchors.fill: parent
@ -931,6 +971,15 @@ Window {
fillMode: Image.PreserveAspectCrop
smooth: true
asynchronous: true
visible: hasArt
}
Kirigami.Icon {
anchors.centerIn: parent
implicitWidth: root.compactMode ? Kirigami.Units.iconSizes.large : Kirigami.Units.iconSizes.huge
implicitHeight: implicitWidth
source: icon
visible: !hasArt
}
Rectangle {
@ -954,76 +1003,35 @@ Window {
}
}
// Title beneath artwork
ColumnLayout {
Layout.fillWidth: true
spacing: 0
PC3.Label {
Layout.fillWidth: true
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
text: name
maximumLineCount: 1
elide: Text.ElideRight
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignLeft
leftPadding: Kirigami.Units.smallSpacing
rightPadding: Kirigami.Units.smallSpacing
color: parent.parent.parent.isCurrent
? Kirigami.Theme.highlightedTextColor
: Kirigami.Theme.textColor
}
}
// ---- fallback icon tile ----
ColumnLayout {
anchors.fill: parent
anchors.margins: Kirigami.Units.smallSpacing
visible: !hasArt
spacing: Kirigami.Units.smallSpacing
Item { Layout.fillHeight: true }
Kirigami.Icon {
Layout.alignment: Qt.AlignHCenter
implicitWidth: Kirigami.Units.iconSizes.huge
implicitHeight: Kirigami.Units.iconSizes.huge
source: icon
scale: parent.parent.parent.isCurrent ? 1.08 : 1.0
Behavior on scale {
MobileShell.MotionNumberAnimation { type: MobileShell.Motion.EffectsFast; duration: root.shortAnimationDuration }
}
}
PC3.Label {
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
text: name
maximumLineCount: 2
wrapMode: Text.Wrap
horizontalAlignment: Text.AlignHCenter
visible: lastPlayedText.length > 0
text: lastPlayedText
maximumLineCount: 1
elide: Text.ElideRight
color: parent.parent.parent.isCurrent
? Kirigami.Theme.highlightedTextColor
: Kirigami.Theme.textColor
}
Rectangle {
Layout.alignment: Qt.AlignHCenter
visible: source !== "desktop"
radius: height / 2
color: root.sourceChipColor(source)
implicitHeight: sourceChipLabel.implicitHeight + Kirigami.Units.smallSpacing
implicitWidth: sourceChipLabel.implicitWidth + Kirigami.Units.largeSpacing
PC3.Label {
id: sourceChipLabel
anchors.centerIn: parent
text: root.sourceLabel(source)
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.72
font.weight: Font.DemiBold
color: "white"
leftPadding: Kirigami.Units.smallSpacing
rightPadding: Kirigami.Units.smallSpacing
opacity: 0.65
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.78
}
}
Item { Layout.fillHeight: true }
}
}
@ -1074,6 +1082,7 @@ Window {
Repeater {
model: GamingShell.GamepadManager
visible: root.bigScreenMode
RowLayout {
spacing: Kirigami.Units.smallSpacing
@ -1113,7 +1122,7 @@ Window {
PC3.Label {
Layout.fillWidth: true
text: root.controlLegendText()
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.75
font.pointSize: Kirigami.Theme.defaultFont.pointSize * (root.compactMode ? 0.7 : 0.75)
opacity: 0.5
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignRight
@ -1126,6 +1135,7 @@ Window {
GamingQuickSettings {
id: quickSettings
z: 50
compactMode: root.compactMode
}
// Launch transition: brief fade to black, then dismiss