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

134 lines
3.6 KiB
QML

import QtQuick
import QtQuick.Layouts
import Nebula.Bigscreen
ColumnLayout {
id: root
signal goBack()
readonly property var categories: [
"Network",
"Audio",
"Display",
"Controller",
"System"
]
property int categoryIndex: 0
property int itemIndex: 0
readonly property int settingCount: 3
property string statusText: "Choose a setting with Accept. Detailed controls come later."
spacing: 20
Text {
text: "Settings"
font: Theme.brandFont
color: Theme.textPrimary
Layout.leftMargin: 40
}
Row {
Layout.leftMargin: 40
spacing: 16
Repeater {
model: root.categories
Rectangle {
height: 40
width: categoryLabel.implicitWidth + 24
radius: 8
color: index === root.categoryIndex ? "#22304A66" : "transparent"
border.color: index === root.categoryIndex ? Theme.accentCyan : "transparent"
border.width: index === root.categoryIndex ? 2 : 0
Text {
id: categoryLabel
anchors.centerIn: parent
text: modelData
font: Theme.bodyFont
color: index === root.categoryIndex ? Theme.accentCyan : Theme.textMuted
}
}
}
}
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.margins: 40
radius: 16
color: Theme.backgroundPanel
border.color: Theme.accentLine
ColumnLayout {
anchors.fill: parent
anchors.margins: 24
spacing: 12
Text {
text: root.categories[root.categoryIndex]
font: Theme.titleFont
color: Theme.textPrimary
}
Repeater {
model: root.settingCount
Rectangle {
Layout.fillWidth: true
height: 52
radius: 10
color: index === root.itemIndex ? "#22304A66" : Theme.backgroundMid
border.color: index === root.itemIndex ? Theme.accentCyan : Theme.accentLine
border.width: index === root.itemIndex ? 2 : 1
Text {
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 16
text: "Placeholder setting " + (index + 1)
font: Theme.bodyFont
color: Theme.textPrimary
}
}
}
Text {
text: root.statusText
font: Theme.metaFont
color: Theme.textMuted
}
}
}
function handleInput(action) {
switch (action) {
case 3:
categoryIndex = Math.max(0, categoryIndex - 1)
itemIndex = 0
break
case 4:
categoryIndex = Math.min(categories.length - 1, categoryIndex + 1)
itemIndex = 0
break
case 1:
itemIndex = Math.max(0, itemIndex - 1)
break
case 2:
itemIndex = Math.min(settingCount - 1, itemIndex + 1)
break
case 6:
root.goBack()
break
case 5:
statusText = categories[categoryIndex] + " setting " + (itemIndex + 1) + " selected."
break
default:
break
}
}
}