Обратное проектирование NSMenu для элемента строки состояния - PullRequest
1 голос
/ 31 октября 2010

Я хочу создать меню для элемента строки состояния, подобное тому, которое вы видели в приложении PastebotSync Tapbot:

Есть ли у кого-нибудь идеи, как достичь пользовательской области в верхней части меню, которая находится вровень с верхней частью?

Я пробовал / думал о нескольких возможных способах сделать это:

  • Стандартный NSMenuItem с видом - не совпадает с верхней частью меню
  • Некоторый хакерский код для размещения NSWindow над областью в верхней части меню - не очень хорошо, так как он не исчезает с меню, когда оно закрывается
  • Отказаться от NSMenu полностью и использовать вместо этого NSView - еще не пробовал, но я не хочу делать какие-то фальшивые кнопки или что-то, что действует как NSMenuItems

У кого-нибудь есть лучшие идеи или предложения?

Спасибо!

Ответы [ 2 ]

4 голосов
/ 07 марта 2011

На случай, если кто-нибудь придет посмотреть, я разместил решение по этому вопросу на Разрыв выше настраиваемого представления NSMenuItem

Вот код:

@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;
1 голос
/ 01 ноября 2010

У меня была такая же потребность в ранних версиях HoudahSpot 2. Я работал с одним ограничением: мой код оставляет меню с квадратными углами внизу.

С тех пор я отказался от этой настройки, так какфункция BlitzSearch в HoudahSpot стала нуждаться в более сложном пользовательском интерфейсе, я столкнулся с другими ограничениями при использовании NSViews в NSMenu.

В любом случае, вот оригинальный код, который заботится об этих дополнительных 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;
}
...