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

Проблема, связанная с ярким фоном Yosemite NSMenuItem, содержащим пользовательский вид

Я пытаюсь добавить пользовательский вид в NSMenuItem в строке меню OS X 10.10 Yosemite.

Пользовательский вид - это просто фон NSView с меткой NSTextField.

Проблема заключается в том, что фоновому NSView присваивается яркость/прозрачность стиля Йосемити при добавлении в меню. Метка NSTextfield не является.

Background colors do not match

Благодаря использованию NSRectFillUsingOperation, я получил это, чтобы хорошо выглядеть для некоторых цветов фона в Yosemite. Но другие продолжают не совпадать. Когда он работает, после ручного "выделения" вида исходные цвета меняются и больше не совпадают. Я могу выкопать код примера для этого, если это необходимо.

Затем, когда он выглядит несколько хорошо в Йосемити, он выглядит ужасно в 10.9 Маверикс.

Я также попытался установить для свойства wantsLayer значение YES, чтобы включить представление в CALayer-backed view. Это создает другие проблемы, такие как текст, а не сглаживание правильно на ясном фоне.

Мой вопрос:

Как отобразить метку поверх пользовательского представления NSMenuItem? Фон метки должен точно соответствовать фону представления. Решение должно работать в Йосемити и Маверикс.

Пример кода ниже:

self.statusItem = [[NSStatusBar systemStatusBar]
statusItemWithLength:NSVariableStatusItemLength];
[self.statusItem setTitle:@"TEST"];
[self.statusItem setHighlightMode:YES];
[self.statusItem setEnabled:YES];
[self.statusItem setTarget:self];

NSMenu *menu = [[NSMenu alloc] init];
[menu addItemWithTitle:@"Disabled menu item" action:nil keyEquivalent:@""];
[menu addItemWithTitle:@"Enabled menu item" action:@selector(enabled) keyEquivalent:@""];

NSTextField *label = [[NSTextField alloc] initWithFrame:NSMakeRect(30, 20, 50, 20)];
label.stringValue = @"label";
label.editable = NO;
label.bordered = NO;
label.backgroundColor = [NSColor blueColor];
//label.backgroundColor = [NSColor clearColor];


PKMenuItemView *view = [[PKMenuItemView alloc] initWithFrame:NSMakeRect(0, 0, 200, 50)];
[view addSubview:label];

NSMenuItem *viewMenuItem = [[NSMenuItem alloc] init];
[viewMenuItem setView:view];
[menu addItem:viewMenuItem];

self.statusItem.menu = menu;

Я подклассифицировал NSView, чтобы переопределить drawRect: и нарисовать цветной фон:

- (void)drawRect:(NSRect)dirtyRect {
    [super drawRect:dirtyRect];

    [[NSColor blueColor] setFill];
    NSRectFill(dirtyRect);
    //NSRectFillUsingOperation(dirtyRect, NSCompositeSourceOver);
}
4b9b3361

Ответ 1

Это, конечно, любопытное взломать, но это сработало для меня. Попробуйте добавить NSImageView с пустым изображением в свой собственный вид. Вид изображения должен занимать весь вид.

Using an NSImageView

Ответ 2

Я думаю, что у меня есть менее "хакерское" решение. Это действительно вызвано новым материалом NSVisualEffectView и Vibrancy в Yosemite. Я узнал, что существуют довольно сложные правила рисования представлений, когда они являются подпредставлениями NSVisualEffectView. Об этом говорилось на WWDC 2014 в сеансе 220 - Внедрение расширенных возможностей нового пользовательского интерфейса OS X Yosemite. Я рекомендую вам посмотреть это видео, чтобы получить исчерпывающее объяснение.

Вскоре кажется, что ваша проблема может быть вызвана цветами, которые вы используете. Появились два новых системных цвета - [NSColor labelColor] и [NSColor secondaryLabelColor]. Эти два автоматически настраиваются при рисовании внутри NSVisualEffectView. Кроме того, ваш пользовательский вид должен поддерживать эффект вибрации. Это делается путем переопределения - (BOOL)allowsVibrancy метода - (BOOL)allowsVibrancy и возврата YES.

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

Ответ 3

К сожалению, в Йосемите сейчас несколько проблем. Как уже упоминалось Matthes, вы можете использовать labelColor() и secondaryLabelColor(). Использование этих цветов не заставляет ярлык рисовать странный фон, который вы видите.

Однако labelColor() работает отлично только для VibrantDark, потому что цвет ярлыка белый, когда NSMenuItem выделяется и не выделяется. С VibrantLight labelColor является черным и поэтому очень трудно читать поверх синего выделения.

Для цвета подсветки пользовательского представления вашего NSMenuItem можно подумать, что вы должны использовать selectedMenuItemColor(), учитывая его имя. Проблема заключается в том, что цвет не соответствует цвету выделения меню, который вы видите в NSMenuItems без пользовательского представления. Цвет полностью неправильный как для VibrantLight, так и для VibrantDark.

Tl; dr: Итак, как вы можете создать пользовательский NSMenuItem, который использует тот же цвет текста и цвет подсветки? Вы не можете. Вы должны использовать labelColor() и selectedMenuItemColor(), но первый работает правильно только для VibrantDark, а последний не совпадает вообще.

Я очень надеюсь, что ошибаюсь, потому что я пытаюсь добиться того же: (

Изменить: Здесь - пример проекта, если люди хотят посмотреть.

Ответ 4

Я только что обнаружил, что +[NSColor keyboardFocusIndicatorColor] - правильный цвет (по крайней мере, у El Capitan), тогда как ожидаемый selectedMenuItemColor слишком темный.

Ответ 5

Ответ от технической поддержки Apple Developer, которую я открыл в 2015 году:

Re: DTS Auto-Ack - Яркий фон и выделение пользовательских представлений NSMenuItems

Это сложная проблема, особенно в свете того факта, что рисунок выбора меню не предназначен для элементов меню с пользовательскими представлениями, а чертеж выбора меню (цвета и т.д.) может измениться в будущем. Вот почему мы просим вас сообщать отчеты об ошибках, чтобы выбор меню выполнялся с помощью пользовательских представлений, если их просили, так что будущие изменения в OS X не потребуют от разработчиков постоянного поддержания своего кода для соответствия будущим цветам.

В "Меню приложений и всплывающих списков" говорится следующее:

Виды в пунктах меню -

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

Поскольку все чертежи до разработчика, пользовательские представления в пунктах меню arent обязательно должны рисовать "выбранные".

API-интерфейсы для получения правильного цвета выбора, очевидно, не выполняют то, что его предполагают, следовательно, запрос на получение отчета об ошибке. Хотелось бы, чтобы мы могли предложить более конкретные решения проблемы, но обходной путь, предлагаемый сегодня, может не увенчаться завтра, и мы не хотим ставить плохие прецеденты на обходные пути, которые являются рискованными. Приложения Apple имеют доступ к частным API-интерфейсам более низкого уровня, которые достигают своих результатов. Мы не можем предложить вам эти решения, поскольку они являются частными.

Если выбраноMenuItemColor() не соответствует цвету выделения меню с ярким светом и темным цветом, это ошибка, которая должна быть зарегистрирована и исправлена.

Наконец, Apple рекомендует как можно больше использовать API NSMenuItems для достижения того, что вы хотите в меню. Скриншоты, которые вы включили, могут быть сделаны без применения пользовательских представлений.

Ответ 6

В WWDC 2019 AppKit Lab я работал над этой проблемой с инженерами из команды AppKit.

Они были удивлены, что это не работает по умолчанию, и посоветовали мне подать (больше) радаров:

FB6143574 - Предоставить частный API для NSMenuItem _viewHandlesEvents

Они знали о частном API _viewHandlesEvents на NSMenuItem.

// VibrantMenuBar-Bridging-Header.h

#import <AppKit/AppKit.h>

@interface NSMenuItem ()

@property (setter=_setViewHandlesEvents:) BOOL _viewHandlesEvents;

@end

Установите для viewHandlesEvents значение false и фон пользовательского представления в NSMenuItem будет выбран и отображаться (несколько), как и ожидалось.

Есть еще проблемы с тем, как ярлыки и другие подпредставления реагируют на выбор. Текст Просмотр текста неправильно меняет цвет.

let menuItem = NSMenuItem(title: "", action: nil, keyEquivalent: "")
menuItem.view = label
menuItem._viewHandlesEvents = false

NSMenuItemView with <code>_viewHandlesEvents</code> set to false

В Интернете есть и другие ссылки на _viewHandlesEvents:

Ответ 7

По словам инженеров AppKit на WWDC, это не работает с NSMenuItem. Я добавил этот ответ и на этот вопрос.

Вместо этого они предложили использовать NSPopover для создания faux-NSMenu, присоединенного к помощнику строки меню NSStatusItem.

Использование кода, подобного приведенному ниже, приводит к яркому выбору фона:

override func viewDidLoad() {
    super.viewDidLoad()

    let visualEffectView = NSVisualEffectView()

    visualEffectView.material = .selection
    // .menu or .popover for the non-selected background.

    visualEffectView.state = .active
    visualEffectView.blendingMode = .behindWindow
    visualEffectView.isEmphasized = true

    let label = NSTextField(labelWithString: "Hello, world!")
    label.cell?.backgroundStyle = .emphasized
    visualEffectView.addSubview(label)
    visualEffectView.frame = view.bounds
    label.setFrameOrigin(.zero)
    view.addSubview(visualEffectView)
}

Vibrant Window Background