События MouseDown не доставляются до MouseUp, когда присутствует источник перетаскивания. - PullRequest
17 голосов
/ 11 октября 2010

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

Ответы [ 2 ]

8 голосов
/ 21 октября 2010

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

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

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

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

1 голос
/ 12 января 2012

Я столкнулся с той же проблемой и нашел решение. После того, как вы подключите 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;
}

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

...