2014-09-23 3 views
11

Начиная с OS X 10.10 большая часть NSStatusItem устарела в пользу свойства button, которое состоит из NSStatusBarButton. Он должен работать как обычная кнопка, но, к сожалению, методы cell и setCell в NSStatusButton также устарели. В результате я изо всех сил пытаюсь найти способ выделить кнопку после ее щелчка (обычно кнопка подсвечивается мышью вниз и не высвечивается мышью вверх. Я хочу, чтобы она была подсвечена после мышки).NSStatusBarButton keep highlight

Вызов [NSStatusButton setHighlighted:] в своем действии не работает, потому что кажется, что он не высветится сразу после того, как мышь встала. С другой стороны, использование задержки для вызова на следующем цикле, то есть [self performSelector: withDelay:] заставляет подсветку мигать довольно неприглядно. Он работает, но выглядит не очень хорошо.

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

Это были единственные методы, о которых я мог думать. В любом случае, чтобы переопределить поведение мыши с помощью NSButtonCell?

+0

Где вы узнали, что 'NSStatusItem' устарел? – PnotNP

+0

@NulledPointer https://developer.apple.com/library/prerelease/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSStatusItem_Class/index.html#//apple_ref/doc/uid/TP40004118 – Luke

+3

NSStatusItem не устарел, некоторые из его методов были отвергнуты в 10.10. – ctpenrose

ответ

4

Я добавил подвид к пункту статуса, и в этой точке зрения, я добавил обработчик событий MouseDown и т.д., которые под названием [ [statusItem button] выделить: true]. Как выясняется setHighlighted: не делает то же самое, что и выделить :. не

NSArray *array = [NSArray arrayWithObjects:[statusItem button], [self statusItemView], nil]; 
[[[statusItem button] superview] setSubviews:array]; 
//Highlight like so: 
[[statusItem button] highlight:true]; 

EDIT: В Эль Капитане этот метод больше не работает, и ни один не делает statusItem.button.highlight = true либо

+0

Вы найдете что-нибудь на Эль Капитане? –

1

Одна идея - это swizzling isHighlighted of NSStatusBarButtonCell и вернуть произвольное значение. https://gist.github.com/questbeat/3244601c8dc779484076

Но проблема заключается в том, позволяет ли команда обзора Apple, этот подход или нет ...

2

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 и получить автоматический стиль изображения кнопки.

+1

Я весь день пытался работать с новым API, но благодаря вашему комментарию я опробовал устаревший API 'drawStatusBarBackgroundInRect: withHighlight:', и он работает намного лучше, на мой взгляд. Я уверен, что Apple по-прежнему использует устаревший API для своих значков в меню, так как подсветка остается включенной. Очень странно, что Apple просто запустит подсветку в новом 'NSStatusBarButton'. – iMaddin

0

Ответ Луки замечательный. На этом я просто делюсь своей реализацией.

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); 
2

Борясь с этой проблемой самостоятельно, я обнаружил, что перезапись 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:)] в целевом классе, однако это may cause a leak в АРК проектов.

Редактировать: Это работает и на Эль Капитан.

+0

@xhacker: Протестировано на OS X 10.11. –

0

Если вы планируете выделение кнопки для последующего выполнения в основном потоке, все, кажется, работает. Это работает и на El Capitan.

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) 
     } 
    } 

Это делает работу, но вы можете заметить небольшое мерцание из-за состояния кнопки изменяется с ВКЛ на ВЫКЛ, а затем снова включите. ВЫКЛ происходит из-за всплывающего дисплея. Поэтому, чтобы исправить это, просто переместите вызов appPopover.showRelativeToRect() внутри блока dispatch_async(), сразу после установки кнопки подсвечивается.

+0

Мерцание все еще появляется. – njuri

4

Вот еще один вариант. Не устанавливайте 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

Swift 3 версия Manfred Urban's ответ. Работы на Эль 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() 
    } 
} 

Не забудьте установить правильность выделения свойств кнопок, если это необходимо.

+0

Работы на Сьерра тоже. – rsfinn

-1

Решение Антона совершенное. Здесь в 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.