OSDN Git Service

GLE2: proper feedback on invalid drop targets.
authorRaphael Moll <ralf@android.com>
Sat, 11 Sep 2010 07:47:44 +0000 (00:47 -0700)
committerRaphael Moll <ralf@android.com>
Sat, 11 Sep 2010 07:47:44 +0000 (00:47 -0700)
Change-Id: I412a64fc261314d681009da87ed20fba278afa33

eclipse/plugins/com.android.ide.eclipse.adt/gscripts/android.widget.RelativeLayout.groovy
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/DropFeedback.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasDropListener.java

index 6d97103..60b5998 100755 (executable)
@@ -221,6 +221,8 @@ public class AndroidWidgetRelativeLayoutRule extends BaseLayout {
             feedback.requestPaint = true;
         }
 
+        feedback.invalidTarget = (currZone == null);
+
         return feedback;
     }
 
index 82a07a9..af26ebf 100755 (executable)
@@ -19,26 +19,53 @@ package com.android.ide.eclipse.adt.editors.layout.gscripts;
 import groovy.lang.Closure;
 
 /**
- * Returned by onDropEnter/Move and passed to over onDropXyz methods.
+ * Structure returned by onDropEnter/Move and passed to over onDropXyz methods.
  */
 public class DropFeedback {
     /**
      * User data that the rule can use in any way it wants to carry state from one
      * operation to another.
+     * <p/>
+     * Filled and owned by the groovy view rule.
      */
     public Object userData;
 
     /**
      * If true the next screen update will invoke the paint closure.
+     * <p/>
+     * Filled by the groovy view rule to request a paint, and reset by the canvas
+     * after the paint occurred.
      */
     public boolean requestPaint;
 
     /**
+     * Set to false by the engine when entering a new view target.
+     * The groovy view rule should set this to true if the current view target is not
+     * a valid drop zone.
+     * <p/>
+     * When set to true, the onDropped() method will not be called if the user releases
+     * the mouse button. Depending on the platform or implementation, the mouse cursor
+     * <em>may</em> reflect that the drop operation is invalid.
+     * <p/>
+     * Rationale: an operation like onDropEnter() is called each time the mouse enters
+     * a new view target and is supposed to return null when the drop cannot happen
+     * <em>at all</em> in that target. However a layout like RelativeLayout decorates
+     * potential targets with "hot spots" that are suitable drop zones, whereas some
+     * other parts of the view are not suitable drop zones. In this case the onDropEnter()
+     * or onDropMove() operation would return a {@link DropFeedback} with
+     * <code>invalidTarget=true</code>.
+     */
+    public boolean invalidTarget;
+
+    /**
      * Closure invoked by the canvas to paint the feedback.
+     * <p/>
      * The closure will receive 3 arguments: <br/>
      * - The {@link IGraphics} context to use for painting. Must not be cached. <br/>
      * - The {@link INode} target node last used in a onDropEnter or onDropMove call. <br/>
      * - The {@link DropFeedback} returned by the last onDropEnter or onDropMove call. <br/>
+     * <p/>
+     * Filled by the groovy view rule, called by the engine.
      */
     public Closure paintClosure;
 
@@ -48,6 +75,8 @@ public class DropFeedback {
      * <p/>
      * When the mouse is captured, drop events will keep going to the rule that started the
      * capture and the current INode proxy will not change.
+     * <p/>
+     * Filled by the groovy view rule, read by the engine.
      */
     public Rect captureArea;
 
@@ -55,12 +84,16 @@ public class DropFeedback {
      * Set to true by the drag'n'drop engine when the current drag operation is a copy.
      * When false the operation is a move and <em>after</em> a successful drop the source
      * elements will be deleted.
+     * <p/>
+     * Filled by the engine, read by groovy view rule.
      */
     public boolean isCopy;
 
     /**
      * Set to true when the drag'n'drop starts and ends in the same canvas of the
      * same Eclipse instance.
+     * <p/>
+     * Filled by the engine, read by groovy view rule.
      */
     public boolean sameCanvas;
 
index 8c4e46f..fc06381 100755 (executable)
@@ -221,6 +221,11 @@ import java.util.Arrays;
             mCurrentView = mLeaveView;
         }
 
+        if (mFeedback != null && mFeedback.invalidTarget) {
+            // The script said we can't drop here.
+            event.detail = DND.DROP_NONE;
+        }
+
         if (mLeaveTargetNode == null || event.detail == DND.DROP_NONE) {
             clearDropInfo();
         }
@@ -292,6 +297,7 @@ import java.util.Arrays;
             df.isCopy = event.detail == DND.DROP_COPY;
         }
         df.sameCanvas = mCanvas == mGlobalDragInfo.getSourceCanvas();
+        df.invalidTarget = false;
     }
 
     /**
@@ -300,8 +306,8 @@ import java.util.Arrays;
      */
     public void paintFeedback(GCWrapper gCWrapper) {
         if (mTargetNode != null && mFeedback != null && mFeedback.requestPaint) {
-            mFeedback.requestPaint = false;
             mCanvas.getRulesEngine().callDropFeedbackPaint(gCWrapper, mTargetNode, mFeedback);
+            mFeedback.requestPaint = false;
         }
     }
 
@@ -428,29 +434,22 @@ import java.util.Arrays;
                     }
                 }
 
-                if (df != null && targetNode != mTargetNode) {
+                if (df == null) {
+                    // Provide visual feedback that we are refusing the drop
+                    event.detail = DND.DROP_NONE;
+                    clearDropInfo();
+
+                } else if (targetNode != mTargetNode) {
                     // We found a new target node for the drag'n'drop.
                     // Release the previous one, if any.
                     callDropLeave();
 
-                    // If we previously provided visual feedback that we were refusing
-                    // the drop, we now need to change it to mean we're accepting it.
-                    if (event.detail == DND.DROP_NONE) {
-                        event.detail = DND.DROP_DEFAULT;
-                        recomputeDragType(event);
-                    }
-
                     // And assign the new one
                     mTargetNode = targetNode;
                     mFeedback = df;
 
                     // We don't need onDropMove in this case
                     isMove = false;
-
-                } else if (df == null) {
-                    // Provide visual feedback that we are refusing the drop
-                    event.detail = DND.DROP_NONE;
-                    clearDropInfo();
                 }
             }
 
@@ -464,14 +463,30 @@ import java.util.Arrays;
             updateDropFeedback(mFeedback, event);
             DropFeedback df = mCanvas.getRulesEngine().callOnDropMove(
                     mTargetNode, mCurrentDragElements, mFeedback, p2);
+
             if (df == null) {
                 // The target is no longer interested in the drop move.
+                event.detail = DND.DROP_NONE;
                 callDropLeave();
+
             } else if (df != mFeedback) {
                 mFeedback = df;
             }
         }
 
+        if (mFeedback != null) {
+            if (event.detail == DND.DROP_NONE && !mFeedback.invalidTarget) {
+                // If we previously provided visual feedback that we were refusing
+                // the drop, we now need to change it to mean we're accepting it.
+                event.detail = DND.DROP_DEFAULT;
+                recomputeDragType(event);
+
+            } else if (mFeedback.invalidTarget) {
+                // Provide visual feedback that we are refusing the drop
+                event.detail = DND.DROP_NONE;
+            }
+        }
+
         if (needRedraw || (mFeedback != null && mFeedback.requestPaint)) {
             mCanvas.redraw();
         }