diff options
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; |
