SPB Git

spb/focale Public

Swift 100%
3.2 KB · 111 lines swift
Raw Blame History
1//2//  ControlLayout.swift3//  Focale4//5//  Author: Simon-Pierre Boucher6//  Contact: contact@spboucher.ai7//8//  User-placeable capture interface (CLAUDE.md §6): which controls are9//  visible, in what order, and how haptics behave. Three provided10//  profiles — Simple / Photographe / Expert — all editable.11//1213import Foundation1415/// A manual control the user can place in the capture interface.16enum CaptureControl: String, Codable, Sendable, CaseIterable, Identifiable {17    case iso18    case shutterSpeed19    case exposureBias20    case focus21    case whiteBalance22    case zoom2324    var id: String { rawValue }2526    /// User-facing label, in French.27    var displayName: String {28        switch self {29        case .iso: "ISO"30        case .shutterSpeed: "Vitesse"31        case .exposureBias: "Expo"32        case .focus: "Mise au point"33        case .whiteBalance: "Balance"34        case .zoom: "Zoom"35        }36    }3738    var symbolName: String {39        switch self {40        case .iso: "camera.aperture"41        case .shutterSpeed: "timer"42        case .exposureBias: "plusminus.circle"43        case .focus: "scope"44        case .whiteBalance: "thermometer.sun"45        case .zoom: "plus.magnifyingglass"46        }47    }48}4950/// Per-control haptic configuration (e.g. a detent every third of a stop).51struct HapticProfile: Codable, Sendable {52    var enabled: Bool53    /// Number of detents across the control's full range. 0 = continuous.54    var detentCount: Int5556    static let subtle = HapticProfile(enabled: true, detentCount: 0)57    static let thirdStops = HapticProfile(enabled: true, detentCount: 18)58    static let off = HapticProfile(enabled: false, detentCount: 0)59}6061struct ControlLayout: Codable, Sendable, Identifiable {62    var id: UUID63    var name: String64    /// Controls shown in the dial strip, in order.65    var visibleControls: [CaptureControl]66    var haptics: [CaptureControl.RawValue: HapticProfile]67    var gestureMap: GestureMap6869    init(70        id: UUID = UUID(),71        name: String,72        visibleControls: [CaptureControl],73        haptics: [CaptureControl.RawValue: HapticProfile] = [:],74        gestureMap: GestureMap = .standard75    ) {76        self.id = id77        self.name = name78        self.visibleControls = visibleControls79        self.haptics = haptics80        self.gestureMap = gestureMap81    }8283    func hapticProfile(for control: CaptureControl) -> HapticProfile {84        haptics[control.rawValue] ?? .subtle85    }8687    // MARK: - Provided profiles (all editable)8889    static let simple = ControlLayout(90        name: "Simple",91        visibleControls: [.exposureBias, .zoom]92    )9394    static let photographe = ControlLayout(95        name: "Photographe",96        visibleControls: [.iso, .shutterSpeed, .exposureBias, .zoom],97        haptics: [CaptureControl.iso.rawValue: .thirdStops,98                  CaptureControl.shutterSpeed.rawValue: .thirdStops]99    )100101    static let expert = ControlLayout(102        name: "Expert",103        visibleControls: CaptureControl.allCases,104        haptics: [CaptureControl.iso.rawValue: .thirdStops,105                  CaptureControl.shutterSpeed.rawValue: .thirdStops,106                  CaptureControl.focus.rawValue: .subtle]107    )108109    static let provided: [ControlLayout] = [.simple, .photographe, .expert]110}111