ScrollSwitcher/ScrollSwitcher/AppDelegate.swift

118 lines
4.1 KiB
Swift

//
// AppDelegate.swift
// ScrollSwitcher
//
// Created by Shadowfacts on 8/31/21.
//
import Cocoa
let defaultsKey = "com.apple.swipescrolldirection"
class AppDelegate: NSObject, NSApplicationDelegate {
// things that need to be retained to keep them from disappearing
private var prefPanesSupport: Bundle!
private var item: NSStatusItem!
func applicationDidFinishLaunching(_ aNotification: Notification) {
item = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
item.button!.target = self
item.button!.action = #selector(menuItemClicked)
updateIcon()
// update the icon when system prefs changes
DistributedNotificationCenter.default().addObserver(self, selector: #selector(updateIcon), name: .swipeScrollDirectionDidChangeNotification, object: nil)
}
@objc private func updateIcon() {
item.button!.image = Direction.current.image
}
@objc private func menuItemClicked() {
if let event = NSApp.currentEvent, event.modifierFlags.contains(.shift) {
item.menu = createMenu()
item.button!.performClick(nil)
} else {
toggleScrollDirection()
}
}
private func createMenu() -> NSMenu {
let menu = NSMenu()
menu.delegate = self
let state = Direction.current == .natural ? "On" : "Off"
let status = NSMenuItem(title: "Natural Scrolling: \(state)", action: nil, keyEquivalent: "")
menu.addItem(status)
let verb = Direction.current == .natural ? "Disable" : "Enable"
let toggleItem = NSMenuItem(title: "\(verb) Natural Scrolling", action: #selector(toggleScrollDirection), keyEquivalent: "")
menu.addItem(toggleItem)
menu.addItem(NSMenuItem.separator())
menu.addItem(withTitle: "Quit", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q")
return menu
}
@objc private func toggleScrollDirection() {
setDirection(.current == Direction.natural ? .normal : .natural)
}
private func setDirection(_ new: Direction) {
// can't construct a UserDefaults with NSGlobalDomain, so set it via the command line
let proc = Process()
// let out = Pipe()
// let err = Pipe()
proc.launchPath = "/usr/bin/env"
let newVal = new == .normal ? "NO" : "YES"
proc.arguments = ["defaults", "write", "-g", "com.apple.swipescrolldirection", "-bool", newVal]
// proc.standardOutput = out
// proc.standardError = err
proc.launch()
proc.waitUntilExit()
// let outData = out.fileHandleForReading.readDataToEndOfFile()
// let errData = err.fileHandleForReading.readDataToEndOfFile()
if proc.terminationStatus != 0 {
fatalError("uh oh, exit code: \(proc.terminationStatus)")
}
// makes system preferences update
DistributedNotificationCenter.default().postNotificationName(.swipeScrollDirectionDidChangeNotification, object: nil, userInfo: nil, deliverImmediately: false)
// make the actual input behavior update
setSwipeScrollDirection(new == .natural)
}
}
extension AppDelegate: NSMenuDelegate {
func menuDidClose(_ menu: NSMenu) {
// remove the menu so the next time the item is clicked, it performs the primary action
item.menu = nil
}
}
enum Direction: Equatable {
case normal, natural
static var current: Direction {
let value = UserDefaults.standard.bool(forKey: defaultsKey)
if value {
return .natural
} else {
return .normal
}
}
var image: NSImage {
switch self {
case .normal:
return NSImage(systemSymbolName: "scroll", accessibilityDescription: nil)!
case .natural:
return NSImage(systemSymbolName: "scroll.fill", accessibilityDescription: nil)!
}
}
}
extension Notification.Name {
static let swipeScrollDirectionDidChangeNotification = Notification.Name(rawValue: "SwipeScrollDirectionDidChangeNotification")
}