Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 74e3f32438 | |||
| 1f3dbbce75 | |||
| c98154e2c3 | |||
| 5ead539397 | |||
| 3f63f8fefc | |||
| dcb134242d | |||
| 6c4577199c | |||
| 643e87c199 | |||
| bc2c91fc94 | |||
| 7586157fe2 | |||
| 4d3c2cad2c | |||
| d845d8cda9 | |||
| 2efd8d1dca | |||
| 7bbaed6227 | |||
| fdc43c1cd3 | |||
| a9e6e5241c | |||
| 67f4a5477b | |||
| a1eeb57ad6 | |||
| 934abde616 | |||
| 00dd421e20 | |||
| e2b2d5b9e1 | |||
| 86d5b1ae49 | |||
| 3ea350cec1 | |||
| aeaad2986f | |||
| ef399290e4 | |||
| 38fe428b19 | |||
| de647cf825 | |||
| 0db346d02b | |||
| 0972882368 | |||
| 72598e01af | |||
| 345da61b0d | |||
| b564f5296a | |||
| 439ec69ab8 | |||
| fd6885abdb | |||
| 35e15f4950 | |||
| fc1ec3eb41 | |||
| bd0510c74d | |||
| 15dda682e4 | |||
| 04987a7bd5 | |||
| bf3a032c4b | |||
| 3ebbb7efde | |||
| 66fd9b8e41 | |||
| 1baa2bae9d | |||
| eda4bdb9c9 | |||
| 27eace979e | |||
| 44542896d4 | |||
| bdb64f0177 | |||
| cfc4bd64d0 | |||
| 988fcee208 | |||
| ca561ca70c | |||
| 8dc86c9b62 | |||
| 86eff031bf | |||
| 62c142a7f5 | |||
| 8286403add | |||
| a045295531 | |||
| 807c5d782b | |||
| cd0926808c | |||
| b9b1964b3f | |||
| 5208c981af | |||
| 86a3231398 | |||
| 0a0619461d | |||
| 25d97b68c7 | |||
| 179dcd6cc2 | |||
| 2bbdbbfff9 |
@@ -0,0 +1,3 @@
|
||||
language: objective-c
|
||||
xcode_project: MASShortcut.xcodeproj
|
||||
xcode_scheme: MASShortcut
|
||||
@@ -1,3 +1,24 @@
|
||||
2.2.0 2015/8/18
|
||||
- Basic accessibility support [starkos]
|
||||
- Added an option to hide the shortcut delete button [oreshinya]
|
||||
- Advertised support for Carthage [Tom Brown]
|
||||
- Bugfix for shortcuts not working after set twice [Roman Sokolov]
|
||||
- Ignore a solo Tab key when recording shortcuts [Roman Sokolov]
|
||||
|
||||
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
|
||||
- Trivial Podspec fix.
|
||||
|
||||
2.0.0 2015/1/9
|
||||
- First version with a changes file :)
|
||||
- Major, backwards incompatible refactoring to simplify long-term maintenance.
|
||||
|
||||
@@ -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.
|
||||
+2
-2
@@ -43,7 +43,7 @@ static void *MASObservingContext = &MASObservingContext;
|
||||
context:MASObservingContext];
|
||||
}
|
||||
|
||||
- (void) playShortcutFeedback
|
||||
- (void)playShortcutFeedback
|
||||
{
|
||||
[[NSSound soundNamed:@"Ping"] play];
|
||||
[_feedbackTextField setStringValue:@"Shortcut pressed!"];
|
||||
@@ -83,7 +83,7 @@ static void *MASObservingContext = &MASObservingContext;
|
||||
|
||||
- (void) setHardcodedShortcutEnabled: (BOOL) enabled
|
||||
{
|
||||
MASShortcut *shortcut = [MASShortcut shortcutWithKeyCode:kVK_ANSI_Keypad2 modifierFlags:NSCommandKeyMask];
|
||||
MASShortcut *shortcut = [MASShortcut shortcutWithKeyCode:kVK_F2 modifierFlags:NSCommandKeyMask];
|
||||
if (enabled) {
|
||||
[[MASShortcutMonitor sharedMonitor] registerShortcut:shortcut withAction:^{
|
||||
[self playShortcutFeedback];
|
||||
|
||||
+22
-15
@@ -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"/>
|
||||
@@ -649,25 +649,28 @@
|
||||
</menu>
|
||||
<window title="Demo" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" frameAutosaveName="DemoWindow" animationBehavior="default" id="371">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
|
||||
<rect key="contentRect" x="335" y="390" width="385" height="129"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1057"/>
|
||||
<rect key="contentRect" x="335" y="390" width="393" height="129"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="877"/>
|
||||
<view key="contentView" id="372">
|
||||
<rect key="frame" x="0.0" y="0.0" width="385" height="129"/>
|
||||
<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,17 +679,19 @@
|
||||
<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">
|
||||
<rect key="frame" x="140" y="43" width="227" height="18"/>
|
||||
<buttonCell key="cell" type="check" title="Enable hard-coded shortcut (⌘2)" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="7gv-jN-44g">
|
||||
<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"/>
|
||||
</buttonCell>
|
||||
@@ -694,25 +699,27 @@
|
||||
<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="1" green="0.14901961389999999" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="calibratedRGB"/>
|
||||
<color key="textColor" red="0.37647062540054321" green="0.85098046064376831" blue="0.17647059261798859" alpha="1" colorSpace="deviceRGB"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
</view>
|
||||
<point key="canvasLocation" x="348.5" y="367.5"/>
|
||||
<point key="canvasLocation" x="352.5" y="367.5"/>
|
||||
</window>
|
||||
<customObject id="494" customClass="AppDelegate">
|
||||
<connections>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 52 KiB |
@@ -15,9 +15,9 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2.0.0</string>
|
||||
<string>2.2.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>2.0.0</string>
|
||||
<string>2.2.0</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2014–2015 Vadim Shpakovski. All rights reserved.</string>
|
||||
</dict>
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
extern NSString *const MASDictionaryTransformerName;
|
||||
|
||||
/**
|
||||
@brief 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 @p NSData or some other supported type like an
|
||||
@p 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 (@p NSKeyedUnarchiveFromDataTransformerName)
|
||||
that converts any @p NSCoding types to @p NSData, but with shortcuts
|
||||
it makes sense to use a dictionary instead – the defaults look better
|
||||
when inspected with the @p 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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -1,4 +1,5 @@
|
||||
#import <Carbon/Carbon.h>
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
// These glyphs are missed in Carbon.h
|
||||
enum {
|
||||
@@ -20,7 +21,7 @@ enum {
|
||||
kMASShortcutGlyphPadClear = 0x2327,
|
||||
kMASShortcutGlyphNorthwestArrow = 0x2196,
|
||||
kMASShortcutGlyphSoutheastArrow = 0x2198,
|
||||
} MASShortcutGlyph;
|
||||
};
|
||||
|
||||
NS_INLINE NSString* NSStringFromMASKeyCode(unsigned short ch)
|
||||
{
|
||||
|
||||
+33
-22
@@ -1,59 +1,70 @@
|
||||
#import "MASKeyCodes.h"
|
||||
|
||||
/**
|
||||
@brief 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>
|
||||
|
||||
/**
|
||||
@brief The virtual key code for the keyboard key.
|
||||
The virtual key code for the keyboard key.
|
||||
|
||||
@Hardware independent, same as in NSEvent. 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;
|
||||
|
||||
/**
|
||||
@brief 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;
|
||||
|
||||
/**
|
||||
@brief Same as @p keyCode, just a different type.
|
||||
Same as `keyCode`, just a different type.
|
||||
*/
|
||||
@property (nonatomic, readonly) UInt32 carbonKeyCode;
|
||||
|
||||
/**
|
||||
@brief Carbon modifier flags.
|
||||
Carbon modifier flags.
|
||||
|
||||
A bit sum of @p cmdKey, @p optionKey, etc.
|
||||
A bit sum of `cmdKey`, `optionKey`, etc.
|
||||
*/
|
||||
@property (nonatomic, readonly) UInt32 carbonFlags;
|
||||
|
||||
/**
|
||||
@brief 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;
|
||||
|
||||
/**
|
||||
@brief A key-code string used in key equivalent matching.
|
||||
A key-code string used in key equivalent matching.
|
||||
|
||||
For precise meaning of “key equivalents” see the @p keyEquivalent
|
||||
property of @p NSMenuItem. Here the string is used to support shortcut
|
||||
validation (“is the shortcut already taken in this menu?”) and
|
||||
for display in @p NSMenu.
|
||||
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;
|
||||
|
||||
/**
|
||||
@brief 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;
|
||||
|
||||
/**
|
||||
@brief 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;
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
framework module MASShortcut {
|
||||
umbrella header "Shortcut.h"
|
||||
|
||||
export *
|
||||
module * { export * }
|
||||
}
|
||||
@@ -1,66 +1,66 @@
|
||||
#import "MASShortcutMonitor.h"
|
||||
|
||||
/**
|
||||
@brief Binds actions to user defaults keys.
|
||||
Binds actions to user defaults keys.
|
||||
|
||||
If you store shortcuts in user defaults (for example by binding
|
||||
a @p 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 @p 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
|
||||
|
||||
/**
|
||||
@brief 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;
|
||||
|
||||
/**
|
||||
@brief The underlying shortcut monitor.
|
||||
The underlying shortcut monitor.
|
||||
*/
|
||||
@property(strong) MASShortcutMonitor *shortcutMonitor;
|
||||
|
||||
/**
|
||||
@brief Binding options customizing the access to user defaults.
|
||||
Binding options customizing the access to user defaults.
|
||||
|
||||
As an example, you can use @p NSValueTransformerNameBindingOption to customize
|
||||
the storage format used for the shortcuts. By default the shortcuts are converted
|
||||
from @p NSData (@p NSKeyedUnarchiveFromDataTransformerName). Note that if the
|
||||
binder is to work with @p 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;
|
||||
|
||||
/**
|
||||
@brief 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;
|
||||
|
||||
/**
|
||||
@brief 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;
|
||||
|
||||
/**
|
||||
@brief 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
|
||||
@p bindingOptions and registered using @p 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;
|
||||
|
||||
|
||||
@@ -41,9 +41,15 @@
|
||||
|
||||
- (void) bindShortcutWithDefaultsKey: (NSString*) defaultsKeyName toAction: (dispatch_block_t) action
|
||||
{
|
||||
NSAssert([defaultsKeyName rangeOfString:@"."].location == NSNotFound,
|
||||
@"Illegal character in binding name (“.”), please see http://git.io/x5YS.");
|
||||
NSAssert([defaultsKeyName rangeOfString:@" "].location == NSNotFound,
|
||||
@"Illegal character in binding name (“ ”), please see http://git.io/x5YS.");
|
||||
[_actions setObject:[action copy] forKey:defaultsKeyName];
|
||||
[self bind:defaultsKeyName toObject:[NSUserDefaultsController sharedUserDefaultsController]
|
||||
withKeyPath:[@"values." stringByAppendingString:defaultsKeyName] options:_bindingOptions];
|
||||
[self bind:defaultsKeyName
|
||||
toObject:[NSUserDefaultsController sharedUserDefaultsController]
|
||||
withKeyPath:[@"values." stringByAppendingString:defaultsKeyName]
|
||||
options:_bindingOptions];
|
||||
}
|
||||
|
||||
- (void) breakBindingWithDefaultsKey: (NSString*) defaultsKeyName
|
||||
@@ -103,6 +109,7 @@
|
||||
|
||||
// Just deleting the old shortcut
|
||||
if (newShortcut == nil) {
|
||||
[_shortcuts removeObjectForKey:key];
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -95,4 +95,13 @@ static NSString *const SampleDefaultsKey = @"sampleShortcut";
|
||||
@"Bind shortcut using a default value.");
|
||||
}
|
||||
|
||||
// See issue #64 <http://git.io/x5YS> for rationale and discussion.
|
||||
- (void) testIllegalSymbolsInBindingNames
|
||||
{
|
||||
XCTAssertThrows([_binder bindShortcutWithDefaultsKey:@"foo.bar" toAction:^{}],
|
||||
@"Throw for illegal binding symbols: a dot (“.”).");
|
||||
XCTAssertThrows([_binder bindShortcutWithDefaultsKey:@"foo bar" toAction:^{}],
|
||||
@"Throw for illegal binding symbols: a space (“ ”).");
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#import "MASShortcut.h"
|
||||
|
||||
/**
|
||||
@brief 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;
|
||||
|
||||
/**
|
||||
@brief 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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -21,4 +21,6 @@ typedef enum {
|
||||
/// Returns custom class for drawing control.
|
||||
+ (Class)shortcutCellClass;
|
||||
|
||||
- (void)setAcceptsFirstResponder:(BOOL)value;
|
||||
|
||||
@end
|
||||
|
||||
+117
-21
@@ -3,9 +3,8 @@
|
||||
|
||||
NSString *const MASShortcutBinding = @"shortcutValue";
|
||||
|
||||
#define HINT_BUTTON_WIDTH 23.0
|
||||
#define BUTTON_FONT_SIZE 11.0
|
||||
#define SEGMENT_CHROME_WIDTH 6.0
|
||||
static const CGFloat MASHintButtonWidth = 23;
|
||||
static const CGFloat MASButtonFontSize = 11;
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@@ -13,6 +12,7 @@ NSString *const MASShortcutBinding = @"shortcutValue";
|
||||
|
||||
@property (nonatomic, getter = isHinting) BOOL hinting;
|
||||
@property (nonatomic, copy) NSString *shortcutPlaceholder;
|
||||
@property (nonatomic, assign) BOOL showsDeleteButton;
|
||||
|
||||
@end
|
||||
|
||||
@@ -23,6 +23,7 @@ NSString *const MASShortcutBinding = @"shortcutValue";
|
||||
NSInteger _shortcutToolTipTag;
|
||||
NSInteger _hintToolTipTag;
|
||||
NSTrackingArea *_hintArea;
|
||||
BOOL _acceptsFirstResponder;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
@@ -54,9 +55,11 @@ NSString *const MASShortcutBinding = @"shortcutValue";
|
||||
{
|
||||
_shortcutCell = [[[self.class shortcutCellClass] alloc] init];
|
||||
_shortcutCell.buttonType = NSPushOnPushOffButton;
|
||||
_shortcutCell.font = [[NSFontManager sharedFontManager] convertFont:_shortcutCell.font toSize:BUTTON_FONT_SIZE];
|
||||
_shortcutCell.font = [[NSFontManager sharedFontManager] convertFont:_shortcutCell.font toSize:MASButtonFontSize];
|
||||
_shortcutValidator = [MASShortcutValidator sharedValidator];
|
||||
_enabled = YES;
|
||||
_showsDeleteButton = YES;
|
||||
_acceptsFirstResponder = NO;
|
||||
[self resetShortcutCellStyle];
|
||||
}
|
||||
|
||||
@@ -122,14 +125,27 @@ NSString *const MASShortcutBinding = @"shortcutValue";
|
||||
|
||||
// Only enabled view supports recording
|
||||
if (flag && !self.enabled) return;
|
||||
|
||||
if (_recording != flag) {
|
||||
_recording = flag;
|
||||
self.shortcutPlaceholder = nil;
|
||||
[self resetToolTips];
|
||||
[self activateEventMonitoring:_recording];
|
||||
[self activateResignObserver:_recording];
|
||||
[self setNeedsDisplay:YES];
|
||||
|
||||
// Only care about changes in state
|
||||
if (flag == _recording) return;
|
||||
|
||||
_recording = flag;
|
||||
self.shortcutPlaceholder = nil;
|
||||
[self resetToolTips];
|
||||
[self activateEventMonitoring:_recording];
|
||||
[self activateResignObserver:_recording];
|
||||
[self setNeedsDisplay:YES];
|
||||
|
||||
// Give VoiceOver users feedback on the result. Requires at least 10.9 to run.
|
||||
if (_recording == NO && (&NSAccessibilityPriorityKey != NULL)) {
|
||||
NSString* msg = _shortcutValue ?
|
||||
NSLocalizedString(@"Shortcut set", @"VoiceOver: Shortcut set") :
|
||||
NSLocalizedString(@"Shortcut cleared", @"VoiceOver: Shortcut cleared");
|
||||
NSDictionary *announcementInfo = @{
|
||||
NSAccessibilityAnnouncementKey : msg,
|
||||
NSAccessibilityPriorityKey : @(NSAccessibilityPriorityHigh),
|
||||
};
|
||||
NSAccessibilityPostNotificationWithUserInfo(self, NSAccessibilityAnnouncementRequestedNotification, announcementInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,7 +154,7 @@ NSString *const MASShortcutBinding = @"shortcutValue";
|
||||
_shortcutValue = shortcutValue;
|
||||
[self resetToolTips];
|
||||
[self setNeedsDisplay:YES];
|
||||
[self propagateValue:shortcutValue forBinding:@"shortcutValue"];
|
||||
[self propagateValue:shortcutValue forBinding:MASShortcutBinding];
|
||||
|
||||
if (self.shortcutValueChange) {
|
||||
self.shortcutValueChange(self);
|
||||
@@ -188,9 +204,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
|
||||
@@ -229,11 +251,11 @@ NSString *const MASShortcutBinding = @"shortcutValue";
|
||||
- (void)getShortcutRect:(CGRect *)shortcutRectRef hintRect:(CGRect *)hintRectRef
|
||||
{
|
||||
CGRect shortcutRect, hintRect;
|
||||
CGFloat hintButtonWidth = HINT_BUTTON_WIDTH;
|
||||
CGFloat hintButtonWidth = MASHintButtonWidth;
|
||||
switch (self.style) {
|
||||
case MASShortcutViewStyleTexturedRect: hintButtonWidth += 2.0; break;
|
||||
case MASShortcutViewStyleRounded: hintButtonWidth += 3.0; break;
|
||||
case MASShortcutViewStyleFlat: hintButtonWidth -= 8.0 - (_shortcutCell.font.pointSize - BUTTON_FONT_SIZE); break;
|
||||
case MASShortcutViewStyleFlat: hintButtonWidth -= 8.0 - (_shortcutCell.font.pointSize - MASButtonFontSize); break;
|
||||
default: break;
|
||||
}
|
||||
CGRectDivide(self.bounds, &hintRect, &shortcutRect, hintButtonWidth, CGRectMaxXEdge);
|
||||
@@ -369,13 +391,18 @@ 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) {
|
||||
|
||||
// Create a shortcut from the event
|
||||
MASShortcut *shortcut = [MASShortcut shortcutWithEvent:event];
|
||||
|
||||
// Tab key must pass through.
|
||||
if (shortcut.keyCode == kVK_Tab){
|
||||
return event;
|
||||
}
|
||||
|
||||
// If the shortcut is a plain Delete or Backspace, clear the current shortcut and cancel recording
|
||||
if (!shortcut.modifierFlags && ((shortcut.keyCode == kVK_Delete) || (shortcut.keyCode == kVK_ForwardDelete))) {
|
||||
weakSelf.shortcutValue = nil;
|
||||
@@ -450,7 +477,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;
|
||||
@@ -464,7 +491,7 @@ void *kUserDataHint = &kUserDataHint;
|
||||
#pragma mark Bindings
|
||||
|
||||
// http://tomdalling.com/blog/cocoa/implementing-your-own-cocoa-bindings/
|
||||
-(void) propagateValue:(id)value forBinding:(NSString*)binding;
|
||||
-(void) propagateValue:(id)value forBinding:(NSString*)binding
|
||||
{
|
||||
NSParameterAssert(binding != nil);
|
||||
|
||||
@@ -508,4 +535,73 @@ void *kUserDataHint = &kUserDataHint;
|
||||
[boundObject setValue:value forKeyPath:boundKeyPath];
|
||||
}
|
||||
|
||||
#pragma mark - Accessibility
|
||||
|
||||
- (BOOL)accessibilityIsIgnored
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (NSString *)accessibilityHelp
|
||||
{
|
||||
return NSLocalizedString(@"To record a new shortcut, click this button, and then type the"
|
||||
@" new shortcut, or press delete to clear an existing shortcut.",
|
||||
@"VoiceOver shortcut help");
|
||||
}
|
||||
|
||||
- (NSString *)accessibilityLabel
|
||||
{
|
||||
NSString* title = _shortcutValue.description ?: @"Empty";
|
||||
title = [title stringByAppendingFormat:@" %@", NSLocalizedString(@"keyboard shortcut", @"VoiceOver title")];
|
||||
return title;
|
||||
}
|
||||
|
||||
- (BOOL)accessibilityPerformPress
|
||||
{
|
||||
if (self.isRecording == NO) {
|
||||
self.recording = YES;
|
||||
return YES;
|
||||
}
|
||||
else {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)accessibilityRole
|
||||
{
|
||||
return NSAccessibilityButtonRole;
|
||||
}
|
||||
|
||||
- (BOOL)acceptsFirstResponder
|
||||
{
|
||||
return _acceptsFirstResponder;
|
||||
}
|
||||
|
||||
- (void)setAcceptsFirstResponder:(BOOL)value
|
||||
{
|
||||
_acceptsFirstResponder = value;
|
||||
}
|
||||
|
||||
- (BOOL)becomeFirstResponder
|
||||
{
|
||||
[self setNeedsDisplay:YES];
|
||||
return [super becomeFirstResponder];
|
||||
}
|
||||
|
||||
- (BOOL)resignFirstResponder
|
||||
{
|
||||
[self setNeedsDisplay:YES];
|
||||
return [super resignFirstResponder];
|
||||
}
|
||||
|
||||
- (void)drawFocusRingMask
|
||||
{
|
||||
[_shortcutCell drawFocusRingMaskWithFrame:[self bounds] inView:self];
|
||||
}
|
||||
|
||||
- (NSRect)focusRingMaskBounds
|
||||
{
|
||||
return [self bounds];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
+14
-14
@@ -1,17 +1,17 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'MASShortcut'
|
||||
s.version = '2.0.0'
|
||||
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'
|
||||
s.authors = { 'Vadim Shpakovski' => 'vadim@shpakovski.com',
|
||||
'Tomáš Znamenáček' => 'tomas.znamenacek@gmail.com' }
|
||||
s.name = 'MASShortcut'
|
||||
s.version = '2.2.0'
|
||||
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'
|
||||
s.authors = { 'Vadim Shpakovski' => 'vadim@shpakovski.com',
|
||||
'Tomáš Znamenáček' => 'tomas.znamenacek@gmail.com' }
|
||||
|
||||
s.platform = :osx
|
||||
s.deployment_target = "10.7"
|
||||
s.source = { :git => 'https://github.com/shpakovski/MASShortcut.git', :tag => '2.0.0' }
|
||||
s.source_files = 'Framework/*.{h,m}'
|
||||
s.exclude_files = 'Framework/*Tests.m'
|
||||
s.osx.frameworks = 'Carbon', 'AppKit'
|
||||
s.requires_arc = true
|
||||
s.platform = :osx
|
||||
s.osx.deployment_target = "10.6"
|
||||
s.source = { :git => 'https://github.com/shpakovski/MASShortcut.git', :tag => '2.2.0' }
|
||||
s.source_files = 'Framework/*.{h,m}'
|
||||
s.exclude_files = 'Framework/*Tests.m'
|
||||
s.osx.frameworks = 'Carbon', 'AppKit'
|
||||
s.requires_arc = true
|
||||
end
|
||||
|
||||
@@ -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 */; };
|
||||
@@ -17,9 +19,8 @@
|
||||
0D827D711990D6110010B8EF /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D827D6D1990D6110010B8EF /* main.m */; };
|
||||
0D827D721990D6110010B8EF /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 0D827D6E1990D6110010B8EF /* MainMenu.xib */; };
|
||||
0D827D731990D6590010B8EF /* MASShortcut.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D827CD31990D4420010B8EF /* MASShortcut.framework */; };
|
||||
0D827D751990D6A60010B8EF /* MASShortcut.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 0D827CD31990D4420010B8EF /* MASShortcut.framework */; };
|
||||
0D827D751990D6A60010B8EF /* MASShortcut.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 0D827CD31990D4420010B8EF /* MASShortcut.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||
0D827D771990F81E0010B8EF /* Shortcut.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D827D761990F81E0010B8EF /* Shortcut.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
0D827D8419910AFF0010B8EF /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D827CEB1990D4420010B8EF /* XCTest.framework */; };
|
||||
0D827D9419910B740010B8EF /* MASShortcutTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D827D9319910B740010B8EF /* MASShortcutTests.m */; };
|
||||
0D827D9519910C1E0010B8EF /* MASShortcut.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0D827CD31990D4420010B8EF /* MASShortcut.framework */; };
|
||||
0D827D9719910FF70010B8EF /* MASKeyCodes.h in Headers */ = {isa = PBXBuildFile; fileRef = 0D827D9619910FF70010B8EF /* MASKeyCodes.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
@@ -65,12 +66,13 @@
|
||||
/* 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; };
|
||||
0D827CDA1990D4420010B8EF /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
|
||||
0D827CDB1990D4420010B8EF /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
|
||||
0D827CEB1990D4420010B8EF /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
|
||||
0D827D1B1990D55E0010B8EF /* MASShortcut.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MASShortcut.h; path = Framework/MASShortcut.h; sourceTree = "<group>"; };
|
||||
0D827D1C1990D55E0010B8EF /* MASShortcut.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MASShortcut.m; path = Framework/MASShortcut.m; sourceTree = "<group>"; };
|
||||
0D827D211990D55E0010B8EF /* MASShortcutView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MASShortcutView.h; path = Framework/MASShortcutView.h; sourceTree = "<group>"; };
|
||||
@@ -104,6 +106,7 @@
|
||||
0DC2F18F199372B4003A0131 /* MASDictionaryTransformerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MASDictionaryTransformerTests.m; path = Framework/MASDictionaryTransformerTests.m; sourceTree = "<group>"; };
|
||||
0DC2F19619938EFA003A0131 /* MASShortcutView+Bindings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "MASShortcutView+Bindings.h"; path = "Framework/MASShortcutView+Bindings.h"; sourceTree = "<group>"; };
|
||||
0DC2F19719938EFA003A0131 /* MASShortcutView+Bindings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "MASShortcutView+Bindings.m"; path = "Framework/MASShortcutView+Bindings.m"; sourceTree = "<group>"; };
|
||||
EAFFDC811AACFF3300F38834 /* MASShortcut.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; name = MASShortcut.modulemap; path = Framework/MASShortcut.modulemap; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -128,7 +131,6 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0D827D8419910AFF0010B8EF /* XCTest.framework in Frameworks */,
|
||||
0D827D9519910C1E0010B8EF /* MASShortcut.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -161,7 +163,6 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0D827CD61990D4420010B8EF /* Cocoa.framework */,
|
||||
0D827CEB1990D4420010B8EF /* XCTest.framework */,
|
||||
0D827CD91990D4420010B8EF /* Foundation.framework */,
|
||||
0D827CDA1990D4420010B8EF /* CoreData.framework */,
|
||||
0D827CDB1990D4420010B8EF /* AppKit.framework */,
|
||||
@@ -177,6 +178,7 @@
|
||||
0DC2F18A19937060003A0131 /* User Defaults Storage */,
|
||||
0D827DA119912A6D0010B8EF /* UI */,
|
||||
0D827D2F1990D5640010B8EF /* Info.plist */,
|
||||
EAFFDC811AACFF3300F38834 /* MASShortcut.modulemap */,
|
||||
0D827D98199110F60010B8EF /* Prefix.pch */,
|
||||
0D827D761990F81E0010B8EF /* Shortcut.h */,
|
||||
);
|
||||
@@ -235,8 +237,10 @@
|
||||
children = (
|
||||
0DC2F17419922798003A0131 /* MASHotKey.h */,
|
||||
0DC2F17519922798003A0131 /* MASHotKey.m */,
|
||||
0D39DCA11A668A4400639145 /* MASHotKeyTests.m */,
|
||||
0D827DA319912D240010B8EF /* MASShortcutMonitor.h */,
|
||||
0D827DA419912D240010B8EF /* MASShortcutMonitor.m */,
|
||||
0D39DCA31A668E5500639145 /* MASShortcutMonitorTests.m */,
|
||||
);
|
||||
name = Monitoring;
|
||||
sourceTree = "<group>";
|
||||
@@ -314,9 +318,9 @@
|
||||
productReference = 0D827D371990D5E70010B8EF /* Demo.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
0D827D8219910AFF0010B8EF /* Tests */ = {
|
||||
0D827D8219910AFF0010B8EF /* MASShortcutTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 0D827D9219910AFF0010B8EF /* Build configuration list for PBXNativeTarget "Tests" */;
|
||||
buildConfigurationList = 0D827D9219910AFF0010B8EF /* Build configuration list for PBXNativeTarget "MASShortcutTests" */;
|
||||
buildPhases = (
|
||||
0D827D7F19910AFF0010B8EF /* Sources */,
|
||||
0D827D8019910AFF0010B8EF /* Frameworks */,
|
||||
@@ -327,7 +331,7 @@
|
||||
dependencies = (
|
||||
0D827D8F19910AFF0010B8EF /* PBXTargetDependency */,
|
||||
);
|
||||
name = Tests;
|
||||
name = MASShortcutTests;
|
||||
productName = Tests;
|
||||
productReference = 0D827D8319910AFF0010B8EF /* Tests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
@@ -338,7 +342,7 @@
|
||||
0D827CCA1990D4420010B8EF /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0510;
|
||||
LastUpgradeCheck = 0610;
|
||||
ORGANIZATIONNAME = "Vadim Shpakovski";
|
||||
TargetAttributes = {
|
||||
0D827D8219910AFF0010B8EF = {
|
||||
@@ -360,8 +364,8 @@
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
0D827CD21990D4420010B8EF /* MASShortcut */,
|
||||
0D827D8219910AFF0010B8EF /* MASShortcutTests */,
|
||||
0D827D361990D5E70010B8EF /* Demo */,
|
||||
0D827D8219910AFF0010B8EF /* Tests */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
@@ -423,6 +427,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;
|
||||
};
|
||||
@@ -513,6 +519,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
FRAMEWORK_VERSION = A;
|
||||
@@ -520,6 +527,8 @@
|
||||
GCC_PREFIX_HEADER = Framework/Prefix.pch;
|
||||
INFOPLIST_FILE = Framework/Info.plist;
|
||||
INSTALL_PATH = "@executable_path/../Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.6;
|
||||
MODULEMAP_FILE = Framework/MASShortcut.modulemap;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
WRAPPER_EXTENSION = framework;
|
||||
@@ -530,6 +539,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEFINES_MODULE = YES;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
FRAMEWORK_VERSION = A;
|
||||
@@ -537,6 +547,8 @@
|
||||
GCC_PREFIX_HEADER = Framework/Prefix.pch;
|
||||
INFOPLIST_FILE = Framework/Info.plist;
|
||||
INSTALL_PATH = "@executable_path/../Frameworks";
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.6;
|
||||
MODULEMAP_FILE = Framework/MASShortcut.modulemap;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
WRAPPER_EXTENSION = framework;
|
||||
@@ -555,6 +567,7 @@
|
||||
"$(inherited)",
|
||||
);
|
||||
INFOPLIST_FILE = Demo/Info.plist;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.6;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
WRAPPER_EXTENSION = app;
|
||||
};
|
||||
@@ -568,6 +581,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;
|
||||
};
|
||||
@@ -576,6 +590,7 @@
|
||||
0D827D9019910AFF0010B8EF /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(DEVELOPER_FRAMEWORKS_DIR)",
|
||||
"$(inherited)",
|
||||
@@ -595,6 +610,7 @@
|
||||
0D827D9119910AFF0010B8EF /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(DEVELOPER_FRAMEWORKS_DIR)",
|
||||
"$(inherited)",
|
||||
@@ -637,7 +653,7 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
0D827D9219910AFF0010B8EF /* Build configuration list for PBXNativeTarget "Tests" */ = {
|
||||
0D827D9219910AFF0010B8EF /* Build configuration list for PBXNativeTarget "MASShortcutTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
0D827D9019910AFF0010B8EF /* Debug */,
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0610"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D827D361990D5E70010B8EF"
|
||||
BuildableName = "Demo.app"
|
||||
BlueprintName = "Demo"
|
||||
ReferencedContainer = "container:MASShortcut.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
buildConfiguration = "Debug">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D827D361990D5E70010B8EF"
|
||||
BuildableName = "Demo.app"
|
||||
BlueprintName = "Demo"
|
||||
ReferencedContainer = "container:MASShortcut.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Debug"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D827D361990D5E70010B8EF"
|
||||
BuildableName = "Demo.app"
|
||||
BlueprintName = "Demo"
|
||||
ReferencedContainer = "container:MASShortcut.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Release"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D827D361990D5E70010B8EF"
|
||||
BuildableName = "Demo.app"
|
||||
BlueprintName = "Demo"
|
||||
ReferencedContainer = "container:MASShortcut.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -0,0 +1,110 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0610"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D827CD21990D4420010B8EF"
|
||||
BuildableName = "MASShortcut.framework"
|
||||
BlueprintName = "MASShortcut"
|
||||
ReferencedContainer = "container:MASShortcut.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "NO"
|
||||
buildForArchiving = "NO"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D827D8219910AFF0010B8EF"
|
||||
BuildableName = "MASShortcutTests.xctest"
|
||||
BlueprintName = "MASShortcutTests"
|
||||
ReferencedContainer = "container:MASShortcut.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
buildConfiguration = "Debug">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D827D8219910AFF0010B8EF"
|
||||
BuildableName = "MASShortcutTests.xctest"
|
||||
BlueprintName = "MASShortcutTests"
|
||||
ReferencedContainer = "container:MASShortcut.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D827CD21990D4420010B8EF"
|
||||
BuildableName = "MASShortcut.framework"
|
||||
BlueprintName = "MASShortcut"
|
||||
ReferencedContainer = "container:MASShortcut.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Debug"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
allowLocationSimulation = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D827CD21990D4420010B8EF"
|
||||
BuildableName = "MASShortcut.framework"
|
||||
BlueprintName = "MASShortcut"
|
||||
ReferencedContainer = "container:MASShortcut.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
buildConfiguration = "Release"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "0D827CD21990D4420010B8EF"
|
||||
BuildableName = "MASShortcut.framework"
|
||||
BlueprintName = "MASShortcut"
|
||||
ReferencedContainer = "container:MASShortcut.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -1,8 +1,31 @@
|
||||
[](https://travis-ci.org/shpakovski/MASShortcut)
|
||||
[](https://github.com/Carthage/Carthage)
|
||||
|
||||
# Intro
|
||||
|
||||
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.
|
||||
|
||||

|
||||
|
||||
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
|
||||
* Basic accessibility support
|
||||
|
||||
Important features currently missing:
|
||||
|
||||
* Localisation
|
||||
|
||||
Pull requests welcome :)
|
||||
|
||||
# Installation
|
||||
|
||||
@@ -14,6 +37,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:
|
||||
@@ -93,9 +118,16 @@ _observableKeyPath = [@"values." stringByAppendingString:kPreferenceGlobalShortc
|
||||
context:kGlobalShortcutContext];
|
||||
```
|
||||
|
||||
# Non-ARC Version
|
||||
# Using in Swift projects
|
||||
|
||||
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.
|
||||
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:
|
||||
|
||||
```objective-c
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <MASShortcut/Shortcut.h>
|
||||
```
|
||||
|
||||
# Copyright
|
||||
|
||||
|
||||
@@ -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.)
|
||||
* 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.
|
||||
|
||||
Reference in New Issue
Block a user