作者:臭小子 | 来源:互联网 | 2023-09-06 14:59
我需要创建不透明的透明窗口,并在其中创建不透明的子视图(称为surfaceView)。每个子视图可以包含数千个子视图(称它们为controlView)。所以结构必须是这样的:
NSWindow(非透明)
-NSView(非透明)(窗口contentview)
-NSView(不透明)(SurfaceView)
--- NSView(不透明)(ControlView)
Illustration
问题是,当内部有成千上万的controlViews时,WindowServer会变得过载。看起来窗口中的所有NSView都变得不透明。我不明白我该怎么办。
如果NSWindow不透明,则WindowServer不会过载。但是我需要不透明的窗口。这种情况证明,不透明的NSView可以包含许多性能良好的子视图。
如果NSWindow的样式掩码为[.titled,.resizable],则窗口变为透明(这证明透明窗口具有良好的性能是可能的),但没有鼠标过载,但鼠标无法通过窗口的透明部分工作。此外,NSWindow还具有私有类NSThemeframe的圆角。该解决方案非常脏,因为它需要重新实现鼠标事件并替换私有类中的某些方法。
如果表面是childWindows而不是子视图,则没有重载。但是在这种情况下,曲面不能被主窗口裁剪,缩小动画效果不起作用,并且实际上该窗口并不是真正的窗口。
我曾尝试与CAlayer,opaque和其他应用程序进行不同的组合和操作,但看起来像个bug。绝对有可能获得良好的性能,但我不知道如何。有什么想法吗?
也许这会对某人有所帮助。问题是两个意外事实结合在一起的。
- NSWindow属性ignoresMouseEvents的行为非常奇怪。如果此 ignoresMouseEvents 为true,则窗口对鼠标完全透明;如果为false,则窗口对鼠标完全不透明,即使窗口的某些部分是透明的。窗口 ignoresMouseEvents 的默认值为false,但窍门是,如果您不更改此属性,则WindowsignosMouseEvents的真实行为将具有第三个选项-窗口对于鼠标透明是透明的零件,这是窗口的默认状态。
所以 ignoresMouseEvents 是一个布尔值,实际上具有三个选项:true,false和Schrödinger的默认状态(如果您不更改的话)。
- 如果窗口具有透明的背景并且窗口内有很多CALayer,则ignoresMouseEvents的这种不直观的第三个选项会导致windowServer的意外cpu重载。因此,在某些情况下,windowServer的鼠标透明性检查非常慢。
因此,默认情况下,此代码将使您的windowServer在任何鼠标移动时都会哭泣:
let window = NSWindow(contentRect: windowrect,styleMask: [.borderless],backing: .buffered,defer: false);
window.setIsVisible(true);
window.contentView?.wantsLayer = true;
window.contentView?.layer?.borderWidth = 1;
window.backgroundColor = NSColor.clear;
for _ in 0...5000 {
let view = NSView(frame: viewrect);
view.wantsLayer = true;
view.layer?.backgroundColor = CGColor(gray: 1.0,alpha: 1.0);
window.contentView?.addSubview(view);
}
您的视图或图层是否不透明并不重要,它们是否是其他视图的子视图。但是,如果您添加 window.ignoresMouseEvents = false或true ,则windowServer会平静下来,但窗口对鼠标变为完全不透明或完全透明。
该解决方案是针对鼠标移动(使用trackingArea或GlobalMonitor)实现您自己的Windows透明性检查,如果鼠标位于窗口的透明部分,则将 ignoresmouseevents 设置为true,否则设置为false。幸运的是,很难编写鼠标检查的实现,这比默认的windowServer方法要慢。
P.S。抱歉,语言不好。