Подтвердить что ты не робот

События MouseDown не доставляются до тех пор, пока MouseUp не будет присутствовать при наличии источника перетаскивания

У меня есть слушатель мыши. Он имеет некоторый код для ответа на события 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();
        }
    }
4b9b3361

Ответ 1

Чтобы ответить на ваш конкретный вопрос о том, почему это происходит - на Cocoa мы не рассматриваем перетаскивание, которое нужно было запустить, пока мышь не переместила несколько пикселей. Это позволяет избежать "случайных" перетаскиваний, если вы небрежны с кликами. В Linux и Win32 инструментарий окна может выполнять обнаружение перетаскивания. Если вы просто удерживаете нажатой кнопку, время обнаружения и выключение мыши будут доставлены. На Cocoa у нас нет времени, поэтому ничего не происходит до тех пор, пока не будет обнаружено перетаскивание или не появится мышь.

Это много деталей, но вывод заключается в том, что поведение непоследовательно, и мы всегда должны иметь возможность немедленно доставить мышь, не дожидаясь завершения обнаружения перетаскивания.

Я не вижу обходного пути, так как это происходит до того, как элемент управления видит событие.

Смотрите эта ошибка, в которой есть исправления для win32, gtk и Cocoa SWT.

Ответ 2

Я столкнулся с той же проблемой и нашел решение. Когда вы присоедините DragSource к своему пользовательскому виджету, цикл событий будет заблокирован в этом виджетах с помощью мыши и будет содержать события перемещения мыши для обнаружения перетаскивания. (Я только посмотрел в GTK-код SWT, чтобы найти это, поэтому на других платформах он может работать по-другому, но мое решение работает на 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;
}

И это заменит встроенное, блокирующее обнаружение перетаскивания для вас.