用过GMF就知道,GMF默认生成的editor有一个悬浮的创建助手功能:当把鼠标移动到某个editpart上,稍停,则会出现一个提示条,上面列出当前位置的editpart上可以新建的子元素,直接选择某项进行新建而无需Palette的参与。
就像之前那篇连接助手一样,使用此功能可以大大方便新建过程。所以这里就和大家讨论一下这个功能的自我实现。
没有详细的研究过GMF中这个功能的实现代码,大家有兴趣的可以去参考GMF的实现,这里我只聊聊我自己的想法。
这里我以实现一个很简单的UML图为例,假设只能有以下模型:
OK,这样模型结构就定义完了,最后我希望出现以下图样:
Figure 1: Project Creation Helper
Figure 2: Package Creation Helper
Figure 3: Class Method Creation Helper
Figure 4: Class Field Creation Helper
当鼠标悬停在某个Editpart之上时,对应的创建助手条就会出现,当鼠标在某个项上滑过的时候,该项会高亮,且有虚线围绕。如果此时点击,则在对应位置就会创建出一个新对象。
要实现创建助手条,不用说,大家都能想到:
为了知道每个对象上可创建的子项,我定义了一个接口:IHoverHelper,它只有一个方法:getHoverChildren(),为简单起,我让这方法只要返回一个Class对象数组即可。然后,显然每个需要助手功能的模型都需要实现这个接口。
除此之外,为了知道每个对象对应的图标,我也定义了一个工厂,用来定义模型-图标对,当需要某个对象的图标时,就从这个工厂取。
下面就是在适当的时候让助手条显示出来了。
之前已经说过,悬停的时候出现,所在我让模型的editpart实现MouseMouseListner,然后把这个Listener安装在模型对应的figure上。这样,一个事件触器就定义好了。
假设我们有一个类CreationPopupBarFigure就是用来显示这个助手条。那么,我们就需要在mouseHover()方法里把它显示出来;显然,我们不能让它一直呆在那,所以还需要在mouseMoved()方法里让它消失。
这里有几点需要考虑的:
1.助手条显示图加在谁上的问题
我们知道,一个Figure需要加在某个已经显示出来的Figure上,才能被看到。那我这个提示条加在谁上呢?我就想到RootEditPart的那几个层,有一点可以肯定的就是这个提示条一旦出现,就不应该被什么东西挡住对吧?那它的层次应该比较高。一开始我是想加在FeedBack层,但是发现它对鼠标滑过没反应,最后就加在了Handle层了。
2.对象声明的问题
应该可以肯定,同一时间不可能出现多个提示条对吧?如果是的放,那就是思路出问题了。所以整个程序中应该只有一个提示条出现才对。所以我们可以声明一个static的提示条变量来避免创建多个提示条。
3.消失的问题
之前已经说过了,在mouseHover()方法里显示,在mouseMoved()方法里消失。所以我们首先要让提示条出现的位置在紧挨着鼠标悬停的位置;其次,我们还要空许一些额外的空间,就像一条连接线,也会有一些额外的空间来表示在线的范围内。这样做的目的是避免用户一移动,提示条就消失。
OK,大致实现过程就结束了。最后就到了CreationPopupBarFigure。
大致创建已经讲了,就是显示在handle层上,显示在挨着鼠标的当前位置上。
还有一些需要补充的:
1.自动消失的问题
如果鼠标不动,让它一直显示在那也是不好的。所以我加了一个两秒的消失时间。
2.不自动消失的情况
上面已经说了两种消失情况:鼠标移动、定时消失。想像这样一种情形,我们鼠标正在提示条上滑动以进行选择,结果消失了。这肯定让人很气愤,对吧?
所以还要有一种不消失的情况,那就是在鼠标在条上滑动进行选择的时候。因此我们的提示条和它上面的子项都需要加一个鼠标监听,来确保当鼠标在它们之上移动时,就让提示条一直显示。
OK,最后当鼠标在某项上点击时,创建出一个新对象,大致可以如下写:
CreateRequest createRequest = new CreateRequest(modelClass); createRequest.setLocation(new Point(x, y)); createRequest.setFactory(new SimpleFactory(modelClass)); createRequest.setType(RequestConstants.REQ_CREATE); Command command = host.getCommand(createRequest); host.getViewer().getEditDomain().getCommandStack().execute(command);
试试新功能,我觉得挺好的,不用再点palette了。