我有一个鼠标监听器。它有一些代码来响应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();
}
}
答案 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;
}
这将取代内置的阻止拖动检测。