【问题标题】:MouseDown events are not delivered until MouseUp when a Drag Source is present当存在拖动源时,直到 MouseUp 才传递 MouseDown 事件
【发布时间】:2023-04-05 13:04:02
【问题描述】:

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

但是,一旦我添加了 DragSource,我的 mouseDown 事件就不再传递 - 直到我释放鼠标按钮!

这很容易重现 - 下面是一个简单的程序,它包含一个普通的外壳,只有一个鼠标监听器和一个拖动监听器。当我(在 Mac 上)运行它并按住鼠标按钮时,什么也没有发生 - 但是只要我松开鼠标按钮,我就会立即看到鼠标按下和鼠标按下事件都已传递。如果我注释掉拖动源,那么鼠标事件将按应有的方式传递。

我已经搜索过其他有类似问题的人,我找到的最接近解释的是:

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

但是,我不明白为什么这是真的——为什么操作系统必须吃掉鼠标事件来确定我是否有拖动?直到我按下按钮时鼠标移动事件才会开始拖动。

更重要的是:任何人都可以提出解决方法吗? (我尝试在按下鼠标时动态添加和删除我的拖动源,但是由于它从未看到初始按键,因此我无法正常进行拖放操作 - 而且我找不到以编程方式启动的方法拖动。)

这是示例程序:

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

【问题讨论】:

  • 我已经尝试过了,您的代码在 Windows 上运行。可能是特定于操作系统的错误
  • 我刚刚在 Ubuntu 10.04 上试过这个,它有点工作。按下鼠标左键你没有得到任何事件。移动鼠标同时获得mouseDown 和dragStart。当你放手时,你会得到mouseUp 事件。如果您保持鼠标不动并按下左键,您会在明显的 1-2 秒延迟后收到mouseDown。无论哪种方式,在放开鼠标之前总会看到mouseDown。一定是 Mac 上的 SWT 问题?
  • 谢谢你们 - 听起来这绝对是特定于平台的。但是,听起来它在 Linux 上也很糟糕——在传递 mouseDown 之前延迟一秒是行不通的,因为我试图将一些处理从 mouseUp 切换到 mouseDown 的原因是为了让 UI 感觉更灵敏。 .
  • 我今天能够在 Windows 上进行测试(Vista,使用最新版本的 SWT),但我看不到 nanda 看到的内容 - 对我来说,它的损坏方式与它的打开方式相同Linux:mouseDown 事件不会立即传递;它不像在 Mac 上那样无限期举行,而是在一秒钟后交付(但这似乎是因为在 Windows 上它考虑按住按钮一秒钟拖动,所以一秒钟后它发布了一个 dragStart 事件,当然也像在 Mac 上一样触发 mouseDown。)
  • 救命问题!谢谢托尔!

标签: swt eclipse-rcp


【解决方案1】:

回答您关于为什么会发生这种情况的具体问题 - 在 Cocoa 上,我们认为直到鼠标移动几个像素后才开始拖动。如果您对点击马虎,这可以确保防止“意外”拖动。在 Linux 和 Win32 上,窗口工具包可以进行拖动检测。如果您只是按住按钮,则检测超时并且鼠标按下。在 Cocoa 上,我们没有超时,这就是为什么在检测到拖动或鼠标向上之前什么都不会发生的原因。

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

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

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

【讨论】:

  • 哇!一个补丁,不少于所有平台,在一两天内!感人的!谢谢!
  • 显然在拖动过程中延迟 MouseDown 是故意的。对于那些在家玩的人,请观看上面提到的 Eclipse 错误以了解更多详细信息。
【解决方案2】:

我遇到了同样的问题并找到了解决方案。将 DragSource 附加到自定义小部件后,事件循环将被阻止在该小部件的鼠标按下钩子中,并将吃掉鼠标移动事件以检测拖动。 (我只是查看了 SWT 的 GTK 代码来发现这一点,所以它在其他平台上的工作方式可能会有所不同,但我的解决方案适用于 GTK、Win32 和 Cocoa。)在我的情况下,我不是那么多有兴趣在鼠标按下事件发生时立即检测它,但我对显着减少拖动检测延迟感兴趣,因为我的 Canvas 实现的全部目的是让用户拖动东西。要关闭事件循环阻塞和内置拖动检测,您只需:

setDragDetect(false);

在我的代码中,我在附加 DragSource 之前执行此操作。正如您已经指出的那样,这会给您带来无法再启动拖动的问题。但我也找到了解决方案。幸运的是,拖动事件的生成是纯 Java 的,而不是 SWT 中特定于平台的(只有拖动检测是)。因此,您可以在方便的时候生成自己的 DragDetect 事件。我在 Canvas 上附加了一个 MouseMoveListener,它存储了最后一个鼠标位置、累积的拖动距离以及它是否已经生成了 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;
}

这将为您替换内置的阻止拖动检测。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-20
    • 2011-01-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多