2

I've been reading the Cocoa Event Handling Guide to understand how to handle keyboard events for my application. However, I'm not quite sure if the solution I've found is the idiomatic way to approach this problem or even the best.

I have a view called CustomView and controller called CustomViewController that contains a NSTextView (within a NSScrollView) and a NSTextField. Basically a chat window.

What I would like is that even if the NSTextField is not selected, and the user starts typing, the NSTextField should be made the first responder and the text the user types should be appended to the NSTextField.

I've come up with a solution (below) where my RootWindowController captures keyDown events, makes the NSTextField the first responder, gets the field editor, and appends the text.

Is this the idiomatic approach Cocoa developers take or is their a simpler way that I am missing? Code is below. Thanks.

RootWindowController.m

#import "RootWindowController.h"

@implementation RootWindowController

- (id)initWithWindow:(NSWindow *)window
{
    self = [super initWithWindow:window];
    if (self) {
        self.customViewController = [[CustomViewController alloc] init];

        NSView *contentView = self.window.contentView;
        CGFloat viewWidth = contentView.frame.size.width;
        CGFloat viewHeight = contentView.frame.size.height;

        self.customViewController.customView.textField.frame =
            NSMakeRect(0, 0, viewWidth, 50);
        self.customViewController.customView.scrollView.frame = 
            NSMakeRect(0, 51, viewWidth, viewHeight - 50);
        self.customViewController.customView.textView.frame = 
            NSMakeRect(0, 51, viewWidth, viewHeight - 50);

       self.window.contentView = self.customViewController.customView;
    }

    return self;
}

- (void)keyDown:(NSEvent *)theEvent
{    
    [self.window 
       makeFirstResponder:self.customViewController.customView.textField];

    NSString *characters = theEvent.characters;    
    NSText *fieldEditor = [self.window fieldEditor:YES
       forObject:self.customViewController.customView.textField];
    [fieldEditor setString:
       [NSString stringWithFormat:@"%@%@", fieldEditor.string, characters]];
    [fieldEditor setSelectedRange:NSMakeRange(fieldEditor.string.length, 0)];
    [fieldEditor setNeedsDisplay:YES];
}

@end

CustomViewController.m

#import "CustomViewController.h"

@implementation CustomViewController

- (id)init
{
    self = [super init];
    if (self) {
        self.customView = [[CustomView alloc] initWithFrame:NSZeroRect];
        [self.customView setAutoresizesSubviews:YES];

        self.view = self.customView;
    }
    return self;
}

@end

CustomView.m

#import "CustomView.h"

@implementation CustomView

- (id)initWithFrame:(NSRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        self.scrollView = [[NSScrollView alloc] initWithFrame:NSZeroRect];
        self.textView   = [[NSTextView alloc] initWithFrame:NSZeroRect];
        self.textField  = [[NSTextField alloc] initWithFrame:NSZeroRect];

        [self.textView setAutoresizesSubviews:YES];

        [self.scrollView setDocumentView:self.textView];
        [self.scrollView setHasVerticalScroller:YES];
        [self.scrollView setAutoresizesSubviews:YES];

        [self.textField setStringValue:@"Placeholder Text"];

        [self addSubview:self.scrollView];
        [self addSubview:self.textField];

        [self.textView setNeedsDisplay:YES];
    }
    return self;
}

- (BOOL)acceptsFirstResponder
{
    return YES;
}

@end
John Drake
  • 183
  • 1
  • 8
  • Wow, 8 years and nothing. This does sound almost like the correct way to handle this. But I'd put the event handler in the `superview` immediately above the `NSTextView` and `NSFieldView` as long as the text view (and scroll view) doesn't respond to keyEvents it will pass the event to the next responder in the chain, which you've said is your custom view, put the `-(void) keyDown:(NSEvent*)event` in there. – silicontrip Apr 25 '21 at 03:06

0 Answers0