diff --git a/MeetingBar/Extensions/DefaultsKeys.swift b/MeetingBar/Extensions/DefaultsKeys.swift index ed04ed56..0032368c 100644 --- a/MeetingBar/Extensions/DefaultsKeys.swift +++ b/MeetingBar/Extensions/DefaultsKeys.swift @@ -42,6 +42,7 @@ extension Defaults.Keys { static let statusbarEventTitleLength = Key("statusbarEventTitleLength", default: statusbarEventTitleLengthLimits.max) static let hideMeetingTitle = Key("hideMeetingTitle", default: false) + static let timeBasedStatusBarColor = Key("timeBasedStatusBarColor", default: false) static let dismissedEvents = Key<[ProcessedEvent]>("dismissedEvents", default: []) static let ongoingEventVisibility = Key("ongoingEventVisibility", default: .showTenMinBeforeNext) diff --git a/MeetingBar/Resources /Localization /en.lproj/Localizable.strings b/MeetingBar/Resources /Localization /en.lproj/Localizable.strings index 5c95734f..67b9e63c 100644 --- a/MeetingBar/Resources /Localization /en.lproj/Localizable.strings +++ b/MeetingBar/Resources /Localization /en.lproj/Localizable.strings @@ -101,6 +101,7 @@ "preferences_appearance_status_bar_ongoing_time_immediate_value" = "hide after start"; "preferences_appearance_status_bar_ongoing_time_ten_after_value" = "hide 10 min after start"; "preferences_appearance_status_bar_ongoing_time_ten_before_next_value" = "hide 10 min before next event"; +"preferences_appearance_status_bar_time_color_toggle" = "Color countdown by time remaining"; "preferences_appearance_status_bar_next_event_toggle" = "Show only events starting within"; "preferences_appearance_status_bar_next_event_stepper" = "%d minutes"; "preferences_appearance_menu_title" = "Menu"; diff --git a/MeetingBar/UI/StatusBar/StatusBarItemController.swift b/MeetingBar/UI/StatusBar/StatusBarItemController.swift index f663446a..ba5189a9 100644 --- a/MeetingBar/UI/StatusBar/StatusBarItemController.swift +++ b/MeetingBar/UI/StatusBar/StatusBarItemController.swift @@ -71,7 +71,7 @@ final class StatusBarItemController { .timeFormat, .bookmarks, .eventTitleFormat, .personalEventsAppereance, .pastEventsAppereance, .declinedEventsAppereance, .ongoingEventVisibility, - .showTimelineInMenu, + .showTimelineInMenu, .timeBasedStatusBarColor, options: [] ) .receive(on: DispatchQueue.main) @@ -258,11 +258,6 @@ final class StatusBarItemController { let menuTitle = NSMutableAttributedString() if Defaults[.eventTimeFormat] != .show_under_title || Defaults[.eventTitleFormat] == .none { - var eventTitle = title - if Defaults[.eventTimeFormat] == .show { - eventTitle += " " + time - } - var styles = [NSAttributedString.Key: Any]() styles[NSAttributedString.Key.font] = NSFont.systemFont(ofSize: MenuStyleConstants.defaultFontSize) @@ -274,7 +269,15 @@ final class StatusBarItemController { styles[NSAttributedString.Key.underlineStyle] = NSUnderlineStyle.single.rawValue | NSUnderlineStyle.patternDot.rawValue | NSUnderlineStyle.byWord.rawValue } - menuTitle.append(NSAttributedString(string: eventTitle, attributes: styles)) + menuTitle.append(NSAttributedString(string: title, attributes: styles)) + + if Defaults[.eventTimeFormat] == .show, !time.isEmpty { + var timeStyles = styles + if Defaults[.timeBasedStatusBarColor] { + timeStyles[NSAttributedString.Key.foregroundColor] = countdownColor(for: nextEvent) + } + menuTitle.append(NSAttributedString(string: " " + time, attributes: timeStyles)) + } } else { let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineHeightMultiple = 0.7 @@ -298,9 +301,10 @@ final class StatusBarItemController { menuTitle.append(NSAttributedString(string: title, attributes: styles)) - let timeAttributes = [ - NSAttributedString.Key.font: NSFont.systemFont(ofSize: 9), - NSAttributedString.Key.foregroundColor: NSColor.lightGray + let timeColor: NSColor = Defaults[.timeBasedStatusBarColor] ? countdownColor(for: nextEvent) : .lightGray + let timeAttributes: [NSAttributedString.Key: Any] = [ + .font: NSFont.systemFont(ofSize: 9), + .foregroundColor: timeColor ] menuTitle.append(NSAttributedString(string: "\n" + time, attributes: timeAttributes)) @@ -536,6 +540,24 @@ func shortenTitle(title: String?, offset: Int) -> String { return eventTitle } +func countdownColor(for event: MBEvent) -> NSColor { + let now = Date() + let timeRemaining: TimeInterval + if event.startDate <= now, event.endDate > now { + timeRemaining = event.endDate.timeIntervalSinceNow + } else { + timeRemaining = event.startDate.timeIntervalSinceNow + } + + if timeRemaining >= 15 * 60 { + return .systemGreen + } else if timeRemaining >= 5 * 60 { + return .systemYellow + } else { + return .systemRed + } +} + func createEventStatusString(title: String, startDate: Date, endDate: Date) -> (String, String) { var eventTime: String diff --git a/MeetingBar/UI/Views/Preferences/AppearanceTab.swift b/MeetingBar/UI/Views/Preferences/AppearanceTab.swift index 38c25073..cd5be311 100644 --- a/MeetingBar/UI/Views/Preferences/AppearanceTab.swift +++ b/MeetingBar/UI/Views/Preferences/AppearanceTab.swift @@ -149,6 +149,7 @@ struct StatusBarSection: View { @Default(.showEventMaxTimeUntilEventThreshold) var showEventMaxTimeUntilEventThreshold @Default(.showEventMaxTimeUntilEventEnabled) var showEventMaxTimeUntilEventEnabled @Default(.ongoingEventVisibility) var ongoingEventVisibility + @Default(.timeBasedStatusBarColor) var timeBasedStatusBarColor var body: some View { GroupBox(label: Label("preferences_appearance_status_bar_title".loco(), systemImage: "menubar.rectangle")) { @@ -236,6 +237,11 @@ struct StatusBarSection: View { ) .disabled(!showEventMaxTimeUntilEventEnabled) } + Toggle( + "preferences_appearance_status_bar_time_color_toggle".loco(), + isOn: $timeBasedStatusBarColor + ) + Picker("preferences_appearance_status_bar_ongoing_title".loco(), selection: $ongoingEventVisibility) { Text("preferences_appearance_status_bar_ongoing_time_immediate_value".loco()).tag( OngoingEventVisibility.hideImmediateAfter)