From ccb7fcaff2f1d3a04961d29c5c11e3fdf6c39b57 Mon Sep 17 00:00:00 2001 From: Kyle Date: Sat, 10 Oct 2026 00:18:32 +0800 Subject: [PATCH] feat(gesture): implement physical button press gesture --- .../Gesture/PhysicalButtonPressGesture.swift | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 Sources/OpenSwiftUI/Event/Gesture/PhysicalButtonPressGesture.swift diff --git a/Sources/OpenSwiftUI/Event/Gesture/PhysicalButtonPressGesture.swift b/Sources/OpenSwiftUI/Event/Gesture/PhysicalButtonPressGesture.swift new file mode 100644 index 000000000..d4420a8bf --- /dev/null +++ b/Sources/OpenSwiftUI/Event/Gesture/PhysicalButtonPressGesture.swift @@ -0,0 +1,93 @@ +// +// PhysicalButtonPressGesture.swift +// OpenSwiftUI +// +// Audited for 6.5.4 +// Status: Complete +// ID: 239A42CFB2879B10E971E788DAD9A1E6 (SwiftUI) + +#if os(iOS) || os(visionOS) + +@_spi(ForOpenSwiftUIOnly) +import OpenSwiftUICore + +// MARK: - PhysicalButtonPressGesture + +struct PhysicalButtonPressGesture: Gesture { + var allowedButtonTypes: AllowedButtonTypes + + var body: some Gesture { + EventListener() + .mapPhase { phase -> GesturePhase in + switch phase { + case .possible: + .possible(nil) + case let .active(event): + allowedButtonTypes.contains(event.type) ? .active(event.type) : .possible(nil) + case let .ended(event): + allowedButtonTypes.contains(event.type) ? .ended(event.type) : .failed + case .failed: + .failed + } + } + .dependency(.failIfActive) + } +} + +// MARK: - PhysicalButtonPressGesture.AllowedButtonTypes + +extension PhysicalButtonPressGesture { + struct AllowedButtonTypes: OptionSet { + typealias Element = PhysicalButtonEvent.ButtonType + + let rawValue: Int + + init(rawValue: Int) { + self.rawValue = rawValue + } + + private init(_ button: Element) { + rawValue = switch button { + case .upArrow: 1 << 0 + case .downArrow: 1 << 1 + case .leftArrow: 1 << 2 + case .rightArrow: 1 << 3 + case .select: 1 << 4 + case .menu: 1 << 5 + case .playPause: 1 << 6 + case .pageUp: 1 << 7 + case .pageDown: 1 << 8 + case .back: 1 << 9 + } + } + + func contains(_ member: Element) -> Bool { + rawValue & Self(member).rawValue != 0 + } + + @discardableResult + mutating func insert(_ newMember: Element) -> (inserted: Bool, memberAfterInsert: Element) { + let inserted = !contains(newMember) + if inserted { + formUnion(Self(newMember)) + } + return (inserted, newMember) + } + + @discardableResult + mutating func remove(_ member: Element) -> Element? { + let removed = contains(member) ? member : nil + subtract(Self(member)) + return removed + } + + @discardableResult + mutating func update(with newMember: Element) -> Element? { + let oldMember = contains(newMember) ? newMember : nil + formUnion(Self(newMember)) + return oldMember + } + } +} + +#endif