27 Commits

Author SHA1 Message Date
Tomáš Znamenáček 74e3f32438 Updated CHANGES, Info.plist and podspec for the 2.2.0 release. 2015-08-18 11:17:51 +02:00
Vadim Shpakovski 1f3dbbce75 Merge pull request #71 from Stillness-2/master
Some fix
2015-08-10 02:44:50 +03:00
Roman Sokolov c98154e2c3 fix for keyboard navigation
Tab key must pass through. This is important if you want to do keyboard navigation through this control.
2015-08-09 21:55:03 +03:00
Roman Sokolov 5ead539397 Fix
Fix problem: 
Set hotkey [TheSameHOTKEY], then mousedown on delete button, then set hotkey again  [TheSameHOTKEY], then this hotkey do not work.
2015-08-09 21:27:19 +03:00
Tomáš Znamenáček 3f63f8fefc Merge pull request #69 from pfandrade/master
Not declaring MASShortcutGlyph as a variable in the header.

Prevents duplicate symbol errors.
2015-04-10 09:06:12 +02:00
Paulo F. Andrade dcb134242d Not declaring MASShortcutGlyph as a variable in the header 2015-04-09 20:20:36 +01:00
Tomáš Znamenáček 6c4577199c Namespaced the testing build scheme name (Tests → MASShortcutTests). 2015-03-09 14:32:38 +01:00
Tomáš Znamenáček 643e87c199 Updated CHANGES. 2015-03-09 14:15:32 +01:00
Vadim Shpakovski bc2c91fc94 Merge pull request #67 from brow/carthage
Support installation with Carthage
2015-03-09 15:37:05 +03:00
Tom Brow 7586157fe2 add Carthage compatibility flag to README 2015-03-08 15:17:58 -07:00
Tom Brow 4d3c2cad2c missing import? 2015-03-08 15:12:59 -07:00
Tom Brow d845d8cda9 module map 2015-03-08 15:12:27 -07:00
Tom Brow 2efd8d1dca DEFINES_MODULE = YES 2015-03-08 14:49:30 -07:00
Tomáš Znamenáček 7bbaed6227 Removed support for dots and spaces in user defaults keys (#64).
I could find no way to make the feature work, so I have pulled it
from the code. I have also inserted asserts to warn library users
who would attempt to use illegal characters in user defaults keys
in the future. In short, you want your user defaults keys to be
something identifier-like.
2015-03-05 14:32:51 +01:00
Tomáš Znamenáček fdc43c1cd3 Fix a problem with defaults keys with dot symbols (#64). 2015-03-05 10:59:01 +01:00
Tomáš Znamenáček a9e6e5241c Added a test case for a binder problem with dot symbols (#64). 2015-03-04 16:28:25 +01:00
Tomáš Znamenáček 67f4a5477b Trivial refactoring. 2015-03-04 16:15:47 +01:00
Tomáš Znamenáček a1eeb57ad6 Fix accessibility-related crashes on older OS X releases (#47).
The NSAccessibilityPriorityKey symbol was only introduced in 10.9,
so that we have to check for its availability before using it, otherwise
we get a SIGSEGV.
2015-03-04 16:07:46 +01:00
Tomáš Znamenáček 934abde616 Updated README and CHANGES. 2015-03-04 11:13:01 +01:00
Tomáš Znamenáček 00dd421e20 Merge pull request #66 from starkos/issue-47-accessibility
Add basic accessibility support.
2015-03-04 11:08:47 +01:00
Jason Perkins e2b2d5b9e1 Allow first responder support to be turned on and off 2015-02-16 10:22:22 -05:00
Jason Perkins 86d5b1ae49 Merge branch 'master' into issue-47-accessibility 2015-02-16 10:09:48 -05:00
Vadim Shpakovski 3ea350cec1 Merge pull request #65 from oreshinya/add_option_showing_delete_hotkey_button
Added options to show button that delete hot key.
2015-02-12 19:57:02 +03:00
shinya takahashi aeaad2986f Added options to show button that delete hot key. 2015-02-13 01:05:14 +09:00
Jason Perkins b564f5296a Enable control to become first responder 2015-01-21 07:36:08 -05:00
Jason Perkins 439ec69ab8 Fix "semicolon in method body" warning 2015-01-20 14:04:23 -05:00
Jason Perkins eda4bdb9c9 Add initial accessibility using 10.10 APIs 2015-01-14 12:23:22 -05:00
12 changed files with 171 additions and 36 deletions
+7
View File
@@ -1,3 +1,10 @@
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 -2
View File
@@ -15,9 +15,9 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>2.1.2</string>
<string>2.2.0</string>
<key>CFBundleVersion</key>
<string>2.1.2</string>
<string>2.2.0</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 20142015 Vadim Shpakovski. All rights reserved.</string>
</dict>
+2 -1
View File
@@ -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)
{
+6
View File
@@ -0,0 +1,6 @@
framework module MASShortcut {
umbrella header "Shortcut.h"
export *
module * { export * }
}
+9 -2
View File
@@ -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;
}
+9
View File
@@ -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
+2
View File
@@ -21,4 +21,6 @@ typedef enum {
/// Returns custom class for drawing control.
+ (Class)shortcutCellClass;
- (void)setAcceptsFirstResponder:(BOOL)value;
@end
+115 -19
View File
@@ -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 : kMASShortcutGlyphClear)
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);
@@ -376,6 +398,11 @@ void *kUserDataHint = &kUserDataHint;
// 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;
@@ -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
+2 -2
View File
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'MASShortcut'
s.version = '2.1.2'
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'
@@ -9,7 +9,7 @@ Pod::Spec.new do |s|
s.platform = :osx
s.osx.deployment_target = "10.6"
s.source = { :git => 'https://github.com/shpakovski/MASShortcut.git', :tag => '2.1.2' }
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'
+11 -5
View File
@@ -106,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 */
@@ -177,6 +178,7 @@
0DC2F18A19937060003A0131 /* User Defaults Storage */,
0D827DA119912A6D0010B8EF /* UI */,
0D827D2F1990D5640010B8EF /* Info.plist */,
EAFFDC811AACFF3300F38834 /* MASShortcut.modulemap */,
0D827D98199110F60010B8EF /* Prefix.pch */,
0D827D761990F81E0010B8EF /* Shortcut.h */,
);
@@ -316,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 */,
@@ -329,7 +331,7 @@
dependencies = (
0D827D8F19910AFF0010B8EF /* PBXTargetDependency */,
);
name = Tests;
name = MASShortcutTests;
productName = Tests;
productReference = 0D827D8319910AFF0010B8EF /* Tests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
@@ -362,8 +364,8 @@
projectRoot = "";
targets = (
0D827CD21990D4420010B8EF /* MASShortcut */,
0D827D8219910AFF0010B8EF /* MASShortcutTests */,
0D827D361990D5E70010B8EF /* Demo */,
0D827D8219910AFF0010B8EF /* Tests */,
);
};
/* End PBXProject section */
@@ -517,6 +519,7 @@
isa = XCBuildConfiguration;
buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
FRAMEWORK_VERSION = A;
@@ -525,6 +528,7 @@
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;
@@ -535,6 +539,7 @@
isa = XCBuildConfiguration;
buildSettings = {
COMBINE_HIDPI_IMAGES = YES;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
FRAMEWORK_VERSION = A;
@@ -543,6 +548,7 @@
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;
@@ -647,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 */,
@@ -29,8 +29,8 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D827D8219910AFF0010B8EF"
BuildableName = "Tests.xctest"
BlueprintName = "Tests"
BuildableName = "MASShortcutTests.xctest"
BlueprintName = "MASShortcutTests"
ReferencedContainer = "container:MASShortcut.xcodeproj">
</BuildableReference>
</BuildActionEntry>
@@ -47,8 +47,8 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "0D827D8219910AFF0010B8EF"
BuildableName = "Tests.xctest"
BlueprintName = "Tests"
BuildableName = "MASShortcutTests.xctest"
BlueprintName = "MASShortcutTests"
ReferencedContainer = "container:MASShortcut.xcodeproj">
</BuildableReference>
</TestableReference>
+2 -1
View File
@@ -1,4 +1,5 @@
[![Build Status](https://travis-ci.org/shpakovski/MASShortcut.svg?branch=master)](https://travis-ci.org/shpakovski/MASShortcut)
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
# Intro
@@ -18,11 +19,11 @@ Features:
* 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
* Accessibility
Pull requests welcome :)