mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 15:14:33 +00:00
Update stuff for lock screen widget width
This commit is contained in:
parent
e32c863442
commit
ab6877e59c
7 changed files with 200 additions and 17 deletions
|
|
@ -7493,8 +7493,9 @@
|
||||||
"comment" : "A description of how to interact with the music slot configuration view.",
|
"comment" : "A description of how to interact with the music slot configuration view.",
|
||||||
"isCommentAutoGenerated" : true
|
"isCommentAutoGenerated" : true
|
||||||
},
|
},
|
||||||
"Drag the previews to adjust vertical placement. Positive values lift the panel; negative values lower it. Changes apply instantly while the widgets are visible." : {
|
"Drag the previews to adjust vertical placement. Positive values lift the panel; negative values lower it. Use the width sliders below to narrow the media and timer widgets without exceeding their default size. Changes apply instantly while the widgets are visible." : {
|
||||||
|
"comment" : "A footer text describing the functionality of the lock screen positioning controls.",
|
||||||
|
"isCommentAutoGenerated" : true
|
||||||
},
|
},
|
||||||
"Drop files here" : {
|
"Drop files here" : {
|
||||||
"localizations" : {
|
"localizations" : {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,11 @@ struct LockScreenMusicPanel: View {
|
||||||
let usesLiquidGlass: Bool
|
let usesLiquidGlass: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
static let collapsedSize = CGSize(width: 420, height: 180)
|
static let collapsedHeight: CGFloat = 180
|
||||||
|
static let defaultCollapsedWidth: CGFloat = 420
|
||||||
|
static var collapsedSize: CGSize {
|
||||||
|
CGSize(width: CGFloat(Defaults[.lockScreenMusicPanelWidth]), height: collapsedHeight)
|
||||||
|
}
|
||||||
static let expandedSize = CGSize(width: 720, height: 340)
|
static let expandedSize = CGSize(width: 720, height: 340)
|
||||||
|
|
||||||
@ObservedObject var musicManager = MusicManager.shared
|
@ObservedObject var musicManager = MusicManager.shared
|
||||||
|
|
@ -45,6 +49,7 @@ struct LockScreenMusicPanel: View {
|
||||||
@Default(.musicSkipBehavior) private var musicSkipBehavior
|
@Default(.musicSkipBehavior) private var musicSkipBehavior
|
||||||
@Default(.enableLyrics) private var enableLyrics
|
@Default(.enableLyrics) private var enableLyrics
|
||||||
@Default(.lockScreenMusicAlbumParallaxEnabled) private var lockScreenParallaxEnabled
|
@Default(.lockScreenMusicAlbumParallaxEnabled) private var lockScreenParallaxEnabled
|
||||||
|
@Default(.lockScreenMusicPanelWidth) private var collapsedPanelWidth
|
||||||
|
|
||||||
init(animator: LockScreenPanelAnimator) {
|
init(animator: LockScreenPanelAnimator) {
|
||||||
_animator = ObservedObject(wrappedValue: animator)
|
_animator = ObservedObject(wrappedValue: animator)
|
||||||
|
|
@ -66,10 +71,14 @@ struct LockScreenMusicPanel: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
private var currentSize: CGSize {
|
private var currentSize: CGSize {
|
||||||
let base = isExpanded ? Self.expandedSize : Self.collapsedSize
|
let base = isExpanded ? Self.expandedSize : collapsedPanelSize
|
||||||
return CGSize(width: base.width, height: base.height + totalExtraHeight)
|
return CGSize(width: base.width, height: base.height + totalExtraHeight)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var collapsedPanelSize: CGSize {
|
||||||
|
CGSize(width: CGFloat(collapsedPanelWidth), height: Self.collapsedHeight)
|
||||||
|
}
|
||||||
|
|
||||||
private var panelCornerRadius: CGFloat {
|
private var panelCornerRadius: CGFloat {
|
||||||
isExpanded ? expandedPanelCornerRadius : collapsedPanelCornerRadius
|
isExpanded ? expandedPanelCornerRadius : collapsedPanelCornerRadius
|
||||||
}
|
}
|
||||||
|
|
@ -95,7 +104,7 @@ struct LockScreenMusicPanel: View {
|
||||||
panelContent
|
panelContent
|
||||||
} else {
|
} else {
|
||||||
Color.clear
|
Color.clear
|
||||||
.frame(width: Self.collapsedSize.width, height: Self.collapsedSize.height)
|
.frame(width: collapsedPanelSize.width, height: collapsedPanelSize.height)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,11 @@ import SwiftUI
|
||||||
import Defaults
|
import Defaults
|
||||||
|
|
||||||
struct LockScreenTimerWidget: View {
|
struct LockScreenTimerWidget: View {
|
||||||
static let preferredSize = CGSize(width: 420, height: 96)
|
static let defaultWidth: Double = 420
|
||||||
|
static let preferredHeight: CGFloat = 96
|
||||||
|
static var preferredSize: CGSize {
|
||||||
|
CGSize(width: CGFloat(Defaults[.lockScreenTimerWidgetWidth]), height: preferredHeight)
|
||||||
|
}
|
||||||
static let cornerRadius: CGFloat = 26
|
static let cornerRadius: CGFloat = 26
|
||||||
|
|
||||||
@ObservedObject private var animator: LockScreenTimerWidgetAnimator
|
@ObservedObject private var animator: LockScreenTimerWidgetAnimator
|
||||||
|
|
@ -12,6 +16,7 @@ struct LockScreenTimerWidget: View {
|
||||||
@Default(.lockScreenTimerLiquidGlassVariant) private var timerGlassVariant
|
@Default(.lockScreenTimerLiquidGlassVariant) private var timerGlassVariant
|
||||||
@Default(.lockScreenTimerWidgetUsesBlur) private var timerGlassModeIsGlass
|
@Default(.lockScreenTimerWidgetUsesBlur) private var timerGlassModeIsGlass
|
||||||
@Default(.timerPresets) private var timerPresets
|
@Default(.timerPresets) private var timerPresets
|
||||||
|
@Default(.lockScreenTimerWidgetWidth) private var widgetWidth
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
init(animator: LockScreenTimerWidgetAnimator? = nil) {
|
init(animator: LockScreenTimerWidgetAnimator? = nil) {
|
||||||
|
|
@ -146,6 +151,10 @@ struct LockScreenTimerWidget: View {
|
||||||
.fill(Color.black.opacity(0.65))
|
.fill(Color.black.opacity(0.65))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var widgetSize: CGSize {
|
||||||
|
CGSize(width: CGFloat(widgetWidth), height: Self.preferredHeight)
|
||||||
|
}
|
||||||
|
|
||||||
private var pauseIcon: String {
|
private var pauseIcon: String {
|
||||||
timerManager.isPaused ? "play.fill" : "pause.fill"
|
timerManager.isPaused ? "play.fill" : "pause.fill"
|
||||||
}
|
}
|
||||||
|
|
@ -175,7 +184,7 @@ struct LockScreenTimerWidget: View {
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 12)
|
.padding(.horizontal, 12)
|
||||||
.padding(.vertical, 18)
|
.padding(.vertical, 18)
|
||||||
.frame(width: Self.preferredSize.width, height: Self.preferredSize.height)
|
.frame(width: widgetSize.width, height: widgetSize.height)
|
||||||
.background(widgetBackground)
|
.background(widgetBackground)
|
||||||
.clipShape(RoundedRectangle(cornerRadius: Self.cornerRadius, style: .continuous))
|
.clipShape(RoundedRectangle(cornerRadius: Self.cornerRadius, style: .continuous))
|
||||||
.overlay {
|
.overlay {
|
||||||
|
|
|
||||||
|
|
@ -3648,14 +3648,18 @@ private struct LockScreenPositioningControls: View {
|
||||||
@Default(.lockScreenWeatherVerticalOffset) private var weatherOffset
|
@Default(.lockScreenWeatherVerticalOffset) private var weatherOffset
|
||||||
@Default(.lockScreenMusicVerticalOffset) private var musicOffset
|
@Default(.lockScreenMusicVerticalOffset) private var musicOffset
|
||||||
@Default(.lockScreenTimerVerticalOffset) private var timerOffset
|
@Default(.lockScreenTimerVerticalOffset) private var timerOffset
|
||||||
|
@Default(.lockScreenMusicPanelWidth) private var musicWidth
|
||||||
|
@Default(.lockScreenTimerWidgetWidth) private var timerWidth
|
||||||
private let offsetRange: ClosedRange<Double> = -160...160
|
private let offsetRange: ClosedRange<Double> = -160...160
|
||||||
|
private let musicWidthRange: ClosedRange<Double> = 320...Double(LockScreenMusicPanel.defaultCollapsedWidth)
|
||||||
|
private let timerWidthRange: ClosedRange<Double> = 320...LockScreenTimerWidget.defaultWidth
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Section {
|
Section {
|
||||||
let weatherBinding = Binding<Double>(
|
let weatherBinding = Binding<Double>(
|
||||||
get: { weatherOffset },
|
get: { weatherOffset },
|
||||||
set: { newValue in
|
set: { newValue in
|
||||||
let clampedValue = clamp(newValue)
|
let clampedValue = clampOffset(newValue)
|
||||||
if weatherOffset != clampedValue {
|
if weatherOffset != clampedValue {
|
||||||
weatherOffset = clampedValue
|
weatherOffset = clampedValue
|
||||||
}
|
}
|
||||||
|
|
@ -3666,7 +3670,7 @@ private struct LockScreenPositioningControls: View {
|
||||||
let timerBinding = Binding<Double>(
|
let timerBinding = Binding<Double>(
|
||||||
get: { timerOffset },
|
get: { timerOffset },
|
||||||
set: { newValue in
|
set: { newValue in
|
||||||
let clampedValue = clamp(newValue)
|
let clampedValue = clampOffset(newValue)
|
||||||
if timerOffset != clampedValue {
|
if timerOffset != clampedValue {
|
||||||
timerOffset = clampedValue
|
timerOffset = clampedValue
|
||||||
}
|
}
|
||||||
|
|
@ -3677,7 +3681,7 @@ private struct LockScreenPositioningControls: View {
|
||||||
let musicBinding = Binding<Double>(
|
let musicBinding = Binding<Double>(
|
||||||
get: { musicOffset },
|
get: { musicOffset },
|
||||||
set: { newValue in
|
set: { newValue in
|
||||||
let clampedValue = clamp(newValue)
|
let clampedValue = clampOffset(newValue)
|
||||||
if musicOffset != clampedValue {
|
if musicOffset != clampedValue {
|
||||||
musicOffset = clampedValue
|
musicOffset = clampedValue
|
||||||
}
|
}
|
||||||
|
|
@ -3685,7 +3689,35 @@ private struct LockScreenPositioningControls: View {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
LockScreenPositioningPreview(weatherOffset: weatherBinding, timerOffset: timerBinding, musicOffset: musicBinding)
|
let musicWidthBinding = Binding<Double>(
|
||||||
|
get: { musicWidth },
|
||||||
|
set: { newValue in
|
||||||
|
let clampedValue = clamp(newValue, within: musicWidthRange)
|
||||||
|
if musicWidth != clampedValue {
|
||||||
|
musicWidth = clampedValue
|
||||||
|
propagateMusicWidthChange(animated: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
let timerWidthBinding = Binding<Double>(
|
||||||
|
get: { timerWidth },
|
||||||
|
set: { newValue in
|
||||||
|
let clampedValue = clamp(newValue, within: timerWidthRange)
|
||||||
|
if timerWidth != clampedValue {
|
||||||
|
timerWidth = clampedValue
|
||||||
|
propagateTimerWidthChange(animated: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
LockScreenPositioningPreview(
|
||||||
|
weatherOffset: weatherBinding,
|
||||||
|
timerOffset: timerBinding,
|
||||||
|
musicOffset: musicBinding,
|
||||||
|
musicWidth: musicWidthBinding,
|
||||||
|
timerWidth: timerWidthBinding
|
||||||
|
)
|
||||||
.frame(height: 260)
|
.frame(height: 260)
|
||||||
.padding(.vertical, 8)
|
.padding(.vertical, 8)
|
||||||
|
|
||||||
|
|
@ -3719,18 +3751,45 @@ private struct LockScreenPositioningControls: View {
|
||||||
|
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Divider()
|
||||||
|
.padding(.vertical, 8)
|
||||||
|
|
||||||
|
VStack(alignment: .leading, spacing: 16) {
|
||||||
|
widthSlider(
|
||||||
|
title: "Media Panel Width",
|
||||||
|
value: musicWidthBinding,
|
||||||
|
range: musicWidthRange,
|
||||||
|
resetTitle: "Reset Media Width",
|
||||||
|
resetAction: resetMusicWidth,
|
||||||
|
helpText: "Shrinks the lock screen media panel while keeping the expanded view full width."
|
||||||
|
)
|
||||||
|
|
||||||
|
widthSlider(
|
||||||
|
title: "Timer Widget Width",
|
||||||
|
value: timerWidthBinding,
|
||||||
|
range: timerWidthRange,
|
||||||
|
resetTitle: "Reset Timer Width",
|
||||||
|
resetAction: resetTimerWidth,
|
||||||
|
helpText: "Adjusts the lock screen timer widget width without affecting button sizing."
|
||||||
|
)
|
||||||
|
}
|
||||||
} header: {
|
} header: {
|
||||||
Text("Lock Screen Positioning")
|
Text("Lock Screen Positioning")
|
||||||
} footer: {
|
} footer: {
|
||||||
Text("Drag the previews to adjust vertical placement. Positive values lift the panel; negative values lower it. Changes apply instantly while the widgets are visible.")
|
Text("Drag the previews to adjust vertical placement. Positive values lift the panel; negative values lower it. Use the width sliders below to narrow the media and timer widgets without exceeding their default size. Changes apply instantly while the widgets are visible.")
|
||||||
.textCase(nil)
|
.textCase(nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func clamp(_ value: Double) -> Double {
|
private func clampOffset(_ value: Double) -> Double {
|
||||||
min(max(value, offsetRange.lowerBound), offsetRange.upperBound)
|
min(max(value, offsetRange.lowerBound), offsetRange.upperBound)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func clamp(_ value: Double, within range: ClosedRange<Double>) -> Double {
|
||||||
|
min(max(value, range.lowerBound), range.upperBound)
|
||||||
|
}
|
||||||
|
|
||||||
private func resetWeatherOffset() {
|
private func resetWeatherOffset() {
|
||||||
weatherOffset = 0
|
weatherOffset = 0
|
||||||
propagateWeatherOffsetChange(animated: true)
|
propagateWeatherOffsetChange(animated: true)
|
||||||
|
|
@ -3746,6 +3805,16 @@ private struct LockScreenPositioningControls: View {
|
||||||
propagateMusicOffsetChange(animated: true)
|
propagateMusicOffsetChange(animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func resetMusicWidth() {
|
||||||
|
musicWidth = Double(LockScreenMusicPanel.defaultCollapsedWidth)
|
||||||
|
propagateMusicWidthChange(animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func resetTimerWidth() {
|
||||||
|
timerWidth = LockScreenTimerWidget.defaultWidth
|
||||||
|
propagateTimerWidthChange(animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
private func propagateWeatherOffsetChange(animated: Bool) {
|
private func propagateWeatherOffsetChange(animated: Bool) {
|
||||||
Task { @MainActor in
|
Task { @MainActor in
|
||||||
LockScreenWeatherPanelManager.shared.refreshPositionForOffsets(animated: animated)
|
LockScreenWeatherPanelManager.shared.refreshPositionForOffsets(animated: animated)
|
||||||
|
|
@ -3764,6 +3833,18 @@ private struct LockScreenPositioningControls: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func propagateMusicWidthChange(animated: Bool) {
|
||||||
|
Task { @MainActor in
|
||||||
|
LockScreenPanelManager.shared.applyOffsetAdjustment(animated: animated)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func propagateTimerWidthChange(animated: Bool) {
|
||||||
|
Task { @MainActor in
|
||||||
|
LockScreenTimerWidgetPanelManager.shared.refreshPosition(animated: animated)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private func offsetColumn(title: String, value: Double, resetTitle: String, resetAction: @escaping () -> Void) -> some View {
|
private func offsetColumn(title: String, value: Double, resetTitle: String, resetAction: @escaping () -> Void) -> some View {
|
||||||
VStack(alignment: .leading, spacing: 6) {
|
VStack(alignment: .leading, spacing: 6) {
|
||||||
|
|
@ -3781,15 +3862,57 @@ private struct LockScreenPositioningControls: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
private func widthSlider(
|
||||||
|
title: String,
|
||||||
|
value: Binding<Double>,
|
||||||
|
range: ClosedRange<Double>,
|
||||||
|
resetTitle: String,
|
||||||
|
resetAction: @escaping () -> Void,
|
||||||
|
helpText: String
|
||||||
|
) -> some View {
|
||||||
|
VStack(alignment: .leading, spacing: 6) {
|
||||||
|
HStack {
|
||||||
|
Text(title)
|
||||||
|
Spacer()
|
||||||
|
Text(formattedWidth(value.wrappedValue))
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
}
|
||||||
|
|
||||||
|
Slider(value: value, in: range)
|
||||||
|
|
||||||
|
HStack(alignment: .top) {
|
||||||
|
Button(resetTitle) {
|
||||||
|
resetAction()
|
||||||
|
}
|
||||||
|
.buttonStyle(.bordered)
|
||||||
|
|
||||||
|
Spacer()
|
||||||
|
|
||||||
|
Text(helpText)
|
||||||
|
.font(.caption)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
|
.multilineTextAlignment(.trailing)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func formattedPoints(_ value: Double) -> String {
|
private func formattedPoints(_ value: Double) -> String {
|
||||||
String(format: "%+.0f", value)
|
String(format: "%+.0f", value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func formattedWidth(_ value: Double) -> String {
|
||||||
|
String(format: "%.0f pt", value)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct LockScreenPositioningPreview: View {
|
private struct LockScreenPositioningPreview: View {
|
||||||
@Binding var weatherOffset: Double
|
@Binding var weatherOffset: Double
|
||||||
@Binding var timerOffset: Double
|
@Binding var timerOffset: Double
|
||||||
@Binding var musicOffset: Double
|
@Binding var musicOffset: Double
|
||||||
|
@Binding var musicWidth: Double
|
||||||
|
@Binding var timerWidth: Double
|
||||||
|
|
||||||
@State private var weatherStartOffset: Double = 0
|
@State private var weatherStartOffset: Double = 0
|
||||||
@State private var timerStartOffset: Double = 0
|
@State private var timerStartOffset: Double = 0
|
||||||
|
|
@ -3815,8 +3938,17 @@ private struct LockScreenPositioningPreview: View {
|
||||||
let timerBaseY = screenRect.minY + (screenRect.height * 0.5)
|
let timerBaseY = screenRect.minY + (screenRect.height * 0.5)
|
||||||
let musicBaseY = screenRect.minY + (screenRect.height * 0.78)
|
let musicBaseY = screenRect.minY + (screenRect.height * 0.78)
|
||||||
let weatherSize = CGSize(width: screenRect.width * 0.42, height: screenRect.height * 0.22)
|
let weatherSize = CGSize(width: screenRect.width * 0.42, height: screenRect.height * 0.22)
|
||||||
let timerSize = CGSize(width: screenRect.width * 0.5, height: screenRect.height * 0.2)
|
let defaultMusicWidth = Double(LockScreenMusicPanel.defaultCollapsedWidth)
|
||||||
let musicSize = CGSize(width: screenRect.width * 0.56, height: screenRect.height * 0.34)
|
let musicWidthScale = CGFloat(musicWidth / defaultMusicWidth)
|
||||||
|
let timerWidthScale = CGFloat(timerWidth / LockScreenTimerWidget.defaultWidth)
|
||||||
|
let timerSize = CGSize(
|
||||||
|
width: (screenRect.width * 0.5) * timerWidthScale,
|
||||||
|
height: screenRect.height * 0.2
|
||||||
|
)
|
||||||
|
let musicSize = CGSize(
|
||||||
|
width: (screenRect.width * 0.56) * musicWidthScale,
|
||||||
|
height: screenRect.height * 0.34
|
||||||
|
)
|
||||||
|
|
||||||
ZStack {
|
ZStack {
|
||||||
RoundedRectangle(cornerRadius: screenCornerRadius, style: .continuous)
|
RoundedRectangle(cornerRadius: screenCornerRadius, style: .continuous)
|
||||||
|
|
@ -3844,6 +3976,9 @@ private struct LockScreenPositioningPreview: View {
|
||||||
}
|
}
|
||||||
.animation(.interactiveSpring(response: 0.3, dampingFraction: 0.82), value: weatherOffset)
|
.animation(.interactiveSpring(response: 0.3, dampingFraction: 0.82), value: weatherOffset)
|
||||||
.animation(.interactiveSpring(response: 0.3, dampingFraction: 0.82), value: musicOffset)
|
.animation(.interactiveSpring(response: 0.3, dampingFraction: 0.82), value: musicOffset)
|
||||||
|
.animation(.interactiveSpring(response: 0.3, dampingFraction: 0.82), value: timerOffset)
|
||||||
|
.animation(.interactiveSpring(response: 0.3, dampingFraction: 0.82), value: musicWidth)
|
||||||
|
.animation(.interactiveSpring(response: 0.3, dampingFraction: 0.82), value: timerWidth)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func weatherPanel(size: CGSize) -> some View {
|
private func weatherPanel(size: CGSize) -> some View {
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import AppKit
|
||||||
import SkyLightWindow
|
import SkyLightWindow
|
||||||
import Defaults
|
import Defaults
|
||||||
import QuartzCore
|
import QuartzCore
|
||||||
|
import Combine
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
final class LockScreenPanelAnimator: ObservableObject {
|
final class LockScreenPanelAnimator: ObservableObject {
|
||||||
|
|
@ -32,10 +33,12 @@ class LockScreenPanelManager {
|
||||||
private var hideTask: Task<Void, Never>?
|
private var hideTask: Task<Void, Never>?
|
||||||
private var screenChangeObserver: NSObjectProtocol?
|
private var screenChangeObserver: NSObjectProtocol?
|
||||||
private var workspaceObservers: [NSObjectProtocol] = []
|
private var workspaceObservers: [NSObjectProtocol] = []
|
||||||
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
private init() {
|
private init() {
|
||||||
print("[\(timestamp())] LockScreenPanelManager: initialized")
|
print("[\(timestamp())] LockScreenPanelManager: initialized")
|
||||||
registerScreenChangeObservers()
|
registerScreenChangeObservers()
|
||||||
|
observeDefaultChanges()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func timestamp() -> String {
|
private func timestamp() -> String {
|
||||||
|
|
@ -120,6 +123,7 @@ class LockScreenPanelManager {
|
||||||
|
|
||||||
let hosting = NSHostingView(rootView: LockScreenMusicPanel(animator: panelAnimator))
|
let hosting = NSHostingView(rootView: LockScreenMusicPanel(animator: panelAnimator))
|
||||||
hosting.frame = NSRect(origin: .zero, size: targetFrame.size)
|
hosting.frame = NSRect(origin: .zero, size: targetFrame.size)
|
||||||
|
hosting.autoresizingMask = [.width, .height]
|
||||||
window.contentView = hosting
|
window.contentView = hosting
|
||||||
|
|
||||||
// Ensure the underlying window content is clipped to rounded corners
|
// Ensure the underlying window content is clipped to rounded corners
|
||||||
|
|
@ -235,6 +239,15 @@ class LockScreenPanelManager {
|
||||||
print("[\(timestamp())] LockScreenPanelManager: realigned window due to \(reason)")
|
print("[\(timestamp())] LockScreenPanelManager: realigned window due to \(reason)")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func observeDefaultChanges() {
|
||||||
|
Defaults.publisher(.lockScreenMusicPanelWidth)
|
||||||
|
.receive(on: RunLoop.main)
|
||||||
|
.sink { [weak self] _ in
|
||||||
|
self?.applyOffsetAdjustment(animated: true)
|
||||||
|
}
|
||||||
|
.store(in: &cancellables)
|
||||||
|
}
|
||||||
|
|
||||||
private func collapsedFrame(for screenFrame: NSRect) -> NSRect {
|
private func collapsedFrame(for screenFrame: NSRect) -> NSRect {
|
||||||
let collapsedSize = LockScreenMusicPanel.collapsedSize
|
let collapsedSize = LockScreenMusicPanel.collapsedSize
|
||||||
let originX = screenFrame.midX - (collapsedSize.width / 2)
|
let originX = screenFrame.midX - (collapsedSize.width / 2)
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,13 @@ final class LockScreenTimerWidgetManager {
|
||||||
self.refreshPositionForOffsets(animated: true)
|
self.refreshPositionForOffsets(animated: true)
|
||||||
}
|
}
|
||||||
.store(in: &cancellables)
|
.store(in: &cancellables)
|
||||||
|
|
||||||
|
Defaults.publisher(.lockScreenTimerWidgetWidth, options: [])
|
||||||
|
.receive(on: RunLoop.main)
|
||||||
|
.sink { _ in
|
||||||
|
LockScreenTimerWidgetPanelManager.shared.refreshPosition(animated: true)
|
||||||
|
}
|
||||||
|
.store(in: &cancellables)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateVisibility() {
|
private func updateVisibility() {
|
||||||
|
|
@ -114,6 +121,7 @@ final class LockScreenTimerWidgetPanelManager {
|
||||||
let window = ensureWindow()
|
let window = ensureWindow()
|
||||||
let frame = targetFrame(on: screen)
|
let frame = targetFrame(on: screen)
|
||||||
window.setFrame(frame, display: true)
|
window.setFrame(frame, display: true)
|
||||||
|
updateHostingViewSize(for: window, size: frame.size)
|
||||||
latestFrame = frame
|
latestFrame = frame
|
||||||
window.alphaValue = 1
|
window.alphaValue = 1
|
||||||
window.orderFrontRegardless()
|
window.orderFrontRegardless()
|
||||||
|
|
@ -161,6 +169,7 @@ final class LockScreenTimerWidgetPanelManager {
|
||||||
} else {
|
} else {
|
||||||
window.setFrame(frame, display: true)
|
window.setFrame(frame, display: true)
|
||||||
}
|
}
|
||||||
|
updateHostingViewSize(for: window, size: frame.size)
|
||||||
latestFrame = frame
|
latestFrame = frame
|
||||||
LockScreenReminderWidgetPanelManager.shared.refreshPosition(animated: animated)
|
LockScreenReminderWidgetPanelManager.shared.refreshPosition(animated: animated)
|
||||||
}
|
}
|
||||||
|
|
@ -212,12 +221,17 @@ final class LockScreenTimerWidgetPanelManager {
|
||||||
let view = LockScreenTimerWidget(animator: animator)
|
let view = LockScreenTimerWidget(animator: animator)
|
||||||
let hosting = NSHostingView(rootView: view)
|
let hosting = NSHostingView(rootView: view)
|
||||||
hosting.frame = NSRect(origin: .zero, size: LockScreenTimerWidget.preferredSize)
|
hosting.frame = NSRect(origin: .zero, size: LockScreenTimerWidget.preferredSize)
|
||||||
|
hosting.autoresizingMask = [.width, .height]
|
||||||
hosting.wantsLayer = true
|
hosting.wantsLayer = true
|
||||||
hosting.layer?.masksToBounds = true
|
hosting.layer?.masksToBounds = true
|
||||||
hosting.layer?.cornerRadius = LockScreenTimerWidget.cornerRadius
|
hosting.layer?.cornerRadius = LockScreenTimerWidget.cornerRadius
|
||||||
return hosting
|
return hosting
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func updateHostingViewSize(for window: NSWindow, size: CGSize) {
|
||||||
|
window.contentView?.frame = NSRect(origin: .zero, size: NSSize(width: size.width, height: size.height))
|
||||||
|
}
|
||||||
|
|
||||||
private func targetFrame(on screen: NSScreen) -> NSRect {
|
private func targetFrame(on screen: NSScreen) -> NSRect {
|
||||||
let size = LockScreenTimerWidget.preferredSize
|
let size = LockScreenTimerWidget.preferredSize
|
||||||
let originX = screen.frame.midX - (size.width / 2)
|
let originX = screen.frame.midX - (size.width / 2)
|
||||||
|
|
|
||||||
|
|
@ -700,8 +700,10 @@ extension Defaults.Keys {
|
||||||
static let lockScreenWeatherProviderSource = Key<LockScreenWeatherProviderSource>("lockScreenWeatherProviderSource", default: .openMeteo)
|
static let lockScreenWeatherProviderSource = Key<LockScreenWeatherProviderSource>("lockScreenWeatherProviderSource", default: .openMeteo)
|
||||||
static let lockScreenWeatherVerticalOffset = Key<Double>("lockScreenWeatherVerticalOffset", default: 0)
|
static let lockScreenWeatherVerticalOffset = Key<Double>("lockScreenWeatherVerticalOffset", default: 0)
|
||||||
static let lockScreenMusicVerticalOffset = Key<Double>("lockScreenMusicVerticalOffset", default: 0)
|
static let lockScreenMusicVerticalOffset = Key<Double>("lockScreenMusicVerticalOffset", default: 0)
|
||||||
|
static let lockScreenMusicPanelWidth = Key<Double>("lockScreenMusicPanelWidth", default: 420)
|
||||||
static let lockScreenMusicAlbumParallaxEnabled = Key<Bool>("lockScreenMusicAlbumParallaxEnabled", default: false)
|
static let lockScreenMusicAlbumParallaxEnabled = Key<Bool>("lockScreenMusicAlbumParallaxEnabled", default: false)
|
||||||
static let lockScreenTimerVerticalOffset = Key<Double>("lockScreenTimerVerticalOffset", default: 0)
|
static let lockScreenTimerVerticalOffset = Key<Double>("lockScreenTimerVerticalOffset", default: 0)
|
||||||
|
static let lockScreenTimerWidgetWidth = Key<Double>("lockScreenTimerWidgetWidth", default: 420)
|
||||||
static let lockScreenGlassStyle = Key<LockScreenGlassStyle>("lockScreenGlassStyle", default: .liquid)
|
static let lockScreenGlassStyle = Key<LockScreenGlassStyle>("lockScreenGlassStyle", default: .liquid)
|
||||||
static let lockScreenGlassCustomizationMode = Key<LockScreenGlassCustomizationMode>(
|
static let lockScreenGlassCustomizationMode = Key<LockScreenGlassCustomizationMode>(
|
||||||
"lockScreenGlassCustomizationMode",
|
"lockScreenGlassCustomizationMode",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue