ApplicationCommandManager 3-2

由上例可知,如此一来,所有程序命令目标类的命令全部集中在顶级窗口中调度,即:ApplicationCommandManager程序命令管理器对象将所有的命令目标类与所有命令的快捷键、命令所执行的功能等等集中到了一起,不管当前哪个命令目标类拥有焦点,只要按下快捷键,就由顶级窗口统一调度和分配。

很大程度上讲,程序命令管理器就像一个桥梁和纽带,更像一个所有命令的容器,谁执行我不管,我只保管所有命令,接收到有关消息后(比如用户按下了快捷键),我就及时捕获该按键,并将其转发给已经注册给我的命令目标对象。这一点非常符合OOP的职责单一、低耦合、依赖倒转等设计原则,更大大简化了编程中对象之间的组合与聚合。理解并运用JUCE类库的这一机制,非常重要!

因为按钮也可以绑定具体的命令,因此,在点击按钮后执行具体功能时,完全可以不采用传统的做法(传统做法是:内容组件中实例化按钮对象后,该按钮对象必须绑定捕获器,而后内容组件继承Button::Listener类,并实现其纯虚函数buttonClicked(),在该函数中对按钮点击事件进行判断处理),而是全部通过程序命令目标和程序命令管理器来实现。当然,这两套消息处理模式各有特点与最适合的情况,具体编程时需根据实际情况进行斟酌。

以上步骤完成后,命令目标类中的菜单、按钮等等一切可视或不可视的组件(控件)将可以直接使用程序命令进行触发。其中弹出式菜单添加菜单项时使用addCommandItem()函数,按钮则使用setCommandToTrigger()。此时,运行程序,按Ctrl+Q即可退出。原因:该快捷键是每个JUCE程序预置的退出命令。而程序本身作为命令目标对象已经注册到命令管理器中了。

程序命令不仅能够由快捷键触发,还能够被直接调用,命令管理器和命令目标这两者都有一个invokeDirectly()函数,可用来直接调用某个命令。所不同的是,命令管理器的invokeDirectly(),将命令转发给默认的命令目标,而命令目标的invokeDirectly()则直接调用本类中的某个具体命令。invokeDirectly()函数有两个参数:命令ID,是否同步。
不同的命令目标类也许使用了具有相同快捷键的程序命令,此时,ApplicationCommandManager将尝试选择最佳的ApplicationCommandTarget来接收指定的命令。内部通过命令目标是否拥有键盘焦点等机制进行判断,并自动搜索组件的上级层次(如果父级组件也能够响应该命令)。若当前ApplicationCommandTarget对命令不感兴趣或无法接收,则自动传递给它的上一级组件。命令目标类可使用getNextCommandTarget()手工指定下一个接受命令的组件。注意:此函数中所指定的下一个组件必须也是命令目标类才行。如此一直到getNextCommandTarget()返回0为止。如果此时命令依然无法被接收和处理,则传递给当前的JUCEApplication对象。此时,程序本身成为命令目标(包含退出等命令)。

下面给出一个窗口框架类的构造函数,该函数值得仔细研究和借鉴:(代码略)。

ApplicationCommandManager除具有上述功能之外,其本身还可产生消息。比如:它所持有的命令被调用,或者被改变时,均可产生相应的消息。这些消息可由ApplicationCommandManagerListener类捕获并处理,命令捕获器是一个独立的抽象基类,Button和MenuBarModel均是其派生类。使用该类捕获命令管理器的消息,思路与流程与其他具有addListener()函数的控件完全一致。

ApplicationCommandManagerListener的两个纯虚函数为:

  1. applicationCommandInvoked () 要执行某个程序命令时自动调用该函数
  2. applicationCommandListChanged () 命令注册与否,激活与否时自动调用此函数