mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-04-29 15:03: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)
|
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;
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
QVariant data(const QModelIndex &index, int role) const override;
|
QVariant data(const QModelIndex &index, int role) const override;
|
||||||
QHash<int, QByteArray> roleNames() const override;
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,16 @@ Window {
|
||||||
exitGamingDialog.item.open()
|
exitGamingDialog.item.open()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function launchGame(index) {
|
||||||
|
GamingShell.GameLauncherProvider.launch(index)
|
||||||
|
launchFade.restart()
|
||||||
|
}
|
||||||
|
|
||||||
|
function launchGameByStorageId(storageId) {
|
||||||
|
GamingShell.GameLauncherProvider.launchByStorageId(storageId)
|
||||||
|
launchFade.restart()
|
||||||
|
}
|
||||||
|
|
||||||
width: Screen.width
|
width: Screen.width
|
||||||
height: Screen.height
|
height: Screen.height
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
|
|
@ -91,8 +101,7 @@ Window {
|
||||||
break
|
break
|
||||||
case GamingShell.GamepadManager.ButtonA:
|
case GamingShell.GamepadManager.ButtonA:
|
||||||
if (grid.activeFocus && grid.currentItem) {
|
if (grid.activeFocus && grid.currentItem) {
|
||||||
GamingShell.GameLauncherProvider.launch(grid.currentIndex)
|
root.launchGame(grid.currentIndex)
|
||||||
root.gameStarted()
|
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case GamingShell.GamepadManager.ButtonB:
|
case GamingShell.GamepadManager.ButtonB:
|
||||||
|
|
@ -103,6 +112,56 @@ Window {
|
||||||
break
|
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 {
|
Rectangle {
|
||||||
|
|
@ -219,10 +278,7 @@ Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: {
|
onClicked: root.launchGameByStorageId(modelData.storageId)
|
||||||
GamingShell.GameLauncherProvider.launchByStorageId(modelData.storageId)
|
|
||||||
root.gameStarted()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -304,8 +360,7 @@ Window {
|
||||||
|
|
||||||
Keys.onReturnPressed: {
|
Keys.onReturnPressed: {
|
||||||
if (currentIndex >= 0) {
|
if (currentIndex >= 0) {
|
||||||
GamingShell.GameLauncherProvider.launch(currentIndex)
|
root.launchGame(currentIndex)
|
||||||
root.gameStarted()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Keys.onEnterPressed: Keys.onReturnPressed(event)
|
Keys.onEnterPressed: Keys.onReturnPressed(event)
|
||||||
|
|
@ -427,10 +482,7 @@ Window {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onClicked: {
|
onClicked: root.launchGame(index)
|
||||||
GamingShell.GameLauncherProvider.launch(index)
|
|
||||||
root.gameStarted()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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 {
|
Loader {
|
||||||
id: exitGamingDialog
|
id: exitGamingDialog
|
||||||
active: false
|
active: false
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue