2010-10-11 1 views
17

Ich habe einen Maus-Listener. Es gibt Code, der auf MouseUp- und MouseDown-Ereignisse reagiert. Dies funktioniert korrekt.MouseDown-Ereignisse werden erst ausgegeben, wenn MouseUp eine Drag-Quelle enthält

Sobald ich jedoch eine DragSource hinzufüge, wird mein mouseDown Event nicht mehr geliefert - bis ich die Maustaste loslasse!

Dies ist trivial zu reproduzieren - unten ist ein einfaches Programm, das eine einfache Shell mit nur einem Maus-Listener und einem Drag-Listener enthält. Wenn ich das (auf einem Mac) ausführe und die Maustaste drücke und halte, passiert nichts - aber sobald ich die Maustaste loslasse, sehe ich sofort sowohl die Mouse-Down- als auch Mouse-Up-Ereignisse. Wenn ich die Ziehquelle auskommentiere, werden die Mausereignisse so geliefert, wie sie sein sollten.

Ich habe für andere mit ähnlichen Problemen gesucht, und die nächste, die ich auf eine Erklärung gefunden habe, ist dies:

https://bugs.eclipse.org/bugs/show_bug.cgi?id=26605#c16 „Wenn Sie ziehen erkennen Haken, das Betriebssystem benötigt, bis es Mausereignisse essen bestimmt, dass Sie entweder gezogen haben oder nicht. "

Allerdings verstehe ich nicht, warum das stimmt - warum muss das Betriebssystem Mausereignisse essen, um festzustellen, ob ich einen Widerstand habe oder nicht? Der Drag startet erst, wenn ich bei gedrückter Taste ein Maus-Bewegungsereignis habe.

Noch wichtiger: Kann jemand einen Workaround vorschlagen? (Ich habe versucht, meine Ziehquelle dynamisch hinzuzufügen und zu entfernen, wenn die Maus gedrückt wird, aber dann konnte ich nicht ziehen & drop um richtig zu funktionieren, da es nie den ersten Tastendruck sah - und ich kann keinen Weg finden, programmgesteuert zu initiieren . drag)

Hier ist das Beispielprogramm:

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

Ich habe es versucht und Ihr Code funktioniert unter Windows. Könnte OS-spezifischen Fehler sein – nanda

+0

Ich habe gerade dies auf Ubuntu 10.04 versucht und es funktioniert irgendwie. Drücken Sie die linke Maustaste, Sie erhalten kein Ereignis. Bewegen Sie die Maus und Sie erhalten 'mouseDown' und' dragStart' * gleichzeitig *. Wenn Sie loslassen, erhalten Sie das 'mouseUp'-Ereignis. Wenn Sie die Maus ruhig halten und den linken Knopf drücken, erhalten Sie nach einer spürbaren Verzögerung von 1-2 Sekunden einen "mouseDown". In beiden Fällen wird 'mouseDown' immer vor dem Loslassen der Maus angezeigt. Muss ein SWT bei Mac Problem sein? – richq

+0

Danke euch beiden - es klingt, als wäre das definitiv plattformspezifisch. Es klingt jedoch so, als wäre es auch unter Linux ziemlich kaputt - eine Sekunde Verzögerung, bevor mouseDown geliefert wird, wird nicht funktionieren, da ich versuche, etwas von mouseUp auf mouseDown umzustellen, um die Benutzeroberfläche ansprechender zu machen. –

Antwort

8

um Ihre Frage zu beantworten, warum dies geschieht - auf Cocoa wir nicht ein Hemmschuh betrachten haben begonnen, bis die Maus ein paar Pixel bewegt hat . Dies schützt vor "versehentlichen" Verzögerungen, wenn Sie mit den Klicks schlampig sind. Unter Linux und Win32 kann das Window Toolkit die Zieherkennung durchführen. Wenn Sie die Taste gedrückt halten, wird die Erkennung unterbrochen und die Maustaste gedrückt gehalten. Auf Cocoa haben wir keine Auszeit, weshalb nichts passiert, bis das Ziehen erkannt wird oder ein Mouse Up passiert.

Das ist eine Menge von Details, aber die Schlussfolgerung ist, dass das Verhalten inkonsistent ist, und wir sollten immer in der Lage sein, die Maus sofort zu liefern, ohne auf die Zieherkennung warten zu müssen.

Ich sehe keine Problemumgehung, da dies geschieht, bevor die Steuerung das Ereignis sieht.

Siehe this bug, die Patches für Win32, GTK und Kakao SWT hat.

+0

Wow! Ein Patch für alle Plattformen, nicht weniger als einen Tag oder zwei! Beeindruckend! Vielen Dank! –

+0

Anscheinend ist das verzögerte MouseDown während eines Ziehens absichtlich. Für diejenigen, die zu Hause mitspielen, beachten Sie bitte den oben genannten Eclipse-Bug für weitere Details. –

1

Ich hatte das gleiche Problem und fand eine Lösung. Sobald Sie eine DragSource an Ihr benutzerdefiniertes Widget angefügt haben, wird die Ereignisschleife im Maus-Hook dieses Widgets blockiert und es werden Mausbewegungsereignisse gegessen, um einen Widerstand zu erkennen. (Ich habe nur den GTK-Code von SWT untersucht, um das herauszufinden, also kann es auf anderen Plattformen ein wenig anders funktionieren, aber meine Lösung funktioniert auf GTK, Win32 und Cocoa.) In meiner Situation war ich nicht so viel Ich interessierte mich dafür, das Mouse-Down-Ereignis genau zu erkennen, als es passierte, aber ich war daran interessiert, die Verzögerung der Verzögerungserkennung erheblich zu reduzieren, da der gesamte Zweck meiner Canvas-Implementierung darin bestand, den Benutzer Dinge zu ziehen.die Ereignisschleife Sperrung und integrierte Drag Erkennung zu deaktivieren, alles, was Sie tun müssen, ist:

setDragDetect(false); 

In meinem Code, ich dies tun, bevor Sie die Dragsource-Befestigung. Wie Sie bereits festgestellt haben, bleibt Ihnen das Problem, dass Sie keinen Widerstand mehr auslösen können. Aber ich habe auch dafür eine Lösung gefunden. Glücklicherweise ist die Ziehereignisgenerierung reines Java und nicht plattformspezifisch in SWT (nur die Zieherkennung). Sie können also Ihr eigenes DragDetect-Ereignis zu einem Zeitpunkt generieren, an dem es für Sie praktisch ist. Ich habe einen MouseMoveListener an meinen Canvas angehängt, und er speichert die letzte Mausposition, die akkumulierte Schleppdistanz und ob er bereits ein DragDetect-Ereignis generiert hat (neben anderen nützlichen Dingen). Dies ist die mouse() Umsetzung:

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

Und das wird die eingebaute, Sperrung drag Erkennung für Sie ersetzen.