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:
@ -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 */;
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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 {
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
26
phpmon/Domain/Preferences/PrefsWC.swift
Normal file
26
phpmon/Domain/Preferences/PrefsWC.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
|
||||
|
Reference in New Issue
Block a user