Browse Source

Initial attempt at showing next meeting event in compact menubar mode.

pull/113/head
Abhishek 3 years ago
parent
commit
a44f0f7291
  1. 1
      Clocker/Preferences/Calendar/CalendarViewController.swift
  2. 3
      Clocker/Preferences/Menu Bar/MenubarTitleProvider.swift
  3. 64
      Clocker/Preferences/Menu Bar/StatusContainerView.swift
  4. 40
      Clocker/Preferences/Menu Bar/StatusItemHandler.swift

1
Clocker/Preferences/Calendar/CalendarViewController.swift

@ -84,7 +84,6 @@ class CalendarViewController: ParentViewController {
verifyCalendarAccess()
showSegmentedControl.selectedSegment = DataStore.shared().shouldDisplay(ViewType.upcomingEventView) ? 0 : 1
showNextMeetingInMenubarControl.isEnabled = DataStore.shared().shouldDisplay(.menubarCompactMode) ? false : true
}
private func verifyCalendarAccess() {

3
Clocker/Preferences/Menu Bar/MenubarTitleProvider.swift

@ -34,7 +34,7 @@ class MenubarTitleProvider: NSObject {
return nil
}
private func checkForUpcomingEvents() -> String? {
func checkForUpcomingEvents() -> String? {
if DataStore.shared().shouldDisplay(.showMeetingInMenubar) {
let filteredDates = EventCenter.sharedCenter().eventsForDate
let autoupdatingCal = EventCenter.sharedCenter().autoupdatingCalendar
@ -48,7 +48,6 @@ class MenubarTitleProvider: NSObject {
if timeForEventToStart > 30 {
Logger.info("Our next event: \(event.event.title ?? "Error") starts in \(timeForEventToStart) mins")
continue
}

64
Clocker/Preferences/Menu Bar/StatusContainerView.swift

@ -18,6 +18,10 @@ func bufferCalculatedWidth() -> Int {
if DataStore.shared().shouldShowDateInMenubar() {
totalWidth += 20
}
if DataStore.shared().shouldDisplay(.showMeetingInMenubar) {
totalWidth += 100
}
return totalWidth
}
@ -55,6 +59,14 @@ func compactWidth(for timezone: TimezoneData) -> Int {
// Test with Sat 12:46 AM
let bufferWidth: CGFloat = 9.5
protocol StatusItemViewConforming {
/// Mark that we need to refresh the text we're showing in the menubar
func statusItemViewSetNeedsDisplay()
/// Status Item Views can be used to represent different information (like time in location, or an upcoming meeting). Distinguish between different status items view through this identifier
func statusItemViewIdentifier() -> String
}
class StatusContainerView: NSView {
private var previousX: Int = 0
@ -64,8 +76,20 @@ class StatusContainerView: NSView {
layer?.backgroundColor = NSColor.clear.cgColor
}
init(with timezones: [Data]) {
init(with timezones: [Data], showUpcomingEventView: Bool) {
func addSubviews() {
if showUpcomingEventView,
let events = EventCenter.sharedCenter().eventsForDate[NSCalendar.autoupdatingCurrent.startOfDay(for: Date())],
events.isEmpty == false,
let upcomingEvent = EventCenter.sharedCenter().nextOccuring(events) {
let calculatedWidth = bestWidth(for: upcomingEvent)
let frame = NSRect(x: previousX, y: 0, width: calculatedWidth, height: 30)
let calendarItemView = UpcomingEventStatusItemView(frame: frame)
calendarItemView.dataObject = upcomingEvent
addSubview(calendarItemView)
previousX += calculatedWidth
}
timezones.forEach {
if let timezoneObject = TimezoneData.customObject(from: $0) {
addTimezone(timezoneObject)
@ -80,7 +104,7 @@ class StatusContainerView: NSView {
]
func containerWidth(for timezones: [Data]) -> CGFloat {
let compressedWidth = timezones.reduce(0.0) { result, timezone -> CGFloat in
var compressedWidth = timezones.reduce(0.0) { result, timezone -> CGFloat in
if let timezoneObject = TimezoneData.customObject(from: timezone) {
let precalculatedWidth = Double(compactWidth(for: timezoneObject))
@ -95,6 +119,10 @@ class StatusContainerView: NSView {
return result + CGFloat(bufferCalculatedWidth())
}
if showUpcomingEventView {
compressedWidth += 70
}
let calculatedWidth = min(compressedWidth,
CGFloat(timezones.count * bufferCalculatedWidth()))
return calculatedWidth
@ -145,11 +173,41 @@ class StatusContainerView: NSView {
return Int(max(bestSize.width, bestTitleSize.width) + bufferWidth)
}
private func bestWidth(for eventInfo: EventInfo) -> Int {
var textColor = hasDarkAppearance ? NSColor.white : NSColor.black
if #available(OSX 11.0, *) {
textColor = NSColor.white
}
let timeBasedAttributes = [
NSAttributedString.Key.font: compactModeTimeFont,
NSAttributedString.Key.foregroundColor: textColor,
NSAttributedString.Key.backgroundColor: NSColor.clear,
NSAttributedString.Key.paragraphStyle: defaultParagraphStyle
]
let bestSize = compactModeTimeFont.size(eventInfo.metadataForMeeting(),
55, // Default for a location based status view
attributes: timeBasedAttributes)
let bestTitleSize = compactModeTimeFont.size(eventInfo.event.title,
70, // Little more buffer since meeting titles tend to be longer
attributes: timeBasedAttributes)
return Int(max(bestSize.width, bestTitleSize.width) + bufferWidth)
}
func updateTime() {
if subviews.isEmpty {
assertionFailure("Subviews count should > 0")
}
for view in subviews {
if let conformingView = view as? StatusItemViewConforming {
conformingView.statusItemViewSetNeedsDisplay()
}
}
// See if frame's width needs any adjustment
adjustWidthIfNeccessary()
@ -170,8 +228,6 @@ class StatusContainerView: NSView {
y: statusItem.frame.origin.y,
width: newBestWidth,
height: statusItem.frame.size.height)
statusItem.updateTimeInMenubar()
}
}

40
Clocker/Preferences/Menu Bar/StatusItemHandler.swift

@ -24,7 +24,7 @@ class StatusItemHandler: NSObject {
private var menubarTitleHandler = MenubarTitleProvider()
private var parentView: StatusContainerView?
private var statusContainerView: StatusContainerView?
private var nsCalendar = Calendar.autoupdatingCurrent
@ -42,7 +42,7 @@ class StatusItemHandler: NSObject {
switch oldValue {
case .compactText:
statusItem.view = nil
parentView = nil
statusContainerView = nil
case .standardText:
statusItem.button?.title = CLEmptyString
case .icon:
@ -134,8 +134,8 @@ class StatusItemHandler: NSObject {
}
}
private func constructCompactView() {
parentView = nil
private func constructCompactView(with upcomingEventView: Bool = false) {
statusContainerView = nil
let menubarTimezones = DataStore.shared().menubarTimezones() ?? []
if menubarTimezones.isEmpty {
@ -143,8 +143,9 @@ class StatusItemHandler: NSObject {
return
}
parentView = StatusContainerView(with: menubarTimezones)
statusItem.view = parentView
statusContainerView = StatusContainerView(with: menubarTimezones,
showUpcomingEventView: upcomingEventView)
statusItem.view = statusContainerView
statusItem.view?.window?.backgroundColor = NSColor.clear
}
@ -241,7 +242,20 @@ class StatusItemHandler: NSObject {
}
func updateCompactMenubar() {
parentView?.updateTime()
if let upcomingEvent = menubarTitleHandler.checkForUpcomingEvents() {
print("Need to construct upcoming event view \(upcomingEvent)")
// Iterate and see if we're showing the calendar item view
let upcomingEventView = retrieveUpcomingEventStatusView()
// If not, reconstruct Status Container View with another view
if upcomingEventView == nil {
constructCompactView(with: true)
}
} else {
let upcomingEventView = retrieveUpcomingEventStatusView()
upcomingEventView?.removeFromSuperview()
}
// This will internally call `statusItemViewSetNeedsDisplay` on all subviews ensuring all text in the menubar is up-to-date.
statusContainerView?.updateTime()
}
func refresh() {
@ -347,7 +361,17 @@ class StatusItemHandler: NSObject {
menubarTimer?.invalidate()
menubarTimer = nil
constructCompactView()
constructCompactView(with: menubarTitleHandler.checkForUpcomingEvents() != nil)
updateMenubar()
}
private func retrieveUpcomingEventStatusView() -> NSView? {
let upcomingEventView = statusContainerView?.subviews.first(where: { statusItemView in
if let upcomingEventView = statusItemView as? StatusItemViewConforming {
return upcomingEventView.statusItemViewIdentifier() == "upcoming_event_view"
}
return false
})
return upcomingEventView
}
}

Loading…
Cancel
Save