NSStatusBarButton оставайся выделенным
Начиная с OS X 10.10 большая часть NSStatusItem устарела в пользу button
свойство, которое состоит из NSStatusBarButton. Он должен работать как обычная кнопка, но, к сожалению, cell
а также setCell
методы в NSStatusButton также устарели. В результате этого я изо всех сил пытаюсь найти способ, чтобы кнопка оставалась выделенной после нажатия (обычно кнопка подсвечивается при нажатии мыши вниз и не подсвечивается при нажатии вверх мыши. Я хочу, чтобы она оставалась подсвеченной после нажатия кнопки мыши вверх).
призвание [NSStatusButton setHighlighted:]
в его действии не работает, потому что он, кажется, не выделяет себя, когда мышь поднята. С другой стороны, используя задержку для вызова в следующем цикле, т.е. [self performSelector: withDelay:]
заставляет основной момент мигать довольно неприглядным способом. Это работает, но не выглядит красиво.
Установка типа кнопки на NSToggleButton
полностью удаляет подсветку и вместо этого выделяет шаблонное изображение, которое было странным.
Это были единственные методы, о которых я мог подумать. Есть ли способ переопределить это поведение мыши NSButtonCell?
10 ответов
Я добавил подпредставление к элементу состояния, и в этом представлении я добавил обработчики событий для mouseDown и т. Д., Которые называются [[statusItem button] highlight:true]. Как оказалось, setHighlighted: не делает то же самое, что и highlight:.
NSArray *array = [NSArray arrayWithObjects:[statusItem button], [self statusItemView], nil];
[[[statusItem button] superview] setSubviews:array];
//Highlight like so:
[[statusItem button] highlight:true];
РЕДАКТИРОВАТЬ: На El Capitan этот метод больше не работает, и не работает statusItem.button.highlight = true
или
Вот еще один вариант. Не установлен NSStatusItem
"s action
имущество. Вместо этого добавьте локальный монитор событий:
[NSEvent addLocalMonitorForEventsMatchingMask:(NSLeftMouseDown | NSRightMouseDown)
handler:^NSEvent *(NSEvent *event) {
if (event.window == self.statusItem.button.window) {
[self itemClicked];
return nil;
}
return event;
}];
Затем в -itemClicked
выделите кнопку, используя highlight:
метод:
- (void)itemClicked {
[self.statusItem.button highlight:YES];
// Do other stuff
}
Чтобы выделить, просто нажмите кнопку highlight:NO
где вам нужно.
Свифт 3 версия ответа Manfred Urban. Работает на El Capitan.
extension NSStatusBarButton {
public override func mouseDown(_ event: NSEvent) {
if (event.modifierFlags.contains(NSControlKeyMask)) {
self.rightMouseDown(event)
return
}
self.highlight(true)
(self.target as? TrivialTargetClass)?.togglePopover()
}
}
Не забудьте снова установить для свойства подсветки кнопок значение false, если это необходимо.
Борясь с этой проблемой сам, я обнаружил, что перезапись mouseDown:
в категории на NSStatusBarButton
работает:
#import "MUTargetClass.h"
@implementation NSStatusBarButton (Additions)
- (void)mouseDown:(NSEvent *)theEvent
{
// Relay CTRL+Click to perform a right click action
if(theEvent.modifierFlags & NSControlKeyMask)
{
[self rightMouseDown:theEvent];
return;
}
// Handle highlighting
[self setHighlighted:YES];
// Perform action on target
[(MUTargetClass *)self.target actionSelector:self];
}
@end
MUTargetClass
мог бы, например, реализовать:
#import "NSStatusBarButton+Additions.h"
@implementation MUTargetClass
[…]
self.statusItem = [[NSStatusBar systemStatusBar] statusItemWithLength:NSVariableStatusItemLength];
NSStatusBarButton *button = [self.statusItem button];
[button setTarget:self];
[…]
- (void)actionSelector:(id)sender
{
// Whatever behavior a click on the button should invoke
}
[…]
// Reset button's highlighting status when done
[[self.statusItem button] setHighlighted:NO];
[…]
@end
Обратите внимание, что функциональность CTRL+ нажатие на кнопку теряется в mouseDown:
-override. Как показано выше, его можно восстановить, передав событие rightMouseDown:
,
Более простой способ вызвать действие было бы что-то вроде [self.target performSelector:self.action]
в категории и [self.statusItem setAction:@selector(actionSelector:)]
в целевом классе, однако это может вызвать утечку в проектах ARC.
Изменить: Это работает на Эль-Капитан, тоже.
TL;DR: любой NSButton
экземпляр (с NSImage
который имеет template=YES
) внутри NSStatusItem
свойство визуально выглядит так же, как NSStatusBarButton
, Вы можете контролировать их highlight
собственность, как вы хотите.
Если вы хотите контролировать выделение вашего NSStatusItem
вручную и в то же время получить все преимущества внешнего вида от использования NSStatusBarButton
Вы можете использовать немного другой подход. Вы можете создать свой собственный NSButton
экземпляр, который имеет свое собственное свойство highlight
это полностью под вашим контролем. Тогда вы должны создать NSImage
экземпляр и установить его template
собственность на YES
, Тогда вы должны добавить это button
в [NSStatusItem view]
(да, это softly deprecated
) или даже как подпредставление к системе, созданной [NSStatusItem button]
, После этого вы должны нарисовать фон NSStatusItem
вручную с [NSStatusItem drawStatusBarBackgroundInRect:withHighlight:]
(что также не рекомендуется, о)
При таком подходе вы можете сочетать полный контроль над внешним видом NSStatusItem
и получите автоматическую стилизацию изображения кнопки.
Решение Антона идеально. Вот это в Swift 3:
NSEvent.addLocalMonitorForEvents(matching: .leftMouseDown) { [weak self] event in
if event.window == self?.statusItem.button?.window {
// Your action:
self?.togglePopover(self?.statusItem.button)
return nil
}
return event
}
Я добавил наблюдателя для NSApplicationWillResignActive
закрыть поповер и установить кнопку isHighlighted
в false
,
Одна идея извергает isHighlighted
из NSStatusBarButtonCell
и вернуть произвольное значение. https://gist.github.com/questbeat/3244601c8dc779484076
Но проблема в том, позволяет ли команда Apple проверять такой подход или нет...
Вот версия ответа @erikvdplas для Swift 5. Я тестировал это на macOS 11.4 Big Sur.
extension NSStatusBarButton {
public override func mouseDown(with event: NSEvent) {
super.mouseDown(with: event)
self.highlight((NSApp.delegate as! YourAppDelegateClass).popover.isShown)
}
}
Ответ Люка великолепен. Я просто делюсь своей реализацией, основанной на этом.
NSButton *button = [[NSButton alloc] initWithFrame:self.statusItem.button.frame];
button.alphaValue = 0;
NSArray *array = @[self.statusItem.button, button];
self.statusItem.button.superview.subviews = array;
[button sendActionOn:(NSLeftMouseDownMask | NSRightMouseDownMask)];
button.target = self;
button.action = @selector(statusItemClicked);
Если вы запланируете выделение кнопки для последующего выполнения в главном потоке, все, похоже, сработает. Это работает на Эль-Капитан, а также.
if self.appPopover.shown {
self.appPopover.performClose(sender)
} else {
if let button = statusItem.button {
// set the button's highlighted property to true
dispatch_async(dispatch_get_main_queue(), {
button.highlighted = true
})
appPopover.showRelativeToRect(button.bounds, ofView: button, preferredEdge: NSRectEdge.MinY)
}
}
Это работает, но вы можете заметить небольшое мерцание из-за изменения состояния кнопки с ON на OFF и затем снова на ON. ВЫКЛ происходит из-за всплывающего дисплея. Итак, чтобы это исправить, просто переместите вызов appPopover.showRelativeToRect() в блок dispatch_async(), сразу после установки выделенной кнопки.