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