进一步完善 -- GEF创建助手工具条
昨天讨论了在图形元素上显示工具条的方法,应该说工作的还不是很完美,因为在选定了创建Connection的工具后,并不能像使用palette那样,在鼠标移动的过程中,有一个连接动态跟随,当鼠标释放后,如果释放位置在一个图形元素之上,那么将建立这个Connection,如果不在,那么这个连接将自动消失;今天想讨论一下如何实现这个功能;
我们知道GEF是以Draw2D的LightweightSystem作为实现的基础的,在LightweightSystm中对于mouseDown和mouseMove的实现如下:
2 public void mouseDoubleClick(MouseEvent e) {
3 getEventDispatcher().dispatchMouseDoubleClicked(e);
4 }
5
6 /** @see MouseListener#mouseDown(MouseEvent) */
7 public void mouseDown(MouseEvent e) {
8 getEventDispatcher().dispatchMousePressed(e);
9 }
10
11 /** @see MouseTrackListener#mouseEnter(MouseEvent) */
12 public void mouseEnter(MouseEvent e) {
13 getEventDispatcher().dispatchMouseEntered(e);
14 }
15
16 /** @see MouseTrackListener#mouseExit(MouseEvent) */
17 public void mouseExit(MouseEvent e) {
18 getEventDispatcher().dispatchMouseExited(e);
19 }
20
21 /** @see MouseTrackListener#mouseHover(MouseEvent) */
22 public void mouseHover(MouseEvent e) {
23 getEventDispatcher().dispatchMouseHover(e);
24 }
25
26 /** @see MouseMoveListener#mouseMove(MouseEvent) */
27 public void mouseMove(MouseEvent e) {
28 getEventDispatcher().dispatchMouseMoved(e);
29 }
30
31 /** @see MouseListener#mouseUp(MouseEvent) */
32 public void mouseUp(MouseEvent e) {
33 getEventDispatcher().dispatchMouseReleased(e);
34 }
35
36 /** @see DisposeListener#widgetDisposed(DisposeEvent) */
37 public void widgetDisposed(DisposeEvent e) {
38 getUpdateManager().dispose();
39 }
40
由此可见,Draw2D对于鼠标事件的处理是由Event Dispacher进行派发的。那么这些派发的事件是怎么处理的呢?在org.eclipse.gef.tools.AbstractTool类中有很多相应的handleXXX方法,例如:handleDoubleClick、handleDrag方法等等,这就是处理这些鼠标事件的位置,当然这些方法在这个类中,没有做什么处理,仅仅返回了false;但是如果我们继续向下查找的话,会发现其子类AbstractConnectionCreationTool重写了一些handle方法,其中之一就是handleDrag,而handleDrag调用了handleMove;
* @see org.eclipse.gef.tools.AbstractTool#handleDrag()
*/
protected boolean handleDrag() {
if (isInState(STATE_CONNECTION_STARTED))
return handleMove();
return false;
}
/**
* @see org.eclipse.gef.tools.AbstractTool#handleDragInProgress()
*/
protected boolean handleDragInProgress() {
if (isInState(STATE_ACCESSIBLE_DRAG_IN_PROGRESS))
return handleMove();
return false;
}
/**
* @see org.eclipse.gef.tools.AbstractTool#handleFocusLost()
*/
protected boolean handleFocusLost() {
if (isInState(STATE_CONNECTION_STARTED)) {
eraseSourceFeedback();
eraseTargetFeedback();
setState(STATE_INVALID);
handleFinished();
}
return super.handleFocusLost();
}
/**
* @see org.eclipse.gef.tools.TargetingTool#handleHover()
*/
protected boolean handleHover() {
if (isInState(STATE_CONNECTION_STARTED))
updateAutoexposeHelper();
return true;
}
/**
* @see org.eclipse.gef.tools.TargetingTool#handleInvalidInput()
*/
protected boolean handleInvalidInput() {
eraseSourceFeedback();
setConnectionSource(null);
return super.handleInvalidInput();
}
/**
* @see org.eclipse.gef.tools.AbstractTool#handleMove()
*/
protected boolean handleMove() {
if (isInState(STATE_CONNECTION_STARTED) && viewer != getCurrentViewer())
return false;
if (isInState(STATE_CONNECTION_STARTED | STATE_INITIAL
| STATE_ACCESSIBLE_DRAG_IN_PROGRESS)) {
updateTargetRequest();
updateTargetUnderMouse();
showSourceFeedback();
showTargetFeedback();
setCurrentCommand(getCommand());
}
return true;
}
看到handleMove中的那个showSourceFeedback()方法了吧,其实我们今天讨论的问题,在GEF框架中就是通过它实现的;爬源码,我们可以知道这个方法事实上是调用了AbstractEditPart的showSourceFeedback()方法,而后者又调用了GraphicalNodeEditPolicy的showSourceFeedback方法,这个方法又调用了showCreationFeedback方法;
* Shows feedback during a creation.
*
* @param request
* CreateConnectionRequest
*/
protected void showCreationFeedback(CreateConnectionRequest request) {
FeedbackHelper helper = getFeedbackHelper(request);
Point p = new Point(request.getLocation());
helper.update(getTargetConnectionAnchor(request), p);
}
/**
* calls {@link #showCreationFeedback(CreateConnectionRequest)} when
* appropriate.
*
* @see org.eclipse.gef.EditPolicy#showSourceFeedback(Request)
*/
public void showSourceFeedback(Request request) {
if (REQ_CONNECTION_END.equals(request.getType()))
showCreationFeedback((CreateConnectionRequest) request);
}
这下我们就明白了,我们只要照猫画虎,在我们的工具条的mouse move方法中实现上面的那个showCreationFeedback方法就行了;以下就是我们的实现:
3 request = new CreateConnectionRequest();
4 ScalableFreeformRootEditPart root = (ScalableFreeformRootEditPart) getSourceEditPart().getRoot();
5 double zoom = root.getZoomManager().getZoom();
6
7 request.setLocation(new Point(hoverX * zoom, hoverY * zoom));
8 request.setType("connection start");
9
10 FeedbackHelper feedbackHelper = new FeedbackHelper();
11 Point p = new Point(me.x, me.y);
12 Connection connectionFeedback = FigureFactory.createNewWorkflowPath(null);
13
14 connectionFeedback.setConnectionRouter(((ConnectionLayer) getLayer(LayerConstants.CONNECTION_LAYER)).getConnectionRouter());
15 connectionFeedback.setSourceAnchor(getSourceConnectionAnchor(request));
16 feedbackHelper.setConnection(connectionFeedback);
17
18 IFigure figure = getLayer(LayerConstants.FEEDBACK_LAYER);
19 for (int i = 0; i < figure.getChildren().size(); i++) {
20 if (figure.getChildren().get(i) instanceof Connection) {
21 figure.remove((IFigure) figure.getChildren().get(i));
22 }
23 }
24 getLayer(LayerConstants.FEEDBACK_LAYER).add(connectionFeedback);
25 request.setLocation(new Point(me.x * zoom, me.y * zoom));
26 feedbackHelper.update(null, new Point(me.x + offsetX * zoom, me.y + offsetY * zoom));
27 }
28
至此,我们的问题就解决了。但是还有一个问题,那就是,如果鼠标被释放了,那么这个被放置在反馈层的Connection应该被擦除掉,而同时还应该根据鼠标的位置决定是不是应该创建Connection;所以我们还应该再改一下mouse released方法,增加如下处理:
IFigure feedBackLayer = getLayer(LayerConstants.FEEDBACK_LAYER);
feedBackLayer.getChildren().clear();
2010-08-05更正,以上部分不正确,应该是:
2
3 for (int i = 0; i < feedBackLayer.getChildren().size(); i++) {
4 if (feedBackLayer.getChildren().get(i) instanceof Connection) {
5 feedBackLayer.remove((IFigure) feedBackLayer.getChildren().get(i));
6 }
7 }
8 ......
通过以上的处理,我们就可以实现类似于palette中Connection Tool的功能了。当我们在一个图形元素上Hover时,首先程序先将与悬停位置最近的锚点作为源锚点,并显示工具条,当我们点击工具条上的Connection图标时,随着鼠标的拖动,一个Connection将动态跟随鼠标的移动被画出来,随着鼠标的释放,Connection将根据释放位置决定是否建立一个Connection,还是将这个动态的Connection清除掉;
posted on 2010-08-04 16:04 wayne.wang 阅读(1598) 评论(5) 编辑 收藏 举报