diff options
| author | Tomáš Znamenáček | 2014-08-06 18:05:43 +0200 |
|---|---|---|
| committer | Tomáš Znamenáček | 2015-01-07 15:39:39 +0100 |
| commit | 444d1bccb9770738fa4ea40383c23f44a55089c2 (patch) | |
| tree | a4f06e82263b751685e51ee3e1479fd9eebb1d16 /Framework/MASShortcutBinder.m | |
| parent | d8efb0755ab99ef60411f77fa4b635f466973d0f (diff) | |
| download | MASShortcut-444d1bccb9770738fa4ea40383c23f44a55089c2.tar.bz2 | |
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.
Diffstat (limited to 'Framework/MASShortcutBinder.m')
| -rw-r--r-- | Framework/MASShortcutBinder.m | 83 |
1 files changed, 83 insertions, 0 deletions
diff --git a/Framework/MASShortcutBinder.m b/Framework/MASShortcutBinder.m new file mode 100644 index 0000000..d6249a8 --- /dev/null +++ b/Framework/MASShortcutBinder.m @@ -0,0 +1,83 @@ +#import "MASShortcutBinder.h" +#import "MASShortcut.h" + +@interface MASShortcutBinder () +@property(strong) NSMutableDictionary *actions; +@property(strong) NSMutableDictionary *shortcuts; +@end + +@implementation MASShortcutBinder + +#pragma mark Initialization + +- (id) init +{ + self = [super init]; + [self setActions:[NSMutableDictionary dictionary]]; + [self setShortcuts:[NSMutableDictionary dictionary]]; + return self; +} + +- (void) dealloc +{ + for (NSString *bindingName in [_actions allKeys]) { + [self unbind:bindingName]; + } +} + +#pragma mark Bindings + +- (void) bindShortcutWithDefaultsKey: (NSString*) defaultsKeyName toAction: (dispatch_block_t) action +{ + [_actions setObject:[action copy] forKey:defaultsKeyName]; + NSDictionary *bindingOptions = @{NSValueTransformerNameBindingOption: NSKeyedUnarchiveFromDataTransformerName}; + [self bind:defaultsKeyName toObject:[NSUserDefaultsController sharedUserDefaultsController] + withKeyPath:[@"values." stringByAppendingString:defaultsKeyName] options:bindingOptions]; +} + +- (void) breakBindingWithDefaultsKey: (NSString*) defaultsKeyName +{ + [_shortcutMonitor unregisterShortcut:[_shortcuts objectForKey:defaultsKeyName]]; + [_shortcuts removeObjectForKey:defaultsKeyName]; + [_actions removeObjectForKey:defaultsKeyName]; + [self unbind:defaultsKeyName]; +} + +- (BOOL) isRegisteredAction: (NSString*) name +{ + return !![_actions objectForKey:name]; +} + +- (id) valueForUndefinedKey: (NSString*) key +{ + return [self isRegisteredAction:key] ? + [_shortcuts objectForKey:key] : + [super valueForUndefinedKey:key]; +} + +- (void) setValue: (id) value forUndefinedKey: (NSString*) key +{ + if (![self isRegisteredAction:key]) { + [super setValue:value forUndefinedKey:key]; + return; + } + + MASShortcut *newShortcut = value; + MASShortcut *currentShortcut = [_shortcuts objectForKey:key]; + + // Unbind previous shortcut if any + if (currentShortcut != nil) { + [_shortcutMonitor unregisterShortcut:currentShortcut]; + } + + // Just deleting the old shortcut + if (newShortcut == nil) { + return; + } + + // Bind new shortcut + [_shortcuts setObject:newShortcut forKey:key]; + [_shortcutMonitor registerShortcut:newShortcut withAction:[_actions objectForKey:key]]; +} + +@end |
