aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJason Perkins2015-02-16 10:09:48 -0500
committerJason Perkins2015-02-16 10:09:48 -0500
commit86d5b1ae49105c1b0a789cbdac17dd7ce0da0479 (patch)
tree58f9e363bd64e9a667e35b990655093481bbf64e
parentb564f5296a489d83d0007b8b21185c5b3326dbc8 (diff)
parent3ea350cec127d7118ef64f0e84a9ad84fa249a11 (diff)
downloadMASShortcut-86d5b1ae49105c1b0a789cbdac17dd7ce0da0479.tar.bz2
Merge branch 'master' into issue-47-accessibility
-rw-r--r--CHANGES10
-rw-r--r--CONTRIBUTING.md21
-rw-r--r--Demo/MainMenu.xib23
-rw-r--r--Demo/screenshot.pngbin0 -> 53006 bytes
-rw-r--r--Framework/Info.plist4
-rw-r--r--Framework/MASDictionaryTransformer.h22
-rw-r--r--Framework/MASHotKey.m2
-rw-r--r--Framework/MASHotKeyTests.m15
-rw-r--r--Framework/MASShortcut.h57
-rw-r--r--Framework/MASShortcut.m4
-rw-r--r--Framework/MASShortcutBinder.h58
-rw-r--r--Framework/MASShortcutMonitor.h16
-rw-r--r--Framework/MASShortcutMonitor.m11
-rw-r--r--Framework/MASShortcutMonitorTests.m23
-rw-r--r--Framework/MASShortcutView+Bindings.h20
-rw-r--r--Framework/MASShortcutView.m18
-rw-r--r--MASShortcut.podspec6
-rw-r--r--MASShortcut.xcodeproj/project.pbxproj12
-rw-r--r--README.md35
-rw-r--r--Spec.md12
20 files changed, 259 insertions, 110 deletions
diff --git a/CHANGES b/CHANGES
index 6f70664..1cf1502 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,12 @@
-Unreleased yet
+2.1.2 2015/1/28
+ - Better key equivalent handling for non-ASCII layouts.
+ [Dmitry Obukhov]
+
+2.1.1 2015/1/16
+ - Another headerdoc fix for CocoaDocs, hopefully the last one.
+
+2.1.0 2015/1/16
+ - Added support for older OS X versions down to 10.6 included.
- Headerdoc markup that plays better with CocoaDocs.
2.0.1 2015/1/9
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..9f53769
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,21 @@
+# How to Release a New Version
+
+First, update the version numbers. (MASShortcut uses [Semantic Versioning](http://semver.org/), so please read the docs if you’re not sure what the deal is.) The version number is stored in `Framework/Info.plist` and `MASShortcut.podspec` (twice in both files).
+
+Then update the `CHANGES` file. Add information about the new version (see the previous versions for an example) and add the release date.
+
+Now commit the changes:
+
+ $ git commit -a -m "Version bump to x.y.z."
+
+And tag the last commit:
+
+ $ git tag -a x.y.z
+
+Now push both the commits and tags (`--tags`) to GitHub and push the new podspec to CocoaPods:
+
+ $ pod trunk push MASShortcut.podspec
+
+This will run sanity checks on the podspec and fail if the spec does not validate.
+
+That’s it. Go have a beer or a cup of tea to celebrate. \ No newline at end of file
diff --git a/Demo/MainMenu.xib b/Demo/MainMenu.xib
index a04814d..a4bf180 100644
--- a/Demo/MainMenu.xib
+++ b/Demo/MainMenu.xib
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6254" systemVersion="14B25" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
+<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6254" systemVersion="14B25" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6254"/>
@@ -655,19 +655,22 @@
<rect key="frame" x="0.0" y="0.0" width="393" height="129"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
- <customView fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="536" customClass="MASShortcutView">
+ <customView id="536" customClass="MASShortcutView">
<rect key="frame" x="142" y="90" width="158" height="19"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
</customView>
- <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="PG0-jh-Onh">
+ <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="PG0-jh-Onh">
<rect key="frame" x="18" y="92" width="111" height="17"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Custom shortcut:" id="85u-1A-n7H">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
- <button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="zCi-ki-Uuh">
+ <button id="zCi-ki-Uuh">
<rect key="frame" x="140" y="63" width="169" height="18"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="Enable custom shortcut" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="Y47-p3-sDa">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
@@ -676,16 +679,18 @@
<binding destination="rCO-Ve-DT5" name="value" keyPath="values.customShortcutEnabled" id="VjS-3V-VdA"/>
</connections>
</button>
- <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="KnS-ut-phz">
+ <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="KnS-ut-phz">
<rect key="frame" x="18" y="65" width="111" height="17"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Options:" id="cUE-gA-heG">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
- <button fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="F4r-KM-wn9">
+ <button id="F4r-KM-wn9">
<rect key="frame" x="140" y="43" width="235" height="18"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="Enable hard-coded shortcut (⌘F2)" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="7gv-jN-44g">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
@@ -694,16 +699,18 @@
<binding destination="rCO-Ve-DT5" name="value" keyPath="values.hardcodedShortcutEnabled" id="dlZ-si-HeN"/>
</connections>
</button>
- <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="9fB-XS-8pK">
+ <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="9fB-XS-8pK">
<rect key="frame" x="18" y="20" width="111" height="17"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Feedback:" id="Zbz-mV-NDc">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
- <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Aso-dH-W8I">
+ <textField horizontalHuggingPriority="251" verticalHuggingPriority="750" id="Aso-dH-W8I">
<rect key="frame" x="140" y="20" width="211" height="17"/>
+ <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="left" placeholderString="Press a shortcut to see feedback" id="Ckx-v7-e6x">
<font key="font" metaFont="system"/>
<color key="textColor" red="0.37647062540054321" green="0.85098046064376831" blue="0.17647059261798859" alpha="1" colorSpace="deviceRGB"/>
diff --git a/Demo/screenshot.png b/Demo/screenshot.png
new file mode 100644
index 0000000..926f4ca
--- /dev/null
+++ b/Demo/screenshot.png
Binary files differ
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;
diff --git a/MASShortcut.podspec b/MASShortcut.podspec
index 8cf824f..fb89e5a 100644
--- a/MASShortcut.podspec
+++ b/MASShortcut.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'MASShortcut'
- s.version = '2.0.1'
+ s.version = '2.1.2'
s.summary = 'Modern framework for managing global keyboard shortcuts compatible with Mac App Store'
s.homepage = 'https://github.com/shpakovski/MASShortcut'
s.license = 'BSD 2-clause'
@@ -8,8 +8,8 @@ Pod::Spec.new do |s|
'Tomáš Znamenáček' => 'tomas.znamenacek@gmail.com' }
s.platform = :osx
- s.osx.deployment_target = "10.7"
- s.source = { :git => 'https://github.com/shpakovski/MASShortcut.git', :tag => '2.0.1' }
+ s.osx.deployment_target = "10.6"
+ s.source = { :git => 'https://github.com/shpakovski/MASShortcut.git', :tag => '2.1.2' }
s.source_files = 'Framework/*.{h,m}'
s.exclude_files = 'Framework/*Tests.m'
s.osx.frameworks = 'Carbon', 'AppKit'
diff --git a/MASShortcut.xcodeproj/project.pbxproj b/MASShortcut.xcodeproj/project.pbxproj
index 2ab08a8..ea5125c 100644
--- a/MASShortcut.xcodeproj/project.pbxproj
+++ b/MASShortcut.xcodeproj/project.pbxproj
@@ -7,6 +7,8 @@
objects = {
/* Begin PBXBuildFile section */
+ 0D39DCA21A668A4400639145 /* MASHotKeyTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D39DCA11A668A4400639145 /* MASHotKeyTests.m */; };
+ 0D39DCA41A668E5500639145 /* MASShortcutMonitorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D39DCA31A668E5500639145 /* MASShortcutMonitorTests.m */; };
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 */; };
@@ -64,6 +66,8 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
+ 0D39DCA11A668A4400639145 /* MASHotKeyTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MASHotKeyTests.m; path = Framework/MASHotKeyTests.m; sourceTree = "<group>"; };
+ 0D39DCA31A668E5500639145 /* MASShortcutMonitorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MASShortcutMonitorTests.m; path = Framework/MASShortcutMonitorTests.m; sourceTree = "<group>"; };
0D827CD31990D4420010B8EF /* MASShortcut.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MASShortcut.framework; sourceTree = BUILT_PRODUCTS_DIR; };
0D827CD61990D4420010B8EF /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
0D827CD91990D4420010B8EF /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
@@ -231,8 +235,10 @@
children = (
0DC2F17419922798003A0131 /* MASHotKey.h */,
0DC2F17519922798003A0131 /* MASHotKey.m */,
+ 0D39DCA11A668A4400639145 /* MASHotKeyTests.m */,
0D827DA319912D240010B8EF /* MASShortcutMonitor.h */,
0D827DA419912D240010B8EF /* MASShortcutMonitor.m */,
+ 0D39DCA31A668E5500639145 /* MASShortcutMonitorTests.m */,
);
name = Monitoring;
sourceTree = "<group>";
@@ -419,6 +425,8 @@
0DC2F190199372B4003A0131 /* MASDictionaryTransformerTests.m in Sources */,
0D827D9419910B740010B8EF /* MASShortcutTests.m in Sources */,
0DC2F18919925F8F003A0131 /* MASShortcutBinderTests.m in Sources */,
+ 0D39DCA21A668A4400639145 /* MASHotKeyTests.m in Sources */,
+ 0D39DCA41A668E5500639145 /* MASShortcutMonitorTests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -516,6 +524,7 @@
GCC_PREFIX_HEADER = Framework/Prefix.pch;
INFOPLIST_FILE = Framework/Info.plist;
INSTALL_PATH = "@executable_path/../Frameworks";
+ MACOSX_DEPLOYMENT_TARGET = 10.6;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
WRAPPER_EXTENSION = framework;
@@ -533,6 +542,7 @@
GCC_PREFIX_HEADER = Framework/Prefix.pch;
INFOPLIST_FILE = Framework/Info.plist;
INSTALL_PATH = "@executable_path/../Frameworks";
+ MACOSX_DEPLOYMENT_TARGET = 10.6;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
WRAPPER_EXTENSION = framework;
@@ -551,6 +561,7 @@
"$(inherited)",
);
INFOPLIST_FILE = Demo/Info.plist;
+ MACOSX_DEPLOYMENT_TARGET = 10.6;
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
@@ -564,6 +575,7 @@
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = Demo/Prefix.pch;
INFOPLIST_FILE = Demo/Info.plist;
+ MACOSX_DEPLOYMENT_TARGET = 10.6;
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
diff --git a/README.md b/README.md
index 9a98ddb..fccf923 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,27 @@
Some time ago Cocoa developers used a brilliant framework [ShortcutRecorder](http://wafflesoftware.net/shortcut/) for managing keyboard shortcuts in application preferences. However, it became incompatible with the new plugin architecture of Xcode 4.
-The MASShortcut project introduces a modern API and user interface for recording, storing and using system-wide keyboard shortcuts. All code is compatible with recent Xcode & OS X versions and the sandboxed environment.
+The MASShortcut project introduces a modern API and user interface for recording, storing and using system-wide keyboard shortcuts.
+
+![Screenshot of the demo project](/Demo/screenshot.png?raw=true "This is how the demo looks like")
+
+Features:
+
+* Record and display keyboard shortcuts
+* Watch for shortcuts and execute actions, system-wide
+* A nice, [documented API](http://cocoadocs.org/docsets/MASShortcut/)
+* Can be configured to be compatible with Shortcut Recorder
+* Can be installed both through CocoaPods and as a Git submodule
+* Mac App Store friendly
+* Works on OS X 10.6 and up
+* Hacking-friendly codebase covered with tests
+
+Important features currently missing:
+
+* Localisation
+* Accessibility
+
+Pull requests welcome :)
# Installation
@@ -16,6 +36,8 @@ If you want to stick to the 1.x branch, you can use the version smart match oper
pod 'MASShortcut', '~> 1'
+Or can use Git submodules and link against the MASShortcut framework.
+
# Usage
I hope, it is really easy:
@@ -95,9 +117,16 @@ _observableKeyPath = [@"values." stringByAppendingString:kPreferenceGlobalShortc
context:kGlobalShortcutContext];
```
-# Non-ARC Version
+# Using in Swift projects
+
+ 1. Install as a Pod using the latest CocoaPods with Swift support.
+ 2. Create a bridging header file [using the instructions here](http://swiftalicio.us/2014/11/using-cocoapods-from-swift/)
+ 3. Your bridging header file should contain the following [two](https://github.com/shpakovski/MASShortcut/issues/36) imports:
-If you like retain/release, please check out these forks: [heardrwt/MASShortcut](https://github.com/heardrwt/MASShortcut) and [chendo/MASShortcut](https://github.com/chendo/MASShortcut). However, the preferred way is to enable the `-fobjc-arc` in Xcode source options.
+```objective-c
+#import <Cocoa/Cocoa.h>
+#import <MASShortcut/Shortcut.h>
+```
# Copyright
diff --git a/Spec.md b/Spec.md
index 9a8663a..e13576c 100644
--- a/Spec.md
+++ b/Spec.md
@@ -12,4 +12,14 @@ Please stay high-level when writing the spec, do not document particular classes
* If the shortcut is Cmd-W or Cmd-Q, the recording must be cancelled and the keypress passed through to the system, closing the window or quitting the app.
* If a shortcut is already taken by system and is enabled, it must be rejected. (Examples: Cmd-S, Cmd-N. TBD: What exactly does it mean that the shortcut is “enabled”?)
* TBD: Option-key handling.
-* All other shortcuts must be accepted. (Examples: Ctrl-Esc, Cmd-Delete, F16.) \ No newline at end of file
+* All other shortcuts must be accepted. (Examples: Ctrl-Esc, Cmd-Delete, F16.)
+
+# Formatting Shortcuts
+
+On different keyboard layouts (such as US and Czech), a single shortcut (a combination of physical keys) may be formatted into different strings.
+
+For example, the default system shortcut for toggling directly to Space #2 is Control–2. But when you switch to the Czech keyboard layout, the physical key with the `2` label now inserts the `ě` character. Thus, on most keyboard layouts the shortcut for toggling to Space #2 is called `^2`, but on the Czech layout it’s called `^ě`. (I stress that this is the same combination of hardware keys and the same `MASShortcut` instance.)
+
+This is reflected by the system: When you open the System Preferences → Keyboard → Shortcuts pane, the shortcuts displayed depend on the currently selected keyboard layout (try switching between the US and Czech keyboard layouts and reopening the preference pane).
+
+This means that the identity of a shortcut is given by its key code and modifiers (such as `kVK_ANSI_2` and `NSControlKeyMask`), not the `keyCodeString` returned by the `MASShortcut` class. This string may change depending on the current keyboard layout: `^2` with the US keyboard active, but `^ě` with the Czech keyboard active.