You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
424 lines
17 KiB
424 lines
17 KiB
// 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! |
|
@IBOutlet var syncLabel: NSTextField! |
|
|
|
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() |
|
syncLabel.stringValue = "Enable iCloud Sync".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, syncLabel, 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() |
|
} |
|
|
|
@IBAction func toggleSync(_ sender: NSSegmentedControl) { |
|
DataStore.shared().setupSyncNotification() |
|
} |
|
} |
|
|
|
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 |
|
} |
|
}
|
|
|