草庐IT

swift - 鼠标事件后自定义 Carbon 键事件处理程序失败

coder 2023-09-05 原文

我正在尝试编写一个自定义 NSMenu,它将能够列出键输入并拦截必要的事件。这是为了为我打开的 source clipboard manager 提供简单的“按类型搜索”功能。 .

似乎唯一的方法是安装一个自定义的 Carbon 事件处理程序,它将监听关键事件并相应地处理它们,但这样的自定义处理程序似乎存在问题。

通常,我可以将事件向下传播到其他处理程序(例如系统处理程序),它们应该得到妥善处理。这可以通过一个简单的回调来完成:

let eventHandlerCallback: EventHandlerUPP = { eventHandlerCallRef, eventRef, userData in
  let response = CallNextEventHandler(eventHandlerCallRef, eventRef!)
  print("Response \(response)")
  return response
}

此回调完美运行,并始终打印 Response 0。此响应表示事件已正确处理。

但是,一旦我们在键盘事件之前发送鼠标事件,事情就会变得很奇怪。在这种情况下,回调失败并打印 Response -9874。此响应表示事件未正确处理。

似乎事件无法在我的自定义 View 下方的某处处理,我不知道确切的位置或如何解决这个问题。

为了重现,我已经 uploaded the code to Gist可以将其添加到 XCode Playground 并运行。看到弹出菜单后,按一些键(最好是箭头键,因为它们不会关闭菜单)并观察控制台中的 Response 0。之后,将光标移到菜单内并按更多箭头键。您现在应该在控制台中看到 Response -9874

最佳答案

不清楚你是否有一个 NSTextField 作为你的菜单 View ,但如果你使用一个,那么很容易为该文本字段设置一个委托(delegate),它可以在用户键入时获取该字段的当前内容(这会照顾他们使用箭头键向后移动,然后删除字符,使用删除键等)。您的委托(delegate)实现适当的委托(delegate)方法,并在每次文本更改时被调用:

extension CustomMenuItemViewController: NSTextFieldDelegate {
    func controlTextDidChange( _ obj: Notification) {
        if let postingObject = obj.object as? NSTextField {
            let text = postingObject.stringValue
            print("the text is now: \(text)")
        }
    }
}

为了确认这按预期工作,我在 xib 文件中为自定义菜单项 View (标签 + 编辑字段)创建了 ViewController 类,然后动态构建了一个简单的测试菜单,其中包含一个具有自定义 View 的菜单项 Controller 的 View 并将其添加到我的应用程序委托(delegate)中的菜单栏:

func installCustomMenuItem() {
    let menuBarItem = NSMenuItem(title: "Test", action: nil, keyEquivalent: "")
    let menu = NSMenu(title: "TestMenu" )
    let subMenuBarItem = NSMenuItem(title: "Custom View", action: nil, keyEquivalent: "")

    subMenuBarItem.view = menuItemVC.view
    menu.addItem(subMenuBarItem)
    menuBarItem.submenu = menu
    NSApp.mainMenu?.addItem(menuBarItem)
}

在我输入“hello”后看起来像这样:

您可以从控制台看到我的处理程序为输入的每个字符调用:

the text is now: H 
the text is now: He 
the text is now: Hel 
the text is now: Hell
the text is now: Hello

您的情况可能有点不同,但看起来这种方法非常干净,可能适合您。如果由于某种原因它不会,请添加一个澄清评论,我们会看看我们是否不能让它为您工作。


添加:

我突然想到您可能不希望使用 NSTextField,所以我很好奇使用自定义 View 是否同样容易做到这一点并且相对容易。

创建 NSView 的子类:

class CustomMenuView: NSView {

    override var acceptsFirstResponder: Bool {
        return true
    }

    override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)

        // Drawing code here.
    }

    override func keyDown(with event: NSEvent) {
        print("key down with character: \(String(describing: event.characters)) " )
    }
}

在自定义 View Controller 中将 Root View 的类设置为此类,然后像以前一样完成所有操作 - 在 applicationDidFinishLaunching 中加载 View Controller 并构建菜单和 View Controller 的 View (现在是 CustomMenuView) 被设置为 menuBarItem.view。

就是这样。当菜单下拉时,您现在可以为每个按下的键调用 keyDown 方法。

key down with character: Optional("H") 
key down with character: Optional("e") 
key down with character: Optional("l") 
key down with character: Optional("l") 
key down with character: Optional("o") 
key down with character: Optional(" ")
key down with character: Optional("T") 
key down with character: Optional("h") 
key down with character: Optional("i") 
key down with character: Optional("s") 
key down with character: Optional(" ") 
key down with character: Optional("i") 
key down with character: Optional("s") 
key down with character: Optional(" ") 
key down with character: Optional("c") 
key down with character: Optional("o") 
key down with character: Optional("o") 
key down with character: Optional("l") 

:)

现在您的自定义 View (和 subview ,如果您愿意)可以进行它们自己的绘图等。


在没有 ViewController 的情况下添加请求的示例:

// Simple swift playground test
// The pop-up menu will show up onscreen in the playground at a fixed location.   
// Click in the popup and then all key commands will be logged.
// The ViewController in my example above may be taking care of putting the custom view in the responder chain, or the fact that it's in a menubar and being invoked via a MenuItem might be.
// I'd suggest trying it in the actual environment rather than in a playground. In my test app you click the menu name in the menubar to drop down the menu and it is added to the responder chain and works as expected without having to click in the menu first to get the events flowing.
// There is no reason you need to be hooking events either with carbon events or the newer format.  If you're in the responder chain of and implement the necessary, method then you'll get the key events you're looking for.

import AppKit

class CustomMenuView: NSView {

    override var acceptsFirstResponder: Bool {
        return true
    }

    override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)

        // Drawing code here.
    }

    override func keyDown(with event: NSEvent) {
        print("key down with character: \(String(describing: event.characters)) " )
    }
}


func installCustomMenuItem() -> NSMenu {
//  let menuBarItem = NSMenuItem(title: "Test", action: nil, keyEquivalent: "")
    let resultMenu = NSMenu(title: "TestMenu" )
    let subMenuBarItem = NSMenuItem(title: "Custom View", action: nil, keyEquivalent: "")

    subMenuBarItem.view = CustomMenuView(frame: NSRect(x: 0, y: 0, width: 40, height: 44))
    resultMenu.addItem(subMenuBarItem)
//  menuBarItem.submenu = menu

    return resultMenu
}

var menu = installCustomMenuItem()

menu.popUp(positioning: nil, at: NSPoint(x:600,y:400), in: nil)

关于swift - 鼠标事件后自定义 Carbon 键事件处理程序失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53273191/

有关swift - 鼠标事件后自定义 Carbon 键事件处理程序失败的更多相关文章

  1. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

    我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

  2. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  3. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  4. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

  5. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

  6. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  7. ruby - 定义方法参数的条件 - 2

    我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano

  8. ruby - 如何在 Grape 中定义哈希数组? - 2

    我使用Ember作为我的前端和GrapeAPI来为我的API提供服务。前端发送类似:{"service"=>{"name"=>"Name","duration"=>"30","user"=>nil,"organization"=>"org","category"=>nil,"description"=>"description","disabled"=>true,"color"=>nil,"availabilities"=>[{"day"=>"Saturday","enabled"=>false,"timeSlots"=>[{"startAt"=>"09:00AM","endAt"=>

  9. ruby - 获取模块中定义的所有常量的值 - 2

    我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c

  10. ruby - 这两个 Ruby 类初始化定义有什么区别? - 2

    我正在阅读一本关于Ruby的书,作者在编写类初始化定义时使用的形式与他在本书前几节中使用的形式略有不同。它看起来像这样:classTicketattr_accessor:venue,:datedefinitialize(venue,date)self.venue=venueself.date=dateendend在本书的前几节中,它的定义如下:classTicketattr_accessor:venue,:datedefinitialize(venue,date)@venue=venue@date=dateendend在第一个示例中使用setter方法与在第二个示例中使用实例变量之间是

随机推荐