From ed6161850b76e423c6e503be00fc89104124cad6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Oct 2019 20:33:41 +0000 Subject: [PATCH 1/9] Bump ffi from 1.9.23 to 1.11.1 in /docs Bumps [ffi](https://github.com/ffi/ffi) from 1.9.23 to 1.11.1. - [Release notes](https://github.com/ffi/ffi/releases) - [Changelog](https://github.com/ffi/ffi/blob/master/CHANGELOG.md) - [Commits](https://github.com/ffi/ffi/compare/1.9.23...1.11.1) Signed-off-by: dependabot[bot] --- docs/Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index e9305f0..e340c06 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -27,7 +27,7 @@ GEM eventmachine (>= 0.12.9) http_parser.rb (~> 0.6.0) eventmachine (1.2.5) - ffi (1.9.23) + ffi (1.11.1) forwardable-extended (2.6.0) http_parser.rb (0.6.0) i18n (0.9.5) From 18fb61b1619b1c796bc8942d2c849b8958d2cc9b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 12 Jul 2021 20:42:50 +0000 Subject: [PATCH 2/9] Bump addressable from 2.5.2 to 2.8.0 in /docs Bumps [addressable](https://github.com/sporkmonger/addressable) from 2.5.2 to 2.8.0. - [Release notes](https://github.com/sporkmonger/addressable/releases) - [Changelog](https://github.com/sporkmonger/addressable/blob/main/CHANGELOG.md) - [Commits](https://github.com/sporkmonger/addressable/compare/addressable-2.5.2...addressable-2.8.0) --- updated-dependencies: - dependency-name: addressable dependency-type: indirect ... Signed-off-by: dependabot[bot] --- docs/Gemfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock index e9305f0..92ffb6e 100644 --- a/docs/Gemfile.lock +++ b/docs/Gemfile.lock @@ -19,8 +19,8 @@ GIT GEM remote: https://rubygems.org/ specs: - addressable (2.5.2) - public_suffix (>= 2.0.2, < 4.0) + addressable (2.8.0) + public_suffix (>= 2.0.2, < 5.0) colorator (1.1.0) concurrent-ruby (1.0.5) em-websocket (0.5.1) @@ -51,7 +51,7 @@ GEM mercenary (0.3.6) pathutil (0.16.1) forwardable-extended (~> 2.6) - public_suffix (3.0.2) + public_suffix (4.0.6) rb-fsevent (0.10.3) rb-inotify (0.9.10) ffi (>= 0.5.0, < 2) From d134f6b03d7c13092f995d0cd1c39e9884a0bee2 Mon Sep 17 00:00:00 2001 From: thompsonate Date: Sun, 14 Nov 2021 01:42:41 -0500 Subject: [PATCH 3/9] refactor status notification block --- Shifty/AppDelegate.swift | 4 ++ Shifty/NightShiftManager.swift | 21 ++++++----- Shifty/PrefGeneralViewController.swift | 51 +++++++++++++------------- 3 files changed, 40 insertions(+), 36 deletions(-) diff --git a/Shifty/AppDelegate.swift b/Shifty/AppDelegate.swift index 179e6a5..75cb2ef 100644 --- a/Shifty/AppDelegate.swift +++ b/Shifty/AppDelegate.swift @@ -102,6 +102,10 @@ class AppDelegate: NSObject, NSApplicationDelegate { updateMenuBarIcon() setStatusToggle() + NightShiftManager.shared.onNightShiftChange { + self.updateMenuBarIcon() + } + statusItem.behavior = .terminationOnRemoval statusItem.isVisible = true diff --git a/Shifty/NightShiftManager.swift b/Shifty/NightShiftManager.swift index ad5020c..88b8496 100644 --- a/Shifty/NightShiftManager.swift +++ b/Shifty/NightShiftManager.swift @@ -12,6 +12,8 @@ import SwiftLog class NightShiftManager { static let shared = NightShiftManager() let client = CBBlueLightClient.shared + + var nightShiftChangeListeners = [() -> Void]() var userSet: UserSet = .notSet var userInitiatedShift = false @@ -81,16 +83,11 @@ class NightShiftManager { } self.updateDarkMode() - - DispatchQueue.main.async { - let appDelegate = NSApplication.shared.delegate as! AppDelegate - appDelegate.updateMenuBarIcon() - - let prefWindow = appDelegate.preferenceWindowController - let prefGeneral = prefWindow.viewControllers.compactMap { childViewController in - return childViewController as? PrefGeneralViewController - }.first - prefGeneral?.updateSchedule?() + + for listener in self.nightShiftChangeListeners { + DispatchQueue.main.async { + listener() + } } } @@ -108,6 +105,10 @@ class NightShiftManager { } } + func onNightShiftChange(_ listener: @escaping () -> Void) { + nightShiftChangeListeners.append(listener) + } + func updateDarkMode() { if UserDefaults.standard.bool(forKey: Keys.isDarkModeSyncEnabled) { let scheduledState = client.scheduledState diff --git a/Shifty/PrefGeneralViewController.swift b/Shifty/PrefGeneralViewController.swift index 77f90ac..d369ff7 100644 --- a/Shifty/PrefGeneralViewController.swift +++ b/Shifty/PrefGeneralViewController.swift @@ -57,11 +57,6 @@ class PrefGeneralViewController: NSViewController, MASPreferencesViewController @IBOutlet weak var toLabel: NSTextField! @IBOutlet weak var customTimeStackView: NSStackView! - let prefs = UserDefaults.standard - var setStatusToggle: (() -> Void)? - var updateSchedule: (() -> Void)? - var updateDarkMode: (() -> Void)? - var appDelegate: AppDelegate! var prefWindow: NSWindow! @@ -72,6 +67,10 @@ class PrefGeneralViewController: NSViewController, MASPreferencesViewController appDelegate = NSApplication.shared.delegate as? AppDelegate prefWindow = appDelegate.preferenceWindowController.window + + NightShiftManager.shared.onNightShiftChange { + self.updateSchedule() + } //Hide True Tone settings on unsupported computers if #available(macOS 10.14, *) { @@ -86,31 +85,31 @@ class PrefGeneralViewController: NSViewController, MASPreferencesViewController if !ProcessInfo().isOperatingSystemAtLeast(OperatingSystemVersion(majorVersion: 10, minorVersion: 13, patchVersion: 0)) { view.wantsLayer = false } - - updateSchedule = { - switch NightShiftManager.shared.schedule { - case .off: - self.schedulePopup.select(self.offMenuItem) - self.customTimeStackView.isHidden = true - case .custom(start: let startTime, end: let endTime): - self.schedulePopup.select(self.customMenuItem) - let startDate = Date(startTime) - let endDate = Date(endTime) - - self.fromTimePicker.dateValue = startDate - self.toTimePicker.dateValue = endDate - self.customTimeStackView.isHidden = false - case .solar: - self.schedulePopup.select(self.sunMenuItem) - self.customTimeStackView.isHidden = true - } - } } override func viewWillAppear() { super.viewWillAppear() - updateSchedule?() + updateSchedule() + } + + func updateSchedule() { + switch NightShiftManager.shared.schedule { + case .off: + self.schedulePopup.select(self.offMenuItem) + self.customTimeStackView.isHidden = true + case .custom(start: let startTime, end: let endTime): + self.schedulePopup.select(self.customMenuItem) + let startDate = Date(startTime) + let endDate = Date(endTime) + + self.fromTimePicker.dateValue = startDate + self.toTimePicker.dateValue = endDate + self.customTimeStackView.isHidden = false + case .solar: + self.schedulePopup.select(self.sunMenuItem) + self.customTimeStackView.isHidden = true + } } //MARK: IBActions @@ -149,7 +148,7 @@ class PrefGeneralViewController: NSViewController, MASPreferencesViewController if !UIElement.isProcessTrusted() { logw("Accessibility permissions alert shown") - prefs.set(false, forKey: Keys.isWebsiteControlEnabled) + UserDefaults.standard.set(false, forKey: Keys.isWebsiteControlEnabled) NSApp.runModal(for: AccessibilityWindow().window!) } } else { From 184d475bfa0688a5730a0a55bc534c8507a87e66 Mon Sep 17 00:00:00 2001 From: thompsonate Date: Sun, 14 Nov 2021 02:24:42 -0500 Subject: [PATCH 4/9] add Brave support --- .../xcshareddata/xcschemes/Shifty.xcscheme | 8 +- Shifty/Assets.xcassets/Contents.json | 6 +- .../braveIcon.imageset/Contents.json | 12 + .../braveIcon.imageset/braveIcon.png | Bin 0 -> 3034 bytes Shifty/Base.lproj/Setup.storyboard | 400 ++++++++++-------- Shifty/BrowserManager.swift | 8 +- 6 files changed, 250 insertions(+), 184 deletions(-) create mode 100644 Shifty/Assets.xcassets/braveIcon.imageset/Contents.json create mode 100644 Shifty/Assets.xcassets/braveIcon.imageset/braveIcon.png diff --git a/Shifty.xcodeproj/xcshareddata/xcschemes/Shifty.xcscheme b/Shifty.xcodeproj/xcshareddata/xcschemes/Shifty.xcscheme index 45850ff..3950274 100644 --- a/Shifty.xcodeproj/xcshareddata/xcschemes/Shifty.xcscheme +++ b/Shifty.xcodeproj/xcshareddata/xcschemes/Shifty.xcscheme @@ -62,13 +62,13 @@ + value = "true" + isEnabled = "NO"> + value = "true" + isEnabled = "NO"> zk#d4MF{%T{k1pF(Gl zcq|Um3T=nB1prHPOAD+8cv~aQEv1Z=6#ziRke~60=SJOb{bI!C0WpF~5R=$xg@jKPWzl{aX-^M*WR1>B(?US>~IcUR)`QXhC-(>C`=v~#93gb^9R#&kVcMmW6@chEo>5*g0patB{Ng>vl8F5 zWo<;n(?E+{uxdCf%kP-k2fqaR{SG+eU57b}ED z72ruc3QpSXW)|RWP59C6-{3QJW}u~o#%`u?S@dvecla=AJRF$nuf(%aZY&0yL*dHG zWBwg7Q#Md%#_X6C;kizXY(VbnZnrszh41n3TFU9$XIT8@JNLNjz%|gnkU{L<~Hx&1IsG8 zco;Skn-m0x3)ZNrKKB;%xDR*>sL*GPJB39`J8b}$FsN%*2=pA(;uI90Jn%R-Wvwba z{am^aoh4*-f7&1N=ID0)NmI$%s*N;OkVFgd5h3T1G|#WKc+(**QB>X^+6PCeX5P8B z(1y!#r$!E;Od?14&(uZ>^XjjuIM&z+Y?mUEs)B~$RuS-`AXMUzJ94zpM>rpot_4^k zQiYe=QTLqWAV$V)EALi0lVZ1;YLhmCudj$CwsrFMJKX>{b(|?HLaPEX)epb!)lf}A zrjQGAGBzW&aUdRxwsdIu4fk5K9P=7Y&W>|VQIn)Db>4JMp(VfS0m(UE#re5sPv1~t zti6|aPWW2|dUFxosYQs(rkjr^mX#iN!2>15Iyu)zN)!;Cu*^abd9>_R z{QZ;k%!{LKg_GFFb%bcfp?Go*v|Lp7;R6rI^41NV# zVu2QvUA?I7N}FU!S1q*8PtPZYay0RIZ=!X2suSdPZo*mL({`c(vL(@U$&0r`nIosN zEb!P0(XE_)uZJHlK&5cpb2lsyj75+tgdx@Q?fxmf->0W}$6F-KNOsaeMitn44wc>I3=> z!wTh!i@Ph!89F_aexgK%KaE_r@#8|>{i}EoVwfcQPt#NQ1Kc;miqzZHz8Cr)8}{T2 z29;x#ha93a18p@;PIT!%`#g4Aa;Dq^6O=2EUzxrR4fqG^V`2i8g$E8hj}5$jU)GSL zIGE=9C?*0{&p93Fc}??`%0%z_f$*gKD*(mzMV>8J z2^d&+1gLw5^;TqPR&=tXR0wF-U2@j+`7P)@+CAkl){fSB-KqBL?K5sg`uEYN)D(Oz z0Q!MF+ialATQL3ymmayN39$CV#`NR@7RhUfGK?xF7rj5<=xWusI^TAn78rc_#PajT zn)r|8s@GoB|G9T$W+=?+=lNDG~aE#jjsqL%&yXLB(#Y3*~3nQ>D z#i4VHSBR9_8QRfF8G&m`$zgsGp{f;M!oC*F%l~`ts1n=XAGSPrxS3C}nuzrXPG0+x z(si>|q3!`ZsW@h==EElK+}bT>z!{Q%fD530<8y=7g(6aqR?ozVLB81m(=UFFD{R9M z=a`P{b=j^LR2vbnW?@$CI%CgDNx*K0aB-uY%GLkwx&FkcX33gmPOBZ`j|U6?5oU)u z4e%c*OES#h;-_Y)XQ!4q-duk#R(G69Dlco+9KMiGU6dt?sxg0y>6_Qo-l?os`QBIT z-_{WCw%_O=v9-3v=5+W$occHqcP`{S>#HbUwnV!Q@AD}e_QE~x; zOTu@*dZ@cjRZkTkT?ZRaRtnt#?Kqbd(_iXutTq|tKGt#RLtzolo>=CWy;4hp4198! zEgmR$d$r5sYG%(TSY4s(>Ry^aqjWdB#3rpoJ- - + - + @@ -73,7 +73,7 @@ - + @@ -81,7 +81,7 @@ - + - + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - - - - - - - - - - + + + + + + + + + - - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + - - - - - - - - - - + + + + + + + + + - - - + + - - - - - - - - - - - - - - - - - - + + @@ -444,7 +490,7 @@ DQ - + @@ -455,7 +501,7 @@ DQ - + @@ -542,7 +588,7 @@ DQ - + @@ -586,7 +632,7 @@ DQ - + @@ -624,7 +670,7 @@ DQ - + @@ -142,7 +142,7 @@ DQ - + @@ -162,7 +162,7 @@ DQ - - + + + + + + + + + + - - - - - - - - - - + + - - + + - - - - - - - - - - - - - - - - - - - - + + - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + - - - - - - - - - + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + - - - - - - - - - + + + + + + + + + + + + + - - + + - - - - - - - - - - + + + + + + + + + + + - + - + + + - - - + @@ -720,9 +708,8 @@ DQ - - + From 8a5083f6108288b51fb05a3ef30b013f8a9ffd77 Mon Sep 17 00:00:00 2001 From: thompsonate Date: Sun, 14 Nov 2021 20:56:53 -0500 Subject: [PATCH 6/9] add disabling when app is running --- Shifty/MainMenu.xib | 31 ++-- Shifty/PrefManager.swift | 35 +---- Shifty/PrefShortcutsViewController.swift | 4 +- Shifty/RuleManager.swift | 187 ++++++++++++++++------- Shifty/StatusMenuController.swift | 45 ++++-- 5 files changed, 187 insertions(+), 115 deletions(-) diff --git a/Shifty/MainMenu.xib b/Shifty/MainMenu.xib index 33add29..058bd15 100644 --- a/Shifty/MainMenu.xib +++ b/Shifty/MainMenu.xib @@ -1,8 +1,8 @@ - + - + @@ -22,10 +22,11 @@ - + + @@ -55,10 +56,10 @@ - + - + + + + + + + @@ -115,11 +122,11 @@ - + - + - + @@ -140,14 +147,14 @@ - + - + @@ -185,7 +192,7 @@ - + diff --git a/Shifty/PrefManager.swift b/Shifty/PrefManager.swift index 55f4ac1..2368e30 100644 --- a/Shifty/PrefManager.swift +++ b/Shifty/PrefManager.swift @@ -16,7 +16,8 @@ enum Keys { static let isWebsiteControlEnabled = "isWebsiteControlEnabled" static let trueToneControl = "trueToneControl" static let analyticsPermission = "fabricCrashlyticsPermission" - static let disabledApps = "disabledApps" + static let currentAppDisableRules = "disabledApps" + static let runningAppDisableRules = "disabledRunningApps" static let browserRules = "browserRules" static let toggleNightShiftShortcut = "toggleNightShiftShortcut" @@ -55,40 +56,12 @@ class PrefManager { Keys.isWebsiteControlEnabled: NSNumber(value: false), Keys.trueToneControl: NSNumber(value: false), Keys.analyticsPermission: NSNumber(value: false), - Keys.disabledApps: NSData(), + Keys.currentAppDisableRules: NSData(), + Keys.runningAppDisableRules: NSData(), Keys.browserRules: NSData(), Keys.hasSetupWindowShown: NSNumber(value: false) ] as [String : Any] userDefaults.register(defaults: factoryDefaults) } - - func synchronize() { - userDefaults.synchronize() - } - - func reset() { - userDefaults.removeObject(forKey: Keys.isAutoLaunchEnabled) - userDefaults.removeObject(forKey: Keys.isStatusToggleEnabled) - userDefaults.removeObject(forKey: Keys.isIconSwitchingEnabled) - userDefaults.removeObject(forKey: Keys.isDarkModeSyncEnabled) - userDefaults.removeObject(forKey: Keys.isWebsiteControlEnabled) - userDefaults.removeObject(forKey: Keys.trueToneControl) - userDefaults.removeObject(forKey: Keys.disabledApps) - userDefaults.removeObject(forKey: Keys.browserRules) - userDefaults.removeObject(forKey: Keys.toggleNightShiftShortcut) - userDefaults.removeObject(forKey: Keys.incrementColorTempShortcut) - userDefaults.removeObject(forKey: Keys.decrementColorTempShortcut) - userDefaults.removeObject(forKey: Keys.disableAppShortcut) - userDefaults.removeObject(forKey: Keys.disableDomainShortcut) - userDefaults.removeObject(forKey: Keys.disableSubdomainShortcut) - userDefaults.removeObject(forKey: Keys.disableHourShortcut) - userDefaults.removeObject(forKey: Keys.disableCustomShortcut) - userDefaults.removeObject(forKey: Keys.toggleTrueToneShortcut) - userDefaults.removeObject(forKey: Keys.toggleDarkModeShortcut) - userDefaults.removeObject(forKey: Keys.analyticsPermission) - userDefaults.removeObject(forKey: Keys.hasSetupWindowShown) - - synchronize() - } } diff --git a/Shifty/PrefShortcutsViewController.swift b/Shifty/PrefShortcutsViewController.swift index 35b6340..93389ce 100644 --- a/Shifty/PrefShortcutsViewController.swift +++ b/Shifty/PrefShortcutsViewController.swift @@ -128,8 +128,8 @@ class PrefShortcutsViewController: NSViewController, MASPreferencesViewControlle MASShortcutBinder.shared().bindShortcut(withDefaultsKey: Keys.disableAppShortcut) { guard let menu = self.statusMenuController else { return } - if !menu.disableAppMenuItem.isHidden && menu.disableAppMenuItem.isEnabled { - self.statusMenuController?.disableForApp(self) + if !menu.disableCurrentAppMenuItem.isHidden && menu.disableCurrentAppMenuItem.isEnabled { + self.statusMenuController?.disableForCurrentApp(self) } else { NSSound.beep() } diff --git a/Shifty/RuleManager.swift b/Shifty/RuleManager.swift index d9da9e1..cbb2611 100644 --- a/Shifty/RuleManager.swift +++ b/Shifty/RuleManager.swift @@ -13,13 +13,67 @@ import ScriptingBridge class RuleManager { static var shared = RuleManager() - var disabledApps = Set() { - didSet { - UserDefaults.standard.set(try? PropertyListEncoder().encode(disabledApps), forKey: Keys.disabledApps) + init() { + if let appData = UserDefaults.standard.value(forKey: Keys.currentAppDisableRules) as? Data { + do { + currentAppDisableRules = try PropertyListDecoder().decode(Set.self, from: appData) + } catch { + logw("Error: \(error.localizedDescription)") + } + } + + if let appData = UserDefaults.standard.value(forKey: Keys.runningAppDisableRules) as? Data { + do { + runningAppDisableRules = try PropertyListDecoder().decode(Set.self, from: appData) + } catch let error { + logw("Error: \(error.localizedDescription)") + } + } + + if let browserData = UserDefaults.standard.value(forKey: Keys.browserRules) as? Data { + do { + browserRules = try PropertyListDecoder().decode(Set.self, from: browserData) + } catch let error { + logw("Error: \(error.localizedDescription)") + } + } + + NSWorkspace.shared.notificationCenter.addObserver( + forName: NSWorkspace.didActivateApplicationNotification, + object: nil, + queue: nil) + { notification in + self.appSwitched(notification: notification) + } + + NSWorkspace.shared.notificationCenter.addObserver( + forName: NSWorkspace.didLaunchApplicationNotification, + object: nil, + queue: nil) + { notification in + self.appSwitched(notification: notification) + } + + NSWorkspace.shared.notificationCenter.addObserver( + forName: NSWorkspace.didTerminateApplicationNotification, + object: nil, + queue: nil) + { notification in + self.appSwitched(notification: notification) } } + private var currentAppDisableRules = Set() { + didSet { + UserDefaults.standard.set(try? PropertyListEncoder().encode(currentAppDisableRules), forKey: Keys.currentAppDisableRules) + } + } + private var runningAppDisableRules = Set() { + didSet { + UserDefaults.standard.set(try? PropertyListEncoder().encode(runningAppDisableRules), forKey: Keys.runningAppDisableRules) + } + } var browserRules = Set() { didSet(newValue) { @@ -27,41 +81,77 @@ class RuleManager { } } - - var currentApp: NSRunningApplication? { - return NSWorkspace.shared.menuBarOwningApplication + NSWorkspace.shared.menuBarOwningApplication } + var runningApps: [NSRunningApplication] { + NSWorkspace.shared.runningApplications + } - var disabledForApp: Bool { - get { - guard let bundleIdentifier = currentApp?.bundleIdentifier else { - logw("Could not obtain bundle identifier of current application") - return false - } - return disabledApps.filter { - $0.bundleIdentifier == bundleIdentifier }.count > 0 + var isDisabledForCurrentApp: Bool { + guard let bundleIdentifier = currentApp?.bundleIdentifier else { + logw("Could not obtain bundle identifier of current application") + return false } - set(newValue) { - guard let bundleIdentifier = currentApp?.bundleIdentifier else { - logw("Could not obtain bundle identifier of current application") - return - } - let rule = AppRule(bundleIdentifier: bundleIdentifier, fullScreenOnly: false) - if newValue { - disabledApps.insert(rule) - NightShiftManager.shared.respond(to: .nightShiftDisableRuleActivated) - } else { - guard let index = disabledApps.firstIndex(of: rule) else { return } - disabledApps.remove(at: index) - NightShiftManager.shared.respond(to: .nightShiftDisableRuleDeactivated) - } + return currentAppDisableRules.filter { + $0.bundleIdentifier == bundleIdentifier }.count > 0 + } + + func addCurrentAppDisableRule(forApp app: NSRunningApplication) { + guard let bundleID = app.bundleIdentifier else { return } + let rule = AppRule(bundleIdentifier: bundleID, fullScreenOnly: false) + currentAppDisableRules.insert(rule) + NightShiftManager.shared.respond(to: .nightShiftDisableRuleActivated) + } + + func removeCurrentAppDisableRule(forApp app: NSRunningApplication) { + guard let bundleID = app.bundleIdentifier, + let index = currentAppDisableRules.firstIndex(where: { + $0.bundleIdentifier == bundleID + }) else { return } + + currentAppDisableRules.remove(at: index) + NightShiftManager.shared.respond(to: .nightShiftDisableRuleDeactivated) + } + + + var isDisabledForRunningApp: Bool { + disabledCurrentlyRunningApps.count > 0 + } + + /// The currently running apps that Night Shift is disabled for + var disabledCurrentlyRunningApps: [NSRunningApplication] { + let disabledBundleIDs = Set(runningAppDisableRules.map { $0.bundleIdentifier }) + return runningApps.filter { + guard let bundleID = $0.bundleIdentifier else { return false } + return disabledBundleIDs.contains(bundleID) } } + func isDisabledWhenRunningApp(_ app: NSRunningApplication) -> Bool { + guard let bundleID = app.bundleIdentifier else { return false } + return runningAppDisableRules.contains(where: { $0.bundleIdentifier == bundleID }) + } + + func addRunningAppDisableRule(forApp app: NSRunningApplication) { + guard let bundleID = app.bundleIdentifier else { return } + let rule = AppRule(bundleIdentifier: bundleID, fullScreenOnly: false) + + runningAppDisableRules.insert(rule) + NightShiftManager.shared.respond(to: .nightShiftDisableRuleActivated) + } + func removeRunningAppDisableRule(forApp app: NSRunningApplication) { + guard let bundleID = app.bundleIdentifier, + let index = runningAppDisableRules.firstIndex(where: { + $0.bundleIdentifier == bundleID + }) else { return } + + runningAppDisableRules.remove(at: index) + NightShiftManager.shared.respond(to: .nightShiftDisableRuleDeactivated) + } var disabledForDomain: Bool { get { @@ -156,48 +246,28 @@ class RuleManager { var disableRuleIsActive: Bool { - return disabledForApp || (disabledForDomain && ruleForSubdomain != .enabled) || ruleForSubdomain == .disabled + return isDisabledForCurrentApp || isDisabledForRunningApp || + (disabledForDomain && ruleForSubdomain != .enabled) || + ruleForSubdomain == .disabled } func removeRulesForCurrentState() { - disabledForApp = false - disabledForDomain = false - ruleForSubdomain = .none - } - - - - init() { - NSWorkspace.shared.notificationCenter.addObserver(forName: NSWorkspace.didActivateApplicationNotification, - object: nil, - queue: nil) { - self.appSwitched(notification: $0) - } - - if let appData = UserDefaults.standard.value(forKey: Keys.disabledApps) as? Data { - do { - disabledApps = try PropertyListDecoder().decode(Set.self, from: appData) - } catch let error { - logw("Error: \(error.localizedDescription)") - } - } - - if let browserData = UserDefaults.standard.value(forKey: Keys.browserRules) as? Data { - do { - browserRules = try PropertyListDecoder().decode(Set.self, from: browserData) - } catch let error { - logw("Error: \(error.localizedDescription)") + if let currentApp = currentApp { + removeCurrentAppDisableRule(forApp: currentApp) + for app in disabledCurrentlyRunningApps { + removeRunningAppDisableRule(forApp: app) } } + disabledForDomain = false + ruleForSubdomain = .none } - private func appSwitched(notification: Notification) { BrowserManager.shared.stopBrowserWatcher() - if disabledForApp { + if isDisabledForCurrentApp || isDisabledForRunningApp { NightShiftManager.shared.respond(to: .nightShiftDisableRuleActivated) } else if BrowserManager.shared.currentAppIsSupportedBrowser { BrowserManager.shared.updateForSupportedBrowser() @@ -229,6 +299,7 @@ enum SubdomainRuleType: String, Codable { struct AppRule: CustomStringConvertible, Hashable, Codable { var bundleIdentifier: BundleIdentifier + // Currently unused var fullScreenOnly: Bool var description: String { diff --git a/Shifty/StatusMenuController.swift b/Shifty/StatusMenuController.swift index c2a53a2..7762979 100644 --- a/Shifty/StatusMenuController.swift +++ b/Shifty/StatusMenuController.swift @@ -18,7 +18,8 @@ class StatusMenuController: NSObject, NSMenuDelegate { @IBOutlet weak var trueToneMenuItem: NSMenuItem! @IBOutlet weak var sliderMenuItem: NSMenuItem! @IBOutlet weak var descriptionMenuItem: NSMenuItem! - @IBOutlet weak var disableAppMenuItem: NSMenuItem! + @IBOutlet weak var disableCurrentAppMenuItem: NSMenuItem! + @IBOutlet weak var disableRunningAppMenuItem: NSMenuItem! @IBOutlet weak var disableDomainMenuItem: NSMenuItem! @IBOutlet weak var disableSubdomainMenuItem: NSMenuItem! @IBOutlet weak var enableBrowserAutomationMenuItem: NSMenuItem! @@ -102,7 +103,7 @@ class StatusMenuController: NSObject, NSMenuDelegate { setDescriptionText() assignKeyboardShortcutToMenuItem(powerMenuItem, userDefaultsKey: Keys.toggleNightShiftShortcut) - assignKeyboardShortcutToMenuItem(disableAppMenuItem, userDefaultsKey: Keys.disableAppShortcut) + assignKeyboardShortcutToMenuItem(disableCurrentAppMenuItem, userDefaultsKey: Keys.disableAppShortcut) assignKeyboardShortcutToMenuItem(disableDomainMenuItem, userDefaultsKey: Keys.disableDomainShortcut) assignKeyboardShortcutToMenuItem(disableSubdomainMenuItem, userDefaultsKey: Keys.disableSubdomainShortcut) assignKeyboardShortcutToMenuItem(disableHourMenuItem, userDefaultsKey: Keys.disableHourShortcut) @@ -155,12 +156,22 @@ class StatusMenuController: NSObject, NSMenuDelegate { //MARK: disable for app - if RuleManager.shared.disabledForApp { - disableAppMenuItem.state = .on - disableAppMenuItem.title = String(format: NSLocalizedString("menu.disabled_for", comment: "Disabled for %@"), currentAppName) + if RuleManager.shared.isDisabledForCurrentApp { + disableCurrentAppMenuItem.state = .on + disableCurrentAppMenuItem.title = String(format: NSLocalizedString("menu.disabled_for", comment: "Disabled for %@"), currentAppName) } else { - disableAppMenuItem.state = .off - disableAppMenuItem.title = String(format: NSLocalizedString("menu.disable_for", comment: "Disable for %@"), currentAppName) + disableCurrentAppMenuItem.state = .off + disableCurrentAppMenuItem.title = String(format: NSLocalizedString("menu.disable_for", comment: "Disable for %@"), currentAppName) + } + + if let currentApp = RuleManager.shared.currentApp, + RuleManager.shared.isDisabledWhenRunningApp(currentApp) + { + disableRunningAppMenuItem.state = .on + disableRunningAppMenuItem.title = "Disabled when \(currentAppName) is running" + } else { + disableRunningAppMenuItem.state = .off + disableRunningAppMenuItem.title = "Disable when \(currentAppName) is running" } @@ -382,16 +393,26 @@ class StatusMenuController: NSObject, NSMenuDelegate { - @IBAction func disableForApp(_ sender: Any) { - if RuleManager.shared.disabledForApp { - RuleManager.shared.disabledForApp = false + @IBAction func disableForCurrentApp(_ sender: Any) { + guard let currentApp = RuleManager.shared.currentApp else { return } + + if RuleManager.shared.isDisabledForCurrentApp { + RuleManager.shared.removeCurrentAppDisableRule(forApp: currentApp) } else { - RuleManager.shared.disabledForApp = true + RuleManager.shared.addCurrentAppDisableRule(forApp: currentApp) } Event.disableForCurrentApp(state: (sender as? NSMenuItem)?.state == .on).record() } - + @IBAction func disableForRunningApp(_ sender: Any) { + guard let currentApp = RuleManager.shared.currentApp else { return } + + if RuleManager.shared.isDisabledForRunningApp { + RuleManager.shared.removeRunningAppDisableRule(forApp: currentApp) + } else { + RuleManager.shared.addRunningAppDisableRule(forApp: currentApp) + } + } @IBAction func disableForDomain(_ sender: Any) { if RuleManager.shared.disabledForDomain { From d8b521300dcedd314c3790826ba0931baf9d2a61 Mon Sep 17 00:00:00 2001 From: thompsonate Date: Sun, 14 Nov 2021 21:30:04 -0500 Subject: [PATCH 7/9] refactor domain rules API in RuleManager --- Shifty/BrowserManager.swift | 4 +- Shifty/RuleManager.swift | 165 +++++++++++++++--------------- Shifty/StatusMenuController.swift | 32 +++--- 3 files changed, 105 insertions(+), 96 deletions(-) diff --git a/Shifty/BrowserManager.swift b/Shifty/BrowserManager.swift index 564b34e..2202272 100644 --- a/Shifty/BrowserManager.swift +++ b/Shifty/BrowserManager.swift @@ -138,9 +138,9 @@ class BrowserManager { } private func fireNightShiftEvent() { - if RuleManager.shared.ruleForSubdomain == .enabled { + if RuleManager.shared.ruleForCurrentSubdomain == .enabled { NightShiftManager.shared.respond(to: .nightShiftEnableRuleActivated) - } else if RuleManager.shared.disabledForDomain || RuleManager.shared.ruleForSubdomain == .disabled { + } else if RuleManager.shared.isDisabledForDomain || RuleManager.shared.ruleForCurrentSubdomain == .disabled { NightShiftManager.shared.respond(to: .nightShiftDisableRuleActivated) } else { NightShiftManager.shared.respond(to: .nightShiftDisableRuleDeactivated) diff --git a/Shifty/RuleManager.swift b/Shifty/RuleManager.swift index cbb2611..944d68d 100644 --- a/Shifty/RuleManager.swift +++ b/Shifty/RuleManager.swift @@ -153,106 +153,107 @@ class RuleManager { NightShiftManager.shared.respond(to: .nightShiftDisableRuleDeactivated) } - var disabledForDomain: Bool { - get { - guard let currentDomain = BrowserManager.shared.currentDomain else { return false } - let disabledDomain = browserRules.filter { - $0.type == .domain && $0.host == currentDomain }.count > 0 - return disabledDomain + + var isDisabledForDomain: Bool { + guard let currentDomain = BrowserManager.shared.currentDomain else { return false } + let disabledDomain = browserRules.filter { + $0.type == .domain && $0.host == currentDomain }.count > 0 + return disabledDomain + } + + func addDomainDisableRule(forDomain domain: String) { + let rule = BrowserRule(type: .domain, host: domain) + browserRules.insert(rule) + + NightShiftManager.shared.respond(to: .nightShiftDisableRuleActivated) + } + + func removeDomainDisableRule(forDomain domain: String) { + let rule = BrowserRule(type: .domain, host: domain) + guard let index = browserRules.firstIndex(of: rule) else { return } + browserRules.remove(at: index) + + if let currentSubdomain = BrowserManager.shared.currentSubdomain, + getSubdomainRule(forSubdomain: currentSubdomain) == .enabled + { + setSubdomainRule(.none, forSubdomain: currentSubdomain) } - set(newValue) { - guard let currentDomain = BrowserManager.shared.currentDomain else { return } - let rule = BrowserRule(type: .domain, host: currentDomain) - if newValue { - browserRules.insert(rule) - NightShiftManager.shared.respond(to: .nightShiftDisableRuleActivated) - } else { - guard let index = browserRules.firstIndex(of: rule) else { return } - - if ruleForSubdomain == .enabled { - ruleForSubdomain = .none - } - browserRules.remove(at: index) - NightShiftManager.shared.respond(to: .nightShiftDisableRuleDeactivated) + + NightShiftManager.shared.respond(to: .nightShiftDisableRuleDeactivated) + } + + + func getSubdomainRule(forSubdomain subdomain: String) -> SubdomainRuleType { + if isDisabledForDomain { + let isEnabled = (browserRules.filter { + $0.type == .subdomainEnabled + && $0.host == subdomain + }.count > 0) + if isEnabled { + return .enabled + } + } else { + let isDisabled = (browserRules.filter { + $0.type == .subdomainDisabled + && $0.host == subdomain + }.count > 0) + if isDisabled { + return .disabled } } + return .none } + var ruleForCurrentSubdomain: SubdomainRuleType { + guard let currentSubdomain = BrowserManager.shared.currentSubdomain else { return .none } + return getSubdomainRule(forSubdomain: currentSubdomain) + } - - var ruleForSubdomain: SubdomainRuleType { - get { - guard let currentSubdomain = BrowserManager.shared.currentSubdomain else { return .none } + func setSubdomainRule(_ ruleType: SubdomainRuleType, forSubdomain subdomain: String) { + switch ruleType { + case .disabled: + let rule = BrowserRule(type: .subdomainDisabled, host: subdomain) + browserRules.insert(rule) + NightShiftManager.shared.respond(to: .nightShiftDisableRuleActivated) + case .enabled: + let rule = BrowserRule(type: .subdomainEnabled, host: subdomain) + browserRules.insert(rule) + NightShiftManager.shared.respond(to: .nightShiftEnableRuleActivated) + case .none: + var rule: BrowserRule + let prevValue = getSubdomainRule(forSubdomain: subdomain) - if disabledForDomain { - let isEnabled = (browserRules.filter { - $0.type == .subdomainEnabled - && $0.host == currentSubdomain - }.count > 0) - if isEnabled { - return .enabled - } - } else { - let isDisabled = (browserRules.filter { - $0.type == .subdomainDisabled - && $0.host == currentSubdomain - }.count > 0) - if isDisabled { - return .disabled - } + //Remove rule from set before triggering NightShiftEvent + switch prevValue { + case .disabled: + rule = BrowserRule(type: .subdomainDisabled, host: subdomain) + case .enabled: + rule = BrowserRule(type: .subdomainEnabled, host: subdomain) + case .none: + return } - return .none - } - set(newValue) { - guard let currentSubdomain = BrowserManager.shared.currentSubdomain else { return } + guard let index = browserRules.firstIndex(of: rule) else { return } + browserRules.remove(at: index) - switch newValue { + switch prevValue { case .disabled: - let rule = BrowserRule(type: .subdomainDisabled, host: currentSubdomain) - browserRules.insert(rule) - NightShiftManager.shared.respond(to: .nightShiftDisableRuleActivated) + NightShiftManager.shared.respond(to: .nightShiftDisableRuleDeactivated) case .enabled: - let rule = BrowserRule(type: .subdomainEnabled, host: currentSubdomain) - browserRules.insert(rule) - NightShiftManager.shared.respond(to: .nightShiftEnableRuleActivated) + NightShiftManager.shared.respond(to: .nightShiftEnableRuleDeactivated) case .none: - var rule: BrowserRule - let prevValue = ruleForSubdomain - - //Remove rule from set before triggering NightShiftEvent - switch prevValue { - case .disabled: - rule = BrowserRule(type: .subdomainDisabled, host: currentSubdomain) - case .enabled: - rule = BrowserRule(type: .subdomainEnabled, host: currentSubdomain) - case .none: - return - } - guard let index = browserRules.firstIndex(of: rule) else { return } - browserRules.remove(at: index) - - switch prevValue { - case .disabled: - NightShiftManager.shared.respond(to: .nightShiftDisableRuleDeactivated) - case .enabled: - NightShiftManager.shared.respond(to: .nightShiftEnableRuleDeactivated) - case .none: - break - } + break } } } - var disableRuleIsActive: Bool { return isDisabledForCurrentApp || isDisabledForRunningApp || - (disabledForDomain && ruleForSubdomain != .enabled) || - ruleForSubdomain == .disabled + (isDisabledForDomain && ruleForCurrentSubdomain != .enabled) || + ruleForCurrentSubdomain == .disabled } - func removeRulesForCurrentState() { if let currentApp = currentApp { removeCurrentAppDisableRule(forApp: currentApp) @@ -260,8 +261,12 @@ class RuleManager { removeRunningAppDisableRule(forApp: app) } } - disabledForDomain = false - ruleForSubdomain = .none + if let currentDomain = BrowserManager.shared.currentDomain { + removeDomainDisableRule(forDomain: currentDomain) + } + if let currentSubdomain = BrowserManager.shared.currentSubdomain { + setSubdomainRule(.none, forSubdomain: currentSubdomain) + } } diff --git a/Shifty/StatusMenuController.swift b/Shifty/StatusMenuController.swift index 7762979..29be521 100644 --- a/Shifty/StatusMenuController.swift +++ b/Shifty/StatusMenuController.swift @@ -178,7 +178,7 @@ class StatusMenuController: NSObject, NSMenuDelegate { // MARK: disable for domain if BrowserManager.shared.hasValidDomain { disableDomainMenuItem.isHidden = false - if RuleManager.shared.disabledForDomain { + if RuleManager.shared.isDisabledForDomain { disableDomainMenuItem.state = .on disableDomainMenuItem.title = String(format: NSLocalizedString("menu.disabled_for", comment: "Disabled for %@"), currentDomain ?? "") } else { @@ -193,13 +193,13 @@ class StatusMenuController: NSObject, NSMenuDelegate { // MARK: disable for subdomain if BrowserManager.shared.hasValidSubdomain { disableSubdomainMenuItem.isHidden = false - if RuleManager.shared.ruleForSubdomain == .enabled { + if RuleManager.shared.ruleForCurrentSubdomain == .enabled { disableSubdomainMenuItem.state = .on disableSubdomainMenuItem.title = String(format: NSLocalizedString("menu.enabled_for", comment: "Enabled for %@"), currentSubdomain ?? "") - } else if RuleManager.shared.ruleForSubdomain == .disabled { + } else if RuleManager.shared.ruleForCurrentSubdomain == .disabled { disableSubdomainMenuItem.state = .on disableSubdomainMenuItem.title = String(format: NSLocalizedString("menu.disabled_for", comment: "Disabled for %@"), currentSubdomain ?? "") - } else if RuleManager.shared.disabledForDomain { + } else if RuleManager.shared.isDisabledForDomain { disableSubdomainMenuItem.state = .off disableSubdomainMenuItem.title = String(format: NSLocalizedString("menu.enable_for", comment: "Enable for %@"), currentSubdomain ?? "") } else { @@ -268,9 +268,9 @@ class StatusMenuController: NSObject, NSMenuDelegate { case .disabled: if NightShiftManager.shared.isDisableRuleActive { trueToneMenuItem.isEnabled = false - if RuleManager.shared.disabledForDomain { + if RuleManager.shared.isDisabledForDomain { trueToneMenuItem.title = String(format: NSLocalizedString("menu.true_tone_disabled_for", comment: "True Tone is disabled for %@"), currentDomain ?? "") - } else if RuleManager.shared.ruleForSubdomain == .disabled { + } else if RuleManager.shared.ruleForCurrentSubdomain == .disabled { trueToneMenuItem.title = String(format: NSLocalizedString("menu.true_tone_disabled_for", comment: "True Tone is disabled for %@"), currentSubdomain ?? "") } else { trueToneMenuItem.title = String(format: NSLocalizedString("menu.true_tone_disabled_for", comment: "True Tone is disabled for %@"), currentAppName) @@ -415,24 +415,28 @@ class StatusMenuController: NSObject, NSMenuDelegate { } @IBAction func disableForDomain(_ sender: Any) { - if RuleManager.shared.disabledForDomain { - RuleManager.shared.disabledForDomain = false + guard let currentDomain = BrowserManager.shared.currentDomain else { return } + + if RuleManager.shared.isDisabledForDomain { + RuleManager.shared.removeDomainDisableRule(forDomain: currentDomain) } else { - RuleManager.shared.disabledForDomain = true + RuleManager.shared.addDomainDisableRule(forDomain: currentDomain) } } @IBAction func disableForSubdomain(_ sender: Any) { - if RuleManager.shared.ruleForSubdomain == .none { - if RuleManager.shared.disabledForDomain { - RuleManager.shared.ruleForSubdomain = .enabled + guard let currentSubdomain = BrowserManager.shared.currentSubdomain else { return } + + if RuleManager.shared.ruleForCurrentSubdomain == .none { + if RuleManager.shared.isDisabledForDomain { + RuleManager.shared.setSubdomainRule(.enabled, forSubdomain: currentSubdomain) } else { - RuleManager.shared.ruleForSubdomain = .disabled + RuleManager.shared.setSubdomainRule(.disabled, forSubdomain: currentSubdomain) } } else { - RuleManager.shared.ruleForSubdomain = .none + RuleManager.shared.setSubdomainRule(.none, forSubdomain: currentSubdomain) } } From 6f9a2c47316e478a4bcb64dbfa58f9a22c2e9ac1 Mon Sep 17 00:00:00 2001 From: thompsonate Date: Sun, 14 Nov 2021 21:45:48 -0500 Subject: [PATCH 8/9] used NSLocalizedString for new strings --- Shifty/StatusMenuController.swift | 8 ++++++-- Shifty/en.lproj/Localizable.strings | 2 ++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Shifty/StatusMenuController.swift b/Shifty/StatusMenuController.swift index 29be521..414bd5a 100644 --- a/Shifty/StatusMenuController.swift +++ b/Shifty/StatusMenuController.swift @@ -168,10 +168,14 @@ class StatusMenuController: NSObject, NSMenuDelegate { RuleManager.shared.isDisabledWhenRunningApp(currentApp) { disableRunningAppMenuItem.state = .on - disableRunningAppMenuItem.title = "Disabled when \(currentAppName) is running" + disableRunningAppMenuItem.title = String(format: NSLocalizedString( + "menu.disabled_for_running_app", + comment: "Disabled when %@ is running"), currentAppName) } else { disableRunningAppMenuItem.state = .off - disableRunningAppMenuItem.title = "Disable when \(currentAppName) is running" + disableRunningAppMenuItem.title = String(format: NSLocalizedString( + "menu.disable_for_running_app", + comment: "Disable when %@ is running"), currentAppName) } diff --git a/Shifty/en.lproj/Localizable.strings b/Shifty/en.lproj/Localizable.strings index e8ae230..3b256fe 100644 --- a/Shifty/en.lproj/Localizable.strings +++ b/Shifty/en.lproj/Localizable.strings @@ -13,6 +13,8 @@ "menu.toggle_off" = "Turn off Night Shift"; "menu.disable_for" = "Disable for %@"; "menu.disabled_for" = "Disabled for %@"; +"menu.disable_for_running_app" = "Disable when %@ is running"; +"menu.disabled_for_running_app" = "Disabled when %@ is running"; "menu.enable_for" = "Enable for %@"; "menu.enabled_for" = "Enabled for %@"; From 46e07f54838ce083fc000702343c2b4853f6175a Mon Sep 17 00:00:00 2001 From: thompsonate Date: Sun, 14 Nov 2021 22:10:45 -0500 Subject: [PATCH 9/9] right click on status item to toggle when Quick Toggle is disabled --- Shifty/AppDelegate.swift | 47 +++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/Shifty/AppDelegate.swift b/Shifty/AppDelegate.swift index 75cb2ef..9bb7b0f 100644 --- a/Shifty/AppDelegate.swift +++ b/Shifty/AppDelegate.swift @@ -216,29 +216,40 @@ class AppDelegate: NSObject, NSApplicationDelegate { } func setStatusToggle() { - if prefs.bool(forKey: Keys.isStatusToggleEnabled) { - statusItem.menu = nil - if let button = statusItem.button { - button.action = #selector(statusBarButtonClicked) - button.sendAction(on: [.leftMouseUp, .leftMouseDown, .rightMouseUp, .rightMouseDown]) - } - } else { - statusItem.menu = statusMenu + statusItem.menu = nil + if let button = statusItem.button { + button.action = #selector(statusBarButtonClicked) + button.sendAction(on: [.leftMouseUp, .leftMouseDown, .rightMouseUp, .rightMouseDown]) } } @objc func statusBarButtonClicked(sender: NSStatusBarButton) { - let event = NSApp.currentEvent! + guard let event = NSApp.currentEvent else { return } - if event.type == .rightMouseDown - || event.type == .rightMouseUp - || event.modifierFlags.contains(.control) - { - statusItem.menu = statusMenu - statusItem.button?.performClick(self) - statusItem.menu = nil - } else if event.type == .leftMouseUp { - statusItemClicked?() + if UserDefaults.standard.bool(forKey: Keys.isStatusToggleEnabled) { + if event.type == .rightMouseDown + || event.type == .rightMouseUp + || event.modifierFlags.contains(.control) + { + statusItem.menu = statusMenu + statusItem.button?.performClick(sender) + statusItem.menu = nil + } else if event.type == .leftMouseUp { + statusItemClicked?() + } + } else { + if event.type == .rightMouseUp + || (event.type == .leftMouseUp + && event.modifierFlags.contains(.control)) + { + statusItemClicked?() + } else if event.type == .leftMouseDown + && !event.modifierFlags.contains(.control) + { + statusItem.menu = statusMenu + statusItem.button?.performClick(sender) + statusItem.menu = nil + } } }