Snow Leopard & LSUIElement → приложение не активируется должным образом, окно не является "активным", несмотря на "ключ",

У меня возникла проблема с фоновым приложением, которое использует LSUIElement = 1, чтобы скрыть свой элемент док-станции, строку меню и предотвратить ее появление в переключателе приложений Command-Tab.

Кажется, проблема с Snow Leopard.

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

Первое, что не работает, - это то, что окно не упорядочивается спереди, а появляется за всеми окнами других приложений.

Я попытался исправить это, вызвав

[[NSApplication sharedApplication] activateIgnoringOtherApps: YES]

но это не сработало.

Через некоторое время я понял, что меню блокирует сообщение в цикле запуска от отправки, поэтому я написал другой метод в MainController и отправил сообщение с задержкой:

[self performSelector: @selector (setFront:)             withObject: [preferencesController window] afterDelay: 1.0];

-(void)setFront: (id) theWindow {

 [[NSApplication sharedApplication]activateIgnoringOtherApps:YES];
 [theWindow orderFrontRegardless];
 [theWindow makeKeyWindow]; 
        [[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
}

Обратите внимание на метод send-all-possible-message-to-make-it-do-what-it-should-be-doing-approach.

Это работает, вид, окно выводится на передний план поверх всех других окон из всех приложений, но большую часть времени он неактивен, что означает, что строка заголовка выделена серым цветом. Нажатие на строку заголовка также не сделает окно активным. Нажатие INSIDE окна сделает его активным!?

Все это, казалось, не было проблемой в Leopard; просто вызов activIgnoringOtherApps и создание оконного ключа, казалось, работали нормально.

В Snow Leopard появился новый API, предназначенный для замены LSUIElement, который должен эмулировать его поведение:

http://developer.apple.com/mac/library/releasenotes/cocoa/appkit.html

Я играл с этим, но это только SL, и я не смог установить LSUIElement.

Ответ 1

Странно: я пишу приложение LSUIElement под Snow Leopard, и у меня не было таких проблем, как вы описали... У меня возникла проблема, что новое окно не появилось на передней панели, но я исправил его, вызвав активизациюIgnoringOtherApps. Это все, что я должен был сделать, чтобы он работал так, как должен:

[NSApp activateIgnoringOtherApps: YES];
[preferencesWindow makeKeyAndOrderFront: self];

Я даже не касался ничего, у которого была "политика" в названии.

Ответ 2

После публикации вопроса в отчаянии я продолжал искать и в конце концов нашел решение. Так как это натолкнуло меня на несколько дней, и, похоже, нет другого ответа, который может найти Google, я объясню решение для "будущих поколений".

Snow Leopard добавляет новый API-интерфейс представления приложений NSApplication:

http://developer.apple.com/mac/library/releasenotes/cocoa/appkit.html

Это должно имитировать способ работы LSUIElement, но обеспечить больше контроля над разработчиками. К сожалению, симуляция не идеальна, поэтому происходит изменение поведения между 10.5 и 10.6.

В частности, если ваше приложение имеет строку LSUIElement = 1 в своем info.plist, Snow Leopard будет инициализировать "presentationmentsOptions приложений... вместо эквивалентной комбинации флагов NSApplicationPresentationOptions".

Только это не так. Он устанавливает новый NSApplication setActivationPolicy для NSApplicationActivationPolicyAccessory:

"Приложение не отображается в Dock и не имеет панели меню, но может быть активировано программно или щелчком по одному из его окон. Это соответствует значению ключа LSUIElement в приложениях Info.plist, 1."

Несмотря на упоминание активизации программно, activateIgnoringOtherApps: просто игнорируется полностью.

Решение состоит в том, чтобы установить политику активации "regular":

[[NSApplication sharedApplication] setActivationPolicy: NSApplicationActivationPolicyRegular];

Конечно, вы можете сделать это только в том случае, если используете 10.6 SDK в качестве базового SDK, чего мало кто хочет сделать в данный момент, так что ниже это 10,5-безопасный способ сделать это:

NSApplication* app = [NSApplication sharedApplication];

if( [app respondsToSelector: @selector(setActivationPolicy:)] ) {

    NSMethodSignature* method = [[app class] instanceMethodSignatureForSelector: @selector(setActivationPolicy:)];
    NSInvocation* invocation = [NSInvocation invocationWithMethodSignature: method];
    [invocation setTarget: app];
    [invocation setSelector: @selector(setActivationPolicy:)];
    NSInteger myNSApplicationActivationPolicyAccessory = 0;
    [invocation setArgument: &myNSApplicationActivationPolicyAccessory atIndex: 2];
    [invocation invoke];

}

Я надеюсь, что кто-нибудь найдет это полезным.