2010-10-11 65 views
17

我有一個鼠標偵聽器。它有一些代碼來響應mouseUp和mouseDown事件。這工作正常。MouseDown事件直到MouseUp時纔會傳遞,當拖動源存在時

但是,只要我添加一個DragSource,我的mouseDown事件就不再傳遞 - 直到我釋放鼠標按鈕!

這很容易重現 - 下面是一個簡單的程序,它包含一個只有鼠標監聽器和一個拖拽監聽器的純shell。當我運行這個程序(在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(); 
     } 
    } 
+0

我試過了,你的代碼在Windows上工作。可能是操作系統特定的錯誤 – nanda 2010-10-13 11:49:44

+0

我剛剛在Ubuntu 10.04上試過這個,它有點作品。按下鼠標左鍵不會發生任何事件。移動鼠標,同時獲得'mouseDown'和'dragStart' * *。當你放開時,你會得到'mouseUp'事件。如果你仍然按住鼠標左鍵,你會在1-2秒的延遲後得到一個'mouseDown'。無論哪種方式,在放開鼠標之前總會看到'mouseDown'。必須是Mac上的SWT問題? – richq 2010-10-17 14:18:36

+0

謝謝你們 - 聽起來這絕對是平臺特定的。但是,它聽起來像在Linux上也很糟糕 - 在傳遞mouseDown之前的一秒延遲將不起作用,因爲我試圖將某些處理從mouseUp切換到mouseDown的原因是爲了使UI更加響應.. – 2010-10-18 16:20:44

回答

8

要回答你關於爲什麼發生這種情況的具體問題 - 可可我們並不認爲一拖已經開始,直到鼠標移動幾個像素。這樣可以確保如果您對咔嗒聲不敏感,則可以防止「意外」拖動。在Linux和Win32上,窗口工具包可以執行拖動檢測。如果您只按住按鈕,則檢測超時,鼠標放下。在Cocoa上,我們沒有時間,這就是爲什麼在檢測到拖拽或鼠標移動之前沒有任何事情發生。

這是很多細節,但結論是行爲不一致,我們應該始終能夠立即傳遞鼠標,而不必等待拖動檢測完成。

我沒有看到解決方法,因爲這是在控件看到事件之前發生的。

請參閱this bug其中有win32,gtk和cocoa SWT補丁。

+0

哇!在一兩天內,所有平臺都可以獲得補丁!令人印象深刻!謝謝! – 2010-10-22 15:01:02

+0

顯然拖延期間的延遲MouseDown是故意的。對於那些在家中玩耍的人,請觀看上述Eclipse錯誤以瞭解更多細節。 – 2010-10-22 17:08:37

1

我遇到了同樣的問題,並找到了解決方案。一旦將DragSource附加到自定義小部件,事件循環將被阻止在該小部件的鼠標下方,並且會吃掉鼠標移動事件來檢測拖動。 (我只研究了SWT的GTK代碼以找到它,所以它可能在其他平臺上有點不同,但我的解決方案適用於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; 
} 

而且這將替代內置的阻塞阻力檢測。

相關問題