From aae7baf22487684475efbefa38b330b38c71abc3 Mon Sep 17 00:00:00 2001 From: acheronfail Date: Wed, 23 Jan 2019 22:14:23 +1100 Subject: [PATCH] feat: icon is tinted red while listening for a key sequence fix: when Apptivator is disabled all sequences are unregistered --- Apptivator.xcodeproj/project.pbxproj | 4 +++ Apptivator/APState.swift | 29 ++++++++++++++++----- Apptivator/AppDelegate.swift | 7 ++++- Apptivator/Extensions.swift | 19 ++++++++++++++ Apptivator/UI/APPopoverViewController.swift | 2 +- 5 files changed, 52 insertions(+), 9 deletions(-) create mode 100644 Apptivator/Extensions.swift diff --git a/Apptivator.xcodeproj/project.pbxproj b/Apptivator.xcodeproj/project.pbxproj index d8e9e67..ee51162 100644 --- a/Apptivator.xcodeproj/project.pbxproj +++ b/Apptivator.xcodeproj/project.pbxproj @@ -33,6 +33,7 @@ 15FC8CC8204E2E2C000B5E1E /* APState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15FC8CC7204E2E2C000B5E1E /* APState.swift */; }; B3B4C2C11E25894B009F8E4E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3B4C2C01E25894B009F8E4E /* AppDelegate.swift */; }; B3B4C2C31E25894B009F8E4E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B3B4C2C21E25894B009F8E4E /* Assets.xcassets */; }; + BA54C46621F87F480084BB36 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA54C46521F87F480084BB36 /* Extensions.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -91,6 +92,7 @@ B3B4C2C21E25894B009F8E4E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; B3B4C2C51E25894B009F8E4E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/APPopoverViewController.xib; sourceTree = ""; }; B3B4C2C71E25894B009F8E4E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + BA54C46521F87F480084BB36 /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -188,6 +190,7 @@ B3B4C2C01E25894B009F8E4E /* AppDelegate.swift */, 1537F5E0204C8D860056EE50 /* APAppEntry.swift */, 15FC8CC7204E2E2C000B5E1E /* APState.swift */, + BA54C46521F87F480084BB36 /* Extensions.swift */, 15492102207B53AE00E45BBC /* Util.swift */, 15673D6F20B6140E00542276 /* README.md */, 155D2C02204F61220087478B /* Credits.rtfd */, @@ -336,6 +339,7 @@ 15F39F3C204CB72D00847CD5 /* APPopoverViewController.swift in Sources */, 15492103207B53AE00E45BBC /* Util.swift in Sources */, B3B4C2C11E25894B009F8E4E /* AppDelegate.swift in Sources */, + BA54C46621F87F480084BB36 /* Extensions.swift in Sources */, 1561AED2207615AC00C69338 /* UIOverrides.swift in Sources */, 15FC8CC8204E2E2C000B5E1E /* APState.swift in Sources */, 1537F5E1204C8D860056EE50 /* APAppEntry.swift in Sources */, diff --git a/Apptivator/APState.swift b/Apptivator/APState.swift index b93e125..9830f57 100644 --- a/Apptivator/APState.swift +++ b/Apptivator/APState.swift @@ -12,6 +12,9 @@ import CleanroomLogger // Only one instance of this class should be used at a time. static var shared = APState(atPath: defaultConfigurationPath()) + // A reference to the Application's AppDelegate (used to set the menu bar icon). + let appDelegate = NSApp.delegate as! AppDelegate + // Location of our serialised application state. let savePath: URL @@ -41,7 +44,14 @@ import CleanroomLogger private var _isEnabled = true var isEnabled: Bool { get { return _isEnabled && !currentlyRecording } - set { _isEnabled = newValue } + set { + _isEnabled = newValue + if newValue { + registerShortcutsIfEnabled() + } else { + unregisterShortcuts() + } + } } // The list of application -> shortcut mappings. Made private because whenever we need to @@ -81,7 +91,7 @@ import CleanroomLogger // Add an entry. func addEntry(_ entry: APAppEntry) { entries.append(entry) - registerShortcuts() + registerShortcutsIfEnabled() } // In order for an entry to be cleaned up by ARC, there must be no more references to it. @@ -94,7 +104,7 @@ import CleanroomLogger } shortcutView.shortcutValue = nil }) - registerShortcuts() + registerShortcutsIfEnabled() } func sortEntries(comparator: (APAppEntry, APAppEntry) -> Bool) { @@ -108,8 +118,10 @@ import CleanroomLogger // This resets the shortcut state to its initial setting. This should be called whenever a // an ApplicationEntry updates its sequence. - func registerShortcuts() { - registerShortcuts(atIndex: 0, last: nil) + func registerShortcutsIfEnabled() { + if isEnabled { + registerShortcuts(atIndex: 0, last: nil) + } } // Unregister all previously registered application shortcuts. We can't just use @@ -164,8 +176,10 @@ import CleanroomLogger // If this is a sequential shortcut, then start a timer to reset back to the initial state // if no other shortcuts were hit. if index > 0 { + appDelegate.setMenuBarIcon(ICON_REC) let interval = TimeInterval(defaults.float(forKey: "sequentialShortcutDelay")) sequenceTimer = Timer.scheduledTimer(withTimeInterval: interval, repeats: false) { _ in + self.appDelegate.setMenuBarIcon(ICON_ON) self.sequenceTimer = nil self.registerShortcuts(atIndex: 0, last: nil) Log.debug?.message("Resetting shortcut state.") @@ -183,6 +197,7 @@ import CleanroomLogger if i == entry.sequence.count { entry.apptivate() registerShortcuts(atIndex: 0, last: nil) + appDelegate.setMenuBarIcon(ICON_ON) Log.debug?.message("Apptivating \(entry.name).") } else { // Advance shortcut state with last shortcut and the number of shortcuts hit. @@ -237,7 +252,7 @@ import CleanroomLogger } } - registerShortcuts() + registerShortcutsIfEnabled() } func loadFromString(_ jsonString: String) throws { @@ -249,7 +264,7 @@ import CleanroomLogger case "darkModeEnabled": if !mojaveDarkModeSupported() { darkModeEnabled = value.bool ?? false } case "appIsEnabled": - _isEnabled = value.bool ?? true + isEnabled = value.bool ?? true case "entries": entries = APAppEntry.deserialiseList(fromJSON: value) default: diff --git a/Apptivator/AppDelegate.swift b/Apptivator/AppDelegate.swift index 28513dd..579b2f9 100644 --- a/Apptivator/AppDelegate.swift +++ b/Apptivator/AppDelegate.swift @@ -10,6 +10,7 @@ let ENABLED_INDICATOR_ON = "\(APP_NAME): on" let ENABLED_INDICATOR_OFF = "\(APP_NAME): off" let ICON_ON = setupMenuBarIcon(NSImage(named: NSImage.Name(stringLiteral: "icon-on"))) let ICON_OFF = setupMenuBarIcon(NSImage(named: NSImage.Name(stringLiteral: "icon-off"))) +let ICON_REC = setupMenuBarIcon(NSImage(named: NSImage.Name(stringLiteral: "icon-on")))?.tinted(with: NSColor.red) @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { @IBOutlet weak var popover: NSPopover! @@ -75,9 +76,13 @@ let ICON_OFF = setupMenuBarIcon(NSImage(named: NSImage.Name(stringLiteral: "icon APState.shared.saveToDisk() } + func setMenuBarIcon(_ image: NSImage?) { + menuBarItem?.image = image + } + func enable(_ flag: Bool) { APState.shared.isEnabled = flag - menuBarItem?.image = flag ? ICON_ON : ICON_OFF + setMenuBarIcon(flag ? ICON_ON : ICON_OFF) enabledIndicator.title = flag ? ENABLED_INDICATOR_ON : ENABLED_INDICATOR_OFF } diff --git a/Apptivator/Extensions.swift b/Apptivator/Extensions.swift new file mode 100644 index 0000000..b999147 --- /dev/null +++ b/Apptivator/Extensions.swift @@ -0,0 +1,19 @@ +// +// Extensions.swift +// Apptivator +// + +// http://homecoffeecode.com/nsimage-tinted-as-easily-as-a-uiimage/ +extension NSImage { + func tinted(with tintColor: NSColor) -> NSImage { + guard let cgImage = self.cgImage(forProposedRect: nil, context: nil, hints: nil) else { return self } + + return NSImage(size: size, flipped: false) { bounds in + guard let context = NSGraphicsContext.current?.cgContext else { return false } + tintColor.set() + context.clip(to: bounds, mask: cgImage) + context.fill(bounds) + return true + } + } +} diff --git a/Apptivator/UI/APPopoverViewController.swift b/Apptivator/UI/APPopoverViewController.swift index ed2ad3a..fc26d61 100644 --- a/Apptivator/UI/APPopoverViewController.swift +++ b/Apptivator/UI/APPopoverViewController.swift @@ -146,7 +146,7 @@ class APPopoverViewController: NSViewController { self.tableView.selectRowIndexes([], byExtendingSelection: false) } sequenceEditor!.beforeRemoved = { - APState.shared.registerShortcuts() + APState.shared.registerShortcutsIfEnabled() self.addButton.isEnabled = true self.removeButton.isEnabled = self.tableView.selectedRowIndexes.count > 0 self.reloadView()