mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 15:14:33 +00:00
Merge pull request #241 from Ebullioscopic/ui/fixes
BugFix: Display Lock and Unlock Lag
This commit is contained in:
commit
76b6c6da1a
8 changed files with 132 additions and 18 deletions
|
|
@ -0,0 +1,83 @@
|
|||
import AppKit
|
||||
|
||||
struct LockScreenDisplayContext {
|
||||
let screen: NSScreen
|
||||
let frame: NSRect
|
||||
let identifier: String
|
||||
}
|
||||
|
||||
@MainActor
|
||||
final class LockScreenDisplayContextProvider {
|
||||
static let shared = LockScreenDisplayContextProvider()
|
||||
|
||||
private(set) var context: LockScreenDisplayContext?
|
||||
private var screenChangeObserver: NSObjectProtocol?
|
||||
private var workspaceObservers: [NSObjectProtocol] = []
|
||||
|
||||
private init() {
|
||||
refresh(reason: "init")
|
||||
registerObservers()
|
||||
}
|
||||
|
||||
deinit {
|
||||
if let screenChangeObserver {
|
||||
NotificationCenter.default.removeObserver(screenChangeObserver)
|
||||
}
|
||||
|
||||
let workspaceCenter = NSWorkspace.shared.notificationCenter
|
||||
workspaceObservers.forEach { workspaceCenter.removeObserver($0) }
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func refresh(reason: String) -> LockScreenDisplayContext? {
|
||||
guard let screen = NSScreen.main ?? NSScreen.screens.first else {
|
||||
context = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
let snapshot = LockScreenDisplayContext(
|
||||
screen: screen,
|
||||
frame: screen.frame,
|
||||
identifier: screen.localizedName
|
||||
)
|
||||
|
||||
context = snapshot
|
||||
return snapshot
|
||||
}
|
||||
|
||||
func contextSnapshot() -> LockScreenDisplayContext? {
|
||||
if let context {
|
||||
return context
|
||||
}
|
||||
return refresh(reason: "snapshot-miss")
|
||||
}
|
||||
|
||||
private func registerObservers() {
|
||||
screenChangeObserver = NotificationCenter.default.addObserver(
|
||||
forName: NSApplication.didChangeScreenParametersNotification,
|
||||
object: nil,
|
||||
queue: .main
|
||||
) { [weak self] _ in
|
||||
self?.refresh(reason: "screen-parameters")
|
||||
}
|
||||
|
||||
let workspaceCenter = NSWorkspace.shared.notificationCenter
|
||||
let wakeObserver = workspaceCenter.addObserver(
|
||||
forName: NSWorkspace.screensDidWakeNotification,
|
||||
object: nil,
|
||||
queue: .main
|
||||
) { [weak self] _ in
|
||||
self?.refresh(reason: "screens-did-wake")
|
||||
}
|
||||
|
||||
let spaceObserver = workspaceCenter.addObserver(
|
||||
forName: NSWorkspace.activeSpaceDidChangeNotification,
|
||||
object: nil,
|
||||
queue: .main
|
||||
) { [weak self] _ in
|
||||
self?.refresh(reason: "space-changed")
|
||||
}
|
||||
|
||||
workspaceObservers = [wakeObserver, spaceObserver]
|
||||
}
|
||||
}
|
||||
|
|
@ -83,7 +83,7 @@ class LockScreenLiveActivityWindowManager {
|
|||
}
|
||||
|
||||
private func lockContext() -> (notchSize: CGSize, screen: NSScreen)? {
|
||||
guard let screen = NSScreen.main else {
|
||||
guard let screen = LockScreenDisplayContextProvider.shared.contextSnapshot()?.screen ?? NSScreen.main else {
|
||||
print("[\(timestamp())] LockScreenLiveActivityWindowManager: no main screen available")
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ class LockScreenManager: ObservableObject {
|
|||
print("[\(timestamp())] LockScreenManager: 🔒 Screen LOCKED event received")
|
||||
Logger.log("LockScreenManager: Screen locked", category: .lifecycle)
|
||||
LockSoundPlayer.shared.playLockChime()
|
||||
LockScreenDisplayContextProvider.shared.refresh(reason: "screen-locked")
|
||||
|
||||
// Update state SYNCHRONOUSLY without Task/await to avoid any delay
|
||||
lastUpdated = Date()
|
||||
|
|
@ -134,6 +135,7 @@ class LockScreenManager: ObservableObject {
|
|||
print("[\(timestamp())] LockScreenManager: 🔓 Screen UNLOCKED event received")
|
||||
Logger.log("LockScreenManager: Screen unlocked", category: .lifecycle)
|
||||
LockSoundPlayer.shared.playUnlockChime()
|
||||
LockScreenDisplayContextProvider.shared.refresh(reason: "screen-unlocked")
|
||||
lastUpdated = Date()
|
||||
updateIdleState(locked: false)
|
||||
isLocked = false
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ class LockScreenPanelManager {
|
|||
return
|
||||
}
|
||||
|
||||
guard let screen = NSScreen.main else {
|
||||
guard let screen = currentScreen() else {
|
||||
print("[\(timestamp())] LockScreenPanelManager: no main screen available")
|
||||
return
|
||||
}
|
||||
|
|
@ -188,7 +188,7 @@ class LockScreenPanelManager {
|
|||
}
|
||||
|
||||
func applyOffsetAdjustment(animated: Bool = true) {
|
||||
guard let screen = NSScreen.main else { return }
|
||||
guard let screen = currentScreen() else { return }
|
||||
let screenFrame = screen.frame
|
||||
let newCollapsed = collapsedFrame(for: screenFrame)
|
||||
collapsedFrame = newCollapsed
|
||||
|
|
@ -225,7 +225,7 @@ class LockScreenPanelManager {
|
|||
private func handleScreenGeometryChange(reason: String) {
|
||||
guard let window = panelWindow else { return }
|
||||
guard window.isVisible || panelAnimator.isPresented else { return }
|
||||
guard let screen = NSScreen.main else { return }
|
||||
guard let screen = currentScreen() else { return }
|
||||
|
||||
let screenFrame = screen.frame
|
||||
collapsedFrame = collapsedFrame(for: screenFrame)
|
||||
|
|
@ -245,4 +245,8 @@ class LockScreenPanelManager {
|
|||
let originY = baseOriginY + defaultLowering + clampedOffset
|
||||
return NSRect(x: originX, y: originY, width: collapsedSize.width, height: collapsedSize.height)
|
||||
}
|
||||
|
||||
private func currentScreen() -> NSScreen? {
|
||||
LockScreenDisplayContextProvider.shared.contextSnapshot()?.screen ?? NSScreen.main
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ final class LockScreenReminderWidgetPanelManager {
|
|||
}
|
||||
|
||||
func refreshPosition(animated: Bool) {
|
||||
guard let window, window.isVisible, let screen = NSScreen.main else { return }
|
||||
guard let window, window.isVisible, let screen = currentScreen() else { return }
|
||||
let target = frame(for: window.frame.size, on: screen)
|
||||
if animated {
|
||||
NSAnimationContext.runAnimationGroup { context in
|
||||
|
|
@ -49,7 +49,7 @@ final class LockScreenReminderWidgetPanelManager {
|
|||
}
|
||||
|
||||
private func render(snapshot: LockScreenReminderWidgetSnapshot, makeVisible: Bool) {
|
||||
guard let screen = NSScreen.main else { return }
|
||||
guard let screen = currentScreen() else { return }
|
||||
if !makeVisible, window == nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -161,5 +161,9 @@ final class LockScreenReminderWidgetPanelManager {
|
|||
refreshPosition(animated: false)
|
||||
print("LockScreenReminderWidgetPanelManager: realigned window due to \(reason)")
|
||||
}
|
||||
|
||||
private func currentScreen() -> NSScreen? {
|
||||
LockScreenDisplayContextProvider.shared.contextSnapshot()?.screen ?? NSScreen.main
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ final class LockScreenTimerWidgetPanelManager {
|
|||
}
|
||||
|
||||
func showWidget() {
|
||||
guard let screen = NSScreen.main else { return }
|
||||
guard let screen = currentScreen() else { return }
|
||||
let window = ensureWindow()
|
||||
let frame = targetFrame(on: screen)
|
||||
window.setFrame(frame, display: true)
|
||||
|
|
@ -150,7 +150,7 @@ final class LockScreenTimerWidgetPanelManager {
|
|||
|
||||
|
||||
func refreshPosition(animated: Bool) {
|
||||
guard let window, window.isVisible, let screen = NSScreen.main else { return }
|
||||
guard let window, window.isVisible, let screen = currentScreen() else { return }
|
||||
let frame = targetFrame(on: screen)
|
||||
if animated {
|
||||
NSAnimationContext.runAnimationGroup { context in
|
||||
|
|
@ -279,4 +279,8 @@ final class LockScreenTimerWidgetPanelManager {
|
|||
refreshPosition(animated: false)
|
||||
print("LockScreenTimerWidgetPanelManager: realigned window due to \(reason)")
|
||||
}
|
||||
|
||||
private func currentScreen() -> NSScreen? {
|
||||
LockScreenDisplayContextProvider.shared.contextSnapshot()?.screen ?? NSScreen.main
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,19 +35,32 @@ final class LockScreenWeatherManager: ObservableObject {
|
|||
}
|
||||
|
||||
locationProvider.prepareAuthorization()
|
||||
let existingSnapshot = snapshot
|
||||
let hadSnapshot = existingSnapshot != nil
|
||||
var cachedSnapshot = existingSnapshot
|
||||
|
||||
if cachedSnapshot == nil, let payload = latestWeatherPayload {
|
||||
let derivedSnapshot = makeSnapshot(from: payload)
|
||||
snapshot = derivedSnapshot
|
||||
cachedSnapshot = derivedSnapshot
|
||||
}
|
||||
|
||||
if let cachedSnapshot {
|
||||
deliver(cachedSnapshot, forceShow: true)
|
||||
}
|
||||
|
||||
let needsForceShowOnRefresh = cachedSnapshot == nil
|
||||
let shouldForceRefresh = !hadSnapshot
|
||||
|
||||
Task { @MainActor [weak self] in
|
||||
guard let self else { return }
|
||||
let snapshot = await self.refresh(force: self.snapshot == nil)
|
||||
let refreshedSnapshot = await self.refresh(force: shouldForceRefresh)
|
||||
|
||||
guard LockScreenManager.shared.currentLockStatus else {
|
||||
LockScreenWeatherPanelManager.shared.hide()
|
||||
return
|
||||
}
|
||||
guard LockScreenManager.shared.currentLockStatus else { return }
|
||||
|
||||
if let snapshot {
|
||||
self.deliver(snapshot, forceShow: true)
|
||||
} else {
|
||||
if let refreshedSnapshot {
|
||||
self.deliver(refreshedSnapshot, forceShow: needsForceShowOnRefresh)
|
||||
} else if needsForceShowOnRefresh {
|
||||
LockScreenWeatherPanelManager.shared.hide()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ final class LockScreenWeatherPanelManager {
|
|||
}
|
||||
|
||||
private func render(snapshot: LockScreenWeatherSnapshot, makeVisible: Bool) {
|
||||
guard let screen = NSScreen.main else { return }
|
||||
guard let screen = currentScreen() else { return }
|
||||
if !makeVisible, window == nil {
|
||||
return
|
||||
}
|
||||
|
|
@ -111,7 +111,7 @@ final class LockScreenWeatherPanelManager {
|
|||
|
||||
func refreshPositionForOffsets(animated: Bool = true) {
|
||||
guard let window, let snapshot = lastSnapshot else { return }
|
||||
guard let screen = NSScreen.main else { return }
|
||||
guard let screen = currentScreen() else { return }
|
||||
let size = lastContentSize ?? window.frame.size
|
||||
let targetFrame = frame(for: size, snapshot: snapshot, on: screen)
|
||||
latestFrame = targetFrame
|
||||
|
|
@ -153,4 +153,8 @@ final class LockScreenWeatherPanelManager {
|
|||
refreshPositionForOffsets(animated: false)
|
||||
print("LockScreenWeatherPanelManager: realigned window due to \(reason)")
|
||||
}
|
||||
|
||||
private func currentScreen() -> NSScreen? {
|
||||
LockScreenDisplayContextProvider.shared.contextSnapshot()?.screen ?? NSScreen.main
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue