NSStatusBarButton поддерживает выделение
В OS X 10.10 большая часть NSStatusItem устарела в пользу свойства button
, которое состоит из NSStatusBarButton. Он должен работать как обычная кнопка, но, к сожалению, методы cell
и setCell
в NSStatusButton также устарели. В результате этого я изо всех сил пытаюсь найти способ подсветки кнопки после щелчка (обычно кнопка подсвечивается мышью вниз и не высвечивается мышью вверх. Я хочу, чтобы она была подсвечена после мыши).
Вызов [NSStatusButton setHighlighted:]
в его действии не работает, потому что кажется, что он не высвечивается сразу после того, как мышь встала. С другой стороны, использование задержки для ее вызова в следующем цикле, то есть [self performSelector: withDelay:]
, вызывает подсветку, чтобы она выглядела довольно неприглядно. Он работает, но выглядит не очень хорошо.
Настройка типа кнопки на NSToggleButton
полностью удаляет выделение и вместо этого выделяет изображение шаблона, которое было нечетным.
Это были единственные методы, о которых я мог думать. В любом случае, чтобы переопределить поведение мыши с помощью NSButtonCell?
Ответы
Ответ 1
Я добавил subview в элемент состояния, и внутри этого представления я добавил обработчики событий для mouseDown и т.д., которые вызвали [[statusItem button] highlight: true]. Как выясняется setHighlighted: не делает то же самое, что выделить:.
NSArray *array = [NSArray arrayWithObjects:[statusItem button], [self statusItemView], nil];
[[[statusItem button] superview] setSubviews:array];
//Highlight like so:
[[statusItem button] highlight:true];
EDIT: по El Capitan этот метод больше не работает, и ни statusItem.button.highlight = true
, ни 😀🔫
Ответ 2
Вот еще один вариант. Не устанавливайте свойство NSStatusItem
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
Свифт 3 версии Ответ Манфреда Урбана.
Работает на Эль Капитан.
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.
Ответ 4
TL; DR: Любой экземпляр NSButton
(с NSImage
с template=YES
) внутри свойства NSStatusItem
визуально выглядит точно так же, как NSStatusBarButton
. Вы можете управлять свойством highlight
, как хотите.
Если вы хотите вручную управлять подсветкой своего NSStatusItem
и в то же время получить все преимущества от использования NSStatusBarButton
, вы можете использовать несколько иной подход.
Вы можете создать свой собственный экземпляр NSButton
, у которого есть собственное свойство highlight
, которое полностью находится под вашим контролем. Затем вам нужно создать экземпляр NSImage
и установить его свойство template
в YES
. Затем вы должны добавить этот button
в [NSStatusItem view]
(да, который есть softly deprecated
) или даже как subview для созданной системы [NSStatusItem button]
. После этого вы должны нарисовать фон NSStatusItem
вручную с помощью [NSStatusItem drawStatusBarBackgroundInRect:withHighlight:]
(который также устарел, ох).
С помощью этого подхода вы можете комбинировать полный контроль над стилем вашего t23 и получить автоматический стиль изображения кнопки.
Ответ 5
Борясь с этой проблемой, я обнаружил, что перезапись 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 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.
Изменить: Это работает и на El Capitan.
Ответ 6
Одна идея - swizzling isHighlighted
of NSStatusBarButtonCell
и возвращает произвольное значение.
https://gist.github.com/questbeat/3244601c8dc779484076
Но проблема в том, разрешает ли Apple обзор команды такой подход или нет...
Ответ 7
Ответ Луке велик. Я просто использую мою реализацию на основе этого.
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);
Ответ 8
Если вы планируете выделение кнопки для последующего выполнения в основном потоке, все, кажется, работает. Это работает и на El Capitan.
if self.appPopover.shown {
self.appPopover.performClose(sender)
} else {
if let button = statusItem.button {
// set the button highlighted property to true
dispatch_async(dispatch_get_main_queue(), {
button.highlighted = true
})
appPopover.showRelativeToRect(button.bounds, ofView: button, preferredEdge: NSRectEdge.MinY)
}
}
Это работает, но вы можете заметить небольшое мерцание из-за того, что состояние кнопки изменяется от ON на OFF, а затем снова включено. ВЫКЛ происходит из-за всплывающего дисплея. Поэтому, чтобы исправить это, просто переместите вызов appPopover.showRelativeToRect() внутри блока dispatch_async(), сразу после установки кнопки подсвечивается.
Ответ 9
Решение Антона прекрасное. Здесь он находится в 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
, чтобы закрыть popover и установить кнопку isHighlighted
на false
.