2014-10-24 16 views
5

Khi App Store có bản cập nhật, nó cho thấy một yếu tố phong cách nội tuyến trong mục menu, giống như '1 mới' trong hình dưới đây:Làm thế nào để vẽ một nhãn kiểu inline (hoặc nút) bên NSMenuItem

enter image description here

Một địa điểm khác mà chúng ta có thể thấy loại thực đơn này là 10.10 menu chia sẻ của Yosemite. Khi bạn cài đặt bất kỳ ứng dụng nào thêm tiện ích mở rộng chia sẻ mới, mục 'Khác' từ trình đơn chia sẻ sẽ hiển thị 'N mới' giống như menu cửa hàng ứng dụng.

Mục 'App Store ...' trông có vẻ là bình thường NSMenuItem. Có cách nào dễ dàng để thực hiện điều này hoặc có bất kỳ API nào hỗ trợ nó mà không thiết lập chế độ xem tùy chỉnh cho mục menu không?

+0

+1. Ngoài ra tìm kiếm câu trả lời. Có vẻ như bạn có thể thiết lập NSView để hiển thị thay cho tiêu đề NSMenuItem thông thường. Nhưng đây không phải là cách tôi gọi là 'dễ dàng'. – UJey

+0

Bất kỳ may mắn nào với điều này? – mileusna

+0

@mileusna Tôi chưa thử giải pháp của BonzaiThePenguin từ câu trả lời, điều này có thể hoạt động tốt. –

Trả lời

2

NSMenus "Cocoa" thực sự được xây dựng hoàn toàn trên Carbon, vì vậy trong khi API Cocoa không hiển thị nhiều chức năng bạn có thể nhúng xuống vào vùng đất Carbon và nhận quyền truy cập nhiều hơn. Đó là những gì Apple đã làm, dù sao - các mục menu Apple đang subclassed từ IBCarbonMenuItem, như có thể thấy ở đây:

/System/Library/Frameworks/Carbon.framework/Versions/A/Frameworks/HIToolbox.framework/Versions/A/Resources/English.lproj/StandardMenus.nib/objects.xib 

Đáng tiếc là các API Carbon 64-bit dường như thủng với lỗi và chức năng bị mất, mà làm cho nó nhiều khó cài đặt trình xử lý vẽ hoạt động hơn so với phiên bản 32 bit. Dưới đây là một phiên bản hacky tôi đến với:

#import <Carbon/Carbon.h> 

OSStatus eventHandler(EventHandlerCallRef inHandlerRef, EventRef inEvent, void *inUserData) { 
    OSStatus ret = 0; 

    if (GetEventClass(inEvent) == kEventClassMenu) { 
    if (GetEventKind(inEvent) == kEventMenuDrawItem) { 
     // draw the standard menu stuff 
     ret = CallNextEventHandler(inHandlerRef, inEvent); 

     MenuTrackingData tracking_data; 
     GetMenuTrackingData(menuRef, &tracking_data); 

     MenuItemIndex item_index; 
     GetEventParameter(inEvent, kEventParamMenuItemIndex, typeMenuItemIndex, nil, sizeof(item_index), nil, &item_index); 

     if (tracking_data.itemSelected == item_index) { 
     HIRect item_rect; 
     GetEventParameter(inEvent, kEventParamMenuItemBounds, typeHIRect, nil, sizeof(item_rect), nil, &item_rect); 

     CGContextRef context; 
     GetEventParameter(inEvent, kEventParamCGContextRef, typeCGContextRef, nil, sizeof(context), nil, &context); 

     // first REMOVE a state from the graphics stack, instead of pushing onto the stack 
     // this is to remove the clipping and translation values that are completely useless without the context height value 
     extern void *CGContextCopyTopGState(CGContextRef); 
     void *state = CGContextCopyTopGState(context); 

     CGContextRestoreGState(context); 

     // draw our content on top of the menu item 
     CGContextSetRGBFillColor(context, 0.0, 0.0, 0.0, 0.5); 
     CGContextFillRect(context, CGRectMake(0, item_rect.origin.y - tracking_data.virtualMenuTop, item_rect.size.width, item_rect.size.height)); 

     // and push a dummy graphics state onto the stack so the calling function can pop it again and be none the wiser 
     CGContextSaveGState(context); 
     extern void CGContextReplaceTopGState(CGContextRef, void *); 
     CGContextReplaceTopGState(context, state); 

     extern void CGGStateRelease(void *); 
     CGGStateRelease(state); 
     } 
    } 
    } 
} 

- (void)beginTracking:(NSNotification *)notification { 
    // install a Carbon event handler to custom draw in the menu 
    if (menuRef == nil) { 
    extern MenuRef _NSGetCarbonMenu(NSMenu *); 
    extern EventTargetRef GetMenuEventTarget(MenuRef); 

    menuRef = _NSGetCarbonMenu(menu); 
    if (menuRef == nil) return; 

    EventTypeSpec events[1]; 
    events[0].eventClass = kEventClassMenu; 
    events[0].eventKind = kEventMenuDrawItem; 

    InstallEventHandler(GetMenuEventTarget(menuRef), NewEventHandlerUPP(&eventHandler), GetEventTypeCount(events), events, nil, nil); 
    } 

    if (menuRef != nil) { 
    // set the kMenuItemAttrCustomDraw attrib on the menu item 
    // this attribute is needed in order to receive the kMenuEventDrawItem event in the Carbon event handler 
    extern OSStatus ChangeMenuItemAttributes(MenuRef, MenuItemIndex, MenuItemAttributes, MenuItemAttributes); 
    ChangeMenuItemAttributes(menuRef, item_index, kMenuItemAttrCustomDraw, 0); 
    } 
} 

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { 
    menu = [[NSMenu alloc] initWithTitle:@""]; 

    // register for the BeginTracking notification so we can install our Carbon event handler as soon as the menu is constructed 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(beginTracking:) name:NSMenuDidBeginTrackingNotification object:menu]; 
} 

Đầu tiên nó đăng ký cho một thông báo BeginTracking, như _NSGetCarbonMenu chỉ trả về một xử lý hợp lệ sau khi trình đơn đã được xây dựng và BeginTracking được gọi trước khi thực đơn được rút ra.

Sau đó, nó sử dụng gọi lại thông báo để nhận MenuRef Carbon và đính kèm trình xử lý sự kiện Carbon tiêu chuẩn vào menu.

Thông thường chúng ta chỉ cần lấy tham số sự kiện kEventParamMenuContextHeight và lật CGContextRef và bắt đầu vẽ, nhưng thông số đó chỉ có sẵn ở chế độ 32 bit. Tài liệu của Apple khuyến nghị sử dụng chiều cao của cổng hiện tại khi giá trị đó không có sẵn, nhưng điều đó cũng chỉ có ở chế độ 32 bit.

Vì vậy, vì trạng thái đồ họa được cung cấp cho chúng tôi là vô ích, hãy bật từ ngăn xếp và sử dụng trạng thái đồ họa trước đó. Nó chỉ ra rằng trạng thái mới này được dịch sang đầu ảo của menu, có thể được lấy ra bằng cách sử dụng GetMenuTrackingData.virtualMenuTop. Giá trị kEventParamVirtualMenuTop cũng không chính xác ở chế độ 64 bit nên nó phải sử dụng GetMenuTrackingData.

Đó là hacky và vô lý, nhưng nó tốt hơn so với sử dụng setView và reimplementing toàn bộ hành vi mục menu. API trình đơn trên OS X là một số bit của một mớ hỗn độn.

Các vấn đề liên quan