diff options
10 files changed, 553 insertions, 63 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/ExplodedRenderingHelper.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/ExplodedRenderingHelper.java new file mode 100644 index 0000000..8d244dd --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/ExplodedRenderingHelper.java @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2009 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; + +import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; +import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors; +import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; +import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData; +import com.android.ide.eclipse.adt.internal.sdk.Sdk; +import com.android.sdklib.IAndroidTarget; + +import org.eclipse.core.resources.IProject; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class computes the new screen size in "exploded rendering" mode. + * It goes through the whole layout tree and figures out how many embedded layouts will have + * extra padding and compute how that will affect the screen size. + * + * TODO + * - find a better class name :) + * - move the logic for each layout to groovy scripts? + * - support custom classes (by querying JDT for its super class and reverting to its behavior) + */ +final class ExplodedRenderingHelper { + /** value of the padding in pixel. + * TODO: make a preference? + */ + public final static int PADDING_VALUE = 10; + + private final int[] mPadding = new int[] { 0, 0 }; + private List<ElementDescriptor> mLayoutDescriptors; + + ExplodedRenderingHelper(UiElementNode top, IProject iProject) { + // get the layout descriptor + IAndroidTarget target = Sdk.getCurrent().getTarget(iProject); + AndroidTargetData data = Sdk.getCurrent().getTargetData(target); + LayoutDescriptors descriptors = data.getLayoutDescriptors(); + mLayoutDescriptors = descriptors.getLayoutDescriptors(); + + computePadding(top, mPadding); + } + + /** + * Returns the number of extra padding in the X axis. This doesn't return a number of pixel + * or dip, but how many paddings are pushing the screen dimension out. + */ + int getWidthPadding() { + return mPadding[0]; + } + + /** + * Returns the number of extra padding in the Y axis. This doesn't return a number of pixel + * or dip, but how many paddings are pushing the screen dimension out. + */ + int getHeightPadding() { + return mPadding[1]; + } + + /** + * Computes the number of padding for a given view, and fills the given array of int. + * <p/>index 0 is X axis, index 1 is Y axis + * @param view the view to compute + * @param padding the result padding (index 0 is X axis, index 1 is Y axis) + */ + private void computePadding(UiElementNode view, int[] padding) { + String localName = view.getDescriptor().getXmlLocalName(); + + // first compute for each children + List<UiElementNode> children = view.getUiChildren(); + int count = children.size(); + if (count > 0) { + // compute the padding for all the children. + List<int[]> childrenPadding = new ArrayList<int[]>(count); + for (int i = 0 ; i < count ; i++) { + UiElementNode child = children.get(i); + int[] p = new int[] { 0, 0 }; + childrenPadding.add(p); + computePadding(child, p); + } + + // now combine/compare based on the parent. + // TODO: need a better way to do this, groovy or other. + if (count == 1) { + int[] p = childrenPadding.get(0); + padding[0] = p[0]; + padding[1] = p[1]; + } else { + if ("LinearLayout".equals(localName)) { + // TODO: figure out the orientation of the layout + combineLinearLayout(childrenPadding, padding, false); + } else if ("TableLayout".equals(localName)) { + combineLinearLayout(childrenPadding, padding, false); + } else if ("TableRow".equals(localName)) { + combineLinearLayout(childrenPadding, padding, true); + } else { + // TODO: need to figure out what we do here + throw new UnsupportedOperationException(); + } + } + } + + // if the view itself is a layout, add its padding + for (ElementDescriptor desc : mLayoutDescriptors) { + if (localName.equals(desc.getXmlName())) { + padding[0]++; + padding[1]++; + break; + } + } + } + + private void combineLinearLayout(List<int[]> paddings, int[] resultPadding, + boolean horizontal) { + // The way the children are combined will depend on the direction. + // For instance in a vertical layout, we add the y padding as they all add to the length + // of the needed canvas, while we take the biggest x padding needed by the children + + // the axis in which we take the sum of the padding of the children + int sumIndex = horizontal ? 0 : 1; + // the axis in which we take the max of the padding of the children + int maxIndex = horizontal ? 1 : 0; + + int max = -1; + for (int[] p : paddings) { + resultPadding[sumIndex] += p[sumIndex]; + if (max == -1 || max < p[maxIndex]) { + max = p[maxIndex]; + } + } + resultPadding[maxIndex] = max; + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/GraphicalEditorPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/GraphicalEditorPart.java index dc6a865..1d4a71c 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/GraphicalEditorPart.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/GraphicalEditorPart.java @@ -882,15 +882,32 @@ public class GraphicalEditorPart extends EditorPart implements IGraphicalLayoutE // get the selected theme
String theme = mConfigComposite.getTheme();
if (theme != null) {
-
// Compute the layout
- UiElementPullParser parser = new UiElementPullParser(getModel());
Rectangle rect = getBounds();
- boolean isProjectTheme = mConfigComposite.isProjectTheme();
+
+ boolean explodedView = !mConfigComposite.getClipping(); //FIXME: need new toggle
+ int width = rect.width;
+ int height = rect.height;
+ if (explodedView) {
+ // compute how many padding in x and y will bump the screen size
+ ExplodedRenderingHelper helper = new ExplodedRenderingHelper(
+ getModel(), iProject);
+
+ // there are 2 paddings for each view
+ // left and right, or top and bottom.
+ int paddingValue = ExplodedRenderingHelper.PADDING_VALUE * 2;
+
+ width += helper.getWidthPadding() * paddingValue;
+ height += helper.getHeightPadding() * paddingValue;
+ }
int density = mConfigComposite.getDensity().getDpiValue();
float xdpi = mConfigComposite.getXDpi();
float ydpi = mConfigComposite.getYDpi();
+ boolean isProjectTheme = mConfigComposite.isProjectTheme();
+
+ UiElementPullParser parser = new UiElementPullParser(getModel(),
+ explodedView, density, xdpi, iProject);
ILayoutResult result = computeLayout(bridge, parser,
iProject /* projectKey */,
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/GraphicalLayoutEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/GraphicalLayoutEditor.java index 7e4311f..400f729 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/GraphicalLayoutEditor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/GraphicalLayoutEditor.java @@ -27,6 +27,7 @@ import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewEleme import com.android.ide.eclipse.adt.internal.editors.layout.parts.ElementCreateCommand; import com.android.ide.eclipse.adt.internal.editors.layout.parts.UiElementEditPart; import com.android.ide.eclipse.adt.internal.editors.layout.parts.UiElementsEditPartFactory; +import com.android.ide.eclipse.adt.internal.editors.layout.parts.UiElementsEditPartFactory.IOutlineProvider; import com.android.ide.eclipse.adt.internal.editors.ui.tree.CopyCutAction; import com.android.ide.eclipse.adt.internal.editors.ui.tree.PasteAction; import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode; @@ -116,7 +117,7 @@ import java.util.Map; * @since GLE1 */ public class GraphicalLayoutEditor extends GraphicalEditorWithPalette - implements IGraphicalLayoutEditor, IConfigListener, ILayoutReloadListener { + implements IGraphicalLayoutEditor, IConfigListener, ILayoutReloadListener, IOutlineProvider { /** Reference to the layout editor */ @@ -187,6 +188,7 @@ public class GraphicalLayoutEditor extends GraphicalEditorWithPalette } }; + private boolean mExplodedView; public GraphicalLayoutEditor(LayoutEditor layoutEditor) { mLayoutEditor = layoutEditor; @@ -318,7 +320,7 @@ public class GraphicalLayoutEditor extends GraphicalEditorWithPalette super.configureGraphicalViewer(); GraphicalViewer viewer = getGraphicalViewer(); - viewer.setEditPartFactory(new UiElementsEditPartFactory(mParent.getDisplay())); + viewer.setEditPartFactory(new UiElementsEditPartFactory(mParent.getDisplay(), this)); viewer.setRootEditPart(new ScalableFreeformRootEditPart()); // Disable the following -- we don't drag *from* the GraphicalViewer yet: @@ -943,19 +945,36 @@ public class GraphicalLayoutEditor extends GraphicalEditorWithPalette // get the selected theme String theme = mConfigComposite.getTheme(); if (theme != null) { - // Compute the layout - UiElementPullParser parser = new UiElementPullParser(getModel()); Rectangle rect = getBounds(); - boolean isProjectTheme = mConfigComposite.isProjectTheme(); + + mExplodedView = !mConfigComposite.getClipping(); //FIXME: need new toggle + int width = rect.width; + int height = rect.height; + if (mExplodedView) { + // compute how many padding in x and y will bump the screen size + ExplodedRenderingHelper helper = new ExplodedRenderingHelper( + getModel(), iProject); + + // there are 2 paddings for each view + // left and right, or top and bottom. + int paddingValue = ExplodedRenderingHelper.PADDING_VALUE * 2; + + width += helper.getWidthPadding() * paddingValue; + height += helper.getHeightPadding() * paddingValue; + } int density = mConfigComposite.getDensity().getDpiValue(); float xdpi = mConfigComposite.getXDpi(); float ydpi = mConfigComposite.getYDpi(); + boolean isProjectTheme = mConfigComposite.isProjectTheme(); + + UiElementPullParser parser = new UiElementPullParser(getModel(), + mExplodedView, density, xdpi, iProject); ILayoutResult result = computeLayout(bridge, parser, iProject /* projectKey */, - rect.width, rect.height, !mConfigComposite.getClipping(), + width, height, !mConfigComposite.getClipping(), density, xdpi, ydpi, theme, isProjectTheme, configuredProjectRes, frameworkResources, mProjectCallback, @@ -1350,4 +1369,8 @@ public class GraphicalLayoutEditor extends GraphicalEditorWithPalette logger); } } + + public boolean hasOutline() { + return mExplodedView; + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/UiElementPullParser.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/UiElementPullParser.java index 3443272..a7ecdd5 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/UiElementPullParser.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/UiElementPullParser.java @@ -16,14 +16,29 @@ package com.android.ide.eclipse.adt.internal.editors.layout; +import com.android.ide.eclipse.adt.AndroidConstants; +import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; +import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors; +import com.android.ide.eclipse.adt.internal.editors.uimodel.UiAttributeNode; import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; +import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData; +import com.android.ide.eclipse.adt.internal.sdk.Sdk; import com.android.layoutlib.api.IXmlPullParser; +import com.android.layoutlib.api.IDensityBasedResourceValue.Density; +import com.android.sdklib.IAndroidTarget; +import com.android.sdklib.SdkConstants; +import org.eclipse.core.resources.IProject; +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.xmlpull.v1.XmlPullParserException; import java.util.ArrayList; +import java.util.Collection; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * {@link IXmlPullParser} implementation on top of {@link UiElementNode}. @@ -31,24 +46,47 @@ import java.util.List; * files. */ public final class UiElementPullParser extends BasePullParser { - + private final static String ATTR_PADDING = "padding"; //$NON-NLS-1$ + private final static Pattern sFloatPattern = Pattern.compile("(-?[0-9]+(?:\\.[0-9]+)?)(.*)"); //$NON-NLS-1$ + + private final int[] sIntOut = new int[1]; + private final ArrayList<UiElementNode> mNodeStack = new ArrayList<UiElementNode>(); private UiElementNode mRoot; - - public UiElementPullParser(UiElementNode top) { + private final boolean mExplodedRendering; + private boolean mZeroAttributeIsPadding = false; + private boolean mIncreaseExistingPadding = false; + private List<ElementDescriptor> mLayoutDescriptors; + private final int mDensityValue; + private final float mXdpi; + private final String mDefaultPaddingValue; + + public UiElementPullParser(UiElementNode top, boolean explodeRendering, int densityValue, + float xdpi, IProject project) { super(); mRoot = top; + mExplodedRendering = explodeRendering; + mDensityValue = densityValue; + mXdpi = xdpi; + mDefaultPaddingValue = ExplodedRenderingHelper.PADDING_VALUE + "px"; //$NON-NLS-1$ + if (mExplodedRendering) { + // get the layout descriptor + IAndroidTarget target = Sdk.getCurrent().getTarget(project); + AndroidTargetData data = Sdk.getCurrent().getTargetData(target); + LayoutDescriptors descriptors = data.getLayoutDescriptors(); + mLayoutDescriptors = descriptors.getLayoutDescriptors(); + } push(mRoot); } - + private UiElementNode getCurrentNode() { if (mNodeStack.size() > 0) { return mNodeStack.get(mNodeStack.size()-1); } - + return null; } - + private Node getAttribute(int i) { if (mParsingState != START_TAG) { throw new IndexOutOfBoundsException(); @@ -56,7 +94,7 @@ public final class UiElementPullParser extends BasePullParser { // get the current uiNode UiElementNode uiNode = getCurrentNode(); - + // get its xml node Node xmlNode = uiNode.getXmlNode(); @@ -66,11 +104,33 @@ public final class UiElementPullParser extends BasePullParser { return null; } - + private void push(UiElementNode node) { mNodeStack.add(node); + + mZeroAttributeIsPadding = false; + mIncreaseExistingPadding = false; + + if (mExplodedRendering) { + // first get the node name + String xml = node.getDescriptor().getXmlLocalName(); + for (ElementDescriptor descriptor : mLayoutDescriptors) { + if (xml.equals(descriptor.getXmlLocalName())) { + NamedNodeMap attributes = node.getXmlNode().getAttributes(); + Node padding = attributes.getNamedItemNS(SdkConstants.NS_RESOURCES, "padding"); + if (padding == null) { + // we'll return an extra padding + mZeroAttributeIsPadding = true; + } else { + mIncreaseExistingPadding = true; + } + + break; + } + } + } } - + private UiElementNode pop() { return mNodeStack.remove(mNodeStack.size()-1); } @@ -79,7 +139,7 @@ public final class UiElementPullParser extends BasePullParser { /** * {@inheritDoc} - * + * * This implementation returns the underlying DOM node. */ public Object getViewKey() { @@ -92,16 +152,36 @@ public final class UiElementPullParser extends BasePullParser { return "XML DOM element depth:" + mNodeStack.size(); } + /* + * This does not seem to be called by the layoutlib, but we keep this (and maintain + * it) just in case. + */ public int getAttributeCount() { UiElementNode node = getCurrentNode(); + if (node != null) { - return node.getUiAttributes().size(); + Collection<UiAttributeNode> attributes = node.getUiAttributes(); + int count = attributes.size(); + + return count + (mZeroAttributeIsPadding ? 1 : 0); } return 0; } + /* + * This does not seem to be called by the layoutlib, but we keep this (and maintain + * it) just in case. + */ public String getAttributeName(int i) { + if (mZeroAttributeIsPadding) { + if (i == 0) { + return ATTR_PADDING; + } else { + i--; + } + } + Node attribute = getAttribute(i); if (attribute != null) { return attribute.getLocalName(); @@ -110,7 +190,19 @@ public final class UiElementPullParser extends BasePullParser { return null; } + /* + * This does not seem to be called by the layoutlib, but we keep this (and maintain + * it) just in case. + */ public String getAttributeNamespace(int i) { + if (mZeroAttributeIsPadding) { + if (i == 0) { + return SdkConstants.NS_RESOURCES; + } else { + i--; + } + } + Node attribute = getAttribute(i); if (attribute != null) { return attribute.getNamespaceURI(); @@ -118,7 +210,21 @@ public final class UiElementPullParser extends BasePullParser { return ""; //$NON-NLS-1$ } + /* + * This does not seem to be called by the layoutlib, but we keep this (and maintain + * it) just in case. + */ public String getAttributePrefix(int i) { + if (mZeroAttributeIsPadding) { + if (i == 0) { + // figure out the prefix associated with the android namespace. + Document doc = mRoot.getXmlDocument(); + return doc.lookupPrefix(AndroidConstants.NS_CUSTOM_RESOURCES); + } else { + i--; + } + } + Node attribute = getAttribute(i); if (attribute != null) { return attribute.getPrefix(); @@ -126,26 +232,58 @@ public final class UiElementPullParser extends BasePullParser { return null; } + /* + * This does not seem to be called by the layoutlib, but we keep this (and maintain + * it) just in case. + */ public String getAttributeValue(int i) { + if (mZeroAttributeIsPadding) { + if (i == 0) { + return mDefaultPaddingValue; + } else { + i--; + } + } + Node attribute = getAttribute(i); if (attribute != null) { - return attribute.getNodeValue(); + String value = attribute.getNodeValue(); + if (mIncreaseExistingPadding && ATTR_PADDING.equals(attribute.getLocalName()) && + SdkConstants.NS_RESOURCES.equals(attribute.getNamespaceURI())) { + // add the padding and return the value + return addPaddingToValue(value); + } + return value; } - + return null; } + /* + * This is the main method used by the LayoutInflater to query for attributes. + */ public String getAttributeValue(String namespace, String localName) { + if (mZeroAttributeIsPadding && ATTR_PADDING.equals(localName) && + SdkConstants.NS_RESOURCES.equals(namespace)) { + return mDefaultPaddingValue; + } + // get the current uiNode UiElementNode uiNode = getCurrentNode(); - + // get its xml node Node xmlNode = uiNode.getXmlNode(); - + if (xmlNode != null) { Node attribute = xmlNode.getAttributes().getNamedItemNS(namespace, localName); if (attribute != null) { - return attribute.getNodeValue(); + String value = attribute.getNodeValue(); + if (mIncreaseExistingPadding && ATTR_PADDING.equals(localName) && + SdkConstants.NS_RESOURCES.equals(namespace)) { + // add the padding and return the value + return addPaddingToValue(value); + } + return value; } } @@ -174,10 +312,8 @@ public final class UiElementPullParser extends BasePullParser { public String getPrefix() { if (mParsingState == START_TAG || mParsingState == END_TAG) { - // FIXME will NEVER work - if (getCurrentNode().getDescriptor().getXmlLocalName().startsWith("android:")) { //$NON-NLS-1$ - return "android"; //$NON-NLS-1$ - } + Document doc = mRoot.getXmlDocument(); + return doc.lookupPrefix(getCurrentNode().getDescriptor().getNamespace()); } return null; @@ -187,16 +323,16 @@ public final class UiElementPullParser extends BasePullParser { if (mParsingState == START_TAG) { return getCurrentNode().getUiChildren().size() == 0; } - + throw new XmlPullParserException("Call to isEmptyElementTag while not in START_TAG", this, null); } - + @Override public void onNextFromStartDocument() { onNextFromStartTag(); } - + @Override public void onNextFromStartTag() { // get the current node, and look for text or children (children first) @@ -205,7 +341,7 @@ public final class UiElementPullParser extends BasePullParser { if (children.size() > 0) { // move to the new child, and don't change the state. push(children.get(0)); - + // in case the current state is CURRENT_DOC, we set the proper state. mParsingState = START_TAG; } else { @@ -217,7 +353,7 @@ public final class UiElementPullParser extends BasePullParser { } } } - + @Override public void onNextFromEndTag() { // look for a sibling. if no sibling, go back to the parent @@ -232,7 +368,7 @@ public final class UiElementPullParser extends BasePullParser { } else { // move back to the parent pop(); - + // we have only one element left (mRoot), then we're done with the document. if (mNodeStack.size() == 1) { mParsingState = END_DOCUMENT; @@ -241,4 +377,150 @@ public final class UiElementPullParser extends BasePullParser { } } } + + // ------- TypedValue stuff + // This is adapted from com.android.layoutlib.bridge.ResourceHelper + // (but modified to directly take the parsed value and convert it into pixel instead of + // storing it into a TypedValue) + // this was originally taken from platform/frameworks/base/libs/utils/ResourceTypes.cpp + + private static final class DimensionEntry { + String name; + int type; + + DimensionEntry(String name, int unit) { + this.name = name; + this.type = unit; + } + } + + /** {@link #TYPE_DIMENSION} complex unit: Value is raw pixels. */ + public static final int COMPLEX_UNIT_PX = 0; + /** {@link #TYPE_DIMENSION} complex unit: Value is Device Independent + * Pixels. */ + public static final int COMPLEX_UNIT_DIP = 1; + /** {@link #TYPE_DIMENSION} complex unit: Value is a scaled pixel. */ + public static final int COMPLEX_UNIT_SP = 2; + /** {@link #TYPE_DIMENSION} complex unit: Value is in points. */ + public static final int COMPLEX_UNIT_PT = 3; + /** {@link #TYPE_DIMENSION} complex unit: Value is in inches. */ + public static final int COMPLEX_UNIT_IN = 4; + /** {@link #TYPE_DIMENSION} complex unit: Value is in millimeters. */ + public static final int COMPLEX_UNIT_MM = 5; + + private final static DimensionEntry[] sDimensions = new DimensionEntry[] { + new DimensionEntry("px", COMPLEX_UNIT_PX), + new DimensionEntry("dip", COMPLEX_UNIT_DIP), + new DimensionEntry("dp", COMPLEX_UNIT_DIP), + new DimensionEntry("sp", COMPLEX_UNIT_SP), + new DimensionEntry("pt", COMPLEX_UNIT_PT), + new DimensionEntry("in", COMPLEX_UNIT_IN), + new DimensionEntry("mm", COMPLEX_UNIT_MM), + }; + + /** + * Adds padding to an existing dimension. + * <p/>This will resolve the attribute value (which can be px, dip, dp, sp, pt, in, mm) to + * a pixel value, add the padding value ({@link ExplodedRenderingHelper#PADDING_VALUE}), + * and then return a string with the new value as a px string ("42px"); + * If the conversion fails, only the special padding is returned. + */ + private String addPaddingToValue(String s) { + int padding = ExplodedRenderingHelper.PADDING_VALUE; + if (stringToPixel(s)) { + padding += sIntOut[0]; + } + + return padding + "px"; //$NON-NLS-1$ + } + + /** + * Convert the string into a pixel value, and puts it in {@link #sIntOut} + * @param s the dimension value from an XML attribute + * @return true if success. + */ + private boolean stringToPixel(String s) { + // remove the space before and after + s.trim(); + int len = s.length(); + + if (len <= 0) { + return false; + } + + // check that there's no non ascii characters. + char[] buf = s.toCharArray(); + for (int i = 0 ; i < len ; i++) { + if (buf[i] > 255) { + return false; + } + } + + // check the first character + if (buf[0] < '0' && buf[0] > '9' && buf[0] != '.') { + return false; + } + + // now look for the string that is after the float... + Matcher m = sFloatPattern.matcher(s); + if (m.matches()) { + String f_str = m.group(1); + String end = m.group(2); + + float f; + try { + f = Float.parseFloat(f_str); + } catch (NumberFormatException e) { + // this shouldn't happen with the regexp above. + return false; + } + + if (end.length() > 0 && end.charAt(0) != ' ') { + // We only support dimension-type values, so try to parse the unit for dimension + DimensionEntry dimension = parseDimension(end); + if (dimension != null) { + // convert the value into pixel based on the dimention type + // This is similar to TypedValue.applyDimension() + switch (dimension.type) { + case COMPLEX_UNIT_PX: + // do nothing, value is already in px + break; + case COMPLEX_UNIT_DIP: + case COMPLEX_UNIT_SP: // intended fall-through since we don't + // adjust for font size + f *= (float)mDensityValue / Density.DEFAULT_DENSITY; + break; + case COMPLEX_UNIT_PT: + f *= mXdpi * (1.0f / 72); + break; + case COMPLEX_UNIT_IN: + f *= mXdpi; + break; + case COMPLEX_UNIT_MM: + f *= mXdpi * (1.0f / 25.4f); + break; + } + + // store result (converted to int) + sIntOut[0] = (int) (f + 0.5); + + return true; + } + } + } + + return false; + } + + private static DimensionEntry parseDimension(String str) { + str = str.trim(); + + for (DimensionEntry d : sDimensions) { + if (d.name.equals(str)) { + return d; + } + } + + return null; + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/ElementFigure.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/ElementFigure.java index 23c2c9e..f3dc1ac 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/ElementFigure.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/ElementFigure.java @@ -16,6 +16,8 @@ package com.android.ide.eclipse.adt.internal.editors.layout.parts; +import com.android.ide.eclipse.adt.internal.editors.layout.parts.UiElementsEditPartFactory.IOutlineProvider; + import org.eclipse.draw2d.ColorConstants; import org.eclipse.draw2d.Figure; import org.eclipse.draw2d.Graphics; @@ -34,8 +36,10 @@ class ElementFigure extends Figure { private boolean mIsSelected; private Rectangle mInnerBounds; + private final IOutlineProvider mProvider; - public ElementFigure() { + public ElementFigure(IOutlineProvider provider) { + mProvider = provider; setOpaque(false); } @@ -67,10 +71,10 @@ class ElementFigure extends Figure { protected void paintBorder(Graphics graphics) { super.paintBorder(graphics); - if (mIsSelected) { + if (mIsSelected || (mProvider != null && mProvider.hasOutline())) { graphics.setLineWidth(1); graphics.setLineStyle(SWT.LINE_SOLID); - graphics.setForegroundColor(ColorConstants.red); + graphics.setForegroundColor(mIsSelected ? ColorConstants.red : ColorConstants.white); graphics.drawRectangle(getInnerBounds()); } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/LayoutFigure.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/LayoutFigure.java index 6334751..a4e3573 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/LayoutFigure.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/LayoutFigure.java @@ -16,6 +16,7 @@ package com.android.ide.eclipse.adt.internal.editors.layout.parts; +import com.android.ide.eclipse.adt.internal.editors.layout.parts.UiElementsEditPartFactory.IOutlineProvider; import com.android.ide.eclipse.adt.internal.editors.layout.parts.UiLayoutEditPart.HighlightInfo; import org.eclipse.draw2d.ColorConstants; @@ -37,8 +38,8 @@ class LayoutFigure extends ElementFigure { private HighlightInfo mHighlightInfo; - public LayoutFigure() { - super(); + public LayoutFigure(IOutlineProvider provider) { + super(provider); } public void setHighlighInfo(HighlightInfo highlightInfo) { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiElementsEditPartFactory.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiElementsEditPartFactory.java index 5f17937..31a1e2e 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiElementsEditPartFactory.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiElementsEditPartFactory.java @@ -34,9 +34,16 @@ import org.eclipse.swt.widgets.Display; public class UiElementsEditPartFactory implements EditPartFactory { private Display mDisplay; + private boolean mShowOutline = false; + private IOutlineProvider mProvider; - public UiElementsEditPartFactory(Display display) { + public interface IOutlineProvider { + boolean hasOutline(); + } + + public UiElementsEditPartFactory(Display display, IOutlineProvider provider) { mDisplay = display; + mProvider = provider; } public EditPart createEditPart(EditPart context, Object model) { @@ -46,7 +53,7 @@ public class UiElementsEditPartFactory implements EditPartFactory { UiElementNode node = (UiElementNode) model; if (node.getDescriptor().hasChildren()) { - return new UiLayoutEditPart(node); + return new UiLayoutEditPart(node, mProvider); } return new UiViewEditPart(node); @@ -54,4 +61,8 @@ public class UiElementsEditPartFactory implements EditPartFactory { return null; } + public void setShowOutline(boolean showOutline) { + mShowOutline = showOutline; + } + } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiLayoutEditPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiLayoutEditPart.java index 14d6edb..d0495e2 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiLayoutEditPart.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiLayoutEditPart.java @@ -16,6 +16,7 @@ package com.android.ide.eclipse.adt.internal.editors.layout.parts; +import com.android.ide.eclipse.adt.internal.editors.layout.parts.UiElementsEditPartFactory.IOutlineProvider; import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; import org.eclipse.draw2d.IFigure; @@ -52,9 +53,11 @@ public final class UiLayoutEditPart extends UiElementEditPart { } private final HighlightInfo mHighlightInfo = new HighlightInfo(); + private final IOutlineProvider mProvider; - public UiLayoutEditPart(UiElementNode uiElementNode) { + public UiLayoutEditPart(UiElementNode uiElementNode, IOutlineProvider provider) { super(uiElementNode); + mProvider = provider; } @Override @@ -73,7 +76,7 @@ public final class UiLayoutEditPart extends UiElementEditPart { @Override protected IFigure createFigure() { - IFigure f = new LayoutFigure(); + IFigure f = new LayoutFigure(mProvider); f.setLayoutManager(new XYLayout()); return f; } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiViewEditPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiViewEditPart.java index 430e1ce..f90bd67 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiViewEditPart.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/parts/UiViewEditPart.java @@ -34,7 +34,7 @@ public class UiViewEditPart extends UiElementEditPart { @Override protected IFigure createFigure() { - IFigure f = new ElementFigure(); + IFigure f = new ElementFigure(null); f.setLayoutManager(new XYLayout()); return f; } diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/UiElementPullParserTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/UiElementPullParserTest.java index e234d6b..649b400 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/UiElementPullParserTest.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/UiElementPullParserTest.java @@ -77,7 +77,7 @@ public class UiElementPullParserTest extends TestCase { ElementDescriptor[] a = new ElementDescriptor[] { buttonDescriptor, textDescriptor, linearDescriptor, relativeDescriptor }; - + linearDescriptor.setChildren(a); relativeDescriptor.setChildren(a); @@ -85,9 +85,9 @@ public class UiElementPullParserTest extends TestCase { ElementDescriptor rootDescriptor = new ElementDescriptor("root", "", "", "", new AttributeDescriptor[] { }, a, false); - + ui = new UiElementNode(rootDescriptor); - + /* create a dummy XML file. * <LinearLayout android:orientation="vertical"> * <Button android:name="button1" android:text="button1text"/> @@ -101,7 +101,7 @@ public class UiElementPullParserTest extends TestCase { null); button1.addAttributes(SdkConstants.NS_RESOURCES, "name", "button1"); button1.addAttributes(SdkConstants.NS_RESOURCES, "text", "button1text"); - + // create a map of the attributes we add to the multi-attribute nodes so that // we can more easily test the values when we parse the XML. // This is due to some attributes showing in a certain order for a node and in a different @@ -118,7 +118,7 @@ public class UiElementPullParserTest extends TestCase { button2Map = new HashMap<String, String>(); button2Map.put("name", "button2"); button2Map.put("text", "button2text"); - + MockXmlNode text = new MockXmlNode(null /* namespace */, "TextView", Node.ELEMENT_NODE, null); text.addAttributes(SdkConstants.NS_RESOURCES, "name", "text1"); @@ -131,14 +131,14 @@ public class UiElementPullParserTest extends TestCase { MockXmlNode relative = new MockXmlNode(null /* namespace */, "RelativeLayout", Node.ELEMENT_NODE, new MockXmlNode[] { button2, text }); relative.addAttributes(SdkConstants.NS_RESOURCES, "orientation", "toto"); - + MockXmlNode linear = new MockXmlNode(null /* namespace */, "LinearLayout", Node.ELEMENT_NODE, new MockXmlNode[] { button1, relative }); linear.addAttributes(SdkConstants.NS_RESOURCES, "orientation", "vertical"); - + MockXmlNode root = new MockXmlNode(null /* namespace */, "root", Node.ELEMENT_NODE, new MockXmlNode[] { linear }); - + // put the namespace/prefix in place root.setPrefix(SdkConstants.NS_RESOURCES, "android"); @@ -152,14 +152,14 @@ public class UiElementPullParserTest extends TestCase { protected void tearDown() throws Exception { super.tearDown(); } - + public void testParser() { try { // wrap the parser around the ui element node, and start parsing - UiElementPullParser parser = new UiElementPullParser(ui); - + UiElementPullParser parser = new UiElementPullParser(ui, false, null); + assertEquals(XmlPullParser.START_DOCUMENT, parser.getEventType()); - + // top level Linear layout assertEquals(XmlPullParser.START_TAG, parser.next()); assertEquals("LinearLayout", parser.getName()); @@ -168,7 +168,7 @@ public class UiElementPullParserTest extends TestCase { assertEquals(SdkConstants.NS_RESOURCES, parser.getAttributeNamespace(0)); assertEquals("android", parser.getAttributePrefix(0)); assertEquals("vertical", parser.getAttributeValue(0)); - + // Button assertEquals(XmlPullParser.START_TAG, parser.next()); assertEquals("Button", parser.getName()); @@ -204,14 +204,14 @@ public class UiElementPullParserTest extends TestCase { check(parser, 1, textMap); // end of TextView assertEquals(XmlPullParser.END_TAG, parser.next()); - + // end of RelativeLayout assertEquals(XmlPullParser.END_TAG, parser.next()); - + // end of top level linear layout assertEquals(XmlPullParser.END_TAG, parser.next()); - + assertEquals(XmlPullParser.END_DOCUMENT, parser.next()); } catch (XmlPullParserException e) { e.printStackTrace(); @@ -229,11 +229,11 @@ public class UiElementPullParserTest extends TestCase { private void check(UiElementPullParser parser, int i, HashMap<String, String> map) { String name = parser.getAttributeName(i); String value = parser.getAttributeValue(i); - + String referenceValue = map.get(name); assertNotNull(referenceValue); assertEquals(referenceValue, value); - + assertEquals(SdkConstants.NS_RESOURCES, parser.getAttributeNamespace(i)); assertEquals("android", parser.getAttributePrefix(i)); } |