TL; DR - Проблема здесь в активации приложения.
Это не дает точного ответа на ваш вопрос:
Как сделать запретить другим приложениям прослушивать события мыши в macOS?
Это ответ, демонстрирующий, как достичь желаемого, не мешая другим приложениям прослушивать события мыши в macOS.
Активное и неактивное окно
Проверьте следующие снимки экрана. Первый содержит активное окно Xcode, а другой - неактивное окно Xcode. Ваша цель - сохранить активное окно другого приложения, даже если вы щелкнете по оверлею. Неважно, запускает ли другое приложение презентацию (например, Keynote, в полноэкранном режиме) или нет.
![enter image description here](https://i.stack.imgur.com/gsFKF.png)
Пример настройки проекта
- Создание нового проекта (Xcode - приложение macOS - Swift и раскадровка)
- Откройте
Main.storyboard
и удалите окно и сцены контроллера просмотра - Установите
LSUIElement
на YES
(Info.plist
) - Добавьте пакет HotKey Swift (https://github.com/soffes/HotKey)
- Скопируйте и вставьте код
AppDelegate.swift
( ниже) - Запустить
- Переключить красный оверлей с помощью Cmd + Opt + O
Я только что протестировал его с Keynote 10.0 и macOS Catalina 10.15.4 ( 19E287), и он работает должным образом - я могу щелкнуть внутри красной накладки, не прерывая текущую презентацию, я могу управлять презентацией с помощью клавиатуры, ...
Важные части
- Использование
NSPanel
вместо NSWindow
- Используйте
styleMask
и .nonactivatingPanel
(нельзя использовать с NSWindow
) - Не активировать -> не деактивировать e другие
- Установить
hidesOnDeactivate
на false
- Не скрывать, когда вы запускаете свое приложение, активируется, а затем вы активируете любое другое приложение
- Установить
becomesKeyOnlyIfNeeded
на true
- Избегайте использования ключевого окна при щелчках мыши
- Найдите
needsPanelToBecomeKey
, если вам нужен ввод с клавиатуры
- Установить
collectionBehavior
на [.canJoinAllSpaces, .fullScreenAuxiliary]
.canJoinAllSpaces
= окно появляется во всех пробелах (как строка меню) .fullScreenAuxiliary
= окно с этим поведением коллекции может отображаться в том же пространстве, что и полноэкранное окно
AppDelegate.swift
import Cocoa
import HotKey
final class OverlayView: NSView {
private var path: NSBezierPath?
override func keyDown(with event: NSEvent) {
print("keyDown - \(event.keyCode)")
}
override func keyUp(with event: NSEvent) {
print("keyUp - \(event.keyCode)")
}
override func mouseDown(with event: NSEvent) {
let point = self.convert(event.locationInWindow, from: nil)
path = NSBezierPath()
path?.move(to: point)
needsDisplay = true
}
override func mouseUp(with event: NSEvent) {
path = nil
needsDisplay = true
}
override func mouseDragged(with event: NSEvent) {
let point = self.convert(event.locationInWindow, from: nil)
path?.line(to: point)
needsDisplay = true
}
override func draw(_ dirtyRect: NSRect) {
guard let ctx = NSGraphicsContext.current?.cgContext else {
return
}
defer {
ctx.restoreGState()
}
ctx.saveGState()
NSColor.green.set()
ctx.stroke(bounds, width: 8.0)
guard let path = path else {
return
}
path.lineWidth = 5.0
NSColor.green.set()
path.stroke()
}
override var acceptsFirstResponder: Bool {
true
}
override var needsPanelToBecomeKey: Bool {
true
}
}
final class OverlayWindow: NSPanel {
convenience init() {
self.init(
contentRect: NSScreen.main!.frame,
styleMask: [.borderless, .fullSizeContentView, .nonactivatingPanel],
backing: .buffered,
defer: false
)
canHide = false
hidesOnDeactivate = false
contentView = OverlayView()
isFloatingPanel = true
becomesKeyOnlyIfNeeded = true
acceptsMouseMovedEvents = true
isOpaque = false
hasShadow = false
titleVisibility = .hidden
level = .popUpMenu
backgroundColor = NSColor.black.withAlphaComponent(0.001)
collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary]
}
override var canBecomeKey: Bool {
true
}
}
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
private var hotKey: HotKey!
private var overlayWindowController: NSWindowController?
func applicationDidFinishLaunching(_ aNotification: Notification) {
hotKey = HotKey(key: .o, modifiers: [.command, .option])
hotKey.keyDownHandler = toggleOverlay
}
private func toggleOverlay() {
if overlayWindowController != nil {
overlayWindowController?.close()
overlayWindowController = nil
} else {
overlayWindowController = NSWindowController(window: OverlayWindow())
overlayWindowController?.showWindow(self)
overlayWindowController?.window?.makeKey()
}
}
func applicationWillTerminate(_ aNotification: Notification) {
}
}