Hopefully fixed the Positioning

This commit is contained in:
Hariharan Mudaliar 2026-02-07 23:42:06 +05:30
commit 40e805a785
No known key found for this signature in database
GPG key ID: 025583EB0BE5640E
3 changed files with 145 additions and 9 deletions

View file

@ -827,6 +827,18 @@
} }
} }
}, },
"%@. Focus active: %@" : {
"comment" : "A row that combines a calendar event with a focus indicator. The first argument is the text of the calendar event. The second argument is the name of the focus.",
"isCommentAutoGenerated" : true,
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "%1$@. Focus active: %2$@"
}
}
}
},
"%@°" : { "%@°" : {
"comment" : "A label inside the temperature gauge that shows the current temperature. The value is shown in degrees Celsius (or Fahrenheit, if specified in the snapshot).", "comment" : "A label inside the temperature gauge that shows the current temperature. The value is shown in degrees Celsius (or Fahrenheit, if specified in the snapshot).",
"isCommentAutoGenerated" : true, "isCommentAutoGenerated" : true,

View file

@ -393,6 +393,14 @@ struct LockScreenWeatherWidget: View {
lockScreenShowCalendarEvent && nextCalendarEvent != nil lockScreenShowCalendarEvent && nextCalendarEvent != nil
} }
private var shouldShowFocusInCalendarSlot: Bool {
shouldShowFocusWidget && !shouldShowCalendarRow
}
private var shouldCombineFocusAndCalendar: Bool {
shouldShowFocusWidget && shouldShowCalendarRow
}
private enum RowKind { private enum RowKind {
case weather case weather
case focus case focus
@ -422,6 +430,8 @@ struct LockScreenWeatherWidget: View {
var enabled: Set<RowKind> = [.weather] var enabled: Set<RowKind> = [.weather]
if lockScreenShowCalendarEvent { enabled.insert(.calendar) } if lockScreenShowCalendarEvent { enabled.insert(.calendar) }
if shouldShowFocusWidget { enabled.insert(.focus) } if shouldShowFocusWidget { enabled.insert(.focus) }
if shouldShowFocusInCalendarSlot { enabled.insert(.calendar); enabled.remove(.focus) }
if shouldCombineFocusAndCalendar { enabled.remove(.focus) }
return orderedRowKinds.filter { enabled.contains($0) } return orderedRowKinds.filter { enabled.contains($0) }
} }
@ -462,8 +472,13 @@ struct LockScreenWeatherWidget: View {
private var isInline: Bool { snapshot.widgetStyle == .inline } private var isInline: Bool { snapshot.widgetStyle == .inline }
private var stackAlignment: VerticalAlignment { isInline ? .firstTextBaseline : .top } private var stackAlignment: VerticalAlignment { isInline ? .firstTextBaseline : .top }
private var stackSpacing: CGFloat { isInline ? 14 : 22 } private var stackSpacing: CGFloat { isInline ? 14 : 22 }
private var mainRowAlignment: Alignment { isInline ? .leading : .center } private var mainRowAlignment: Alignment {
private var secondaryRowAlignment: Alignment { isInline ? .leading : .center } if isInline {
return shouldCombineFocusAndCalendar ? .center : .leading
}
return .center
}
private var secondaryRowAlignment: Alignment { .center }
private var focusRowAlignment: Alignment { .center } private var focusRowAlignment: Alignment { .center }
private var gaugeDiameter: CGFloat { 64 } private var gaugeDiameter: CGFloat { 64 }
private var topPadding: CGFloat { isInline ? 6 : 22 } private var topPadding: CGFloat { isInline ? 6 : 22 }
@ -570,15 +585,44 @@ struct LockScreenWeatherWidget: View {
case .weather: case .weather:
mainWidgetRow mainWidgetRow
case .calendar: case .calendar:
nextEventRow if shouldCombineFocusAndCalendar {
combinedFocusCalendarRow
} else if shouldShowFocusInCalendarSlot {
focusRowInCalendarSlot
} else {
nextEventRow
}
case .focus: case .focus:
focusWidget if shouldCombineFocusAndCalendar {
.opacity(shouldShowFocusWidget ? 1 : 0) EmptyView()
.accessibilityHidden(!shouldShowFocusWidget) } else {
.allowsHitTesting(false) focusWidget
.opacity(shouldShowFocusWidget ? 1 : 0)
.accessibilityHidden(!shouldShowFocusWidget)
.allowsHitTesting(false)
}
} }
} }
private var focusRowInCalendarSlot: some View {
HStack(alignment: .center, spacing: 8) {
focusIcon
.font(.system(size: 20, weight: .semibold))
.frame(width: 26, height: 26)
Text(focusDisplayName)
.font(inlinePrimaryFont)
.lineLimit(1)
.minimumScaleFactor(0.85)
.allowsTightening(true)
}
.frame(maxWidth: .infinity, alignment: secondaryRowAlignment)
.padding(.horizontal, 2)
.opacity(shouldShowFocusWidget ? 1 : 0)
.accessibilityLabel("Focus active: \(focusDisplayName)")
.allowsHitTesting(false)
}
private var mainWidgetRow: some View { private var mainWidgetRow: some View {
HStack(alignment: stackAlignment, spacing: stackSpacing) { HStack(alignment: stackAlignment, spacing: stackSpacing) {
if let charging = snapshot.charging { if let charging = snapshot.charging {
@ -627,6 +671,77 @@ struct LockScreenWeatherWidget: View {
) )
} }
private var combinedFocusCalendarRow: some View {
let event = nextCalendarEvent
let line = shouldShowCalendarRow ? eventLineText(for: event) : lastCalendarLine
let iconConfig = calendarRowIconConfig(for: event)
return HStack(alignment: .center, spacing: 10) {
HStack(alignment: .center, spacing: 6) {
calendarIconView(iconConfig)
.frame(width: 26, height: 26)
Text(line)
.font(inlinePrimaryFont)
.lineLimit(1)
.minimumScaleFactor(0.85)
.allowsTightening(true)
}
.layoutPriority(2)
Text("•")
.font(inlinePrimaryFont)
.foregroundStyle(secondaryLabelColor)
.accessibilityHidden(true)
HStack(alignment: .center, spacing: 8) {
focusIcon
.font(.system(size: 20, weight: .semibold))
.frame(width: 26, height: 26)
Text(focusDisplayName)
.font(inlinePrimaryFont)
.lineLimit(1)
.minimumScaleFactor(0.85)
.allowsTightening(true)
}
.layoutPriority(1)
}
.frame(maxWidth: .infinity, alignment: .center)
.padding(.horizontal, 2)
.id(calendarRowRenderToken)
.opacity(shouldShowFocusWidget && shouldShowCalendarRow ? 1 : 0)
.accessibilityLabel("\(line). Focus active: \(focusDisplayName)")
.allowsHitTesting(false)
}
@ViewBuilder
private func calendarIconView(_ iconConfig: (name: String, renderingMode: SymbolRenderingMode, colors: [Color])) -> some View {
let image = Image(systemName: iconConfig.name)
.font(.system(size: 20, weight: .semibold))
let isPalette = String(describing: iconConfig.renderingMode)
.lowercased()
.contains("palette")
if isPalette, iconConfig.colors.count >= 2 {
image
.symbolRenderingMode(.palette)
.foregroundStyle(iconConfig.colors[0], iconConfig.colors[1])
} else if iconConfig.colors.count >= 2 {
image
.symbolRenderingMode(iconConfig.renderingMode)
.foregroundStyle(iconConfig.colors[0], iconConfig.colors[1])
} else if iconConfig.colors.count == 1 {
image
.symbolRenderingMode(iconConfig.renderingMode)
.foregroundStyle(iconConfig.colors[0])
} else {
image
.symbolRenderingMode(iconConfig.renderingMode)
}
}
private func calendarRowIconConfig(for event: EventModel?) -> (name: String, renderingMode: SymbolRenderingMode, colors: [Color]) { private func calendarRowIconConfig(for event: EventModel?) -> (name: String, renderingMode: SymbolRenderingMode, colors: [Color]) {
guard let event else { guard let event else {
return (name: "calendar.badge", renderingMode: .hierarchical, colors: [Color.white.opacity(0.9)]) return (name: "calendar.badge", renderingMode: .hierarchical, colors: [Color.white.opacity(0.9)])

View file

@ -31,6 +31,7 @@ final class LockScreenWeatherPanelManager {
private(set) var latestFrame: NSRect? private(set) var latestFrame: NSRect?
private var lastSnapshot: LockScreenWeatherSnapshot? private var lastSnapshot: LockScreenWeatherSnapshot?
private var lastContentSize: CGSize? private var lastContentSize: CGSize?
private var lastInlineBaselineHeight: CGFloat = 0
private var screenChangeObserver: NSObjectProtocol? private var screenChangeObserver: NSObjectProtocol?
private var workspaceObservers: [NSObjectProtocol] = [] private var workspaceObservers: [NSObjectProtocol] = []
@ -64,6 +65,9 @@ final class LockScreenWeatherPanelManager {
let view = LockScreenWeatherWidget(snapshot: snapshot) let view = LockScreenWeatherWidget(snapshot: snapshot)
let hostingView = NSHostingView(rootView: view) let hostingView = NSHostingView(rootView: view)
let fittingSize = hostingView.fittingSize let fittingSize = hostingView.fittingSize
if snapshot.widgetStyle == .inline {
lastInlineBaselineHeight = max(lastInlineBaselineHeight, fittingSize.height)
}
hostingView.frame = NSRect(origin: .zero, size: fittingSize) hostingView.frame = NSRect(origin: .zero, size: fittingSize)
let targetFrame = frame(for: fittingSize, snapshot: snapshot, on: screen) let targetFrame = frame(for: fittingSize, snapshot: snapshot, on: screen)
@ -116,13 +120,18 @@ final class LockScreenWeatherPanelManager {
let verticalOffset = screenFrame.height * 0.15 let verticalOffset = screenFrame.height * 0.15
let isCircular = snapshot.widgetStyle == .circular let isCircular = snapshot.widgetStyle == .circular
let topMargin: CGFloat = isCircular ? 120 : 48 let topMargin: CGFloat = isCircular ? 120 : 48
let maxY = screenFrame.maxY - size.height - topMargin let inlineBaselineHeight: CGFloat = max(lastInlineBaselineHeight, 80)
let positionHeight = snapshot.widgetStyle == .inline
? max(size.height, inlineBaselineHeight)
: size.height
let maxY = screenFrame.maxY - positionHeight - topMargin
let baseY = min(maxY, screenFrame.midY + verticalOffset) let baseY = min(maxY, screenFrame.midY + verticalOffset)
let loweredY = baseY - 36 let loweredY = baseY - 36
let inlineLift: CGFloat = snapshot.widgetStyle == .inline ? 44 : 0 let inlineLift: CGFloat = snapshot.widgetStyle == .inline ? 44 : 0
let circularDrop: CGFloat = isCircular ? 28 : 0 let circularDrop: CGFloat = isCircular ? 28 : 0
let sizeDrop = max(0, size.height - 80) * 0.35 let sizeDropHeight = positionHeight
let sizeDrop = max(0, sizeDropHeight - 80) * 0.35
let userOffset = CGFloat(Defaults[.lockScreenWeatherVerticalOffset]) let userOffset = CGFloat(Defaults[.lockScreenWeatherVerticalOffset])
let clampedOffset = min(max(userOffset, -160), 160) let clampedOffset = min(max(userOffset, -160), 160)
let adjustedY = loweredY + inlineLift + clampedOffset - circularDrop - sizeDrop let adjustedY = loweredY + inlineLift + clampedOffset - circularDrop - sizeDrop