【飞秋】进一步完善 -- GEF创建助手工具条

本文探讨了在GEF框架中实现Connection动态绘制的方法,通过重写工具类中的handleDrag等方法,使得在选定创建Connection工具后,能随着鼠标移动实时显示连接线条,释放鼠标后根据位置决定是否创建。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

我们知道GEF是以Draw2D的LightweightSystem作为实现的基础的,在LightweightSystm中对于mouseDown和mouseMove的实现如下:

事件发布
 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方法,例如:handleDoubleClick、handleDrag方法等等,这就是处理这些鼠标事件的位置,当然这些方法在这个类中,没有做什么处理,仅仅返回了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框架中就是通过它实现的;爬源码,我们可以知道这个方法事实上是调用了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方法就行了;以下就是我们的实现:

 

 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(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();

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

关注技术文章飞秋:http://www.freeeim.com/,24小时专业转载。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值