2010-10-11 8 views
17

Mam słuchacza myszy. Ma trochę kodu reagującego na zdarzenia mouseUp i mouseDown. Działa to poprawnie.Zdarzenia MouseDown nie są dostarczane aż do MouseUp, gdy obecne jest źródło przeciągania.

Jednak, jak tylko dodaję DragSource, moje zdarzenie mouseDown nie jest już dostarczane - dopóki nie zwolnię przycisku myszy!

To jest trywialne do odtworzenia - poniżej jest prosty program, który zawiera zwykłą powłokę za pomocą tylko myszy słuchacza i słuchacza drag. Gdy uruchomię to (na komputerze Mac), a następnie naciśnie i przytrzymam przycisk myszy, nic się nie dzieje - ale zaraz po zwolnieniu przycisku myszy natychmiast wyświetlana jest zarówno dostarczona mysz, jak i zdarzenia związane z myszką. Jeśli skomentuję źródło przeciągania, zdarzenia myszy są dostarczane tak, jak powinny.

Rozglądałem się za innych z podobnymi problemami, a najbliżej znalazłem do wyjaśnienia jest to:

https://bugs.eclipse.org/bugs/show_bug.cgi?id=26605#c16 „Jeśli hak przeciągnij wykryć, system operacyjny musi jeść zdarzeń myszy aż do niej określa, czy przeciągasz, czy nie. "

Jednak nie rozumiem, dlaczego to prawda - dlaczego system operacyjny musi jeść zdarzenia myszy, aby określić, czy mam opór, czy nie? Przeciąganie nie rozpocznie się, dopóki nie zostanie wywołane zdarzenie "myszka" z wciśniętym przyciskiem.

Co ważniejsze: Czy ktoś może zaproponować obejście tego problemu? (Próbowałem dynamicznie dodawać i usuwać moje źródło przeciągania po naciśnięciu myszy, ale wtedy nie mogłem się przeciągnąć, aby funkcja działała prawidłowo, ponieważ nigdy nie widziałem początkowego naciśnięcia klawisza - i nie mogę znaleźć sposobu na programowe zainicjowanie . drag)

Oto przykładowy program:

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

Próbowałem już, a twój kod działa w systemie Windows. Może być błąd specyficzny dla systemu operacyjnego – nanda

+0

Właśnie próbowałem tego na Ubuntu 10.04 i to sort-of works. Naciśnij lewy przycisk myszy, nie otrzymasz żadnego zdarzenia. Przesuń mysz, otrzymasz 'mouseDown' i' dragStart' * w tym samym czasie *. Kiedy puścisz, otrzymasz zdarzenie 'mouseUp'. Jeśli trzymasz mysz nieruchomo i naciśniesz lewy przycisk, otrzymasz "mouseDown" po zauważalnym 1-2 sekundowym opóźnieniu. Tak czy inaczej, 'mouseDown' jest zawsze widoczne przed puszczeniem myszy. Musi być problem z SWT na Macu? – richq

+0

Dziękuję wam obojgu - wygląda na to, że jest to zdecydowanie platforma. Jednak wygląda na to, że jest on również uszkodzony na Linuksie - jedno sekundowe opóźnienie przed dostarczeniem MouseDown nie zadziała, ponieważ powodem, dla którego próbuję zmienić sposób obsługi z mouseUp na mouseDown, jest sprawienie, aby interfejs był bardziej responsywny. –

Odpowiedz

8

aby odpowiedzieć na konkretne pytanie dlaczego tak się dzieje - na kakao nie uważamy przeciągnij aby zaczęli aż mysz przeniósł kilka pikseli . To zabezpiecza przed "przypadkowymi" przeciągnięciami, jeśli jesteś niechlujny dzięki kliknięciom. W systemach Linux i Win32 zestaw narzędzi do okien może wykonywać wykrywanie przeciągania. Jeśli po prostu przytrzymasz przycisk, czas wykrywania zostanie przekroczony, a mysz zostanie dostarczona. Na kakao nie mamy czasu na przerwę, dlatego nic się nie dzieje, dopóki nie zostanie wykryty przeciągnięcie lub nie pojawi się mysz.

To dużo szczegółów, ale wniosek jest taki, że zachowanie jest niespójne, i zawsze powinniśmy być w stanie natychmiast dostarczyć mysz, nie czekając na zakończenie wykrywania oporu.

Nie widzę obejścia, ponieważ dzieje się to zanim Kontrola zobaczy zdarzenie.

Zobacz this bug z łatkami dla win32, gtk i kakao SWT.

+0

Wow! Łatka, na wszystkie platformy nie mniej, w ciągu jednego dnia lub dwóch! Imponujący! Dziękuję Ci! –

+0

Wygląda na to, że opóźniony MouseDown podczas przeciągania jest zamierzony. Dla tych, którzy grają w domu, obejrzyj wyżej wymieniony błąd Eclipse po więcej szczegółów. –

1

Miałem do czynienia z tym samym problemem i znalazłem rozwiązanie. Po dołączeniu DragSource do niestandardowego widżetu, pętla zdarzeń zostanie zablokowana w haku myszy widgetu i będzie jadła zdarzenia ruchu myszy w celu wykrycia przeciągnięcia. (Sprawdziłem tylko kod GTK SWT, aby to znaleźć, więc może działać trochę inaczej na innych platformach, ale moje rozwiązanie działa na GTK, Win32 i Cocoa.) W mojej sytuacji nie byłem tak bardzo Interesowało mnie wykrycie zdarzenia typu "my mouse down", gdy to się stało, ale byłem zainteresowany znacznym zmniejszeniem opóźnienia wykrywania oporu, ponieważ cały cel mojej implementacji Canvas polegał na tym, że użytkownik przeciągał coś.Aby wyłączyć blokadę pętli zdarzeń oraz wbudowaną wykrywania oporu, wszystko co musisz zrobić, to:

setDragDetect(false); 

W moim kodu, robię to przed załączeniem DragSource. Jak już zaznaczyłeś, pozostawi to ci problem, że nie możesz już zainicjować przeciągania. Ale znalazłem również rozwiązanie tego problemu. Na szczęście generowanie zdarzeń przeciągania jest czystą Java, a nie specyficzne dla platformy w SWT (tylko wykrywanie drag jest). Możesz więc po prostu wygenerować własne zdarzenie DragDetect w czasie, gdy jest to dla ciebie wygodne. Połączyłem MouseMoveListener z moim Canvas i przechowuje on ostatnią pozycję myszy, zgromadzoną odległość przeciągania i czy już wygenerował zdarzenie DragDetect (pośród innych użytecznych rzeczy). To jest implementacja 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; 
} 

I to zastąpi wbudowane, blokujące wykrywanie oporu dla ciebie.

Powiązane problemy