Search code examples
cocoaquicklook

Prevent QLPreviewView from grabbing focus


I have a list of files. Next to it I have a QLPreviewView which shows the currently selected file.

Unfortunately QLPreviewView loads a web view to preview bookmark files. Some web pages can grab keyboard focus. E.g. the Gmail login form places the insertion point into the user name field.

This breaks the flow of my application. I want to navigate my list using arrow keys. This is disrupted when keyboard focus is taken away from the table view.

So far the best I could come up with is to override - [NSWindow makeFirstResponder:] and not call super for instances of classes named with a QL prefix. Yuck.

Is there a more reasonable way to

  • Prevent unwanted changes of first responder?
  • or prevent user interaction on QLPreviewView and its subviews?

Solution

  • I ended up using a NSWindow subclass that allows QLPreviewViews and its private subviews to become first responder on user interaction, but prevents these views from simply stealing focus.

    - (BOOL)makeFirstResponder:(NSResponder *)aResponder
    {
        NSString *classname = NSStringFromClass([aResponder class]);
    
        // This is a hack to prevent Quick Look from stealing first responder
        if ([classname hasPrefix:@"QL"]) {
            BOOL shouldMakeFirstRespnder = NO;
            NSEvent *currentEvent = [[NSApplication sharedApplication] currentEvent] ;
            NSEventType eventType = currentEvent.type;
    
            if ((eventType == NSLeftMouseDown) || (eventType == NSRightMouseDown) || (eventType == NSMouseEntered)) {
                if ([aResponder isKindOfClass:[NSView class]]) {
                    NSView *view = (NSView *)aResponder;
                    NSPoint locationInWindow = currentEvent.locationInWindow;
                    NSPoint locationInView = [view convertPoint:locationInWindow fromView:nil];
                    BOOL pointInRect = NSPointInRect(locationInView, [view bounds]);
    
                    shouldMakeFirstRespnder = pointInRect;
                }
            }
    
            if (!shouldMakeFirstRespnder) {
                return NO;
            }
        }
    
        return [super makeFirstResponder:aResponder];
    }