OSDN Git Service

Visual adjustments to the layout feedback
authorTor Norbye <tnorbye@google.com>
Wed, 6 Oct 2010 22:30:37 +0000 (15:30 -0700)
committerTor Norbye <tnorbye@google.com>
Thu, 7 Oct 2010 00:31:32 +0000 (17:31 -0700)
This changeset makes a number of adjustments to the visual feedback
shown during layout dragging & dropping, selection, etc.

These are:

1. Render text on top of a translucent mask (white on a transparent
gray rectangle). This ensures that the text is readable regardless of
what content is under the text. We can't just use the background color
for the normal SWT drawString for two reasons: First, we need to
control the alpha of the background only (since we want the text to be
opaque and the background to be translucent), and second, we often
want to draw multiple lines of text, so we need to manually draw a
background rectangle which accommodates the maximum width of ALL the
lines such taht we don't have a ragged background.

2. Paint the outline mode lines using solid, translucent lines. And
adjust the bounding box computation in this case by 1 pixel such that
when you have adjacent boxes (such as in LinearLayout), you don't end
up with two thin lines next to each other creating a thick line.

3. Change the DrawingStyle internal API from "foreground" and
"background" colors to "stroke" (border) and "fill" (interior), with
individual alphas. This makes the color definitions simpler (for
example we don't need two separate styles, one for the selection
border and one for the selection interior).

4. Make the hover more subtle, using no border and just a light
translucent gray rectangle to only slightly draw attention to the view
under the mouse.

5. Change the appearance of selection to light blue and use a long
dashed border around it. The anchor lines are more faint and more
translucent to make them stand out less.

6. Change the appearance of the drop-preview to use a dash patterned
border identical to the selection border, but colored orange instead.
The matching border pattern will hopefully reinforce that the
drop-preview line is where the selection (also dashed) will go.

7. Use green to show available drop zones (the grid surrounding a
target in RelativeLayout, and the positions between elements in
LinearLayout).

8. For invalid drops, use a white X on a red background as the overlay
fill.

9. I replaced the oval+X pattern from the LayoutHelpers and replaced
them with the orange dashed insert-position lines. I also adjusted the
LinearLayout to draw the bounding boxes such that the middle of the
bounding box, rather than the top left edge, are aligned with the
insert position.

Change-Id: I85c77b9fa84b732a78aac635442f96e7ccfc3983

eclipse/plugins/com.android.ide.eclipse.adt/gscripts/BaseView.groovy
eclipse/plugins/com.android.ide.eclipse.adt/gscripts/android.widget.AbsoluteLayout.groovy
eclipse/plugins/com.android.ide.eclipse.adt/gscripts/android.widget.FrameLayout.groovy
eclipse/plugins/com.android.ide.eclipse.adt/gscripts/android.widget.LinearLayout.groovy
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/DrawingStyle.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/editors/layout/gscripts/IGraphics.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditor.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GCWrapper.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtDrawingStyle.java

index 9c5b52f..7241a6b 100755 (executable)
@@ -313,9 +313,8 @@ public class BaseView implements IViewRule {
             return;
         }
 
-        gc.useStyle(DrawingStyle.SELECTION_FILL);
+        gc.useStyle(DrawingStyle.SELECTION);
         gc.fillRect(r);
-        gc.useStyle(DrawingStyle.SELECTION_BORDER);
         gc.drawRect(r);
 
         if (displayName == null || isMultipleSelection) {
@@ -327,7 +326,9 @@ public class BaseView implements IViewRule {
         if (ys < 0) {
             ys = r.y + r.h;
         }
-        gc.drawString(displayName, xs, ys);
+        gc.useStyle(DrawingStyle.HELP);
+        gc.drawBoxedStrings(xs, ys, [displayName]);
+
     }
 
     public void onChildSelected(IGraphics gc, INode parentNode, INode childNode) {
index 4e8daad..e2a58f0 100755 (executable)
@@ -51,9 +51,7 @@ public class AndroidWidgetAbsoluteLayoutRule extends BaseLayout {
         }
 
         // Highlight the receiver
-        gc.setForeground(gc.registerColor(0x00FFFF00));
-        gc.setLineStyle(IGraphics.LineStyle.LINE_SOLID);
-        gc.setLineWidth(2);
+        gc.useStyle(DrawingStyle.DROP_RECIPIENT);
         gc.drawRect(b);
 
         // Get the drop point
@@ -74,15 +72,23 @@ public class AndroidWidgetAbsoluteLayoutRule extends BaseLayout {
             // the drop point.
             int offsetX = x - be.x;
             int offsetY = y - be.y;
+            gc.useStyle(DrawingStyle.DROP_PREVIEW);
             elements.each {
                 drawElement(gc, it, offsetX, offsetY);
             }
         } else {
             // We don't have bounds for new elements. In this case
-            // just draw a mark at the drop point.
-            gc.drawLine(x - 10, y - 10, x + 10, y + 10);
-            gc.drawLine(x + 10, y - 10, x - 10, y + 10);
-            gc.drawOval(x - 10, y - 10, x + 10, y + 10);
+            // just draw cross hairs to the drop point.
+            gc.useStyle(DrawingStyle.GUIDELINE);
+            gc.drawLine(x, b.y, x, b.y + b.h);
+            gc.drawLine(b.x, y, b.x + b.w, y);
+
+            // Use preview lines to indicate the bottom quadrant as well (to indicate
+            // that you are looking at the top left position of the drop, not the center
+            // for example)
+            gc.useStyle(DrawingStyle.DROP_PREVIEW);
+            gc.drawLine(x, y, b.x + b.w, y);
+            gc.drawLine(x, y, x, b.y + b.h);
         }
     }
 
index 97d67ba..5a30a9f 100755 (executable)
@@ -48,9 +48,7 @@ public class AndroidWidgetFrameLayoutRule extends BaseLayout {
             return;
         }
 
-        gc.setForeground(gc.registerColor(0x00FFFF00));
-        gc.setLineStyle(IGraphics.LineStyle.LINE_SOLID);
-        gc.setLineWidth(2);
+        gc.useStyle(DrawingStyle.DROP_RECIPIENT);
         gc.drawRect(b);
 
         // Get the drop point
@@ -71,6 +69,7 @@ public class AndroidWidgetFrameLayoutRule extends BaseLayout {
             // the drop point.
             int offsetX = x - be.x;
             int offsetY = y - be.y;
+            gc.useStyle(DrawingStyle.DROP_PREVIEW);
             elements.each {
                 drawElement(gc, it, offsetX, offsetY);
             }
index 1865044..ac6543b 100755 (executable)
@@ -80,12 +80,24 @@ public class AndroidWidgetLinearLayoutRule extends BaseLayout {
         targetNode.getChildren().each {
             def bc = it.getBounds();
             if (bc.isValid()) {
-                // add an insertion point between the last point and the start of this child
-                int v = isVertical ? bc.y : bc.x;
-                v = (last + v) / 2;
-                indexes.add( [v, pos++] );
+                // First see if this node looks like it's the same as one of the *dragged* bounds
+                boolean isDragged = false;
+                for (element in elements) {
+                    // This tries to determine if an INode corresponds to an IDragElement, by
+                    // comparing their bounds.
+                    if (bc == element.getBounds()) {
+                        isDragged = true;
+                    }
+                }
 
-                last = isVertical ? (bc.y + bc.h) : (bc.x + bc.w);
+                if (!isDragged) {
+                    // add an insertion point between the last point and the start of this child
+                    int v = isVertical ? bc.y : bc.x;
+                    v = (last + v) / 2;
+                    indexes.add( [v, pos++] );
+
+                    last = isVertical ? (bc.y + bc.h) : (bc.x + bc.w);
+                }
             }
         }
 
@@ -123,8 +135,6 @@ public class AndroidWidgetLinearLayoutRule extends BaseLayout {
         gc.drawRect(b);
 
         gc.useStyle(DrawingStyle.DROP_ZONE);
-        gc.setLineStyle(IGraphics.LineStyle.LINE_DOT);
-        gc.setLineWidth(1);
 
         def indexes = feedback.userData.indexes;
         boolean isVertical = feedback.userData.isVertical;
@@ -149,17 +159,26 @@ public class AndroidWidgetLinearLayoutRule extends BaseLayout {
             int x = currX;
             int y = currY;
 
-            // Draw a mark at the drop point.
-            gc.setLineStyle(IGraphics.LineStyle.LINE_SOLID);
-            gc.setLineWidth(2);
-
-            gc.drawLine(x - 10, y - 10, x + 10, y + 10);
-            gc.drawLine(x + 10, y - 10, x - 10, y + 10);
-            gc.drawOval(x - 10, y - 10, x + 10, y + 10);
-
             Rect be = elements[0].getBounds();
 
-            if (be.isValid()) {
+            // Draw a mark at the drop point.
+            if (!be.isValid()) {
+                // We don't have valid bounds; this typically means we are dragging a new
+                // View from the palette whose bounds are unknown, so we simply show a single
+                // dividing line in the center of the position between the children
+                gc.useStyle(DrawingStyle.DROP_PREVIEW);
+                if (feedback.userData.width != null) {
+                    int width = feedback.userData.width;
+                    int fromX = x - width / 2;
+                    int toX = x + width / 2;
+                    gc.drawLine(fromX, y, toX, y);
+                } else if (feedback.userData.height != null) {
+                    int height = (int)feedback.userData.height;
+                    int fromY = y - height / 2;
+                    int toY = y + height / 2;
+                    gc.drawLine(x, fromY, x, toY);
+                }
+            } else {
                 // At least the first element has a bound. Draw rectangles
                 // for all dropped elements with valid bounds, offset at
                 // the drop point.
@@ -172,11 +191,16 @@ public class AndroidWidgetLinearLayoutRule extends BaseLayout {
                 if (pb.isValid()) {
                     if (isVertical) {
                         offsetX = b.x - pb.x;
+                        // Place the -center- of the bounds at child boundary!
+                        offsetY -= be.h / 2;
                     } else {
                         offsetY = b.y - pb.y;
+                        // Place the -center- of the bounds at child boundary!
+                        offsetX -= be.w / 2;
                     }
                 }
 
+                gc.useStyle(DrawingStyle.DROP_PREVIEW);
                 for (element in elements) {
                     drawElement(gc, element, offsetX, offsetY);
                 }
@@ -221,9 +245,11 @@ public class AndroidWidgetLinearLayoutRule extends BaseLayout {
             if (isVertical) {
                 data.currX = b.x + b.w / 2;
                 data.currY = bestIndex;
+                data.width = b.w;
             } else {
                 data.currX = bestIndex;
                 data.currY = b.y + b.h / 2;
+                data.height = b.h;
             }
 
             data.insertPos = bestPos;
index 2cd34aa..983f45a 100755 (executable)
@@ -74,14 +74,11 @@ public class AndroidWidgetRelativeLayoutRule extends BaseLayout {
         addAttr("centerVertical");
 
         if (infos) {
-            gc.setForeground(gc.registerColor(0x00222222));
+            gc.useStyle(DrawingStyle.HELP);
             int x = b.x + 10;
             int y = b.y + b.h + 10;
             int h = gc.getFontHeight();
-            infos.each {
-                y += h;
-                gc.drawString(it, x, y);
-            }
+            gc.drawBoxedStrings(x, y, infos);
         }
     }
 
@@ -452,9 +449,7 @@ public class AndroidWidgetRelativeLayoutRule extends BaseLayout {
 
         if (data.curr) {
             gc.useStyle(DrawingStyle.DROP_ZONE_ACTIVE);
-            gc.setAlpha(200);
             gc.fillRect(data.curr.rect);
-            gc.setAlpha(255);
 
             def r = feedback.captureArea;
             int x = r.x + 5;
@@ -466,11 +461,13 @@ public class AndroidWidgetRelativeLayoutRule extends BaseLayout {
                 id = data.child.getStringAttr(ANDROID_URI, ATTR_ID);
             }
 
-            for (s in data.curr.attr) {
-                if (id) s = "$s=$id";
-                gc.drawString(s, x, y);
-                y += h;
+            // Print constraints (with id appended if applicable)
+            gc.useStyle(DrawingStyle.HELP);
+            def strings = []
+            data.curr.attr.each {
+               strings << id ? it + "=" + id : it;
             }
+            gc.drawBoxedStrings(x, y, strings);
 
             def mark = data.curr.get("mark");
             if (mark) {
@@ -511,7 +508,7 @@ public class AndroidWidgetRelativeLayoutRule extends BaseLayout {
                     offsetX -= be.w;
                 }
 
-                gc.setForeground(gc.registerColor(0x00FFFF00));
+                gc.useStyle(DrawingStyle.DROP_PREVIEW);
 
                 for (element in elements) {
                     drawElement(gc, element, offsetX, offsetY);
@@ -522,6 +519,7 @@ public class AndroidWidgetRelativeLayoutRule extends BaseLayout {
         if (data.rejected) {
             def br = data.rejected;
             gc.useStyle(DrawingStyle.INVALID);
+            gc.fillRect(br);
             gc.drawLine(br.x, br.y       , br.x + br.w, br.y + br.h);
             gc.drawLine(br.x, br.y + br.h, br.x + br.w, br.y       );
         }
index ea6c5e8..712e95c 100644 (file)
@@ -23,14 +23,15 @@ package com.android.ide.eclipse.adt.editors.layout.gscripts;
  */
 public enum DrawingStyle {
     /**
-     * The style used for the border of a selected view
+     * The style used to draw the selected views
      */
-    SELECTION_BORDER,
+    SELECTION,
 
     /**
-     * The style used for the interior of a selected view
+     * The style used to draw guidelines - overlay lines which indicate
+     * significant geometric positions.
      */
-    SELECTION_FILL,
+    GUIDELINE,
 
     /**
      * The style used for hovered views (e.g. when the mouse is directly on top
@@ -75,6 +76,17 @@ public enum DrawingStyle {
     DROP_ZONE_ACTIVE,
 
     /**
+     * The style used to draw a preview of where a dropped view would appear if
+     * it were to be dropped at a given location.
+     */
+    DROP_PREVIEW,
+
+    /**
+     * The style used to draw help/hint text.
+     */
+    HELP,
+
+    /**
      * The style used to raw illegal/error/invalid markers
      */
     INVALID,
index 7c1dc15..ef10e73 100755 (executable)
@@ -16,6 +16,8 @@
 
 package com.android.ide.eclipse.adt.editors.layout.gscripts;
 
+import java.util.List;
+
 /**
  * Represents a graphical context that rules can use to draw on the canvas.
  * <p/>
@@ -94,6 +96,21 @@ public interface IGraphics {
     void drawString(String string, Point topLeft);
 
     /**
+     * Draw the given strings, using the current stroke color and alpha for the
+     * text, and the current fill color and alpha for a rectangle behind the
+     * bounding box fitting all the lines of text. Each subsequent string is
+     * drawn on consecutive lines below the previous string.
+     *
+     * @param x The left edge to start each string at
+     * @param y The top position of the first string; subsequent strings are
+     *            painted on lines below
+     * @param strings An array of labels to be displayed (should not be null).
+     *            The actual String used is the {@link Object#toString()} value
+     *            of each list item.
+     */
+    void drawBoxedStrings(int x, int y, List<?> strings);
+
+    /**
      * Set up the graphics context to use the given style for subsequent drawing
      * operations.
      *
@@ -158,10 +175,8 @@ public interface IGraphics {
      * This operation requires the operating system's advanced
      * graphics subsystem which may not be available on some
      * platforms.
-     *
-     * @return False if the GC doesn't support alpha.
      */
-    boolean setAlpha(int alpha);
+    void setAlpha(int alpha);
 
     /**
      * A line style for {@link IGraphics#setLineStyle(LineStyle)}.
index d6e8f0c..ff176de 100644 (file)
@@ -278,7 +278,7 @@ public class LayoutEditor extends AndroidXmlEditor implements IShowEditorInput,
     /**
      * Returns the custom IContentOutlinePage or IPropertySheetPage when asked for it.
      */
-    @SuppressWarnings({"rawtypes", "unchecked"})
+    @SuppressWarnings("unchecked")
     @Override
     public Object getAdapter(Class adapter) {
         // for the outline, force it to come from the Graphical Editor.
index 2927c0e..676606a 100755 (executable)
@@ -32,6 +32,7 @@ import org.eclipse.swt.graphics.RGB;
 
 import java.util.EnumMap;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -50,6 +51,11 @@ public class GCWrapper implements IGraphics {
     private GC mGc;
 
     /**
+     * Current style being used for drawing.
+     */
+    private SwtDrawingStyle mCurrentStyle = SwtDrawingStyle.INVALID;
+
+    /**
      * Implementation of IColor wrapping an SWT color.
      */
     private class ColorWrapper implements IColor {
@@ -68,10 +74,17 @@ public class GCWrapper implements IGraphics {
     private final HashMap<Integer, ColorWrapper> mColorMap = new HashMap<Integer, ColorWrapper>();
 
     /**
-     * A map of the {@link SwtDrawingStyle} colors that we have actually used
-     * (to be disposed)
+     * A map of the {@link SwtDrawingStyle} stroke colors that we have actually
+     * used (to be disposed)
      */
-    private final Map<DrawingStyle, Color> mStyleColorMap = new EnumMap<DrawingStyle, Color>(
+    private final Map<DrawingStyle, Color> mStyleStrokeMap = new EnumMap<DrawingStyle, Color>(
+            DrawingStyle.class);
+
+    /**
+     * A map of the {@link SwtDrawingStyle} fill colors that we have actually
+     * used (to be disposed)
+     */
+    private final Map<DrawingStyle, Color> mStyleFillMap = new EnumMap<DrawingStyle, Color>(
             DrawingStyle.class);
 
     /** The cached pixel height of the default current font. */
@@ -108,10 +121,15 @@ public class GCWrapper implements IGraphics {
         }
         mColorMap.clear();
 
-        for (Color c : mStyleColorMap.values()) {
+        for (Color c : mStyleStrokeMap.values()) {
             c.dispose();
         }
-        mStyleColorMap.clear();
+        mStyleStrokeMap.clear();
+
+        for (Color c : mStyleFillMap.values()) {
+            c.dispose();
+        }
+        mStyleFillMap.clear();
     }
 
     //-------------
@@ -166,13 +184,13 @@ public class GCWrapper implements IGraphics {
         getGc().setBackground(((ColorWrapper) color).getColor());
     }
 
-    public boolean setAlpha(int alpha) {
+    public void setAlpha(int alpha) {
         checkGC();
         try {
             getGc().setAlpha(alpha);
-            return true;
         } catch (SWTException e) {
-            return false;
+            // This means that we cannot set the alpha on this platform; this is
+            // an acceptable no-op.
         }
     }
 
@@ -213,6 +231,7 @@ public class GCWrapper implements IGraphics {
 
     public void drawLine(int x1, int y1, int x2, int y2) {
         checkGC();
+        useStrokeAlpha();
         x1 = mHScale.translate(x1);
         y1 = mVScale.translate(y1);
         x2 = mHScale.translate(x2);
@@ -228,6 +247,7 @@ public class GCWrapper implements IGraphics {
 
     public void drawRect(int x1, int y1, int x2, int y2) {
         checkGC();
+        useStrokeAlpha();
         int x = mHScale.translate(x1);
         int y = mVScale.translate(y1);
         int w = mHScale.scale(x2 - x1);
@@ -241,6 +261,7 @@ public class GCWrapper implements IGraphics {
 
     public void drawRect(Rect r) {
         checkGC();
+        useStrokeAlpha();
         int x = mHScale.translate(r.x);
         int y = mVScale.translate(r.y);
         int w = mHScale.scale(r.w);
@@ -250,6 +271,7 @@ public class GCWrapper implements IGraphics {
 
     public void fillRect(int x1, int y1, int x2, int y2) {
         checkGC();
+        useFillAlpha();
         int x = mHScale.translate(x1);
         int y = mVScale.translate(y1);
         int w = mHScale.scale(x2 - x1);
@@ -263,6 +285,7 @@ public class GCWrapper implements IGraphics {
 
     public void fillRect(Rect r) {
         checkGC();
+        useFillAlpha();
         int x = mHScale.translate(r.x);
         int y = mVScale.translate(r.y);
         int w = mHScale.scale(r.w);
@@ -274,6 +297,7 @@ public class GCWrapper implements IGraphics {
 
     public void drawOval(int x1, int y1, int x2, int y2) {
         checkGC();
+        useStrokeAlpha();
         int x = mHScale.translate(x1);
         int y = mVScale.translate(y1);
         int w = mHScale.scale(x2 - x1);
@@ -287,6 +311,7 @@ public class GCWrapper implements IGraphics {
 
     public void drawOval(Rect r) {
         checkGC();
+        useStrokeAlpha();
         int x = mHScale.translate(r.x);
         int y = mVScale.translate(r.y);
         int w = mHScale.scale(r.w);
@@ -296,6 +321,7 @@ public class GCWrapper implements IGraphics {
 
     public void fillOval(int x1, int y1, int x2, int y2) {
         checkGC();
+        useFillAlpha();
         int x = mHScale.translate(x1);
         int y = mVScale.translate(y1);
         int w = mHScale.scale(x2 - x1);
@@ -309,6 +335,7 @@ public class GCWrapper implements IGraphics {
 
     public void fillOval(Rect r) {
         checkGC();
+        useFillAlpha();
         int x = mHScale.translate(r.x);
         int y = mVScale.translate(r.y);
         int w = mHScale.scale(r.w);
@@ -321,11 +348,48 @@ public class GCWrapper implements IGraphics {
 
     public void drawString(String string, int x, int y) {
         checkGC();
+        useStrokeAlpha();
         x = mHScale.translate(x);
         y = mVScale.translate(y);
+        // Background fill of text is not useful because it does not
+        // use the alpha; we instead supply a separate method (drawBoxedStrings) which
+        // first paints a semi-transparent mask for the text to sit on
+        // top of (this ensures that the text is readable regardless of
+        // colors of the pixels below the text)
         getGc().drawString(string, x, y, true /*isTransparent*/);
     }
 
+    public void drawBoxedStrings(int x, int y, List<?> strings) {
+        checkGC();
+
+        x = mHScale.translate(x);
+        y = mVScale.translate(y);
+
+        // Compute bounds of the box by adding up the sum of the text heights
+        // and the max of the text widths
+        int width = 0;
+        int height = 0;
+        int lineHeight = getGc().getFontMetrics().getHeight();
+        for (Object s : strings) {
+            org.eclipse.swt.graphics.Point extent = getGc().stringExtent(s.toString());
+            height += extent.y;
+            width = Math.max(width, extent.x);
+        }
+
+        // Paint a box below the text
+        int padding = 2;
+        useFillAlpha();
+        getGc().fillRectangle(x - padding, y - padding, width + 2 * padding, height + 2 * padding);
+
+        // Finally draw strings on top
+        useStrokeAlpha();
+        int lineY = y;
+        for (Object s : strings) {
+            getGc().drawString(s.toString(), x, lineY, true /* isTransparent */);
+            lineY += lineHeight;
+        }
+    }
+
     public void drawString(String string, Point topLeft) {
         drawString(string, topLeft.x, topLeft.y);
     }
@@ -338,37 +402,84 @@ public class GCWrapper implements IGraphics {
         // Look up the specific SWT style which defines the actual
         // colors and attributes to be used for the logical drawing style.
         SwtDrawingStyle swtStyle = SwtDrawingStyle.of(style);
-        RGB fg = swtStyle.getForeground();
-        if (fg != null) {
-            Color color = getStyleColor(style, fg);
+        RGB stroke = swtStyle.getStrokeColor();
+        if (stroke != null) {
+            Color color = getStrokeColor(style, stroke);
             mGc.setForeground(color);
         }
-        RGB bg = swtStyle.getBackground();
-        if (bg != null) {
-            Color color = getStyleColor(style, bg);
+        RGB fill = swtStyle.getFillColor();
+        if (fill != null) {
+            Color color = getFillColor(style, fill);
             mGc.setBackground(color);
         }
         mGc.setLineWidth(swtStyle.getLineWidth());
         mGc.setLineStyle(swtStyle.getLineStyle());
-        mGc.setAlpha(swtStyle.getAlpha());
+        if (swtStyle.getLineStyle() == SWT.LINE_CUSTOM) {
+            mGc.setLineDash(new int[] {
+                    8, 4
+            });
+        }
+        mCurrentStyle = swtStyle;
+    }
+
+    /** Use the stroke alpha for subsequent drawing operations */
+    private void useStrokeAlpha() {
+        mGc.setAlpha(mCurrentStyle.getStrokeAlpha());
+    }
+
+    /** Use the fill alpha for subsequent drawing operations */
+    private void useFillAlpha() {
+        mGc.setAlpha(mCurrentStyle.getFillAlpha());
+    }
+
+    /**
+     * Get the SWT stroke color (foreground/border) to use for the given style,
+     * using the provided color description if we haven't seen this color yet.
+     * The color will also be placed in the {@link #mStyleStrokeMap} such that
+     * it can be disposed of at cleanup time.
+     *
+     * @param style The drawing style for which we want a color
+     * @param defaultColorDesc The RGB values to initialize the color to if we
+     *            haven't seen this color before
+     * @return The color object
+     */
+    private Color getStrokeColor(DrawingStyle style, RGB defaultColorDesc) {
+        return getStyleColor(style, defaultColorDesc, mStyleStrokeMap);
+    }
+
+    /**
+     * Get the SWT fill (background/interior) color to use for the given style,
+     * using the provided color description if we haven't seen this color yet.
+     * The color will also be placed in the {@link #mStyleStrokeMap} such that
+     * it can be disposed of at cleanup time.
+     *
+     * @param style The drawing style for which we want a color
+     * @param defaultColorDesc The RGB values to initialize the color to if we
+     *            haven't seen this color before
+     * @return The color object
+     */
+    private Color getFillColor(DrawingStyle style, RGB defaultColorDesc) {
+        return getStyleColor(style, defaultColorDesc, mStyleFillMap);
     }
 
     /**
      * Get the SWT color to use for the given style, using the provided color
      * description if we haven't seen this color yet. The color will also be
-     * placed in the {@link #mStyleColorMap} such that it can be disposed of at
-     * cleanup time.
+     * placed in the map referenced by the map parameter such that it can be
+     * disposed of at cleanup time.
      *
      * @param style The drawing style for which we want a color
      * @param defaultColorDesc The RGB values to initialize the color to if we
      *            haven't seen this color before
+     * @param map The color map to use
      * @return The color object
      */
-    private Color getStyleColor(DrawingStyle style, RGB defaultColorDesc) {
-        Color color = mStyleColorMap.get(style);
+    private Color getStyleColor(DrawingStyle style, RGB defaultColorDesc,
+            Map<DrawingStyle, Color> map) {
+        Color color = map.get(style);
         if (color == null) {
             color = new Color(getGc().getDevice(), defaultColorDesc);
-            mStyleColorMap.put(style, color);
+            map.put(style, color);
         }
 
         return color;
index 237deb8..705021e 100755 (executable)
@@ -208,9 +208,12 @@ class LayoutCanvas extends Canvas implements ISelectionProvider {
     private Rectangle mHoverRect;
 
     /** Hover border color. Must be disposed, it's NOT a system color. */
-    private Color mHoverFgColor;
+    private Color mHoverStrokeColor;
 
-    /** Outline color. Do not dispose, it's a system color. */
+    /** Hover fill color. Must be disposed, it's NOT a system color. */
+    private Color mHoverFillColor;
+
+    /** Outline color. Must be disposed, it's NOT a system color. */
     private Color mOutlineColor;
 
     /**
@@ -287,9 +290,14 @@ class LayoutCanvas extends Canvas implements ISelectionProvider {
         mGCWrapper = new GCWrapper(mHScale, mVScale);
 
         Display d = getDisplay();
-        mSelectionFgColor = new Color(d, SwtDrawingStyle.SELECTION_BORDER.getForeground());
-        mHoverFgColor     = new Color(d, SwtDrawingStyle.HOVER.getForeground());
-        mOutlineColor     = new Color(d, SwtDrawingStyle.OUTLINE.getForeground());
+        mSelectionFgColor = new Color(d, SwtDrawingStyle.SELECTION.getStrokeColor());
+        if (SwtDrawingStyle.HOVER.getStrokeColor() != null) {
+            mHoverStrokeColor = new Color(d, SwtDrawingStyle.HOVER.getStrokeColor());
+        }
+        if (SwtDrawingStyle.HOVER.getFillColor() != null) {
+            mHoverFillColor = new Color(d, SwtDrawingStyle.HOVER.getFillColor());
+        }
+        mOutlineColor = new Color(d, SwtDrawingStyle.OUTLINE.getStrokeColor());
 
         mFont = d.getSystemFont();
 
@@ -358,9 +366,19 @@ class LayoutCanvas extends Canvas implements ISelectionProvider {
             mOutlinePage = null;
         }
 
-        if (mHoverFgColor != null) {
-            mHoverFgColor.dispose();
-            mHoverFgColor = null;
+        if (mOutlineColor != null) {
+            mOutlineColor.dispose();
+            mOutlineColor = null;
+        }
+
+        if (mHoverStrokeColor != null) {
+            mHoverStrokeColor.dispose();
+            mHoverStrokeColor = null;
+        }
+
+        if (mHoverFillColor != null) {
+            mHoverFillColor.dispose();
+            mHoverFillColor = null;
         }
 
         if (mDropTarget != null) {
@@ -963,19 +981,33 @@ class LayoutCanvas extends Canvas implements ISelectionProvider {
             if (mShowOutline && mLastValidViewInfoRoot != null) {
                 gc.setForeground(mOutlineColor);
                 gc.setLineStyle(SwtDrawingStyle.OUTLINE.getLineStyle());
+                int oldAlpha = gc.getAlpha();
+                gc.setAlpha(SwtDrawingStyle.OUTLINE.getStrokeAlpha());
                 drawOutline(gc, mLastValidViewInfoRoot);
+                gc.setAlpha(oldAlpha);
             }
 
             if (mHoverRect != null) {
-                gc.setForeground(mHoverFgColor);
-                gc.setLineStyle(SwtDrawingStyle.HOVER.getLineStyle());
-
                 int x = mHScale.translate(mHoverRect.x);
                 int y = mVScale.translate(mHoverRect.y);
                 int w = mHScale.scale(mHoverRect.width);
                 int h = mVScale.scale(mHoverRect.height);
 
-                gc.drawRectangle(x, y, w, h);
+                if (mHoverStrokeColor != null) {
+                    int oldAlpha = gc.getAlpha();
+                    gc.setForeground(mHoverStrokeColor);
+                    gc.setLineStyle(SwtDrawingStyle.HOVER.getLineStyle());
+                    gc.setAlpha(SwtDrawingStyle.HOVER.getStrokeAlpha());
+                    gc.drawRectangle(x, y, w, h);
+                    gc.setAlpha(oldAlpha);
+                }
+
+                if (mHoverFillColor != null) {
+                    int oldAlpha = gc.getAlpha();
+                    gc.setAlpha(SwtDrawingStyle.HOVER.getFillAlpha());
+                    gc.fillRectangle(x, y, w, h);
+                    gc.setAlpha(oldAlpha);
+                }
             }
 
             int n = mSelections.size();
@@ -1011,7 +1043,12 @@ class LayoutCanvas extends Canvas implements ISelectionProvider {
         int w = mHScale.scale(r.width);
         int h = mVScale.scale(r.height);
 
-        gc.drawRectangle(x, y, w, h);
+        // Add +1 to the width and +1 to the height such that when you have a
+        // series of boxes (in say a LinearLayout), instead of the bottom of one
+        // box and the top of the next box being -adjacent-, they -overlap-.
+        // This makes the outline nicer visually since you don't get
+        // "double thickness" lines for all adjacent boxes.
+        gc.drawRectangle(x, y, w + 1, h + 1);
 
         for (CanvasViewInfo vi : info.getChildren()) {
             drawOutline(gc, vi);
index 1d30c7b..cb2c7f1 100644 (file)
@@ -27,122 +27,136 @@ import org.eclipse.swt.graphics.RGB;
  * class which defines the drawing styles but does not introduce any specific
  * SWT values to the API clients.
  * <p>
- * TODO: This class should eventually pick up theme preferences.
+ * TODO: This class should eventually be replaced by a scheme where the color
+ * constants are instead coming from the theme.
  */
 public enum SwtDrawingStyle {
     /**
-     * The style used for the border of a selected view
+     * The style definition corresponding to {@link DrawingStyle#SELECTION}
      */
-    SELECTION_BORDER(new RGB(0xFF, 0x00, 0x00), null, 1, SWT.LINE_SOLID, 255),
+    SELECTION(new RGB(0x00, 0x99, 0xFF), 255, new RGB(0x00, 0x99, 0xFF), 64, 2, SWT.LINE_DASH),
 
     /**
-     * The style used for the interior of a selected view
+     * The style definition corresponding to {@link DrawingStyle#GUIDELINE}
      */
-    SELECTION_FILL(null, new RGB(0xFF, 0x00, 0x00), 1, SWT.LINE_SOLID, 64),
+    GUIDELINE(new RGB(0x00, 0xFF, 0x00), 255, SWT.LINE_DOT),
 
     /**
-     * The style used for hovered views (e.g. when the mouse is directly on top
-     * of the view)
+     * The style definition corresponding to {@link DrawingStyle#HOVER}
      */
-    HOVER(new RGB(0xFF, 0x99, 0x00), null, 1, SWT.LINE_DOT, 255),
+    HOVER(null, 0, new RGB(0xFF, 0xFF, 0xFF), 64, 1, SWT.LINE_DOT),
 
     /**
-     * The style used to draw anchors (lines to the other views the given view
-     * is anchored to)
+     * The style definition corresponding to {@link DrawingStyle#ANCHOR}
      */
-    ANCHOR(new RGB(0xFF, 0x00, 0x00), null, 1, SWT.LINE_SOLID, 255),
+    ANCHOR(new RGB(0x00, 0x99, 0xFF), 96, SWT.LINE_SOLID),
 
     /**
-     * The style used to draw outlines (the structure of views)
+     * The style definition corresponding to {@link DrawingStyle#OUTLINE}
      */
-    OUTLINE(new RGB(0x00, 0xFF, 0x00), null, 1, SWT.LINE_DOT, 255),
+    OUTLINE(new RGB(0x88, 0xFF, 0x88), 160, SWT.LINE_SOLID),
 
     /**
-     * The style used to draw the recipient/target View of a drop. This is
-     * typically going to be the bounding-box of the view into which you are
-     * adding a new child.
+     * The style definition corresponding to {@link DrawingStyle#DROP_RECIPIENT}
      */
-    DROP_RECIPIENT(new RGB(0xFF, 0x99, 0x00), new RGB(0xFF, 0x99, 0x00), 1, SWT.LINE_SOLID, 255),
+    DROP_RECIPIENT(new RGB(0xFF, 0x99, 0x00), 255, new RGB(0xFF, 0x99, 0x00), 160, 1,
+            SWT.LINE_SOLID),
 
     /**
-     * The style used to draw a potential drop area <b>within</b> a
-     * {@link #DROP_RECIPIENT}. For example, if you are dragging into a view
-     * with a LinearLayout, the {@link #DROP_RECIPIENT} will be the view itself,
-     * whereas each possible insert position between two children will be a
-     * {@link #DROP_ZONE}. If the mouse is over a {@link #DROP_ZONE} it should
-     * be drawn using the style {@link #DROP_ZONE_ACTIVE}.
+     * The style definition corresponding to {@link DrawingStyle#DROP_ZONE}
      */
-    DROP_ZONE(new RGB(0xFF, 0x99, 0x00), new RGB(0xFF, 0x99, 0x00), 1, SWT.LINE_DOT, 255),
+    DROP_ZONE(new RGB(0x00, 0xAA, 0x00), 220, new RGB(0x55, 0xAA, 0x00), 200, 1, SWT.LINE_SOLID),
 
     /**
-     * The style used to draw a currently active drop zone within a drop
-     * recipient. See the documentation for {@link #DROP_ZONE} for details on
-     * the distinction between {@link #DROP_RECIPIENT}, {@link #DROP_ZONE} and
-     * {@link #DROP_ZONE_ACTIVE}.
+     * The style definition corresponding to
+     * {@link DrawingStyle#DROP_ZONE_ACTIVE}
      */
-    DROP_ZONE_ACTIVE(new RGB(0xFF, 0x99, 0x00), new RGB(0xFF, 0x99, 0x00), 1, SWT.LINE_SOLID, 255),
+    DROP_ZONE_ACTIVE(new RGB(0x00, 0xAA, 0x00), 220, new RGB(0x00, 0xAA, 0x00), 128, 2,
+            SWT.LINE_SOLID),
 
     /**
-     * The style used to raw illegal/error/invalid markers
+     * The style definition corresponding to {@link DrawingStyle#DROP_PREVIEW}
      */
-    INVALID(new RGB(0x00, 0x00, 0xFF), null, 3, SWT.LINE_SOLID, 255),
+    DROP_PREVIEW(new RGB(0xFF, 0x99, 0x00), 255, null, 0, 2, SWT.LINE_CUSTOM),
 
     /**
-     * A style used for unspecified purposes; can be used by a client to have
-     * yet another color that is domain specific; using this color constant
-     * rather than your own hardcoded value means that you will be guaranteed to
-     * pick up a color that is themed properly and will look decent with the
-     * rest of the colors
+     * The style definition corresponding to {@link DrawingStyle#HELP}
      */
-    CUSTOM1(new RGB(0xFF, 0x00, 0xFF), null, 1, SWT.LINE_SOLID, 255),
+    HELP(new RGB(0xFF, 0xFF, 0xFF), 255, new RGB(0x00, 0x00, 0x00), 128, 1, SWT.LINE_SOLID),
 
     /**
-     * A second styled used for unspecified purposes; see {@link #CUSTOM1} for
-     * details.
+     * The style definition corresponding to {@link DrawingStyle#INVALID}
      */
-    CUSTOM2(new RGB(0x00, 0xFF, 0xFF), null, 1, SWT.LINE_DOT, 255);
+    INVALID(new RGB(0xFF, 0xFF, 0xFF), 255, new RGB(0xFF, 0x00, 0x00), 150, 2, SWT.LINE_SOLID),
+
+    /**
+     * The style definition corresponding to {@link DrawingStyle#CUSTOM1}
+     */
+    CUSTOM1(new RGB(0xFF, 0x00, 0xFF), 255, null, 0, 1, SWT.LINE_SOLID),
+
+    /**
+     * The style definition corresponding to {@link DrawingStyle#CUSTOM2}
+     */
+    CUSTOM2(new RGB(0x00, 0xFF, 0xFF), 255, null, 0, 1, SWT.LINE_DOT);
 
     /**
      * Construct a new style value with the given foreground, background, width,
      * linestyle and transparency.
      *
-     * @param fg A color descriptor for the foreground color, or null if no
+     * @param stroke A color descriptor for the foreground color, or null if no
      *            foreground color should be set
-     * @param bg A color descriptor for the background color, or null if no
+     * @param fill A color descriptor for the background color, or null if no
      *            foreground color should be set
-     * @param width The line width, in pixels, or 0 if no line width should be
-     *            set
+     * @param lineWidth The line width, in pixels, or 0 if no line width should
+     *            be set
      * @param lineStyle The SWT line style - such as {@link SWT#LINE_SOLID}.
-     * @param alpha The alpha value, an integer in the range 0 to 255 where 0 is
-     *            fully transparent and 255 is fully opaque.
+     * @param strokeAlpha The alpha value of the stroke, an integer in the range 0 to 255
+     *            where 0 is fully transparent and 255 is fully opaque.
+     * @param fillAlpha The alpha value of the fill, an integer in the range 0 to 255
+     *            where 0 is fully transparent and 255 is fully opaque.
      */
-    private SwtDrawingStyle(RGB fg, RGB bg, int width, int lineStyle, int alpha) {
-        mFg = fg;
-        mBg = bg;
-        mWidth = width;
+    private SwtDrawingStyle(RGB stroke, int strokeAlpha, RGB fill, int fillAlpha, int lineWidth,
+            int lineStyle) {
+        mStroke = stroke;
+        mFill = fill;
+        mLineWidth = lineWidth;
         mLineStyle = lineStyle;
-        mAlpha = alpha;
+        mStrokeAlpha = strokeAlpha;
+        mFillAlpha = fillAlpha;
     }
 
     /**
-     * Return the foreground RGB color description to be used for this style, or
-     * null if none
+     * Convenience constructor for typical drawing styles, which do not specify
+     * a fill and use a standard thickness line
+     *
+     * @param stroke Stroke color to be used (e.g. for the border/foreground)
+     * @param strokeAlpha Transparency to use for the stroke; 0 is transparent
+     *            and 255 is fully opaque.
+     * @param lineStyle The SWT line style - such as {@link SWT#LINE_SOLID}.
      */
-    public RGB getForeground() {
-        return mFg;
+    private SwtDrawingStyle(RGB stroke, int strokeAlpha, int lineStyle) {
+        this(stroke, strokeAlpha, null, 255, 1, lineStyle);
     }
 
     /**
-     * Return the background RGB color description to be used for this style, or
-     * null if none
+     * Return the stroke/foreground/border RGB color description to be used for
+     * this style, or null if none
      */
-    public RGB getBackground() {
-        return mBg;
+    public RGB getStrokeColor() {
+        return mStroke;
+    }
+
+    /**
+     * Return the fill/background/interior RGB color description to be used for
+     * this style, or null if none
+     */
+    public RGB getFillColor() {
+        return mFill;
     }
 
     /** Return the line width to be used for this style */
     public int getLineWidth() {
-        return mWidth;
+        return mLineWidth;
     }
 
     /** Return the SWT line style to be used for this style */
@@ -150,9 +164,20 @@ public enum SwtDrawingStyle {
         return mLineStyle;
     }
 
-    /** Return the alpha value (in the range 0,255) to be used for this style */
-    public int getAlpha() {
-        return mAlpha;
+    /**
+     * Return the stroke alpha value (in the range 0,255) to be used for this
+     * style
+     */
+    public int getStrokeAlpha() {
+        return mStrokeAlpha;
+    }
+
+    /**
+     * Return the fill alpha value (in the range 0,255) to be used for this
+     * style
+     */
+    public int getFillAlpha() {
+        return mFillAlpha;
     }
 
     /**
@@ -161,10 +186,10 @@ public enum SwtDrawingStyle {
      */
     public static SwtDrawingStyle of(DrawingStyle style) {
         switch (style) {
-            case SELECTION_BORDER:
-                return SELECTION_BORDER;
-            case SELECTION_FILL:
-                return SELECTION_FILL;
+            case SELECTION:
+                return SELECTION;
+            case GUIDELINE:
+                return GUIDELINE;
             case HOVER:
                 return HOVER;
             case ANCHOR:
@@ -177,6 +202,10 @@ public enum SwtDrawingStyle {
                 return DROP_ZONE_ACTIVE;
             case DROP_RECIPIENT:
                 return DROP_RECIPIENT;
+            case DROP_PREVIEW:
+                return DROP_PREVIEW;
+            case HELP:
+                return HELP;
             case INVALID:
                 return INVALID;
             case CUSTOM1:
@@ -190,13 +219,21 @@ public enum SwtDrawingStyle {
         }
     }
 
-    private final RGB mFg;
+    /** RGB description of the stroke/foreground/border color */
+    private final RGB mStroke;
 
-    private final RGB mBg;
+    /** RGB description of the fill/foreground/interior color */
+    private final RGB mFill;
 
-    private final int mWidth;
+    /** Pixel thickness of the stroke/border */
+    private final int mLineWidth;
 
+    /** SWT line style of the border/stroke */
     private final int mLineStyle;
 
-    private final int mAlpha;
+    /** Alpha (in the range 0-255) of the stroke/border */
+    private final int mStrokeAlpha;
+
+    /** Alpha (in the range 0-255) of the fill/interior */
+    private final int mFillAlpha;
 }