【问题标题】:Reverse engineering an NSMenu for a Status Bar Item对状态栏项的 NSMenu 进行逆向工程
【发布时间】:2011-05-03 03:19:36
【问题描述】:

我想为状态栏项目创建一个菜单,就像在 Tapbot 的 PastebotSync 应用程序中看到的那样:

有没有人知道如何在菜单顶部实现与顶部齐平的自定义区域?

我已经尝试/想到了几种可能的方法:

  • 带有视图的标准 NSMenuItem - 不与菜单顶部齐平
  • 一些 hack-ish 代码将 NSWindow 放置在菜单顶部的区域上 - 不是很好,因为它不会在菜单关闭时很好地淡出
  • 完全放弃 NSMenu 并改用 NSView - 还没有尝试过,但我真的不想制作一些假按钮或充当 NSMenuItems 的东西

谁有更好的想法或建议?

谢谢!

【问题讨论】:

    标签: cocoa nsmenu nsmenuitem


    【解决方案1】:

    如果有人来看,我在Gap above NSMenuItem custom view 发布了一个解决方案

    代码如下:

    @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
    

    像这样使用它:

    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;
    

    【讨论】:

      【解决方案2】:

      我在 HoudahSpot 2 的早期版本中也有同样的需求。我确实得到了它的一个限制:我的代码在底部留下了方角的菜单。

      我已经放弃了这个设置,因为 HoudahSpot 中的 BlitzSearch 功能需要更复杂的 UI,我在 NSMenu 中使用 NSViews 时遇到了其他限制。

      不管怎样,这里是处理那些额外 3 个像素的原始代码:

      - (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;
      }
      

      对不起,我忘记了:

      - (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;
      }
      

      【讨论】:

      • 代码看起来很有希望,但我还不能让它工作。为我在 Xcode 中构建时会产生很多错误和警告,也许现在所有这些都已弃用?
      • 在将 [menu carbonMenuRef] 替换为 _NSGetCarbonMenu(menu) 后,我实际上已经成功构建了它,但它似乎对菜单没有影响。
      • 重要的一行是“metrics.top = 0;”
      • 我也无法让它工作,似乎[menu carbonMenuRef] 返回 NULL。
      • 嗨,我正在寻找相同的解决方案。我试过你的代码,但它给出了错误。请为我们提供上述代码的示例项目。我会很感激你的。谢谢
      猜你喜欢
      • 1970-01-01
      • 2016-04-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-23
      • 1970-01-01
      • 2011-08-13
      • 2010-09-22
      • 1970-01-01
      相关资源
      最近更新 更多