diff options
author | Tor Norbye <tnorbye@google.com> | 2010-11-19 13:02:51 -0800 |
---|---|---|
committer | Android Code Review <code-review@android.com> | 2010-11-19 13:02:51 -0800 |
commit | d59c1f5ad1dc6219fd901940a8c40f320dad5685 (patch) | |
tree | c34dc576e5a2c706727d5b27b8d3f32508a8e8bf | |
parent | a64e53e7f4e0b5799bab64b9443b7067c9eb0bea (diff) | |
parent | a2d7874ed23bfc2fa7665cc84901e0f4781b4e51 (diff) | |
download | sdk-d59c1f5ad1dc6219fd901940a8c40f320dad5685.zip sdk-d59c1f5ad1dc6219fd901940a8c40f320dad5685.tar.gz sdk-d59c1f5ad1dc6219fd901940a8c40f320dad5685.tar.bz2 |
Merge "Add per-view custom initialization logic"
38 files changed, 1394 insertions, 211 deletions
diff --git a/eclipse/dictionary.txt b/eclipse/dictionary.txt index fe06849..b9356c0 100644 --- a/eclipse/dictionary.txt +++ b/eclipse/dictionary.txt @@ -1,5 +1,6 @@ aapt adb +addon adt aidl alt @@ -16,9 +17,11 @@ avds basename bitmask breadcrumb +builtin callback callbacks checkbox +classloader classpath clipboard clipboards @@ -37,13 +40,18 @@ dedent deprecated deselect deselects +designtime dev dex dexified diff +diffs dirs dpi editable +enum +enums +env exec fallback foo @@ -65,6 +73,7 @@ int javadoc layoutlib leaky +lib lifecycle linestyle linux @@ -74,6 +83,7 @@ luminance mac macs marquee +metadata min multi namespace @@ -89,12 +99,16 @@ plugin pre precompiler pref +preload +preloads +primordial printf programmatically proguard proxies proxy recompilation +rect redo regexp registry @@ -107,14 +121,18 @@ scrollbar scrollbars sdk se +searchable semi +serializer settable spec standalone stateless stderr stdout +stretchiness subclassing +submenu supertype syncs themed @@ -122,10 +140,12 @@ tmp tooltip traceview ui +uncomment undescribed uninstall uninstallation uninstalling +upcoming uri url validator diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IClientRulesEngine.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IClientRulesEngine.java index 647c6a0..a17af01 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IClientRulesEngine.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IClientRulesEngine.java @@ -28,8 +28,6 @@ import com.android.sdklib.annotations.Nullable; * <b>NOTE: This is not a public or final API; if you rely on this be prepared * to adjust your code for the next tools release.</b> * </p> - * - * @see IViewRule#RULES_ENGINE */ public interface IClientRulesEngine { @@ -57,6 +55,14 @@ public interface IClientRulesEngine { IViewRule loadRule(String fqcn); /** + * Returns the metadata associated with the given fully qualified class name. + * + * @param fqcn a fully qualified class name for an Android view class + * @return the metadata associated with the given fully qualified class name. + */ + IViewMetadata getMetadata(String fqcn); + + /** * Displays the given message string in an alert dialog with an "OK" button. */ void displayAlert(String message); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IViewMetadata.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IViewMetadata.java new file mode 100644 index 0000000..9397ef8 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IViewMetadata.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2010 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.common.api; + +/** + * Metadata about a particular view. The metadata for a View can be found by asking the + * {@link IClientRulesEngine} for the metadata for a given class via + * {@link IClientRulesEngine#getMetadata}. + */ +public interface IViewMetadata { + /** + * Returns true if this view is a potential parent (e.g. it <b>can</b> have children). + * + * @return true if this view can have children + */ + public boolean isParent(); + + /** + * Returns the display name views of this type (a name suitable to display to the + * user, normally capitalized and usually but not necessarily tied to the + * implementation class). To be clear, a specific view may have an id attribute and a + * text attribute, <b>neither</b> of these is the display name. Instead, the class + * android.widget.ZoomControls may have the display name "Zoom Controls", and an + * individual view created into a layout can for example have the id "ZoomControl01". + * + * @return the user visible name of views of this type (never null) + */ + public String getDisplayName(); + + /** + * Returns the tooltip for this view, if any + * + * @return a tooltip, or null + */ + public String getTooltip(); + + /** + * Returns the {@link FillPreference} of this view + * + * @return the {@link FillPreference} of this view + */ + public FillPreference getFillPreference(); + + /** + * Types of fill behavior that views can prefer. + * <p> + * TODO: Consider better names. FillPolicy? Stretchiness? + */ + public enum FillPreference { + /** This view does not want to fill */ + NONE, + /** This view wants to always fill both horizontal and vertical */ + BOTH, + /** This view wants to fill horizontally but not vertically */ + WIDTH, + /** This view wants to fill vertically but not horizontally */ + HEIGHT, + /** + * This view wants to fill in the opposite dimension of the context, e.g. in a + * vertical context it wants to fill horizontally, and vice versa + */ + OPPOSITE, + /** This view wants to fill horizontally, but only in a vertical context */ + WIDTH_IN_VERTICAL, + /** This view wants to fill vertically, but only in a horizontal context */ + HEIGHT_IN_HORIZONTAL; + + /** + * Returns true if this view wants to fill horizontally, if the context is + * vertical or horizontal as indicated by the parameter. + * + * @param verticalContext If true, the context is vertical, otherwise it is + * horizontal. + * @return true if this view wants to fill horizontally + */ + public boolean fillHorizontally(boolean verticalContext) { + return (this == BOTH || this == WIDTH || + (verticalContext && (this == OPPOSITE || this == WIDTH_IN_VERTICAL))); + } + + /** + * Returns true if this view wants to fill vertically, if the context is + * vertical or horizontal as indicated by the parameter. + * + * @param verticalContext If true, the context is vertical, otherwise it is + * horizontal. + * @return true if this view wants to fill vertically + */ + public boolean fillVertically(boolean verticalContext) { + return (this == BOTH || this == HEIGHT || + (!verticalContext && (this == OPPOSITE || this == HEIGHT_IN_HORIZONTAL))); + } + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IViewRule.java index 13699b4..0ec8eb3 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IViewRule.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/IViewRule.java @@ -17,7 +17,6 @@ package com.android.ide.common.api; import java.util.List; -import java.util.Map; /** @@ -45,13 +44,6 @@ import java.util.Map; public interface IViewRule { /** - * The name of the property that returns a {@link IClientRulesEngine} created for this - * rules. The instance lets rules use some methods from the rules engine, for example - * for accessing other rules. - */ - final static String RULES_ENGINE = "_rules_engine"; - - /** * This method is called by the rule engine when the script is first loaded. * It gives the rule a chance to initialize itself. * @@ -63,7 +55,7 @@ public interface IViewRule { * the engine during initialization and then use it later to invoke some of the * {@link IClientRulesEngine} methods for example to request user input. * @return True if this rule can handle the given FQCN. False if the rule can't handle the - * given FQCN, in which case the rule engine will find another rule matching a parent clas. + * given FQCN, in which case the rule engine will find another rule matching a parent class. */ boolean onInitialize(String fqcn, IClientRulesEngine engine); @@ -140,23 +132,6 @@ public interface IViewRule { INode childNode); - // ==== XML Creation ==== - - /** - * Returns the default attributes that a new XML element of this type should have - * when added to an XML layout file. Note that these defaults can be overridden by the - * specific code performing the insertion. - * - * TODO: - * - added=>created - * - list tuple(uri, local name, str: value) - * - gen id - * - * @return A map of attribute:values for a new element of this type. Can be null or empty. - */ - Map<?, ?> getDefaultAttributes(); - - // ==== Drag'n'drop support ==== /** @@ -215,4 +190,40 @@ public interface IViewRule { * @param pastedElements The elements being pasted. */ void onPaste(INode targetNode, IDragElement[] pastedElements); + + // ==== XML Creation ==== + + /** + * Called when a view for this rule is being created. This allows for the rule to + * customize the newly created object. Note that this method is called not just when a + * view is created from a palette drag, but when views are constructed via a drag-move + * (where views are created in the destination and then deleted from the source), and + * even when views are constructed programmatically from other view rules. The + * {@link InsertType} parameter can be used to distinguish the context for the + * insertion. For example, the <code>DialerFilterRule</code> will insert EditText children + * when a DialerFilter is first created, but not during a copy/paste or a move. + * + * @param node the newly created node (which will always be a View that applies to + * this {@link IViewRule}) + * @param parent the parent of the node (which may not yet contain the newly created + * node in its child list) + * @param insertType whether this node was created as part of a newly created view, or + * as a copy, or as a move, etc. + */ + void onCreate(INode node, INode parent, InsertType insertType); + + /** + * Called when a child for this view has been created and is being inserted into the + * view parent for which this {@link IViewRule} applies. Allows the parent to perform + * customizations of the object. As with {@link #onCreate}, the {@link InsertType} + * parameter can be used to handle new creation versus moves versus copy/paste + * operations differently. + * + * @param child the newly created node + * @param parent the parent of the newly created node (which may not yet contain the + * newly created node in its child list) + * @param insertType whether this node was created as part of a newly created view, or + * as a copy, or as a move, etc. + */ + void onChildInserted(INode child, INode parent, InsertType insertType); } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/InsertType.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/InsertType.java new file mode 100644 index 0000000..15d3a98 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/InsertType.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2010 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.common.api; + +/** + * An enumerated type of different insertion events, such as an insertion from a + * copy/paste operation or as the first half of a move operation. + */ +public enum InsertType { + /** The view is newly created (by for example a palette drag) */ + CREATE, + + /** The view is being inserted here because it was moved from somewhere else */ + MOVE, + + /** + * The view is being inserted here as a result of a copy/paste from elsewhere + * (including drags, but not from the palette) + */ + PASTE; +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/MenuAction.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/MenuAction.java index 210e565..d82049d 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/MenuAction.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/MenuAction.java @@ -152,7 +152,7 @@ public abstract class MenuAction { /** * The base class for {@link Toggle} and {@link Choices}. */ - public static abstract class Action extends MenuAction { + public static class Action extends MenuAction { /** * A callback executed when the action is selected in the context menu. @@ -176,7 +176,7 @@ public abstract class MenuAction { * @param callback The callback executed when the action is selected. * Must not be null. */ - private Action(String id, String title, String groupId, IMenuCallback callback) { + public Action(String id, String title, String groupId, IMenuCallback callback) { super(id, title); mGroupId = groupId; mCallback = callback; diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/Rect.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/Rect.java index 32caf62..b34d729 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/Rect.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/api/Rect.java @@ -16,7 +16,6 @@ package com.android.ide.common.api; -import org.eclipse.swt.graphics.Rectangle; /** @@ -49,11 +48,6 @@ public class Rect { } /** Initialize rectangle to the given values. They can be invalid. */ - public Rect(Rectangle swtRect) { - set(swtRect); - } - - /** Initialize rectangle to the given values. They can be invalid. */ public Rect set(int x, int y, int w, int h) { this.x = x; this.y = y; @@ -68,12 +62,6 @@ public class Rect { return this; } - /** Initialize rectangle to match the given one. */ - public Rect set(Rectangle swtRect) { - set(swtRect.x, swtRect.y, swtRect.width, swtRect.height); - return this; - } - /** Returns a new instance of a rectangle with the same values. */ public Rect copy() { return new Rect(x, y, w, h); @@ -161,11 +149,6 @@ public class Rect { } return this.x == rhs.x && this.y == rhs.y && this.w == rhs.w && this.h == rhs.h; - - } else if (obj instanceof Rectangle) { - Rectangle rhs = (Rectangle) obj; - return this.x == rhs.x && this.y == rhs.y && - this.w == rhs.width && this.h == rhs.height; } return false; @@ -173,10 +156,10 @@ public class Rect { @Override public int hashCode() { - int h = x; - h ^= ((y >> 8) & 0x0FFFFFF) | ((y & 0x00000FF) << 24); - h ^= ((w >> 16) & 0x000FFFF) | ((w & 0x000FFFF) << 16); - h ^= ((h >> 24) & 0x00000FF) | ((h & 0x0FFFFFF) << 8); - return h; + int hc = x; + hc ^= ((y >> 8) & 0x0FFFFFF) | ((y & 0x00000FF) << 24); + hc ^= ((w >> 16) & 0x000FFFF) | ((w & 0x000FFFF) << 16); + hc ^= ((h >> 24) & 0x00000FF) | ((h & 0x0FFFFFF) << 8); + return hc; } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/AbsListViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/AbsListViewRule.java new file mode 100644 index 0000000..cd1b0fc --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/AbsListViewRule.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2010 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.common.layout; + +import com.android.ide.common.api.IViewRule; + +/** + * An {@link IViewRule} for android.widget.AbsListViewRule + */ +public class AbsListViewRule extends IgnoredLayoutRule { + + // GridViews and ListViews are not configurable via XML + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseView.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseView.java index 15871d7..045f36d 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseView.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseView.java @@ -26,6 +26,7 @@ import com.android.ide.common.api.IMenuCallback; import com.android.ide.common.api.INode; import com.android.ide.common.api.INodeHandler; import com.android.ide.common.api.IViewRule; +import com.android.ide.common.api.InsertType; import com.android.ide.common.api.MenuAction; import com.android.ide.common.api.Point; import com.android.ide.common.api.Rect; @@ -45,7 +46,7 @@ import java.util.Set; * Common IViewRule processing to all view and layout classes. */ public class BaseView implements IViewRule { - private IClientRulesEngine mRulesEngine; + protected IClientRulesEngine mRulesEngine; /** * Namespace for the Android resource XML, i.e. @@ -53,23 +54,42 @@ public class BaseView implements IViewRule { */ public static String ANDROID_URI = "http://schemas.android.com/apk/res/android"; //$NON-NLS-1$ + /** The fully qualified class name of an EditText view */ + public static final String FQCN_EDIT_TEXT = "android.widget.EditText"; //$NON-NLS-1$ + + /** The fully qualified class name of a LinearLayout view */ + public static final String FQCN_LINEAR_LAYOUT = "android.widget.LinearLayout"; //$NON-NLS-1$ + + /** The fully qualified class name of a FrameLayout view */ + public static final String FQCN_FRAME_LAYOUT = "android.widget.FrameLayout"; //$NON-NLS-1$ + + /** The fully qualified class name of a TableRow view */ + public static final String FQCN_TABLE_ROW = "android.widget.TableRow"; //$NON-NLS-1$ + + /** The fully qualified class name of a TabWidget view */ + public static final String FQCN_TAB_WIDGET = "android.widget.TabWidget"; //$NON-NLS-1$ + // Some common Android layout attribute names used by the view rules. // All these belong to the attribute namespace ANDROID_URI. - public static String ATTR_ID = "id"; //$NON-NLS-1$ + public static final String ATTR_ID = "id"; //$NON-NLS-1$ - public static String ATTR_TEXT = "text"; //$NON-NLS-1$ + public static final String ATTR_TEXT = "text"; //$NON-NLS-1$ - public static String ATTR_LAYOUT_WIDTH = "layout_width"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_WIDTH = "layout_width"; //$NON-NLS-1$ - public static String ATTR_LAYOUT_HEIGHT = "layout_height"; //$NON-NLS-1$ + public static final String ATTR_LAYOUT_HEIGHT = "layout_height"; //$NON-NLS-1$ + + public static final String ATTR_SRC = "src"; //$NON-NLS-1$ + + public static final String ATTR_LAYOUT_BELOW = "layout_below"; //$NON-NLS-1$ // Some common Android layout attribute values used by the view rules. - public static String VALUE_FILL_PARENT = "fill_parent"; //$NON-NLS-1$ + public static final String VALUE_FILL_PARENT = "fill_parent"; //$NON-NLS-1$ // like fill_parent for API 8 - public static String VALUE_MATCH_PARENT = "match_parent"; //$NON-NLS-1$ + public static final String VALUE_MATCH_PARENT = "match_parent"; //$NON-NLS-1$ - public static String VALUE_WRAP_CONTENT = "wrap_content"; //$NON-NLS-1$ + public static final String VALUE_WRAP_CONTENT = "wrap_content"; //$NON-NLS-1$ // Cache of attributes. Key is FQCN of a node mixed with its view hierarchy // parent. Values are a custom map as needed by getContextMenu. @@ -98,11 +118,6 @@ public class BaseView implements IViewRule { return null; } - public Map<?, ?> getDefaultAttributes() { - // The base rule does not have any custom default attributes. - return null; - } - // === Context Menu === /** @@ -531,4 +546,26 @@ public class BaseView implements IViewRule { return mChoices; } } + + /** + * Returns a source attribute value which points to a sample image. This is typically + * used to provide an initial image shown on ImageButtons, etc. There is no guarantee + * that the source pointed to by this method actually exists. + * + * @return a source attribute to use for sample images, never null + */ + protected String getSampleImageSrc() { + // For now, we point to the sample icon which is written into new Android projects + // created in ADT. We could alternatively look into the project resources folder + // and try to pick something else, or even return some builtin image resource + // in the @android namespace. + + return "@drawable/icon"; //$NON-NLS-1$ + } + + public void onCreate(INode node, INode parent, InsertType insertType) { + } + + public void onChildInserted(INode node, INode parent, InsertType insertType) { + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/DialerFilterRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/DialerFilterRule.java new file mode 100644 index 0000000..54b511e --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/DialerFilterRule.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2010 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.common.layout; + +import com.android.ide.common.api.INode; +import com.android.ide.common.api.IViewRule; +import com.android.ide.common.api.InsertType; + +/** + * An {@link IViewRule} for android.widget.DialerFilterRule. + */ +public class DialerFilterRule extends BaseView { + + @Override + public void onCreate(INode node, INode parent, InsertType insertType) { + super.onCreate(node, parent, insertType); + + // A DialerFilter requires a couple of nested EditTexts with fixed ids: + if (insertType == InsertType.CREATE) { + INode hint = node.appendChild(FQCN_EDIT_TEXT); + hint.setAttribute(ANDROID_URI, BaseView.ATTR_TEXT, "Hint"); + hint.setAttribute(ANDROID_URI, ATTR_ID, "@android:id/hint"); //$NON-NLS-1$ + hint.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, VALUE_FILL_PARENT); + + INode primary = node.appendChild(FQCN_EDIT_TEXT); + primary.setAttribute(ANDROID_URI, BaseView.ATTR_TEXT, "Primary"); + primary.setAttribute(ANDROID_URI, ATTR_ID, "@android:id/primary"); //$NON-NLS-1$ + primary.setAttribute(ANDROID_URI, ATTR_LAYOUT_BELOW, + "@android:id/hint"); //$NON-NLS-1$ + primary.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, VALUE_FILL_PARENT); + + + // What do we initialize the icon to? + //INode icon = node.appendChild("android.widget.ImageView"); //$NON-NLS-1$ + //icon.setAttribute(ANDROID_URI, ATTR_ID, "@android:id/icon"); //$NON-NLS-1$ + } + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/HorizontalScrollViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/HorizontalScrollViewRule.java new file mode 100644 index 0000000..547dc04 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/HorizontalScrollViewRule.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2010 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.common.layout; + +import com.android.ide.common.api.INode; +import com.android.ide.common.api.IViewRule; +import com.android.ide.common.api.InsertType; + +/** + * An {@link IViewRule} for android.widget.HorizontalScrollView. + */ +public class HorizontalScrollViewRule extends BaseView { + + @Override + public void onChildInserted(INode child, INode parent, InsertType insertType) { + super.onChildInserted(child, parent, insertType); + + // The child of the ScrollView should fill in both directions + child.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, VALUE_FILL_PARENT); + child.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, VALUE_FILL_PARENT); + } + + @Override + public void onCreate(INode node, INode parent, InsertType insertType) { + super.onCreate(node, parent, insertType); + + if (insertType == InsertType.CREATE) { + // Insert a horizontal linear layout which is commonly used with horizontal scrollbars + // as described by the documentation for HorizontalScrollbars. + INode linearLayout = node.appendChild(FQCN_LINEAR_LAYOUT); + linearLayout.setAttribute(ANDROID_URI, LinearLayoutRule.ATTR_ORIENTATION, + LinearLayoutRule.VALUE_HORIZONTAL); + } + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ImageButtonRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ImageButtonRule.java new file mode 100644 index 0000000..7a23f17 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ImageButtonRule.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2010 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.common.layout; + +import com.android.ide.common.api.INode; +import com.android.ide.common.api.IViewRule; +import com.android.ide.common.api.InsertType; + +/** + * An {@link IViewRule} for android.widget.ImageButtonRule. + */ +public class ImageButtonRule extends BaseView { + + @Override + public void onCreate(INode node, INode parent, InsertType insertType) { + super.onCreate(node, parent, insertType); + + if (insertType == InsertType.CREATE) { + node.setAttribute(ANDROID_URI, ATTR_SRC, getSampleImageSrc()); + } + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ImageViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ImageViewRule.java new file mode 100644 index 0000000..67cd422 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ImageViewRule.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2010 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.common.layout; + +import com.android.ide.common.api.INode; +import com.android.ide.common.api.IViewRule; +import com.android.ide.common.api.InsertType; + +/** + * An {@link IViewRule} for android.widget.ImageViewRule. + */ +public class ImageViewRule extends BaseView { + + @Override + public void onCreate(INode node, INode parent, InsertType insertType) { + super.onCreate(node, parent, insertType); + + if (insertType == InsertType.CREATE) { + node.setAttribute(ANDROID_URI, ATTR_SRC, getSampleImageSrc()); + } + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LinearLayoutRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LinearLayoutRule.java index e5334f8..e31424c 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LinearLayoutRule.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LinearLayoutRule.java @@ -24,10 +24,13 @@ import com.android.ide.common.api.IGraphics; import com.android.ide.common.api.IMenuCallback; import com.android.ide.common.api.INode; import com.android.ide.common.api.INodeHandler; +import com.android.ide.common.api.IViewMetadata; import com.android.ide.common.api.IViewRule; +import com.android.ide.common.api.InsertType; import com.android.ide.common.api.MenuAction; import com.android.ide.common.api.Point; import com.android.ide.common.api.Rect; +import com.android.ide.common.api.IViewMetadata.FillPreference; import java.util.ArrayList; import java.util.List; @@ -47,34 +50,57 @@ public class LinearLayoutRule extends BaseLayout { */ @Override public List<MenuAction> getContextMenu(final INode selectedNode) { + if (supportsOrientation()) { + String curr_orient = selectedNode.getStringAttr(ANDROID_URI, ATTR_ORIENTATION); + if (curr_orient == null || curr_orient.length() == 0) { + curr_orient = VALUE_HORIZONTAL; + } - String curr_orient = selectedNode.getStringAttr(ANDROID_URI, ATTR_ORIENTATION); - if (curr_orient == null || curr_orient.length() == 0) { - curr_orient = VALUE_HORIZONTAL; + IMenuCallback onChange = new IMenuCallback() { + public void action(MenuAction action, final String valueId, Boolean newValue) { + String actionId = action.getId(); + final INode node = selectedNode; + + if (actionId.equals("_orientation")) { //$NON-NLS-1$ + node.editXml("Change LinearLayout " + ATTR_ORIENTATION, new INodeHandler() { + public void handle(INode n) { + node.setAttribute(ANDROID_URI, ATTR_ORIENTATION, valueId); + } + }); + } + } + }; + + return concatenate(super.getContextMenu(selectedNode), + new MenuAction.Choices("_orientation", "Orientation", //$NON-NLS-1$ + mapify( + "horizontal", "Horizontal", //$NON-NLS-1$ + "vertical", "Vertical" //$NON-NLS-1$ + ), + curr_orient, onChange)); + } else { + return super.getContextMenu(selectedNode); } + } - IMenuCallback onChange = new IMenuCallback() { - public void action(MenuAction action, final String valueId, Boolean newValue) { - String actionId = action.getId(); - final INode node = selectedNode; + /** + * Returns true if the given node represents a vertical linear layout. + * @param node the node to check layout orientation for + * @return true if the layout is in vertical mode, otherwise false + */ + protected boolean isVertical(INode node) { + // Horizontal is the default, so if no value is specified it is horizontal. + return VALUE_VERTICAL.equals(node.getStringAttr(ANDROID_URI, + ATTR_ORIENTATION)); + } - if (actionId.equals("_orientation")) { //$NON-NLS-1$ - node.editXml("Change LinearLayout " + ATTR_ORIENTATION, new INodeHandler() { - public void handle(INode n) { - node.setAttribute(ANDROID_URI, ATTR_ORIENTATION, valueId); - } - }); - } - } - }; - - return concatenate(super.getContextMenu(selectedNode), - new MenuAction.Choices("_orientation", "Orientation", //$NON-NLS-1$ - mapify( - "horizontal", "Horizontal", //$NON-NLS-1$ - "vertical", "Vertical" //$NON-NLS-1$ - ), - curr_orient, onChange)); + /** + * Returns true if this LinearLayout supports switching orientation. + * + * @return true if this layout supports orientations + */ + protected boolean supportsOrientation() { + return true; } // ==== Drag'n'drop support ==== @@ -91,8 +117,7 @@ public class LinearLayoutRule extends BaseLayout { return null; } - boolean isVertical = VALUE_VERTICAL.equals(targetNode.getStringAttr(ANDROID_URI, - ATTR_ORIENTATION)); + boolean isVertical = isVertical(targetNode); // Prepare a list of insertion points: X coords for horizontal, Y for // vertical. @@ -247,11 +272,28 @@ public class LinearLayoutRule extends BaseLayout { gc.useStyle(DrawingStyle.DROP_PREVIEW); for (IDragElement element : elements) { Rect bounds = element.getBounds(); - if (bounds.isValid() && (bounds.w > b.w || bounds.h > b.h)) { + if (bounds.isValid() && (bounds.w > b.w || bounds.h > b.h) && + node.getChildren().length == 0) { // The bounds of the child does not fully fit inside the target. - // Limit the bounds to the layout bounds. - Rect within = new Rect(b.x, b.y, - Math.min(bounds.w, b.w), Math.min(bounds.h, b.h)); + // Limit the bounds to the layout bounds (but only when there + // are no children, since otherwise positioning around the existing + // children gets difficult) + final int px, py, pw, ph; + if (bounds.w > b.w) { + px = b.x; + pw = b.w; + } else { + px = bounds.x + offsetX; + pw = bounds.w; + } + if (bounds.h > b.h) { + py = b.y; + ph = b.h; + } else { + py = bounds.y + offsetY; + ph = bounds.h; + } + Rect within = new Rect(px, py, pw, ph); gc.drawRect(within); } else { drawElement(gc, element, offsetX, offsetY); @@ -369,6 +411,25 @@ public class LinearLayoutRule extends BaseLayout { }); } + @Override + public void onChildInserted(INode node, INode parent, InsertType insertType) { + // Attempt to set fill-properties on newly added views such that for example, + // in a vertical layout, a text field defaults to filling horizontally, but not + // vertically. + String fqcn = node.getFqcn(); + IViewMetadata metadata = mRulesEngine.getMetadata(fqcn); + if (metadata != null) { + boolean vertical = isVertical(parent); + FillPreference fill = metadata.getFillPreference(); + if (fill.fillHorizontally(vertical)) { + node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, VALUE_FILL_PARENT); + } + if (fill.fillVertically(vertical)) { + node.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, VALUE_FILL_PARENT); + } + } + } + /** A possible match position */ private class MatchPos { /** The pixel distance */ diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ListViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ListViewRule.java index 67a3cf3..347057a 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ListViewRule.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ListViewRule.java @@ -16,10 +16,9 @@ package com.android.ide.common.layout; +import com.android.ide.common.api.INode; import com.android.ide.common.api.IViewRule; - -import java.util.HashMap; -import java.util.Map; +import com.android.ide.common.api.InsertType; /** * An {@link IViewRule} for android.widget.ListView and all its derived classes. @@ -28,19 +27,10 @@ import java.util.Map; */ public class ListViewRule extends BaseView { - // ==== XML Creation ==== - - /** - * The default for new views is to be wrap_content on both width/height. - * However ListView is special in that ideally we want fill_parent width by - * default. - */ @Override - public Map<?, ?> getDefaultAttributes() { - // TODO: find a way to plug in the new value VALUE_MATCH_PARENT. - Map<String, String> result = new HashMap<String, String>(); - result.put(ATTR_LAYOUT_WIDTH, VALUE_FILL_PARENT); - return result; - } + public void onCreate(INode node, INode parent, InsertType insertType) { + super.onCreate(node, parent, insertType); + node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, VALUE_FILL_PARENT); + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/MapViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/MapViewRule.java new file mode 100644 index 0000000..2eff369 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/MapViewRule.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2010 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.common.layout; + +import com.android.ide.common.api.INode; +import com.android.ide.common.api.IViewRule; +import com.android.ide.common.api.InsertType; + +/** + * An {@link IViewRule} for com.google.android.maps.MapView. + * <p> + * TODO: This class should be pulled out of the ADT and bundled with the add ons + * (not the core jar but an optional tool jar) + */ +public class MapViewRule extends BaseView { + + @Override + public void onCreate(INode node, INode parent, InsertType insertType) { + super.onCreate(node, parent, insertType); + + if (insertType == InsertType.CREATE) { + node.setAttribute(ANDROID_URI, "android:apiKey", //$NON-NLS-1$ + "Your API key: see " + //$NON-NLS-1$ + "http://code.google.com/android/add-ons/google-apis/mapkey.html"); //$NON-NLS-1$ + } + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ScrollViewRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ScrollViewRule.java new file mode 100644 index 0000000..5ce8bad --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ScrollViewRule.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2010 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.common.layout; + +import com.android.ide.common.api.INode; +import com.android.ide.common.api.IViewRule; +import com.android.ide.common.api.InsertType; + +/** + * An {@link IViewRule} for android.widget.ScrollView. + */ +public class ScrollViewRule extends BaseView { + + @Override + public void onChildInserted(INode child, INode parent, InsertType insertType) { + super.onChildInserted(child, parent, insertType); + + // The child of the ScrollView should fill in both directions + child.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, VALUE_FILL_PARENT); + child.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, VALUE_FILL_PARENT); + } + + @Override + public void onCreate(INode node, INode parent, InsertType insertType) { + super.onCreate(node, parent, insertType); + + if (insertType == InsertType.CREATE) { + // Insert a default linear layout (which will in turn be registered as + // a child of this node and the create child method above will set its + // fill parent attributes, its id, etc. + node.appendChild(FQCN_LINEAR_LAYOUT); + } + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/SeekBarRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/SeekBarRule.java new file mode 100644 index 0000000..b6d184f --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/SeekBarRule.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2010 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.common.layout; + +import com.android.ide.common.api.INode; +import com.android.ide.common.api.IViewRule; +import com.android.ide.common.api.InsertType; + +/** + * An {@link IViewRule} for android.widget.SeekBar + */ +public class SeekBarRule extends BaseView { + + @Override + public void onCreate(INode node, INode parent, InsertType insertType) { + super.onCreate(node, parent, insertType); + + // A SeekBar isn't useful with wrap_content because it packs itself down to + // almost no usable width -- so just make it grow in all layouts + node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, VALUE_FILL_PARENT); + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TabHostRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TabHostRule.java index 4885bb9..f47da68 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TabHostRule.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TabHostRule.java @@ -16,7 +16,9 @@ package com.android.ide.common.layout; +import com.android.ide.common.api.INode; import com.android.ide.common.api.IViewRule; +import com.android.ide.common.api.InsertType; /** * An {@link IViewRule} for android.widget.TabHost. @@ -26,6 +28,32 @@ public class TabHostRule extends IgnoredLayoutRule { // manipulate its children via the TabHost rather than directly manipulating // the child elements yourself, e.g. via addTab() etc. - // TODO: We should add a context menu here to add tabs. We should also add - // creation code to pre-populate a tab + @Override + public void onCreate(INode node, INode parent, InsertType insertType) { + super.onCreate(node, parent, insertType); + + if (insertType == InsertType.CREATE) { + // Configure default Table setup as described in the Table tutorial + node.setAttribute(ANDROID_URI, ATTR_ID, "@android:id/tabhost"); //$NON-NLS-1$ + node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, VALUE_FILL_PARENT); + node.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, VALUE_FILL_PARENT); + + INode linear = node.appendChild(FQCN_LINEAR_LAYOUT); + linear.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, VALUE_FILL_PARENT); + linear.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, VALUE_FILL_PARENT); + linear.setAttribute(ANDROID_URI, LinearLayoutRule.ATTR_ORIENTATION, + LinearLayoutRule.VALUE_VERTICAL); + + INode tab = linear.appendChild(FQCN_TAB_WIDGET); + tab.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, VALUE_FILL_PARENT); + tab.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, VALUE_WRAP_CONTENT); + tab.setAttribute(ANDROID_URI, ATTR_ID, "@android:id/tabs"); //$NON-NLS-1$ + + INode frame = linear.appendChild(FQCN_FRAME_LAYOUT); + frame.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, VALUE_FILL_PARENT); + frame.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, VALUE_FILL_PARENT); + frame.setAttribute(ANDROID_URI, ATTR_ID, "@android:id/tabcontent"); //$NON-NLS-1$ + } + } + } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TableLayoutRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TableLayoutRule.java new file mode 100644 index 0000000..a4d528c --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TableLayoutRule.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2010 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.common.layout; + +import com.android.ide.common.api.IMenuCallback; +import com.android.ide.common.api.INode; +import com.android.ide.common.api.IViewRule; +import com.android.ide.common.api.InsertType; +import com.android.ide.common.api.MenuAction; + +import java.util.List; + +/** + * An {@link IViewRule} for android.widget.TableLayout. + */ +public class TableLayoutRule extends LinearLayoutRule { + // A table is a linear layout, but with a few differences: + // the default is vertical, not horizontal + // The fill of all children should be wrap_content + + @Override + protected boolean isVertical(INode node) { + // Tables are always vertical + return true; + } + + @Override + protected boolean supportsOrientation() { + return false; + } + + @Override + public void onChildInserted(INode node, INode parent, InsertType insertType) { + // Overridden to inhibit the setting of layout_width/layout_height since + // it should always be match_parent + } + + /** + * Add an explicit "Add Row" action to the context menu + */ + @Override + public List<MenuAction> getContextMenu(final INode selectedNode) { + IMenuCallback addTab = new IMenuCallback() { + public void action(MenuAction action, final String valueId, Boolean newValue) { + final INode node = selectedNode; + node.appendChild(FQCN_TABLE_ROW); + + } + }; + return concatenate(super.getContextMenu(selectedNode), + new MenuAction.Action("_addrow", "Add Row", //$NON-NLS-1$ + null, addTab)); + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TableRowRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TableRowRule.java new file mode 100644 index 0000000..e734f4a --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/TableRowRule.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2010 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.common.layout; + +import com.android.ide.common.api.INode; +import com.android.ide.common.api.IViewRule; +import com.android.ide.common.api.InsertType; + +/** + * An {@link IViewRule} for android.widget.TableRow. + */ +public class TableRowRule extends LinearLayoutRule { + @Override + protected boolean isVertical(INode node) { + return false; + } + + @Override + protected boolean supportsOrientation() { + return false; + } + + @Override + public void onChildInserted(INode child, INode parent, InsertType insertType) { + // Overridden to inhibit the setting of layout_width/layout_height since + // the table row will enforce match_parent and wrap_content for width and height + // respectively. + } + +} diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AndroidConstants.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AndroidConstants.java index 04abe34..9c31b11 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AndroidConstants.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/AndroidConstants.java @@ -127,10 +127,10 @@ public class AndroidConstants { /** Absolute path of the workspace root, i.e. "/" */ public final static String WS_ROOT = WS_SEP; - /** Absolute path of the resource folder, eg "/res".<br> This is a workspace path. */ + /** Absolute path of the resource folder, e.g. "/res".<br> This is a workspace path. */ public final static String WS_RESOURCES = WS_SEP + SdkConstants.FD_RESOURCES; - /** Absolute path of the resource folder, eg "/assets".<br> This is a workspace path. */ + /** Absolute path of the resource folder, e.g. "/assets".<br> This is a workspace path. */ public final static String WS_ASSETS = WS_SEP + SdkConstants.FD_ASSETS; /** Leaf of the javaDoc folder. Does not start with a separator. */ @@ -212,7 +212,7 @@ public class AndroidConstants { public final static String MARKER_ATTR_TYPE_PROVIDER = "provider"; //$NON-NLS-1$ /** - * Prefered compiler level, i.e. "1.5". + * Preferred compiler level, i.e. "1.5". */ public final static String COMPILER_COMPLIANCE_PREFERRED = "1.5"; //$NON-NLS-1$ /** diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/IconFactory.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/IconFactory.java index a899bba..0c10363 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/IconFactory.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/IconFactory.java @@ -198,7 +198,7 @@ public class IconFactory { gc.setAntialias(SWT.ON); gc.setTextAntialias(SWT.ON); - // image.setBackground() does not appear to have any affect; we must explicitly + // image.setBackground() does not appear to have any effect; we must explicitly // paint into the image the background color we want masked out later. // HOWEVER, alpha transparency does not work; we only get to mark a single color // as transparent. You might think we could pick a system color (to avoid having diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/IGraphicalLayoutEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/IGraphicalLayoutEditor.java index 3a22b64..c200e47 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/IGraphicalLayoutEditor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/IGraphicalLayoutEditor.java @@ -22,6 +22,7 @@ import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; import com.android.layoutlib.api.LayoutScene; import org.eclipse.core.resources.IFile; +import org.eclipse.draw2d.geometry.Rectangle; import org.eclipse.ui.IEditorPart; import java.util.Set; @@ -96,8 +97,10 @@ public interface IGraphicalLayoutEditor extends IEditorPart { * * @param model the model to be rendered, which can be different than the editor's own * {@link #getModel()}. - * @param width the width to use for the layout - * @param height the height to use for the layout + * @param width the width to use for the layout, or -1 to use the width of the screen + * associated with this editor + * @param height the height to use for the layout, or -1 to use the height of the screen + * associated with this editor * @param explodeNodes a set of nodes to explode, or null for none * @param transparentBackground If true, the rendering will <b>not</b> paint the * normal background requested by the theme, and it will instead paint the @@ -107,4 +110,10 @@ public interface IGraphicalLayoutEditor extends IEditorPart { abstract LayoutScene render(UiDocumentNode model, int width, int height, Set<UiElementNode> explodeNodes, boolean transparentBackground); + /** + * Returns the current bounds of the Android device screen, in canvas control pixels + * + * @return the bounds of the screen, never null + */ + abstract Rectangle getScreenBounds(); } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditor.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditor.java index ed8a205..41bf099 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditor.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/LayoutEditor.java @@ -28,6 +28,7 @@ import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewEleme import com.android.ide.eclipse.adt.internal.editors.layout.gle2.GraphicalEditorPart; import com.android.ide.eclipse.adt.internal.editors.layout.gle2.OutlinePage2; import com.android.ide.eclipse.adt.internal.editors.layout.gle2.PropertySheetPage2; +import com.android.ide.eclipse.adt.internal.editors.layout.gre.RulesEngine; import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode; import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData; import com.android.ide.eclipse.adt.internal.sdk.Sdk; @@ -202,7 +203,7 @@ public class LayoutEditor extends AndroidXmlEditor implements IShowEditorInput, // Optional: set the default page. Eventually a default page might be // restored by selectDefaultPage() later based on the last page used by the user. // For example, to make the last page the default one (rather than the first page), - // un-comment this line: + // uncomment this line: // setActivePage(getPageCount() - 1); } @@ -604,4 +605,17 @@ public class LayoutEditor extends AndroidXmlEditor implements IShowEditorInput, return null; } + + /** + * Returns the {@link RulesEngine} associated with this editor + * + * @return the {@link RulesEngine} associated with this editor, or null + */ + public RulesEngine getRulesEngine() { + if (mGraphicalEditor instanceof GraphicalEditorPart) { + return ((GraphicalEditorPart) mGraphicalEditor).getRulesEngine(); + } + + return null; + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java index 2da2467..303c58d 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/CanvasViewInfo.java @@ -335,7 +335,7 @@ public class CanvasViewInfo implements IPropertySource { String fqcn = SimpleXmlTransfer.getFqcn(uiNode.getDescriptor()); String parentFqcn = null; - Rect bounds = new Rect(getAbsRect()); + Rect bounds = SwtUtils.toRect(getAbsRect()); Rect parentBounds = null; UiElementNode uiParent = uiNode.getUiParent(); @@ -343,7 +343,7 @@ public class CanvasViewInfo implements IPropertySource { parentFqcn = SimpleXmlTransfer.getFqcn(uiParent.getDescriptor()); } if (getParent() != null) { - parentBounds = new Rect(getParent().getAbsRect()); + parentBounds = SwtUtils.toRect(getParent().getAbsRect()); } SimpleElement e = new SimpleElement(fqcn, parentFqcn, bounds, parentBounds); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DynamicContextMenu.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DynamicContextMenu.java index 66f2327..a514447 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DynamicContextMenu.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/DynamicContextMenu.java @@ -179,6 +179,9 @@ import java.util.regex.Pattern; contrib = createDynamicChoices( (MenuAction.Choices)firstAction, choiceMap, actionsMap); } + } else { + // Must be a plain action + contrib = createDynamicAction(firstAction, actionsMap); } if (contrib != null) { @@ -328,7 +331,7 @@ import java.util.regex.Pattern; mEditor.wrapUndoEditXmlModel(label, new Runnable() { public void run() { - // Invoke the closures of all the actions using the same action-id + // Invoke the callbacks of all the actions using the same action-id for (MenuAction a2 : actions) { if (a2 instanceof MenuAction.Action) { IMenuCallback c = ((MenuAction.Action) a2).getCallback(); @@ -354,6 +357,66 @@ import java.util.regex.Pattern; /** * Invoked by {@link #populateDynamicContextMenu()} to create a new menu item + * for a plain action. This is nearly identical to {@link #createDynamicMenuToggle}, + * except for the {@link IAction} type and the removal of setChecked, isChecked, etc. + * + * @param firstAction The action to convert to a menu item. In the case of a + * multiple selection, this is the first of many similar actions. + * @param actionsMap Map of all contributed actions. + * @return a new {@link IContributionItem} to add to the context menu + */ + private IContributionItem createDynamicAction( + final MenuAction.Action firstAction, + final TreeMap<String, ArrayList<MenuAction>> actionsMap) { + + Action a = new Action(firstAction.getTitle(), IAction.AS_PUSH_BUTTON) { + @Override + public void run() { + final List<MenuAction> actions = actionsMap.get(firstAction.getId()); + if (actions == null || actions.isEmpty()) { + return; + } + + String label = actions.get(0).getTitle(); + if (actions.size() > 1) { + label += String.format(" (%d elements)", actions.size()); + } + + if (mEditor.isEditXmlModelPending()) { + // This should not be happening. + logError("Action '%s' failed: XML changes pending, document might be corrupt.", //$NON-NLS-1$ + label); + return; + } + + mEditor.wrapUndoEditXmlModel(label, new Runnable() { + public void run() { + // Invoke the callbacks of all the actions using the same action-id + for (MenuAction a2 : actions) { + if (a2 instanceof MenuAction.Action) { + IMenuCallback c = ((MenuAction.Action) a2).getCallback(); + if (c != null) { + try { + // Values do not apply for plain actions + c.action(a2, null /* valueId */, null /* newValue */); + } catch (Exception e) { + RulesEngine gre = mCanvas.getRulesEngine(); + gre.logError("XML edit operation failed: %s", e.toString()); + } + } + } + } + } + }); + } + }; + a.setId(firstAction.getId()); + + return new ActionContributionItem(a); + } + + /** + * Invoked by {@link #populateDynamicContextMenu()} to create a new menu item * for a {@link MenuAction.Choices}. * <p/> * Multiple-choices are represented by a sub-menu containing checked items. @@ -429,7 +492,7 @@ import java.util.regex.Pattern; } // We consider the item to be checked if all actions are all checked. - // This means a mixed item will be first toggled from off to on by all the closures. + // This means a mixed item will be first toggled from off to on by all the callbacks. final boolean isChecked = numOff == 0 && numOn > 0; boolean isMixed = numOff > 0 && numOn > 0; @@ -456,7 +519,7 @@ import java.util.regex.Pattern; mEditor.wrapUndoEditXmlModel(label, new Runnable() { public void run() { - // Invoke the closures of all the actions using the same action-id + // Invoke the callbacks of all the actions using the same action-id for (MenuAction a2 : actions) { if (a2 instanceof MenuAction.Action) { try { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java index 73711aa..728c089 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/GraphicalEditorPart.java @@ -899,7 +899,7 @@ public class GraphicalEditorPart extends EditorPart } if (mRulesEngine == null) { - mRulesEngine = new RulesEngine(mEditedFile.getProject()); + mRulesEngine = new RulesEngine(this, mEditedFile.getProject()); if (mCanvasViewer != null) { mCanvasViewer.getCanvas().setRulesEngine(mRulesEngine); } @@ -949,6 +949,15 @@ public class GraphicalEditorPart extends EditorPart return mLayoutEditor; } + /** + * Returns the {@link RulesEngine} associated with this editor + * + * @return the {@link RulesEngine} associated with this editor, never null + */ + public RulesEngine getRulesEngine() { + return mRulesEngine; + } + /* package */ LayoutCanvas getCanvasControl() { if (mCanvasViewer != null) { return mCanvasViewer.getCanvas(); @@ -1174,7 +1183,7 @@ public class GraphicalEditorPart extends EditorPart Set<UiElementNode> explodeNodes = canvas.getNodesToExplode(); // Compute the layout - Rectangle rect = getBounds(); + Rectangle rect = getScreenBounds(); int width = rect.width; int height = rect.height; @@ -1355,7 +1364,7 @@ public class GraphicalEditorPart extends EditorPart // FIXME: get rid of the current LayoutScene if any. } - public Rectangle getBounds() { + public Rectangle getScreenBounds() { return mConfigComposite.getScreenBounds(); } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/MoveGesture.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/MoveGesture.java index c11d00b..d36b96c 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/MoveGesture.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/MoveGesture.java @@ -17,6 +17,7 @@ package com.android.ide.eclipse.adt.internal.editors.layout.gle2; import com.android.ide.common.api.DropFeedback; import com.android.ide.common.api.INode; +import com.android.ide.common.api.InsertType; import com.android.ide.common.api.Point; import com.android.ide.common.api.Rect; import com.android.ide.eclipse.adt.AdtPlugin; @@ -316,10 +317,19 @@ public class MoveGesture extends DropGesture { String label = computeUndoLabel(mTargetNode, elements, event.detail); mCanvas.getLayoutEditor().wrapUndoEditXmlModel(label, new Runnable() { public void run() { + InsertType insertType; + if (event.detail == DND.DROP_MOVE) { + insertType = InsertType.MOVE; + } else if (GlobalCanvasDragInfo.getInstance().getSourceCanvas() != null) { + insertType = InsertType.PASTE; + } else { + insertType = InsertType.CREATE; + } mCanvas.getRulesEngine().callOnDropped(mTargetNode, elementsFinal, mFeedback, - new Point(canvasPoint.x, canvasPoint.y)); + new Point(canvasPoint.x, canvasPoint.y), + insertType); // Clean up drag if applicable if (event.detail == DND.DROP_MOVE) { GlobalCanvasDragInfo.getInstance().removeSource(); diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteComposite.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteComposite.java index c249981..6b46688 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteComposite.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/PaletteComposite.java @@ -16,15 +16,21 @@ package com.android.ide.eclipse.adt.internal.editors.layout.gle2; +import com.android.ide.common.api.InsertType; import com.android.ide.common.api.Rect; +import com.android.ide.common.layout.BaseView; import com.android.ide.common.layoutlib.LayoutLibrary; import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils; 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.descriptors.XmlnsAttributeDescriptor; import com.android.ide.eclipse.adt.internal.editors.layout.IGraphicalLayoutEditor; import com.android.ide.eclipse.adt.internal.editors.layout.LayoutConstants; 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.gre.NodeFactory; +import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy; +import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode; import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode; import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData; @@ -60,10 +66,13 @@ import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.ScrollBar; +import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.StringWriter; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; @@ -548,11 +557,18 @@ public class PaletteComposite extends Composite { // not to make it much larger than necessary since to crop this we rely on // actually scanning pixels. - /** Width of the rendered preview image (before it is cropped) */ - private static final int RENDER_HEIGHT = 400; + /** + * Width of the rendered preview image (before it is cropped), although the actual + * width may be smaller (since we also take the device screen's size into account) + */ + private static final int MAX_RENDER_HEIGHT = 400; - /** Height of the rendered preview image (before it is cropped) */ - private static final int RENDER_WIDTH = 500; + /** + * Height of the rendered preview image (before it is cropped), although the + * actual width may be smaller (since we also take the device screen's size into + * account) + */ + private static final int MAX_RENDER_WIDTH = 500; /** Amount of alpha to multiply into the image (divided by 256) */ private static final int IMG_ALPHA = 216; @@ -655,6 +671,8 @@ public class PaletteComposite extends Composite { Document document = null; DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); try { + factory.setNamespaceAware(true); + factory.setValidating(false); DocumentBuilder builder = factory.newDocumentBuilder(); document = builder.newDocument(); } catch (ParserConfigurationException e) { @@ -668,6 +686,13 @@ public class PaletteComposite extends Composite { String viewName = desc.getXmlLocalName(); Element element = document.createElement(viewName); + + // Set up a proper name space + Attr attr = document.createAttributeNS(XmlnsAttributeDescriptor.XMLNS_URI, + "xmlns:android"); //$NON-NLS-1$ + attr.setValue(BaseView.ANDROID_URI); + element.getAttributes().setNamedItemNS(attr); + element.setAttributeNS(SdkConstants.NS_RESOURCES, LayoutConstants.ATTR_LAYOUT_WIDTH, LayoutConstants.VALUE_WRAP_CONTENT); element.setAttributeNS(SdkConstants.NS_RESOURCES, @@ -679,15 +704,37 @@ public class PaletteComposite extends Composite { String text = DescriptorsUtils.getFreeWidgetId(uiRoot, viewName); element.setAttributeNS(SdkConstants.NS_RESOURCES, LayoutConstants.ATTR_TEXT, text); - document.insertBefore(element, null); + document.appendChild(element); // Construct UI model from XML - DocumentDescriptor documentDescriptor = - new DocumentDescriptor("layout_doc", null); //$NON-NLS-1$ - UiDocumentNode model = new UiDocumentNode(documentDescriptor); + AndroidTargetData data = layoutEditor.getTargetData(); + DocumentDescriptor documentDescriptor; + if (data == null) { + documentDescriptor = new DocumentDescriptor("temp", null /*children*/); //$NON-NLS-1$ + } else { + documentDescriptor = data.getLayoutDescriptors().getDescriptor(); + } + UiDocumentNode model = (UiDocumentNode) documentDescriptor.createUiNode(); model.setEditor(layoutEditor); + model.setUnknownDescriptorProvider(editor.getModel().getUnknownDescriptorProvider()); model.loadFromXmlNode(document); + // Call the create-hooks such that we for example insert mandatory + // children into views like the DialerFilter, apply image source attributes + // to ImageButtons, etc. + if (editor instanceof GraphicalEditorPart) { + LayoutCanvas canvas = ((GraphicalEditorPart) editor).getCanvasControl(); + NodeFactory nodeFactory = canvas.getNodeFactory(); + UiElementNode parent = model.getUiRoot(); + UiElementNode child = parent.getUiChildren().get(0); + if (child instanceof UiViewElementNode) { + UiViewElementNode childUiNode = (UiViewElementNode) child; + NodeProxy childNode = nodeFactory.create(childUiNode); + canvas.getRulesEngine().callCreateHooks(layoutEditor, + null, childNode, InsertType.CREATE); + } + } + boolean hasTransparency = false; LayoutLibrary layoutLibrary = editor.getLayoutLibrary(); if (layoutLibrary != null) { @@ -697,8 +744,23 @@ public class PaletteComposite extends Composite { } } - LayoutScene scene = editor.render(model, RENDER_WIDTH, RENDER_HEIGHT, + LayoutScene scene = null; + try { + // Use at most the size of the screen for the preview render. + // This is important since when we fill the size of certain views (like + // a SeekBar), we want it to at most be the width of the screen, and for small + // screens the RENDER_WIDTH was wider. + org.eclipse.draw2d.geometry.Rectangle screenBounds = editor.getScreenBounds(); + int renderWidth = Math.min(screenBounds.width, MAX_RENDER_WIDTH); + int renderHeight = Math.min(screenBounds.height, MAX_RENDER_HEIGHT); + + scene = editor.render(model, renderWidth, renderHeight, null /* explodeNodes */, hasTransparency); + } catch (Throwable t) { + // Previews can fail for a variety of reasons -- let's not bug + // the user with it + return null; + } if (scene != null) { if (scene.getResult() == SceneResult.SUCCESS) { @@ -761,6 +823,41 @@ public class PaletteComposite extends Composite { } /** + * Utility method to print out the contents of the given XML document. This is + * really useful when working on the preview code above. I'm including all the + * code inside a constant false, which means the compiler will omit all the code, + * but I'd like to leave it in the code base and by doing it this way rather than + * as commented out code the code won't be accidentally broken. + */ + @SuppressWarnings("all") + private static void dumpDocument(Document document) { + // Diagnostics: print out the XML that we're about to render + if (false) { // Will be omitted by the compiler + org.apache.xml.serialize.OutputFormat outputFormat = + new org.apache.xml.serialize.OutputFormat( + "XML", "ISO-8859-1", true); //$NON-NLS-1$ //$NON-NLS-2$ + outputFormat.setIndent(2); + outputFormat.setLineWidth(100); + outputFormat.setIndenting(true); + outputFormat.setOmitXMLDeclaration(true); + outputFormat.setOmitDocumentType(true); + StringWriter stringWriter = new StringWriter(); + // Using FQN here to avoid having an import above, which will result + // in a deprecation warning, and there isn't a way to annotate a single + // import element with a SuppressWarnings. + org.apache.xml.serialize.XMLSerializer serializer = + new org.apache.xml.serialize.XMLSerializer(stringWriter, outputFormat); + serializer.setNamespaces(true); + try { + serializer.serialize(document.getDocumentElement()); + System.out.println(stringWriter.toString()); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + /** * Returns the image shown as the drag source effect. The image may be a preview * of the palette item, or just a placeholder image; {@link #isPlaceholder()} can * tell the difference. diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtils.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtils.java index 6c55dd3..c2c46f1 100644 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtils.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtils.java @@ -15,9 +15,12 @@ */ package com.android.ide.eclipse.adt.internal.editors.layout.gle2; +import com.android.ide.common.api.Rect; + import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.ImageData; import org.eclipse.swt.graphics.PaletteData; +import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Display; import java.awt.image.BufferedImage; @@ -109,4 +112,38 @@ public class SwtUtils { return awtImage; } + + /** + * Converts the given SWT {@link Rectangle} into an ADT {@link Rect} + * + * @param swtRect the SWT {@link Rectangle} + * @return an equivalent {@link Rect} + */ + public static Rect toRect(Rectangle swtRect) { + return new Rect(swtRect.x, swtRect.y, swtRect.width, swtRect.height); + } + + /** + * Sets the values of the given ADT {@link Rect} to the values of the given SWT + * {@link Rectangle} + * + * @param target the ADT {@link Rect} to modify + * @param source the SWT {@link Rectangle} to read values from + */ + public static void set(Rect target, Rectangle source) { + target.set(source.x, source.y, source.width, source.height); + } + + /** + * Compares an ADT {@link Rect} with an SWT {@link Rectangle} and returns true if they + * are equivalent + * + * @param r1 the ADT {@link Rect} + * @param r2 the SWT {@link Rectangle} + * @return true if the two rectangles are equivalent + */ + public static boolean equals(Rect r1, Rectangle r2) { + return r1.x == r2.x && r1.y == r2.y && r1.w == r2.width && r1.h == r2.height; + + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeFactory.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeFactory.java index 825a0e4..e608377 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeFactory.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeFactory.java @@ -18,6 +18,7 @@ package com.android.ide.eclipse.adt.internal.editors.layout.gre; import com.android.ide.common.api.INode; import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CanvasViewInfo; +import com.android.ide.eclipse.adt.internal.editors.layout.gle2.SwtUtils; import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode; import org.eclipse.swt.graphics.Rectangle; @@ -67,7 +68,7 @@ public class NodeFactory { proxy = new NodeProxy(uiNode, bounds, this); mNodeMap.put(uiNode, proxy); - } else if (bounds != null && !proxy.getBounds().equals(bounds)) { + } else if (bounds != null && !SwtUtils.equals(proxy.getBounds(), bounds)) { // Update the bounds if necessary proxy.setBounds(bounds); } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java index 3916eaa..59c6e15 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/NodeProxy.java @@ -29,6 +29,7 @@ import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescripto import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor; 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.SwtUtils; import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode; import com.android.ide.eclipse.adt.internal.editors.uimodel.UiAttributeNode; import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode; @@ -71,7 +72,7 @@ public class NodeProxy implements INode { if (bounds == null) { mBounds = new Rect(); } else { - mBounds = new Rect(bounds); + mBounds = SwtUtils.toRect(bounds); } } @@ -85,7 +86,7 @@ public class NodeProxy implements INode { * This is a package-protected method, only the {@link NodeFactory} uses this method. */ /*package*/ void setBounds(Rectangle bounds) { - mBounds.set(bounds); + SwtUtils.set(mBounds, bounds); } /** @@ -195,38 +196,14 @@ public class NodeProxy implements INode { } public INode appendChild(String viewFqcn) { - checkEditOK(); - - // Find the descriptor for this FQCN - ViewElementDescriptor vd = getFqcnViewDescriptor(viewFqcn); - if (vd == null) { - warnPrintf("Can't create a new %s element", viewFqcn); - return null; - } - - // Append at the end. - UiElementNode uiNew = mNode.appendNewUiChild(vd); - - // TODO we probably want to defer that to the GRE to use IViewRule#getDefaultAttributes() - DescriptorsUtils.setDefaultLayoutAttributes(uiNew, false /*updateLayout*/); - - Node xmlNode = uiNew.createXmlNode(); - - if (!(uiNew instanceof UiViewElementNode) || xmlNode == null) { - // Both things are not supposed to happen. When they do, we're in big trouble. - // We don't really know how to revert the state at this point and the UI model is - // now out of sync with the XML model. - // Panic ensues. - // The best bet is to abort now. The edit wrapper will release the edit and the - // XML/UI should get reloaded properly (with a likely invalid XML.) - warnPrintf("Failed to create a new %s element", viewFqcn); - throw new RuntimeException("XML node creation failed."); //$NON-NLS-1$ - } - - return mFactory.create((UiViewElementNode) uiNew); + return insertOrAppend(viewFqcn, -1); } public INode insertChildAt(String viewFqcn, int index) { + return insertOrAppend(viewFqcn, index); + } + + private INode insertOrAppend(String viewFqcn, int index) { checkEditOK(); // Find the descriptor for this FQCN @@ -236,13 +213,18 @@ public class NodeProxy implements INode { return null; } - // Insert at the requested position or at the end. - int n = mNode.getUiChildren().size(); - UiElementNode uiNew = null; - if (index < 0 || index >= n) { + final UiElementNode uiNew; + if (index == -1) { + // Append at the end. uiNew = mNode.appendNewUiChild(vd); } else { - uiNew = mNode.insertNewUiChild(index, vd); + // Insert at the requested position or at the end. + int n = mNode.getUiChildren().size(); + if (index < 0 || index >= n) { + uiNew = mNode.appendNewUiChild(vd); + } else { + uiNew = mNode.insertNewUiChild(index, vd); + } } // TODO we probably want to defer that to the GRE to use IViewRule#getDefaultAttributes() @@ -261,7 +243,18 @@ public class NodeProxy implements INode { throw new RuntimeException("XML node creation failed."); //$NON-NLS-1$ } - return mFactory.create((UiViewElementNode) uiNew); + UiViewElementNode uiNewView = (UiViewElementNode) uiNew; + NodeProxy newNode = mFactory.create(uiNewView); + + AndroidXmlEditor editor = mNode.getEditor(); + if (editor instanceof LayoutEditor) { + RulesEngine engine = ((LayoutEditor)editor).getRulesEngine(); + if (engine != null) { + engine.callCreateHooks(editor, this, newNode, null); + } + } + + return newNode; } public boolean setAttribute(String uri, String name, String value) { diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java index 7856cd9..a14d812 100755 --- a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/RulesEngine.java @@ -22,12 +22,16 @@ import com.android.ide.common.api.IDragElement; import com.android.ide.common.api.IGraphics; import com.android.ide.common.api.INode; import com.android.ide.common.api.IValidator; +import com.android.ide.common.api.IViewMetadata; import com.android.ide.common.api.IViewRule; +import com.android.ide.common.api.InsertType; import com.android.ide.common.api.MenuAction; import com.android.ide.common.api.Point; import com.android.ide.common.layout.ViewRule; import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor; import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; +import com.android.ide.eclipse.adt.internal.editors.layout.IGraphicalLayoutEditor; import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor; import com.android.ide.eclipse.adt.internal.editors.layout.gle2.SimpleElement; import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode; @@ -63,6 +67,15 @@ import java.util.Map; public class RulesEngine { private final IProject mProject; private final Map<Object, IViewRule> mRulesCache = new HashMap<Object, IViewRule>(); + /** + * The type of any upcoming node manipulations performed by the {@link IViewRule}s. + * When actions are performed in the tool (like a paste action, or a drag from palette, + * or a drag move within the canvas, etc), these are different types of inserts, + * and we don't want to have the rules track them closely (and pass them back to us + * in the {@link INode#insertChildAt} methods etc), so instead we track the state + * here on behalf of the currently executing rule. + */ + private InsertType mInsertType = InsertType.CREATE; /** * Class loader (or null) used to load user/project-specific IViewRule @@ -77,14 +90,21 @@ public class RulesEngine { private boolean mUserClassLoaderInited; /** + * The editor which owns this {@link RulesEngine} + */ + private IGraphicalLayoutEditor mEditor; + + /** * Creates a new {@link RulesEngine} associated with the selected project. * <p/> * The rules engine will look in the project for a tools jar to load custom view rules. * + * @param editor the editor which owns this {@link RulesEngine} * @param project A non-null open project. */ - public RulesEngine(IProject project) { + public RulesEngine(IGraphicalLayoutEditor editor, IProject project) { mProject = project; + mEditor = editor; } /** @@ -194,6 +214,7 @@ public class RulesEngine { if (rule != null) { try { + mInsertType = InsertType.CREATE; return rule.getContextMenu(selectedNode); } catch (Exception e) { @@ -335,12 +356,14 @@ public class RulesEngine { public void callOnDropped(NodeProxy targetNode, IDragElement[] elements, DropFeedback feedback, - Point where) { + Point where, + InsertType insertType) { // try to find a rule for this element's FQCN IViewRule rule = loadRule(targetNode.getNode()); if (rule != null) { try { + mInsertType = insertType; rule.onDropped(targetNode, elements, feedback, where); } catch (Exception e) { @@ -379,6 +402,7 @@ public class RulesEngine { if (rule != null) { try { + mInsertType = InsertType.PASTE; rule.onPaste(targetNode, pastedElements); } catch (Exception e) { @@ -389,6 +413,74 @@ public class RulesEngine { } } + // ---- Creation customizations ---- + + /** + * Invokes the create hooks ({@link IViewRule#onCreate}, + * {@link IViewRule#onChildInserted} when a new child has been created/pasted/moved, and + * is inserted into a given parent. The parent may be null (for example when rendering + * top level items for preview). + * + * @param editor the XML editor to apply edits to the model for (performed by view + * rules) + * @param parentNode the parent XML node, or null if unknown + * @param childNode the XML node of the new node, never null + * @param overrideInsertType If not null, specifies an explicit insert type to use for + * edits made during the customization + */ + public void callCreateHooks( + AndroidXmlEditor editor, + NodeProxy parentNode, NodeProxy childNode, + InsertType overrideInsertType) { + IViewRule parentRule = null; + + if (parentNode != null) { + UiViewElementNode parentUiNode = parentNode.getNode(); + parentRule = loadRule(parentUiNode); + } + + if (overrideInsertType != null) { + mInsertType = overrideInsertType; + } + + UiViewElementNode newUiNode = childNode.getNode(); + IViewRule childRule = loadRule(newUiNode); + if (childRule != null || parentRule != null) { + callCreateHooks(editor, mInsertType, parentRule, parentNode, + childRule, childNode); + } + } + + private static void callCreateHooks( + final AndroidXmlEditor editor, final InsertType insertType, + final IViewRule parentRule, final INode parentNode, + final IViewRule childRule, final INode newNode) { + // Notify the parent about the new child in case it wants to customize it + // (For example, a ScrollView parent can go and set all its children's layout params to + // fill the parent.) + if (!editor.isEditXmlModelPending()) { + editor.wrapUndoEditXmlModel("Customize creation", new Runnable() { + public void run() { + callCreateHooks(editor, insertType, + parentRule, parentNode, childRule, newNode); + } + }); + return; + } + + if (parentRule != null) { + parentRule.onChildInserted(newNode, parentNode, insertType); + } + + // Look up corresponding IViewRule, and notify the rule about + // this create action in case it wants to customize the new object. + // (For example, a rule for TabHosts can go and create a default child tab + // when you create it.) + if (childRule != null) { + childRule.onCreate(newNode, parentNode, insertType); + } + } + // ---- private --- /** @@ -531,7 +623,11 @@ public class RulesEngine { // than in the same package as the widgets. String ruleClassName; ClassLoader classLoader; - if (realFqcn.startsWith("android.")) { //$NON-NLS-1$ + if (realFqcn.startsWith("android.") || //$NON-NLS-1$ + // FIXME: Remove this special case as soon as we pull + // the MapViewRule out of this code base and bundle it + // with the add ons + realFqcn.startsWith("com.google.android.maps.")) { //$NON-NLS-1$ // This doesn't handle a case where there are name conflicts // (e.g. where there are multiple different views with the same // class name and only differing in package names, but that's a @@ -697,6 +793,9 @@ public class RulesEngine { } return null; } - } + public IViewMetadata getMetadata(final String fqcn) { + return new ViewMetadata(mEditor.getLayoutEditor(), fqcn); + } + } } diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ViewMetadata.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ViewMetadata.java new file mode 100644 index 0000000..eeebad7 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gre/ViewMetadata.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2010 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; + +/** + * An implementation of {@link IViewMetadata} which consults the + * SDK descriptors to answer metadata questions about views. + */ +import com.android.ide.common.api.IViewMetadata; +import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor; +import com.android.ide.eclipse.adt.internal.editors.layout.IGraphicalLayoutEditor; +import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor; + +import java.util.HashMap; +import java.util.Map; + +final class ViewMetadata implements IViewMetadata { + /** The {@link ElementDescriptor} for this view, computed lazily */ + private ElementDescriptor mDescriptor; + + /** The fully qualified class name of the view whose metadata this class represents */ + private String mFqcn; + + /** The {@link IGraphicalLayoutEditor} associated with the view we're looking up */ + private LayoutEditor mEditor; + + /** + * A map from class names to {@link FillPreference} which indicates how each view + * prefers to grow when added in various layout contexts + */ + private static final Map<String,FillPreference> mFill = new HashMap<String,FillPreference>(); + static { + // Hardcoded metadata about fill preferences for various known views. We should + // work to try to get this into the platform as designtime annotations. + + mFill.put("android.widget.EditText", FillPreference.WIDTH_IN_VERTICAL); //$NON-NLS-1$ + mFill.put("android.widget.DialerFilter", FillPreference.WIDTH_IN_VERTICAL); //$NON-NLS-1$ + mFill.put("android.widget.SeekBar", FillPreference.WIDTH_IN_VERTICAL); //$NON-NLS-1$ + mFill.put("android.widget.Spinner", FillPreference.WIDTH_IN_VERTICAL); //$NON-NLS-1$ + mFill.put("android.widget.AutoComplete", FillPreference.WIDTH_IN_VERTICAL); //$NON-NLS-1$ + mFill.put("android.widget.ListView", FillPreference.WIDTH_IN_VERTICAL); //$NON-NLS-1$ + mFill.put("android.widget.GridView", FillPreference.OPPOSITE); //$NON-NLS-1$ + mFill.put("android.widget.Gallery", FillPreference.WIDTH_IN_VERTICAL); //$NON-NLS-1$ + mFill.put("android.widget.TabWidget", FillPreference.WIDTH_IN_VERTICAL); //$NON-NLS-1$ + mFill.put("android.widget.MapView", FillPreference.OPPOSITE); //$NON-NLS-1$ + mFill.put("android.widget.WebView", FillPreference.OPPOSITE); //$NON-NLS-1$ + + // In addition, all layouts are FillPreference.OPPOSITE - these are computed + // lazily rather than enumerating them here + + // For any other view, the fallback fill preference is FillPreference.NONE. + } + + public ViewMetadata(LayoutEditor editor, String fqcn) { + super(); + mFqcn = fqcn; + mEditor = editor; + } + + /** Lazily look up the descriptor for the FQCN of this metadata object */ + private boolean findDescriptor() { + if (mDescriptor == null) { + // Look up the corresponding view element node. We don't need the graphical part; + // we just need the project context. Maybe I should extract this code into + // a utility. + mDescriptor = mEditor.getFqcnViewDescriptor(mFqcn); + } + + return mDescriptor != null; + } + + public boolean isParent() { + if (findDescriptor()) { + return mDescriptor.hasChildren(); + } + + return false; + } + + public String getDisplayName() { + if (findDescriptor()) { + return mDescriptor.getUiName(); + } + + return mFqcn.substring(mFqcn.lastIndexOf('.') + 1); // This also works when there is no "." + } + + public String getTooltip() { + if (findDescriptor()) { + return mDescriptor.getTooltip(); + } + + return null; + } + + /** Returns true if this view represents a layout */ + private boolean isLayout() { + if (findDescriptor()) { + return mDescriptor.hasChildren(); + } + return false; + } + + public FillPreference getFillPreference() { + FillPreference fillPreference = mFill.get(mFqcn); + if (fillPreference == null) { + if (isLayout()) { + fillPreference = FillPreference.OPPOSITE; + } else { + fillPreference = FillPreference.NONE; + } + mFill.put(mFqcn, fillPreference); + } + + return fillPreference; + } +} diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/api/RectTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/api/RectTest.java index 043c9ee..36fa743 100755 --- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/api/RectTest.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/api/RectTest.java @@ -16,11 +16,6 @@ package com.android.ide.common.api; -import com.android.ide.common.api.Point; -import com.android.ide.common.api.Rect; - -import org.eclipse.swt.graphics.Rectangle; - import junit.framework.TestCase; public class RectTest extends TestCase { @@ -54,16 +49,6 @@ public class RectTest extends TestCase { assertEquals(r2, r); } - public final void testRectRectangle() { - Rectangle r = new Rectangle(3, 4, 20, 30); - Rect r2 = new Rect(r); - - assertEquals(3, r2.x); - assertEquals(4, r2.y); - assertEquals(20, r2.w); - assertEquals(30, r2.h); - } - public final void testSetIntIntIntInt() { Rect r = new Rect(1, 2, 3, 4); Rect r2 = r.set(3, 4, 20, 30); @@ -88,19 +73,6 @@ public class RectTest extends TestCase { assertEquals(30, r.h); } - public final void testSetRectangle() { - Rect r = new Rect(1, 2, 3, 4); - Rectangle r2 = new Rectangle(3, 4, 20, 30); - Rect r3 = r.set(r2); - - assertSame(r3, r); - assertNotSame(r3, r2); - assertEquals(3, r.x); - assertEquals(4, r.y); - assertEquals(20, r.w); - assertEquals(30, r.h); - } - public final void testCopy() { Rect r = new Rect(1, 2, 3, 4); Rect r2 = r.copy(); diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/AbsoluteLayoutRuleTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/AbsoluteLayoutRuleTest.java index 2f4e22f..7853410 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/AbsoluteLayoutRuleTest.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/common/layout/AbsoluteLayoutRuleTest.java @@ -54,10 +54,10 @@ public class AbsoluteLayoutRuleTest extends AbstractLayoutRuleTest { "useStyle(DROP_RECIPIENT), drawRect(Rect[0,0,240,480])", // Drop preview - "useStyle(DROP_PREVIEW), drawRect(Rect[30,-10,105,80])"); + "useStyle(DROP_PREVIEW), drawRect(Rect[-22,-50,105,80])"); - assertEquals("30dip", inserted.getStringAttr(BaseLayout.ANDROID_URI, "layout_x")); - assertEquals("-10dip", inserted.getStringAttr(BaseLayout.ANDROID_URI, "layout_y")); + assertEquals("-22dip", inserted.getStringAttr(BaseLayout.ANDROID_URI, "layout_x")); + assertEquals("-50dip", inserted.getStringAttr(BaseLayout.ANDROID_URI, "layout_y")); // Without drag bounds we should just draw guide lines instead inserted = dragInto(new Rect(0, 0, 0, 0), new Point(30, -10), 4, -1, diff --git a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtilsTest.java b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtilsTest.java index e383796..c575f46 100644 --- a/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtilsTest.java +++ b/eclipse/plugins/com.android.ide.eclipse.tests/unittests/com/android/ide/eclipse/adt/internal/editors/layout/gle2/SwtUtilsTest.java @@ -16,8 +16,11 @@ package com.android.ide.eclipse.adt.internal.editors.layout.gle2; +import com.android.ide.common.api.Rect; + import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; @@ -134,4 +137,25 @@ public class SwtUtilsTest extends TestCase { } } + public final void testSetRectangle() { + Rect r = new Rect(1, 2, 3, 4); + Rectangle r2 = new Rectangle(3, 4, 20, 30); + SwtUtils.set(r, r2); + + assertEquals(3, r.x); + assertEquals(4, r.y); + assertEquals(20, r.w); + assertEquals(30, r.h); + } + + public final void testRectRectangle() { + Rectangle r = new Rectangle(3, 4, 20, 30); + Rect r2 = SwtUtils.toRect(r); + + assertEquals(3, r2.x); + assertEquals(4, r2.y); + assertEquals(20, r2.w); + assertEquals(30, r2.h); + } + } |