Move NSWindow by dragging NSView that is above other views

I have a macOS application that contains an NSTableView and an NSVisualEffectView. The visual effect view is acting like a bar at the bottom of the window, it is in the table view (containing a few buttons/etc..).


Anyway if I want to move the NSWindow by dragging the visual effect view, it will only work if the table view is not below the visual effect view. The reason I want visual effect view to be above the table view is so that I get a nice blur effect when then the user is scrolling through the table view content.

However, when the visual effect view is above the table view, the mouse/drag/etc events are not registered. Instead, they get passed to the table view. How can I stop this from happening?


I tried subclassing NSVisualEffectView, but everything I have tried has failed. Here is my code:


#import <Cocoa/Cocoa.h>

@interface BottomMainBar : NSVisualEffectView {


} @end


Here is the implementation code:


#import "BottomMainbar.h"

@implementation BottomMainBar


-(void)drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];
   
    [self setWantsLayer:YES];
    [self.window setMovableByWindowBackground:YES];
    [self setAcceptsTouchEvents:YES];
    [self registeredDraggedTypes];
}

-(void)mouseDown:(NSEvent *)event {
    [super mouseDown:event];
}

-(void)mouseDragged:(NSEvent *)event {
    [super mouseDragged:event];
}

-(void)mouseUp:(NSEvent *)event {
    [super mouseUp:event];
}

-(void)mouseEntered:(NSEvent *)event {
    [super mouseEntered:event];
}

-(void)mouseExited:(NSEvent *)event {
    [super mouseExited:event];
}

-(BOOL)mouseDownCanMoveWindow {
    return YES;
}

-(BOOL)acceptsFirstMouse:(NSEvent *)event {
    return YES;
}

@end


Nothing I have tried has worked, how can I stop the visual effect view from passing on the mouse events to the layer below it?


Thanks for your time, Dan.

Replies

>> The visual effect view is … in the table view


You don't mean that, right? According to the rest of your question, it's fixed at the bottom of the window, which means it has to be a sibling of the scroll view that encloses the table view, right? Otherwise the effect view would move when you scroll.


-(void)drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];
  
    [self setWantsLayer:YES];
    [self.window setMovableByWindowBackground:YES];
    [self setAcceptsTouchEvents:YES];
    [self registeredDraggedTypes];
}


Do not — do NOT — do this. The "drawRect" method is called every time the view needs to be redrawn, and you should do the things in lines 4-7 only once. In addition, the documentation for NSVisualEffectView:


https://developer.apple.com/documentation/appkit/nsvisualeffectview


says: "You should not override drawRect: or updateLayer", so you shouldn't have this override at all, and I would take it to imply that "setWantsLayer" is a really bad idea, too.


What I suggest you try is to position a custom NSView above the NSEffectView (un-subclassed), and add the behavior you want to the custom view.

If I'm understanding your question correctly, you may want to implement

-mouseDown:
in your NSVisualEffectView to call the NSWindow method
-[NSWindow performWindowDragWithEvent:]
. This will explicitly start a window drag, and it'll transfer the event tracking to the windowing system in the process.