diff --git a/CalendarrWatchComplications/SmallComplicationViews.swift b/CalendarrWatchComplications/SmallComplicationViews.swift index fe099ce..e80ce1a 100644 --- a/CalendarrWatchComplications/SmallComplicationViews.swift +++ b/CalendarrWatchComplications/SmallComplicationViews.swift @@ -69,17 +69,19 @@ struct DateComplicationView: View { Label("\(WatchComplicationSupport.monthAbbreviation(entry.date, language: entry.language)) \(day)", systemImage: "calendar") default: - ZStack { - AccessoryWidgetBackground() - VStack(spacing: 0) { - Text("\(day)") - .font(.system(size: 22, weight: .bold)) - .minimumScaleFactor(0.7) - .widgetAccentable() - Text(WatchComplicationSupport.monthAbbreviation(entry.date, language: entry.language)) - .font(.system(size: 8, weight: .semibold)) - } + // A closed gauge, filling the slot edge to edge — the WidgetKit + // equivalent of ClockKit's ClosedGaugeText, which no longer exists. + // The ring is not decoration: it drains as the day does. + Gauge(value: WatchComplicationSupport.dayRemainingFraction(at: entry.date)) { + Text(WatchComplicationSupport.monthAbbreviation(entry.date, language: entry.language)) + } currentValueLabel: { + Text("\(day)") + .font(.system(size: 20, weight: .semibold)) + .lineLimit(1) + .minimumScaleFactor(0.6) } + .gaugeStyle(.accessoryCircularCapacity) + .widgetAccentable() } } } @@ -99,18 +101,17 @@ struct TodayCountComplicationView: View { Label("\(count) \(WatchL10n.t("watch.events_today", entry.language))", systemImage: "calendar") default: - ZStack { - AccessoryWidgetBackground() - VStack(spacing: 0) { - Text("\(count)") - .font(.system(size: 22, weight: .bold)) - .widgetAccentable() - Text(WatchL10n.t("watch.events_today", entry.language).uppercased()) - .font(.system(size: 7, weight: .semibold)) - .lineLimit(1) - .minimumScaleFactor(0.6) - } + Gauge(value: WatchComplicationSupport.todayRemainingFraction(in: entry.snapshot, + at: entry.date)) { + Image(systemName: "calendar") + } currentValueLabel: { + Text("\(count)") + .font(.system(size: 20, weight: .semibold)) + .lineLimit(1) + .minimumScaleFactor(0.6) } + .gaugeStyle(.accessoryCircularCapacity) + .widgetAccentable() } } } diff --git a/CalendarrWatchComplications/WatchComplicationSupport.swift b/CalendarrWatchComplications/WatchComplicationSupport.swift index 169b494..65f141a 100644 --- a/CalendarrWatchComplications/WatchComplicationSupport.swift +++ b/CalendarrWatchComplications/WatchComplicationSupport.swift @@ -50,6 +50,31 @@ enum WatchComplicationSupport { return min(1, max(0, target.timeIntervalSince(now) / window)) } + /// How much of today is still ahead. Drains through the day, matching the + /// countdown ring rather than inventing a second direction. + static func dayRemainingFraction(at now: Date, + calendar: Calendar = Calendar(identifier: .gregorian)) -> Double { + let dayStart = calendar.startOfDay(for: now) + guard let dayEnd = calendar.date(byAdding: .day, value: 1, to: dayStart) else { return 0 } + let total = dayEnd.timeIntervalSince(dayStart) + guard total > 0 else { return 0 } + return min(1, max(0, dayEnd.timeIntervalSince(now) / total)) + } + + /// The share of today's appointments still ahead. Zero when there are none, + /// which leaves the ring empty rather than full — an empty day should not + /// look like an untouched one. + static func todayRemainingFraction(in snapshot: CalendarrSnapshot?, at now: Date, + calendar: Calendar = Calendar(identifier: .gregorian)) -> Double { + guard let snapshot else { return 0 } + let dayStart = calendar.startOfDay(for: now) + guard let dayEnd = calendar.date(byAdding: .day, value: 1, to: dayStart) else { return 0 } + let today = snapshot.events.filter { $0.start < dayEnd && $0.end > dayStart } + guard !today.isEmpty else { return 0 } + let ahead = today.filter { $0.end > now }.count + return min(1, max(0, Double(ahead) / Double(today.count))) + } + static func time(_ date: Date, language: String) -> String { let formatter = DateFormatter() formatter.locale = WatchL10n.locale(language)