diff --git a/LiveActivityWidget/Info.plist b/LiveActivityWidget/Info.plist index 2bd5c9b..bca3042 100644 --- a/LiveActivityWidget/Info.plist +++ b/LiveActivityWidget/Info.plist @@ -17,7 +17,7 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 1.12 + 1.13 CFBundleVersion 1 NSExtension diff --git a/MacApp/Info.plist b/MacApp/Info.plist index 3457114..6d4f196 100644 --- a/MacApp/Info.plist +++ b/MacApp/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.12 + 1.13 CFBundleVersion 1 LSUIElement diff --git a/WatchApp/ContentView.swift b/WatchApp/ContentView.swift index d7a2225..16dfa1e 100644 --- a/WatchApp/ContentView.swift +++ b/WatchApp/ContentView.swift @@ -12,7 +12,10 @@ struct ContentView: View { @State private var showSettings = false @State private var crownOffset: Double = 0 @State private var lastCrownDetent: Int = 0 + @State private var crownDetentAccumulator: Int = 0 @AppStorage("invertCrown") private var invertCrown = false + @AppStorage("crownControlEnabled") private var crownControlEnabled = true + @AppStorage("crownDetentsPerSlide") private var crownDetentsPerSlide = 1 private var isFlickMode: Bool { gestureMode == "flickWrist" } @@ -49,16 +52,38 @@ struct ContentView: View { isContinuous: true ) .onChange(of: crownOffset) { _, newValue in - let detent = Int(newValue.rounded()) - guard detent != lastCrownDetent else { return } - let isForward = invertCrown ? (detent < lastCrownDetent) : (detent > lastCrownDetent) - WKInterfaceDevice.current().play(.click) - if isForward { + handleCrownChange(newValue) + } + .onChange(of: crownDetentsPerSlide) { _, _ in + crownDetentAccumulator = 0 + } + } + + // MARK: - Digital Crown + + private func handleCrownChange(_ newValue: Double) { + let detent = Int(newValue.rounded()) + let delta = detent - lastCrownDetent + guard delta != 0 else { return } + lastCrownDetent = detent + + guard crownControlEnabled else { + crownDetentAccumulator = 0 + return + } + + crownDetentAccumulator += invertCrown ? -delta : delta + let detentsNeeded = max(crownDetentsPerSlide, 1) + + while abs(crownDetentAccumulator) >= detentsNeeded { + if crownDetentAccumulator > 0 { connectionManager.nextSlide() + crownDetentAccumulator -= detentsNeeded } else { connectionManager.previousSlide() + crownDetentAccumulator += detentsNeeded } - lastCrownDetent = detent + WKInterfaceDevice.current().play(.click) } } diff --git a/WatchApp/SettingsView.swift b/WatchApp/SettingsView.swift index ea12e61..952e6ad 100644 --- a/WatchApp/SettingsView.swift +++ b/WatchApp/SettingsView.swift @@ -4,6 +4,32 @@ struct SettingsView: View { @EnvironmentObject var gestureManager: GestureManager @AppStorage("gestureMode") private var gestureMode = "doubleTap" @AppStorage("invertCrown") private var invertCrown = false + @AppStorage("crownControlEnabled") private var crownControlEnabled = true + @AppStorage("crownDetentsPerSlide") private var crownDetentsPerSlide = 1 + + private var crownFooterText: String { + guard crownControlEnabled else { + return "Digital Crown slide navigation is disabled to prevent accidental slide changes." + } + + let direction = invertCrown + ? "clockwise = previous slide, counterclockwise = next slide" + : "clockwise = next slide, counterclockwise = previous slide" + + let sensitivity: String + switch crownDetentsPerSlide { + case 1: + sensitivity = "Fast: every crown detent advances a slide." + case 2: + sensitivity = "Balanced: two detents are required per slide." + case 3: + sensitivity = "Deliberate: three detents are required per slide." + default: + sensitivity = "Locked-in: five detents are required per slide." + } + + return "\(sensitivity) Crown \(direction)." + } var body: some View { List { @@ -71,14 +97,26 @@ struct SettingsView: View { } Section { + Toggle(isOn: $crownControlEnabled) { + Label("Crown Control", systemImage: "digitalcrown.horizontal.arrow.clockwise") + .font(.system(size: 15)) + } + + Picker("Sensitivity", selection: $crownDetentsPerSlide) { + Text("Fast").tag(1) + Text("Balanced").tag(2) + Text("Deliberate").tag(3) + Text("Locked-in").tag(5) + } + .disabled(!crownControlEnabled) + Toggle(isOn: $invertCrown) { Label("Invert Crown", systemImage: "digitalcrown.horizontal.arrow.counterclockwise") .font(.system(size: 15)) } + .disabled(!crownControlEnabled) } footer: { - Text(invertCrown - ? "Crown clockwise = previous slide, counterclockwise = next slide." - : "Crown clockwise = next slide, counterclockwise = previous slide.") + Text(crownFooterText) .font(.system(size: 11)) .foregroundColor(.secondary) } diff --git a/iPhoneApp/Info.plist b/iPhoneApp/Info.plist index ad1c09b..4190129 100644 --- a/iPhoneApp/Info.plist +++ b/iPhoneApp/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.12 + 1.13 CFBundleVersion 1 NSBonjourServices diff --git a/project.yml b/project.yml index eb8a803..15c2143 100644 --- a/project.yml +++ b/project.yml @@ -31,7 +31,7 @@ targets: path: MacApp/Info.plist properties: CFBundleName: Deck - CFBundleShortVersionString: "1.12" + CFBundleShortVersionString: "1.13" LSUIElement: true NSBonjourServices: - _clickerremote._tcp @@ -48,7 +48,7 @@ targets: PRODUCT_BUNDLE_IDENTIFIER: com.dou.clicker-mac PRODUCT_NAME: Deck MACOSX_DEPLOYMENT_TARGET: "14.0" - MARKETING_VERSION: "1.12" + MARKETING_VERSION: "1.13" CURRENT_PROJECT_VERSION: "1" CODE_SIGN_STYLE: Automatic CODE_SIGN_ENTITLEMENTS: MacApp/DeckMac.entitlements @@ -83,7 +83,7 @@ targets: path: iPhoneApp/Info.plist properties: CFBundleName: Deck - CFBundleShortVersionString: "1.12" + CFBundleShortVersionString: "1.13" UILaunchScreen: {} UISupportedInterfaceOrientations: - UIInterfaceOrientationPortrait @@ -97,7 +97,7 @@ targets: PRODUCT_BUNDLE_IDENTIFIER: com.dou.clicker-ios PRODUCT_NAME: Deck IPHONEOS_DEPLOYMENT_TARGET: "18.0" - MARKETING_VERSION: "1.12" + MARKETING_VERSION: "1.13" CURRENT_PROJECT_VERSION: "1" CODE_SIGN_STYLE: Automatic DEVELOPMENT_TEAM: HD35YQ72U4 @@ -123,7 +123,7 @@ targets: path: LiveActivityWidget/Info.plist properties: CFBundleDisplayName: Deck - CFBundleShortVersionString: "1.12" + CFBundleShortVersionString: "1.13" NSExtension: NSExtensionPointIdentifier: com.apple.widgetkit-extension settings: @@ -131,7 +131,7 @@ targets: PRODUCT_BUNDLE_IDENTIFIER: com.dou.clicker-ios.LiveActivity PRODUCT_NAME: DeckLiveActivity IPHONEOS_DEPLOYMENT_TARGET: "18.0" - MARKETING_VERSION: "1.12" + MARKETING_VERSION: "1.13" CURRENT_PROJECT_VERSION: "1" CODE_SIGN_STYLE: Automatic DEVELOPMENT_TEAM: HD35YQ72U4 @@ -153,7 +153,7 @@ targets: path: WatchApp/Info.plist properties: CFBundleName: Deck - CFBundleShortVersionString: "1.12" + CFBundleShortVersionString: "1.13" WKApplication: true WKCompanionAppBundleIdentifier: com.dou.clicker-ios WKBackgroundModes: @@ -169,7 +169,7 @@ targets: PRODUCT_BUNDLE_IDENTIFIER: com.dou.clicker-ios.watchkitapp PRODUCT_NAME: DeckWatch WATCHOS_DEPLOYMENT_TARGET: "10.0" - MARKETING_VERSION: "1.12" + MARKETING_VERSION: "1.13" CURRENT_PROJECT_VERSION: "1" CODE_SIGN_STYLE: Automatic DEVELOPMENT_TEAM: HD35YQ72U4