Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to accept editing and dismiss pop-up menu containing custom view?

I want to add the ability to use a date picker when editing a particular column in my table view, and used the code snippet from here, which worked well. However NSDatePicker is not appropriate for my needs so I am using my own custom view, created using IB and loaded via a NSViewController subclass to edit the date.

However I cannot figure out how to dismiss the pop-up menu in a way that accepts the edit, i.e. returns YES in userAcceptedEdit:

BOOL userAcceptedEdit = [menu popUpMenuPositioningItem:nil
                                            atLocation:frame.origin
                                                inView:tableView];

This was working fine when NSDatePicker was the menu's view, but not with my custom view.

I am trapping the enter key actions from the text fields in my custom view, but all I can figure out is how to cancel the menu tracking which makes userAcceptedEdit == NO:

MyCustomViewController.mm:

- (IBAction)textFieldAction:(id)sender {
    logdbg(@"Action");
    NSMenu* menu = [[self.view enclosingMenuItem] menu];
    [menu cancelTracking];
}

The Views in Menu Items section of Apple's Application Menu and Pop-up List Programming Topics doesn't cover it either...

EDIT Here is a sample project, that demonstrates the issue.

Can someone provide some guidance please?

like image 370
trojanfoe Avatar asked Feb 14 '14 18:02

trojanfoe


2 Answers

You should also be able to set the textFields' delegate to the NSViewController, implement NSTextFieldDelegate within the ViewController and do something like this

- (void)controlTextDidEndEditing:(NSNotification *)aNotification{
    // NSTextField * textField = [aNotification object];
    NSUInteger whyEnd = [[[aNotification userInfo] objectForKey:@"NSTextMovement"] unsignedIntValue];
    if(whyEnd == NSReturnTextMovement){
        // Create new event here using the below routine

        /*
        [[self window] keyDown: [NSEvent keyEventWithType:(NSEventType)type 
                                                 location:(NSPoint)location 
                                            modifierFlags:(NSUInteger)flags 
                                                timestamp:(NSTimeInterval)time 
                                             windowNumber:(NSInteger)windowNum 
                                                  context:(NSGraphicsContext *)context 
                                               characters:(NSString *)characters 
                              charactersIgnoringModifiers:(NSString *)unmodCharacters 
                                                isARepeat:(BOOL)repeatKey
                                                  keyCode:(unsigned short)code]
         ];
         */ 
    }
 }

Here you are essentially TRANSLATING the notification to an EVENT by creating a NEW event to pass along to the parent view

Should also be noted that this becomes the central "dispatch" return catcher for all textfields.

Here is a great link on using the NSEvent creation methods: http://advinprog.blogspot.com/2008/06/so-you-want-to-post-keyboard-event-in.html

Notice in this writeup how the simulate a key_down and a key_up!!!

like image 83
trumpetlicks Avatar answered Oct 30 '22 18:10

trumpetlicks


Ha! Did it. Changed the NSTextField to NSTextView, subclassed it, and here we go:

@interface LNTextView : NSTextView

@end

@implementation LNTextView

- (void)keyDown:(NSEvent *)theEvent
{
    if(theEvent.keyCode == 36)
    {
        [[self window] keyDown:theEvent];
    }
}

@end

I noticed that, normally, when enter key is pressed on NSDatePicker, the keyDown: eventually goes to the menu's window and then it is accepted. So this is what I did here. NSTextField uses a text view internally, so it cannot hear keyDown: messages, thus the need to switch to full blown NSTextView instead.

You could still use your text field, and create a new NSEvent using + (NSEvent *)keyEventWithType:(NSEventType)type location:(NSPoint)location modifierFlags:(NSUInteger)flags timestamp:(NSTimeInterval)time windowNumber:(NSInteger)windowNum context:(NSGraphicsContext *)context characters:(NSString *)characters charactersIgnoringModifiers:(NSString *)unmodCharacters isARepeat:(BOOL)repeatKey keyCode:(unsigned short)code in your action method, passing it to the field's window instead of calling cancelTracking on the menu.

like image 36
Léo Natan Avatar answered Oct 30 '22 18:10

Léo Natan