Подтвердить что ты не робот

Выделение NSMenuItem с пользовательским представлением?

Я создал простой NSStatusBar с NSMenu, установленным в качестве меню. Я также добавил несколько NSMenuItems в это меню, которые отлично работают (включая селекторы и выделение), но как только я добавляю пользовательский вид (setView:) не выделяется подсветка.

CustomMenuItem *menuItem = [[CustomMenuItem alloc] initWithTitle:@"" action:@selector(openPreferences:) keyEquivalent:@""];
[menuItem foo];
[menuItem setTarget:self];
[statusMenu insertItem:menuItem atIndex:0];
[menuItem release];

И мой метод foo:

- (void)foo {
  NSView *view = [[NSView alloc] initWithFrame:CGRectMake(5, 10, 100, 20)];
  [self setView:view];
}

Если я удалю метод setView, он выделит.

Я искал и искал и не могу найти способ реализации/включения этого.

Edit

Я реализовал выделение, выполнив код в этом вопросе в моем подклассе NSView:

Представление NSMenuItem (экземпляр подкласса NSView) не выделяется при наведении

#define menuItem ([self enclosingMenuItem])

- (void) drawRect: (NSRect) rect {
    BOOL isHighlighted = [menuItem isHighlighted];
    if (isHighlighted) {
        [[NSColor selectedMenuItemColor] set];
        [NSBezierPath fillRect:rect];
    } else {
        [super drawRect: rect];
    }
}
4b9b3361

Ответ 1

Если вы добавляете представление в элемент меню, это представление должно рисовать выделение. Боюсь, это бесплатно. Из Темы программирования меню:

Элемент меню с видом не отображает его заголовок, состояние, шрифт или другие стандартные атрибуты чертежа и полностью назначает ответственность за оформление.

Ответ 2

Здесь приведена менее длинная версия выше. Это сработало для меня. (backgroundColour - ivar.)

- (void)drawRect:(NSRect)rect
{
    if ([[self enclosingMenuItem] isHighlighted]) {
        [[NSColor selectedMenuItemColor] set];
    } else if (backgroundColour) {
        [backgroundColour set];
    }
    NSRectFill(rect);
}

Ответ 3

Да, как упоминалось ранее, вы должны сделать это самостоятельно. Я использую AppKit NSDrawThreePartImage (...) для рисования, а также включает проверки, чтобы использовать внешний вид пользовательского элемента управления (синий или графитовый.) Чтобы получить изображения, я просто взял их с экрана (если кто-то знает лучший способ, добавьте комментарий.) Здесь часть моего MenuItemView 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);
}

ИЗМЕНИТЬ

Должно быть определено следующее:

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

Они соответствуют двум вариантам внешнего вида в системных настройках. Кроме того, menuItemFillGray и menuItemFillBlue - это всего лишь NSImages стандартных градиентов заполнения пунктов меню.

Ответ 4

Обновление для 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)
    }
}

Установите один из них в ваш menuItem.view.

(Кредит принадлежит Сэму Соффсу, который помог мне разобраться в этом и прислал мне почти дословно этот код.)