进一步完善 -- GEF创建助手工具条

昨天讨论了在图形元素上显示工具条的方法,应该说工作的还不是很完美,因为在选定了创建Connection的工具后,并不能像使用palette那样,在鼠标移动的过程中,有一个连接动态跟随,当鼠标释放后,如果释放位置在一个图形元素之上,那么将建立这个Connection,如果不在,那么这个连接将自动消失;今天想讨论一下如何实现这个功能;

我们知道GEF是以Draw2DLightweightSystem作为实现的基础的,在LightweightSystm中对于mouseDownmouseMove的实现如下:

事件发布
 1         /** @see MouseListener#mouseDoubleClick(MouseEvent) */
 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方法,例如:handleDoubleClickhandleDrag方法等等,这就是处理这些鼠标事件的位置,当然这些方法在这个类中,没有做什么处理,仅仅返回了false;但是如果我们继续向下查找的话,会发现其子类AbstractConnectionCreationTool重写了一些handle方法,其中之一就是handleDrag,而handleDrag调用了handleMove 

handleXXX的实现
/**
     * 
@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框架中就是通过它实现的;爬源码,我们可以知道这个方法事实上是调用了AbstractEditPartshowSourceFeedback()方法,而后者又调用了GraphicalNodeEditPolicyshowSourceFeedback方法,这个方法又调用了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方法就行了;以下就是我们的实现:

 1 public void mouseDragged(MouseEvent me) {
 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(nullnew 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更正,以上部分不正确,应该是:

1 IFigure feedBackLayer = getLayer(LayerConstants.FEEDBACK_LAYER);
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 ......

 

通过以上的处理,我们就可以实现类似于paletteConnection Tool的功能了。当我们在一个图形元素上Hover时,首先程序先将与悬停位置最近的锚点作为源锚点,并显示工具条,当我们点击工具条上的Connection图标时,随着鼠标的拖动,一个Connection将动态跟随鼠标的移动被画出来,随着鼠标的释放,Connection将根据释放位置决定是否建立一个Connection,还是将这个动态的Connection清除掉;

posted on 2010-08-04 16:04  wayne.wang  阅读(1598)  评论(5编辑  收藏  举报

导航