1
0
mirror of https://github.com/nicoverbruggen/phpmon.git synced 2025-08-07 12:00:09 +02:00

🏗 WIP: Global shortcut key (#33)

This commit is contained in:
2021-04-02 17:50:19 +02:00
parent acb18474c8
commit 2925b0ff79
8 changed files with 358 additions and 29 deletions

View File

@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objectVersion = 52;
objects = {
/* Begin PBXBuildFile section */
@ -38,6 +38,9 @@
C48D0C9625CC80B100CC7490 /* HeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D0C9525CC80B100CC7490 /* HeaderView.swift */; };
C48D0C9A25CC888B00CC7490 /* HeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C48D0C9925CC888B00CC7490 /* HeaderView.xib */; };
C48D0CA325CC992000CC7490 /* StatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C48D0CA225CC992000CC7490 /* StatsView.swift */; };
C4998F0626175E7200B2526E /* HotKey in Frameworks */ = {isa = PBXBuildFile; productRef = C4998F0526175E7200B2526E /* HotKey */; };
C4998F0A2617633900B2526E /* PrefsWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PrefsWC.swift */; };
C4998F0B2617633900B2526E /* PrefsWC.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4998F092617633900B2526E /* PrefsWC.swift */; };
C49EAB46259FC305007F6C3B /* Paths.swift in Sources */ = {isa = PBXBuildFile; fileRef = C49EAB45259FC305007F6C3B /* Paths.swift */; };
C4ACA38F25C754C100060C66 /* PhpExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4ACA38E25C754C100060C66 /* PhpExtension.swift */; };
C4D8016622B1584700C6DA1B /* Startup.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4D8016522B1584700C6DA1B /* Startup.swift */; };
@ -113,6 +116,7 @@
C48D0C9525CC80B100CC7490 /* HeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderView.swift; sourceTree = "<group>"; };
C48D0C9925CC888B00CC7490 /* HeaderView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = HeaderView.xib; sourceTree = "<group>"; };
C48D0CA225CC992000CC7490 /* StatsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsView.swift; sourceTree = "<group>"; };
C4998F092617633900B2526E /* PrefsWC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrefsWC.swift; sourceTree = "<group>"; };
C49EAB45259FC305007F6C3B /* Paths.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Paths.swift; sourceTree = "<group>"; };
C4ACA38E25C754C100060C66 /* PhpExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhpExtension.swift; sourceTree = "<group>"; };
C4D8016522B1584700C6DA1B /* Startup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Startup.swift; sourceTree = "<group>"; };
@ -135,6 +139,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
C4998F0626175E7200B2526E /* HotKey in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -151,6 +156,7 @@
5420395726135DB800FB00FA /* Preferences */ = {
isa = PBXGroup;
children = (
C4998F092617633900B2526E /* PrefsWC.swift */,
5420395826135DC100FB00FA /* PrefsVC.swift */,
5420395E2613607600FB00FA /* Preferences.swift */,
);
@ -305,6 +311,9 @@
dependencies = (
);
name = "PHP Monitor";
packageProductDependencies = (
C4998F0526175E7200B2526E /* HotKey */,
);
productName = phpmon;
productReference = C41C1B3322B0097F00E7CF16 /* PHP Monitor.app */;
productType = "com.apple.product-type.application";
@ -355,6 +364,9 @@
Base,
);
mainGroup = C41C1B2A22B0097F00E7CF16;
packageReferences = (
C4998F0426175E7200B2526E /* XCRemoteSwiftPackageReference "HotKey" */,
);
productRefGroup = C41C1B3422B0097F00E7CF16 /* Products */;
projectDirPath = "";
projectRoot = "";
@ -399,6 +411,7 @@
files = (
C4ACA38F25C754C100060C66 /* PhpExtension.swift in Sources */,
C4D8016622B1584700C6DA1B /* Startup.swift in Sources */,
C4998F0A2617633900B2526E /* PrefsWC.swift in Sources */,
C4F8C0A422D4F12C002EFE61 /* DateExtension.swift in Sources */,
5420395926135DC100FB00FA /* PrefsVC.swift in Sources */,
C41C1B4722B009A400E7CF16 /* Shell.swift in Sources */,
@ -442,6 +455,7 @@
C481F79726164A78004FBCFF /* PrefsVC.swift in Sources */,
C4F7809C25D80344000DBC97 /* CommandTest.swift in Sources */,
C4F780BA25D80B62000DBC97 /* AppDelegate.swift in Sources */,
C4998F0B2617633900B2526E /* PrefsWC.swift in Sources */,
C4F780A225D804AA000DBC97 /* Paths.swift in Sources */,
C4F780BD25D80B65000DBC97 /* Constants.swift in Sources */,
C4F780C325D80B75000DBC97 /* HeaderView.swift in Sources */,
@ -605,7 +619,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 54;
CURRENT_PROJECT_VERSION = 55;
DEVELOPMENT_TEAM = 8M54J5J787;
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = phpmon/Info.plist;
@ -613,7 +627,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MARKETING_VERSION = 3.3;
MARKETING_VERSION = 3.4;
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -629,7 +643,7 @@
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 54;
CURRENT_PROJECT_VERSION = 55;
DEVELOPMENT_TEAM = 8M54J5J787;
ENABLE_HARDENED_RUNTIME = YES;
INFOPLIST_FILE = phpmon/Info.plist;
@ -637,7 +651,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MARKETING_VERSION = 3.3;
MARKETING_VERSION = 3.4;
PRODUCT_BUNDLE_IDENTIFIER = com.nicoverbruggen.phpmon;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@ -718,6 +732,25 @@
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
C4998F0426175E7200B2526E /* XCRemoteSwiftPackageReference "HotKey" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/soffes/HotKey";
requirement = {
kind = upToNextMinorVersion;
minimumVersion = 0.1.3;
};
};
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
C4998F0526175E7200B2526E /* HotKey */ = {
isa = XCSwiftPackageProductDependency;
package = C4998F0426175E7200B2526E /* XCRemoteSwiftPackageReference "HotKey" */;
productName = HotKey;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = C41C1B2B22B0097F00E7CF16 /* Project object */;
}

View File

@ -4,12 +4,18 @@
//
// Copyright © 2021 Nico Verbruggen. All rights reserved.
//
import Cocoa
import HotKey
class App {
static let shared = App()
init() {
loadGlobalHotkey()
}
static var phpInstall: PhpInstallation? {
return App.shared.currentInstall
}
@ -66,4 +72,42 @@ class App {
*/
var brewPhpVersion: String = "8.0"
/**
The shortcut the user has requested.
*/
var shortcutHotkey: HotKey? = nil {
didSet {
self.setupGlobalHotkeyListener()
}
}
// MARK: - Methods
private func loadGlobalHotkey() {
let hotkey = Preferences.preferences[.globalHotkey] as! String?
if hotkey == nil {
return
}
let keybindPref = GlobalKeybindPreference.fromJson(hotkey!)
if (keybindPref != nil) {
self.shortcutHotkey = HotKey(keyCombo: KeyCombo(
carbonKeyCode: keybindPref!.keyCode,
carbonModifiers: keybindPref!.carbonFlags
))
} else {
self.shortcutHotkey = nil
}
}
private func setupGlobalHotkeyListener() {
guard let hotKey = self.shortcutHotkey else {
return
}
hotKey.keyDownHandler = {
MainMenu.shared.statusItem.button?.performClick(nil)
}
}
}

View File

@ -58,7 +58,7 @@
<!--Window Controller-->
<scene sceneID="PQa-AT-b2a">
<objects>
<windowController storyboardIdentifier="preferencesWindow" showSeguePresentationStyle="single" id="hLJ-Fd-wRr" sceneMemberID="viewController">
<windowController storyboardIdentifier="preferencesWindow" showSeguePresentationStyle="single" id="hLJ-Fd-wRr" customClass="PrefsWC" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController">
<window key="window" title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="h4c-3b-nko">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
@ -85,7 +85,7 @@
<objects>
<viewController title="Preferences" storyboardIdentifier="preferences" showSeguePresentationStyle="single" id="AW2-rV-rbS" customClass="PrefsVC" customModule="PHP_Monitor" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="Pf1-A5-3Xz">
<rect key="frame" x="0.0" y="0.0" width="462" height="139"/>
<rect key="frame" x="0.0" y="0.0" width="462" height="185"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="GSr-K5-3yw">
@ -99,7 +99,7 @@
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="MEf-MN-oXt">
<rect key="frame" x="18" y="102" width="424" height="18"/>
<rect key="frame" x="18" y="148" width="424" height="18"/>
<buttonCell key="cell" type="check" title="DYN_ICON" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="m5s-qp-Iaj">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
@ -109,9 +109,40 @@
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="JrH-aa-AzL">
<rect key="frame" x="18" y="81" width="426" height="14"/>
<rect key="frame" x="18" y="127" width="426" height="14"/>
<textFieldCell key="cell" title="DYN_ICON_DESC" id="MHA-Xt-xgF">
<font key="font" metaFont="system" size="11"/>
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="V7b-jv-oCB">
<rect key="frame" x="13" y="72" width="164" height="32"/>
<constraints>
<constraint firstAttribute="width" constant="150" id="9jD-Bf-T2M"/>
</constraints>
<buttonCell key="cell" type="push" title="SET_SHORTCUT" bezelStyle="rounded" alignment="center" borderStyle="border" inset="2" id="R63-tN-KVQ">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="register:" target="AW2-rV-rbS" id="4Mj-eM-4eW"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="YsQ-AZ-Aei">
<rect key="frame" x="175" y="72" width="154" height="32"/>
<buttonCell key="cell" type="push" title="CLEAR_SHORTCUT" bezelStyle="rounded" alignment="center" enabled="NO" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="nvE-5d-VOS">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="unregister:" target="AW2-rV-rbS" id="2RI-4w-6Td"/>
</connections>
</button>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="1TO-9H-z2d">
<rect key="frame" x="18" y="57" width="101" height="14"/>
<textFieldCell key="cell" title="SHORTCUT_DESC" id="nYP-yi-DBf">
<font key="font" metaFont="smallSystem"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
@ -120,24 +151,33 @@
<constraints>
<constraint firstAttribute="trailing" secondItem="JrH-aa-AzL" secondAttribute="trailing" constant="20" symbolic="YES" id="8iM-Xf-ShU"/>
<constraint firstAttribute="trailing" secondItem="GSr-K5-3yw" secondAttribute="trailing" constant="20" symbolic="YES" id="AT9-5F-6g1"/>
<constraint firstItem="YsQ-AZ-Aei" firstAttribute="leading" secondItem="V7b-jv-oCB" secondAttribute="trailing" constant="12" symbolic="YES" id="Bk6-4V-GLk"/>
<constraint firstItem="YsQ-AZ-Aei" firstAttribute="top" secondItem="V7b-jv-oCB" secondAttribute="top" id="CzF-dC-Jm8"/>
<constraint firstItem="V7b-jv-oCB" firstAttribute="leading" secondItem="MEf-MN-oXt" secondAttribute="leading" id="El4-TW-O0d"/>
<constraint firstItem="MEf-MN-oXt" firstAttribute="top" secondItem="Pf1-A5-3Xz" secondAttribute="top" constant="20" symbolic="YES" id="FJC-Lx-L8a"/>
<constraint firstItem="V7b-jv-oCB" firstAttribute="top" secondItem="JrH-aa-AzL" secondAttribute="bottom" constant="28" id="I62-Jw-hfy"/>
<constraint firstItem="MEf-MN-oXt" firstAttribute="leading" secondItem="Pf1-A5-3Xz" secondAttribute="leading" constant="20" symbolic="YES" id="Imd-YJ-Ae7"/>
<constraint firstItem="JrH-aa-AzL" firstAttribute="top" secondItem="MEf-MN-oXt" secondAttribute="bottom" constant="8" symbolic="YES" id="Vf8-fx-H50"/>
<constraint firstItem="1TO-9H-z2d" firstAttribute="firstBaseline" secondItem="V7b-jv-oCB" secondAttribute="baseline" constant="25" id="bJG-ed-pch"/>
<constraint firstAttribute="bottom" secondItem="GSr-K5-3yw" secondAttribute="bottom" constant="20" symbolic="YES" id="dAS-yW-vua"/>
<constraint firstItem="JrH-aa-AzL" firstAttribute="leading" secondItem="MEf-MN-oXt" secondAttribute="leading" id="dzR-S7-M6U"/>
<constraint firstItem="1TO-9H-z2d" firstAttribute="leading" secondItem="V7b-jv-oCB" secondAttribute="leading" id="lzx-14-MwL"/>
<constraint firstItem="GSr-K5-3yw" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="Pf1-A5-3Xz" secondAttribute="leading" constant="20" symbolic="YES" id="mTE-WD-54L"/>
<constraint firstAttribute="trailing" secondItem="MEf-MN-oXt" secondAttribute="trailing" constant="20" symbolic="YES" id="pJg-zj-cBs"/>
</constraints>
</view>
<connections>
<outlet property="buttonClearShortcut" destination="YsQ-AZ-Aei" id="1xo-hk-HgM"/>
<outlet property="buttonClose" destination="GSr-K5-3yw" id="d4I-Cf-gXD"/>
<outlet property="buttonDynamicIcon" destination="MEf-MN-oXt" id="qEN-Vg-EZS"/>
<outlet property="buttonSetShortcut" destination="V7b-jv-oCB" id="2aS-S4-cKR"/>
<outlet property="labelDynamicIcon" destination="JrH-aa-AzL" id="CFc-fF-oPq"/>
<outlet property="labelShortcut" destination="1TO-9H-z2d" id="paF-hK-78x"/>
</connections>
</viewController>
<customObject id="eQC-8B-FkX" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="216" y="319"/>
<point key="canvasLocation" x="216" y="341.5"/>
</scene>
</scenes>
</document>

View File

@ -169,7 +169,7 @@ class MainMenu: NSObject, NSWindowDelegate {
if (App.busy) {
setStatusBar(image: NSImage(named: NSImage.Name("StatusBarIcon"))!)
} else {
if Preferences.preferences[.shouldDisplayDynamicIcon] == false {
if Preferences.preferences[.shouldDisplayDynamicIcon] as! Bool == false {
// Static icon has been requested
setStatusBar(image: NSImage(named: NSImage.Name("StatusBarIconStatic"))!)
} else {

View File

@ -10,6 +10,7 @@ import Foundation
enum PreferenceName: String {
case shouldDisplayDynamicIcon = "use_dynamic_icon"
case globalHotkey = "global_hotkey"
}
class Preferences {
@ -28,22 +29,27 @@ class Preferences {
print("Saving first-time preferences!")
}
static func retrieve() -> [PreferenceName: Bool] {
static func retrieve() -> [PreferenceName: Any] {
Preferences.handleFirstTimeLaunch()
return [
.shouldDisplayDynamicIcon: UserDefaults.standard.bool(
forKey: PreferenceName.shouldDisplayDynamicIcon.rawValue
)
forKey: PreferenceName.shouldDisplayDynamicIcon.rawValue) as Any,
.globalHotkey: UserDefaults.standard.string(
forKey: PreferenceName.globalHotkey.rawValue) as Any
]
}
static var preferences: [PreferenceName: Bool] {
static var preferences: [PreferenceName: Any?] {
return Preferences.retrieve()
}
static func update(_ preference: PreferenceName, value: Bool) {
UserDefaults.standard.setValue(value, forKey: preference.rawValue)
static func update(_ preference: PreferenceName, value: Any?) {
if (value == nil) {
UserDefaults.standard.removeObject(forKey: preference.rawValue)
} else {
UserDefaults.standard.setValue(value, forKey: preference.rawValue)
}
UserDefaults.standard.synchronize()
}

View File

@ -7,6 +7,8 @@
//
import Cocoa
import HotKey
import Carbon
class PrefsVC: NSViewController {
@ -14,38 +16,212 @@ class PrefsVC: NSViewController {
@IBOutlet weak var labelDynamicIcon: NSTextField!
@IBOutlet weak var buttonClose: NSButton!
@IBOutlet weak var buttonSetShortcut: NSButton!
@IBOutlet weak var buttonClearShortcut: NSButton!
@IBOutlet weak var labelShortcut: NSTextField!
// MARK: - Variables
var listening = false {
didSet {
if listening {
DispatchQueue.main.async { [weak self] in
self?.buttonSetShortcut.highlight(true)
self?.buttonSetShortcut.title = "prefs.shortcut_listening".localized
}
} else {
DispatchQueue.main.async { [weak self] in
self?.buttonSetShortcut.highlight(false)
if (App.shared.shortcutHotkey == nil) {
self?.buttonSetShortcut.title = "prefs.shortcut_set".localized
}
}
}
}
}
// MARK: - Display
public static func show(delegate: NSWindowDelegate? = nil) {
if (App.shared.windowController == nil) {
let vc = NSStoryboard(name: "Main", bundle: nil).instantiateController(withIdentifier: "preferences") as! PrefsVC
let vc = NSStoryboard(name: "Main", bundle: nil)
.instantiateController(withIdentifier: "preferences") as! PrefsVC
let window = NSWindow(contentViewController: vc)
window.title = "prefs.title".localized
window.delegate = delegate
window.styleMask = [.titled, .closable]
App.shared.windowController = NSWindowController(window: window)
App.shared.windowController = PrefsWC(window: window)
}
App.shared.windowController!.showWindow(self)
NSApp.activate(ignoringOtherApps: true)
}
// MARK: - Lifecycle
override func viewWillAppear() {
// Load localization
buttonDynamicIcon.title = "prefs.dynamic_icon_title".localized
labelDynamicIcon.stringValue = "prefs.dynamic_icon_desc".localized
buttonClose.title = "prefs.close".localized
labelShortcut.stringValue = "prefs.shortcut_desc".localized
buttonSetShortcut.title = "prefs.shortcut_set".localized
buttonClearShortcut.title = "prefs.shortcut_clear".localized
let prefs = Preferences.preferences
// Load dynamic icon
self.buttonDynamicIcon.state = (prefs[.shouldDisplayDynamicIcon] as! Bool == true) ? .on : .off
// Load global keybind initial state
let globalKeybind = GlobalKeybindPreference.fromJson(prefs[.globalHotkey] as! String?)
if (globalKeybind != nil) {
updateKeybindButton(globalKeybind!)
}
buttonClearShortcut.isEnabled = globalKeybind != nil
}
// MARK: - Shortcut
// Adapted from: https://dev.to/mitchartemis/creating-a-global-configurable-shortcut-for-macos-apps-in-swift-25e9
func updateGlobalShortcut(_ event : NSEvent) {
self.listening = false
if let characters = event.charactersIgnoringModifiers {
let newGlobalKeybind = GlobalKeybindPreference.init(
function: event.modifierFlags.contains(.function),
control: event.modifierFlags.contains(.control),
command: event.modifierFlags.contains(.command),
shift: event.modifierFlags.contains(.shift),
option: event.modifierFlags.contains(.option),
capsLock: event.modifierFlags.contains(.capsLock),
carbonFlags: event.modifierFlags.carbonFlags,
characters: characters,
keyCode: UInt32(event.keyCode)
)
Preferences.update(.globalHotkey, value: newGlobalKeybind.toJson())
updateKeybindButton(newGlobalKeybind)
buttonClearShortcut.isEnabled = true
App.shared.shortcutHotkey = HotKey(
keyCombo: KeyCombo(
carbonKeyCode: UInt32(event.keyCode),
carbonModifiers: event.modifierFlags.carbonFlags
)
)
}
}
@IBAction func register(_ sender: Any) {
unregister(nil)
listening = true
view.window?.makeFirstResponder(nil)
}
@IBAction func unregister(_ sender: Any?) {
listening = false
App.shared.shortcutHotkey = nil
buttonSetShortcut.title = ""
Preferences.update(.globalHotkey, value: nil)
}
func updateClearButton(_ globalKeybindPreference: GlobalKeybindPreference?) {
if globalKeybindPreference != nil {
buttonClearShortcut.isEnabled = true
} else {
buttonClearShortcut.isEnabled = false
}
}
func updateKeybindButton(_ globalKeybindPreference: GlobalKeybindPreference) {
buttonSetShortcut.title = globalKeybindPreference.description
}
// MARK: - Actions
@IBAction func toggledDynamicIcon(_ sender: Any) {
Preferences.update(.shouldDisplayDynamicIcon, value: buttonDynamicIcon.state == .on)
MainMenu.shared.refreshIcon()
}
override func viewWillAppear() {
buttonDynamicIcon.title = "prefs.dynamic_icon_title".localized
labelDynamicIcon.stringValue = "prefs.dynamic_icon_desc".localized
buttonClose.title = "prefs.close".localized
let prefs = Preferences.preferences
self.buttonDynamicIcon.state = (prefs[.shouldDisplayDynamicIcon] == true) ? .on : .off
}
@IBAction func pressed(_ sender: Any) {
self.view.window?.windowController?.close()
}
// MARK: - Deinitialization
deinit {
print("VC deallocated")
}
}
struct GlobalKeybindPreference: Codable, CustomStringConvertible {
// MARK: - Internal variables
let function : Bool
let control : Bool
let command : Bool
let shift : Bool
let option : Bool
let capsLock : Bool
let carbonFlags : UInt32
let characters : String?
let keyCode : UInt32
// MARK: - How the keybind is display in Preferences
var description: String {
var stringBuilder = ""
if self.function {
stringBuilder += "Fn"
}
if self.control {
stringBuilder += ""
}
if self.option {
stringBuilder += ""
}
if self.command {
stringBuilder += ""
}
if self.shift {
stringBuilder += ""
}
if self.capsLock {
stringBuilder += ""
}
if let characters = self.characters {
stringBuilder += characters.uppercased()
}
return "\(stringBuilder)"
}
// MARK: - Persisting data to UserDefaults (as JSON)
public func toJson() -> String {
let jsonData = try! JSONEncoder().encode(self)
return String(data: jsonData, encoding: .utf8)!
}
public static func fromJson(_ string: String?) -> GlobalKeybindPreference? {
if string == nil {
return nil
}
if let jsonData = string!.data(using: .utf8) {
let decoder = JSONDecoder()
do {
return try decoder.decode(GlobalKeybindPreference.self, from: jsonData)
} catch {
return nil
}
}
return nil
}
}

View File

@ -0,0 +1,26 @@
//
// PrefsWC.swift
// PHP Monitor
//
// Created by Nico Verbruggen on 02/04/2021.
// Copyright © 2021 Nico Verbruggen. All rights reserved.
//
import Cocoa
class PrefsWC: NSWindowController {
override func windowDidLoad() {
super.windowDidLoad()
}
override func keyDown(with event: NSEvent) {
super.keyDown(with: event)
if let vc = self.contentViewController as? PrefsVC {
if vc.listening {
vc.updateGlobalShortcut(event)
}
}
}
}

View File

@ -49,6 +49,10 @@
"prefs.close" = "Close";
"prefs.dynamic_icon_title" = "Show a dynamic icon in the menu bar";
"prefs.dynamic_icon_desc" = "If you uncheck this box, the truck icon will always be visible.\nIf checked, it will display the major version number of the currently linked PHP version.";
"prefs.shortcut_set" = "Set global shortcut";
"prefs.shortcut_listening" = "<press for shortcut>";
"prefs.shortcut_clear" = "Clear";
"prefs.shortcut_desc" = "If a shortcut combination is set up, you can toggle PHP Monitor\nwherever you are by pressing the key combination you chose.";
// NOTIFICATIONS