Fully fixed: Timer Lock Screen Widget

This commit is contained in:
Hariharan Mudaliar 2025-11-28 20:04:51 +05:30
commit a08540c589
3 changed files with 36 additions and 14 deletions

View file

@ -201,22 +201,22 @@ struct LockScreenTimerWidget: View {
} }
private func stopTimer() { private func stopTimer() {
let stopAction: @MainActor () -> Void = { let allowsManualInteraction = timerManager.allowsManualInteraction
if timerManager.allowsManualInteraction {
timerManager.stopTimer()
} else {
timerManager.endExternalTimer(triggerSmoothClose: false)
}
}
Task { @MainActor in LockScreenTimerWidgetPanelManager.shared.hide()
// Trigger the scale-out animation first so the dismissal mirrors the unlock flow.
LockScreenTimerWidgetPanelManager.shared.hide()
Task.detached(priority: .userInitiated) { [allowsManualInteraction] in
try? await Task.sleep(nanoseconds: LockScreenTimerWidgetPanelManager.hideAnimationDurationNanoseconds) try? await Task.sleep(nanoseconds: LockScreenTimerWidgetPanelManager.hideAnimationDurationNanoseconds)
guard !Task.isCancelled else { return } guard !Task.isCancelled else { return }
stopAction()
await MainActor.run {
if allowsManualInteraction {
TimerManager.shared.stopTimer()
} else {
TimerManager.shared.endExternalTimer(triggerSmoothClose: false)
}
}
} }
} }

View file

@ -137,6 +137,7 @@ final class LockScreenTimerWidgetPanelManager {
} }
} }
func refreshPosition(animated: Bool) { func refreshPosition(animated: Bool) {
guard let window, window.isVisible, let screen = NSScreen.main else { return } guard let window, window.isVisible, let screen = NSScreen.main else { return }
let frame = targetFrame(on: screen) let frame = targetFrame(on: screen)

View file

@ -4,7 +4,7 @@ import SwiftUI
import SkyLightWindow import SkyLightWindow
import QuartzCore import QuartzCore
struct TimerControlWindowMetrics { struct TimerControlWindowMetrics: Equatable {
let notchHeight: CGFloat let notchHeight: CGFloat
let notchWidth: CGFloat let notchWidth: CGFloat
let rightWingWidth: CGFloat let rightWingWidth: CGFloat
@ -90,15 +90,23 @@ final class TimerControlWindowManager {
guard window != nil else { guard window != nil else {
return present(using: viewModel, metrics: metrics) return present(using: viewModel, metrics: metrics)
} }
if let lastMetrics, lastMetrics == metrics {
return true
}
return present(using: viewModel, metrics: metrics) return present(using: viewModel, metrics: metrics)
} }
func hide(animated: Bool = true) { func hide(animated: Bool = true, tearDown: Bool = true) {
guard let window else { return } guard let window else { return }
guard animated, window.alphaValue > 0.01 else { guard animated, window.alphaValue > 0.01 else {
window.orderOut(nil) window.orderOut(nil)
window.alphaValue = 0 window.alphaValue = 0
if tearDown {
tearDownWindowResources(using: window)
}
return return
} }
@ -110,13 +118,26 @@ final class TimerControlWindowManager {
context.timingFunction = CAMediaTimingFunction(name: .easeIn) context.timingFunction = CAMediaTimingFunction(name: .easeIn)
window.animator().setFrame(retreatFrame, display: true) window.animator().setFrame(retreatFrame, display: true)
window.animator().alphaValue = 0 window.animator().alphaValue = 0
} completionHandler: { } completionHandler: { [weak self] in
window.setFrame(originalFrame, display: false) window.setFrame(originalFrame, display: false)
window.orderOut(nil) window.orderOut(nil)
window.alphaValue = 0 window.alphaValue = 0
if tearDown {
self?.tearDownWindowResources(using: window)
}
} }
} }
private func tearDownWindowResources(using window: NSWindow? = nil) {
let targetWindow = window ?? self.window
targetWindow?.contentView = nil
targetWindow?.orderOut(nil)
hostingView = nil
lastMetrics = nil
self.window = nil
hasDelegated = false
}
private func ensureHostingView(with overlay: TimerControlOverlay) -> NSHostingView<TimerControlOverlay> { private func ensureHostingView(with overlay: TimerControlOverlay) -> NSHostingView<TimerControlOverlay> {
if let hostingView { if let hostingView {
hostingView.rootView = overlay hostingView.rootView = overlay