mirror of
https://github.com/FBukovina/Atoll.git
synced 2026-10-27 15:14:33 +00:00
Hopefully fixed the Positioning
This commit is contained in:
parent
f411676d02
commit
40e805a785
3 changed files with 145 additions and 9 deletions
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,14 +585,43 @@ struct LockScreenWeatherWidget: View {
|
||||||
case .weather:
|
case .weather:
|
||||||
mainWidgetRow
|
mainWidgetRow
|
||||||
case .calendar:
|
case .calendar:
|
||||||
|
if shouldCombineFocusAndCalendar {
|
||||||
|
combinedFocusCalendarRow
|
||||||
|
} else if shouldShowFocusInCalendarSlot {
|
||||||
|
focusRowInCalendarSlot
|
||||||
|
} else {
|
||||||
nextEventRow
|
nextEventRow
|
||||||
|
}
|
||||||
case .focus:
|
case .focus:
|
||||||
|
if shouldCombineFocusAndCalendar {
|
||||||
|
EmptyView()
|
||||||
|
} else {
|
||||||
focusWidget
|
focusWidget
|
||||||
.opacity(shouldShowFocusWidget ? 1 : 0)
|
.opacity(shouldShowFocusWidget ? 1 : 0)
|
||||||
.accessibilityHidden(!shouldShowFocusWidget)
|
.accessibilityHidden(!shouldShowFocusWidget)
|
||||||
.allowsHitTesting(false)
|
.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) {
|
||||||
|
|
@ -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)])
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue