Added global Keyboard shortcut

So we could switch between different proxy modes with global shortcut.
This commit is contained in:
Vicent Tsai
2016-12-11 15:39:56 +08:00
parent cc342df5b5
commit 23967eca20
10 changed files with 236 additions and 20 deletions

View File

@ -7,7 +7,7 @@
//
import Cocoa
import Carbon
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDelegate {
@ -18,7 +18,11 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
var proxyPreferencesWinCtrl: ProxyPreferencesController!
var editUserRulesWinCtrl: UserRulesController!
var httpPreferencesWinCtrl : HTTPPreferencesWindowController!
let keyCode = kVK_ANSI_P
let modifierKeys = cmdKey+controlKey
var hotKeyRef: EventHotKeyRef?
var launchAtLoginController: LaunchAtLoginController = LaunchAtLoginController()
@IBOutlet weak var window: NSWindow!
@ -38,7 +42,23 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
@IBOutlet var serversPreferencesMenuItem: NSMenuItem!
@IBOutlet weak var lanchAtLoginMenuItem: NSMenuItem!
@IBOutlet weak var hudWindow: NSPanel!
@IBOutlet weak var panelView: NSView!
@IBOutlet weak var isNameTextField: NSTextField!
let kHudFadeInDuration: Double = 0.25
let kHudFadeOutDuration: Double = 0.5
let kHudDisplayDuration: Double = 2.0
let kHudAlphaValue: CGFloat = 0.75
let kHudCornerRadius: CGFloat = 18.0
let kHudHorizontalMargin: CGFloat = 30
let kHudHeight: CGFloat = 90.0
var timerToFadeOut: Timer? = nil
var fadingOut: Bool = false
var statusItem: NSStatusItem!
static let StatusItemIconWidth:CGFloat = 20
@ -135,8 +155,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
userNote.subtitle = "By Handle SS URL".localized
}
userNote.informativeText = "Host: \(profile.serverHost)"
" Port: \(profile.serverPort)"
" Encription Method: \(profile.method)".localized
//" Port: \(profile.serverPort)"
//" Encription Method: \(profile.method)".localized
userNote.soundName = NSUserNotificationDefaultSoundName
NSUserNotificationCenter.default
@ -165,6 +185,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
ProxyConfHelper.startMonitorPAC()
applyConfig()
SyncSSLocal()
// Register global hotkey
registerHotkey()
}
func applicationWillTerminate(_ aNotification: Notification) {
@ -172,8 +195,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
StopSSLocal()
StopPrivoxy()
ProxyConfHelper.disableProxy()
if let ref = hotKeyRef { UnregisterEventHotKey(ref) }
}
func applyConfig() {
let defaults = UserDefaults.standard
let isOn = defaults.bool(forKey: "ShadowsocksOn")
@ -195,7 +219,64 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
ProxyConfHelper.disableProxy()
}
}
// MARK: - Hotkey Methods
func registerHotkey() -> Void {
var gMyHotKeyID = EventHotKeyID()
gMyHotKeyID.signature = OSType(fourCharCodeFrom(string: "sxng"))
gMyHotKeyID.id = UInt32(keyCode)
var eventType = EventTypeSpec()
eventType.eventClass = OSType(kEventClassKeyboard)
eventType.eventKind = OSType(kEventHotKeyPressed)
// Void pointer to `self`:
let context = Unmanaged.passUnretained(self).toOpaque()
// Install handler.
InstallEventHandler(GetApplicationEventTarget(), {(nextHanlder, theEvent, userContext) -> OSStatus in
// Extract pointer to `self` from void pointer:
let mySelf = Unmanaged<AppDelegate>.fromOpaque(userContext!).takeUnretainedValue()
switch Globals.proxyType {
case .pac:
Globals.proxyType = .global
UserDefaults.standard.setValue("global", forKey: "ShadowsocksRunningMode")
mySelf.isNameTextField.stringValue = "Gobal Mode"
mySelf.updateRunningModeMenu()
mySelf.applyConfig()
case .global:
Globals.proxyType = .pac
UserDefaults.standard.setValue("auto", forKey: "ShadowsocksRunningMode")
mySelf.isNameTextField.stringValue = "Auto Mode"
mySelf.updateRunningModeMenu()
mySelf.applyConfig()
}
mySelf.fadeInHud()
return noErr
}, 1, &eventType, context, nil)
// Register hotkey.
RegisterEventHotKey(UInt32(keyCode),
UInt32(modifierKeys),
gMyHotKeyID,
GetApplicationEventTarget(),
0,
&hotKeyRef)
}
func fourCharCodeFrom(string: String) -> FourCharCode {
assert(string.characters.count == 4, "String length must be 4")
var result: FourCharCode = 0
for char in string.utf16 {
result = (result << 8) + FourCharCode(char)
}
return result
}
// MARK: - UI Methods
@IBAction func toggleRunning(_ sender: NSMenuItem) {
let defaults = UserDefaults.standard
var isOn = defaults.bool(forKey: "ShadowsocksOn")
@ -510,3 +591,93 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
}
}
extension AppDelegate {
func fadeInHud() -> Void {
if timerToFadeOut != nil {
timerToFadeOut?.invalidate()
timerToFadeOut = nil
}
fadingOut = false
hudWindow.orderFrontRegardless()
CATransaction.begin()
CATransaction.setAnimationDuration(kHudFadeInDuration)
CATransaction.setCompletionBlock { self.didFadeIn() }
panelView.layer?.opacity = 1.0
CATransaction.commit()
}
func didFadeIn() -> Void {
timerToFadeOut = Timer.scheduledTimer(
timeInterval: kHudDisplayDuration,
target: self,
selector: #selector(fadeOutHud),
userInfo: nil,
repeats: false)
}
func fadeOutHud() -> Void {
fadingOut = true
CATransaction.begin()
CATransaction.setAnimationDuration(kHudFadeOutDuration)
CATransaction.setCompletionBlock { self.didFadeOut() }
panelView.layer?.opacity = 0.0
CATransaction.commit()
}
func didFadeOut() -> Void {
if fadingOut {
self.hudWindow.orderOut(nil)
}
fadingOut = false
}
func setupHud() -> Void {
isNameTextField.stringValue = "Global Mode"
isNameTextField.sizeToFit()
var labelFrame: CGRect = isNameTextField.frame
var hudWindowFrame: CGRect = hudWindow.frame
hudWindowFrame.size.width = labelFrame.size.width + kHudHorizontalMargin * 2
hudWindowFrame.size.height = kHudHeight
let screenRect: NSRect = NSScreen.screens()![0].visibleFrame
hudWindowFrame.origin.x = (screenRect.size.width - hudWindowFrame.size.width) / 2
hudWindowFrame.origin.y = (screenRect.size.height - hudWindowFrame.size.height) / 2
hudWindow.setFrame(hudWindowFrame, display: true)
var viewFrame: NSRect = hudWindowFrame;
viewFrame.origin.x = 0
viewFrame.origin.y = 0
panelView.frame = viewFrame
labelFrame.origin.x = kHudHorizontalMargin
labelFrame.origin.y = (hudWindowFrame.size.height - labelFrame.size.height) / 2
isNameTextField.frame = labelFrame
}
func initUIComponent() -> Void {
hudWindow.isOpaque = false
hudWindow.backgroundColor = .clear
hudWindow.level = Int(CGWindowLevelForKey(.utilityWindow)) + 1000
hudWindow.styleMask = .borderless
hudWindow.hidesOnDeactivate = false
hudWindow.collectionBehavior = .canJoinAllSpaces
let viewLayer: CALayer = CALayer()
viewLayer.backgroundColor = CGColor.init(red: 0.05, green: 0.05, blue: 0.05, alpha: kHudAlphaValue)
viewLayer.cornerRadius = kHudCornerRadius
panelView.wantsLayer = true
panelView.layer = viewLayer
panelView.layer?.opacity = 0.0
setupHud()
}
override func awakeFromNib() {
initUIComponent()
}
}