Show NSPopover without Focus

2019-09-05 20:25发布

问题:

I'm working on creating an IntelliSense style popover that shows the user who is typing into a text field what syntax is valid. Does anyone know how to show an NSPopover without giving it focus, so that the user can continue to type into the textField? The popover is triggered by controlTextDidChange:

- (void) controlTextDidChange:(NSNotification *)obj
{

    NSTextField *field = [obj object];
    NSString *command = [field stringValue];

    if ([[command substringFromIndex: command.length - 1] isEqualToString: @"#"]){
        CompletionMenuController *completionController = [[CompletionMenuController alloc] initWithNibName: @"CompletionMenuController" bundle:[NSBundle mainBundle]];
        completionMenuPopover = [[NSPopoverInformation alloc] init];
        [completionMenuPopover setContentViewController: completionController];
        [completionMenuPopover setContentSize: completionController.view.frame.size];
        [completionMenuPopover setBehavior: NSPopoverBehaviorTransient];
        [completionMenuPopover setAppearance: NSPopoverAppearanceHUD];
        [completionMenuPopover showRelativeToRect:[_commandBar frame] ofView:_commandBar preferredEdge:NSMaxYEdge];
    }
}

回答1:

This is a workaround, not exactly how I was trying to accomplish this, but it works. I save the cursor position in the textfield prior to opening the popover, then give the textfield first responder and change the cursor position back to where it was.

if ([_commandBar stringValue].length > 0){
    NSString *command   = [_commandBar stringValue];
    NSRange range       = [[_commandBar currentEditor] selectedRange];

    //Open popover if command is being typed
    if ([[command substringFromIndex: command.length - 1] isEqualToString: @"#"]){
        CompletionMenuController *completionController = [[CompletionMenuController alloc] initWithNibName: @"CompletionMenuController" bundle:[NSBundle mainBundle]];

        //Configure and Open Popover
        if ([completionMenuPopover isShown]) [completionMenuPopover close];
        completionMenuPopover = [[NSPopover alloc] init];
        [completionMenuPopover setContentViewController: completionController];
        [completionMenuPopover setContentSize: completionController.view.frame.size];
        [completionMenuPopover setBehavior: NSPopoverBehaviorTransient];
        [completionMenuPopover setAppearance: NSPopoverAppearanceHUD];
        [completionMenuPopover setAnimates: NO];
        [completionMenuPopover showRelativeToRect:[_commandBar frame] ofView:_commandBar preferredEdge:NSMaxYEdge];

        //Reset Command Bar as First Responder
        [_commandBar becomeFirstResponder];
        [[_commandBar currentEditor] setSelectedRange: range];
    }
}


回答2:

The auto closing of NSPopover seems to depend on detecting focus changes. That means it requires to set the first responder status to the popover if it is transient. Try NSPopoverBehaviorApplicationDefined and see if that solves the focus issue. You have to take care to close the popover then, however. Explicitely setting back the focs to the edit control is however also a possible option, if there's no other way. Showhing the popover will not change the visual appearance, so there will be no flicker for that short focus switch.