2010-10-11 16 views
17

Ho un listener di mouse. Ha del codice per rispondere agli eventi mouseUp e mouseDown. Funziona correttamenteGli eventi MouseDown non vengono recapitati fino a MouseUp quando è presente una sorgente di trascinamento

Tuttavia, non appena aggiungo un DragSource, il mio evento mouseDown non viene più recapitato - finché non rilascio il pulsante del mouse!

Questo è banale da riprodurre: di seguito è un semplice programma che contiene una semplice shell con un semplice listener di mouse e un ascoltatore di trascinamento. Quando eseguo questo (su un Mac), e premo e tengo premuto il pulsante del mouse, non succede nulla, ma non appena rilascio il pulsante del mouse, vedo immediatamente sia gli eventi del mouse che quelli del mouse consegnati. Se commento la sorgente di trascinamento, gli eventi del mouse vengono consegnati nel modo in cui dovrebbero essere.

Ho cercato per gli altri con problemi simili, e la più vicina che ho trovato per una spiegazione è questa:

https://bugs.eclipse.org/bugs/show_bug.cgi?id=26605#c16 "Se si collega la resistenza di rilevare, il sistema operativo ha bisogno di mangiare gli eventi del mouse fino a quando non determina che hai trascinato o meno. "

Tuttavia, non capisco perché sia ​​vero - perché il sistema operativo deve mangiare gli eventi del mouse per determinare se ho una resistenza o no? Il trascinamento non si avvia finché non si verifica un evento del mouse -move- con il pulsante premuto.

Ancora più importante: qualcuno può suggerire una soluzione alternativa? (Ho provato ad aggiungere e rimuovere dinamicamente la mia sorgente di trascinamento quando si preme il mouse, ma non riesco a ottenere il trascinamento di & rilascio perché non ha mai visto la pressione iniziale del tasto - e non riesco a trovare un modo per avviare a livello di programmazione . trascinare)

Ecco il programma di esempio:

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

Ho provato e il tuo codice funziona su Windows. Potrebbe essere un bug specifico del sistema operativo – nanda

+0

Ho appena provato questo su Ubuntu 10.04 e funziona in modo ordinario. Premi il tasto sinistro del mouse per ottenere nessun evento. Muovi il mouse e ottieni 'mouseDown' e' dragStart' * allo stesso tempo *. Quando si rilascia, si ottiene l'evento 'mouseUp'. Se si tiene il mouse fermo e si preme il pulsante sinistro, si ottiene un 'mouseDown' dopo un ritardo di 1-2 secondi evidente. Ad ogni modo, 'mouseDown' è sempre visto prima di lasciar andare il mouse. Deve essere un problema SWT su Mac? – richq

+0

Grazie a entrambi - sembra che questo sia decisamente specifico per la piattaforma. Tuttavia, sembra che sia anche piuttosto rotto su Linux - un secondo di ritardo prima del rilascio di mouseDown non funzionerà dato che il motivo per cui sto provando a passare un po 'di gestione da mouseUp a mouseDown è rendere l'interfaccia utente più reattiva. –

risposta

8

per rispondere alla tua domanda specifica sul perché questo accade - il cacao non consideriamo un trascinamento di aver iniziato fino a quando il mouse si è spostato un paio di pixel . Questo assicura contro la trascinamento "accidentale" se sei sciatto con i clic. Su Linux e Win32 il toolkit della finestra può eseguire il rilevamento del trascinamento. Se si tiene premuto il pulsante, il rilevamento scade e viene rilasciato il mouse. Su Cocoa non abbiamo timeout, motivo per cui non succede nulla finché non viene rilevato il trascinamento o si verifica un mouse.

Questo è un sacco di dettagli, ma la conclusione è che il comportamento non è coerente, e dovremmo sempre essere in grado di consegnare il mouse verso il basso subito, senza attendere la rilevazione trascinare per completare.

Non vedo una soluzione, poiché ciò sta accadendo prima che Control veda l'evento.

Vedere this bug che contiene le patch per win32, gtk e cacao SWT.

+0

Wow! Una patch, per tutte le piattaforme non meno, in un giorno o due! Impressionante! Grazie! –

+0

Apparentemente il MouseDown ritardato durante una resistenza è intenzionale. Per coloro che giocano a casa, si prega di guardare il bug di Eclipse sopra menzionato per maggiori dettagli. –

1

Avevo affrontato lo stesso problema e trovato una soluzione. Una volta collegato un DragSource al tuo widget personalizzato, il ciclo degli eventi verrà bloccato nel gancio del mouse di quel widget e manterrà gli eventi di spostamento del mouse per rilevare un trascinamento. (Ho solo esaminato il codice GTK di SWT per scoprirlo, quindi potrebbe funzionare in modo un po 'diverso su altre piattaforme, ma la mia soluzione funziona su GTK, Win32 e Cocoa.) Nella mia situazione, non ero così tanto interessato a rilevare l'evento mouse down proprio nel momento in cui è successo, ma ero interessato a ridurre significativamente il ritardo di rilevamento del trascinamento, dal momento che l'intero scopo della mia implementazione Canvas era che l'utente trascinasse cose.Per disattivare il blocco ciclo di eventi e built-in di rilevamento trascinare, tutto ciò che dovete fare è:

setDragDetect(false); 

Nel mio codice, sto facendo questo prima di collegare il DragSource. Come hai già sottolineato, questo ti lascerà il problema che non puoi più iniziare un trascinamento. Ma ho trovato una soluzione anche per questo. Fortunatamente, la generazione di eventi di trascinamento è pura Java e non specifica per piattaforma in SWT (solo il rilevamento del trascinamento è). Quindi puoi solo generare il tuo evento DragDetect in un momento in cui è conveniente per te. Ho collegato MouseMoveListener al Canvas e memorizza l'ultima posizione del mouse, la distanza di trascinamento accumulata e se ha già generato o meno un evento DragDetect (tra le altre cose utili). Questa è l'implementazione 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; 
} 

E che andrà a sostituire il, il blocco di rilevamento trascinare built-in per voi.