From b489f10c28b520d904a574acee63bb1540c3b0f1 Mon Sep 17 00:00:00 2001 From: Scarriffle Date: Fri, 11 Sep 2026 18:33:38 +0200 Subject: [PATCH] Make the countdown narrow enough for the slot it sits in MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Text(style: .timer) renders 1:23:45 once more than an hour out. An accessoryCorner has room for three or four characters beside its curved label, and a circular gauge for about the same, so the live timer overlapped what it sat next to. Replaced with a compact value — "23m", "2h", "3d" — recomputed per timeline entry, which is why the timeline now carries a mark every minute for the first hour. Both corner kinds and the gauge also get lineLimit(1) and a minimum scale factor, so an unexpectedly wide value shrinks instead of colliding. Co-Authored-By: Claude Opus 5 --- .../CornerComplicationViews.swift | 9 ++++++++- .../SmallComplicationViews.swift | 7 +++++-- .../WatchComplicationSupport.swift | 16 ++++++++++++++++ CalendarrWatchShared/WatchL10n.swift | 2 ++ 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/CalendarrWatchComplications/CornerComplicationViews.swift b/CalendarrWatchComplications/CornerComplicationViews.swift index b96bb86..07ad46e 100644 --- a/CalendarrWatchComplications/CornerComplicationViews.swift +++ b/CalendarrWatchComplications/CornerComplicationViews.swift @@ -14,7 +14,12 @@ struct CornerCountdownComplicationView: View { let next = WatchComplicationSupport.nextEvent(in: entry.snapshot, at: entry.date) Group { if let next { - Text(WatchComplicationSupport.countdownTarget(next, at: entry.date), style: .timer) + Text(WatchComplicationSupport.compactCountdown( + to: WatchComplicationSupport.countdownTarget(next, at: entry.date), + at: entry.date, + language: entry.language)) + .lineLimit(1) + .minimumScaleFactor(0.6) .widgetAccentable() } else { Image(systemName: "calendar") @@ -37,6 +42,8 @@ struct CornerTimeComplicationView: View { Text(next.isAllDay ? WatchL10n.t("watch.allday", entry.language) : WatchComplicationSupport.time(next.start, language: entry.language)) + .lineLimit(1) + .minimumScaleFactor(0.6) .widgetAccentable() } else { Image(systemName: "calendar") diff --git a/CalendarrWatchComplications/SmallComplicationViews.swift b/CalendarrWatchComplications/SmallComplicationViews.swift index b06eb57..fe099ce 100644 --- a/CalendarrWatchComplications/SmallComplicationViews.swift +++ b/CalendarrWatchComplications/SmallComplicationViews.swift @@ -24,8 +24,11 @@ struct CountdownComplicationView: View { Gauge(value: WatchComplicationSupport.gaugeFraction(to: target, at: entry.date)) { Image(systemName: "calendar") } currentValueLabel: { - Text(target, style: .timer) - .font(.system(size: 12)) + Text(WatchComplicationSupport.compactCountdown(to: target, at: entry.date, + language: entry.language)) + .font(.system(size: 13)) + .lineLimit(1) + .minimumScaleFactor(0.6) } .gaugeStyle(.accessoryCircularCapacity) .widgetAccentable() diff --git a/CalendarrWatchComplications/WatchComplicationSupport.swift b/CalendarrWatchComplications/WatchComplicationSupport.swift index db566b6..169b494 100644 --- a/CalendarrWatchComplications/WatchComplicationSupport.swift +++ b/CalendarrWatchComplications/WatchComplicationSupport.swift @@ -28,6 +28,22 @@ enum WatchComplicationSupport { event.start > now ? event.start : event.end } + /// A countdown narrow enough for a corner or a circular gauge: at most + /// three characters. `Text(style: .timer)` would be live and free of + /// timeline entries, but renders `1:23:45` and overlaps whatever sits + /// beside it — so the timeline carries minute-granular entries instead and + /// this is recomputed per entry. + static func compactCountdown(to target: Date, at now: Date, language: String) -> String { + let seconds = target.timeIntervalSince(now) + guard seconds > 0 else { return WatchL10n.t("watch.now", language) } + let minutes = Int(seconds / 60) + if minutes < 1 { return WatchL10n.t("watch.now", language) } + if minutes < 60 { return "\(minutes)m" } + let hours = minutes / 60 + if hours < 24 { return "\(hours)h" } + return "\(hours / 24)d" + } + /// How full a draining ring should be, over a two-hour lead window. static func gaugeFraction(to target: Date, at now: Date) -> Double { let window: TimeInterval = 2 * 3600 diff --git a/CalendarrWatchShared/WatchL10n.swift b/CalendarrWatchShared/WatchL10n.swift index 5e53409..ea11014 100644 --- a/CalendarrWatchShared/WatchL10n.swift +++ b/CalendarrWatchShared/WatchL10n.swift @@ -26,6 +26,7 @@ enum WatchL10n { "watch.calendars": "Kalender", "watch.allday": "Ganztägig", "watch.no_events": "Keine Termine", + "watch.now": "jetzt", "watch.today": "Heute", "watch.tomorrow": "Morgen", "watch.events_today": "Termine", @@ -41,6 +42,7 @@ enum WatchL10n { "watch.calendars": "Calendars", "watch.allday": "All-day", "watch.no_events": "No events", + "watch.now": "now", "watch.today": "Today", "watch.tomorrow": "Tomorrow", "watch.events_today": "events",