|
|
|
// Copyright © 2015 Abhishek Banthia
|
|
|
|
|
|
|
|
import Cocoa
|
|
|
|
import CoreLoggerKit
|
|
|
|
import CoreModelKit
|
|
|
|
|
|
|
|
class AppearanceViewController: ParentViewController {
|
|
|
|
@IBOutlet var timeFormat: NSPopUpButton!
|
|
|
|
@IBOutlet var theme: NSPopUpButton!
|
|
|
|
@IBOutlet var informationLabel: NSTextField!
|
|
|
|
@IBOutlet var sliderDayRangePopup: NSPopUpButton!
|
|
|
|
@IBOutlet var visualEffectView: NSVisualEffectView!
|
|
|
|
@IBOutlet var menubarMode: NSSegmentedControl!
|
|
|
|
@IBOutlet var includeDayInMenubarControl: NSSegmentedControl!
|
|
|
|
@IBOutlet var includeDateInMenubarControl: NSSegmentedControl!
|
|
|
|
@IBOutlet var includePlaceNameControl: NSSegmentedControl!
|
|
|
|
@IBOutlet var appearanceTab: NSTabView!
|
|
|
|
@IBOutlet var appDisplayControl: NSSegmentedControl!
|
|
|
|
|
|
|
|
private var themeDidChangeNotification: NSObjectProtocol?
|
|
|
|
|
|
|
|
private var previewTimezones: [TimezoneData] = []
|
|
|
|
|
|
|
|
override func viewDidLoad() {
|
|
|
|
super.viewDidLoad()
|
|
|
|
|
|
|
|
informationLabel.stringValue = "Favourite a timezone to enable menubar display options.".localized()
|
|
|
|
informationLabel.textColor = NSColor.secondaryLabelColor
|
|
|
|
|
|
|
|
let chosenFormat = DataStore.shared().timezoneFormat().intValue
|
|
|
|
let supportedTimeFormats = ["h:mm a (7:08 PM)",
|
|
|
|
"HH:mm (19:08)",
|
|
|
|
"-- With Seconds --",
|
|
|
|
"h:mm:ss a (7:08:09 PM)",
|
|
|
|
"HH:mm:ss (19:08:09)",
|
|
|
|
"-- 12 Hour with Preceding 0 --",
|
|
|
|
"hh:mm a (07:08 PM)",
|
|
|
|
"hh:mm:ss a (07:08:09 PM)",
|
|
|
|
"-- 12 Hour w/o AM/PM --",
|
|
|
|
"hh:mm (07:08)",
|
|
|
|
"hh:mm:ss (07:08:09)",
|
|
|
|
"Epoch Time"]
|
|
|
|
timeFormat.removeAllItems()
|
|
|
|
timeFormat.addItems(withTitles: supportedTimeFormats)
|
|
|
|
|
|
|
|
timeFormat.item(at: 2)?.isEnabled = false
|
|
|
|
timeFormat.item(at: 5)?.isEnabled = false
|
|
|
|
timeFormat.item(at: 8)?.isEnabled = false
|
|
|
|
timeFormat.autoenablesItems = false
|
|
|
|
timeFormat.selectItem(at: chosenFormat)
|
|
|
|
timeFormat.setAccessibilityIdentifier("TimeFormatPopover")
|
|
|
|
|
|
|
|
informationLabel.setAccessibilityIdentifier("InformationLabel")
|
|
|
|
|
|
|
|
sliderDayRangePopup.removeAllItems()
|
|
|
|
sliderDayRangePopup.addItems(withTitles: [
|
|
|
|
"1 day",
|
|
|
|
"2 days",
|
|
|
|
"3 days",
|
|
|
|
"4 days",
|
|
|
|
"5 days",
|
|
|
|
"6 days",
|
|
|
|
"7 days"
|
|
|
|
])
|
|
|
|
|
|
|
|
if #available(macOS 11.0, *) {} else {
|
|
|
|
theme.font = NSFont.systemFont(ofSize: 13)
|
|
|
|
}
|
|
|
|
|
|
|
|
setup()
|
|
|
|
|
|
|
|
themeDidChangeNotification = NotificationCenter.default.addObserver(forName: .themeDidChangeNotification, object: nil, queue: OperationQueue.main) { _ in
|
|
|
|
self.setup()
|
|
|
|
self.animateBackgroundColorChange()
|
|
|
|
self.view.needsDisplay = true // Let's make the color change permanent.
|
|
|
|
self.previewPanelTableView.reloadData()
|
|
|
|
}
|
|
|
|
|
|
|
|
previewTimezones = [TimezoneData(with: ["customLabel": "San Francisco",
|
|
|
|
"formattedAddress": "San Francisco",
|
|
|
|
"place_id": "TestIdentifier",
|
|
|
|
"timezoneID": "America/Los_Angeles",
|
|
|
|
"nextUpdate": "",
|
|
|
|
"note": "Your individual note about this location goes here!",
|
|
|
|
"latitude": "37.7749295",
|
|
|
|
"longitude": "-122.4194155"])]
|
|
|
|
|
|
|
|
// Ensure the more beautiful tab is selected
|
|
|
|
appearanceTab.selectTabViewItem(at: 0)
|
|
|
|
|
|
|
|
// Setup Preview Pane
|
|
|
|
previewPanelTableView.dataSource = self
|
|
|
|
previewPanelTableView.delegate = self
|
|
|
|
previewPanelTableView.reloadData()
|
|
|
|
previewPanelTableView.selectionHighlightStyle = .none
|
|
|
|
previewPanelTableView.enclosingScrollView?.hasVerticalScroller = false
|
|
|
|
previewPanelTableView.enclosingScrollView?.wantsLayer = true
|
|
|
|
previewPanelTableView.enclosingScrollView?.layer?.cornerRadius = 12
|
|
|
|
}
|
|
|
|
|
|
|
|
deinit {
|
|
|
|
if let themeDidChangeNotif = themeDidChangeNotification {
|
|
|
|
NotificationCenter.default.removeObserver(themeDidChangeNotif)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private func animateBackgroundColorChange() {
|
|
|
|
let colorAnimation = CABasicAnimation(keyPath: "backgroundColor")
|
|
|
|
colorAnimation.duration = 0.25
|
|
|
|
colorAnimation.fromValue = previousBackgroundColor.cgColor
|
|
|
|
colorAnimation.toValue = Themer.shared().mainBackgroundColor().cgColor
|
|
|
|
view.layer?.add(colorAnimation, forKey: "backgroundColor")
|
|
|
|
}
|
|
|
|
|
|
|
|
override func viewWillAppear() {
|
|
|
|
super.viewWillAppear()
|
|
|
|
|
|
|
|
if let menubarFavourites = DataStore.shared().menubarTimezones() {
|
|
|
|
visualEffectView.isHidden = menubarFavourites.isEmpty ? false : true
|
|
|
|
informationLabel.isHidden = menubarFavourites.isEmpty ? false : true
|
|
|
|
}
|
|
|
|
|
|
|
|
if let selectedIndex = DataStore.shared().retrieve(key: CLFutureSliderRange) as? NSNumber {
|
|
|
|
sliderDayRangePopup.selectItem(at: selectedIndex.intValue)
|
|
|
|
}
|
|
|
|
|
|
|
|
if #available(macOS 10.14, *) {} else {
|
|
|
|
theme.removeItem(at: 2)
|
|
|
|
}
|
|
|
|
|
|
|
|
let shouldDisplayCompact = DataStore.shared().shouldDisplay(.menubarCompactMode)
|
|
|
|
menubarMode.setSelected(true, forSegment: shouldDisplayCompact ? 0 : 1)
|
|
|
|
|
|
|
|
// True is Menubar Only and False is Menubar + Dock
|
|
|
|
let appDisplayOptions = DataStore.shared().shouldDisplay(.appDisplayOptions)
|
|
|
|
appDisplayControl.setSelected(true, forSegment: appDisplayOptions ? 0 : 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
@IBOutlet var timeFormatLabel: NSTextField!
|
|
|
|
@IBOutlet var panelTheme: NSTextField!
|
|
|
|
@IBOutlet var dayDisplayOptionsLabel: NSTextField!
|
|
|
|
@IBOutlet var showSliderLabel: NSTextField!
|
|
|
|
@IBOutlet var showSunriseLabel: NSTextField!
|
|
|
|
@IBOutlet var largerTextLabel: NSTextField!
|
|
|
|
@IBOutlet var futureSliderRangeLabel: NSTextField!
|
|
|
|
@IBOutlet var includeDateLabel: NSTextField!
|
|
|
|
@IBOutlet var includeDayLabel: NSTextField!
|
|
|
|
@IBOutlet var includePlaceLabel: NSTextField!
|
|
|
|
@IBOutlet var appDisplayLabel: NSTextField!
|
|
|
|
@IBOutlet var menubarModeLabel: NSTextField!
|
|
|
|
@IBOutlet var previewLabel: NSTextField!
|
|
|
|
@IBOutlet var miscelleaneousLabel: NSTextField!
|
|
|
|
@IBOutlet var dstTransitionField: NSTextField!
|
|
|
|
|
|
|
|
// Panel Preview
|
|
|
|
@IBOutlet var previewPanelTableView: NSTableView!
|
|
|
|
|
|
|
|
private func setup() {
|
|
|
|
timeFormatLabel.stringValue = "Time Format".localized()
|
|
|
|
panelTheme.stringValue = "Panel Theme".localized()
|
|
|
|
dayDisplayOptionsLabel.stringValue = "Day Display Options".localized()
|
|
|
|
showSliderLabel.stringValue = "Time Scroller".localized()
|
|
|
|
showSunriseLabel.stringValue = "Show Sunrise/Sunset".localized()
|
|
|
|
largerTextLabel.stringValue = "Larger Text".localized()
|
|
|
|
futureSliderRangeLabel.stringValue = "Future Slider Range".localized()
|
|
|
|
includeDateLabel.stringValue = "Include Date".localized()
|
|
|
|
includeDayLabel.stringValue = "Include Day".localized()
|
|
|
|
includePlaceLabel.stringValue = "Include Place Name".localized()
|
|
|
|
menubarModeLabel.stringValue = "Menubar Mode".localized()
|
|
|
|
previewLabel.stringValue = "Preview".localized()
|
|
|
|
miscelleaneousLabel.stringValue = "Miscellaneous".localized()
|
|
|
|
|
|
|
|
[timeFormatLabel, panelTheme,
|
|
|
|
dayDisplayOptionsLabel, showSliderLabel,
|
|
|
|
showSunriseLabel, largerTextLabel, futureSliderRangeLabel,
|
|
|
|
includeDayLabel, includeDateLabel, includePlaceLabel, appDisplayLabel, menubarModeLabel,
|
|
|
|
previewLabel, miscelleaneousLabel, dstTransitionField].forEach {
|
|
|
|
$0?.textColor = Themer.shared().mainTextColor()
|
|
|
|
}
|
|
|
|
|
|
|
|
previewPanelTableView.backgroundColor = Themer.shared().mainBackgroundColor()
|
|
|
|
}
|
|
|
|
|
|
|
|
@IBAction func timeFormatSelectionChanged(_ sender: NSPopUpButton) {
|
|
|
|
let selection = NSNumber(value: sender.indexOfSelectedItem)
|
|
|
|
|
|
|
|
UserDefaults.standard.set(selection, forKey: CLSelectedTimeZoneFormatKey)
|
|
|
|
refresh(panel: true, floating: true)
|
|
|
|
|
|
|
|
if let selectedFormat = sender.selectedItem?.title,
|
|
|
|
selectedFormat.contains("ss") {
|
|
|
|
Logger.info("Selected format contains timezone format")
|
|
|
|
guard let panelController = PanelController.panel() else { return }
|
|
|
|
panelController.pauseTimer()
|
|
|
|
}
|
|
|
|
|
|
|
|
updateStatusItem()
|
|
|
|
previewPanelTableView.reloadData()
|
|
|
|
}
|
|
|
|
|
|
|
|
private var previousBackgroundColor = NSColor.white
|
|
|
|
|
|
|
|
@IBAction func themeChanged(_ sender: NSPopUpButton) {
|
|
|
|
previousBackgroundColor = Themer.shared().mainBackgroundColor()
|
|
|
|
|
|
|
|
let selectedMenuItem = sender.indexOfSelectedItem
|
|
|
|
Themer.shared().set(theme: selectedMenuItem)
|
|
|
|
|
|
|
|
refresh(panel: false, floating: true)
|
|
|
|
|
|
|
|
guard let panelController = PanelController.panel() else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
panelController.refreshBackgroundView()
|
|
|
|
|
|
|
|
panelController.shutdownButton.image = Themer.shared().shutdownImage()
|
|
|
|
panelController.preferencesButton.image = Themer.shared().preferenceImage()
|
|
|
|
panelController.pinButton.image = Themer.shared().pinImage()
|
|
|
|
panelController.sharingButton.image = Themer.shared().sharingImage()
|
|
|
|
|
|
|
|
let defaultTimezones = panelController.defaultPreferences
|
|
|
|
if defaultTimezones.isEmpty {
|
|
|
|
panelController.updatePanelColor()
|
|
|
|
}
|
|
|
|
|
|
|
|
panelController.updateTableContent()
|
|
|
|
|
|
|
|
switch selectedMenuItem {
|
|
|
|
case 0:
|
|
|
|
Logger.log(object: ["themeSelected": "Light"], for: "Theme")
|
|
|
|
case 1:
|
|
|
|
Logger.log(object: ["themeSelected": "Dark"], for: "Theme")
|
|
|
|
case 2:
|
|
|
|
Logger.log(object: ["themeSelected": "System"], for: "Theme")
|
|
|
|
default:
|
|
|
|
Logger.log(object: ["themeSelected": "System"], for: "Theme")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private func loggingStringForRelativeDisplaySelection(_ selection: Int) -> String {
|
|
|
|
switch selection {
|
|
|
|
case 0:
|
|
|
|
return "Relative Day"
|
|
|
|
case 1:
|
|
|
|
return "Actual Day"
|
|
|
|
case 2:
|
|
|
|
return "Actual Date Day"
|
|
|
|
case 3:
|
|
|
|
return "Hide"
|
|
|
|
default:
|
|
|
|
return "Unexpected Selection"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@IBAction func changeRelativeDayDisplay(_ sender: NSSegmentedControl) {
|
|
|
|
Logger.log(object: ["dayPreference": loggingStringForRelativeDisplaySelection(sender.selectedSegment)], for: "RelativeDate")
|
|
|
|
|
|
|
|
refresh(panel: true, floating: true)
|
|
|
|
|
|
|
|
previewPanelTableView.reloadData()
|
|
|
|
}
|
|
|
|
|
|
|
|
@IBAction func showFutureSlider(_: Any) {
|
|
|
|
refresh(panel: false, floating: true)
|
|
|
|
}
|
|
|
|
|
|
|
|
@IBAction func showSunriseSunset(_ sender: NSSegmentedControl) {
|
|
|
|
Logger.log(object: ["Is It Displayed": sender.selectedSegment == 0 ? "YES" : "NO"], for: "Sunrise Sunset")
|
|
|
|
previewPanelTableView.reloadData()
|
|
|
|
}
|
|
|
|
|
|
|
|
@IBAction func changeAppDisplayOptions(_ sender: NSSegmentedControl) {
|
|
|
|
if sender.selectedSegment == 0 {
|
|
|
|
Logger.log(object: ["Selection": "Menubar"], for: "Dock Mode")
|
|
|
|
NSApp.setActivationPolicy(.accessory)
|
|
|
|
} else {
|
|
|
|
Logger.log(object: ["Selection": "Menubar and Dock"], for: "Dock Mode")
|
|
|
|
NSApp.setActivationPolicy(.regular)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private func refresh(panel: Bool, floating: Bool) {
|
|
|
|
OperationQueue.main.addOperation {
|
|
|
|
if panel, DataStore.shared().shouldDisplay(ViewType.showAppInForeground) == false {
|
|
|
|
guard let panelController = PanelController.panel() else { return }
|
|
|
|
|
|
|
|
let futureSliderBounds = panelController.futureSlider.bounds
|
|
|
|
panelController.futureSlider.setNeedsDisplay(futureSliderBounds)
|
|
|
|
|
|
|
|
panelController.updateDefaultPreferences()
|
|
|
|
|
|
|
|
panelController.updateTableContent()
|
|
|
|
panelController.setupMenubarTimer()
|
|
|
|
}
|
|
|
|
|
|
|
|
if floating, DataStore.shared().shouldDisplay(ViewType.showAppInForeground) {
|
|
|
|
if DataStore.shared().shouldDisplay(ViewType.showAppInForeground) {
|
|
|
|
let floatingWindow = FloatingWindowController.shared()
|
|
|
|
floatingWindow.updateTableContent()
|
|
|
|
floatingWindow.futureSlider.setNeedsDisplay(floatingWindow.futureSlider.bounds)
|
|
|
|
|
|
|
|
if !panel {
|
|
|
|
floatingWindow.updatePanelColor()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@IBAction func displayDayInMenubarAction(_: Any) {
|
|
|
|
DataStore.shared().updateDayPreference()
|
|
|
|
updateStatusItem()
|
|
|
|
}
|
|
|
|
|
|
|
|
@IBAction func displayDateInMenubarAction(_: Any) {
|
|
|
|
DataStore.shared().updateDateInPreference()
|
|
|
|
updateStatusItem()
|
|
|
|
}
|
|
|
|
|
|
|
|
@IBAction func displayPlaceInMenubarAction(_: Any) {
|
|
|
|
updateStatusItem()
|
|
|
|
}
|
|
|
|
|
|
|
|
private func updateStatusItem() {
|
|
|
|
guard let statusItem = (NSApplication.shared.delegate as? AppDelegate)?.statusItemForPanel() else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if DataStore.shared().shouldDisplay(.menubarCompactMode) {
|
|
|
|
statusItem.setupStatusItem()
|
|
|
|
} else {
|
|
|
|
statusItem.refresh()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@IBAction func menubarModeChanged(_ sender: NSSegmentedControl) {
|
|
|
|
guard let statusItem = (NSApplication.shared.delegate as? AppDelegate)?.statusItemForPanel() else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
statusItem.setupStatusItem()
|
|
|
|
|
|
|
|
if sender.selectedSegment == 0 {
|
|
|
|
Logger.log(object: ["Context": "In Appearance View"], for: "Switched to Compact Mode")
|
|
|
|
} else {
|
|
|
|
Logger.log(object: ["Context": "In Appearance View"], for: "Switched to Standard Mode")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@IBAction func fontSliderChanged(_: Any) {
|
|
|
|
previewPanelTableView.reloadData()
|
|
|
|
}
|
|
|
|
|
|
|
|
@IBAction func toggleDSTTransitionOption(_: Any) {
|
|
|
|
previewPanelTableView.reloadData()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
extension AppearanceViewController: NSTableViewDataSource, NSTableViewDelegate {
|
|
|
|
func numberOfRows(in _: NSTableView) -> Int {
|
|
|
|
return 1
|
|
|
|
}
|
|
|
|
|
|
|
|
func tableView(_ tableView: NSTableView, viewFor _: NSTableColumn?, row: Int) -> NSView? {
|
|
|
|
guard !previewTimezones.isEmpty else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
guard let cellView = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: "previewTimezoneCell"), owner: self) as? TimezoneCellView else {
|
|
|
|
assertionFailure("Unable to create tableviewcell")
|
|
|
|
return NSView()
|
|
|
|
}
|
|
|
|
|
|
|
|
let currentModel = previewTimezones[row]
|
|
|
|
let operation = TimezoneDataOperations(with: currentModel)
|
|
|
|
|
|
|
|
cellView.sunriseSetTime.stringValue = operation.formattedSunriseTime(with: 0)
|
|
|
|
cellView.sunriseImage.image = currentModel.isSunriseOrSunset ? Themer.shared().sunriseImage() : Themer.shared().sunsetImage()
|
|
|
|
cellView.relativeDate.stringValue = operation.date(with: 0, displayType: .panel)
|
|
|
|
cellView.rowNumber = row
|
|
|
|
cellView.customName.stringValue = currentModel.formattedTimezoneLabel()
|
|
|
|
cellView.time.stringValue = operation.time(with: 0)
|
|
|
|
if DataStore.shared().shouldDisplay(.dstTransitionInfo) {
|
|
|
|
cellView.noteLabel.stringValue = "Heads up! DST Transition will occur in 3 days."
|
|
|
|
} else if let note = currentModel.note, !note.isEmpty {
|
|
|
|
cellView.noteLabel.stringValue = note
|
|
|
|
} else {
|
|
|
|
cellView.noteLabel.stringValue = CLEmptyString
|
|
|
|
}
|
|
|
|
cellView.currentLocationIndicator.isHidden = !currentModel.isSystemTimezone
|
|
|
|
cellView.time.setAccessibilityIdentifier("ActualTime")
|
|
|
|
cellView.layout(with: currentModel)
|
|
|
|
|
|
|
|
cellView.setAccessibilityIdentifier(currentModel.formattedTimezoneLabel())
|
|
|
|
cellView.setAccessibilityLabel(currentModel.formattedTimezoneLabel())
|
|
|
|
|
|
|
|
return cellView
|
|
|
|
}
|
|
|
|
|
|
|
|
func tableView(_: NSTableView, heightOfRow row: Int) -> CGFloat {
|
|
|
|
if let userFontSize = DataStore.shared().retrieve(key: CLUserFontSizePreference) as? NSNumber, previewTimezones.count > row {
|
|
|
|
let model = previewTimezones[row]
|
|
|
|
|
|
|
|
let rowHeight: Int = userFontSize == 4 ? 60 : 65
|
|
|
|
if let note = model.note, !note.isEmpty {
|
|
|
|
return CGFloat(rowHeight + userFontSize.intValue + 25)
|
|
|
|
} else if DataStore.shared().shouldDisplay(.dstTransitionInfo) {
|
|
|
|
return CGFloat(rowHeight + userFontSize.intValue + 25)
|
|
|
|
}
|
|
|
|
|
|
|
|
return CGFloat(rowHeight + (userFontSize.intValue * 2))
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
}
|