mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-04-28 14:43:09 +00:00
Add left-stick navigation and launch fade transition
Expose SDL_GamepadAxis as an Axis enum in GamepadManager so QML can identify axis events by name. Convert left-stick deflection into repeated grid navigation events with a 150ms interval and 0.4 deadzone. First movement fires immediately when the stick crosses the threshold. Wrap game launches in a brief fade-to-black curtain (250ms) before dismissing the overlay, giving visual feedback that the launch is in progress.
This commit is contained in:
parent
2a171f3964
commit
d58f691c7a
2 changed files with 102 additions and 12 deletions
|
|
@ -53,6 +53,17 @@ public:
|
|||
};
|
||||
Q_ENUM(Button)
|
||||
|
||||
// Axes matching SDL_GamepadAxis
|
||||
enum Axis {
|
||||
AxisLeftX,
|
||||
AxisLeftY,
|
||||
AxisRightX,
|
||||
AxisRightY,
|
||||
AxisLeftTrigger,
|
||||
AxisRightTrigger,
|
||||
};
|
||||
Q_ENUM(Axis)
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role) const override;
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
|
|
|
|||
|
|
@ -29,6 +29,16 @@ Window {
|
|||
exitGamingDialog.item.open()
|
||||
}
|
||||
|
||||
function launchGame(index) {
|
||||
GamingShell.GameLauncherProvider.launch(index)
|
||||
launchFade.restart()
|
||||
}
|
||||
|
||||
function launchGameByStorageId(storageId) {
|
||||
GamingShell.GameLauncherProvider.launchByStorageId(storageId)
|
||||
launchFade.restart()
|
||||
}
|
||||
|
||||
width: Screen.width
|
||||
height: Screen.height
|
||||
color: "transparent"
|
||||
|
|
@ -91,8 +101,7 @@ Window {
|
|||
break
|
||||
case GamingShell.GamepadManager.ButtonA:
|
||||
if (grid.activeFocus && grid.currentItem) {
|
||||
GamingShell.GameLauncherProvider.launch(grid.currentIndex)
|
||||
root.gameStarted()
|
||||
root.launchGame(grid.currentIndex)
|
||||
}
|
||||
break
|
||||
case GamingShell.GamepadManager.ButtonB:
|
||||
|
|
@ -103,6 +112,56 @@ Window {
|
|||
break
|
||||
}
|
||||
}
|
||||
|
||||
function onAxisChanged(axis, value, gamepadIndex) {
|
||||
if (axis === GamingShell.GamepadManager.AxisLeftX) {
|
||||
stickState.leftX = value
|
||||
} else if (axis === GamingShell.GamepadManager.AxisLeftY) {
|
||||
stickState.leftY = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Left-stick navigation state + repeat timer
|
||||
QtObject {
|
||||
id: stickState
|
||||
property real leftX: 0
|
||||
property real leftY: 0
|
||||
readonly property real deadzone: 0.4
|
||||
}
|
||||
|
||||
function navigateByStick() {
|
||||
if (stickState.leftY < -stickState.deadzone) {
|
||||
if (grid.activeFocus) {
|
||||
if (grid.currentIndex < grid.columns && runningGames.hasTasks) {
|
||||
runningGames.focusFirstTask()
|
||||
} else {
|
||||
grid.moveCurrentIndexUp()
|
||||
}
|
||||
}
|
||||
} else if (stickState.leftY > stickState.deadzone) {
|
||||
if (runningGames.activeFocus) {
|
||||
grid.forceActiveFocus()
|
||||
} else if (grid.activeFocus) {
|
||||
grid.moveCurrentIndexDown()
|
||||
}
|
||||
}
|
||||
if (stickState.leftX < -stickState.deadzone && grid.activeFocus) {
|
||||
grid.moveCurrentIndexLeft()
|
||||
} else if (stickState.leftX > stickState.deadzone && grid.activeFocus) {
|
||||
grid.moveCurrentIndexRight()
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: stickNavTimer
|
||||
interval: 150
|
||||
repeat: true
|
||||
running: root.visible
|
||||
&& (Math.abs(stickState.leftX) > stickState.deadzone
|
||||
|| Math.abs(stickState.leftY) > stickState.deadzone)
|
||||
onRunningChanged: if (running) root.navigateByStick()
|
||||
onTriggered: root.navigateByStick()
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
|
|
@ -219,10 +278,7 @@ Window {
|
|||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
GamingShell.GameLauncherProvider.launchByStorageId(modelData.storageId)
|
||||
root.gameStarted()
|
||||
}
|
||||
onClicked: root.launchGameByStorageId(modelData.storageId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -304,8 +360,7 @@ Window {
|
|||
|
||||
Keys.onReturnPressed: {
|
||||
if (currentIndex >= 0) {
|
||||
GamingShell.GameLauncherProvider.launch(currentIndex)
|
||||
root.gameStarted()
|
||||
root.launchGame(currentIndex)
|
||||
}
|
||||
}
|
||||
Keys.onEnterPressed: Keys.onReturnPressed(event)
|
||||
|
|
@ -427,10 +482,7 @@ Window {
|
|||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
GamingShell.GameLauncherProvider.launch(index)
|
||||
root.gameStarted()
|
||||
}
|
||||
onClicked: root.launchGame(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -481,6 +533,33 @@ Window {
|
|||
}
|
||||
}
|
||||
|
||||
// Launch transition: brief fade to black, then dismiss
|
||||
Rectangle {
|
||||
id: launchCurtain
|
||||
anchors.fill: parent
|
||||
color: "black"
|
||||
opacity: 0
|
||||
z: 100
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: 250; easing.type: Easing.InQuad }
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: launchFade
|
||||
interval: 300
|
||||
onTriggered: {
|
||||
launchCurtain.opacity = 0
|
||||
root.gameStarted()
|
||||
}
|
||||
onRunningChanged: {
|
||||
if (running) {
|
||||
launchCurtain.opacity = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: exitGamingDialog
|
||||
active: false
|
||||
|
|
|
|||
Loading…
Reference in a new issue