objective-ccocoansresponder

Why are certain repeated keys ignored by interpretKeyEvents in NSResponder?


I have an NSView that calls interpretKeyEvents for keyDown events. For some keys like the letter 'a', pressing and holding the key results in repeated calls to insertText. For other keys like 'x', insertText is called once and that is it. I would like press and hold to always call insertText. I'm guessing press and hold for these keys is interpreted as something else, but I cannot find it. I have implemented doCommandBySelector as described by the documentation, but that is never called either.

Below is a minimal example program that demonstrates the behaviour.

// clang++ -framework AppKit -o proof-of-concept proof-of-concept.mm

#include <stdio.h>
#import <Cocoa/Cocoa.h>

@interface AppView : NSView<NSTextInputClient>
@end

@implementation AppView

- (void)keyDown:(NSEvent *)event {
    printf("keydown\n");
    [self interpretKeyEvents:[NSArray arrayWithObject:event]];
}

- (void)insertText:(id)string replacementRange:(NSRange)replacementRange {
    printf("insertText\n");
}

- (BOOL)acceptsFirstResponder { return YES; }
- (void)setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange {}
- (void)unmarkText {}
- (NSRange)selectedRange { return NSMakeRange(NSNotFound, 0); }
- (NSRange)markedRange { return NSMakeRange(NSNotFound, 0); }
- (BOOL)hasMarkedText { return NO; }
- (nullable NSAttributedString *)attributedSubstringForProposedRange:(NSRange)range actualRange:(nullable NSRangePointer)actualRange { return nil; }
- (NSArray<NSAttributedStringKey> *)validAttributesForMarkedText { return [NSArray array]; }
- (NSRect)firstRectForCharacterRange:(NSRange)range actualRange:(nullable NSRangePointer)actualRange { return NSMakeRect(0, 0, 0, 0); }
- (NSUInteger)characterIndexForPoint:(NSPoint)point { return 0; }

@end

int main(int argc, char **argv, char **envp, char **apple) {
    [NSApplication sharedApplication];
    [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];

    NSRect windowDimensions = NSMakeRect(0, 0, 300, 300);
    NSWindow *window = [[NSWindow alloc] initWithContentRect:windowDimensions
                                         styleMask:NSWindowStyleMaskTitled | NSWindowStyleMaskResizable
                                         backing:NSBackingStoreBuffered
                                         defer:NO];

    AppView *windowView = [[AppView alloc] initWithFrame:windowDimensions];
    [window setContentView:windowView];
    [window makeKeyAndOrderFront:nil];

    [NSApp run];
    return 0;
}

Is there an explanation for why interpretKeyEvents handles some key repeats by calling insertText and ignores others?


Solution

  • Turns out the issue was with the character accent popover menu. This issue helped me figure it out. No popover was ever displayed for me, hence my confusion. I assume that is because I did not provide any system cursor information therefore the application doesn't know where to display it, however I haven't confirmed this.

    Adding [[NSUserDefaults standardUserDefaults] setBool: NO forKey: @"ApplePressAndHoldEnabled"]; disabled the accent popover for my application and solved the problem.