Обратный инжиниринг NSMenu для элемента строки состояния

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

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

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

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

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

Спасибо!


person Alistair Holt    schedule 31.10.2010    source источник


Ответы (2)


На случай, если кто-нибудь придет посмотреть, я опубликовал решение этой проблемы в Пробел над настраиваемым представлением 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;
person skue    schedule 07.03.2011

У меня была такая же потребность в ранних версиях 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;
}
person Pierre Bernard    schedule 31.10.2010
comment
Код выглядит многообещающе, но мне еще не удалось заставить его работать. Он генерирует много ошибок и предупреждений при сборке в Xcode для меня, возможно, все это сейчас устарело? - person Alistair Holt; 01.11.2010
comment
Я действительно получил его без ошибок после замены [menu carbonMenuRef] на _NSGetCarbonMenu (menu), но, похоже, это не влияет на меню. - person Alistair Holt; 01.11.2010
comment
Важная строка - metrics.top = 0; - person Pierre Bernard; 02.11.2010
comment
Я тоже не могу заставить его работать, кажется, [menu carbonMenuRef] возвращает NULL. - person Joshua; 27.12.2010
comment
Привет, я ищу такое же решение. Я пробовал ваш код, но он дает ошибки. Предоставьте нам образец проекта для приведенного выше кода. Я буду вам благодарен. Спасибо - person AmitSri; 06.07.2011
comment
Привет, я решил EXC_BAD_ACCESS в строке InstallControlEventHandler, заменив InstallControlEventHandler на HIViewInstallEventHandler. Я получил эту подсказку из примеров разработчиков Apple RecentItems. Надеюсь это поможет - person AmitSri; 09.07.2011
comment
Мой текущий SDK для разработки - 10.6. - person AmitSri; 09.07.2011
comment
проверьте и прокомментируйте stackoverflow.com/questions/6633843/ - person AmitSri; 09.07.2011