Reverse engineering an NSMenu for a Status Bar Item

前端 未结 2 1785
被撕碎了的回忆
被撕碎了的回忆 2020-12-08 08:50

I\'m want to create a menu for a status bar item like the one seen in Tapbot\'s PastebotSync application:

\"\"<

相关标签:
2条回答
  • 2020-12-08 09:25

    In case anyone comes looking, I posted a solution to this at Gap above NSMenuItem custom view

    Here's the code:

    @interface FullMenuItemView : NSView
    @end
    
    @implementation FullMenuItemView
    - (void) drawRect:(NSRect)dirtyRect
    {
        NSRect fullBounds = [self bounds];
        fullBounds.size.height += 4;
        [[NSBezierPath bezierPathWithRect:fullBounds] setClip];
    
        // Then do your drawing, for example...
        [[NSColor blueColor] set];
        NSRectFill( fullBounds );
    }
    @end
    

    Use it like this:

    CGFloat menuItemHeight = 32;
    
    NSRect viewRect = NSMakeRect(0, 0, /* width autoresizes */ 1, menuItemHeight);
    NSView *menuItemView = [[[FullMenuItemView alloc] initWithFrame:viewRect] autorelease];
    menuItemView.autoresizingMask = NSViewWidthSizable;
    
    yourMenuItem.view = menuItemView;
    
    0 讨论(0)
  • 2020-12-08 09:42

    I had the same need in early versions of HoudahSpot 2. I did get it working with one limitation: my code leaves the menu with square corners at the bottom.

    I have since abandonned this setup, as the BlitzSearch feature in HoudahSpot grew to need a complexer UI, I ran into other limitations with using NSViews in a NSMenu.

    Anyway, here is the original code taking care of those extra 3 pixels:

    - (void)awakeFromNib
    {
     HIViewRef contentView;
     MenuRef menuRef = [statusMenu carbonMenuRef];
     HIMenuGetContentView (menuRef, kThemeMenuTypePullDown, &contentView);
    
     EventTypeSpec hsEventSpec[1] = {
      { kEventClassMenu, kEventMenuCreateFrameView }
     };
    
     HIViewInstallEventHandler(contentView,
              NewEventHandlerUPP((EventHandlerProcPtr)hsMenuCreationEventHandler),
              GetEventTypeCount(hsEventSpec),
              hsEventSpec,
              NULL,
              NULL);
    }
    
    
    #pragma mark -
    #pragma mark Carbon handlers
    
    static OSStatus hsMenuContentEventHandler( EventHandlerCallRef caller, EventRef event, void* refcon )
    {
     OSStatus  err;
    
     check( GetEventClass( event ) == kEventClassControl );
     check( GetEventKind( event ) == kEventControlGetFrameMetrics );
    
     err = CallNextEventHandler( caller, event );
     if ( err == noErr )
     {
      HIViewFrameMetrics  metrics;
    
      verify_noerr( GetEventParameter( event, kEventParamControlFrameMetrics, typeControlFrameMetrics, NULL,
              sizeof( metrics ), NULL, &metrics ) );
    
      metrics.top = 0;
    
      verify_noerr( SetEventParameter( event, kEventParamControlFrameMetrics, typeControlFrameMetrics,
              sizeof( metrics ), &metrics ) );
     }
    
     return err;
    }
    
    static OSStatus hsMenuCreationEventHandler( EventHandlerCallRef caller, EventRef event, void* refcon )
    {
     OSStatus  err = eventNotHandledErr;
    
     if ( GetEventKind( event ) == kEventMenuCreateFrameView)
     {
      err = CallNextEventHandler( caller, event );
      if ( err == noErr )
      {
       static const EventTypeSpec  kContentEvents[] =
       {
        { kEventClassControl, kEventControlGetFrameMetrics }
       };
    
       HIViewRef          frame;
       HIViewRef          content;
    
       verify_noerr( GetEventParameter( event, kEventParamMenuFrameView, typeControlRef, NULL,
               sizeof( frame ), NULL, &frame ) );
       verify_noerr( HIViewFindByID( frame, kHIViewWindowContentID, &content ) );
       HIViewInstallEventHandler( content, hsMenuContentEventHandler, GetEventTypeCount( kContentEvents ),
                kContentEvents, 0, NULL );
      }
     }
    
     return err;
    }
    

    Sorry, I forgot that bit:

    - (MenuRef) carbonMenuRef
    {
        MenuRef carbonMenuRef = NULL;
    
        if (carbonMenuRef == NULL) {
            extern MenuRef _NSGetCarbonMenu(NSMenu *);
            carbonMenuRef = _NSGetCarbonMenu(self);
    
            if (carbonMenuRef == NULL) {
                NSMenu *theMainMenu = [NSApp mainMenu];
                NSMenuItem *theDummyMenuItem = [theMainMenu addItemWithTitle: @"sub"  action: NULL keyEquivalent: @""];
    
                if (theDummyMenuItem != nil) {
                    [theDummyMenuItem setSubmenu:self];
                    [theDummyMenuItem setSubmenu:nil];
                    [theMainMenu removeItem:theDummyMenuItem];
    
                    carbonMenuRef = _NSGetCarbonMenu(self);
                }
            }
        }
    
        if (carbonMenuRef == NULL) {
            extern MenuRef _NSGetCarbonMenu2(NSMenu *);
            carbonMenuRef = _NSGetCarbonMenu2(self);
        }
    
        return carbonMenuRef;
    }
    
    0 讨论(0)
提交回复
热议问题