当存在Drag Source时,MouseUp事件直到MouseUp才会传递

时间:2010-10-11 16:54:17

标签: swt eclipse-rcp

我有一个鼠标监听器。它有一些代码来响应mouseUp和mouseDown事件。这可以正常工作。

但是,只要我添加一个DragSource,我的mouseDown事件就不再发送 - 直到我释放鼠标按钮!

重现这一点很简单 - 下面是一个简单的程序,它包含一个只有鼠标监听器和拖动监听器的普通shell。当我运行它(在Mac上),然后按住鼠标按钮时,没有任何反应 - 但是一旦我释放鼠标按钮,我立即看到鼠标按下和鼠标按下事件。如果我注释掉拖动源,那么鼠标事件将以它们应该的方式传递。

我搜索过其他有类似问题的人,我发现最接近解释的是:

https://bugs.eclipse.org/bugs/show_bug.cgi?id=26605#c16 “如果挂钩拖动检测,操作系统需要吃掉鼠标事件,直到它确定你是否已拖动。”

但是,我不明白为什么这是真的 - 为什么操作系统必须吃鼠标事件以确定我是否有阻力?直到我按下按钮时出现鼠标-move-事件才会开始拖动。

更重要的是:有人可以提出解决方法吗? (当我按下鼠标时,我尝试动态添加和删除我的拖动源,但是由于它从未看到初始按键,我无法拖动并放下功能 - 我找不到以编程方式启动的方法拖累。)

以下是示例程序:

    package swttest;

    import org.eclipse.swt.dnd.DND;
    import org.eclipse.swt.dnd.DragSource;
    import org.eclipse.swt.dnd.DragSourceEvent;
    import org.eclipse.swt.dnd.DragSourceListener;
    import org.eclipse.swt.events.MouseEvent;
    import org.eclipse.swt.events.MouseListener;
    import org.eclipse.swt.widgets.Display;
    import org.eclipse.swt.widgets.Shell;

    public class SwtTest {
        public static void main(String[] args) {
            final Display display = new Display();
            final Shell shell = new Shell(display);
            shell.addMouseListener(new MouseListener() {
                public void mouseUp(MouseEvent e) {
                    System.out.println("mouseUp");
                }

                public void mouseDown(MouseEvent e) {
                    System.out.println("mouseDown");
                }

                public void mouseDoubleClick(MouseEvent e) {
                    System.out.println("mouseDoubleClick");
                }
            });
            DragSourceListener dragListener = new DragSourceListener() {

                public void dragFinished(DragSourceEvent event) {
                    System.out.println("dragFinished");

                }

                public void dragSetData(DragSourceEvent event) {
                    System.out.println("dragSetData");

                }

                public void dragStart(DragSourceEvent event) {
                    System.out.println("dragStart");
                }
            };
            DragSource dragSource = new DragSource(shell, DND.DROP_COPY | DND.DROP_MOVE);
            dragSource.addDragListener(dragListener);
            shell.pack();
            shell.open();
            while (!shell.isDisposed()) {
                if (!display.readAndDispatch())
                    display.sleep();
            }
            display.dispose();
        }
    }

2 个答案:

答案 0 :(得分:8)

要回答您关于为什么会发生这种情况的具体问题 - 在Cocoa上,我们不会考虑在鼠标移动几个像素之前开始拖动。如果你匆匆点击,这可以确保防止“意外”拖延。在Linux和Win32上,窗口工具包可以进行拖动检测。如果您只是按住按钮,则检测超时并且鼠标按下。在Cocoa上我们没有时间,这就是为什么在检测到拖动或鼠标移动之前没有任何反应。

这是很多细节,但结论是行为不一致,我们应该始终能够立即交付鼠标,而无需等待拖动检测完成。

我没有看到解决方法,因为这是在Control看到事件之前发生的。

请参阅this bug,其中包含针对win32,gtk和cocoa SWT的补丁。

答案 1 :(得分:1)

我遇到了同样的问题并找到了解决方案。将DragSource附加到自定义窗口小部件后,将在该窗口小部件的鼠标按钮中阻止事件循环,并将使用鼠标移动事件来检测拖动。 (我只是查看了SWT的GTK代码来找到它,所以它可能在其他平台上有所不同,但我的解决方案适用于GTK,Win32和Cocoa。)在我的情况下,我没有那么多有兴趣在它发生时检测鼠标按下事件,但我有兴趣显着减少拖动检测延迟,因为我的Canvas实现的整个目的是让用户拖动东西。要关闭事件循环阻止和内置拖动检测,您所要做的就是:

setDragDetect(false);

在我的代码中,我在附加DragSource之前这样做。正如您已经指出的那样,这将为您留下无法再发起拖动的问题。但我也找到了解决方案。幸运的是,拖动事件生成是纯Java,而不是特定于SWT的平台(只有拖动检测)。因此,您可以在方便的时候生成自己的DragDetect事件。我已经将一个MouseMoveListener附加到我的Canvas,它存储了最后一个鼠标位置,累积的拖动距离以及它是否已经生成了DragDetect事件(以及其他有用的东西)。这是mouseMove()实现:

public void mouseMove(MouseEvent e) {
    if (/* some condition that tell you are expecting a drag*/) {

        int deltaX = fLastMouseX - e.x;
        int deltaY = fLastMouseY - e.y;

        fDragDistance += deltaX * deltaX + deltaY * deltaY;

        if (!fDragEventGenerated && fDragDistance > 3) {
            fDragEventGenerated = true;

            // Create drag event and notify listeners.
            Event event = new Event();
            event.type = SWT.DragDetect;
            event.display = getDisplay();
            event.widget = /* your Canvas class */.this;
            event.button = e.button;
            event.stateMask = e.stateMask;
            event.time = e.time;
            event.x = e.x;
            event.y = e.y;
            if ((getStyle() & SWT.MIRRORED) != 0)
                event.x = getBounds().width - event.x;

            notifyListeners(SWT.DragDetect, event);
        }
    }

    fLastMouseX = e.x;
    fLastMouseY = e.y;
}

这将取代内置的阻止拖动检测。