Add Shortcut "Double R" to Reload JS in iOS
Summary: Enable double tap R on iOS, consistent with Android. Keep the existing Cmd+R on iOS because people are already used to it. Make Cmd+Key and Double Key both invalid when focus is in textview or textfield. Also try to add Cmd+R in Android, but seems no good. Reviewed By: nicklockwood Differential Revision: D3343907 fbshipit-source-id: 68f7e3b0393711c137e1d932db33e1b6a2a19e09
This commit is contained in:
parent
a49558def8
commit
2d32a6df27
|
@ -172,6 +172,15 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
userInfo:nil];
|
||||
}];
|
||||
|
||||
|
||||
[commands registerDoublePressKeyCommandWithInput:@"r"
|
||||
modifierFlags:0
|
||||
action:^(__unused UIKeyCommand *command) {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTReloadNotification
|
||||
object:nil
|
||||
userInfo:nil];
|
||||
}];
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -14,22 +14,41 @@
|
|||
+ (instancetype)sharedInstance;
|
||||
|
||||
/**
|
||||
* Register a keyboard command.
|
||||
* Register a single-press keyboard command.
|
||||
*/
|
||||
- (void)registerKeyCommandWithInput:(NSString *)input
|
||||
modifierFlags:(UIKeyModifierFlags)flags
|
||||
action:(void (^)(UIKeyCommand *command))block;
|
||||
|
||||
/**
|
||||
* Unregister a keyboard command.
|
||||
* Unregister a single-press keyboard command.
|
||||
*/
|
||||
- (void)unregisterKeyCommandWithInput:(NSString *)input
|
||||
modifierFlags:(UIKeyModifierFlags)flags;
|
||||
|
||||
/**
|
||||
* Check if a command is registered.
|
||||
* Check if a single-press command is registered.
|
||||
*/
|
||||
- (BOOL)isKeyCommandRegisteredForInput:(NSString *)input
|
||||
modifierFlags:(UIKeyModifierFlags)flags;
|
||||
|
||||
/**
|
||||
* Register a double-press keyboard command.
|
||||
*/
|
||||
- (void)registerDoublePressKeyCommandWithInput:(NSString *)input
|
||||
modifierFlags:(UIKeyModifierFlags)flags
|
||||
action:(void (^)(UIKeyCommand *command))block;
|
||||
|
||||
/**
|
||||
* Unregister a double-press keyboard command.
|
||||
*/
|
||||
- (void)unregisterDoublePressKeyCommandWithInput:(NSString *)input
|
||||
modifierFlags:(UIKeyModifierFlags)flags;
|
||||
|
||||
/**
|
||||
* Check if a double-press command is registered.
|
||||
*/
|
||||
- (BOOL)isDoublePressKeyCommandRegisteredForInput:(NSString *)input
|
||||
modifierFlags:(UIKeyModifierFlags)flags;
|
||||
|
||||
@end
|
||||
|
|
|
@ -83,17 +83,60 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
|
||||
@implementation UIResponder (RCTKeyCommands)
|
||||
|
||||
+ (UIResponder *)RCT_getFirstResponder:(UIResponder *)view
|
||||
{
|
||||
UIResponder *firstResponder = nil;
|
||||
|
||||
if (view.isFirstResponder) {
|
||||
return view;
|
||||
} else if ([view isKindOfClass:[UIViewController class]]) {
|
||||
if ([(UIViewController *)view parentViewController]) {
|
||||
firstResponder = [UIResponder RCT_getFirstResponder: [(UIViewController *)view parentViewController]];
|
||||
}
|
||||
return firstResponder ? firstResponder : [UIResponder RCT_getFirstResponder: [(UIViewController *)view view]];
|
||||
} else if ([view isKindOfClass:[UIView class]]) {
|
||||
for (UIView *subview in [(UIView *)view subviews]) {
|
||||
firstResponder = [UIResponder RCT_getFirstResponder: subview];
|
||||
if (firstResponder) {
|
||||
return firstResponder;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return firstResponder;
|
||||
}
|
||||
|
||||
- (NSArray<UIKeyCommand *> *)RCT_keyCommands
|
||||
{
|
||||
/**
|
||||
* If current first responder is UITextView or UITextField, disable the shortcut key commands.
|
||||
* For example, double-pressing a key should type two characters into the text view,
|
||||
* not invoke a predefined keyboard shortcut.
|
||||
*/
|
||||
NSString *name = NSStringFromClass(self.class);
|
||||
if ([name isEqualToString : @"AppDelegate"]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
UIResponder *firstResponder = [UIResponder RCT_getFirstResponder: self];
|
||||
if ([firstResponder isKindOfClass:[UITextView class]] ||
|
||||
[firstResponder isKindOfClass:[UITextField class]] ||
|
||||
[firstResponder.class conformsToProtocol:@protocol(UITextViewDelegate)]) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSSet<RCTKeyCommand *> *commands = [RCTKeyCommands sharedInstance].commands;
|
||||
return [[commands valueForKeyPath:@"keyCommand"] allObjects];
|
||||
}
|
||||
|
||||
/**
|
||||
* Single Press Key Command Response
|
||||
* Command + KeyEvent (Command + R/D, etc.)
|
||||
*/
|
||||
- (void)RCT_handleKeyCommand:(UIKeyCommand *)key
|
||||
{
|
||||
// NOTE: throttle the key handler because on iOS 9 the handleKeyCommand:
|
||||
// method gets called repeatedly if the command key is held down.
|
||||
|
||||
static NSTimeInterval lastCommand = 0;
|
||||
if (RCTIsIOS8OrEarlier() || CACurrentMediaTime() - lastCommand > 0.5) {
|
||||
for (RCTKeyCommand *command in [RCTKeyCommands sharedInstance].commands) {
|
||||
|
@ -108,6 +151,61 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Double Press Key Command Response
|
||||
* Double KeyEvent (Double R, etc.)
|
||||
*/
|
||||
- (void)RCT_handleDoublePressKeyCommand:(UIKeyCommand *)key
|
||||
{
|
||||
static BOOL firstPress = YES;
|
||||
static NSTimeInterval lastCommand = 0;
|
||||
static NSTimeInterval lastDoubleCommand = 0;
|
||||
static NSString *lastInput = nil;
|
||||
static UIKeyModifierFlags lastModifierFlags = nil;
|
||||
|
||||
if (firstPress) {
|
||||
for (RCTKeyCommand *command in [RCTKeyCommands sharedInstance].commands) {
|
||||
if ([command.keyCommand.input isEqualToString:key.input] &&
|
||||
command.keyCommand.modifierFlags == key.modifierFlags &&
|
||||
command.block) {
|
||||
|
||||
firstPress = NO;
|
||||
lastCommand = CACurrentMediaTime();
|
||||
lastInput = key.input;
|
||||
lastModifierFlags = key.modifierFlags;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Second keyevent within 0.2 second,
|
||||
// with the same key as the first one.
|
||||
if (CACurrentMediaTime() - lastCommand < 0.2 &&
|
||||
lastInput == key.input &&
|
||||
lastModifierFlags == key.modifierFlags) {
|
||||
|
||||
for (RCTKeyCommand *command in [RCTKeyCommands sharedInstance].commands) {
|
||||
if ([command.keyCommand.input isEqualToString:key.input] &&
|
||||
command.keyCommand.modifierFlags == key.modifierFlags &&
|
||||
command.block) {
|
||||
|
||||
// NOTE: throttle the key handler because on iOS 9 the handleKeyCommand:
|
||||
// method gets called repeatedly if the command key is held down.
|
||||
if (RCTIsIOS8OrEarlier() || CACurrentMediaTime() - lastDoubleCommand > 0.5) {
|
||||
command.block(key);
|
||||
lastDoubleCommand = CACurrentMediaTime();
|
||||
}
|
||||
firstPress = YES;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lastCommand = CACurrentMediaTime();
|
||||
lastInput = key.input;
|
||||
lastModifierFlags = key.modifierFlags;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation UIApplication (RCTKeyCommands)
|
||||
|
@ -118,6 +216,9 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
if (action == @selector(RCT_handleKeyCommand:)) {
|
||||
[self RCT_handleKeyCommand:sender];
|
||||
return YES;
|
||||
} else if (action == @selector(RCT_handleDoublePressKeyCommand:)) {
|
||||
[self RCT_handleDoublePressKeyCommand:sender];
|
||||
return YES;
|
||||
}
|
||||
return [self RCT_sendAction:action to:target from:sender forEvent:event];
|
||||
}
|
||||
|
@ -130,7 +231,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
{
|
||||
if (RCTIsIOS8OrEarlier()) {
|
||||
|
||||
//swizzle UIApplication
|
||||
// swizzle UIApplication
|
||||
RCTSwapInstanceMethods([UIApplication class],
|
||||
@selector(keyCommands),
|
||||
@selector(RCT_keyCommands));
|
||||
|
@ -140,7 +241,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
@selector(RCT_sendAction:to:from:forEvent:));
|
||||
} else {
|
||||
|
||||
//swizzle UIResponder
|
||||
// swizzle UIResponder
|
||||
RCTSwapInstanceMethods([UIResponder class],
|
||||
@selector(keyCommands),
|
||||
@selector(RCT_keyCommands));
|
||||
|
@ -218,6 +319,58 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
return NO;
|
||||
}
|
||||
|
||||
- (void)registerDoublePressKeyCommandWithInput:(NSString *)input
|
||||
modifierFlags:(UIKeyModifierFlags)flags
|
||||
action:(void (^)(UIKeyCommand *))block
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
if (input.length && flags && RCTIsIOS8OrEarlier()) {
|
||||
|
||||
// Workaround around the first cmd not working: http://openradar.appspot.com/19613391
|
||||
// You can register just the cmd key and do nothing. This ensures that
|
||||
// command-key modified commands will work first time. Fixed in iOS 9.
|
||||
|
||||
[self registerDoublePressKeyCommandWithInput:@""
|
||||
modifierFlags:flags
|
||||
action:nil];
|
||||
}
|
||||
|
||||
UIKeyCommand *command = [UIKeyCommand keyCommandWithInput:input
|
||||
modifierFlags:flags
|
||||
action:@selector(RCT_handleDoublePressKeyCommand:)];
|
||||
|
||||
RCTKeyCommand *keyCommand = [[RCTKeyCommand alloc] initWithKeyCommand:command block:block];
|
||||
[_commands removeObject:keyCommand];
|
||||
[_commands addObject:keyCommand];
|
||||
}
|
||||
|
||||
- (void)unregisterDoublePressKeyCommandWithInput:(NSString *)input
|
||||
modifierFlags:(UIKeyModifierFlags)flags
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
for (RCTKeyCommand *command in _commands.allObjects) {
|
||||
if ([command matchesInput:input flags:flags]) {
|
||||
[_commands removeObject:command];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isDoublePressKeyCommandRegisteredForInput:(NSString *)input
|
||||
modifierFlags:(UIKeyModifierFlags)flags
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
for (RCTKeyCommand *command in _commands) {
|
||||
if ([command matchesInput:input flags:flags]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#else
|
||||
|
@ -242,6 +395,19 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
|||
return NO;
|
||||
}
|
||||
|
||||
- (void)registerDoublePressKeyCommandWithInput:(NSString *)input
|
||||
modifierFlags:(UIKeyModifierFlags)flags
|
||||
action:(void (^)(UIKeyCommand *))block {}
|
||||
|
||||
- (void)unregisterDoublePressKeyCommandWithInput:(NSString *)input
|
||||
modifierFlags:(UIKeyModifierFlags)flags {}
|
||||
|
||||
- (BOOL)isDoublePressKeyCommandRegisteredForInput:(NSString *)input
|
||||
modifierFlags:(UIKeyModifierFlags)flags
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue