实现自定义Cocoa事件跟踪循环

时间:2017-05-16 03:24:50

标签: macos cocoa

我正在开发一个自定义跨平台UI库,它需要一个显示弹出窗口的同步“ShowPopup”方法,运行一个事件循环直到它完成,并在弹出窗口外部或按下escape时自动取消。需要将键盘,鼠标和滚轮事件分派到弹出窗口,但是在循环运行时需要将其他事件(绘画,绘图,计时器等)分派到常规目标。

编辑:为了澄清,通过弹出窗口,我的意思是这种菜单式弹出窗口,而不是警报/对话框等......

Cantabile Popup

在Windows上,我通过调用GetMessage / DispatchMessage并根据需要过滤和分派消息,相当简单地实现了这一点。工作正常。

我对Cocoa / OS X的经验却少得多,但发现整个事件循环/调度范例有点令人困惑。我已经看过以下文章,它解释了如何实现与我需要的非常类似的鼠标跟踪循环:

http://stpeterandpaul.ca/tiger/documentation/Cocoa/Conceptual/EventOverview/HandlingMouseEvents/chapter_5_section_4.html

但是...有一些与我有关的事情。

  1. 链接文章指出:“应用程序的主线程无法在事件跟踪循环期间处理任何其他请求,并且计时器可能无法触发”。也许不会?为什么不,如果没有,如何确保他们这样做?

  2. nextEventMatchingMask:untilDate:inMode:dequeue:状态的文档“与指定事件类型之一不匹配的事件将保留在队列中。”这看起来有点奇怪。这是否意味着如果事件循环仅询问鼠标事件,那么一旦循环结束,任何按下的键都将被处理?那太奇怪了。

  3. 是否可以在不删除事件队列的情况下查看事件队列中的消息。例如:我的库的Windows版本使用此版本在外部单击时关闭弹出窗口,但将click事件留在队列中,以便在另一个按钮的弹出窗口外单击不需要再次单击。

    < / LI>
  4. 我已经阅读并重新阅读了关于运行循环模式但仍然没有真正得到它。对这些内容的一个很好的解释是很好的。

  5. 是否有其他为弹出窗口实现事件循环的好例子。更好的是内置NSApplication运行循环的伪代码。

  6. 另一种放置所有这些的方法...... Cocoa相当于Windows PeekMessage(..., PM_REMOVE)PeekMessage(..., PM_NOREMOVE)DispatchMessage()

    非常感谢任何帮助。

1 个答案:

答案 0 :(得分:2)

&#34; popup&#34;究竟是什么?当你使用这个词时?该术语在不同的GUI API中意味着不同的东西。它只是一个模态对话窗口吗?

要对问题进行修改的更新:

您似乎只想实现自定义菜单。 Apple提供了一个示例项目CustomMenus,该项目说明了该技术。它是其中一个WWDC 2010会话视频Session 145, "Key Event Handling in Cocoa Applications"的伴侣。

根据您需要实现的目标,您可能需要使用NSAlert。或者,您可以使用自定义窗口,然后使用-runModalForWindow:的{​​{1}}方法以模态方式运行它。

为了满足用户在窗口外单击时结束模态会话的要求,您可以使用本地事件监视器。在(现代的,当前的)Cocoa事件处理指南Monitoring Events中,甚至还有一个这样的功能的例子。

所有这些都说明了这里(希望不再相关)你的具体问题的答案:

  
      
  1. 链接文章指出:&#34;应用程序的主线程无法在事件跟踪循环期间处理任何其他请求   定时器可能不会开火#34;也许不会?为什么不,如果没有,如何制作   他们确定吗?
  2.   

因为计时器是在特定的运行循环模式或一组模式中安排的。请参阅下面问题4的答案。运行事件跟踪循环时,通常会使用事件跟踪模式,因此未在该模式下调度的计时器将无法运行。

您可以使用默认模式进行事件跟踪循环,但这并不是一个好主意。这可能会导致意外的重新入侵。

假设您的弹出窗口类似于模态窗口,您应该使用NSApplication

  
      
  1. nextEventMatchingMask:untilDate:inMode:dequeue:的文档   state&#34;与指定事件类型之一不匹配的事件是   留在队列中。&#34;。这看起来有点奇怪。这是否意味着如果   一个事件循环只询问鼠标事件然后按任何键   循环完成后处理?这很奇怪。
  2.   

是的,这意味着什么。由您来阻止这种奇怪的结果。如果您在这十年中阅读了“可可事件处理指南”的一个版本,那么您将找到a section on how to deal with this。 ;-P

  
      
  1. 是否可以在不删除事件队列的情况下查看事件队列中的消息。例如:我的库的Windows版本使用它来关闭   弹出窗口在外面点击时,但是将click事件留在了   排队,以便在另一个按钮的弹出窗口外点击不会   需要再次点击。
  2.   

是。你注意到了#34; dequeue:&#34;参数NSModalPanelRunLoopMode?如果您为此传递nextEventMatchingMask:untilDate:inMode:dequeue:,则事件将保留在队列中。

  
      
  1. 我已经阅读并重新阅读了关于运行循环模式但仍然没有真正得到它。对这些内容的一个很好的解释是很好的。
  2.   

如果不知道你对此感到困惑以及Apple guide如何让你失败,很难知道告诉你什么。

您是否熟悉使用NOselect()poll()epoll()的循环处理多个异步通信渠道?它有点像那样,但更加自动化。您不仅要构建一个数据结构,列出您要监视的输入源以及您感兴趣的那些输入源上的特定事件,而且每个输入源还有一个与之关联的回调。运行run循环就像调用上述函数之一来等待输入,但是当输入到达时,调用与源相关的回调来处理该输入。您可以运行该循环的单圈,运行它直到特定时间,或者甚至无限期地运行它。

使用运行循环,输入源可以组织成集。这些集合被称为&#34;模式&#34;并通过名称(即字符串)进行识别。当您运行一个运行循环时,您可以通过指定它应该运行的模式来指定它应该监视哪组输入源。其他输入源仍然是运行循环已知的,但只是暂时被忽略。

kevent()方法或多或少是在内部运行线程的运行循环。除了运行循环中已存在的任何输入源之外,它还临时添加一个输入源来监视来自窗口系统的事件,包括鼠标和键事件。

  
      
  1. 是否有其他为弹出窗口实现事件循环的好例子。更好的是内置的伪代码   NSApplication run loop确实。
  2.   

有旧Apple样本代码,实际上是他们对GLUT的实现。它提供a subclass of NSApplication并覆盖-nextEventMatchingMask:untilDate:inMode:dequeue:方法。当你剥离一些仅与应用程序启动或GLUT相关的东西时,它非常简单。它只是-run-nextEventMatchingMask:...的循环。