Highlighting a NSMenuItem with a custom view?

前端 未结 4 1681
抹茶落季
抹茶落季 2020-12-25 14:08

I have created a simple NSStatusBar with a NSMenu set as the menu. I have also added a few NSMenuItems to this menu, which work fine (

相关标签:
4条回答
  • 2020-12-25 14:38

    If you're adding a view to a menu item, that view has to draw the highlight itself. You don't get that for free, I'm afraid. From the Menu Programming Topics:

    A menu item with a view does not draw its title, state, font, or other standard drawing attributes, and assigns drawing responsibility entirely to the view.

    0 讨论(0)
  • 2020-12-25 14:54

    Update for 2019:

    class CustomMenuItemView: NSView {
        private var effectView: NSVisualEffectView
    
        override init(frame: NSRect) {
            effectView = NSVisualEffectView()
            effectView.state = .active
            effectView.material = .selection
            effectView.isEmphasized = true
            effectView.blendingMode = .behindWindow
    
            super.init(frame: frame)
            addSubview(effectView)
            effectView.frame = bounds
        }
    
        required init?(coder decoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        override func draw(_ dirtyRect: NSRect) {
            effectView.isHidden = !(enclosingMenuItem?.isHighlighted ?? false)
        }
    }
    

    Set one of those to your menuItem.view.

    (Credit belongs to Sam Soffes who helped me figure this out and sent me almost that code verbatim.)

    0 讨论(0)
  • 2020-12-25 14:58

    Yes, as mentioned earlier you must draw it yourself. I use AppKit's NSDrawThreePartImage(…) to draw, and also include checks to use the user's control appearance (blue or graphite.) To get the images, I just took them from a screenshot (if anyone knows a better way, please add a comment.) Here's a piece of my MenuItemView's drawRect:

        // draw the highlight gradient
    if ([[self menuItem] isHighlighted]) {
    
        NSInteger tint = [[NSUserDefaults standardUserDefaults] integerForKey:@"AppleAquaColorVariant"];
        NSImage *image = (AppleAquaColorGraphite == tint) ? menuItemFillGray : menuItemFillBlue;
    
        NSDrawThreePartImage(dirtyRect, nil, image, nil, NO,
            NSCompositeSourceOver, 1.0, [self isFlipped]);
    }
    else if ([self backgroundColor]) {
    
        [[self backgroundColor] set];
        NSRectFill(dirtyRect);
    }
    

    EDIT

    Should have defined these:

    enum AppleAquaColorVariant {
        AppleAquaColorBlue = 1,
        AppleAquaColorGraphite = 6,
    };
    

    These correspond to the two appearance options in System Preferences. Also, menuItemFillGray & menuItemFillBlue are just NSImages of the standard menu item fill gradients.

    0 讨论(0)
  • 2020-12-25 15:02

    Here's a rather less long-winded version of the above. It's worked well for me. (backgroundColour is an ivar.)

    - (void)drawRect:(NSRect)rect
    {
        if ([[self enclosingMenuItem] isHighlighted]) {
            [[NSColor selectedMenuItemColor] set];
        } else if (backgroundColour) {
            [backgroundColour set];
        }
        NSRectFill(rect);
    }
    
    0 讨论(0)
提交回复
热议问题