Search code examples
cocoafocusnstextfield

NSTextField not noticing lost focus when pressing Tab?


I can't seem to find a way to get notified when an NSTextField loses focus by pressing the Tab key. I get a nice textDidEndEditing when clicking another control or when pressing Enter, but not if I change the focus by pressing the Tab key.

Also tried to yank KeyDown and doCommandBySelector for this purpose but I got nowhere.

Any ideas?

Thanks in advance

Edit:

Forgot to mention, but I tried resignFirstResponder too. This is the code I tried:

- (BOOL)resignFirstResponder
{
    NSRunAlertPanel(@"", @"Lost Focus",@"OK", nil, nil);
    return [super resignFirstResponder];
}
- (BOOL)becomeFirstResponder
{
    NSRunAlertPanel(@"", @"Got focus",@"OK", nil, nil);
    return [super becomeFirstResponder];
}

Strangely, what happens here is that when getting focus, both becomeFirstResponder and resignFirstResponder are called one after the other. But when changing focus away from the control, neither are.


Solution

  • Ok, I've found a way to do it: use a window delegate to make the window return a custom field editor. This field editor keeps track of the last TextField that's been activated and calls its textDidEndEditting method when losing firstResponder itself. Here's an example of how to do it:

    #import <Cocoa/Cocoa.h>
    #import <AppKit/AppKit.h>
    
    
    @interface MyTextField : NSTextField
    - (BOOL)resignFirstResponder;
    - (void)textDidEndEditing:(NSNotification *)notification;
    @end
    
    @interface MyFieldEditor : NSTextView
    {
        MyTextField * lastBox;
    }
    -(void) setLastEditBox:(MyTextField*) box;
    @end
    
    @interface MyWindowDelegate : NSWindowController 
    {
        MyFieldEditor *fieldEditor;
    }
    @end
    
    
    
    @implementation MyFieldEditor
    
    -(void) setLastEditBox:(MyTextField*) box{ lastBox = box; }
    
    -(id)init
    {
        if (self = [super init]) 
            [self setFieldEditor:YES];
    
        return self;
    }
    
    - (BOOL)resignFirstResponder
    {
        // Activate the last active editbox editting-end event
        if(lastBox != nil)
        {
            [lastBox textShouldEndEditing:self];
            lastBox = nil;
        }
    
        return [super resignFirstResponder];
    }
    
    @end
    
    
    @implementation MyWindowDelegate
    
    -(id)windowWillReturnFieldEditor:(NSWindow *)sender toObject:(id)client
    {
        if(fieldEditor == nil)  // Return our special field editor
            fieldEditor = [[[MyFieldEditor alloc] autorelease] init];
        return fieldEditor;
    }
    @end
    
    
    @implementation MyTextField
    
    - (BOOL)resignFirstResponder
    {
        // We're losing first responder, inform the field editor that this was the last edit box activated
        MyFieldEditor* myTf = (MyFieldEditor*) [[self window] fieldEditor:YES forObject:self];
        [myTf setLastEditBox:self];
        return [super resignFirstResponder];
    }
    
    - (void)textDidEndEditing:(NSNotification *)notification;
    {
        [super textDidEndEditing:notification];
        [self setStringValue:@"RECEIVED ENDEDITING"];
    }
    
    @end
    
    
    
    
    int main(int argc, char *argv[])
    {   
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        NSApplication *app = [NSApplication sharedApplication];
    
        NSRect frame = NSMakeRect(100, 100, 200, 150);
    
        // Create the window
        NSWindow* window  = [[[NSWindow alloc] autorelease ] initWithContentRect:frame styleMask:NSClosableWindowMask|NSResizableWindowMask
                                                          backing:NSBackingStoreBuffered defer:NO];
    
        [window setDelegate:[[MyWindowDelegate alloc] autorelease]];
    
    
        MyTextField * tf = [ [[ MyTextField alloc ] autorelease] initWithFrame: NSMakeRect( 30.0, 100.0, 150.0, 22.0 ) ];
        [ [ window contentView ] addSubview: tf ];
    
        MyTextField * tf2 = [ [[ MyTextField alloc ] autorelease] initWithFrame: NSMakeRect( 30.0, 40.0, 150.0, 22.0 ) ];
        [ [ window contentView ] addSubview: tf2 ];
    
        [window makeKeyAndOrderFront: window];  
    
        [app run];
        [pool release];
    
        return 0;
    }