OSDN Git Service

Improve the Outline contents
authorTor Norbye <tnorbye@google.com>
Wed, 5 Jan 2011 02:28:43 +0000 (18:28 -0800)
committerTor Norbye <tnorbye@google.com>
Fri, 7 Jan 2011 03:15:18 +0000 (19:15 -0800)
1. Make the outline use StyledStrings such that we can use different
colors for different elements in the outline. Use the decorations
color for the element type that follows the id.

2. For elements that define a "text" property, include the text (or a
prefix of it if it is long) in the outline. Thus, for a Button you
might see something like "Button01 - "Submit Order").

3. For elements that define a "src" property, show the
source. Therefore, for an ImageView you might see "ImageView - logo".

4. For <include> elements, show the name of the included layout.

Change-Id: Ibd4c8339ea0e03c969ccaec1a67bc64436ed67af

eclipse/dictionary.txt
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LayoutConstants.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/OutlinePage.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/resources/uimodel/UiItemElementNode.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/uimodel/UiElementNode.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/refactorings/extractstring/ExtractStringRefactoring.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/wizards/newproject/NewProjectWizard.java

index 5352587..8ab29e6 100644 (file)
@@ -27,6 +27,7 @@ bytecode
 callback
 callbacks
 carlo
+cf
 changeset
 checkbox
 classloader
@@ -178,6 +179,7 @@ submenu
 supertype
 syncs
 temp
+textfields
 thematically
 themed
 tmp
index d9eb8fb..dd0af54 100644 (file)
@@ -135,4 +135,16 @@ public class LayoutConstants {
 
     /** The prefix for existing id attribute values, @id/ */
     public static String ID_PREFIX = "@id/"; //$NON-NLS-1$
+
+    /** Prefix for resources that reference layouts */
+    public static String LAYOUT_PREFIX = "@layout/"; //$NON-NLS-1$
+
+    /** Prefix for resources that reference drawables */
+    public static String DRAWABLE_PREFIX = "@drawable/"; //$NON-NLS-1$
+
+    /** Prefix for resources that reference strings */
+    public static String STRING_PREFIX = "@string/"; //$NON-NLS-1$
+
+    /** Prefix for resources that reference Android strings */
+    public static String ANDROID_STRING_PREFIX = "@android:string/"; //$NON-NLS-1$
 }
index 9ba31e1..97fc96a 100644 (file)
@@ -16,7 +16,9 @@
 
 package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
 
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_STRING_PREFIX;
 import static com.android.ide.common.layout.LayoutConstants.SCROLL_VIEW;
+import static com.android.ide.common.layout.LayoutConstants.STRING_PREFIX;
 import static com.android.ide.eclipse.adt.AndroidConstants.ANDROID_PKG;
 import static com.android.sdklib.resources.Density.DEFAULT_DENSITY;
 
@@ -1927,6 +1929,45 @@ public class GraphicalEditorPart extends EditorPart
         return findResourceFile(type, name, isFrameworkResource);
     }
 
+    /**
+     * Resolve the given @string reference into a literal String using the current project
+     * configuration
+     *
+     * @param text the text resource reference to resolve
+     * @return the resolved string, or null
+     */
+    public String findString(String text) {
+        if (text.startsWith(STRING_PREFIX)) {
+            return findString(text.substring(STRING_PREFIX.length()), false);
+        } else if (text.startsWith(ANDROID_STRING_PREFIX)) {
+            return findString(text.substring(ANDROID_STRING_PREFIX.length()), true);
+        } else {
+            return text;
+        }
+    }
+
+    private String findString(String name, boolean isFrameworkResource) {
+        Map<String, Map<String, ResourceValue>> map;
+        map = isFrameworkResource ? mConfiguredFrameworkRes : mConfiguredProjectRes;
+        if (map == null) {
+            // Not yet configured
+            return null;
+        }
+
+        Map<String, ResourceValue> layoutMap = map.get(ResourceType.STRING.getName());
+        if (layoutMap != null) {
+            ResourceValue value = layoutMap.get(name);
+            if (value != null) {
+                // FIXME: This code does not handle theme value resolution.
+                // There is code to handle this, but it's in layoutlib; we should
+                // expose that and use it here.
+                return value.getValue();
+            }
+        }
+
+        return null;
+    }
+
     /** This StyleRange represents a missing class link that the user can click */
     private static class ClassLinkStyleRange extends StyleRange {}
 
index 0b6c969..4870894 100755 (executable)
 
 package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
 
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_SRC;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_TEXT;
+import static com.android.ide.common.layout.LayoutConstants.DRAWABLE_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.LAYOUT_PREFIX;
+import static org.eclipse.jface.viewers.StyledString.QUALIFIER_STYLER;
+
 import com.android.ide.common.api.INode;
 import com.android.ide.common.api.InsertType;
 import com.android.ide.common.layout.BaseLayoutRule;
@@ -24,6 +31,7 @@ import com.android.ide.eclipse.adt.internal.editors.IconFactory;
 import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
 import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
 import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
+import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors;
 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.IncludeFinder.Reference;
 import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy;
 import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
@@ -43,15 +51,16 @@ import org.eclipse.jface.action.Separator;
 import org.eclipse.jface.viewers.DoubleClickEvent;
 import org.eclipse.jface.viewers.IDoubleClickListener;
 import org.eclipse.jface.viewers.IElementComparer;
-import org.eclipse.jface.viewers.ILabelProvider;
-import org.eclipse.jface.viewers.ILabelProviderListener;
 import org.eclipse.jface.viewers.ISelection;
 import org.eclipse.jface.viewers.ITreeContentProvider;
 import org.eclipse.jface.viewers.ITreeSelection;
+import org.eclipse.jface.viewers.StyledCellLabelProvider;
+import org.eclipse.jface.viewers.StyledString;
 import org.eclipse.jface.viewers.TreePath;
 import org.eclipse.jface.viewers.TreeSelection;
 import org.eclipse.jface.viewers.TreeViewer;
 import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerCell;
 import org.eclipse.swt.dnd.DND;
 import org.eclipse.swt.dnd.Transfer;
 import org.eclipse.swt.events.DisposeEvent;
@@ -73,6 +82,8 @@ import org.eclipse.ui.IWorkbenchPart;
 import org.eclipse.ui.PartInitException;
 import org.eclipse.ui.actions.ActionFactory;
 import org.eclipse.ui.views.contentoutline.ContentOutlinePage;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
 
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -94,6 +105,12 @@ import java.util.Set;
 public class OutlinePage extends ContentOutlinePage
     implements ISelectionListener, INullSelectionListener {
 
+    /** Label which separates outline text from additional attributes like text prefix or url */
+    private static final String LABEL_SEPARATOR = " - ";
+
+    /** Max character count in labels, used for truncation */
+    private static final int LABEL_MAX_WIDTH = 50;
+
     /**
      * The graphical editor that created this outline.
      */
@@ -435,10 +452,12 @@ public class OutlinePage extends ContentOutlinePage
      * Label provider for the Outline model.
      * Objects are going to be {@link CanvasViewInfo}.
      */
-    private class LabelProvider implements ILabelProvider {
-
+    private class LabelProvider extends StyledCellLabelProvider {
         /**
          * Returns the element's logo with a fallback on the android logo.
+         *
+         * @param element the tree element
+         * @return the image to be used as a logo
          */
         public Image getImage(Object element) {
             if (element instanceof CanvasViewInfo) {
@@ -464,9 +483,13 @@ public class OutlinePage extends ContentOutlinePage
         }
 
         /**
-         * Uses UiElementNode.shortDescription for the label for this tree item.
+         * Uses {@link UiElementNode#getStyledDescription} for the label for this tree item.
          */
-        public String getText(Object element) {
+        @Override
+        public void update(ViewerCell cell) {
+            Object element = cell.getElement();
+            StyledString styledString = null;
+
             CanvasViewInfo vi = null;
             if (element instanceof CanvasViewInfo) {
                 vi = (CanvasViewInfo) element;
@@ -475,33 +498,73 @@ public class OutlinePage extends ContentOutlinePage
 
             if (element instanceof UiElementNode) {
                 UiElementNode node = (UiElementNode) element;
-                return node.getShortDescription();
+                styledString = node.getStyledDescription();
+                Node xmlNode = node.getXmlNode();
+                if (xmlNode instanceof Element) {
+                    Element e = (Element) xmlNode;
+                    if (e.hasAttributeNS(ANDROID_URI, ATTR_TEXT)) {
+                        // Show the text attribute
+                        String text = e.getAttributeNS(ANDROID_URI, ATTR_TEXT);
+                        if (text != null && text.length() > 0
+                                && !text.contains(node.getDescriptor().getUiName())) {
+                            if (text.charAt(0) == '@') {
+                                String resolved = mGraphicalEditorPart.findString(text);
+                                if (resolved != null) {
+                                    text = resolved;
+                                }
+                            }
+                            styledString.append(LABEL_SEPARATOR, QUALIFIER_STYLER);
+                            styledString.append('"', QUALIFIER_STYLER);
+                            styledString.append(truncate(text, styledString), QUALIFIER_STYLER);
+                            styledString.append('"', QUALIFIER_STYLER);
+                        }
+                    } else if (e.hasAttributeNS(ANDROID_URI, ATTR_SRC)) {
+                        // Show ImageView source attributes etc
+                        String src = e.getAttributeNS(ANDROID_URI, ATTR_SRC);
+                        if (src != null && src.length() > 0) {
+                            if (src.startsWith(DRAWABLE_PREFIX)) {
+                                src = src.substring(DRAWABLE_PREFIX.length());
+                            }
+                            styledString.append(LABEL_SEPARATOR, QUALIFIER_STYLER);
+                            styledString.append(truncate(src, styledString), QUALIFIER_STYLER);
+                        }
+                    } else if (e.getTagName().equals(LayoutDescriptors.VIEW_INCLUDE)) {
+                        // Show the include reference.
+
+                        // Note: the layout attribute is NOT in the Android namespace
+                        String src = e.getAttribute(LayoutDescriptors.ATTR_LAYOUT);
+                        if (src != null && src.length() > 0) {
+                            if (src.startsWith(LAYOUT_PREFIX)) {
+                                src = src.substring(LAYOUT_PREFIX.length());
+                            }
+                            styledString.append(LABEL_SEPARATOR, QUALIFIER_STYLER);
+                            styledString.append(truncate(src, styledString), QUALIFIER_STYLER);
+                        }
+                    }
+                }
             } else if (element == null && vi != null) {
-                // It's an inclusion-context
+                // It's an inclusion-context: display it
                 Reference includedWithin = mGraphicalEditorPart.getIncludedWithin();
                 if (includedWithin != null) {
-                    return includedWithin.getDisplayName();
+                    styledString = new StyledString();
+                    styledString.append(includedWithin.getDisplayName(), QUALIFIER_STYLER);
                 }
             }
 
-            return element == null ? "(null)" : element.toString();  //$NON-NLS-1$
-        }
-
-        public void addListener(ILabelProviderListener listener) {
-            // pass
-        }
+            if (styledString == null) {
+                styledString = new StyledString();
+                styledString.append(element == null ? "(null)" : element.toString());
+            }
 
-        public void dispose() {
-            // pass
-        }
+           cell.setText(styledString.toString());
+           cell.setStyleRanges(styledString.getStyleRanges());
+           cell.setImage(getImage(element));
+           super.update(cell);
+       }
 
+        @Override
         public boolean isLabelProperty(Object element, String property) {
-            // pass
-            return false;
-        }
-
-        public void removeListener(ILabelProviderListener listener) {
-            // pass
+            return super.isLabelProperty(element, property);
         }
     }
 
@@ -846,4 +909,27 @@ public class OutlinePage extends ContentOutlinePage
 
         return null;
     }
+
+    /**
+     * Truncates the given text such that it will fit into the given {@link StyledString}
+     * up to a maximum length of {@link #LABEL_MAX_WIDTH}.
+     *
+     * @param text the text to truncate
+     * @param string the existing string to be appended to
+     * @return the truncated string
+     */
+    private static String truncate(String text, StyledString string) {
+        int existingLength = string.length();
+
+        if (text.length() + existingLength > LABEL_MAX_WIDTH) {
+            int truncatedLength = LABEL_MAX_WIDTH - existingLength - 3;
+            if (truncatedLength > 0) {
+                return String.format("%1$s...", text.substring(0, truncatedLength));
+            } else {
+                return ""; //$NON-NLS-1$
+            }
+        }
+
+        return text;
+    }
 }
index 1bb0f58..539881a 100644 (file)
@@ -25,14 +25,14 @@ import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 
 /**
- * {@link UiItemElementNode} is apecial version of {@link UiElementNode} that 
+ * {@link UiItemElementNode} is a special version of {@link UiElementNode} that
  * customizes the element display to include the item type attribute if present.
  */
 public class UiItemElementNode extends UiElementNode {
 
     /**
      * Creates a new {@link UiElementNode} described by a given {@link ItemElementDescriptor}.
-     * 
+     *
      * @param elementDescriptor The {@link ItemElementDescriptor} for the XML node. Cannot be null.
      */
     public UiItemElementNode(ItemElementDescriptor elementDescriptor) {
index 1150bd0..c554310 100644 (file)
@@ -44,6 +44,7 @@ import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
 import com.android.sdklib.SdkConstants;
 
 import org.eclipse.core.runtime.IStatus;
+import org.eclipse.jface.viewers.StyledString;
 import org.eclipse.ui.views.properties.IPropertyDescriptor;
 import org.eclipse.ui.views.properties.IPropertySource;
 import org.w3c.dom.Attr;
@@ -214,8 +215,17 @@ public class UiElementNode implements IPropertySource {
      * @return A short string describing the UI node suitable for tree views.
      */
     public String getShortDescription() {
-        if (mXmlNode != null && mXmlNode instanceof Element && mXmlNode.hasAttributes()) {
+        String attr = getDescAttribute();
+        if (attr != null) {
+            return String.format("%1$s (%2$s)", attr, mDescriptor.getUiName());
+        }
 
+        return mDescriptor.getUiName();
+    }
+
+    /** Returns the key attribute that can be used to describe this node, or null */
+    private String getDescAttribute() {
+        if (mXmlNode != null && mXmlNode instanceof Element && mXmlNode.hasAttributes()) {
             // Application and Manifest nodes have a special treatment: they are unique nodes
             // so we don't bother trying to differentiate their strings and we fall back to
             // just using the UI name below.
@@ -254,11 +264,41 @@ public class UiElementNode implements IPropertySource {
                 }
             }
             if (attr != null && attr.length() > 0) {
-                return String.format("%1$s (%2$s)", attr, mDescriptor.getUiName());
+                return attr;
             }
         }
 
-        return String.format("%1$s", mDescriptor.getUiName());
+        return null;
+    }
+
+    /**
+     * Computes a styled string describing the UI node suitable for tree views.
+     * Similar to {@link #getShortDescription()} but styles the Strings.
+     *
+     * @return A styled string describing the UI node suitable for tree views.
+     */
+    public StyledString getStyledDescription() {
+        String uiName = mDescriptor.getUiName();
+
+        StyledString styledString = new StyledString();
+        String attr = getDescAttribute();
+        if (attr != null) {
+            // Don't append the two when it's a repeat, e.g. Button01 (Button),
+            // only when the ui name is not part of the attribute
+            if (attr.indexOf(uiName) == -1) {
+                styledString.append(attr);
+                styledString.append(String.format(" (%1$s)", uiName),
+                        StyledString.DECORATIONS_STYLER);
+            } else {
+                styledString.append(attr);
+            }
+        }
+
+        if (styledString.length() == 0) {
+            styledString.append(uiName);
+        }
+
+        return styledString;
     }
 
     /**
@@ -880,7 +920,7 @@ public class UiElementNode implements IPropertySource {
         }
 
         // If we get here and parentXmlNode is null, the node is to be created
-        // as the root node of the document (which can't be null, cf check above).
+        // as the root node of the document (which can't be null, cf. check above).
         if (parentXmlNode == null) {
             parentXmlNode = doc;
         }
@@ -1308,7 +1348,7 @@ public class UiElementNode implements IPropertySource {
         // Clone the current list of unknown attributes. We'll then remove from this list when
         // we still attributes which are still unknown. What will be left are the old unknown
         // attributes that have been deleted in the current XML attribute list.
-        @SuppressWarnings("unchecked") //$NON-NLS-1$
+        @SuppressWarnings("unchecked")
         HashSet<UiAttributeNode> deleted = (HashSet<UiAttributeNode>) mUnknownUiAttributes.clone();
 
         // We need to ignore hidden attributes.
index c816259..f1d6aa0 100644 (file)
@@ -16,6 +16,8 @@
 
 package com.android.ide.eclipse.adt.internal.refactorings.extractstring;
 
+import static com.android.ide.common.layout.LayoutConstants.STRING_PREFIX;
+
 import com.android.ide.eclipse.adt.AndroidConstants;
 import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
 import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
@@ -1556,7 +1558,7 @@ public class ExtractStringRefactoring extends Refactoring {
                 xmlChange = new TextFileChange(getName(), file);
                 xmlChange.setTextType("xml");   //$NON-NLS-1$
 
-                String quotedReplacement = quotedAttrValue("@string/" + xmlStringId); //$NON-NLS-1$
+                String quotedReplacement = quotedAttrValue(STRING_PREFIX + xmlStringId);
 
                 // Prepare the change set
                 for (IStructuredDocumentRegion regions : sdoc.getStructuredDocumentRegions()) {
index 6258268..9cb746d 100644 (file)
@@ -16,6 +16,7 @@
 
 package com.android.ide.eclipse.adt.internal.wizards.newproject;
 
+import com.android.ide.common.layout.LayoutConstants;
 import com.android.ide.eclipse.adt.AdtPlugin;
 import com.android.ide.eclipse.adt.AndroidConstants;
 import com.android.ide.eclipse.adt.internal.project.AndroidNature;
@@ -176,7 +177,7 @@ public class NewProjectWizard extends Wizard implements INewWizard {
 
     private static final String STRINGS_FILE = "strings.xml";       //$NON-NLS-1$
 
-    private static final String STRING_RSRC_PREFIX = "@string/";    //$NON-NLS-1$
+    private static final String STRING_RSRC_PREFIX = LayoutConstants.STRING_PREFIX;
     private static final String STRING_APP_NAME = "app_name";       //$NON-NLS-1$
     private static final String STRING_HELLO_WORLD = "hello";       //$NON-NLS-1$