Swift MacOS:如何让NSPanel抵制关键焦点(如果需要,请选择Becomeskeyonly?)

Swift MacOS:如何让NSPanel抵制关键焦点(如果需要,请选择Becomeskeyonly?),swift,macos,cocoa,nspanel,Swift,Macos,Cocoa,Nspanel,在MacOS/Cocoa/Swift 4.2/Xcode 10.1中,我试图创建一个NSPanel,它作为一个轻量级标记调色板漂浮在我的文档窗口上,与文档窗口中发生的(主要基于键盘的)编辑会话交互 我在面板中有很多按钮控件,但有一个组合框,当用户点击时,它值得关注。除此之外,我希望避免在按下任何其他控件、通过拖动重新定位面板或在死区中单击面板时成为关键点窗口(避免捕获未来的关键点事件和突出显示标题栏)。对我来说,这听起来就像文档描述的那样,如果我的面板被标记为becomeKeyOnlyIfNee

在MacOS/Cocoa/Swift 4.2/Xcode 10.1中,我试图创建一个NSPanel,它作为一个轻量级标记调色板漂浮在我的文档窗口上,与文档窗口中发生的(主要基于键盘的)编辑会话交互

我在面板中有很多按钮控件,但有一个组合框,当用户点击时,它值得关注。除此之外,我希望避免在按下任何其他控件、通过拖动重新定位面板或在死区中单击面板时成为关键点窗口(避免捕获未来的关键点事件和突出显示标题栏)。对我来说,这听起来就像文档描述的那样,如果我的面板被标记为becomeKeyOnlyIfNeeded,我应该有这样的行为,但我无法让它工作

在Interface Builder中,我将我的面板指定为(我自己的)子类InspectorPanel,样式为:常规面板、标题栏、阴影、关闭、可恢复和停用时隐藏设置(其他所有设置均清除)。我在子类中指定becomeskeyonlyif needed:

class InspectorPanel : NSPanel {
     override var becomesKeyOnlyIfNeeded: SwiftBool {
        get {
            return true
        }
        set {
        }
    }    
}
(我还尝试在控制器窗口上的控制器awakeFromNib中将其设置为?NSPanel。)

此设置似乎没有明显的效果。面板正确地浮动在文档窗口上方,但是当组合框成功地从文档窗口抓取关键点焦点时,按下任何控件、在面板空白处单击、拖动面板标题栏以及其他操作也会如此。虽然我可以为每种情况编写处理程序,手动查找活动文档窗口并生成KeyWindow(该窗口)以将焦点传递回它所属的位置,但从inspector设计的角度来看,这似乎不雅观;强制我编写一些目前不需要的处理程序(例如,标题栏拖动);让我担心我错过的类似案例

是否只有在我的单一组合框需要时,才有办法成功成为关键点?如果是,怎么做

其他努力:

  • 我知道needsPanelToBecomeKey,对它的文档有点困惑。这表明,一旦我在面板上设置BecomeSkeyanyIf needed,在我在特定子视图(例如我的组合框)上启用needsPanelToBecomeKey之前,任何东西都不会允许面板获取焦点。事实上,由于设置BecomeSkeyonyIfNeeded对阻止窗口变为关键点没有任何影响,因此无论是否在子视图上设置comboBox.needsPanelToBecomeKey,我都看不到响应上的差异。)

  • 我也知道refusesFirstResponder,并尝试将其设置在面板中的各种控件(例如NSButtons)上,以查看是否会阻止它们将关键焦点转移到窗口。不走运。)

  • 如果我设置canBecomeKeyWindow=false,那么即使在组合框中,我也无法获得关键点焦点。(这似乎是合理和出乎意料的;我只是将其列为一个全面的例子。)


  • 许多其他应用程序似乎都有我想要的浮动窗口,但我找不到可以帮助我发现错误的源代码。您能提供帮助吗?

    当您以编程方式创建
    NSPanel
    (NSWindow的子类
    NSWindow
    )时,请使用
    NSWindowStyleMaskUtilityWindow
    样式掩码,或者在IB中指定“实用程序面板”样式


    此样式定义了浮动调色板窗口的外观和行为。

    我发现了问题的原因。显然,我不理解我用来覆盖的Swift初始值设定项的含义(尽管我在其他地方成功地对其他属性使用了相同的语法!)。如果我删除将BecomeSkeyOnyIfNeeded定义为always true的尝试,而是沿着窗口初始化路径将其(继承属性)动态设置为true,则面板的行为与我所希望的完全相同,除了“如果需要”(在组合框中单击时)外,它会抵抗键焦点

    换句话说,

    class InspectorPanel : NSPanel {
    
      override func awakeFromNib() {
        super.awakeFromNib()
        becomesKeyOnlyIfNeeded = true // REPLACES THE BELOW
      }
    
      /* REPLACED BY ABOVE
      override var becomesKeyOnlyIfNeeded: SwiftBool {
        get {
            return true
        }
        set {
    
        }
      }
      */
    }
    

    工作完美。如果有人知道我用(注释掉的)初始值设定项做错了什么,我会很感激你的洞察力,但是通过这个解决方法,我可以结束关于面板行为的开放性问题。

    为什么
    SwiftBool
    ?另外,在面板的样式掩码中包含
    .nonactivatingPanel
    是否有帮助?对不起,SwiftBool是typealias SwiftBool=Swift.Bool的类型别名。(这是从导入Obj-C框架的代码复制而来的,该框架定义了不同的Bool,因此两者的客户端代码必须通过名称空间进行区分。)Re.NoActivatingPanel,正如我所理解的,这适用于您不希望windowclicks激活应用程序的情况,而不仅仅是面板。但无论如何,在IB中设置为非激活状态并不能阻止我拖动窗口或单击窗口内的任何位置(子视图或非子视图)时窗口变为关键点(例如,接收becomeKey通知、突出显示标题栏)。我尝试了两种方法,但都没有改变关键点/焦点抓取行为。切换到“工具面板”会使标题栏的大小变得更好,但(如上面的“未激活”)不会阻止我拖动窗口或单击其中任何位置(无论是否为子视图)时窗口变为关键点(例如,接收becomeKey通知,突出显示标题栏)。然后我什么都没有。我曾经有一个这样的窗口,但几年前我把它拿了出来(用一个侧边栏代替),所以我不知道我做了什么。我确实记得覆盖了顶层视图的
    hitTest:
    ;在其中,您可以检查将要返回的视图,并过滤掉那些将窃取焦点仅返回按钮控件的视图,例如.interest。很高兴你明白了。在我看来,这可能只是一个框架缺陷。显然,setter才是最重要的,也许它不调用getter,或者至少不调用重要部分的getter。fwiw,您还可以向awakeFromNib()方法添加
    isFloatingPanel=true
    ,这显然是为了确保窗格