From 3d827c306a780ae4d4a25dcf9c37dc207608c106 Mon Sep 17 00:00:00 2001 From: Jis G Jacob Date: Thu, 12 Mar 2026 22:57:27 +0530 Subject: [PATCH 1/3] Atoll: Fix Timers LS Widget overlapping Music Player Signed-off-by: Jis G Jacob --- .../managers/LockScreenPanelManager.swift | 14 +++++++++++++- .../LockScreenTimerWidgetManager.swift | 13 ++++--------- .../LockScreenWidgetPreviewManager.swift | 19 +++++++++---------- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/DynamicIsland/managers/LockScreenPanelManager.swift b/DynamicIsland/managers/LockScreenPanelManager.swift index 3dbe0c8..5c5dfa7 100644 --- a/DynamicIsland/managers/LockScreenPanelManager.swift +++ b/DynamicIsland/managers/LockScreenPanelManager.swift @@ -202,6 +202,11 @@ class LockScreenPanelManager { currentAdditionalHeight = additionalHeight } + func notifyTimerWidgetFrameChanged(animated: Bool) { + guard panelWindow?.isVisible == true || panelAnimator.isPresented else { return } + applyOffsetAdjustment(animated: animated) + } + func applyOffsetAdjustment(animated: Bool = true) { guard let screen = currentScreen() else { return } let screenFrame = screen.frame @@ -266,7 +271,14 @@ class LockScreenPanelManager { let defaultLowering: CGFloat = -28 let userOffset = CGFloat(Defaults[.lockScreenMusicVerticalOffset]) let clampedOffset = min(max(userOffset, -160), 160) - let originY = baseOriginY + defaultLowering + clampedOffset + var originY = baseOriginY + defaultLowering + clampedOffset + + if let timerFrame = LockScreenTimerWidgetPanelManager.shared.latestFrame { + let maxAllowedTop = timerFrame.minY - 12 + let maxOriginY = maxAllowedTop - collapsedSize.height + originY = min(originY, maxOriginY) + } + return NSRect(x: originX, y: originY, width: collapsedSize.width, height: collapsedSize.height) } diff --git a/DynamicIsland/managers/LockScreenTimerWidgetManager.swift b/DynamicIsland/managers/LockScreenTimerWidgetManager.swift index ff672f4..0337d89 100644 --- a/DynamicIsland/managers/LockScreenTimerWidgetManager.swift +++ b/DynamicIsland/managers/LockScreenTimerWidgetManager.swift @@ -156,6 +156,7 @@ final class LockScreenTimerWidgetPanelManager { hideTask?.cancel() hideTask = nil animator.isPresented = true + LockScreenPanelManager.shared.notifyTimerWidgetFrameChanged(animated: false) LockScreenReminderWidgetPanelManager.shared.refreshPosition(animated: true) } @@ -169,6 +170,7 @@ final class LockScreenTimerWidgetPanelManager { window.orderOut(nil) hideTask = nil latestFrame = nil + LockScreenPanelManager.shared.notifyTimerWidgetFrameChanged(animated: true) LockScreenReminderWidgetPanelManager.shared.refreshPosition(animated: true) return } @@ -179,6 +181,7 @@ final class LockScreenTimerWidgetPanelManager { window?.orderOut(nil) self?.hideTask = nil self?.latestFrame = nil + LockScreenPanelManager.shared.notifyTimerWidgetFrameChanged(animated: true) LockScreenReminderWidgetPanelManager.shared.refreshPosition(animated: true) } } @@ -264,19 +267,11 @@ final class LockScreenTimerWidgetPanelManager { let size = LockScreenTimerWidget.preferredSize let originX = screen.frame.midX - (size.width / 2) let defaultLowering: CGFloat = -18 - var baseY = screen.frame.midY + 24 + defaultLowering - - if let musicFrame = LockScreenPanelManager.shared.latestFrame { - baseY = musicFrame.maxY + 28 + defaultLowering - } + let baseY = screen.frame.midY + 24 + defaultLowering let offset = CGFloat(clampedTimerOffset()) var originY = baseY + offset - if let musicFrame = LockScreenPanelManager.shared.latestFrame { - originY = max(originY, musicFrame.maxY + 12) - } - if let weatherFrame = LockScreenWeatherPanelManager.shared.latestFrame { originY = min(originY, weatherFrame.minY - size.height - 20) } else { diff --git a/DynamicIsland/managers/LockScreenWidgetPreviewManager.swift b/DynamicIsland/managers/LockScreenWidgetPreviewManager.swift index f5fc25d..9ac2c2a 100644 --- a/DynamicIsland/managers/LockScreenWidgetPreviewManager.swift +++ b/DynamicIsland/managers/LockScreenWidgetPreviewManager.swift @@ -444,7 +444,14 @@ private struct LockScreenPreviewScene: View { let defaultLowering: CGFloat = -28 let userOffset = CGFloat(Defaults[.lockScreenMusicVerticalOffset]) let clampedOffset = min(max(userOffset, -160), 160) - let originY = baseOriginY + defaultLowering + clampedOffset + var originY = baseOriginY + defaultLowering + clampedOffset + + if showsTimer { + let maxAllowedTop = timerFrame.minY - 12 + let maxOriginY = maxAllowedTop - size.height + originY = min(originY, maxOriginY) + } + return CGRect(x: originX, y: originY, width: size.width, height: size.height) } @@ -453,19 +460,11 @@ private struct LockScreenPreviewScene: View { let screenFrame = CGRect(origin: .zero, size: screenSize) let originX = screenFrame.midX - (size.width / 2) let defaultLowering: CGFloat = -18 - var baseY = screenFrame.midY + 24 + defaultLowering - - if showsMediaPanel { - baseY = mediaFrame.maxY + 28 + defaultLowering - } + let baseY = screenFrame.midY + 24 + defaultLowering let offset = CGFloat(min(max(Defaults[.lockScreenTimerVerticalOffset], -160), 160)) var originY = baseY + offset - if showsMediaPanel { - originY = max(originY, mediaFrame.maxY + 12) - } - if showsWeather { originY = min(originY, weatherFrame.minY - size.height - 20) } else { From 8a60b0ede5a81450085ae2c302b6ee98b0e1c0d5 Mon Sep 17 00:00:00 2001 From: Jis G Jacob Date: Thu, 12 Mar 2026 23:24:23 +0530 Subject: [PATCH 2/3] Atoll: Adjust LockScreen timer layout and fonts Signed-off-by: Jis G Jacob --- .../LockScreen/LockScreenTimerWidget.swift | 45 +++++++++---------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/DynamicIsland/components/LockScreen/LockScreenTimerWidget.swift b/DynamicIsland/components/LockScreen/LockScreenTimerWidget.swift index e13289d..362c853 100644 --- a/DynamicIsland/components/LockScreen/LockScreenTimerWidget.swift +++ b/DynamicIsland/components/LockScreen/LockScreenTimerWidget.swift @@ -46,7 +46,7 @@ struct LockScreenTimerWidget: View { } private func displayFont(size: CGFloat) -> Font { - .custom("SF Pro Display", size: size) + .system(size: size, weight: .semibold, design: .rounded) } private var hasHoursComponent: Bool { @@ -58,20 +58,20 @@ struct LockScreenTimerWidget: View { } private var titleFrameWidth: CGFloat { - if hasDoubleDigitHours { return 78 } - if hasHoursComponent { return 90 } - return 130 + if hasDoubleDigitHours { return 60 } + if hasHoursComponent { return 80 } + return 110 } private var countdownFrameWidth: CGFloat { - if hasDoubleDigitHours { return 248 } - if hasHoursComponent { return 235 } - return 205 + if hasDoubleDigitHours { return 200 } + if hasHoursComponent { return 185 } + return 160 } private var countdownFont: Font { - let baseSize: CGFloat = hasDoubleDigitHours ? 52 : 56 - return displayFont(size: baseSize) + let baseSize: CGFloat = hasDoubleDigitHours ? 42 : 46 + return .system(size: baseSize, weight: .bold, design: .rounded) } private var timerLabel: String { @@ -191,17 +191,19 @@ struct LockScreenTimerWidget: View { var body: some View { VStack(spacing: 16) { - HStack(alignment: .center, spacing: 8) { + HStack(alignment: .center, spacing: 0) { controlButtons + .padding(.trailing, 12) titleSection .frame(maxWidth: .infinity) countdownSection + .padding(.leading, 12) } } .padding(.horizontal, 12) - .padding(.vertical, 18) + .padding(.vertical, 12) .frame(width: widgetSize.width, height: widgetSize.height) .background(widgetBackground) .clipShape(RoundedRectangle(cornerRadius: Self.cornerRadius, style: .continuous)) @@ -260,18 +262,15 @@ struct LockScreenTimerWidget: View { } private var titleSection: some View { - VStack(alignment: .center, spacing: 0) { - MarqueeText( - .constant(timerLabel), - font: displayFont(size: 18), - nsFont: .title3, - textColor: accentColor, - minDuration: 0.16, - frameWidth: titleFrameWidth - ) - .frame(maxWidth: titleFrameWidth) - } - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center) + MarqueeText( + .constant(timerLabel), + font: displayFont(size: 15), + nsFont: .title3, + textColor: accentColor, + minDuration: 0.16, + frameWidth: titleFrameWidth + ) + .frame(maxWidth: .infinity, alignment: .center) .layoutPriority(0) } From 072491d1e3884342d0a5c509773a8bbf0d94374d Mon Sep 17 00:00:00 2001 From: Jis G Jacob Date: Thu, 12 Mar 2026 23:55:42 +0530 Subject: [PATCH 3/3] Atoll: Fix Timer Stop and Pause button overlap while expanded Signed-off-by: Jis G Jacob --- DynamicIsland/ContentView.swift | 5 +++ .../components/Timer/TimerLiveActivity.swift | 32 +++++++++---------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/DynamicIsland/ContentView.swift b/DynamicIsland/ContentView.swift index df350e4..37c8bbc 100644 --- a/DynamicIsland/ContentView.swift +++ b/DynamicIsland/ContentView.swift @@ -427,6 +427,11 @@ struct ContentView: View { if newState == .closed { removeStickyTerminalClickMonitor() } + #if os(macOS) + if newState == .open { + TimerControlWindowManager.shared.hide() + } + #endif } .onChange(of: vm.isBatteryPopoverActive) { _, newPopoverState in runAfter(0.1) { diff --git a/DynamicIsland/components/Timer/TimerLiveActivity.swift b/DynamicIsland/components/Timer/TimerLiveActivity.swift index 88a5a51..9b740ca 100644 --- a/DynamicIsland/components/Timer/TimerLiveActivity.swift +++ b/DynamicIsland/components/Timer/TimerLiveActivity.swift @@ -207,7 +207,7 @@ struct TimerLiveActivity: View { private var shouldShowControlWindow: Bool { if !controlWindowEnabled { return false } - if shouldDisplayLabel { return false } + if showsLabel { return false } let timerEligible = timerManager.isTimerActive && !timerManager.isExternalTimerActive if !timerEligible { return false } @@ -253,9 +253,6 @@ struct TimerLiveActivity: View { } .onAppear { requestControlWindowSync(forceRefresh: true) - if timerManager.isTimerActive && !timerManager.isFinished && !timerManager.isOvertime { - triggerTransientLabel() - } } .onDisappear { hideControlWindow() @@ -528,23 +525,24 @@ struct TimerLiveActivity: View { let syncDelay = max(0, delay) - pendingControlWindowTask = Task.detached(priority: .userInitiated) { [syncDelay, forceRefresh] in - if syncDelay > 0 { - let nanoseconds = UInt64(syncDelay * 1_000_000_000) - try? await Task.sleep(nanoseconds: nanoseconds) - } + if syncDelay <= 0 { + syncControlWindow(forceRefresh: forceRefresh) + return + } + + pendingControlWindowTask = Task { @MainActor [forceRefresh] in + let nanoseconds = UInt64(syncDelay * 1_000_000_000) + try? await Task.sleep(nanoseconds: nanoseconds) guard !Task.isCancelled else { return } - await MainActor.run { - if shouldShowControlWindow { - syncControlWindow(forceRefresh: forceRefresh) - } else { - hideControlWindow() - } - - pendingControlWindowTask = nil + if shouldShowControlWindow { + syncControlWindow(forceRefresh: forceRefresh) + } else { + hideControlWindow() } + + pendingControlWindowTask = nil } }