Отменить события мыши в NSWindow на основе позиции щелчка

Допустим, у меня есть плавающий круговой файл без полей NSWindow.

Он круглый, потому что в представлении содержимого просто нарисован красный кружок.

Это представление содержимого должно иметь поддержку слоев ([contentView setWantsLayer:YES]), потому что я применяю к нему CoreAnimations, например, анимированное масштабирование.

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

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

щелкните местоположение

Моя первоначальная мысль заключалась в том, чтобы реализовать [NSWindow sendEvent:] в подклассе и проверить, был ли щелчок выполнен в пределах радиуса, используя [theEvent locationInWindow]. Я подумал, что могу просто отбросить событие, если оно выходит за пределы радиуса, не вызывая тогда [super sendEvent:theEvent]. Однако это не сработало: я заметил, что mouseDown :; метод окна вызывается еще до sendEvent :; метод.

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

Вы, ребята, знаете, как это решить?


person Raffael    schedule 13.03.2014    source источник
comment
Вы пробовали перехватывать события на уровне приложения путем создания подкласса NSApplication и реализации -sendEvent: переопределения?   -  person indragie    schedule 14.03.2014
comment
Я только что проверил, но безуспешно. Даже когда я пытаюсь отбросить любое событие, никогда не вызывая [super sendEvent:] в подклассе NSApplication, окно продолжает перемещаться с помощью перетаскивания мышью. Кроме того, перезапись [NSApplication postEvent:] не работает.   -  person Raffael    schedule 14.03.2014


Ответы (2)


Итак, через несколько недель я пришел к следующим результатам:

A) Окно прокси: используйте окно прокси без поддержки уровня, которое размещается поверх целевого окна в качестве дочернего окна. Прокси-окно имеет ту же форму, что и целевое окно, и, поскольку оно не имеет поддержки уровня, оно будет правильно получать и игнорировать события. Прокси-окно делегирует все события целевому окну путем перезаписи sendEvent:. Целевое окно настроено на игнорирование всех событий мыши.

B) Наблюдение за глобальным указателем мыши: установите глобальный и локальный монитор событий для NSMouseMovedMask|NSLeftMouseDraggedMask событий с помощью addGlobalMonitorForEventsMatchingMask и addLocalMonitorForEventsMatchingMask. Мониторы событий отключают и включают игнорирование событий мыши во всех зарегистрированных целевых окнах в зависимости от текущего глобального положения мыши. В случае круглых окон необходимо рассчитать расстояние между указателем мыши и каждым целевым окном.

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

ОБНОВЛЕНИЕ: оба подхода имеют ряд существенных недостатков: в пункте А) окно прокси иногда может не синхронизироваться и может немного смещаться с фактическим окном.

В B) монитор событий оказывает большое влияние на время автономной работы при перемещении мыши, даже если приложение не является самым передним.

person Raffael    schedule 25.03.2014
comment
Пожалуйста, проверьте stackoverflow.com/questions/29441015/ - person c-smile; 10.08.2015

Если вы хотите отменить событие mouseDown в зависимости от позиции, вы можете использовать:

CGPathContainsPoint(path,transform,point,eoFill):Bool

Настройте свой путь в соответствии с вашей графикой. Круги, эллипсы, прямоугольники, треугольники или пути и даже композиционные пути (пути с отверстиями).

person Sentry.co    schedule 31.01.2016
comment
И что потом? В этом случае hitTest () можно переопределить так, чтобы он возвращал nil, но это, похоже, не отправляет щелчок в следующее окно. - person Alex; 01.12.2016
comment
переопределите метод hitTest и обработайте свой код внутри метода hitTest. Затем отправьте событие hitTest с помощью super, если точка mouseDown находится за пределами пути. - person Sentry.co; 01.12.2016
comment
В основном: return nil останавливает поток событий. пересылка события с помощью super продолжает распространять событие. - person Sentry.co; 01.12.2016
comment
Один только возврат nil, похоже, не помогает сфокусироваться на окне за нашим окном, см. Мини-пример: github.com/aphofstede/TransparentWindowClickthroughTest/blob/ - person Alex; 02.12.2016
comment
Мой предыдущий комментарий состоит из двух частей. Первая часть останавливает поток событий. Вторая часть поддерживает распространение потока событий. Попробуйте сделать второй вариант, если для регистрации события вам не нужны элементы графического интерфейса. Это работает для NSViews и должно работать и для NSWindow. - person Sentry.co; 04.12.2016
comment
Вот еще один подход для добавления пользовательских масок Безье в NSWindows: stylekit.org/blog / 2016/01/23 / Chromeless-window (см. Часть о пользовательской форме Безье) Возможно, это не совсем то, что вы ищете, но это может дать вам другое представление. - person Sentry.co; 04.12.2016
comment
Вот статья, которую я написал о HitTesting, которая может пролить свет на концепцию: stylekit.org/blog/2016/01/28/Hit-testing-sub-views - person Sentry.co; 05.12.2016
comment
Ах, это ты, я прочитал это и собирался оставить комментарий, думаю, я уже сделал :) Мне удалось заставить его работать, используя замаскированный слой, но мое настоящее приложение имеет более сложную настройку слоя, и я могу ' t скопируйте его напрямую, я попытаюсь воспроизвести его в демонстрационном проекте, посмотрите, проясняет ли это ситуацию - person Alex; 05.12.2016
comment
Вот версия, которая хорошо работает, с использованием слоя маски: - person Alex; 05.12.2016
comment
Обратите внимание, что использование .resizable в styleMask окна нарушит прозрачные переходы по ссылкам. Мне кажется, что это ошибка, я открыл отчет об ошибке в Apple и на данный момент реализовал собственное изменение размера. - person Alex; 13.12.2016
comment
Я предполагаю, что это как-то связано с ручками изменения размера, которые появляются по краям окна. Решение для изменения размера яблок предназначено для прямоугольных окон. Поэтому они, вероятно, решили прервать прозрачные переходы в тех случаях, когда окно не прямоугольное. Тем не менее, вы занимаетесь новаторской работой. - person Sentry.co; 13.12.2016
comment
Возможно ты прав. Я не понимаю, почему не удалось разобрать его и позволить разработчику определять свои области отслеживания курсора для изменения размера отдельно, если это необходимо, и позволить обработке прозрачности делать свое дело ... - person Alex; 14.12.2016
comment
Я реализовал индивидуальное изменение размера окна один раз, используя комбинацию NSEvent.addLocalMonitorForEventsMatchingMask и NSEvent.removeMonitor. Это должно сработать. Может применяться некоторая смазка для локтя;) - person Sentry.co; 14.12.2016