diff options
Diffstat (limited to 'Framework')
| -rw-r--r-- | Framework/MASShortcutBinder.m | 25 | ||||
| -rw-r--r-- | Framework/MASShortcutBinderTests.m | 15 |
2 files changed, 34 insertions, 6 deletions
diff --git a/Framework/MASShortcutBinder.m b/Framework/MASShortcutBinder.m index bf85c41..9b9f017 100644 --- a/Framework/MASShortcutBinder.m +++ b/Framework/MASShortcutBinder.m @@ -23,7 +23,7 @@ - (void) dealloc { for (NSString *bindingName in [_actions allKeys]) { - [self unbind:bindingName]; + [self unbind:[self encodeBindingName:bindingName]]; } } @@ -42,8 +42,10 @@ - (void) bindShortcutWithDefaultsKey: (NSString*) defaultsKeyName toAction: (dispatch_block_t) action { [_actions setObject:[action copy] forKey:defaultsKeyName]; - [self bind:defaultsKeyName toObject:[NSUserDefaultsController sharedUserDefaultsController] - withKeyPath:[@"values." stringByAppendingString:defaultsKeyName] options:_bindingOptions]; + [self bind:[self encodeBindingName:defaultsKeyName] + toObject:[NSUserDefaultsController sharedUserDefaultsController] + withKeyPath:[@"values." stringByAppendingString:defaultsKeyName] + options:_bindingOptions]; } - (void) breakBindingWithDefaultsKey: (NSString*) defaultsKeyName @@ -51,7 +53,7 @@ [_shortcutMonitor unregisterShortcut:[_shortcuts objectForKey:defaultsKeyName]]; [_shortcuts removeObjectForKey:defaultsKeyName]; [_actions removeObjectForKey:defaultsKeyName]; - [self unbind:defaultsKeyName]; + [self unbind:[self encodeBindingName:defaultsKeyName]]; } - (void) registerDefaultShortcuts: (NSDictionary*) defaultShortcuts @@ -74,6 +76,18 @@ #pragma mark Bindings +static NSString *const MASDotSymbolReplacement = @"__dot__"; + +- (NSString*) encodeBindingName: (NSString*) binding +{ + return [binding stringByReplacingOccurrencesOfString:@"." withString:MASDotSymbolReplacement]; +} + +- (NSString*) decodeBindingName: (NSString*) binding +{ + return [binding stringByReplacingOccurrencesOfString:MASDotSymbolReplacement withString:@"."]; +} + - (BOOL) isRegisteredAction: (NSString*) name { return !![_actions objectForKey:name]; @@ -81,6 +95,7 @@ - (id) valueForUndefinedKey: (NSString*) key { + key = [self decodeBindingName:key]; return [self isRegisteredAction:key] ? [_shortcuts objectForKey:key] : [super valueForUndefinedKey:key]; @@ -88,6 +103,8 @@ - (void) setValue: (id) value forUndefinedKey: (NSString*) key { + key = [self decodeBindingName:key]; + if (![self isRegisteredAction:key]) { [super setValue:value forUndefinedKey:key]; return; diff --git a/Framework/MASShortcutBinderTests.m b/Framework/MASShortcutBinderTests.m index cb04532..35542d4 100644 --- a/Framework/MASShortcutBinderTests.m +++ b/Framework/MASShortcutBinderTests.m @@ -95,11 +95,22 @@ static NSString *const SampleDefaultsKey = @"sampleShortcut"; @"Bind shortcut using a default value."); } +// The connection between user defaults and shortcuts is implemented +// using Cocoa Bindings where the dot symbol (“.”) has a special meaning. +// This means we have to escape the dot symbol used in defaults keys, +// otherwise things blow up. Details at <http://git.io/x5YS>. - (void) testBindingsWithDotSymbol { static NSString *const SampleDefaultsKeyWithDotSymbol = @"sample.Shortcut"; - XCTAssertThrows([_binder bindShortcutWithDefaultsKey:SampleDefaultsKeyWithDotSymbol toAction:^{}], - @"Attempting to use a defaults key with a dot symbol crashes with an exception."); + MASShortcut *shortcut = [MASShortcut shortcutWithKeyCode:1 modifierFlags:1]; + XCTAssertNoThrow([_binder bindShortcutWithDefaultsKey:SampleDefaultsKeyWithDotSymbol toAction:^{}], + @"Binding a shortcut to a defaults key with a dot symbol must not throw."); + [_defaults setObject:[NSKeyedArchiver archivedDataWithRootObject:shortcut] forKey:SampleDefaultsKeyWithDotSymbol]; + XCTAssertTrue([_monitor isShortcutRegistered:shortcut], + @"Read a shortcut value using a defaults key with a dot symbol."); + [_binder breakBindingWithDefaultsKey:SampleDefaultsKeyWithDotSymbol]; + XCTAssertFalse([_monitor isShortcutRegistered:shortcut], + @"Breaking a binding with a dot symbol."); } @end |
