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

// 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
}
}