NSMenuItem自定义视图上方的间隙问题

19

我正在使用setView:方法设置自定义视图的NSMenuItem。在这个自定义视图中,有一个图片占据了整个视图。这个带有自定义视图的NSMenuItem是菜单中的第一个,但问题是它与菜单顶部不对齐,存在一个大的空白间隙,如下所示:

alt text

为什么会出现这种情况,我应该如何解决?


编辑

我现在正在使用这段代码,但在InstallControlEventHandler一行上我遇到了EXC_BAD_ACCESS错误。

-(void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    HIViewRef contentView;
    MenuRef menuRef = [statusMenu carbonMenuRef];

    HIMenuGetContentView(menuRef, kThemeMenuTypePullDown, &contentView);

    EventTypeSpec hsEventSpec[1] = {
        { kEventClassMenu, kEventMenuCreateFrameView }
    };

    InstallControlEventHandler(contentView,
                           NewEventHandlerUPP((EventHandlerProcPtr)hsMenuCreationEventHandler),
                           GetEventTypeCount(hsEventSpec),
                           hsEventSpec,
                           NULL,
                           NULL); // Get EXC_BAD_ACCESS here.
}

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 ) );
            InstallControlEventHandler( content, hsMenuContentEventHandler, GetEventTypeCount( kContentEvents ),
                                       kContentEvents, 0, NULL );
        }
    }

    return err;
}

还要注意这一行代码metrics.top = 0可以消除顶部的间隙。但是我在这一步无法正常工作。有人知道为什么会出现EXC_BAD_ACCESS吗?我已经创建并分配了statusMenu,所以它应该可以正常工作才对?


我假设黑色部分是图像,而不是间隙?由于美观原因,在菜单顶部和底部之间以及分隔符项之间有一些填充。我不确定这是在NSMenu还是NSMenuItem中完成的,但您可能需要对其中一个进行子类化以防止它发生。 - d11wtq
HIMenuGetContentView() 返回什么?我猜它返回的不是 noErr,然后你试图在一个垃圾指针上安装事件处理程序。 - uliwitness
1
Joshua:我通过将“InstallControlEventHandler”函数调用替换为“HIViewInstallEventHandler”使上述代码工作。如果有帮助,请告诉我。 - AmitSri
请注意:我已在10.6中使用了上述代码。 - AmitSri
@AmitSri 非常感谢,那个完美地解决了问题,很高兴能得到一个与我最初尝试相似的解决方案!再次感谢! - Joshua
显示剩余5条评论
1个回答

16

您的帖子标记为"Objective-C"和"Cocoa",但您的示例代码是C和Carbon。我假设您更喜欢使用Cocoa解决方案?

在Cocoa中,实际上非常简单。唯一的技巧是学习如何超越界限绘制。 :-)

@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;

运行得非常好!只是发现你的代码中有一个错别字,你说的是 initWithRect:,但应该是 initWithFrame:。非常感谢,我从未想过这会如此简单! - Joshua
joshua: 如果仍需要以上代码在没有 EXC_BAD_ACCESS 的情况下工作,请查看以下文章 http://stackoverflow.com/questions/6633843/how-to-remove-nsmenuitem-gap-above-custom-view-solved。 - AmitSri
3
不关心AmitSri做了什么,但为了澄清其他人... setClip是公共且受支持的。苹果唯一的警告是如果您使用它,视图可能会绘制在它们的边界之外,这正是我们想要做的。相比之下,AmitSri引用的Carbon代码使用了不推荐使用的(HIMenuGetContentView)和私有的(_NSGetCarbonMenu)API。 - skue
1
你不应该使用蓝色 - 有些人(比如我)使用石墨色方案。(:最好使用[NSColor selectedMenuItemColor],它应该为你选择正确的颜色。 - SilverWolf
嘿,这个解决方案对我不起作用 :( 我有一个自定义的NSView,我正在将其用作NSMenuItem的视图。在我的自定义NSView中,我已经按照您建议的方式实现了drawRect。但问题仍未解决 :(请有人帮帮我!! - Ananth Kamath
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接