From 444d1bccb9770738fa4ea40383c23f44a55089c2 Mon Sep 17 00:00:00 2001 From: Tomáš Znamenáček Date: Wed, 6 Aug 2014 18:05:43 +0200 Subject: Refactored the shortcut dispatcher and bindings to user defaults. This is a big change that was hard to split into smaller commits. There’s now a new class to bind shortcuts to actions, a new class to bind user defaults’ keys to actions, and a new way to associate user defaults with the recorder control (MASShortcutView). I have also updated the demo app to go with the changes. The new class to associate shortcuts with actions is called MASShortcutMonitor. It wraps the Carbon hotkey magic and offers a simple interface to add a shortcut along with a block that should be run when the shortcut is pressed. It’s the lowest-level interface. Since the usual requirement is to store the shortcuts into user defaults, there’s also a higher-level interface offered by the MASShortcutBinder class. That takes a defaults key and associates it with a block. When the shortcut stored under the defaults key changes, the binder automatically switches to the new shortcut. The class is a wrapper built atop of the previous one, the MASShortcutMonitor – it simply adds, updates and removes shortcuts as the user defaults change. I have removed the special user defaults integration code from the recorder control (MASShortcutView) and replaced it with a small Cocoa Bindings shim. This means that in order to keep the recorder control in sync with the defaults you just have to call the usual bind:toObject:withKeyPath:options: method, like this: [_shortcutView bind:MASShortcutBinding toObject:[NSUserDefaultsController sharedUserDefaultsController] withKeyPath[@"values.ExampleDefaultsKey" options:@{NSValueTransformerNameBindingOption:NSKeyedUnarchiveFromDataTransformerName}]; That’s more verbose than the previous solution, but it’s much cleaner and can be swept under a convenience call if needed. I might also add a dictionaryValue property later that would make it possible to bind the value to user defaults directly, without a transformer, and would enable backward compatibility with Shortcut Recorder. --- MASShortcut.xcodeproj/project.pbxproj | 60 +++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 28 deletions(-) (limited to 'MASShortcut.xcodeproj') diff --git a/MASShortcut.xcodeproj/project.pbxproj b/MASShortcut.xcodeproj/project.pbxproj index e79cb66..f8f29e5 100644 --- a/MASShortcut.xcodeproj/project.pbxproj +++ b/MASShortcut.xcodeproj/project.pbxproj @@ -10,14 +10,8 @@ 0D827CD71990D4420010B8EF /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D827CD61990D4420010B8EF /* Cocoa.framework */; }; 0D827D251990D55E0010B8EF /* MASShortcut.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D827D1B1990D55E0010B8EF /* MASShortcut.h */; settings = {ATTRIBUTES = (Public, ); }; }; 0D827D261990D55E0010B8EF /* MASShortcut.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D827D1C1990D55E0010B8EF /* MASShortcut.m */; }; - 0D827D271990D55E0010B8EF /* MASShortcut+Monitoring.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D827D1D1990D55E0010B8EF /* MASShortcut+Monitoring.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 0D827D281990D55E0010B8EF /* MASShortcut+Monitoring.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D827D1E1990D55E0010B8EF /* MASShortcut+Monitoring.m */; }; - 0D827D291990D55E0010B8EF /* MASShortcut+UserDefaults.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D827D1F1990D55E0010B8EF /* MASShortcut+UserDefaults.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 0D827D2A1990D55E0010B8EF /* MASShortcut+UserDefaults.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D827D201990D55E0010B8EF /* MASShortcut+UserDefaults.m */; }; 0D827D2B1990D55E0010B8EF /* MASShortcutView.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D827D211990D55E0010B8EF /* MASShortcutView.h */; settings = {ATTRIBUTES = (Public, ); }; }; 0D827D2C1990D55E0010B8EF /* MASShortcutView.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D827D221990D55E0010B8EF /* MASShortcutView.m */; }; - 0D827D2D1990D55E0010B8EF /* MASShortcutView+UserDefaults.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D827D231990D55E0010B8EF /* MASShortcutView+UserDefaults.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 0D827D2E1990D55E0010B8EF /* MASShortcutView+UserDefaults.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D827D241990D55E0010B8EF /* MASShortcutView+UserDefaults.m */; }; 0D827D381990D5E70010B8EF /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D827CD61990D4420010B8EF /* Cocoa.framework */; }; 0D827D6F1990D6110010B8EF /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D827D6A1990D6110010B8EF /* AppDelegate.m */; }; 0D827D711990D6110010B8EF /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D827D6D1990D6110010B8EF /* main.m */; }; @@ -32,6 +26,13 @@ 0D827D99199110F60010B8EF /* Prefix.pch in Headers */ = {isa = PBXBuildFile; fileRef = 0D827D98199110F60010B8EF /* Prefix.pch */; }; 0D827D9E19911A190010B8EF /* MASShortcutValidator.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D827D9C19911A190010B8EF /* MASShortcutValidator.h */; }; 0D827D9F19911A190010B8EF /* MASShortcutValidator.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D827D9D19911A190010B8EF /* MASShortcutValidator.m */; }; + 0D827DA519912D240010B8EF /* MASShortcutMonitor.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D827DA319912D240010B8EF /* MASShortcutMonitor.h */; }; + 0D827DAD199132840010B8EF /* MASShortcutBinder.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D827DAB199132840010B8EF /* MASShortcutBinder.h */; }; + 0DC2F17619922798003A0131 /* MASHotKey.h in Headers */ = {isa = PBXBuildFile; fileRef = 0DC2F17419922798003A0131 /* MASHotKey.h */; }; + 0DC2F17719922798003A0131 /* MASHotKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DC2F17519922798003A0131 /* MASHotKey.m */; }; + 0DC2F17C199232EA003A0131 /* MASShortcutMonitor.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D827DA419912D240010B8EF /* MASShortcutMonitor.m */; }; + 0DC2F17D199232F7003A0131 /* MASShortcutBinder.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D827DAC199132840010B8EF /* MASShortcutBinder.m */; }; + 0DC2F18919925F8F003A0131 /* MASShortcutBinderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0DC2F18819925F8F003A0131 /* MASShortcutBinderTests.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -67,14 +68,8 @@ 0D827CEB1990D4420010B8EF /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; }; 0D827D1B1990D55E0010B8EF /* MASShortcut.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MASShortcut.h; path = Framework/MASShortcut.h; sourceTree = ""; }; 0D827D1C1990D55E0010B8EF /* MASShortcut.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MASShortcut.m; path = Framework/MASShortcut.m; sourceTree = ""; }; - 0D827D1D1990D55E0010B8EF /* MASShortcut+Monitoring.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "MASShortcut+Monitoring.h"; path = "Framework/MASShortcut+Monitoring.h"; sourceTree = ""; }; - 0D827D1E1990D55E0010B8EF /* MASShortcut+Monitoring.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "MASShortcut+Monitoring.m"; path = "Framework/MASShortcut+Monitoring.m"; sourceTree = ""; }; - 0D827D1F1990D55E0010B8EF /* MASShortcut+UserDefaults.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "MASShortcut+UserDefaults.h"; path = "Framework/MASShortcut+UserDefaults.h"; sourceTree = ""; }; - 0D827D201990D55E0010B8EF /* MASShortcut+UserDefaults.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "MASShortcut+UserDefaults.m"; path = "Framework/MASShortcut+UserDefaults.m"; sourceTree = ""; }; 0D827D211990D55E0010B8EF /* MASShortcutView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MASShortcutView.h; path = Framework/MASShortcutView.h; sourceTree = ""; }; 0D827D221990D55E0010B8EF /* MASShortcutView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MASShortcutView.m; path = Framework/MASShortcutView.m; sourceTree = ""; }; - 0D827D231990D55E0010B8EF /* MASShortcutView+UserDefaults.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "MASShortcutView+UserDefaults.h"; path = "Framework/MASShortcutView+UserDefaults.h"; sourceTree = ""; }; - 0D827D241990D55E0010B8EF /* MASShortcutView+UserDefaults.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "MASShortcutView+UserDefaults.m"; path = "Framework/MASShortcutView+UserDefaults.m"; sourceTree = ""; }; 0D827D2F1990D5640010B8EF /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Framework/Info.plist; sourceTree = ""; }; 0D827D371990D5E70010B8EF /* Demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Demo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 0D827D691990D6110010B8EF /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; @@ -92,6 +87,13 @@ 0D827D98199110F60010B8EF /* Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Prefix.pch; path = Framework/Prefix.pch; sourceTree = ""; }; 0D827D9C19911A190010B8EF /* MASShortcutValidator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MASShortcutValidator.h; path = Framework/MASShortcutValidator.h; sourceTree = ""; }; 0D827D9D19911A190010B8EF /* MASShortcutValidator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MASShortcutValidator.m; path = Framework/MASShortcutValidator.m; sourceTree = ""; }; + 0D827DA319912D240010B8EF /* MASShortcutMonitor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MASShortcutMonitor.h; path = Framework/MASShortcutMonitor.h; sourceTree = ""; }; + 0D827DA419912D240010B8EF /* MASShortcutMonitor.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MASShortcutMonitor.m; path = Framework/MASShortcutMonitor.m; sourceTree = ""; }; + 0D827DAB199132840010B8EF /* MASShortcutBinder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MASShortcutBinder.h; path = Framework/MASShortcutBinder.h; sourceTree = ""; }; + 0D827DAC199132840010B8EF /* MASShortcutBinder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MASShortcutBinder.m; path = Framework/MASShortcutBinder.m; sourceTree = ""; }; + 0DC2F17419922798003A0131 /* MASHotKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MASHotKey.h; path = Framework/MASHotKey.h; sourceTree = ""; }; + 0DC2F17519922798003A0131 /* MASHotKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MASHotKey.m; path = Framework/MASHotKey.m; sourceTree = ""; }; + 0DC2F18819925F8F003A0131 /* MASShortcutBinderTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MASShortcutBinderTests.m; path = Framework/MASShortcutBinderTests.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -161,7 +163,7 @@ isa = PBXGroup; children = ( 0D827DA019912A660010B8EF /* Model */, - 0D827DA219912A870010B8EF /* Handling & Persistence */, + 0D827DA219912A870010B8EF /* Watching & Storage */, 0D827DA119912A6D0010B8EF /* UI */, 0D827D2F1990D5640010B8EF /* Info.plist */, 0D827D98199110F60010B8EF /* Prefix.pch */, @@ -211,21 +213,22 @@ children = ( 0D827D211990D55E0010B8EF /* MASShortcutView.h */, 0D827D221990D55E0010B8EF /* MASShortcutView.m */, - 0D827D231990D55E0010B8EF /* MASShortcutView+UserDefaults.h */, - 0D827D241990D55E0010B8EF /* MASShortcutView+UserDefaults.m */, ); name = UI; sourceTree = ""; }; - 0D827DA219912A870010B8EF /* Handling & Persistence */ = { + 0D827DA219912A870010B8EF /* Watching & Storage */ = { isa = PBXGroup; children = ( - 0D827D1D1990D55E0010B8EF /* MASShortcut+Monitoring.h */, - 0D827D1E1990D55E0010B8EF /* MASShortcut+Monitoring.m */, - 0D827D1F1990D55E0010B8EF /* MASShortcut+UserDefaults.h */, - 0D827D201990D55E0010B8EF /* MASShortcut+UserDefaults.m */, - ); - name = "Handling & Persistence"; + 0DC2F17419922798003A0131 /* MASHotKey.h */, + 0DC2F17519922798003A0131 /* MASHotKey.m */, + 0D827DA319912D240010B8EF /* MASShortcutMonitor.h */, + 0D827DA419912D240010B8EF /* MASShortcutMonitor.m */, + 0D827DAB199132840010B8EF /* MASShortcutBinder.h */, + 0D827DAC199132840010B8EF /* MASShortcutBinder.m */, + 0DC2F18819925F8F003A0131 /* MASShortcutBinderTests.m */, + ); + name = "Watching & Storage"; sourceTree = ""; }; /* End PBXGroup section */ @@ -239,11 +242,11 @@ 0D827D99199110F60010B8EF /* Prefix.pch in Headers */, 0D827D9719910FF70010B8EF /* MASKeyCodes.h in Headers */, 0D827D251990D55E0010B8EF /* MASShortcut.h in Headers */, - 0D827D2D1990D55E0010B8EF /* MASShortcutView+UserDefaults.h in Headers */, - 0D827D271990D55E0010B8EF /* MASShortcut+Monitoring.h in Headers */, + 0D827DAD199132840010B8EF /* MASShortcutBinder.h in Headers */, 0D827D771990F81E0010B8EF /* Shortcut.h in Headers */, - 0D827D291990D55E0010B8EF /* MASShortcut+UserDefaults.h in Headers */, + 0DC2F17619922798003A0131 /* MASHotKey.h in Headers */, 0D827D9E19911A190010B8EF /* MASShortcutValidator.h in Headers */, + 0D827DA519912D240010B8EF /* MASShortcutMonitor.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -368,12 +371,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 0DC2F17719922798003A0131 /* MASHotKey.m in Sources */, 0D827D9F19911A190010B8EF /* MASShortcutValidator.m in Sources */, - 0D827D2E1990D55E0010B8EF /* MASShortcutView+UserDefaults.m in Sources */, + 0DC2F17C199232EA003A0131 /* MASShortcutMonitor.m in Sources */, 0D827D2C1990D55E0010B8EF /* MASShortcutView.m in Sources */, - 0D827D2A1990D55E0010B8EF /* MASShortcut+UserDefaults.m in Sources */, 0D827D261990D55E0010B8EF /* MASShortcut.m in Sources */, - 0D827D281990D55E0010B8EF /* MASShortcut+Monitoring.m in Sources */, + 0DC2F17D199232F7003A0131 /* MASShortcutBinder.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -391,6 +394,7 @@ buildActionMask = 2147483647; files = ( 0D827D9419910B740010B8EF /* MASShortcutTests.m in Sources */, + 0DC2F18919925F8F003A0131 /* MASShortcutBinderTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; -- cgit v1.2.3