// // GestureMap.swift // Focale // // Author: Simon-Pierre Boucher // Contact: contact@spboucher.ai // // Fully reassignable gesture → control mapping (CLAUDE.md §6): // left vertical drag = ISO, horizontal = exposure, etc. // import Foundation /// A gesture slot on the capture screen that can drive a control. enum GestureSlot: String, Codable, Sendable, CaseIterable, Identifiable { case leftVerticalDrag case rightVerticalDrag case horizontalDrag case pinch var id: String { rawValue } /// User-facing label, in French. var displayName: String { switch self { case .leftVerticalDrag: "Glissement vertical gauche" case .rightVerticalDrag: "Glissement vertical droit" case .horizontalDrag: "Glissement horizontal" case .pinch: "Pincement" } } } struct GestureMap: Codable, Sendable { private var assignments: [GestureSlot.RawValue: CaptureControl.RawValue] init(_ assignments: [GestureSlot: CaptureControl]) { self.assignments = Dictionary( uniqueKeysWithValues: assignments.map { ($0.key.rawValue, $0.value.rawValue) } ) } func control(for slot: GestureSlot) -> CaptureControl? { assignments[slot.rawValue].flatMap(CaptureControl.init(rawValue:)) } mutating func assign(_ control: CaptureControl?, to slot: GestureSlot) { assignments[slot.rawValue] = control?.rawValue } static let standard = GestureMap([ .leftVerticalDrag: .iso, .rightVerticalDrag: .shutterSpeed, .horizontalDrag: .exposureBias, .pinch: .zoom, ]) }