Запретить активацию приложения при нажатии на NSWindow / NSView

Я работаю над скриншотом приложения для Mac. Я пытаюсь перестроить то, что происходит при нажатии Cmd-Ctrl-Shift-4: курсор-перекрестие и прямоугольник выделения для скриншота.

Я использую настраиваемый NSWindow без полей поверх всех остальных окон. Я отключил курсор, чтобы нарисовать свой собственный вместе с прямоугольником выделения.

Моя проблема в том, что как только я нажимаю и перетаскиваю, чтобы сделать снимок экрана, мое приложение активируется (потому что щелчок перехватывается моим экранирующим окном).

Есть ли способ получить щелчок в моем пользовательском представлении / окне без активации моего приложения?

Я пробовал использовать NSPanel с флагом NSNonactivatingPanelMask, но в этом случае у меня проблема с курсором: я не могу нарисовать свой собственный, когда активно другое приложение, потому что я не могу скрыть курсор для других приложений ...


person Mark    schedule 08.09.2014    source источник
comment
Я не знаю точно, что вы пытаетесь выполнить, но, возможно, вместо того, чтобы самостоятельно реализовать механизм создания снимков экрана, вы можете обойтись одним из следующих решений: stackoverflow.com/questions/4516852/   -  person Brad Allred    schedule 08.09.2014
comment
@BradAllred Я пытаюсь создать такой же пользовательский интерфейс, как Cmd-Shift-3, но без предварительного сохранения файла пользователем. Похоже, что это то, что предлагают ответы в вопросе, на который вы ссылались.   -  person Mark    schedule 09.09.2014
comment
Итак, к вашему сведению, вы можете удерживать клавишу ctrl, чтобы сделать снимок экрана в буфер обмена ... не знаю, это все, что вы пытаетесь сделать. в противном случае, да, одно из предложений в связанной ветке может быть лучшим (или мой ответ ниже)   -  person Brad Allred    schedule 09.09.2014
comment
Установка NSApplicationActivationPolicy.Prohibited - это другой подход ... конечно, в зависимости от потребностей приложения.   -  person Milos    schedule 27.09.2015


Ответы (4)


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

В NSWindow есть недокументированный метод, который делает именно то, что вы хотите:

@interface NSWindow (Private)
- (void )_setPreventsActivation:(bool)preventsActivation;
@end

[myWindow _setPreventsActivation:true];

Это останавливает активацию окна как самого себя, так и своего приложения, когда пользователь щелкает по нему.

Стандартные предупреждения об использовании недокументированных API, конечно, применимы: Apple может изменить это в какой-то момент (хотя это было во многих версиях OS X, поэтому есть большая вероятность, что они этого не сделают), и использование этого может привести к отклонению вашего приложения из приложения Mac хранить.

person GuyGizmo    schedule 21.06.2016
comment
Как бы я назвал это из Свифта? - person Anshuman Sharma; 20.01.2018
comment
Это может вам помочь: stackoverflow.com/questions/28174541/ - person GuyGizmo; 20.01.2018

Как бы то ни было, есть еще один способ сделать курсор невидимым глобально, кроме создания гигантского окна. Он включает в себя некоторые недокументированные API, если это то, что вы можете использовать:

extern "C" {
    typedef int CGSConnection;
    void CGSSetConnectionProperty(int, int, const void *, const void *);
    int CGSMainConnectionID();
}

void allowHidingCursorForBackgroundOnlyApp()
{
    CFStringRef propertyString = CFStringCreateCopy(NULL, CFSTR("SetsCursorInBackground"));
    CGSSetConnectionProperty(CGSMainConnectionID(), CGSMainConnectionID(), propertyString, kCFBooleanTrue);
    CFRelease((CFTypeRef)propertyString);
}

Совместите это с разумным использованием касаний событий для захвата и фильтрации щелчков мыши, и вы сможете создать тот же эффект, что и встроенная функция снимка экрана.

person GuyGizmo    schedule 21.06.2016

Я молюсь, что сейчас есть лучший способ сделать это, но когда мне пришлось сделать что-то подобное, я позволил моему окну / представлению игнорировать весь ввод мыши, тогда я использовал CGEventTap (см. документация Quarts Event Services) для записи событий мыши. глобально (не удаляя их из очереди событий). Я вручную сопоставил их с моим окном, создал пользовательскую копию NSEvent и вручную отправил ее моему окну.

Огромным недостатком здесь (помимо сложности) является то, что я помню, что мне нужно было запускаться от имени пользователя root, чтобы иметь возможность установить кран событий. Однако я думаю, что есть способ получить разрешение через универсальный доступ.

Я совершенно не уверен, что отправка настраиваемого NSEvent прямо в окно будет иметь такой же побочный эффект, как активация вашего приложения; особенно с учетом того, что после 10.6 многое изменилось ... Я бы предложил простой тест, чтобы увидеть, возможно ли это, прежде чем приступать к нему.

person Brad Allred    schedule 08.09.2014
comment
Да, я думал об этом. Думаю, вы правы, пользователю сначала нужно разрешить доступ для вспомогательных устройств, иначе это не сработает ... - person Mark; 09.09.2014

Еще одна идея, вы можете переопределить - (BOOL)_isNonactivatingPanel частный метод в NSWindow подклассе:

@implementation MyWindow

- (BOOL)_isNonactivatingPanel
{
    return YES;
}

@end

Вуаля, поведение похоже на NSPanel :)

person Aleksey Prykhodko    schedule 06.01.2021