Files
andrew 61c448eb00 Add SDL3-based controller input service
Integrate SDL3 controller support and wire it into the InputRouter.

- Add ControllerInputService (src/ControllerInputService.{h,cpp}) to discover, poll and translate SDL3 gamepad events into InputRouter actions, with axis repeat handling and debouncing.
- Update CMakeLists to find or fetch SDL3, add the new source files to the target, link the SDL3 target, and copy runtime DLLs on Windows.
- Add triggerAction(Action) to InputRouter and use it from existing keyboard handling to centralize action dispatch.
- Instantiate ControllerInputService in main so controllers feed the InputRouter.
- Update QML views (ShellWindow, HomeView, LibraryView, SettingsView) to use numeric action IDs and add small UI/status text and navigation tweaks for controller-driven flows.

These changes enable gamepad/controller input for Bigscreen via SDL3 and adapt UI code to handle the mapped actions.
2026-05-23 21:47:15 +12:00

141 lines
3.1 KiB
QML

import QtQuick
import QtQuick.Controls
import Nebula.Bigscreen
ApplicationWindow {
id: window
width: 1280
height: 720
visible: true
title: "Nebula Bigscreen"
color: Theme.backgroundDeep
property string currentView: "home"
property bool powerOverlayVisible: false
property string signedInUser: "Player"
NebulaBackground {
anchors.fill: parent
}
Column {
anchors.fill: parent
spacing: 0
TopBar {
id: topBar
width: parent.width
userName: window.signedInUser
accentFocus: contentLoader.item && contentLoader.item.accentFocus !== undefined
? contentLoader.item.accentFocus()
: 0
}
Loader {
id: contentLoader
width: parent.width
height: parent.height - topBar.height
sourceComponent: viewComponentFor(window.currentView)
}
}
Connections {
target: contentLoader.item
ignoreUnknownSignals: true
function onNavigate(route) {
window.onChildNavigate(route)
}
function onGoBack() {
window.onChildGoBack()
}
}
PowerOverlay {
id: powerOverlay
visible: window.powerOverlayVisible
z: 10
onDismissed: window.powerOverlayVisible = false
onActionChosen: function(actionId) {
console.log("Power action:", actionId)
window.powerOverlayVisible = false
}
}
Connections {
target: InputRouter
function onActionTriggered(action) {
if (window.powerOverlayVisible) {
window.handlePowerInput(action)
return
}
if (action === 7) {
window.powerOverlayVisible = true
return
}
if (contentLoader.item && contentLoader.item.handleInput) {
contentLoader.item.handleInput(action)
}
}
}
Component {
id: homeComponent
HomeView {}
}
Component {
id: libraryComponent
LibraryView {}
}
Component {
id: settingsComponent
SettingsView {}
}
function viewComponentFor(viewId) {
switch (viewId) {
case "library":
return libraryComponent
case "settings":
return settingsComponent
default:
return homeComponent
}
}
function onChildNavigate(route) {
if (route === "power") {
window.powerOverlayVisible = true
return
}
window.currentView = route
}
function onChildGoBack() {
window.currentView = "home"
}
function handlePowerInput(action) {
switch (action) {
case 1:
powerOverlay.moveFocus(-1)
break
case 2:
powerOverlay.moveFocus(1)
break
case 5:
powerOverlay.activateFocused()
break
case 6:
window.powerOverlayVisible = false
break
default:
break
}
}
}