Skip to content

Commit 70ffa83

Browse files
christophpurrerappden
authored andcommitted
Refactor handling of keyDown/keyUp (microsoft#1338)
This refactors / simplifies certain keyUp|Down event handling. It will make a later change (adding textInput handling for textInput fields) easier (to review) Co-authored-by: Scott Kyle <[email protected]>
1 parent ab0a6c7 commit 70ffa83

File tree

5 files changed

+90
-250
lines changed

5 files changed

+90
-250
lines changed

React/Views/RCTView.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ extern const UIAccessibilityTraits SwitchAccessibilityTrait;
2727

2828
- (BOOL)becomeFirstResponder;
2929
- (BOOL)resignFirstResponder;
30+
31+
#if TARGET_OS_OSX
32+
- (BOOL)handleKeyboardEvent:(NSEvent *)event;
33+
#endif
3034
// ]TODO(OSS Candidate ISS#2710739)
3135

3236
/**

React/Views/RCTView.m

Lines changed: 20 additions & 159 deletions
Original file line numberDiff line numberDiff line change
@@ -1643,177 +1643,38 @@ - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
16431643
#pragma mark - Keyboard Events
16441644

16451645
#if TARGET_OS_OSX
1646-
NSString* const leftArrowPressKey = @"ArrowLeft";
1647-
NSString* const rightArrowPressKey = @"ArrowRight";
1648-
NSString* const upArrowPressKey = @"ArrowUp";
1649-
NSString* const downArrowPressKey = @"ArrowDown";
1650-
1651-
- (RCTViewKeyboardEvent*)keyboardEvent:(NSEvent*)event downPress:(BOOL)downPress {
1652-
// modifiers
1653-
BOOL capsLockKey = NO;
1654-
BOOL shiftKey = NO;
1655-
BOOL controlKey = NO;
1656-
BOOL optionKey = NO;
1657-
BOOL commandKey = NO;
1658-
BOOL numericPadKey = NO;
1659-
BOOL helpKey = NO;
1660-
BOOL functionKey = NO;
1661-
// commonly used key short-cuts
1662-
BOOL leftArrowKey = NO;
1663-
BOOL rightArrowKey = NO;
1664-
BOOL upArrowKey = NO;
1665-
BOOL downArrowKey = NO;
1666-
BOOL tabKeyPressed = NO;
1667-
BOOL escapeKeyPressed = NO;
1668-
NSString *key = event.charactersIgnoringModifiers;
1669-
if ([key length] == 0) {
1670-
return nil;
1671-
}
1672-
unichar const code = [key characterAtIndex:0];
1673-
1674-
// detect arrow key presses
1675-
if (code == NSLeftArrowFunctionKey) {
1676-
leftArrowKey = YES;
1677-
} else if (code == NSRightArrowFunctionKey) {
1678-
rightArrowKey = YES;
1679-
} else if (code == NSUpArrowFunctionKey) {
1680-
upArrowKey = YES;
1681-
} else if (code == NSDownArrowFunctionKey) {
1682-
downArrowKey = YES;
1683-
}
1684-
1685-
// detect special key presses via the key code
1686-
switch (event.keyCode) {
1687-
case 48: // Tab
1688-
tabKeyPressed = YES;
1689-
break;
1690-
case 53: // Escape
1691-
escapeKeyPressed = YES;
1692-
break;
1693-
default:
1694-
break;
1646+
- (RCTViewKeyboardEvent*)keyboardEvent:(NSEvent*)event {
1647+
BOOL keyDown = event.type == NSEventTypeKeyDown;
1648+
NSArray<NSString *> *validKeys = keyDown ? self.validKeysDown : self.validKeysUp;
1649+
NSString *key = [RCTViewKeyboardEvent keyFromEvent:event];
1650+
1651+
// Only post events for keys we care about
1652+
if (![validKeys containsObject:key]) {
1653+
return nil;
16951654
}
16961655

1697-
// detect modifier flags
1698-
if (event.modifierFlags & NSEventModifierFlagCapsLock) {
1699-
capsLockKey = YES;
1700-
} else if (event.modifierFlags & NSEventModifierFlagShift) {
1701-
shiftKey = YES;
1702-
} else if (event.modifierFlags & NSEventModifierFlagControl) {
1703-
controlKey = YES;
1704-
} else if (event.modifierFlags & NSEventModifierFlagOption) {
1705-
optionKey = YES;
1706-
} else if (event.modifierFlags & NSEventModifierFlagCommand) {
1707-
commandKey = YES;
1708-
} else if (event.modifierFlags & NSEventModifierFlagNumericPad) {
1709-
numericPadKey = YES;
1710-
} else if (event.modifierFlags & NSEventModifierFlagHelp) {
1711-
helpKey = YES;
1712-
} else if (event.modifierFlags & NSEventModifierFlagFunction) {
1713-
functionKey = YES;
1714-
}
1715-
1716-
RCTViewKeyboardEvent *keyboardEvent = nil;
1717-
// only post events for keys we care about
1718-
if (downPress) {
1719-
NSString *keyToReturn = [self keyIsValid:key left:leftArrowKey right:rightArrowKey up:upArrowKey down:downArrowKey tabKey:tabKeyPressed escapeKey:escapeKeyPressed validKeys:[self validKeysDown]];
1720-
if (keyToReturn != nil) {
1721-
keyboardEvent = [RCTViewKeyboardEvent keyDownEventWithReactTag:self.reactTag
1722-
capsLockKey:capsLockKey
1723-
shiftKey:shiftKey
1724-
ctrlKey:controlKey
1725-
altKey:optionKey
1726-
metaKey:commandKey
1727-
numericPadKey:numericPadKey
1728-
helpKey:helpKey
1729-
functionKey:functionKey
1730-
leftArrowKey:leftArrowKey
1731-
rightArrowKey:rightArrowKey
1732-
upArrowKey:upArrowKey
1733-
downArrowKey:downArrowKey
1734-
key:keyToReturn];
1735-
}
1736-
} else {
1737-
NSString *keyToReturn = [self keyIsValid:key left:leftArrowKey right:rightArrowKey up:upArrowKey down:downArrowKey tabKey:tabKeyPressed escapeKey:escapeKeyPressed validKeys:[self validKeysUp]];
1738-
if (keyToReturn != nil) {
1739-
keyboardEvent = [RCTViewKeyboardEvent keyUpEventWithReactTag:self.reactTag
1740-
capsLockKey:capsLockKey
1741-
shiftKey:shiftKey
1742-
ctrlKey:controlKey
1743-
altKey:optionKey
1744-
metaKey:commandKey
1745-
numericPadKey:numericPadKey
1746-
helpKey:helpKey
1747-
functionKey:functionKey
1748-
leftArrowKey:leftArrowKey
1749-
rightArrowKey:rightArrowKey
1750-
upArrowKey:upArrowKey
1751-
downArrowKey:downArrowKey
1752-
key:keyToReturn];
1656+
return [RCTViewKeyboardEvent keyEventFromEvent:event reactTag:self.reactTag];
1657+
}
1658+
1659+
- (BOOL)handleKeyboardEvent:(NSEvent *)event {
1660+
if (event.type == NSEventTypeKeyDown ? self.onKeyDown : self.onKeyUp) {
1661+
RCTViewKeyboardEvent *keyboardEvent = [self keyboardEvent:event];
1662+
if (keyboardEvent) {
1663+
[_eventDispatcher sendEvent:keyboardEvent];
1664+
return YES;
17531665
}
17541666
}
1755-
return keyboardEvent;
1756-
}
1757-
1758-
// check if the user typed key matches a key we need to send an event for
1759-
// translate key codes over to JS compatible keys
1760-
- (NSString*)keyIsValid:(NSString*)key left:(BOOL)leftArrowPressed right:(BOOL)rightArrowPressed up:(BOOL)upArrowPressed down:(BOOL)downArrowPressed tabKey:(BOOL)tabKeyPressed escapeKey:(BOOL)escapeKeyPressed validKeys:(NSArray<NSString*>*)validKeys {
1761-
NSString *keyToReturn = key;
1762-
1763-
// Allow the flexibility of defining special keys in multiple ways
1764-
BOOL enterKeyValidityCheck = [key isEqualToString:@"\r"] && ([validKeys containsObject:@"Enter"] || [validKeys containsObject:@"\r"]);
1765-
BOOL tabKeyValidityCheck = tabKeyPressed && ([validKeys containsObject:@"Tab"]); // tab has to be checked via a key code so we can't just use the key itself here
1766-
BOOL escapeKeyValidityCheck = escapeKeyPressed && ([validKeys containsObject:@"Esc"] || [validKeys containsObject:@"Escape"]); // escape has to be checked via a key code so we can't just use the key itself here
1767-
BOOL leftArrowValidityCheck = [validKeys containsObject:leftArrowPressKey] && leftArrowPressed;
1768-
BOOL rightArrowValidityCheck = [validKeys containsObject:rightArrowPressKey] && rightArrowPressed;
1769-
BOOL upArrowValidityCheck = [validKeys containsObject:upArrowPressKey] && upArrowPressed;
1770-
BOOL downArrowValidityCheck = [validKeys containsObject:downArrowPressKey] && downArrowPressed;
1771-
1772-
if (tabKeyValidityCheck) {
1773-
keyToReturn = @"Tab";
1774-
} else if (escapeKeyValidityCheck) {
1775-
keyToReturn = @"Escape";
1776-
} else if (enterKeyValidityCheck) {
1777-
keyToReturn = @"Enter";
1778-
} else if (leftArrowValidityCheck) {
1779-
keyToReturn = leftArrowPressKey;
1780-
} else if (rightArrowValidityCheck) {
1781-
keyToReturn = rightArrowPressKey;
1782-
} else if (upArrowValidityCheck) {
1783-
keyToReturn = upArrowPressKey;
1784-
} else if (downArrowValidityCheck) {
1785-
keyToReturn = downArrowPressKey;
1786-
} else if (![validKeys containsObject:key]) {
1787-
keyToReturn = nil;
1788-
}
1789-
1790-
return keyToReturn;
1667+
return NO;
17911668
}
17921669

17931670
- (void)keyDown:(NSEvent *)event {
1794-
if (self.onKeyDown == nil) {
1795-
[super keyDown:event];
1796-
return;
1797-
}
1798-
1799-
RCTViewKeyboardEvent *keyboardEvent = [self keyboardEvent:event downPress:YES];
1800-
if (keyboardEvent != nil) {
1801-
[_eventDispatcher sendEvent:keyboardEvent];
1802-
} else {
1671+
if (![self handleKeyboardEvent:event]) {
18031672
[super keyDown:event];
18041673
}
18051674
}
18061675

18071676
- (void)keyUp:(NSEvent *)event {
1808-
if (self.onKeyUp == nil) {
1809-
[super keyUp:event];
1810-
return;
1811-
}
1812-
1813-
RCTViewKeyboardEvent *keyboardEvent = [self keyboardEvent:event downPress:NO];
1814-
if (keyboardEvent != nil) {
1815-
[_eventDispatcher sendEvent:keyboardEvent];
1816-
} else {
1677+
if (![self handleKeyboardEvent:event]) {
18171678
[super keyUp:event];
18181679
}
18191680
}

React/Views/RCTViewKeyboardEvent.h

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,33 +7,11 @@
77
#import <React/RCTComponentEvent.h>
88

99
@interface RCTViewKeyboardEvent : RCTComponentEvent
10-
+ (instancetype)keyDownEventWithReactTag:(NSNumber *)reactTag
11-
capsLockKey:(BOOL)capsLockKey
12-
shiftKey:(BOOL)shiftKey
13-
ctrlKey:(BOOL)controlKey
14-
altKey:(BOOL)optionKey
15-
metaKey:(BOOL)commandKey
16-
numericPadKey:(BOOL)numericPadKey
17-
helpKey:(BOOL)helpKey
18-
functionKey:(BOOL)functionKey
19-
leftArrowKey:(BOOL)leftArrowKey
20-
rightArrowKey:(BOOL)rightArrowKey
21-
upArrowKey:(BOOL)upArrowKey
22-
downArrowKey:(BOOL)downArrowKey
23-
key:(NSString *)key;
2410

25-
+ (instancetype)keyUpEventWithReactTag:(NSNumber *)reactTag
26-
capsLockKey:(BOOL)capsLockKey
27-
shiftKey:(BOOL)shiftKey
28-
ctrlKey:(BOOL)controlKey
29-
altKey:(BOOL)optionKey
30-
metaKey:(BOOL)commandKey
31-
numericPadKey:(BOOL)numericPadKey
32-
helpKey:(BOOL)helpKey
33-
functionKey:(BOOL)functionKey
34-
leftArrowKey:(BOOL)leftArrowKey
35-
rightArrowKey:(BOOL)rightArrowKey
36-
upArrowKey:(BOOL)upArrowKey
37-
downArrowKey:(BOOL)downArrowKey
38-
key:(NSString *)key;
11+
#if TARGET_OS_OSX // TODO(macOS GH#774)
12+
+ (NSDictionary *)bodyFromEvent:(NSEvent *)event;
13+
+ (NSString *)keyFromEvent:(NSEvent *)event;
14+
+ (instancetype)keyEventFromEvent:(NSEvent *)event reactTag:(NSNumber *)reactTag;
15+
#endif // TODO(macOS GH#774)
16+
3917
@end

React/Views/RCTViewKeyboardEvent.m

Lines changed: 58 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -9,69 +9,66 @@
99
#import <React/RCTAssert.h>
1010

1111
@implementation RCTViewKeyboardEvent
12-
// Keyboard mappings are aligned cross-platform as much as possible as per this doc https://github.com/microsoft/react-native-windows/blob/master/vnext/proposals/active/keyboard-reconcile-desktop.md
13-
+ (instancetype)keyDownEventWithReactTag:(NSNumber *)reactTag
14-
capsLockKey:(BOOL)capsLockKey
15-
shiftKey:(BOOL)shiftKey
16-
ctrlKey:(BOOL)controlKey
17-
altKey:(BOOL)optionKey
18-
metaKey:(BOOL)commandKey
19-
numericPadKey:(BOOL)numericPadKey
20-
helpKey:(BOOL)helpKey
21-
functionKey:(BOOL)functionKey
22-
leftArrowKey:(BOOL)leftArrowKey
23-
rightArrowKey:(BOOL)rightArrowKey
24-
upArrowKey:(BOOL)upArrowKey
25-
downArrowKey:(BOOL)downArrowKey
26-
key:(NSString *)key {
27-
RCTViewKeyboardEvent *event = [[self alloc] initWithName:@"keyDown"
28-
viewTag:reactTag
29-
body:@{ @"capsLockKey" : @(capsLockKey),
30-
@"shiftKey" : @(shiftKey),
31-
@"ctrlKey" : @(controlKey),
32-
@"altKey" : @(optionKey),
33-
@"metaKey" : @(commandKey),
34-
@"numericPadKey" : @(numericPadKey),
35-
@"helpKey" : @(helpKey),
36-
@"functionKey" : @(functionKey),
37-
@"ArrowLeft" : @(leftArrowKey),
38-
@"ArrowRight" : @(rightArrowKey),
39-
@"ArrowUp" : @(upArrowKey),
40-
@"ArrowDown" : @(downArrowKey),
41-
@"key" : key }];
42-
return event;
12+
13+
#if TARGET_OS_OSX // TODO(macOS GH#774)
14+
+ (NSDictionary *)bodyFromEvent:(NSEvent *)event
15+
{
16+
NSString *key = [self keyFromEvent:event];
17+
NSEventModifierFlags modifierFlags = event.modifierFlags;
18+
19+
return @{
20+
@"key" : key,
21+
@"capsLockKey" : (modifierFlags & NSEventModifierFlagCapsLock) ? @YES : @NO,
22+
@"shiftKey" : (modifierFlags & NSEventModifierFlagShift) ? @YES : @NO,
23+
@"ctrlKey" : (modifierFlags & NSEventModifierFlagControl) ? @YES : @NO,
24+
@"altKey" : (modifierFlags & NSEventModifierFlagOption) ? @YES : @NO,
25+
@"metaKey" : (modifierFlags & NSEventModifierFlagCommand) ? @YES : @NO,
26+
@"numericPadKey" : (modifierFlags & NSEventModifierFlagNumericPad) ? @YES : @NO,
27+
@"helpKey" : (modifierFlags & NSEventModifierFlagHelp) ? @YES : @NO,
28+
@"functionKey" : (modifierFlags & NSEventModifierFlagFunction) ? @YES : @NO,
29+
};
4330
}
4431

45-
+(instancetype)keyUpEventWithReactTag:(NSNumber *)reactTag
46-
capsLockKey:(BOOL)capsLockKey
47-
shiftKey:(BOOL)shiftKey
48-
ctrlKey:(BOOL)controlKey
49-
altKey:(BOOL)optionKey
50-
metaKey:(BOOL)commandKey
51-
numericPadKey:(BOOL)numericPadKey
52-
helpKey:(BOOL)helpKey
53-
functionKey:(BOOL)functionKey
54-
leftArrowKey:(BOOL)leftArrowKey
55-
rightArrowKey:(BOOL)rightArrowKey
56-
upArrowKey:(BOOL)upArrowKey
57-
downArrowKey:(BOOL)downArrowKey
58-
key:(NSString *)key {
59-
RCTViewKeyboardEvent *event = [[self alloc] initWithName:@"keyUp"
60-
viewTag:reactTag
61-
body:@{ @"capsLockKey" : @(capsLockKey),
62-
@"shiftKey" : @(shiftKey),
63-
@"ctrlKey" : @(controlKey),
64-
@"altKey" : @(optionKey),
65-
@"metaKey" : @(commandKey),
66-
@"numericPadKey" : @(numericPadKey),
67-
@"helpKey" : @(helpKey),
68-
@"functionKey" : @(functionKey),
69-
@"ArrowLeft" : @(leftArrowKey),
70-
@"ArrowRight" : @(rightArrowKey),
71-
@"ArrowUp" : @(upArrowKey),
72-
@"ArrowDown" : @(downArrowKey),
73-
@"key" : key }];
74-
return event;
32+
+ (NSString *)keyFromEvent:(NSEvent *)event
33+
{
34+
NSString *key = event.charactersIgnoringModifiers;
35+
unichar const code = key.length > 0 ? [key characterAtIndex:0] : 0;
36+
37+
if (event.keyCode == 48) {
38+
return @"Tab";
39+
} else if (event.keyCode == 53) {
40+
return @"Escape";
41+
} else if (code == NSEnterCharacter || code == NSNewlineCharacter || code == NSCarriageReturnCharacter) {
42+
return @"Enter";
43+
} else if (code == NSLeftArrowFunctionKey) {
44+
return @"ArrowLeft";
45+
} else if (code == NSRightArrowFunctionKey) {
46+
return @"ArrowRight";
47+
} else if (code == NSUpArrowFunctionKey) {
48+
return @"ArrowUp";
49+
} else if (code == NSDownArrowFunctionKey) {
50+
return @"ArrowDown";
51+
} else if (code == NSBackspaceCharacter || code == NSDeleteCharacter) {
52+
return @"Backspace";
53+
} else if (code == NSDeleteFunctionKey) {
54+
return @"Delete";
55+
}
56+
57+
return key;
58+
}
59+
60+
// Keyboard mappings are aligned cross-platform as much as possible as per this doc https://github.com/microsoft/react-native-windows/blob/master/vnext/proposals/active/keyboard-reconcile-desktop.md
61+
+ (instancetype)keyEventFromEvent:(NSEvent *)event reactTag:(NSNumber *)reactTag
62+
{
63+
// Ignore "dead keys" (key press that waits for another key to make a character)
64+
if (!event.charactersIgnoringModifiers.length) {
65+
return nil;
66+
}
67+
68+
return [[self alloc] initWithName:(event.type == NSEventTypeKeyDown ? @"keyDown" : @"keyUp")
69+
viewTag:reactTag
70+
body:[self bodyFromEvent:event]];
7571
}
72+
#endif // TODO(macOS GH#774)
7673

7774
@end

packages/rn-tester/js/examples/KeyboardEventsExample/KeyboardEventsExample.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ class KeyEventExample extends React.Component<{}, State> {
5252
<View>
5353
{Platform.OS === 'macos' ? (
5454
<View
55-
acceptsKeyboardFocus={true}
56-
validKeysDown={['g', 'Tab', 'Esc', 'Enter', 'ArrowLeft']}
55+
focusable={true}
56+
validKeysDown={['g', 'Tab', 'Escape', 'Enter', 'ArrowLeft']}
5757
onKeyDown={this.onKeyDownEvent}
5858
validKeysUp={['c', 'd']}
5959
onKeyUp={this.onKeyUpEvent}>

0 commit comments

Comments
 (0)