2010-10-11 2 views
17

J'ai un écouteur de souris. Il a du code pour répondre aux événements mouseUp et mouseDown. Cela fonctionne correctement.Les événements MouseDown ne sont pas envoyés tant que MouseUp n'est pas présent lorsqu'une source de glissement est présente

Cependant, dès que j'ajoute un DragSource, mon événement mouseDown n'est plus délivré - jusqu'à ce que je relâche le bouton de la souris!

Ceci est trivial à reproduire - ci-dessous est un programme simple qui contient un shell simple avec juste un écouteur de souris et un écouteur de traînée. Quand je lance ceci (sur un Mac) et que j'appuie sur le bouton de la souris, rien ne se passe - mais dès que je relâche le bouton de la souris, je vois instantanément les événements de la souris et de la souris. Si je commente la source de glissement, les événements de souris sont livrés comme ils devraient l'être.

J'ai cherché d'autres avec des problèmes similaires, et le plus proche que j'ai trouvé une explication est la suivante:

https://bugs.eclipse.org/bugs/show_bug.cgi?id=26605#c16 « Si vous accrochez glisser la détection, le système d'exploitation a besoin de manger des événements de la souris jusqu'à ce qu'il détermine que vous avez traîné ou non. "

Cependant, je ne comprends pas pourquoi c'est vrai - pourquoi le système d'exploitation doit-il manger des événements de souris pour déterminer si j'ai une traînée ou pas? Le glissement ne commence pas tant que je n'ai pas un événement mouse -move- avec le bouton enfoncé. Plus important encore: Quelqu'un peut-il suggérer une solution de contournement? (J'ai essayé dynamiquement d'ajouter et de supprimer ma source de drag lorsque la souris est pressée, mais je n'ai pas pu faire glisser & drop pour fonctionner correctement car il n'a jamais vu la touche initiale - et je ne trouve pas un moyen de lancer un programme . glisser)

Voici l'exemple de programme:

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

Je l'ai essayé et votre code fonctionne sous Windows. Peut-être un bogue spécifique au système d'exploitation – nanda

+0

Je viens de l'essayer sur Ubuntu 10.04 et ça marche. Appuyez sur le bouton gauche de la souris, vous n'obtenez aucun événement. Déplacez la souris vous obtenez 'mouseDown' et' dragStart' * en même temps *. Quand vous lâchez, vous obtenez l'événement 'mouseUp'. Si vous maintenez la souris immobile et appuyez sur le bouton gauche, vous obtenez un 'mouseDown' après un délai notable de 1-2 secondes. Dans tous les cas, 'mouseDown' est toujours visible avant de lâcher la souris. Doit être un problème SWT sur Mac? – richq

+0

Merci à vous deux - il semble que ce soit définitivement spécifique à la plateforme. Cependant, il semble que Linux soit également très endommagé - un délai d'une seconde avant que mouseDown ne soit livré ne fonctionnera pas puisque la raison pour laquelle je tente de passer de mouseUp à mouseDown est de rendre l'interface utilisateur plus réactive. –

Répondre

8

pour répondre à votre question de savoir pourquoi cela se produit - le cacao, nous ne considérons pas un frein pour avoir commencé jusqu'à ce que la souris a déplacé quelques pixels . Cela assure contre les dragages «accidentels» si vous êtes bâclé avec les clics. Sous Linux et Win32, le toolkit window peut faire la détection de traînée. Si vous maintenez simplement le bouton enfoncé, la détection expire et la souris est enfoncée. Sur Cocoa, nous n'avons pas de temps d'arrêt, c'est pourquoi rien ne se passe tant que la traînée n'est pas détectée ou qu'une souris ne survient. C'est beaucoup de détails, mais la conclusion est que le comportement est incohérent, et nous devrions toujours être capable de livrer la souris immédiatement, sans attendre la fin de la détection de traînée.

Je ne vois pas une solution de contournement, car cela se produit avant que le contrôle voit l'événement.

Voir this bug qui a des correctifs pour win32, gtk et le cacao SWT.

+0

Wow! Un patch, pour toutes les plateformes pas moins, dans un jour ou deux! Impressionnant! Je vous remercie! –

+0

Apparemment, le retard de la souris lors d'un glisser est intentionnel. Pour ceux qui jouent à la maison, s'il vous plaît regarder le bug Eclipse mentionné ci-dessus pour plus de détails. –

1

J'avais rencontré le même problème et trouvé une solution. Une fois que vous avez attaché une DragSource à votre widget personnalisé, la boucle d'événement sera bloquée dans le hook down de ce widget et mange les événements de déplacement de la souris pour détecter un glisser. (J'ai seulement regardé le code GTK de SWT pour le trouver, donc ça peut fonctionner un peu différemment sur d'autres plateformes, mais ma solution fonctionne sur GTK, Win32 et Cocoa.) Dans ma situation, je n'étais pas tellement Je m'intéressais à la détection de l'événement "souris vers le bas", mais je voulais réduire de manière significative le délai de détection de la traînée, car l'objectif de mon implémentation de Canvas était de faire glisser des choses.Pour désactiver le blocage de la boucle d'événements et intégré la détection de glisser, tout ce que vous avez à faire est:

setDragDetect(false); 

Dans mon code, je fais cela avant de fixer le DragSource. Comme vous l'avez déjà souligné, cela vous laissera le problème que vous ne pouvez plus lancer un glisser. Mais j'ai trouvé une solution pour cela aussi. Heureusement, la génération d'événement de traînée est Java pur et non spécifique à la plate-forme dans SWT (seule la détection de traînée est). Vous pouvez donc générer votre propre événement DragDetect à un moment où cela vous convient. J'ai attaché un MouseMoveListener à mon Canvas, et il stocke la dernière position de la souris, la distance de traînée accumulée et si oui ou non il a déjà généré un événement DragDetect (entre autres choses utiles). Ceci est la mouseMove() mise en œuvre:

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

Et qui remplacera le intégré, le blocage de la détection de traînée pour vous.

Questions connexes