fix: square, grid-aligned D-pad and face buttons

Give both clusters a shared 100x100 footprint so they align on the
same grid: D-pad cells are now square (30x30 with 5pt gaps) and the
ABXY diamond is positioned on its diagonals instead of stacked rows.
Drops the height-matching frame hack from the card layout.

Co-Authored-By: qwen3.8-27b@q3_k_xl: squared and aligned the clusters
This commit is contained in:
portersky
2026-08-29 15:04:46 +02:00
parent 33356afe47
commit 823b4f22d2
3 changed files with 25 additions and 22 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ if(NOT CMAKE_GENERATOR MATCHES "^(Ninja|Xcode)$")
endif() endif()
cmake_minimum_required(VERSION 3.21) cmake_minimum_required(VERSION 3.21)
project(xone_macos VERSION 0.1.23 LANGUAGES CXX Swift) project(xone_macos VERSION 0.1.24 LANGUAGES CXX Swift)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
-2
View File
@@ -63,7 +63,6 @@ struct ControllerCardView: View {
StickPadView(label: "L", x: controller.stickLeftX, y: controller.stickLeftY, StickPadView(label: "L", x: controller.stickLeftX, y: controller.stickLeftY,
pressed: controller.isPressed(.leftStick)) pressed: controller.isPressed(.leftStick))
DPadView(controller: controller) DPadView(controller: controller)
.frame(height: 104)
} }
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
@@ -79,7 +78,6 @@ struct ControllerCardView: View {
StickPadView(label: "R", x: controller.stickRightX, y: controller.stickRightY, StickPadView(label: "R", x: controller.stickRightX, y: controller.stickRightY,
pressed: controller.isPressed(.rightStick)) pressed: controller.isPressed(.rightStick))
FaceButtonsView(controller: controller) FaceButtonsView(controller: controller)
.frame(height: 104)
} }
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
} }
+24 -19
View File
@@ -4,28 +4,34 @@ import SwiftUI
// buttons on top, stick pads and triggers below, mirroring the physical // buttons on top, stick pads and triggers below, mirroring the physical
// layout of an Xbox controller. // layout of an Xbox controller.
// Both clusters (D-pad and face buttons) share a 100x100 footprint so they
// align on the same grid in the card layout.
struct DPadView: View { struct DPadView: View {
let controller: ControllerInput let controller: ControllerInput
private let cell: CGFloat = 30
private let gap: CGFloat = 5
var body: some View { var body: some View {
VStack(spacing: 3) { VStack(spacing: gap) {
cell(.dPadUp) cellView(.dPadUp)
HStack(spacing: 3) { HStack(spacing: gap) {
cell(.dPadLeft) cellView(.dPadLeft)
RoundedRectangle(cornerRadius: 5) RoundedRectangle(cornerRadius: 6)
.fill(Color.secondary.opacity(0.28)) .fill(Color.secondary.opacity(0.28))
.frame(width: 30, height: 26) .frame(width: cell, height: cell)
cell(.dPadRight) cellView(.dPadRight)
} }
cell(.dPadDown) cellView(.dPadDown)
} }
} }
private func cell(_ button: XboxButton) -> some View { private func cellView(_ button: XboxButton) -> some View {
let pressed = controller.isPressed(button) let pressed = controller.isPressed(button)
return RoundedRectangle(cornerRadius: 5) return RoundedRectangle(cornerRadius: 6)
.fill(pressed ? Color.green : Color.secondary.opacity(0.18)) .fill(pressed ? Color.green : Color.secondary.opacity(0.18))
.frame(width: 30, height: 26) .frame(width: cell, height: cell)
.overlay( .overlay(
Image(systemName: button.dPadSymbol ?? "") Image(systemName: button.dPadSymbol ?? "")
.font(.system(size: 9)) .font(.system(size: 9))
@@ -38,21 +44,20 @@ struct FaceButtonsView: View {
let controller: ControllerInput let controller: ControllerInput
var body: some View { var body: some View {
VStack(spacing: 4) { ZStack {
faceButton(.y) faceButton(.y).offset(y: -35)
HStack(spacing: 10) { faceButton(.x).offset(x: -35)
faceButton(.x) faceButton(.b).offset(x: 35)
faceButton(.b) faceButton(.a).offset(y: 35)
}
faceButton(.a)
} }
.frame(width: 100, height: 100)
} }
private func faceButton(_ button: XboxButton) -> some View { private func faceButton(_ button: XboxButton) -> some View {
let pressed = controller.isPressed(button) let pressed = controller.isPressed(button)
return Text(button.label) return Text(button.label)
.font(.callout.weight(.semibold)) .font(.callout.weight(.semibold))
.frame(width: 32, height: 32) .frame(width: 30, height: 30)
.background(Circle().fill(pressed ? Color.green : Color.secondary.opacity(0.18))) .background(Circle().fill(pressed ? Color.green : Color.secondary.opacity(0.18)))
.foregroundStyle(pressed ? .white : .primary) .foregroundStyle(pressed ? .white : .primary)
} }