Update Notch Timer View: Give option to disable/enable presets

This commit is contained in:
Hariharan Mudaliar 2025-12-10 14:28:40 +05:30
commit 57fc9b9bf9
No known key found for this signature in database
GPG key ID: D04F1C2B9194DDFC
4 changed files with 114 additions and 43 deletions

View file

@ -17340,6 +17340,10 @@
} }
} }
}, },
"Show preset list in timer tab" : {
"comment" : "A toggle that lets users decide whether to show a list of presets in the timer tab.",
"isCommentAutoGenerated" : true
},
"Show progress" : { "Show progress" : {
}, },

View file

@ -21,6 +21,7 @@ struct NotchTimerView: View {
@Default(.timerSolidColor) private var solidColor @Default(.timerSolidColor) private var solidColor
@Default(.timerShowsProgress) private var showsProgress @Default(.timerShowsProgress) private var showsProgress
@Default(.timerProgressStyle) private var progressStyle @Default(.timerProgressStyle) private var progressStyle
@Default(.showTimerPresetsInNotchTab) private var showTimerPresetsInNotchTab
@AppStorage("customTimerDuration") private var customTimerDuration: Double = 600 @AppStorage("customTimerDuration") private var customTimerDuration: Double = 600
@State private var customHours: Int = 0 @State private var customHours: Int = 0
@ -249,48 +250,37 @@ struct NotchTimerView: View {
} }
private var customTimerComposer: some View { private var customTimerComposer: some View {
Group {
if showTimerPresetsInNotchTab {
VStack(alignment: .leading, spacing: 12) { VStack(alignment: .leading, spacing: 12) {
DurationInputRow(hours: $customHours, minutes: $customMinutes, seconds: $customSeconds) DurationInputRow(
hours: $customHours,
minutes: $customMinutes,
seconds: $customSeconds,
fieldWidth: durationFieldWidth
)
HStack(spacing: 10) { HStack(spacing: 10) {
Button { startButton
withAnimation(.smooth) { resetButton
timerManager.startTimer(duration: customDurationInSeconds, name: "Custom Timer")
coordinator.currentView = .timer
} }
} label: {
Label("Start", systemImage: "play.fill")
.font(.system(size: 15, weight: .semibold))
.frame(maxWidth: .infinity)
.padding(.vertical, 10)
.foregroundStyle(Color.white)
} }
.buttonStyle(.plain) } else {
.background(startButtonColor.opacity(isStartDisabled ? 0.5 : 1)) HStack(alignment: .center, spacing: 16) {
.clipShape(RoundedRectangle(cornerRadius: 12, style: .continuous)) DurationInputRow(
.overlay( hours: $customHours,
RoundedRectangle(cornerRadius: 12, style: .continuous) minutes: $customMinutes,
.stroke(Color.white.opacity(0.15), lineWidth: 1) seconds: $customSeconds,
fieldWidth: durationFieldWidth
) )
.opacity(isStartDisabled ? 0.7 : 1) .frame(maxWidth: .infinity, alignment: .leading)
.disabled(isStartDisabled)
Button { VStack(spacing: 10) {
resetCustomTimerInputs() startButton
} label: { resetButton
Label("Reset", systemImage: "arrow.counterclockwise") }
.font(.system(size: 15, weight: .semibold)) .frame(width: buttonColumnWidth, alignment: .top)
.frame(maxWidth: .infinity)
.padding(.vertical, 10)
.foregroundStyle(.white.opacity(0.9))
} }
.buttonStyle(.plain)
.background(Color.white.opacity(0.16))
.clipShape(RoundedRectangle(cornerRadius: 12, style: .continuous))
.overlay(
RoundedRectangle(cornerRadius: 12, style: .continuous)
.stroke(Color.white.opacity(0.08), lineWidth: 1)
)
} }
} }
.padding(12) .padding(12)
@ -331,7 +321,7 @@ struct NotchTimerView: View {
} }
private var shouldShowPresetColumn: Bool { private var shouldShowPresetColumn: Bool {
!timerManager.isTimerActive !timerManager.isTimerActive && showTimerPresetsInNotchTab
} }
private var resolvedNotchHeight: CGFloat { private var resolvedNotchHeight: CGFloat {
@ -424,6 +414,53 @@ struct NotchTimerView: View {
customDurationInSeconds == 0 customDurationInSeconds == 0
} }
private var durationFieldWidth: CGFloat {
showTimerPresetsInNotchTab ? 64 : 78
}
private var buttonColumnWidth: CGFloat { 210 }
private var startButton: some View {
Button {
withAnimation(.smooth) {
timerManager.startTimer(duration: customDurationInSeconds, name: "Custom Timer")
coordinator.currentView = .timer
}
} label: {
Label("Start", systemImage: "play.fill")
.font(.system(size: 15, weight: .semibold))
.frame(maxWidth: .infinity)
.padding(.vertical, 10)
.foregroundStyle(Color.white)
}
.buttonStyle(.plain)
.background(startButtonColor.opacity(isStartDisabled ? 0.5 : 1))
.clipShape(RoundedRectangle(cornerRadius: 12, style: .continuous))
.overlay(
RoundedRectangle(cornerRadius: 12, style: .continuous)
.stroke(Color.white.opacity(0.15), lineWidth: 1)
)
.opacity(isStartDisabled ? 0.7 : 1)
.disabled(isStartDisabled)
}
private var resetButton: some View {
Button(action: resetCustomTimerInputs) {
Label("Reset", systemImage: "arrow.counterclockwise")
.font(.system(size: 15, weight: .semibold))
.frame(maxWidth: .infinity)
.padding(.vertical, 10)
.foregroundStyle(.white.opacity(0.9))
}
.buttonStyle(.plain)
.background(Color.white.opacity(0.16))
.clipShape(RoundedRectangle(cornerRadius: 12, style: .continuous))
.overlay(
RoundedRectangle(cornerRadius: 12, style: .continuous)
.stroke(Color.white.opacity(0.08), lineWidth: 1)
)
}
private var customDurationInSeconds: TimeInterval { private var customDurationInSeconds: TimeInterval {
TimeInterval(customHours * 3600 + customMinutes * 60 + customSeconds) TimeInterval(customHours * 3600 + customMinutes * 60 + customSeconds)
} }
@ -496,14 +533,27 @@ private struct DurationInputRow: View {
@Binding var hours: Int @Binding var hours: Int
@Binding var minutes: Int @Binding var minutes: Int
@Binding var seconds: Int @Binding var seconds: Int
let fieldWidth: CGFloat
init(
hours: Binding<Int>,
minutes: Binding<Int>,
seconds: Binding<Int>,
fieldWidth: CGFloat = 64
) {
_hours = hours
_minutes = minutes
_seconds = seconds
self.fieldWidth = fieldWidth
}
var body: some View { var body: some View {
HStack(alignment: .center, spacing: 8) { HStack(alignment: .center, spacing: 8) {
DurationField(label: "HH", value: $hours, range: 0...23) DurationField(label: "HH", value: $hours, range: 0...23, width: fieldWidth)
colon colon
DurationField(label: "MM", value: $minutes, range: 0...59) DurationField(label: "MM", value: $minutes, range: 0...59, width: fieldWidth)
colon colon
DurationField(label: "SS", value: $seconds, range: 0...59) DurationField(label: "SS", value: $seconds, range: 0...59, width: fieldWidth)
} }
} }
@ -518,6 +568,19 @@ private struct DurationField: View {
let label: String let label: String
@Binding var value: Int @Binding var value: Int
let range: ClosedRange<Int> let range: ClosedRange<Int>
let width: CGFloat
init(
label: String,
value: Binding<Int>,
range: ClosedRange<Int>,
width: CGFloat = 64
) {
self.label = label
_value = value
self.range = range
self.width = width
}
var body: some View { var body: some View {
VStack(spacing: 6) { VStack(spacing: 6) {
@ -525,7 +588,7 @@ private struct DurationField: View {
.font(.system(size: 28, weight: .semibold, design: .monospaced)) .font(.system(size: 28, weight: .semibold, design: .monospaced))
.multilineTextAlignment(.center) .multilineTextAlignment(.center)
.textFieldStyle(.plain) .textFieldStyle(.plain)
.frame(width: 64, height: 46) .frame(width: width, height: 46)
.background(Color.white.opacity(0.08)) .background(Color.white.opacity(0.08))
.clipShape(RoundedRectangle(cornerRadius: 12, style: .continuous)) .clipShape(RoundedRectangle(cornerRadius: 12, style: .continuous))

View file

@ -3352,6 +3352,7 @@ struct TimerSettings: View {
@Default(.timerShowsLabel) private var showsLabel @Default(.timerShowsLabel) private var showsLabel
@Default(.timerShowsProgress) private var showsProgress @Default(.timerShowsProgress) private var showsProgress
@Default(.timerProgressStyle) private var progressStyle @Default(.timerProgressStyle) private var progressStyle
@Default(.showTimerPresetsInNotchTab) private var showTimerPresetsInNotchTab
@Default(.timerControlWindowEnabled) private var controlWindowEnabled @Default(.timerControlWindowEnabled) private var controlWindowEnabled
@Default(.mirrorSystemTimer) private var mirrorSystemTimer @Default(.mirrorSystemTimer) private var mirrorSystemTimer
@Default(.timerDisplayMode) private var timerDisplayMode @Default(.timerDisplayMode) private var timerDisplayMode
@ -3494,6 +3495,8 @@ struct TimerSettings: View {
Toggle("Show timer name", isOn: $showsLabel) Toggle("Show timer name", isOn: $showsLabel)
Toggle("Show countdown", isOn: $showsCountdown) Toggle("Show countdown", isOn: $showsCountdown)
Toggle("Show progress", isOn: $showsProgress) Toggle("Show progress", isOn: $showsProgress)
Toggle("Show preset list in timer tab", isOn: $showTimerPresetsInNotchTab)
.settingsHighlight(id: highlightID("Show preset list in timer tab"))
Toggle("Show floating pause/stop controls", isOn: $controlWindowEnabled) Toggle("Show floating pause/stop controls", isOn: $controlWindowEnabled)
.disabled(showsLabel) .disabled(showsLabel)

View file

@ -547,6 +547,7 @@ extension Defaults.Keys {
static let enableTimerFeature = Key<Bool>("enableTimerFeature", default: true) static let enableTimerFeature = Key<Bool>("enableTimerFeature", default: true)
static let timerDisplayMode = Key<TimerDisplayMode>("timerDisplayMode", default: .tab) static let timerDisplayMode = Key<TimerDisplayMode>("timerDisplayMode", default: .tab)
static let timerPresets = Key<[TimerPreset]>("timerPresets", default: TimerPreset.defaultPresets) static let timerPresets = Key<[TimerPreset]>("timerPresets", default: TimerPreset.defaultPresets)
static let showTimerPresetsInNotchTab = Key<Bool>("showTimerPresetsInNotchTab", default: true)
static let timerIconColorMode = Key<TimerIconColorMode>("timerIconColorMode", default: .adaptive) static let timerIconColorMode = Key<TimerIconColorMode>("timerIconColorMode", default: .adaptive)
static let timerSolidColor = Key<Color>("timerSolidColor", default: .blue) static let timerSolidColor = Key<Color>("timerSolidColor", default: .blue)
static let timerShowsCountdown = Key<Bool>("timerShowsCountdown", default: true) static let timerShowsCountdown = Key<Bool>("timerShowsCountdown", default: true)