【问题标题】:Simulate a mouse click in an NSWindow在 NSWindow 中模拟鼠标点击
【发布时间】:2012-06-15 20:05:38
【问题描述】:

我想在 Cocoa 应用程序上模拟鼠标点击而不实际点击鼠标,并且*在给定当前鼠标位置的情况下,不必弄清楚哪个视图应该响应点击。

我希望 Cocoa 框架来处理确定哪个视图应该响应,所以我不认为 N​​SView 对象上的方法调用是我正在寻找的。也就是说,我认为我需要一个最终会调用此方法的方法调用。

我目前通过使用 CGEventCreateMouseEvent 和 CGEventPost 在特定的全局位置单击鼠标来实现此功能。然而,这种技术实际上是点击鼠标。所以这行得通,但我对这种行为并不完全满意。例如,如果我在调用 CGEventPost 时按住键盘上的一个键,则该键将被包装到事件中。此外,如果我将另一个进程的窗口移动到我想模拟单击的窗口上,那么 CGEventPost 方法将在该窗口中单击鼠标。也就是说,它在全球范围内跨流程发挥作用。我想要一种适用于单个窗口的技术。 NSWindow 对象上可能有什么东西?

我在 Cocoa 文档中读到“鼠标事件由 NSWindow 对象分派到发生事件的 NSView 对象”。

好的。所以我想知道被调用来进行调度的方法。在窗口上调用此方法,然后让框架根据当前鼠标位置确定要调用哪个 NSView。

任何帮助将不胜感激。我刚刚开始学习 Cocoa 框架,所以如果这里的任何术语/措辞不完全正确,我深表歉意。

【问题讨论】:

  • -[NSApplication sendEvent:] 本质上是输入事件的入口点;从那里它(通常)被传递到一个窗口。也就是说,您试图在“模拟”和“实际”单击鼠标之间做出区分并不清楚。您要避免的CGEvent 过程有哪些功能? (例如,物理鼠标设备中没有移动按钮的伺服器,这就是我所说的“实际”点击。)
  • 已编辑。感谢您的 cmets。
  • @JoshCaswell 这看起来是个不错的方法,但我无法让它发挥作用。我会发布代码,但它是使用 Objective C 桥用 Clozure 编写的,因此解析起来可能有点费力。我在网上找不到任何使用 sendEvent 模拟鼠标点击的例子。你能碰巧吗?
  • 你不只是在寻找 -[NSView hitTest:],是吗?

标签: objective-c macos cocoa


【解决方案1】:

对于实际点击所发生的情况,很难确切知道您要寻找多少保真度。例如,您希望点击激活应用程序吗?您希望单击将窗口带到顶部吗?让它成为关键还是主要?如果该位置在标题栏中,您是否希望它可能关闭、最小化、缩放或移动窗口?

正如 John Caswell 所指出的,如果您将适当构造的 NSEvent 传递给 -[NSApplication sendEvent:],它将密切模拟真实事件的处理。在大多数情况下,NSApplication 会将事件转发到事件的窗口及其-[NSWindow sendEvent:] 方法。如果你想避免NSApplication 做其他事情的机会,你可以直接调度到窗口的-sendEvent: 方法。但这可能会破坏一些理想的行为,具体取决于您想要什么。

如果点击的窗口或视图的响应是运行内部事件跟踪循环会发生什么?它将是同步的;也就是说,调用-sendEvent: 的代码在该循环完成之前不会重新获得控制权,如果您无法传递后续事件,它可能无法完成。事实上,这样的循环将通过-[NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:] 查找后续事件,因此如果您的合成事件不在队列中,它们将不会被看到。因此,更好地模拟真实事件的处理可能需要您使用-[NSApplication postEvent:atStart:] 将事件(鼠标按下、鼠标拖动、鼠标向上)发布到队列中。

我认为你的首要任务是深入思考你想要完​​成的任务、所有潜在的陷阱和极端情况,并决定你想如何处理这些问题。

关于CGEvent... 的东西,您可以使用CGEventPostToPSN() 将事件发布到特定进程,并且不会点击其他应用程序的窗口,即使它们位于目标窗口的前面。但是,它可能仍会单击目标应用程序中的其他窗口。

【讨论】:

    【解决方案2】:

    好的。所以我想知道被调用来进行调度的方法。在窗口上调用此方法,然后让框架根据当前鼠标位置确定要调用哪个 NSView。

    NSView *target = [[theWindow contentView] hitTest:thePoint];
    

    我对您的问题并不完全清楚,所以我不知道您是否只想在目标上调用 mouseDown:。但如果你这样做了,那将与真正的鼠标点击几乎完全相同。

    这是用于传递实时点击的消息。它遍历视图层次结构,自动处理重叠、隐藏消息等,并让视图链中的每个步骤在需要时进行干预。如果一个视图想要阻止子视图获得点击,它会通过吃 hitTest: 来做到这一点,这意味着它会影响你的代码,就像它影响真正的点击一样。 如果点击会被传递,这个方法总是会告诉你它会被传递到哪里

    但是,它不一定能处理可能无法传递点击的所有原因(acceptsFirstMouse、模式对话框等)。此外,您必须知道窗口和点(在适当的坐标系中),但听起来这就是您要开始的地方。

    【讨论】:

    • 感谢 abarnert。这似乎是一种简单的方法。将尽快进行测试,并发布结果。
    【解决方案3】:

    你可以像这样调用mouseDown:来模拟鼠标点击:

    [self mouseDown: nil];
    

    并在屏幕上获取鼠标位置:

    -(void)mouseDown:(NSEvent *)theEvent {
    
        NSPoint mouseLocation = [NSEvent mouseLocation];
    
        NSLog(@"x:  %f", mouseLocation.x);
        NSLog(@"y:  %f", mouseLocation.y);
    }
    

    【讨论】:

    • 这需要已经知道鼠标事件的正确目标。
    • @JoshCaswell 知道鼠标事件的正确目标是什么意思?
    • 这看起来是正确的方向,但我想我需要为鼠标点击构建一个合适的 NSEvent 对象。并且该代码应该包含鼠标位置,是吗?
    • 只有在光标下的对象已经被识别后才会调用该方法;它不做任何调度。 @粘土
    • @claytontstanley:顺便说一句:负坐标不一定在视图之外。 (考虑在具有两台监视器的系统上发生的情况,右边的一台设置为“主”。)过去认为从 -32768 到 -32000 的坐标绝对无效是合理的,但我怀疑这仍然是正确的。跨度>
    猜你喜欢
    • 2017-04-17
    • 2022-11-18
    • 1970-01-01
    • 1970-01-01
    • 2011-12-30
    • 2011-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多