Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use a NSStatusItem as a drag destination?

I'm trying to build an application that allows users to drag files from Finder to the menubar icon for processing. I've made progress in my journey, but I can't seem to summit this hill. I tried subclassing NSView and implementing the drag messages.

@interface CMDroppableView : NSView <NSMenuDelegate>

I wanted to not only accept drag operations, but to provide a NSMenu when the user clicks the icon. I've managed to get the NSMenu to display properly, but the drag functionality remains elusive.

It's my understanding that I needed to register the accepted drag types which I have done here:

-(void)awakeFromNib{
[self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
}

Drag messages:

-(NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender{
    NSLog(@"Drag Enter");
    return NSDragOperationCopy;
}

-(NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender{
    return NSDragOperationCopy;
}

-(void)draggingExited:(id <NSDraggingInfo>)sender{
    NSLog(@"Drag Exit");
}

-(BOOL)prepareForDragOperation:(id <NSDraggingInfo>)sender{
     return YES;
}

-(BOOL)performDragOperation:(id <NSDraggingInfo>)sender{
    return YES;
}

Here is the code where the custom view is set:

statusItemView = [[CMDroppableView alloc] init];
[statusItemView retain];
[statusItemView setMenu: statusMenu];

[statusItem setView: statusItemView];   

Still nothing. So where have I gone wrong?

Thanks!

like image 975
doomspork Avatar asked May 14 '11 23:05

doomspork


2 Answers

Through trial and error I stumbled onto the solution. I was registering my acceptable drag types within the awakeFromNib message (as seen in the original question). However, I was not using IB to set this view up so this method was never called. Moving the registration code to the initFromFrame message seemed to fix the problem.

- (id)initWithFrame:(NSRect)frame {
    self = [super initWithFrame:frame];
    if (self) {
        ...
        [self registerForDraggedTypes:[NSArray arrayWithObject:NSFilenamesPboardType]];
    }
    return self;
}
like image 105
doomspork Avatar answered Sep 28 '22 05:09

doomspork


Edit: D'oh, you're registering the drag types in -awakeFromNib, which won't be called if the view isn't being loaded from a nib. Try registering your drag types in -initWithFrame: instead!

Old answer:

IIRC you need to not set the menu on the status item. What I do is have my custom view manage a menu, and do something like this:

- (void)setMenu:(NSMenu *)menu {
    [menu setDelegate:self];
    [super setMenu:menu];
}

- (void)mouseDown:(NSEvent *)event {
    [statusItem popUpStatusItemMenu:[self menu]]; // or another method that returns a menu
}

- (void)menuWillOpen:(NSMenu *)menu {
    highlight = YES;
    [self setNeedsDisplay:YES];
}

- (void)menuDidClose:(NSMenu *)menu {
    highlight = NO;
    [self setNeedsDisplay:YES];
}

- (void)drawRect:(NSRect)rect {
    NSImage *img = highlight ? [alternateImage copy] : [image copy];
    NSRect bounds = [self bounds];
    [statusItem drawStatusBarBackgroundInRect:bounds withHighlight:highlight];
    
    // rest of drawing code goes here, including drawing img where appropriate
}

in my custom view's implementation. This ensures the menu behavior is identical to the default.

like image 44
Wevah Avatar answered Sep 28 '22 06:09

Wevah