public static final String TABLE_LAYOUT = "TableLayout"; //$NON-NLS-1$
public static final String TABLE_ROW = "TableRow"; //$NON-NLS-1$
public static final String LIST_VIEW = "ListView"; //$NON-NLS-1$
+ public static final String EDIT_TEXT = "EditText"; //$NON-NLS-1$
public static final String GALLERY = "Gallery"; //$NON-NLS-1$
public static final String GRID_VIEW = "GridView"; //$NON-NLS-1$
public static final String SCROLL_VIEW = "ScrollView"; //$NON-NLS-1$
public static final String GRAVITY_VALUE_FILL = "fill"; //$NON-NLS-1$
/** The default prefix used for the {@link #ANDROID_URI} name space */
- public static final String ANDROID_NS_PREFIX = "android"; //$NON-NLS-1$
+ public static final String ANDROID_NS_NAME = "android"; //$NON-NLS-1$
+ /** The default prefix used for the {@link #ANDROID_URI} name space including the colon */
+ public static final String ANDROID_NS_NAME_PREFIX = "android:"; //$NON-NLS-1$
/**
* Namespace for the Android resource XML, i.e.
import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_HEIGHT;
import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_WIDTH;
import static com.android.ide.common.layout.LayoutConstants.ATTR_TEXT;
+import static com.android.ide.common.layout.LayoutConstants.EDIT_TEXT;
import static com.android.ide.common.layout.LayoutConstants.EXPANDABLE_LIST_VIEW;
import static com.android.ide.common.layout.LayoutConstants.FQCN_ADAPTER_VIEW;
import static com.android.ide.common.layout.LayoutConstants.GALLERY;
* <p/>
* This does not override attributes which are not empty.
*/
- public static void setDefaultLayoutAttributes(UiElementNode ui_node, boolean updateLayout) {
+ public static void setDefaultLayoutAttributes(UiElementNode node, boolean updateLayout) {
// if this ui_node is a layout and we're adding it to a document, use match_parent for
// both W/H. Otherwise default to wrap_layout.
- boolean fill = ui_node.getDescriptor().hasChildren() &&
- ui_node.getUiParent() instanceof UiDocumentNode;
- ui_node.setAttributeValue(
+ ElementDescriptor descriptor = node.getDescriptor();
+ boolean fill = descriptor.hasChildren() &&
+ node.getUiParent() instanceof UiDocumentNode;
+ node.setAttributeValue(
ATTR_LAYOUT_WIDTH,
SdkConstants.NS_RESOURCES,
fill ? VALUE_FILL_PARENT : VALUE_WRAP_CONTENT,
false /* override */);
- ui_node.setAttributeValue(
+ node.setAttributeValue(
ATTR_LAYOUT_HEIGHT,
SdkConstants.NS_RESOURCES,
fill ? VALUE_FILL_PARENT : VALUE_WRAP_CONTENT,
false /* override */);
- String widget_id = getFreeWidgetId(ui_node);
- if (widget_id != null) {
- ui_node.setAttributeValue(
+ String freeId = getFreeWidgetId(node);
+ if (freeId != null) {
+ node.setAttributeValue(
ATTR_ID,
SdkConstants.NS_RESOURCES,
- widget_id,
+ freeId,
false /* override */);
}
- String widget_type = ui_node.getDescriptor().getUiName();
- ui_node.setAttributeValue(
+ // Don't set default text value into edit texts - they typically start out blank
+ if (!descriptor.getXmlLocalName().equals(EDIT_TEXT)) {
+ String type = descriptor.getUiName();
+ node.setAttributeValue(
ATTR_TEXT,
SdkConstants.NS_RESOURCES,
- widget_type,
+ type,
false /*override*/);
+ }
if (updateLayout) {
- UiElementNode ui_parent = ui_node.getUiParent();
- if (ui_parent != null &&
- ui_parent.getDescriptor().getXmlLocalName().equals(
+ UiElementNode parent = node.getUiParent();
+ if (parent != null &&
+ parent.getDescriptor().getXmlLocalName().equals(
RELATIVE_LAYOUT)) {
- UiElementNode ui_previous = ui_node.getUiPreviousSibling();
- if (ui_previous != null) {
- String id = ui_previous.getAttributeValue(ATTR_ID);
+ UiElementNode previous = node.getUiPreviousSibling();
+ if (previous != null) {
+ String id = previous.getAttributeValue(ATTR_ID);
if (id != null && id.length() > 0) {
id = id.replace("@+", "@"); //$NON-NLS-1$ //$NON-NLS-2$
- ui_node.setAttributeValue(
+ node.setAttributeValue(
ATTR_LAYOUT_BELOW,
SdkConstants.NS_RESOURCES,
id,
package com.android.ide.eclipse.adt.internal.editors.descriptors;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_NAME_PREFIX;
+
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
*/
public final String getNamespace() {
// For now we hard-code the prefix as being "android"
- if (mXmlName.startsWith("android:")) { //$NON-NLs-1$
+ if (mXmlName.startsWith(ANDROID_NS_NAME_PREFIX)) {
return SdkConstants.NS_RESOURCES;
}
package com.android.ide.eclipse.adt.internal.editors.layout.configuration;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_NAME_PREFIX;
+
import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.rendering.api.StyleResourceValue;
import com.android.ide.common.sdk.LoadStatus;
}
// check for framework identifier.
- if (parentStyle.startsWith("android:")) {
+ if (parentStyle.startsWith(ANDROID_NS_NAME_PREFIX)) {
frameworkStyle = true;
- parentStyle = parentStyle.substring("android:".length());
+ parentStyle = parentStyle.substring(ANDROID_NS_NAME_PREFIX.length());
}
// at this point we could have the format style/<name>. we want only the name
*
* @see ElementDescriptor
*/
-public final class ViewElementDescriptor extends ElementDescriptor {
+public class ViewElementDescriptor extends ElementDescriptor {
/** The full class name (FQCN) of this view. */
- private String mFullClassName;
+ private final String mFullClassName;
/** The list of layout attributes. Can be empty but not null. */
- private AttributeDescriptor[] mLayoutAttributes;
+ private final AttributeDescriptor[] mLayoutAttributes;
/** The super-class descriptor. Can be null. */
private ViewElementDescriptor mSuperClassDesc;
public ViewElementDescriptor(String xml_name, String fullClassName) {
super(xml_name);
mFullClassName = fullClassName;
+ mLayoutAttributes = null;
}
/**
// A root node requires the Android XMLNS
uiNew.setAttributeValue(
- LayoutConstants.ANDROID_NS_PREFIX,
+ LayoutConstants.ANDROID_NS_NAME,
XmlnsAttributeDescriptor.XMLNS_URI,
SdkConstants.NS_RESOURCES,
true /*override*/);
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_ORIENTATION;
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 com.android.ide.common.layout.LayoutConstants.LINEAR_LAYOUT;
+import static com.android.ide.common.layout.LayoutConstants.VALUE_VERTICAL;
import static org.eclipse.jface.viewers.StyledString.QUALIFIER_STYLER;
import com.android.annotations.VisibleForTesting;
UiElementNode node = (UiElementNode) element;
ElementDescriptor desc = node.getDescriptor();
if (desc != null) {
- Image img = desc.getIcon();
+ Image img = null;
+ // Special case for the common case of vertical linear layouts:
+ // show vertical linear icon (the default icon shows horizontal orientation)
+ if (desc.getUiName().equals(LINEAR_LAYOUT)) {
+ Element e = (Element) node.getXmlNode();
+ if (VALUE_VERTICAL.equals(e.getAttributeNS(ANDROID_URI,
+ ATTR_ORIENTATION))) {
+ IconFactory factory = IconFactory.getInstance();
+ img = factory.getIcon("VerticalLinearLayout"); //$NON-NLS-1$
+ }
+ }
+ if (img == null) {
+ img = desc.getIcon();
+ }
if (img != null) {
if (node.hasError()) {
return new ErrorImageComposite(img).createImage();
import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeFactory;
import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy;
+import com.android.ide.eclipse.adt.internal.editors.layout.gre.PaletteMetadataDescriptor;
import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepository;
import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
import com.android.ide.eclipse.adt.internal.editors.ui.DecorComposite;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.sdklib.IAndroidTarget;
-import com.android.sdklib.SdkConstants;
import com.android.util.Pair;
import org.eclipse.jface.action.Action;
categoryToItems.put(category, categoryItems);
}
- if (expandedCategories == null && headers.size() > 0) {
+ // Set the categories to expand the first item if
+ // (1) we don't have a previously selected category, or
+ // (2) there's just one category anyway, or
+ // (3) the set of categories have changed so our previously selected category
+ // doesn't exist anymore (can happen when you toggle "Show Categories")
+ if ((expandedCategories == null && headers.size() > 0) || headers.size() == 1 ||
+ (expandedCategories != null && expandedCategories.size() >= 1
+ && !headers.contains(expandedCategories.iterator().next()))) {
// Expand the first category if we don't have a previous selection (e.g. refresh)
expandedCategories = Collections.singleton(headers.get(0));
}
null /* parentFqcn */,
bounds /* bounds */,
null /* parentBounds */);
+ if (mDesc instanceof PaletteMetadataDescriptor) {
+ PaletteMetadataDescriptor pm = (PaletteMetadataDescriptor) mDesc;
+ pm.initializeNew(se);
+ }
mElements = new SimpleElement[] { se };
// Register this as the current dragged data
attr.setValue(ANDROID_URI);
element.getAttributes().setNamedItemNS(attr);
- element.setAttributeNS(SdkConstants.NS_RESOURCES,
+ element.setAttributeNS(ANDROID_URI,
ATTR_LAYOUT_WIDTH, VALUE_WRAP_CONTENT);
- element.setAttributeNS(SdkConstants.NS_RESOURCES,
+ element.setAttributeNS(ANDROID_URI,
ATTR_LAYOUT_HEIGHT, VALUE_WRAP_CONTENT);
// This doesn't apply to all, but doesn't seem to cause harm and makes for a
// better experience with text-oriented views like buttons and texts
- element.setAttributeNS(SdkConstants.NS_RESOURCES, ATTR_TEXT, mDesc.getUiName());
+ element.setAttributeNS(ANDROID_URI, ATTR_TEXT, mDesc.getUiName());
+
+ // Is this a palette variation?
+ if (mDesc instanceof PaletteMetadataDescriptor) {
+ PaletteMetadataDescriptor pm = (PaletteMetadataDescriptor) mDesc;
+ pm.initializeNew(element);
+ }
document.appendChild(element);
import com.android.ide.eclipse.adt.internal.editors.descriptors.DocumentDescriptor;
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.gre.PaletteMetadataDescriptor;
import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepository;
import com.android.ide.eclipse.adt.internal.editors.layout.gre.ViewMetadataRepository.RenderMode;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode;
import com.android.util.Pair;
import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.graphics.RGB;
import org.w3c.dom.Attr;
// Important to get these sizes large enough for clients that don't support
// RenderMode.FULL_EXPAND such as 1.6
int width = 200;
- int height = 2000;
+ int height = documentElement.getChildNodes().getLength() == 1 ? 400 : 1600;
Set<UiElementNode> expandNodes = Collections.<UiElementNode>emptySet();
RenderingMode renderingMode = RenderingMode.FULL_EXPAND;
}
}
}
+ } else {
+ if (session.getResult().getException() != null) {
+ AdtPlugin.log(session.getResult().getException(),
+ session.getResult().getErrorMessage());
+ } else if (session.getResult().getErrorMessage() != null) {
+ AdtPlugin.log(IStatus.WARNING, session.getResult().getErrorMessage());
+ }
}
session.dispose();
}
private String getFileName(ElementDescriptor descriptor) {
+ if (descriptor instanceof PaletteMetadataDescriptor) {
+ PaletteMetadataDescriptor pmd = (PaletteMetadataDescriptor) descriptor;
+ StringBuilder sb = new StringBuilder();
+ String name = pmd.getUiName();
+ // Strip out whitespace, parentheses, etc.
+ for (int i = 0, n = name.length(); i < n; i++) {
+ char c = name.charAt(i);
+ if (Character.isLetter(c)) {
+ sb.append(c);
+ }
+ }
+ return sb.toString() + DOT_PNG;
+ }
return descriptor.getUiName() + DOT_PNG;
}
if (themeName.startsWith(themeNamePrefix)) {
themeName = themeName.substring(themeNamePrefix.length());
}
- String dirName = String.format("palette-preview-r10-%s-%s-%s", cleanup(targetName),
+ String dirName = String.format("palette-preview-r11-%s-%s-%s", cleanup(targetName),
cleanup(themeName), cleanup(mPalette.getCurrentDevice()));
IPath dirPath = pluginState.append(dirName);
--- /dev/null
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ide.eclipse.adt.internal.editors.layout.gre;
+
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_NAME_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI;
+
+import com.android.ide.eclipse.adt.internal.editors.IconFactory;
+import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.SimpleAttribute;
+import com.android.ide.eclipse.adt.internal.editors.layout.gle2.SimpleElement;
+
+import org.eclipse.swt.graphics.Image;
+import org.w3c.dom.Element;
+
+/**
+ * Special version of {@link ViewElementDescriptor} which is initialized by the palette
+ * with specific metadata for how to instantiate particular variations of an existing
+ * {@link ViewElementDescriptor} with initial values.
+ */
+public class PaletteMetadataDescriptor extends ViewElementDescriptor {
+ private String mInitString;
+ private String mIconName;
+
+ public PaletteMetadataDescriptor(ViewElementDescriptor descriptor, String displayName,
+ String initString, String iconName) {
+ super(descriptor.getXmlName(),
+ displayName,
+ descriptor.getFullClassName(),
+ descriptor.getTooltip(),
+ descriptor.getSdkUrl(),
+ descriptor.getAttributes(),
+ descriptor.getLayoutAttributes(),
+ descriptor.getChildren(), descriptor.getMandatory() == Mandatory.MANDATORY);
+ mInitString = initString;
+ mIconName = iconName;
+ }
+
+ /**
+ * Returns a String which contains a comma separated list of name=value tokens,
+ * where the name can start with "android:" to indicate a property in the android namespace,
+ * or no prefix for plain attributes.
+ *
+ * @return the initialization string, which can be empty but never null
+ */
+ public String getInitializedAttributes() {
+ return mInitString != null ? mInitString : ""; //$NON-NLS-1$
+ }
+
+ @Override
+ public Image getIcon() {
+ if (mIconName != null) {
+ IconFactory factory = IconFactory.getInstance();
+ Image icon = factory.getIcon(mIconName);
+ if (icon != null) {
+ return icon;
+ }
+ }
+
+ return super.getIcon();
+ }
+
+ /**
+ * Initializes a new {@link SimpleElement} with the palette initialization
+ * configuration
+ *
+ * @param element the new element to initialize
+ */
+ public void initializeNew(SimpleElement element) {
+ initializeNew(element, null);
+ }
+
+ /**
+ * Initializes a new {@link Element} with the palette initialization configuration
+ *
+ * @param element the new element to initialize
+ */
+ public void initializeNew(Element element) {
+ initializeNew(null, element);
+ }
+
+ private void initializeNew(SimpleElement simpleElement, Element domElement) {
+ String initializedAttributes = mInitString;
+ if (initializedAttributes != null && initializedAttributes.length() > 0) {
+ for (String s : initializedAttributes.split(",")) { //$NON-NLS-1$
+ String[] nameValue = s.split("="); //$NON-NLS-1$
+ String name = nameValue[0];
+ String value = nameValue[1];
+ String nameSpace = ""; //$NON-NLS-1$
+ if (name.startsWith(ANDROID_NS_NAME_PREFIX)) {
+ name = name.substring(ANDROID_NS_NAME_PREFIX.length());
+ nameSpace = ANDROID_URI;
+ }
+
+ if (simpleElement != null) {
+ SimpleAttribute attr = new SimpleAttribute(nameSpace, name, value);
+ simpleElement.addAttribute(attr);
+ }
+
+ if (domElement != null) {
+ domElement.setAttributeNS(nameSpace, name, value);
+ }
+ }
+ }
+ }
+}
package com.android.ide.eclipse.adt.internal.editors.layout.gre;
-import static com.android.ide.common.api.IViewMetadata.FillPreference.NONE;
import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI;
import static com.android.ide.common.layout.LayoutConstants.ATTR_ID;
import static com.android.ide.common.layout.LayoutConstants.ID_PREFIX;
import com.android.annotations.VisibleForTesting;
import com.android.ide.common.api.IViewMetadata.FillPreference;
import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.IconFactory;
import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors;
import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.Comparator;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.TreeMap;
+import java.util.Set;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
// Therefore, we instead use the convention that the id is the fully qualified
// class name, with .'s replaced with _'s.
-
// Special case: for tab host we aren't allowed to mess with the id
-
String id = element.getAttributeNS(ANDROID_URI, ATTR_ID);
if ("@android:id/tabhost".equals(id)) {
Node childNode = children.item(j);
if (childNode.getNodeType() == Node.ELEMENT_NODE) {
Element child = (Element) childNode;
- String fqcn = child.getAttribute("class"); //$NON-NLS-1$
- String fill = child.getAttribute("fill"); //$NON-NLS-1$
- FillPreference fillPreference = null;
- if (fill.length() > 0) {
- fillPreference = fillTypes.get(fill);
- }
- if (fillPreference == null) {
- fillPreference = NONE;
- }
- String skip = child.getAttribute("skip"); //$NON-NLS-1$
- RenderMode mode = RenderMode.NORMAL;
- String render = child.getAttribute("render"); //$NON-NLS-1$
- if (render.length() > 0) {
- mode = RenderMode.get(render);
- }
- String relatedTo = child.getAttribute("relatedTo"); //$NON-NLS-1$
- ViewData view = new ViewData(category, fqcn, fillPreference,
- skip.length() == 0 ? false : Boolean.valueOf(skip),
- mode, relatedTo);
+ ViewData view = createViewData(fillTypes, child,
+ null, FillPreference.NONE, RenderMode.NORMAL);
category.addView(view);
}
}
-
mCategories.add(category);
}
}
return mCategories;
}
+ private ViewData createViewData(Map<String, FillPreference> fillTypes,
+ Element child, String defaultFqcn, FillPreference defaultFill,
+ RenderMode defaultRender) {
+ String fqcn = child.getAttribute("class"); //$NON-NLS-1$
+ if (fqcn.length() == 0) {
+ fqcn = defaultFqcn;
+ }
+ String fill = child.getAttribute("fill"); //$NON-NLS-1$
+ FillPreference fillPreference = null;
+ if (fill.length() > 0) {
+ fillPreference = fillTypes.get(fill);
+ }
+ if (fillPreference == null) {
+ fillPreference = defaultFill;
+ }
+ String skip = child.getAttribute("skip"); //$NON-NLS-1$
+ RenderMode renderMode = defaultRender;
+ String render = child.getAttribute("render"); //$NON-NLS-1$
+ if (render.length() > 0) {
+ renderMode = RenderMode.get(render);
+ }
+ String displayName = child.getAttribute("name"); //$NON-NLS-1$
+ if (displayName.length() == 0) {
+ displayName = null;
+ }
+ String relatedTo = child.getAttribute("relatedTo"); //$NON-NLS-1$
+ ViewData view = new ViewData(fqcn, displayName, fillPreference,
+ skip.length() == 0 ? false : Boolean.valueOf(skip),
+ renderMode, relatedTo);
+
+ String init = child.getAttribute("init"); //$NON-NLS-1$
+ String icon = child.getAttribute("icon"); //$NON-NLS-1$
+
+ view.setInitString(init);
+ if (icon.length() > 0) {
+ view.setIconName(icon);
+ }
+
+ // Nested variations?
+ if (child.hasChildNodes()) {
+ // Palette variations
+ NodeList childNodes = child.getChildNodes();
+ for (int k = 0, kl = childNodes.getLength(); k < kl; k++) {
+ Node variationNode = childNodes.item(k);
+ if (variationNode.getNodeType() == Node.ELEMENT_NODE) {
+ Element variation = (Element) variationNode;
+ ViewData variationView = createViewData(fillTypes, variation,
+ fqcn, fillPreference, renderMode);
+ view.addVariation(variationView);
+ }
+ }
+ }
+
+ return view;
+ }
+
/**
* Computes the palette entries for the given {@link AndroidTargetData}, looking up the
* available node descriptors, categorizing and sorting them.
List<Pair<String, List<ViewElementDescriptor>>> result =
new ArrayList<Pair<String, List<ViewElementDescriptor>>>();
- final Map<String, ViewData> viewMap = getClassToView();
- Map<CategoryData, List<ViewElementDescriptor>> categories =
- new TreeMap<CategoryData, List<ViewElementDescriptor>>();
-
- // Locate the "Other" category
- CategoryData other = null;
- for (CategoryData category : getCategories()) {
- if (category.getViewCount() == 0) {
- other = category;
- break;
- }
- }
-
List<List<ViewElementDescriptor>> lists = new ArrayList<List<ViewElementDescriptor>>(2);
LayoutDescriptors layoutDescriptors = targetData.getLayoutDescriptors();
lists.add(layoutDescriptors.getViewDescriptors());
lists.add(layoutDescriptors.getLayoutDescriptors());
+ // First record map of FQCN to ViewElementDescriptor such that we can quickly
+ // determine if a particular palette entry is available
+ Map<String, ViewElementDescriptor> fqcnToDescriptor =
+ new HashMap<String, ViewElementDescriptor>();
for (List<ViewElementDescriptor> list : lists) {
for (ViewElementDescriptor view : list) {
- ViewData viewData = getClassToView().get(view.getFullClassName());
- CategoryData category = other;
- if (viewData != null) {
- if (viewData.getSkip()) {
- continue;
- }
- category = viewData.getCategory();
+ String fqcn = view.getFullClassName();
+ if (fqcn == null) {
+ // <view> and <merge> tags etc
+ fqcn = view.getUiName();
}
+ fqcnToDescriptor.put(fqcn, view);
+ }
+ }
- List<ViewElementDescriptor> viewList = categories.get(category);
- if (viewList == null) {
- viewList = new ArrayList<ViewElementDescriptor>();
- categories.put(category, viewList);
- }
- viewList.add(view);
+ Set<ViewElementDescriptor> remaining = new HashSet<ViewElementDescriptor>(
+ layoutDescriptors.getViewDescriptors().size()
+ + layoutDescriptors.getLayoutDescriptors().size());
+ remaining.addAll(layoutDescriptors.getViewDescriptors());
+ remaining.addAll(layoutDescriptors.getLayoutDescriptors());
+ // Now iterate in palette metadata order over the items in the palette and include
+ // any that also appear as a descriptor
+ List<ViewElementDescriptor> categoryItems = new ArrayList<ViewElementDescriptor>();
+ for (CategoryData category : getCategories()) {
+ if (createCategories) {
+ categoryItems = new ArrayList<ViewElementDescriptor>();
+ }
+ for (ViewData view : category) {
+ String fqcn = view.getFcqn();
+ ViewElementDescriptor descriptor = fqcnToDescriptor.get(fqcn);
+ if (descriptor != null) {
+ if (view.getDisplayName() != null || view.getInitString().length() > 0) {
+ categoryItems.add(new PaletteMetadataDescriptor(descriptor,
+ view.getDisplayName(), view.getInitString(), view.getIconName()));
+ } else {
+ categoryItems.add(descriptor);
+ }
+ remaining.remove(descriptor);
+
+ if (view.hasVariations()) {
+ for (ViewData variation : view.getVariations()) {
+ String init = variation.getInitString();
+ String icon = variation.getIconName();
+ ViewElementDescriptor desc = new PaletteMetadataDescriptor(descriptor,
+ variation.getDisplayName(), init, icon);
+ categoryItems.add(desc);
+ }
+ }
+ }
}
- }
- if (!createCategories) {
- // Squash all categories into a single one, "Views"
- Map<CategoryData, List<ViewElementDescriptor>> singleCategory =
- new HashMap<CategoryData, List<ViewElementDescriptor>>();
- List<ViewElementDescriptor> items = new ArrayList<ViewElementDescriptor>(100);
- for (Map.Entry<CategoryData, List<ViewElementDescriptor>> entry : categories.entrySet()) {
- items.addAll(entry.getValue());
+ if (createCategories && categoryItems.size() > 0) {
+ if (alphabetical) {
+ Collections.sort(categoryItems);
+ }
+ result.add(Pair.of(category.getName(), categoryItems));
}
- singleCategory.put(new CategoryData("Views"), items);
- categories = singleCategory;
}
- for (Map.Entry<CategoryData, List<ViewElementDescriptor>> entry : categories.entrySet()) {
- String name = entry.getKey().getName();
- List<ViewElementDescriptor> items = entry.getValue();
- if (items == null) {
- continue; // empty category
+ if (remaining.size() > 0) {
+ List<ViewElementDescriptor> otherItems = new ArrayList<ViewElementDescriptor>(remaining);
+ // Always sorted, we don't have a natural order for these unknowns
+ Collections.sort(otherItems);
+ if (createCategories) {
+ result.add(Pair.of("Other", otherItems));
+ } else {
+ categoryItems.addAll(otherItems);
}
+ }
- // Natural sort of the descriptors
+ if (!createCategories) {
if (alphabetical) {
- Collections.sort(items);
- } else {
- Collections.sort(items, new Comparator<ViewElementDescriptor>() {
- public int compare(ViewElementDescriptor v1, ViewElementDescriptor v2) {
- String fqcn1 = v1.getFullClassName();
- String fqcn2 = v2.getFullClassName();
- if (fqcn1 == null) {
- // <view> and <merge> tags etc
- fqcn1 = v1.getUiName();
- }
- if (fqcn2 == null) {
- fqcn2 = v2.getUiName();
- }
- ViewData d1 = viewMap.get(fqcn1);
- ViewData d2 = viewMap.get(fqcn2);
-
- // Use natural sorting order of the view data
- // Sort unknown views to the end (and alphabetically among themselves)
- if (d1 != null) {
- if (d2 != null) {
- return d1.getOrdinal() - d2.getOrdinal();
- } else {
- return 1;
- }
- } else {
- if (d2 == null) {
- return v1.getUiName().compareTo(v2.getUiName());
- } else {
- return -1;
- }
- }
- }
- });
+ Collections.sort(categoryItems);
}
-
-
- result.add(Pair.of(name, items));
+ result.add(Pair.of("Views", categoryItems));
}
return result;
return mName;
}
- public int getViewCount() {
- return mViews.size();
- }
-
// Implements Iterable<ViewData> such that we can use for-each on the category to
// enumerate its views
public Iterator<ViewData> iterator() {
private final String mFqcn;
/** Fill preference of the view */
private final FillPreference mFillPreference;
- /** The category that the view belongs to */
- private final CategoryData mCategory;
/** Skip this item in the palette? */
private final boolean mSkip;
/** Must this item be rendered alone? skipped? etc */
private final String mRelatedTo;
/** The relative rank of the view for natural ordering */
private final int mOrdinal = sNextOrdinal++;
+ /** List of optional variations */
+ private List<ViewData> mVariations;
+ /** Display name. Can be null. */
+ private String mDisplayName;
+ /**
+ * Optional initialization string - a comma separate set of name/value pairs to
+ * initialize the element with
+ */
+ private String mInitString;
+ /** The name of an icon (known to the {@link IconFactory} to show for this view */
+ private String mIconName;
/** Constructs a new view data for the given class */
- private ViewData(CategoryData category, String fqcn,
+ private ViewData(String fqcn, String displayName,
FillPreference fillPreference, boolean skip, RenderMode renderMode,
String relatedTo) {
super();
- mCategory = category;
mFqcn = fqcn;
+ mDisplayName = displayName;
mFillPreference = fillPreference;
mSkip = skip;
mRenderMode = renderMode;
mRelatedTo = relatedTo;
}
- /** Returns the category for views of this type */
- private CategoryData getCategory() {
- return mCategory;
- }
-
/** Returns the {@link FillPreference} for views of this type */
private FillPreference getFillPreference() {
return mFillPreference;
return mFqcn;
}
- /** Relative rank of this view type */
- private int getOrdinal() {
- return mOrdinal;
+ private String getDisplayName() {
+ return mDisplayName;
}
// Implements Comparable<ViewData> such that views can be sorted naturally
return result;
}
}
+
+ void addVariation(ViewData variation) {
+ if (mVariations == null) {
+ mVariations = new ArrayList<ViewData>(4);
+ }
+ mVariations.add(variation);
+ }
+
+ List<ViewData> getVariations() {
+ return mVariations;
+ }
+
+ boolean hasVariations() {
+ return mVariations != null && mVariations.size() > 0;
+ }
+
+ private void setInitString(String initString) {
+ this.mInitString = initString;
+ }
+
+ private String getInitString() {
+ return mInitString;
+ }
+
+ private void setIconName(String iconName) {
+ this.mIconName = iconName;
+ }
+
+ private String getIconName() {
+ return mIconName;
+ }
}
/**
return view.getRenderMode();
}
- return RenderMode.ALONE;
+ return RenderMode.NORMAL;
}
/**
<!ATTLIST category name CDATA #IMPLIED>
<!--- Each view is identified by its full class name and has various
other attributes such as a fill preference -->
-<!ELEMENT view EMPTY>
+<!ELEMENT view (view)*>
<!ATTLIST view
- class CDATA #REQUIRED
+ class CDATA #IMPLIED
+ name CDATA #IMPLIED
+ init CDATA #IMPLIED
+ icon CDATA #IMPLIED
relatedTo CDATA #IMPLIED
skip (true|false) "false"
render (alone|skip|normal) "normal"
class="android.widget.Button"
relatedTo="ImageButton" />
<view
- class="android.widget.CheckBox"
- relatedTo="RadioButton,ToggleButton,CheckedTextView" />
- <view
class="android.widget.ToggleButton"
relatedTo="CheckBox" />
<view
+ class="android.widget.CheckBox"
+ relatedTo="RadioButton,ToggleButton,CheckedTextView" />
+ <view
class="android.widget.RadioButton"
relatedTo="CheckBox,ToggleButton" />
<view
relatedTo="Spinner,TextView,AutoCompleteTextView,MultiAutoCompleteTextView"
fill="width_in_vertical" />
<view
- class="android.widget.AutoCompleteTextView"
- relatedTo="EditText,MultiAutoCompleteTextView"
- fill="width_in_vertical" />
+ class="android.widget.ProgressBar"
+ relatedTo="SeekBar"
+ name="ProgressBar (Large)"
+ init="style=?android:attr/progressBarStyleLarge">
+ <view
+ name="ProgressBar (Normal)"
+ init="" />
+ <view
+ name="ProgressBar (Small)"
+ init="style=?android:attr/progressBarStyleSmall" />
+ <view
+ name="ProgressBar (Horizontal)"
+ init="style=?android:attr/progressBarStyleHorizontal" />
+ </view>
<view
- class="android.widget.MultiAutoCompleteTextView"
- relatedTo="EditText,AutoCompleteTextView"
+ class="android.widget.SeekBar"
+ relatedTo="ProgressBar"
fill="width_in_vertical" />
<view
- class="android.widget.ProgressBar"
- relatedTo="SeekBar" />
- <view
class="android.widget.QuickContactBadge" />
<view
class="android.widget.RadioGroup" />
<view
class="android.widget.RatingBar" />
+ </category>
+ <category
+ name="Text Fields">
<view
- class="android.widget.SeekBar"
- relatedTo="ProgressBar"
+ class="android.widget.EditText"
+ name="Plain Text"
+ init=""
+ relatedTo="Spinner,TextView,AutoCompleteTextView,MultiAutoCompleteTextView"
+ fill="width_in_vertical">
+ <view
+ name="Person Name"
+ init="android:inputType=textPersonName" />
+ <view
+ name="Password"
+ init="android:inputType=textPassword" />
+ <view
+ name="Password (Numeric)"
+ init="android:inputType=numberPassword" />
+ <view
+ name="E-mail"
+ init="android:inputType=textEmailAddress" />
+ <view
+ name="Phone"
+ init="android:inputType=phone" />
+ <view
+ name="Postal Address"
+ init="android:inputType=textPostalAddress" />
+ <view
+ name="Multiline Text"
+ init="android:inputType=textMultiLine" />
+ <view
+ name="Time"
+ init="android:inputType=time" />
+ <view
+ name="Date"
+ init="android:inputType=date" />
+ <view
+ name="Number"
+ init="android:inputType=number" />
+ <view
+ name="Number (Signed)"
+ init="android:inputType=numberSigned" />
+ <view
+ name="Number (Decimal)"
+ init="android:inputType=numberDecimal" />
+ </view>
+ <view
+ class="android.widget.AutoCompleteTextView"
+ fill="width_in_vertical" />
+ <view
+ class="android.widget.MultiAutoCompleteTextView"
fill="width_in_vertical" />
</category>
<category
name="Layouts">
<view
class="android.widget.LinearLayout"
+ name="LinearLayout (Vertical)"
+ init="android:orientation=vertical"
+ icon="VerticalLinearLayout"
fill="opposite"
- render="skip" />
+ render="skip">
+ <view
+ name="LinearLayout (Horizontal)" />
+ </view>
<view
class="android.widget.RelativeLayout"
fill="opposite"
<!-- This is the catch-all category which contains unknown views if we encounter any -->
</category>
<!-- TODO: Add-ons? -->
-</metadata>
\ No newline at end of file
+</metadata>
<?xml version="1.0" encoding="utf-8"?>
<!--
- Default configuration for various views to be rendered
+ Default configuration for various views to be rendered
TODO: Remove views that don't have custom configuration
TODO: Parameterize the custom width (200dip) in the below?
-->
android:text="EditText"
android:layout_width="200dip">
</EditText>
+
+ <EditText
+ android:id="@+id/PlainText"
+ android:text="abc"
+ android:layout_width="200dip"
+ android:layout_height="wrap_content">
+ </EditText>
+
+ <EditText
+ android:id="@+id/Password"
+ android:inputType="textPassword"
+ android:text="••••••••"
+ android:layout_width="200dip"
+ android:layout_height="wrap_content">
+ </EditText>
+
+ <!-- android:inputType="numberPassword" not used here to allow digits in preview only -->
+ <EditText
+ android:id="@+id/PasswordNumeric"
+ android:text="1•••2•••3"
+ android:layout_width="200dip"
+ android:layout_height="wrap_content">
+ </EditText>
+
+ <EditText
+ android:id="@+id/PersonName"
+ android:inputType="textPersonName"
+ android:text="Firstname Lastname"
+ android:layout_width="200dip"
+ android:layout_height="wrap_content">
+ </EditText>
+
+ <EditText
+ android:id="@+id/Phone"
+ android:inputType="phone"
+ android:text="(555) 0100"
+ android:layout_width="200dip"
+ android:layout_height="wrap_content">
+ </EditText>
+
+ <EditText
+ android:id="@+id/PostalAddress"
+ android:inputType="textPostalAddress"
+ android:text="Address"
+ android:layout_width="200dip"
+ android:layout_height="100dip">
+ </EditText>
+
+ <EditText
+ android:id="@+id/MultilineText"
+ android:inputType="textMultiLine"
+ android:text="Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor"
+ android:layout_width="200dip"
+ android:layout_height="100dip">
+ </EditText>
+
+ <EditText
+ android:id="@+id/Date"
+ android:inputType="date"
+ android:text="1/1/2011"
+ android:layout_width="200dip"
+ android:layout_height="wrap_content">
+ </EditText>
+
+ <EditText
+ android:id="@+id/Time"
+ android:inputType="time"
+ android:text="12:00am"
+ android:layout_width="200dip"
+ android:layout_height="wrap_content">
+ </EditText>
+
+ <EditText
+ android:id="@+id/Email"
+ android:inputType="textEmailAddress"
+ android:text="user@domain"
+ android:layout_width="200dip"
+ android:layout_height="wrap_content">
+ </EditText>
+
+ <EditText
+ android:id="@+id/Number"
+ android:inputType="number"
+ android:text="42"
+ android:layout_width="200dip"
+ android:layout_height="wrap_content">
+ </EditText>
+
+ <EditText
+ android:id="@+id/NumberSigned"
+ android:inputType="numberSigned"
+ android:text="-42"
+ android:layout_width="200dip"
+ android:layout_height="wrap_content">
+ </EditText>
+
+ <EditText
+ android:id="@+id/NumberDecimal"
+ android:inputType="numberDecimal"
+ android:text="42.0"
+ android:layout_width="200dip"
+ android:layout_height="wrap_content">
+ </EditText>
+
<ImageButton
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:id="@+id/android_widget_MultiAutoCompleteTextView">
</MultiAutoCompleteTextView>
<ProgressBar
- android:id="@+id/android_widget_ProgressBar"
+ android:id="@+id/android_widget_ProgressBarNormal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</ProgressBar>
+ <ProgressBar
+ android:id="@+id/android_widget_ProgressBarHorizontal"
+ android:layout_width="200dip"
+ android:layout_height="wrap_content"
+ android:progress="30"
+ style="?android:attr/progressBarStyleHorizontal">
+ </ProgressBar>
+ <ProgressBar
+ android:id="@+id/android_widget_ProgressBarLarge"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="?android:attr/progressBarStyleLarge">
+ </ProgressBar>
+ <ProgressBar
+ android:id="@+id/android_widget_ProgressBarSmall"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="?android:attr/progressBarStyleSmall">
+ </ProgressBar>
<QuickContactBadge
android:layout_height="wrap_content"
android:layout_width="wrap_content"
*/
package com.android.ide.eclipse.adt.internal.editors.layout.refactoring;
-import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_NAME;
import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI;
import static com.android.ide.common.layout.LayoutConstants.ATTR_ID;
import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_HEIGHT;
namespaceDeclarations = sb.toString();
if (androidNsPrefix == null) {
- androidNsPrefix = ANDROID_NS_PREFIX;
+ androidNsPrefix = ANDROID_NS_NAME;
}
if (namespaceDeclarations.length() == 0) {
*/
package com.android.ide.eclipse.adt.internal.editors.layout.refactoring;
-import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_NAME;
import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI;
import static com.android.ide.common.layout.LayoutConstants.ANDROID_WIDGET_PREFIX;
import static com.android.ide.common.layout.LayoutConstants.ATTR_ID;
}
if (mAndroidNamespacePrefix == null) {
- mAndroidNamespacePrefix = ANDROID_NS_PREFIX;
+ mAndroidNamespacePrefix = ANDROID_NS_NAME;
}
}
layout_attrs.length);
if (need_xmlns) {
AttributeDescriptor desc = new XmlnsAttributeDescriptor(
- LayoutConstants.ANDROID_NS_PREFIX,
+ LayoutConstants.ANDROID_NS_NAME,
SdkConstants.NS_RESOURCES);
mCachedAttributeDescriptors[direct_attrs.length + layout_attrs.length] = desc;
}
package com.android.ide.eclipse.adt.internal.editors.menu.descriptors;
-import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_NAME;
import com.android.ide.common.resources.platform.DeclareStyleableInfo;
import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
new ElementDescriptor[] { top_item }, // childrenElements,
false /* mandatory */);
- XmlnsAttributeDescriptor xmlns = new XmlnsAttributeDescriptor(ANDROID_NS_PREFIX,
+ XmlnsAttributeDescriptor xmlns = new XmlnsAttributeDescriptor(ANDROID_NS_NAME,
SdkConstants.NS_RESOURCES);
updateElement(mDescriptor, styleMap, "Menu", xmlns); //$NON-NLS-1$
package com.android.ide.eclipse.adt.internal.editors.uimodel;
-import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_NAME;
import static com.android.ide.common.layout.LayoutConstants.ID_PREFIX;
import static com.android.ide.common.layout.LayoutConstants.NEW_ID_PREFIX;
import static com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor.XMLNS;
// We need to make sure the prefix is not one that was declared in the scope
// visited above. Use a default namespace prefix "android" for the Android resource
// NS and use "ns" for all other custom namespaces.
- String prefix = NS_RESOURCES.equals(nsUri) ? ANDROID_NS_PREFIX : "ns"; //$NON-NLS-1$
+ String prefix = NS_RESOURCES.equals(nsUri) ? ANDROID_NS_NAME : "ns"; //$NON-NLS-1$
String base = prefix;
for (int i = 1; visited.contains(prefix); i++) {
prefix = base + Integer.toString(i);
// FrameworkResourceManager expects a specific prefix for the attribute.
String nsPrefix = "";
if (SdkConstants.NS_RESOURCES.equals(descriptor.getNamespaceUri())) {
- nsPrefix = LayoutConstants.ANDROID_NS_PREFIX + ':';
+ nsPrefix = LayoutConstants.ANDROID_NS_NAME + ':';
} else if (XmlnsAttributeDescriptor.XMLNS_URI.equals(descriptor.getNamespaceUri())) {
nsPrefix = XmlnsAttributeDescriptor.XMLNS_COLON;
}
package com.android.ide.eclipse.adt.internal.editors.uimodel;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_NAME_PREFIX;
+
import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
UiElementNode uiNode = getUiParent();
AndroidXmlEditor editor = uiNode.getEditor();
- if (prefix == null || prefix.indexOf("android:") < 0) { //$NON-NLS-1$
+ if (prefix == null || !prefix.contains(ANDROID_NS_NAME_PREFIX)) {
IProject project = editor.getProject();
if (project != null) {
// get the resource repository for this project and the system resources.
}
if (isSystem) {
- sb.append("android:"); //$NON-NLS-1$
+ sb.append(ANDROID_NS_NAME_PREFIX);
}
sb.append(typeName).append('/');
package com.android.ide.eclipse.adt.internal.editors.xml.descriptors;
-import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_PREFIX;
+import static com.android.ide.common.layout.LayoutConstants.ANDROID_NS_NAME;
import com.android.ide.common.resources.platform.AttributeInfo;
import com.android.ide.common.resources.platform.DeclareStyleableInfo;
ViewClassInfo[] prefs, ViewClassInfo[] prefGroups) {
XmlnsAttributeDescriptor xmlns = new XmlnsAttributeDescriptor(
- ANDROID_NS_PREFIX,
+ ANDROID_NS_NAME,
SdkConstants.NS_RESOURCES);
ElementDescriptor searchable = createSearchable(searchableStyleMap, xmlns);