mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 15:14:33 +00:00
Resolve Timer Decoupling
This commit is contained in:
parent
4090100c2a
commit
33a854dffb
3 changed files with 34 additions and 52 deletions
|
|
@ -2,13 +2,13 @@ import SwiftUI
|
||||||
import Defaults
|
import Defaults
|
||||||
|
|
||||||
struct LockScreenTimerWidget: View {
|
struct LockScreenTimerWidget: View {
|
||||||
static let preferredSize = CGSize(width: 420, height: 104)
|
static let preferredSize = CGSize(width: 420, height: 96)
|
||||||
static let cornerRadius: CGFloat = 26
|
static let cornerRadius: CGFloat = 26
|
||||||
|
|
||||||
@ObservedObject private var animator: LockScreenTimerWidgetAnimator
|
@ObservedObject private var animator: LockScreenTimerWidgetAnimator
|
||||||
@ObservedObject private var timerManager = TimerManager.shared
|
@ObservedObject private var timerManager = TimerManager.shared
|
||||||
@Default(.lockScreenGlassStyle) private var glassStyle
|
@Default(.lockScreenGlassStyle) private var glassStyle
|
||||||
@Default(.lockScreenPanelUsesBlur) private var enableBlur
|
@Default(.lockScreenTimerWidgetUsesBlur) private var enableTimerBlur
|
||||||
@Default(.timerPresets) private var timerPresets
|
@Default(.timerPresets) private var timerPresets
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
|
|
@ -20,13 +20,11 @@ struct LockScreenTimerWidget: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var clampedProgress: Double {
|
private var countdownFont: Font { displayFont(size: 56) }
|
||||||
min(max(timerManager.progress, 0), 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var titleFont: Font { .system(size: 18, weight: .semibold, design: .rounded) }
|
private func displayFont(size: CGFloat) -> Font {
|
||||||
private var statusFont: Font { .system(size: 12, weight: .medium, design: .rounded) }
|
.custom("SF Pro Display", size: size)
|
||||||
private var countdownFont: Font { .system(size: 42, weight: .semibold, design: .default) }
|
}
|
||||||
|
|
||||||
private var timerLabel: String {
|
private var timerLabel: String {
|
||||||
timerManager.timerName.isEmpty ? "Timer" : timerManager.timerName
|
timerManager.timerName.isEmpty ? "Timer" : timerManager.timerName
|
||||||
|
|
@ -46,10 +44,6 @@ struct LockScreenTimerWidget: View {
|
||||||
.ensureMinimumBrightness(factor: 0.75)
|
.ensureMinimumBrightness(factor: 0.75)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var secondaryAccentColor: Color {
|
|
||||||
accentColor.ensureMinimumBrightness(factor: 0.55).opacity(0.45)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var accentGradient: LinearGradient {
|
private var accentGradient: LinearGradient {
|
||||||
LinearGradient(
|
LinearGradient(
|
||||||
colors: [
|
colors: [
|
||||||
|
|
@ -70,7 +64,7 @@ struct LockScreenTimerWidget: View {
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private var widgetBackground: some View {
|
private var widgetBackground: some View {
|
||||||
if enableBlur {
|
if enableTimerBlur {
|
||||||
if usesLiquidGlass {
|
if usesLiquidGlass {
|
||||||
liquidBackground
|
liquidBackground
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -110,7 +104,7 @@ struct LockScreenTimerWidget: View {
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(spacing: 16) {
|
VStack(spacing: 16) {
|
||||||
HStack(alignment: .center, spacing: 20) {
|
HStack(alignment: .center, spacing: 18) {
|
||||||
controlButtons
|
controlButtons
|
||||||
|
|
||||||
titleSection
|
titleSection
|
||||||
|
|
@ -141,9 +135,8 @@ struct LockScreenTimerWidget: View {
|
||||||
HStack(spacing: 10) {
|
HStack(spacing: 10) {
|
||||||
CircleButton(
|
CircleButton(
|
||||||
icon: pauseIcon,
|
icon: pauseIcon,
|
||||||
foreground: accentColor,
|
foreground: Color.white.opacity(0.95),
|
||||||
background: Color.white.opacity(0.15),
|
background: accentColor.opacity(0.32),
|
||||||
borderColor: accentColor.opacity(0.8),
|
|
||||||
action: togglePause,
|
action: togglePause,
|
||||||
isEnabled: timerManager.allowsManualInteraction,
|
isEnabled: timerManager.allowsManualInteraction,
|
||||||
helpText: pauseLabel
|
helpText: pauseLabel
|
||||||
|
|
@ -151,9 +144,8 @@ struct LockScreenTimerWidget: View {
|
||||||
|
|
||||||
CircleButton(
|
CircleButton(
|
||||||
icon: "xmark",
|
icon: "xmark",
|
||||||
foreground: Color.white.opacity(0.92),
|
foreground: Color.white.opacity(0.95),
|
||||||
background: Color.white.opacity(0.18),
|
background: Color.black.opacity(0.35),
|
||||||
borderColor: Color.white.opacity(0.35),
|
|
||||||
action: stopTimer,
|
action: stopTimer,
|
||||||
isEnabled: timerManager.allowsManualInteraction,
|
isEnabled: timerManager.allowsManualInteraction,
|
||||||
helpText: "Stop"
|
helpText: "Stop"
|
||||||
|
|
@ -161,41 +153,35 @@ struct LockScreenTimerWidget: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private var titleSection: some View {
|
|
||||||
Group {
|
|
||||||
if timerManager.timerName.count > 18 {
|
|
||||||
MarqueeText(
|
|
||||||
.constant(timerLabel),
|
|
||||||
font: .system(size: 18, weight: .semibold, design: .rounded),
|
|
||||||
nsFont: .title3,
|
|
||||||
textColor: accentColor,
|
|
||||||
minDuration: 0.18,
|
|
||||||
frameWidth: Self.preferredSize.width * 0.4
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Text(timerLabel)
|
|
||||||
.font(titleFont)
|
|
||||||
.foregroundStyle(accentColor)
|
|
||||||
.lineLimit(1)
|
|
||||||
.minimumScaleFactor(0.85)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.frame(maxWidth: .infinity, alignment: .center)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var countdownSection: some View {
|
private var countdownSection: some View {
|
||||||
VStack(alignment: .trailing, spacing: 6) {
|
VStack(alignment: .trailing, spacing: 0) {
|
||||||
Text(countdownText)
|
Text(countdownText)
|
||||||
.font(countdownFont)
|
.font(countdownFont)
|
||||||
|
.monospacedDigit()
|
||||||
.foregroundStyle(timerManager.isOvertime ? Color.red : accentColor)
|
.foregroundStyle(timerManager.isOvertime ? Color.red : accentColor)
|
||||||
.contentTransition(.numericText())
|
.contentTransition(.numericText())
|
||||||
.animation(.smooth(duration: 0.25), value: timerManager.remainingTime)
|
.animation(.smooth(duration: 0.25), value: timerManager.remainingTime)
|
||||||
.frame(maxWidth: .infinity, alignment: .trailing)
|
.frame(maxWidth: .infinity, alignment: .trailing)
|
||||||
}
|
}
|
||||||
.frame(minWidth: 140, maxWidth: 180, alignment: .trailing)
|
.frame(width: 220, alignment: .center)
|
||||||
.padding(.trailing, 2)
|
.padding(.trailing, 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var titleSection: some View {
|
||||||
|
VStack(alignment: .center, spacing: 0) {
|
||||||
|
MarqueeText(
|
||||||
|
.constant(timerLabel),
|
||||||
|
font: displayFont(size: 18),
|
||||||
|
nsFont: .title3,
|
||||||
|
textColor: accentColor,
|
||||||
|
minDuration: 0.18,
|
||||||
|
frameWidth: Self.preferredSize.width * 0.35
|
||||||
|
)
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
|
||||||
|
}
|
||||||
|
|
||||||
private var accentRibbon: some View {
|
private var accentRibbon: some View {
|
||||||
Capsule()
|
Capsule()
|
||||||
.fill(accentGradient)
|
.fill(accentGradient)
|
||||||
|
|
@ -227,7 +213,6 @@ struct LockScreenTimerWidget: View {
|
||||||
let icon: String
|
let icon: String
|
||||||
let foreground: Color
|
let foreground: Color
|
||||||
let background: Color
|
let background: Color
|
||||||
let borderColor: Color?
|
|
||||||
let action: () -> Void
|
let action: () -> Void
|
||||||
let isEnabled: Bool
|
let isEnabled: Bool
|
||||||
let helpText: String
|
let helpText: String
|
||||||
|
|
@ -240,13 +225,6 @@ struct LockScreenTimerWidget: View {
|
||||||
.frame(width: 48, height: 48)
|
.frame(width: 48, height: 48)
|
||||||
.background(background.opacity(isEnabled ? 1 : 0.25))
|
.background(background.opacity(isEnabled ? 1 : 0.25))
|
||||||
.clipShape(Circle())
|
.clipShape(Circle())
|
||||||
.overlay {
|
|
||||||
if let borderColor {
|
|
||||||
Circle()
|
|
||||||
.stroke(borderColor.opacity(isEnabled ? 1 : 0.35), lineWidth: 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.shadow(color: Color.black.opacity(0.35), radius: 6, x: 0, y: 4)
|
|
||||||
.contentTransition(.symbolEffect(.replace))
|
.contentTransition(.symbolEffect(.replace))
|
||||||
}
|
}
|
||||||
.buttonStyle(.plain)
|
.buttonStyle(.plain)
|
||||||
|
|
|
||||||
|
|
@ -179,6 +179,7 @@ struct GeneralSettings: View {
|
||||||
@Default(.lockScreenShowAppIcon) var lockScreenShowAppIcon
|
@Default(.lockScreenShowAppIcon) var lockScreenShowAppIcon
|
||||||
@Default(.lockScreenPanelShowsBorder) var lockScreenPanelShowsBorder
|
@Default(.lockScreenPanelShowsBorder) var lockScreenPanelShowsBorder
|
||||||
@Default(.lockScreenPanelUsesBlur) var lockScreenPanelUsesBlur
|
@Default(.lockScreenPanelUsesBlur) var lockScreenPanelUsesBlur
|
||||||
|
@Default(.enableLockScreenTimerWidget) var enableLockScreenTimerWidget
|
||||||
@Default(.enableLockScreenWeatherWidget) var enableLockScreenWeatherWidget
|
@Default(.enableLockScreenWeatherWidget) var enableLockScreenWeatherWidget
|
||||||
@Default(.lockScreenWeatherShowsLocation) var lockScreenWeatherShowsLocation
|
@Default(.lockScreenWeatherShowsLocation) var lockScreenWeatherShowsLocation
|
||||||
@Default(.lockScreenWeatherShowsCharging) var lockScreenWeatherShowsCharging
|
@Default(.lockScreenWeatherShowsCharging) var lockScreenWeatherShowsCharging
|
||||||
|
|
@ -380,6 +381,8 @@ struct GeneralSettings: View {
|
||||||
Defaults.Toggle("Show lock screen media panel", key: .enableLockScreenMediaWidget)
|
Defaults.Toggle("Show lock screen media panel", key: .enableLockScreenMediaWidget)
|
||||||
Defaults.Toggle("Show lock screen weather", key: .enableLockScreenWeatherWidget)
|
Defaults.Toggle("Show lock screen weather", key: .enableLockScreenWeatherWidget)
|
||||||
Defaults.Toggle("Show lock screen timer", key: .enableLockScreenTimerWidget)
|
Defaults.Toggle("Show lock screen timer", key: .enableLockScreenTimerWidget)
|
||||||
|
Defaults.Toggle("Enable timer blur", key: .lockScreenTimerWidgetUsesBlur)
|
||||||
|
.disabled(!enableLockScreenTimerWidget)
|
||||||
if enableLockScreenWeatherWidget {
|
if enableLockScreenWeatherWidget {
|
||||||
Picker("Widget layout", selection: $lockScreenWeatherWidgetStyle) {
|
Picker("Widget layout", selection: $lockScreenWeatherWidgetStyle) {
|
||||||
ForEach(LockScreenWeatherWidgetStyle.allCases) { style in
|
ForEach(LockScreenWeatherWidgetStyle.allCases) { style in
|
||||||
|
|
|
||||||
|
|
@ -473,6 +473,7 @@ extension Defaults.Keys {
|
||||||
static let lockScreenShowAppIcon = Key<Bool>("lockScreenShowAppIcon", default: false)
|
static let lockScreenShowAppIcon = Key<Bool>("lockScreenShowAppIcon", default: false)
|
||||||
static let lockScreenPanelShowsBorder = Key<Bool>("lockScreenPanelShowsBorder", default: true)
|
static let lockScreenPanelShowsBorder = Key<Bool>("lockScreenPanelShowsBorder", default: true)
|
||||||
static let lockScreenPanelUsesBlur = Key<Bool>("lockScreenPanelUsesBlur", default: true)
|
static let lockScreenPanelUsesBlur = Key<Bool>("lockScreenPanelUsesBlur", default: true)
|
||||||
|
static let lockScreenTimerWidgetUsesBlur = Key<Bool>("lockScreenTimerWidgetUsesBlur", default: true)
|
||||||
static let lockScreenReminderChipStyle = Key<LockScreenReminderChipStyle>("lockScreenReminderChipStyle", default: .eventColor)
|
static let lockScreenReminderChipStyle = Key<LockScreenReminderChipStyle>("lockScreenReminderChipStyle", default: .eventColor)
|
||||||
|
|
||||||
// MARK: Battery
|
// MARK: Battery
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue