Widget-Optionen statt „Quellen", Reiterleiste von der Trennlinie herunter

Die Leiste saß mittig auf der Trennlinie unter dem Titelbalken.
`titlebarAppearsTransparent` allein reichte nicht — macOS zieht darunter
zusätzlich einen Trenner quer durchs Fenster. Der ist jetzt aus. Man setzt
keinen Titel auf eine Trennlinie.

„Quellen" war die Sicht des Programmierers: für den Nutzer ist die
Datenquelle eine Einstellung **des Kalenderwidgets** unter mehreren. Der
Bereich heißt jetzt „Widget-Optionen" und hat einen Abschnitt je Widget.

Kalender: woher die Daten kommen, Monatsraster oder Terminliste — und
endlich, welche Kalender überhaupt mitzählen. Gespeichert werden die
**abgewählten**, nicht die gewählten: kommt ein neuer Kalender dazu, ist er
damit automatisch dabei, statt dass man ihn suchen müsste, ohne zu wissen,
dass es ihn gibt. Der Filter lässt die Abdeckung unberührt — welche Kalender
man sehen will, sagt nichts darüber aus, für welchen Zeitraum Daten
vorliegen.

Wetter: dem Standort folgen oder einen festen Ort setzen, per Stadt oder
Postleitzahl. WeatherKit will Koordinaten, der Nutzer kennt Ortsnamen — die
Umsetzung passiert einmal beim Einstellen statt bei jedem Abruf. Der Ort
überlebt jetzt auch den Neustart; vorher stand nach jedem Start wieder der
Aufenthaltsort da.

Die Ablage-Einstellungen sind vom Widget-Reiter hierher gewandert, wo die
anderen Widget-Optionen stehen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-08-13 14:00:48 +02:00
parent ab96086db7
commit 49e1a46928
10 changed files with 1997 additions and 1416 deletions

View File

@@ -67,3 +67,18 @@ public extension EventWindow {
return EventWindow(events: events, coverage: coverage, asOf: asOf)
}
}
public extension CalendarSourceState {
/// Blendet die abgewählten Kalender aus.
///
/// Die Abdeckung bleibt unangetastet: welche Kalender man sehen will, sagt
/// nichts darüber aus, für welchen Zeitraum Daten vorliegen. Ein Tag ohne
/// sichtbare Termine bleibt ein bekannter Tag.
func filteringHidden(_ hidden: Set<String>) -> CalendarSourceState {
guard case .ready(let window) = self, !hidden.isEmpty else { return self }
return .ready(EventWindow(
events: window.events.filter { !hidden.contains($0.calendarID) },
coverage: window.coverage,
asOf: window.asOf))
}
}

View File

@@ -29,7 +29,24 @@ public final class CalendarModel {
didSet { defaults.set(showsMonthGrid, forKey: Self.gridKey) }
}
/// Welche Kalender Daten liefern. `nil` heißt: alle.
///
/// Bewusst eine Liste der **abgewählten**, nicht der gewählten: kommt ein
/// neuer Kalender dazu, ist er dann automatisch dabei. Andersherum müsste
/// man ihn suchen und anhaken, ohne zu wissen, dass es ihn gibt.
public var hiddenCalendarIDs: Set<String> {
didSet {
guard hiddenCalendarIDs != oldValue else { return }
defaults.set(Array(hiddenCalendarIDs), forKey: Self.hiddenKey)
refresh()
}
}
/// Die Kalender, die zur Auswahl stehen.
public private(set) var availableCalendars: [OnyxCalendar] = []
private static let kindKey = "calendar.source"
private static let hiddenKey = "calendar.hiddenCalendars"
private static let gridKey = "calendar.showsMonthGrid"
private let appleSource: CalendarSource
@@ -51,6 +68,7 @@ public final class CalendarModel {
self.kind = defaults.string(forKey: Self.kindKey)
.flatMap(CalendarSourceKind.init(rawValue:)) ?? .appleCalendar
self.showsMonthGrid = defaults.bool(forKey: Self.gridKey)
self.hiddenCalendarIDs = Set(defaults.stringArray(forKey: Self.hiddenKey) ?? [])
observeCalendarrIfNeeded()
}
@@ -139,7 +157,16 @@ public final class CalendarModel {
states.append(source.isAuthorized ? await source.load(interval) : .denied)
}
guard !Task.isCancelled else { return }
state = CalendarSourceKind.merge(states)
state = CalendarSourceKind.merge(states).filteringHidden(hiddenCalendarIDs)
// Die Liste der wählbaren Kalender nebenbei mitführen sie ändert
// sich, wenn jemand in Kalender einen anlegt oder die Quelle
// wechselt.
var found: [OnyxCalendar] = []
for source in activeSources where source.isAuthorized {
found.append(contentsOf: await source.calendars())
}
availableCalendars = found
}
}