The solution is to keep track of which key is down yourself. Override -keyDown
and -keyUp
to keep track of which keys are being held down. I’m using a C++ unordered_set
for that, but an Objective-C NSIndexSet
would work just as well:
@interface ICGMapView : NSView
{
std::unordered_set pressedKeys;
}
@end
and in the implementation:
-(void) keyDown:(NSEvent *)theEvent
{
NSString * pressedKeyString = theEvent.charactersIgnoringModifiers;
unichar pressedKey = (pressedKeyString.length > 0) ? [pressedKeyString characterAtIndex: 0] : 0;
if( pressedKey )
pressedKeys.insert( pressedKey );
}
-(void) keyUp:(NSEvent *)theEvent
{
NSString * pressedKeyString = theEvent.charactersIgnoringModifiers;
unichar pressedKey = (pressedKeyString.length > 0) ? [pressedKeyString characterAtIndex: 0] : 0;
if( pressedKey )
{
auto foundKey = pressedKeys.find( pressedKey );
if( foundKey != pressedKeys.end() )
pressedKeys.erase(foundKey);
}
}
Then add an NSTimer
to your class that periodically checks whether there are any keys pressed, and if they are, reacts to them:
-(void) dispatchPressedKeys: (NSTimer*)sender
{
BOOL shiftKeyDown = pressedKeys.find(ICGShiftFunctionKey) != pressedKeys.end();
for( unichar pressedKey : pressedKeys )
{
switch( pressedKey )
{
case 'w':
[self moveUp: self fast: shiftKeyDown];
break;
...
}
}
}
Since your timer is polling at an interval here, and you can’t make that interval too fast because it’s the rate at which key repeats will be sent, it is theoretically possible that you would lose keypresses whose duration is shorter than your timer interval. To avoid that, you could store a struct in an array instead of just the keypress in a set. This struct would remember when the key was originally pressed down, and when the last key event was sent out.
That way, when the user begins holding down a key, you’d immediately trigger processing of this key once, and make note of when that happened. From then on, your -dispatchPressedKeys:
method would check whether it’s been long enough since the last time it processed that particular key, and would send key repeats for each key that is due. As a bonus, when a key is released, you could also notify yourself of that.