由于这有很多代码,如果有一个示例项目可以更好地了解当前问题,它可能会有所帮助,我制作了一个简单的示例项目,您可以在GitHub上找到:https://github.com/dehlen/Stackoverflow >
我想实现一些功能与macOS屏幕快照工具非常相似。当鼠标悬停在窗口上时,该窗口应突出显示。但是,我仅在突出显示用户可见的窗口部分时遇到了问题。
我当前的实现如下:
1。获取屏幕上所有可见窗口的列表
static func all() -> [Window] {
let options = CGWindowListOption(arrayLiteral: .excludeDesktopElements, .optionOnScreenOnly)
let windowsListInfo = CGWindowListCopyWindowInfo(options, CGMainDisplayID()) //current window
let infoList = windowsListInfo as! [[String: Any]]
return infoList
.filter { $0["kCGWindowLayer"] as! Int == 0 }
.map { Window(
frame: CGRect(x: ($0["kCGWindowBounds"] as! [String: Any])["X"] as! CGFloat,
y: ($0["kCGWindowBounds"] as! [String: Any])["Y"] as! CGFloat,
width: ($0["kCGWindowBounds"] as! [String: Any])["Width"] as! CGFloat,
height: ($0["kCGWindowBounds"] as! [String: Any])["Height"] as! CGFloat),
applicationName: $0["kCGWindowOwnerName"] as! String)}
}
2。获取鼠标位置
private func registerMouseEvents() {
NSEvent.addLocalMonitorForEvents(matching: [.mouseMoved]) {
self.mouseLocation = NSEvent.mouseLocation
return $0
}
NSEvent.addGlobalMonitorForEvents(matching: [.mouseMoved]) { _ in
self.mouseLocation = NSEvent.mouseLocation
}
}
3。突出显示当前鼠标位置处的窗口:
static func window(at point: CGPoint) -> Window? {
// TODO: only if frontmost
let list = all()
return list.filter { $0.frame.contains(point) }.first
}
var mouseLocation: NSPoint = NSEvent.mouseLocation {
didSet {
//TODO: don't highlight if its the same window
if let window = WindowList.window(at: mouseLocation), !window.isCapture {
highlight(window: window)
} else {
removeHighlight()
}
}
}
private func removeHighlight() {
highlightWindowController?.close()
highlightWindowController = nil
}
func highlight(window: Window) {
removeHighlight()
highlightWindowController = HighlightWindowController()
highlightWindowController?.highlight(frame: window.frame, animate: false)
highlightWindowController?.showWindow(nil)
}
class HighlightWindowController: NSWindowController, NSWindowDelegate {
// MARK: - Initializers
init() {
let bounds = NSRect(x: 0, y: 0, width: 100, height: 100)
let window = NSWindow(contentRect: bounds, styleMask: .borderless, backing: .buffered, defer: true)
window.isOpaque = false
window.level = .screenSaver
window.backgroundColor = NSColor.blue
window.alphaValue = 0.2
window.ignoresMouseEvents = true
super.init(window: window)
window.delegate = self
}
// MARK: - Public API
func highlight(frame: CGRect, animate: Bool) {
if animate {
NSAnimationContext.current.duration = 0.1
}
let target = animate ? window?.animator() : window
target?.setFrame(frame, display: false)
}
}
如您所见,光标下方的窗口突出显示,但是突出显示窗口绘制在其他可能相交的窗口上方。
可能的解决方案 我可以遍历列表中的可用窗口,仅找到与其他窗口不重叠的矩形,以仅针对此部分而不是整个窗口绘制突出显示矩形。
我在问自己,这是否将是解决此问题的更优雅,更高效的解决方案。也许我可以用绘制的HighlightWindow的窗口级别解决此问题?还是我可以利用Apple提供的任何API来获得所需的行为?
答案 0 :(得分:3)
抱歉,我不习惯使用Swift,但是对我来说,自然的解决方案是使用 print(filename)
NameError: global name 'filename' is not defined
。在ObjC中会被添加(在高亮显示窗口之后添加):
- orderWindow:relativeTo:
然后让窗口服务器处理隐藏模糊部分的所有详细信息。当然,当用户在屏幕上四处移动内容时,将高亮窗口保持在目标窗口的正上方会产生不同的头痛,但是...
答案 1 :(得分:2)
我弄乱了您的代码,@ Ted是正确的。 NSWindow.order(_:relativeTo)
正是您需要的。
使用NSWindow.level
对您不起作用,因为普通窗口(如屏幕快照中的窗口)都具有0
或.normal
的窗口级别。如果仅将窗口级别调整为例如“ 1”,则突出显示视图将出现在所有其他窗口的 all 上方。相反,如果将其设置为“ -1”,突出显示视图将出现在所有普通窗口的下方和桌面上方。
没有警告,没有伟大的解决方案了吗?为了使用此方法,您必须将窗口级别设置为0
,以便可以在其他窗口之间进行分层。但是,这将导致在WindowList.window(at: mouseLocation)
方法中选择突出显示窗口。当选中它时,您的if语句会删除它,因为它认为它是主窗口。这将导致闪烁。 (下面的TLDR中包含此问题的修复程序)
此外,如果您尝试突出显示没有级别为0
的窗口,则会遇到问题。要解决此类问题,您需要找到要突出显示的窗口的窗口级别,并将突出显示窗口设置为该级别。 (我的代码未包含针对此问题的修复程序)
除上述问题外,您还需要考虑当用户将鼠标悬停在背景窗口上并在不移动鼠标的情况下单击它时会发生什么。将会发生的情况是背景窗口将变为最前面。不移动突出显示窗口。可能的解决方法是在点击事件上更新突出显示窗口。
最后,我注意到您每次用户移动鼠标时都会创建一个新的HighlightWindowController
+窗口。如果您仅更改鼠标移动中已经存在的HighlightWindowController
的框架(而不是创建鼠标),则对系统的影响可能会更小。要隐藏它,您可以调用NSWindowController.close()
函数,甚至可以将框架设置为{0,0,0,0}(不确定第二个想法)。
这就是我所做的。
1。。更改窗口结构以包括一个窗口号:
struct Window {
let frame: CGRect
let applicationName: String
let windowNumber: Int
init(frame: CGRect, applicationName: String, refNumber: Int) {
self.frame = frame.flippedScreenBounds
self.applicationName = applicationName
self.windowNumber = refNumber
}
var isCapture: Bool {
return applicationName.caseInsensitiveCompare("Capture") == .orderedSame
}
}
2。。在窗口列表功能(即static func all() -> [Window]
)中,包含窗口号:
refNumber: $0["kCGWindowNumber"] as! Int
3。。在窗口突出显示功能中,在highlightWindowController?.showWindow(nil)
之后,相对于要突出显示的窗口对窗口进行排序!
highlightWindowController!.window!.order(.above, relativeTo: window.windowNumber)
4。。请确保在高亮控制器中将窗口级别设置回正常:
window.level = .normal
5。。现在,窗口将闪烁,为避免这种情况,请更新视图控制器的if语句:
if let window = WindowList.window(at: mouseLocation) {
if !window.isCapture {
highlight(window: window)
}
} else {
removeHighlight()
}
祝你好运,玩得开心!
我忘了提及,我的快速版本是4.2(尚未升级),因此语法可能稍有不同。