diff options
| author | Jason Perkins | 2015-02-16 10:09:48 -0500 | 
|---|---|---|
| committer | Jason Perkins | 2015-02-16 10:09:48 -0500 | 
| commit | 86d5b1ae49105c1b0a789cbdac17dd7ce0da0479 (patch) | |
| tree | 58f9e363bd64e9a667e35b990655093481bbf64e /Framework | |
| parent | b564f5296a489d83d0007b8b21185c5b3326dbc8 (diff) | |
| parent | 3ea350cec127d7118ef64f0e84a9ad84fa249a11 (diff) | |
| download | MASShortcut-86d5b1ae49105c1b0a789cbdac17dd7ce0da0479.tar.bz2 | |
Merge branch 'master' into issue-47-accessibility
Diffstat (limited to 'Framework')
| -rw-r--r-- | Framework/Info.plist | 4 | ||||
| -rw-r--r-- | Framework/MASDictionaryTransformer.h | 22 | ||||
| -rw-r--r-- | Framework/MASHotKey.m | 2 | ||||
| -rw-r--r-- | Framework/MASHotKeyTests.m | 15 | ||||
| -rw-r--r-- | Framework/MASShortcut.h | 57 | ||||
| -rw-r--r-- | Framework/MASShortcut.m | 4 | ||||
| -rw-r--r-- | Framework/MASShortcutBinder.h | 58 | ||||
| -rw-r--r-- | Framework/MASShortcutMonitor.h | 16 | ||||
| -rw-r--r-- | Framework/MASShortcutMonitor.m | 11 | ||||
| -rw-r--r-- | Framework/MASShortcutMonitorTests.m | 23 | ||||
| -rw-r--r-- | Framework/MASShortcutView+Bindings.h | 20 | ||||
| -rw-r--r-- | Framework/MASShortcutView.m | 18 | 
12 files changed, 156 insertions, 94 deletions
diff --git a/Framework/Info.plist b/Framework/Info.plist index 91a62a8..679ef34 100644 --- a/Framework/Info.plist +++ b/Framework/Info.plist @@ -15,9 +15,9 @@  	<key>CFBundlePackageType</key>  	<string>FMWK</string>  	<key>CFBundleShortVersionString</key> -	<string>2.0.0</string> +	<string>2.1.2</string>  	<key>CFBundleVersion</key> -	<string>2.0.0</string> +	<string>2.1.2</string>  	<key>NSHumanReadableCopyright</key>  	<string>Copyright © 2014–2015 Vadim Shpakovski. All rights reserved.</string>  </dict> diff --git a/Framework/MASDictionaryTransformer.h b/Framework/MASDictionaryTransformer.h index 8f8e084..6e53fd8 100644 --- a/Framework/MASDictionaryTransformer.h +++ b/Framework/MASDictionaryTransformer.h @@ -1,19 +1,19 @@  extern NSString *const MASDictionaryTransformerName;  /** -    Converts shortcuts for storage in user defaults. + Converts shortcuts for storage in user defaults. -    User defaults can’t stored custom types directly, they have to -    be serialized to `NSData` or some other supported type like an -    `NSDictionary`. In Cocoa Bindings, the conversion can be done -    using value transformers like this one. + User defaults can’t stored custom types directly, they have to + be serialized to `NSData` or some other supported type like an + `NSDictionary`. In Cocoa Bindings, the conversion can be done + using value transformers like this one. -    There’s a built-in transformer (`NSKeyedUnarchiveFromDataTransformerName`) -    that converts any `NSCoding` types to `NSData`, but with shortcuts -    it makes sense to use a dictionary instead – the defaults look better -    when inspected with the `defaults` command-line utility and the -    format is compatible with an older sortcut library called Shortcut -    Recorder. + There’s a built-in transformer (`NSKeyedUnarchiveFromDataTransformerName`) + that converts any `NSCoding` types to `NSData`, but with shortcuts + it makes sense to use a dictionary instead – the defaults look better + when inspected with the `defaults` command-line utility and the + format is compatible with an older sortcut library called Shortcut + Recorder.  */  @interface MASDictionaryTransformer : NSValueTransformer  @end diff --git a/Framework/MASHotKey.m b/Framework/MASHotKey.m index 7886440..c5ab744 100644 --- a/Framework/MASHotKey.m +++ b/Framework/MASHotKey.m @@ -19,7 +19,7 @@ FourCharCode const MASHotKeySignature = 'MASS';      EventHotKeyID hotKeyID = { .signature = MASHotKeySignature, .id = _carbonID };      OSStatus status = RegisterEventHotKey([shortcut carbonKeyCode], [shortcut carbonFlags], -        hotKeyID, GetEventDispatcherTarget(), kEventHotKeyExclusive, &_hotKeyRef); +        hotKeyID, GetEventDispatcherTarget(), 0, &_hotKeyRef);      if (status != noErr) {          return nil; diff --git a/Framework/MASHotKeyTests.m b/Framework/MASHotKeyTests.m new file mode 100644 index 0000000..65361ab --- /dev/null +++ b/Framework/MASHotKeyTests.m @@ -0,0 +1,15 @@ +#import "MASHotKey.h" + +@interface MASHotKeyTests : XCTestCase +@end + +@implementation MASHotKeyTests + +- (void) testBasicFunctionality +{ +    MASHotKey *hotKey = [MASHotKey registeredHotKeyWithShortcut: +        [MASShortcut shortcutWithKeyCode:kVK_ANSI_H modifierFlags:NSCommandKeyMask|NSAlternateKeyMask]]; +    XCTAssertNotNil(hotKey, @"Register a simple Cmd-Alt-H hotkey."); +} + +@end diff --git a/Framework/MASShortcut.h b/Framework/MASShortcut.h index 8ba1b53..8f420e4 100644 --- a/Framework/MASShortcut.h +++ b/Framework/MASShortcut.h @@ -1,59 +1,70 @@  #import "MASKeyCodes.h"  /** -    A model class to hold a key combination. + A model class to hold a key combination. -    This class just represents a combination of keys. It does not care if -    the combination is valid or can be used as a hotkey, it doesn’t watch -    the input system for the shortcut appearance, nor it does access user -    defaults. + This class just represents a combination of keys. It does not care if + the combination is valid or can be used as a hotkey, it doesn’t watch + the input system for the shortcut appearance, nor it does access user + defaults.  */  @interface MASShortcut : NSObject <NSSecureCoding, NSCopying>  /** -    The virtual key code for the keyboard key. + The virtual key code for the keyboard key. -    Hardware independent, same as in `NSEvent`. See `Events.h` in the HIToolbox -    framework for a complete list, or Command-click this symbol: `kVK_ANSI_A`. + Hardware independent, same as in `NSEvent`. See `Events.h` in the HIToolbox + framework for a complete list, or Command-click this symbol: `kVK_ANSI_A`.  */  @property (nonatomic, readonly) NSUInteger keyCode;  /** -    Cocoa keyboard modifier flags. + Cocoa keyboard modifier flags. -    Same as in `NSEvent`: `NSCommandKeyMask`, `NSAlternateKeyMask`, etc. + Same as in `NSEvent`: `NSCommandKeyMask`, `NSAlternateKeyMask`, etc.  */  @property (nonatomic, readonly) NSUInteger modifierFlags;  /** -    Same as `keyCode`, just a different type. + Same as `keyCode`, just a different type.  */  @property (nonatomic, readonly) UInt32 carbonKeyCode;  /** -    Carbon modifier flags. + Carbon modifier flags. -    A bit sum of `cmdKey`, `optionKey`, etc. + A bit sum of `cmdKey`, `optionKey`, etc.  */  @property (nonatomic, readonly) UInt32 carbonFlags;  /** -    A string representing the “key” part of a shortcut, like the `5` in `⌘5`. + A string representing the “key” part of a shortcut, like the `5` in `⌘5`. + + @warning The value may change depending on the active keyboard layout. + For example for the `^2` keyboard shortcut (`kVK_ANSI_2+NSControlKeyMask` + to be precise) the `keyCodeString` is `2` on the US keyboard, but `ě` when + the Czech keyboard layout is active. See the spec for details.  */  @property (nonatomic, readonly) NSString *keyCodeString;  /** -    A key-code string used in key equivalent matching. - -    For precise meaning of “key equivalents” see the `keyEquivalent` -    property of `NSMenuItem`. Here the string is used to support shortcut -    validation (“is the shortcut already taken in this menu?”) and -    for display in `NSMenu`. + A key-code string used in key equivalent matching. + + For precise meaning of “key equivalents” see the `keyEquivalent` + property of `NSMenuItem`. Here the string is used to support shortcut + validation (“is the shortcut already taken in this menu?”) and + for display in `NSMenu`. + + The value of this property may differ from `keyCodeString`. For example + the Russian keyboard has a `Г` (Ge) Cyrillic character in place of the + latin `U` key. This means you can create a `^Г` shortcut, but in menus + that’s always displayed as `^U`. So the `keyCodeString` returns `Г` + and `keyCodeStringForKeyEquivalent` returns `U`.  */  @property (nonatomic, readonly) NSString *keyCodeStringForKeyEquivalent;  /** -    A string representing the shortcut modifiers, like the `⌘` in `⌘5`. + A string representing the shortcut modifiers, like the `⌘` in `⌘5`.  */  @property (nonatomic, readonly) NSString *modifierFlagsString; @@ -61,9 +72,9 @@  + (instancetype)shortcutWithKeyCode:(NSUInteger)code modifierFlags:(NSUInteger)flags;  /** -    Creates a new shortcut from an `NSEvent` object. + Creates a new shortcut from an `NSEvent` object. -    This is just a convenience initializer that reads the key code and modifiers from an `NSEvent`. + This is just a convenience initializer that reads the key code and modifiers from an `NSEvent`.  */  + (instancetype)shortcutWithEvent:(NSEvent *)anEvent; diff --git a/Framework/MASShortcut.m b/Framework/MASShortcut.m index e6fa63d..ef3385d 100644 --- a/Framework/MASShortcut.m +++ b/Framework/MASShortcut.m @@ -139,10 +139,10 @@ static NSString *const MASShortcutModifierFlags = @"ModifierFlags";          case 115: return NSStringFromMASKeyCode(kMASShortcutGlyphNorthwestArrow);      } -    // Everything else should be printable so look it up in the current keyboard +    // Everything else should be printable so look it up in the current ASCII capable keyboard layout      OSStatus error = noErr;      NSString *keystroke = nil; -    TISInputSourceRef inputSource = TISCopyCurrentKeyboardLayoutInputSource(); +    TISInputSourceRef inputSource = TISCopyCurrentASCIICapableKeyboardLayoutInputSource();      if (inputSource) {          CFDataRef layoutDataRef = TISGetInputSourceProperty(inputSource, kTISPropertyUnicodeKeyLayoutData);          if (layoutDataRef) { diff --git a/Framework/MASShortcutBinder.h b/Framework/MASShortcutBinder.h index 1e65e0d..e7406de 100644 --- a/Framework/MASShortcutBinder.h +++ b/Framework/MASShortcutBinder.h @@ -1,66 +1,66 @@  #import "MASShortcutMonitor.h"  /** -    Binds actions to user defaults keys. + Binds actions to user defaults keys. -    If you store shortcuts in user defaults (for example by binding -    a `MASShortcutView` to user defaults), you can use this class to -    connect an action directly to a user defaults key. If the shortcut -    stored under the key changes, the action will get automatically -    updated to the new one. + If you store shortcuts in user defaults (for example by binding + a `MASShortcutView` to user defaults), you can use this class to + connect an action directly to a user defaults key. If the shortcut + stored under the key changes, the action will get automatically + updated to the new one. -    This class is mostly a wrapper around a `MASShortcutMonitor`. It -    watches the changes in user defaults and updates the shortcut monitor -    accordingly with the new shortcuts. + This class is mostly a wrapper around a `MASShortcutMonitor`. It + watches the changes in user defaults and updates the shortcut monitor + accordingly with the new shortcuts.  */  @interface MASShortcutBinder : NSObject  /** -    A convenience shared instance. + A convenience shared instance. -    You may use it so that you don’t have to manage an instance by hand, -    but it’s perfectly fine to allocate and use a separate instance instead. + You may use it so that you don’t have to manage an instance by hand, + but it’s perfectly fine to allocate and use a separate instance instead.  */  + (instancetype) sharedBinder;  /** -    The underlying shortcut monitor. + The underlying shortcut monitor.  */  @property(strong) MASShortcutMonitor *shortcutMonitor;  /** -    Binding options customizing the access to user defaults. + Binding options customizing the access to user defaults. -    As an example, you can use `NSValueTransformerNameBindingOption` to customize -    the storage format used for the shortcuts. By default the shortcuts are converted -    from `NSData` (`NSKeyedUnarchiveFromDataTransformerName`). Note that if the -    binder is to work with `MASShortcutView`, both object have to use the same storage -    format. + As an example, you can use `NSValueTransformerNameBindingOption` to customize + the storage format used for the shortcuts. By default the shortcuts are converted + from `NSData` (`NSKeyedUnarchiveFromDataTransformerName`). Note that if the + binder is to work with `MASShortcutView`, both object have to use the same storage + format.  */  @property(copy) NSDictionary *bindingOptions;  /** -    Binds given action to a shortcut stored under the given defaults key. + Binds given action to a shortcut stored under the given defaults key. -    In other words, no matter what shortcut you store under the given key, -    pressing it will always trigger the given action. + In other words, no matter what shortcut you store under the given key, + pressing it will always trigger the given action.  */  - (void) bindShortcutWithDefaultsKey: (NSString*) defaultsKeyName toAction: (dispatch_block_t) action;  /** -    Disconnect the binding between user defaults and action. + Disconnect the binding between user defaults and action. -    In other words, the shortcut stored under the given key will no longer trigger an action. + In other words, the shortcut stored under the given key will no longer trigger an action.  */  - (void) breakBindingWithDefaultsKey: (NSString*) defaultsKeyName;  /** -    Register default shortcuts in user defaults. + Register default shortcuts in user defaults. -    This is a convenience frontent to `[NSUserDefaults registerDefaults]`. -    The dictionary should contain a map of user defaults’ keys to appropriate -    keyboard shortcuts. The shortcuts will be transformed according to -    `bindingOptions` and registered using `registerDefaults`. + This is a convenience frontent to `[NSUserDefaults registerDefaults]`. + The dictionary should contain a map of user defaults’ keys to appropriate + keyboard shortcuts. The shortcuts will be transformed according to + `bindingOptions` and registered using `registerDefaults`.  */  - (void) registerDefaultShortcuts: (NSDictionary*) defaultShortcuts; diff --git a/Framework/MASShortcutMonitor.h b/Framework/MASShortcutMonitor.h index 69affc1..dc3d458 100644 --- a/Framework/MASShortcutMonitor.h +++ b/Framework/MASShortcutMonitor.h @@ -1,11 +1,11 @@  #import "MASShortcut.h"  /** -    Executes action when a shortcut is pressed. + Executes action when a shortcut is pressed. -    There can only be one instance of this class, otherwise things -    will probably not work. (There’s a Carbon event handler inside -    and there can only be one Carbon event handler of a given type.) + There can only be one instance of this class, otherwise things + will probably not work. (There’s a Carbon event handler inside + and there can only be one Carbon event handler of a given type.)  */  @interface MASShortcutMonitor : NSObject @@ -13,12 +13,12 @@  + (instancetype) sharedMonitor;  /** -    Register a shortcut along with an action. + Register a shortcut along with an action. -    Attempting to insert an already registered shortcut probably won’t work. -    It may burn your house or cut your fingers. You have been warned. + Attempting to insert an already registered shortcut probably won’t work. + It may burn your house or cut your fingers. You have been warned.  */ -- (void) registerShortcut: (MASShortcut*) shortcut withAction: (dispatch_block_t) action; +- (BOOL) registerShortcut: (MASShortcut*) shortcut withAction: (dispatch_block_t) action;  - (BOOL) isShortcutRegistered: (MASShortcut*) shortcut;  - (void) unregisterShortcut: (MASShortcut*) shortcut; diff --git a/Framework/MASShortcutMonitor.m b/Framework/MASShortcutMonitor.m index 099f4b1..fce8022 100644 --- a/Framework/MASShortcutMonitor.m +++ b/Framework/MASShortcutMonitor.m @@ -45,11 +45,16 @@ static OSStatus MASCarbonEventCallback(EventHandlerCallRef, EventRef, void*);  #pragma mark Registration -- (void) registerShortcut: (MASShortcut*) shortcut withAction: (dispatch_block_t) action +- (BOOL) registerShortcut: (MASShortcut*) shortcut withAction: (dispatch_block_t) action  {      MASHotKey *hotKey = [MASHotKey registeredHotKeyWithShortcut:shortcut]; -    [hotKey setAction:action]; -    [_hotKeys setObject:hotKey forKey:shortcut]; +    if (hotKey) { +        [hotKey setAction:action]; +        [_hotKeys setObject:hotKey forKey:shortcut]; +        return YES; +    } else { +        return NO; +    }  }  - (void) unregisterShortcut: (MASShortcut*) shortcut diff --git a/Framework/MASShortcutMonitorTests.m b/Framework/MASShortcutMonitorTests.m new file mode 100644 index 0000000..ccdcaef --- /dev/null +++ b/Framework/MASShortcutMonitorTests.m @@ -0,0 +1,23 @@ +#import "MASShortcutMonitor.h" + +@interface MASShortcutMonitorTests : XCTestCase +@end + +@implementation MASShortcutMonitorTests + +- (void) testMonitorCreation +{ +    XCTAssertNotNil([MASShortcutMonitor sharedMonitor], @"Create a shared shortcut monitor."); +} + +- (void) testShortcutRegistration +{ +    MASShortcutMonitor *monitor = [MASShortcutMonitor sharedMonitor]; +    MASShortcut *shortcut = [MASShortcut shortcutWithKeyCode:kVK_ANSI_H modifierFlags:NSCommandKeyMask|NSAlternateKeyMask]; +    XCTAssertTrue([monitor registerShortcut:shortcut withAction:NULL], @"Register a shortcut."); +    XCTAssertTrue([monitor isShortcutRegistered:shortcut], @"Remember a previously registered shortcut."); +    [monitor unregisterShortcut:shortcut]; +    XCTAssertFalse([monitor isShortcutRegistered:shortcut], @"Forget shortcut after unregistering."); +} + +@end diff --git a/Framework/MASShortcutView+Bindings.h b/Framework/MASShortcutView+Bindings.h index b0148e7..01b2246 100644 --- a/Framework/MASShortcutView+Bindings.h +++ b/Framework/MASShortcutView+Bindings.h @@ -1,19 +1,19 @@  #import "MASShortcutView.h"  /** -    @brief A simplified interface to bind the recorder value to user defaults. + A simplified interface to bind the recorder value to user defaults. -    You can bind the @p shortcutValue to user defaults using the standard -    @p bind:toObject:withKeyPath:options: call, but since that’s a lot to type -    and read, here’s a simpler option. + You can bind the `shortcutValue` to user defaults using the standard + `bind:toObject:withKeyPath:options:` call, but since that’s a lot to type + and read, here’s a simpler option. -    Setting the @p associatedUserDefaultsKey binds the view’s shortcut value -    to the given user defaults key. You can supply a value transformer to convert -    values between user defaults and @p MASShortcut. If you don’t supply -    a transformer, the @p NSUnarchiveFromDataTransformerName will be used -    automatically. + Setting the `associatedUserDefaultsKey` binds the view’s shortcut value + to the given user defaults key. You can supply a value transformer to convert + values between user defaults and `MASShortcut`. If you don’t supply + a transformer, the `NSUnarchiveFromDataTransformerName` will be used + automatically. -    Set @p associatedUserDefaultsKey to @p nil to disconnect the binding. + Set `associatedUserDefaultsKey` to `nil` to disconnect the binding.  */  @interface MASShortcutView (Bindings) diff --git a/Framework/MASShortcutView.m b/Framework/MASShortcutView.m index dfeeac6..4132ce2 100644 --- a/Framework/MASShortcutView.m +++ b/Framework/MASShortcutView.m @@ -13,6 +13,7 @@ NSString *const MASShortcutBinding = @"shortcutValue";  @property (nonatomic, getter = isHinting) BOOL hinting;  @property (nonatomic, copy) NSString *shortcutPlaceholder; +@property (nonatomic, assign) BOOL showsDeleteButton;  @end @@ -57,6 +58,7 @@ NSString *const MASShortcutBinding = @"shortcutValue";      _shortcutCell.font = [[NSFontManager sharedFontManager] convertFont:_shortcutCell.font toSize:BUTTON_FONT_SIZE];      _shortcutValidator = [MASShortcutValidator sharedValidator];      _enabled = YES; +    _showsDeleteButton = YES;      [self resetShortcutCellStyle];  } @@ -198,9 +200,15 @@ NSString *const MASShortcutBinding = @"shortcutValue";  - (void)drawRect:(CGRect)dirtyRect  {      if (self.shortcutValue) { -        [self drawInRect:self.bounds withTitle:NSStringFromMASKeyCode(self.recording ? kMASShortcutGlyphEscape : kMASShortcutGlyphDeleteLeft) -               alignment:NSRightTextAlignment state:NSOffState]; -         +        NSString *buttonTitle; +        if (self.recording) { +            buttonTitle = NSStringFromMASKeyCode(kMASShortcutGlyphEscape); +        } else if (self.showsDeleteButton) { +            buttonTitle = NSStringFromMASKeyCode(kMASShortcutGlyphClear); +        } +        if (buttonTitle != nil) { +            [self drawInRect:self.bounds withTitle:buttonTitle alignment:NSRightTextAlignment state:NSOffState]; +        }          CGRect shortcutRect;          [self getShortcutRect:&shortcutRect hintRect:NULL];          NSString *title = (self.recording @@ -379,7 +387,7 @@ void *kUserDataHint = &kUserDataHint;      static id eventMonitor = nil;      if (shouldActivate) { -        __weak MASShortcutView *weakSelf = self; +        __unsafe_unretained MASShortcutView *weakSelf = self;          NSEventMask eventMask = (NSKeyDownMask | NSFlagsChangedMask);          eventMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:eventMask handler:^(NSEvent *event) { @@ -460,7 +468,7 @@ void *kUserDataHint = &kUserDataHint;      static id observer = nil;      NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];      if (shouldActivate) { -        __weak MASShortcutView *weakSelf = self; +        __unsafe_unretained MASShortcutView *weakSelf = self;          observer = [notificationCenter addObserverForName:NSWindowDidResignKeyNotification object:self.window                                                  queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notification) {                                                      weakSelf.recording = NO;  | 
