ADT: GLE toggle buttons in configuration composite.

New GLE2 toggles:
- explode view
- show borders

Change-Id: I638b1d4591bee4729be7b4dff753cb166b3eaa61
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java
index 1178449..f2f4ce5 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/configuration/ConfigurationComposite.java
@@ -41,6 +41,7 @@
 import org.eclipse.swt.events.SelectionAdapter;
 import org.eclipse.swt.events.SelectionEvent;
 import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.layout.GridLayout;
 import org.eclipse.swt.widgets.Button;
@@ -83,12 +84,13 @@
     private final ArrayList<ResourceQualifier[] > mLocaleList =
         new ArrayList<ResourceQualifier[]>();
 
-    private final IConfigListener mListener;
-
     private boolean mClipping = true;
 
     private LayoutDevice mCurrentDevice;
 
+    /** The config listener given to the constructor. Never null. */
+    private final IConfigListener mListener;
+
     /**
      * Interface implemented by the part which owns a {@link ConfigurationComposite}.
      * This notifies the owners when the configuration change.
@@ -99,7 +101,7 @@
         void onConfigurationChange();
         void onThemeChange();
         void onCreate();
-        void OnClippingChange();
+        void onClippingChange();
 
         ProjectResources getProjectResources();
         ProjectResources getFrameworkResources();
@@ -107,17 +109,91 @@
         Map<String, Map<String, IResourceValue>> getConfiguredFrameworkResources();
     }
 
-    public ConfigurationComposite(IConfigListener listener, Composite parent, int style) {
+    /**
+     * Interface implemented by the part which owns a {@link ConfigurationComposite}
+     * to define and handle custom toggle buttons in the button bar. Each toggle is
+     * implemented using a button, with a callback when the button is selected.
+     */
+    public static abstract class CustomToggle {
+
+        /** The UI label of the toggle. Can be null if the image exists. */
+        private final String mUiLabel;
+
+        /** The image to use for this toggle. Can be null if the label exists. */
+        private final Image mImage;
+
+        /** The tooltip for the toggle. Can be null. */
+        private final String mUiTooltip;
+
+        /**
+         * Initializes a new {@link CustomToggle}. The values set here will be used
+         * later to create the actual toggle.
+         *
+         * @param uiLabel   The UI label of the toggle. Can be null if the image exists.
+         * @param image     The image to use for this toggle. Can be null if the label exists.
+         * @param uiTooltip The tooltip for the toggle. Can be null.
+         */
+        public CustomToggle(
+                String uiLabel,
+                Image image,
+                String uiTooltip) {
+            mUiLabel = uiLabel;
+            mImage = image;
+            mUiTooltip = uiTooltip;
+        }
+
+        /** Called by the {@link ConfigurationComposite} when the button is selected. */
+        public abstract void onSelected(boolean newState);
+
+        private void createToggle(Composite parent) {
+            final Button b = new Button(parent, SWT.TOGGLE | SWT.FLAT);
+
+            if (mUiTooltip != null) {
+                b.setToolTipText(mUiTooltip);
+            }
+            if (mImage != null) {
+                b.setImage(mImage);
+            }
+            if (mUiLabel != null) {
+                b.setText(mUiLabel);
+            }
+
+            b.addSelectionListener(new SelectionAdapter() {
+                @Override
+                public void widgetSelected(SelectionEvent e) {
+                    onSelected(b.getSelection());
+                }
+            });
+        }
+    }
+
+    /**
+     * Creates a new {@link ConfigurationComposite} and adds it to the parent.
+     *
+     * @param listener An {@link IConfigListener} that gets and sets configuration properties.
+     *          Mandatory, cannot be null.
+     * @param customToggles An array of {@link CustomToggle} to define extra toggles button
+     *          to display at the top of the composite. Can be empty or null.
+     * @param parent The parent composite.
+     * @param style The style of this composite.
+     */
+    public ConfigurationComposite(IConfigListener listener,
+            CustomToggle[] customToggles,
+            Composite parent, int style) {
         super(parent, style);
         mListener = listener;
 
+        if (customToggles == null) {
+            customToggles = new CustomToggle[0];
+        }
+
         GridLayout gl;
         GridData gd;
-        int cols = 10; // device*2+config*2+locale*2+separator*2+theme+createBtn
+        int cols = 10;  // device*2+config*2+locale*2+separator*2+theme+createBtn
 
-        // ---- First line: collapse button, clipping button, editing config display.
+        // ---- First line: custom buttons, clipping button, editing config display.
         Composite labelParent = new Composite(this, SWT.NONE);
-        labelParent.setLayout(gl = new GridLayout(3, false));
+        labelParent.setLayout(gl = new GridLayout(3 + customToggles.length, false));
         gl.marginWidth = gl.marginHeight = 0;
         labelParent.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
         gd.horizontalSpan = cols;
@@ -127,6 +203,10 @@
         mCurrentLayoutLabel.setLayoutData(gd = new GridData(GridData.FILL_HORIZONTAL));
         gd.widthHint = 50;
 
+        for (CustomToggle toggle : customToggles) {
+            toggle.createToggle(labelParent);
+        }
+
         mClippingButton = new Button(labelParent, SWT.TOGGLE | SWT.FLAT);
         mClippingButton.setSelection(mClipping);
         mClippingButton.setToolTipText("Toggles screen clipping on/off");
@@ -134,7 +214,7 @@
         mClippingButton.addSelectionListener(new SelectionAdapter() {
             @Override
             public void widgetSelected(SelectionEvent e) {
-                OnClippingChange();
+                onClippingChange();
             }
         });
 
@@ -675,10 +755,10 @@
         }
     }
 
-    protected void OnClippingChange() {
+    protected void onClippingChange() {
         mClipping = mClippingButton.getSelection();
         if (mListener != null) {
-            mListener.OnClippingChange();
+            mListener.onClippingChange();
         }
     }
 
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle1/GraphicalLayoutEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle1/GraphicalLayoutEditor.java
index 25b67df..2280b30 100644
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle1/GraphicalLayoutEditor.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle1/GraphicalLayoutEditor.java
@@ -29,6 +29,7 @@
 import com.android.ide.eclipse.adt.internal.editors.layout.LayoutReloadMonitor.ILayoutReloadListener;
 import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationComposite;
 import com.android.ide.eclipse.adt.internal.editors.layout.configuration.LayoutCreatorDialog;
+import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationComposite.CustomToggle;
 import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationComposite.IConfigListener;
 import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
 import com.android.ide.eclipse.adt.internal.editors.layout.parts.ElementCreateCommand;
@@ -147,6 +148,8 @@
     private ProjectCallback mProjectCallback;
     private ILayoutLog mLogger;
 
+    private boolean mUseExplodeMode;
+    private boolean mUseOutlineMode;
     private boolean mNeedsXmlReload = false;
     private boolean mNeedsRecompute = false;
 
@@ -195,8 +198,6 @@
         }
     };
 
-    private boolean mExplodedView;
-
     public GraphicalLayoutEditor(LayoutEditor layoutEditor) {
         mLayoutEditor = layoutEditor;
         setEditDomain(new DefaultEditDomain(this));
@@ -220,7 +221,33 @@
         gl.marginHeight = gl.marginWidth = 0;
 
         // create the top part for the configuration control
-        mConfigComposite = new ConfigurationComposite(this, parent, SWT.NONE);
+
+        CustomToggle[] toggles = new CustomToggle[] {
+                new CustomToggle(
+                        "Explode",
+                        null, //image
+                        "Displays extra margins in the layout."
+                        ) {
+                    @Override
+                    public void onSelected(boolean newState) {
+                        mUseExplodeMode = newState;
+                        recomputeLayout();
+                    }
+                },
+                new CustomToggle(
+                        "Outline",
+                        null, //image
+                        "Shows the outline of all views in the layout."
+                        ) {
+                    @Override
+                    public void onSelected(boolean newState) {
+                        mUseOutlineMode = newState;
+                        recomputeLayout();
+                    }
+                }
+        };
+
+        mConfigComposite = new ConfigurationComposite(this, toggles, parent, SWT.NONE);
 
         // create a new composite that will contain the standard editor controls.
         Composite editorParent = new Composite(parent, SWT.NONE);
@@ -822,7 +849,7 @@
         recomputeLayout();
     }
 
-    public void OnClippingChange() {
+    public void onClippingChange() {
         recomputeLayout();
     }
 
@@ -955,10 +982,9 @@
                             // Compute the layout
                             Rectangle rect = getBounds();
 
-                            mExplodedView = !mConfigComposite.getClipping(); //FIXME: need new toggle
                             int width = rect.width;
                             int height = rect.height;
-                            if (mExplodedView) {
+                            if (mUseExplodeMode) {
                                 // compute how many padding in x and y will bump the screen size
                                 ExplodedRenderingHelper helper = new ExplodedRenderingHelper(
                                         getModel(), iProject);
@@ -977,7 +1003,7 @@
                             boolean isProjectTheme = mConfigComposite.isProjectTheme();
 
                             UiElementPullParser parser = new UiElementPullParser(getModel(),
-                                    mExplodedView, density, xdpi, iProject);
+                                    mUseExplodeMode, density, xdpi, iProject);
 
                             ILayoutResult result = computeLayout(bridge, parser,
                                     iProject /* projectKey */,
@@ -1378,6 +1404,6 @@
     }
 
     public boolean hasOutline() {
-        return mExplodedView;
+        return mUseOutlineMode;
     }
 }
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java
index 796bce1..a8c6386 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java
@@ -26,6 +26,7 @@
 import com.android.ide.eclipse.adt.internal.editors.layout.LayoutReloadMonitor.ILayoutReloadListener;

 import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationComposite;

 import com.android.ide.eclipse.adt.internal.editors.layout.configuration.LayoutCreatorDialog;

+import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationComposite.CustomToggle;

 import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationComposite.IConfigListener;

 import com.android.ide.eclipse.adt.internal.editors.layout.parts.ElementCreateCommand;

 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode;

@@ -147,6 +148,8 @@
 

     private ReloadListener mReloadListener;

 

+    protected boolean mUseExplodeMode;

+

 

     public GraphicalEditorPart(LayoutEditor layoutEditor) {

         mLayoutEditor = layoutEditor;

@@ -215,8 +218,33 @@
         gl.marginHeight = gl.marginWidth = 0;

 

         // create the top part for the configuration control

+

+        CustomToggle[] toggles = new CustomToggle[] {

+                new CustomToggle(

+                        "Explode",

+                        null, //image

+                        "Displays extra margins in the layout."

+                        ) {

+                    @Override

+                    public void onSelected(boolean newState) {

+                        mUseExplodeMode = newState;

+                        recomputeLayout();

+                    }

+                },

+                new CustomToggle(

+                        "Outline",

+                        null, //image

+                        "Shows the of all views in the layout."

+                        ) {

+                    @Override

+                    public void onSelected(boolean newState) {

+                        mLayoutCanvas.setShowOutline(newState);

+                    }

+                }

+        };

+

         mConfigListener = new ConfigListener();

-        mConfigComposite = new ConfigurationComposite(mConfigListener, parent, SWT.BORDER);

+        mConfigComposite = new ConfigurationComposite(mConfigListener, toggles, parent, SWT.BORDER);

         mConfigComposite.updateUIFromResources();

 

         mSashPalette = new SashForm(parent, SWT.HORIZONTAL);

@@ -390,7 +418,7 @@
             recomputeLayout();

         }

 

-        public void OnClippingChange() {

+        public void onClippingChange() {

             recomputeLayout();

         }

 

@@ -891,10 +919,9 @@
                             // Compute the layout

                             Rectangle rect = getBounds();

 

-                            boolean explodedView = !mConfigComposite.getClipping(); //FIXME: need new toggle

                             int width = rect.width;

                             int height = rect.height;

-                            if (explodedView) {

+                            if (mUseExplodeMode) {

                                 // compute how many padding in x and y will bump the screen size

                                 ExplodedRenderingHelper helper = new ExplodedRenderingHelper(

                                         getModel(), iProject);

@@ -913,7 +940,7 @@
                             boolean isProjectTheme = mConfigComposite.isProjectTheme();

 

                             UiElementPullParser parser = new UiElementPullParser(getModel(),

-                                    explodedView, density, xdpi, iProject);

+                                    mUseExplodeMode, density, xdpi, iProject);

 

                             ILayoutResult result = computeLayout(bridge, parser,

                                     iProject /* projectKey */,

@@ -962,7 +989,7 @@
      * the implementation API level.

      *

      * Implementation detail: the bridge's computeLayout() method already returns a newly

-     * allocated ILayourResult.

+     * allocated ILayoutResult.

      */

     @SuppressWarnings("deprecation")

     private static ILayoutResult computeLayout(LayoutBridge bridge,

diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java
index d0e810c..1f7658f 100755
--- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/LayoutCanvas.java
@@ -123,12 +123,18 @@
     /** Hover border color. Must be disposed, it's NOT a system color. */

     private Color mHoverFgColor;

 

+    /** Outline color. Do not dispose, it's a system color. */

+    private Color mOutlineColor;

+

     /**

      * The <em>current</em> alternate selection, if any, which changes when the Alt key is

      * used during a selection. Can be null.

      */

     private AlternateSelection mAltSelection;

 

+    /** When true, always display the outline of all views. */

+    private boolean mShowOutline;

+

 

     public LayoutCanvas(Composite parent, int style) {

         super(parent, style | SWT.DOUBLE_BUFFERED);

@@ -136,6 +142,7 @@
         Display d = getDisplay();

         mSelectionFgColor = d.getSystemColor(SWT.COLOR_RED);

         mHoverFgColor     = new Color(d, 0xFF, 0x99, 0x00); // orange

+        mOutlineColor     = d.getSystemColor(SWT.COLOR_GREEN);

         mSelectionFont    = d.getSystemFont();

 

         addPaintListener(new PaintListener() {

@@ -217,6 +224,11 @@
         redraw();

     }

 

+    public void setShowOutline(boolean newState) {

+        mShowOutline = newState;

+        redraw();

+    }

+

     /**

      * Called by the {@link GraphicalEditorPart} when the Copy action is requested.

      *

@@ -307,6 +319,12 @@
             }

         }

 

+        if (mShowOutline) {

+            gc.setForeground(mOutlineColor);

+            gc.setLineStyle(SWT.LINE_DOT);

+            drawOutline(gc, mLastValidViewInfoRoot);

+        }

+

         if (mHoverRect != null) {

             gc.setForeground(mHoverFgColor);

             gc.setLineStyle(SWT.LINE_DOT);

@@ -325,6 +343,16 @@
         }

     }

 

+    private void drawOutline(GC gc, ViewInfo info) {

+

+        Rectangle r = info.getAbsRect();

+        gc.drawRectangle(r.x + IMAGE_MARGIN, r.y + IMAGE_MARGIN, r.width, r.height);

+

+        for (ViewInfo vi : info.getChildren()) {

+            drawOutline(gc, vi);

+        }

+    }

+

     private void drawSelection(GC gc, Selection s) {

         Rectangle r = s.getRect();

 

@@ -611,7 +639,7 @@
      * have a fixed API.

      */

     private static class ViewInfo {

-        private final Rectangle mRealRect;

+        private final Rectangle mAbsRect;

         private final Rectangle mSelectionRect;

         private final String mName;

         private final Object mKey;

@@ -638,13 +666,13 @@
             int w = viewInfo.getRight() - x;

             int h = viewInfo.getBottom() - y;

 

-            mRealRect = new Rectangle(x, y, w, h);

-

             if (parent != null) {

                 x += parentX;

                 y += parentY;

             }

 

+            mAbsRect = new Rectangle(x, y, w - 1, h - 1);

+

             if (viewInfo.getChildren() != null) {

                 for (ILayoutViewInfo child : viewInfo.getChildren()) {

                     mChildren.add(new ViewInfo(child, this, x, y));

@@ -669,12 +697,11 @@
         }

 

         /**

-         * Returns the original {@link ILayoutResult} bounds, relative to the parent.

-         * @deprecated TODO Remove if it's not going to be used

+         * Returns the original {@link ILayoutResult} bounds in absolute coordinates

+         * over the whole graphic.

          */

-        @SuppressWarnings("unused")

-        public Rectangle getRealRect() {

-            return mRealRect;

+        public Rectangle getAbsRect() {

+            return mAbsRect;

         }

 

         /*

@@ -860,5 +887,4 @@
         }

     }

 

-

 }