【问题标题】:MacOS: how to get NSPanel to resist key focus (with becomesKeyOnlyIfNeeded?)MacOS:如何让 NSPanel 抵抗关键焦点(使用 becomeKeyOnlyIfNeeded?)
【发布时间】:2019-07-26 04:09:58
【问题描述】:

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

我在面板中主要有按钮控件,但是当用户单击它时,一个组合框值得关注。除此之外,我想避免成为关键窗口(避免捕获未来的关键事件并避免突出显示标题栏)当任何其他控件被按下时,或者当面板通过拖动重新定位时,或者面板点击时死角。在我看来,这与文档描述的行为完全一样,如果我的面板被标记为 becomeKeyOnlyIfNeeded,我应该具有的行为,但我无法让它工作。

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

class InspectorPanel : NSPanel {
     override var becomesKeyOnlyIfNeeded: SwiftBool {
        get {
            return true
        }
        set {
        }
    }    
}

(我也尝试在控制器的 awakeFromNib 中设置它,在控制器的窗口中设置为?NSPanel。)

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

只有当我的单个组合框需要它时,是否有某种方法可以成功成为关键?如果有,怎么做?

其他努力:

  1. 我知道 needsPanelToBecomeKey 并且对其文档感到有些困惑。它表明,一旦我在面板上设置了 becomeKeyOnlyIfNeeded,NOTHING 将允许面板获取焦点,直到我在特定子视图(例如我的组合框)上启用 needsPanelToBecomeKey。事实上,由于设置 becomeKeyOnlyIfNeeded 对阻止窗口成为键没有任何影响,因此无论我是否在子视图上设置 comboBox.needsPanelToBecomeKey,我都看不出响应有什么不同。)

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

  3. 如果我设置 canBecomeKeyWindow = false,那么我永远不会获得关键焦点,即使在我的组合框中也是如此。 (这似乎是合理且出乎意料的;我只是列出它是为了全面。)

许多其他应用程序似乎都有浮动窗口,其行为与我想要的一样,但我没有找到可以帮助我找到错误的源代码。你能帮忙吗?

【问题讨论】:

  • 为什么是SwiftBool?另外,在面板的样式掩码中包含.nonactivatingPanel 是否有帮助?
  • 抱歉,SwiftBool 是 typealias SwiftBool=Swift.Bool 的类型别名。 (这是从同样导入了不同定义 Bool 的 Obj-C 框架的代码中复制的,因此两者的客户端代码必须通过命名空间来区分。) Re .nonactivatingPanel,正如我所读到的,这适用于您不想要的情况窗口点击激活app,而不仅仅是panel。但无论如何,在 IB 中设置 Non Activating 并不会阻止当我拖动窗口或单击其中的 anywhere 时窗口成为键(例如接收 becomeKey 通知、突出显示标题栏) (子视图与否)。

标签: swift macos cocoa nspanel


【解决方案1】:

我找到了问题的原因。显然我不明白我用来覆盖上面 becomeKeyOnlyIfNeeded 的 Swift 初始化程序的含义(尽管我在其他地方成功地对其他属性使用了相同的语法!)。如果我删除了将 becomeKeyOnlyIfNeeded 定义为 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 {

    }
  }
  */
}

完美运行。如果有人理解我的(已注释掉的)初始化程序做错了什么,我将不胜感激,但通过这种解决方法,我可以关闭有关面板行为的未决问题。

【讨论】:

  • 有趣。很高兴你想出来了。对我来说,看起来它可能只是一个框架错误。显然,setter 才是最重要的,也许它不会调用 getter,或者至少对于重要的部分没有。
  • fwiw,您还可以将isFloatingPanel = true 添加到 awakeFromNib() 方法中,这很明显可以确保面板始终可供单击,而无需四处寻找。
【解决方案2】:

在以编程方式创建NSPanelNSWindow 的子类)时使用NSWindowStyleMaskUtilityWindow 样式掩码,或在 IB 中指定“实用程序面板”样式。

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

【讨论】:

  • 我尝试了两种方式,但都没有改变关键/焦点抓取行为。切换到实用程序面板会更好地更改标题栏的大小,但是(如上面的 Non Activating )不会阻止窗口在我拖动时变为键(例如接收 becomeKey 通知,突出显示标题栏)窗口或单击其中的任意位置(子视图或非子视图)。
  • 那我什么都没有。我曾经有一个这样的窗口,但几年前我把它拿出来(用侧边栏代替它)所以我不知道我做了什么。我确实记得覆盖了顶级视图的hitTest:;在其中,您可以检查将要返回的视图并过滤掉会窃取焦点的视图——例如,仅返回按钮控件。
猜你喜欢
  • 2012-01-11
  • 2018-10-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-07
  • 1970-01-01
  • 2014-09-11
相关资源
最近更新 更多