OSDN Git Service

Guidelines for match_parent, and linear layout weight fixes
authorTor Norbye <tnorbye@google.com>
Mon, 6 Jun 2011 19:39:25 +0000 (12:39 -0700)
committerTor Norbye <tnorbye@google.com>
Tue, 7 Jun 2011 00:44:18 +0000 (17:44 -0700)
First, add guidelines to allow snapping to "match_parent" (or
fill_parent, depending on the API level).

Second, fix the linear layout resizing scheme to handle corner cases a
bit better (corner cases such as resizing to a smaller size than the
wrap_content bounds, or resizing inside a layout that is "full"). Also
split up the resizing code into a compute-method and an apply-method
such that we can display feedback for the current weight during the
resizing operation.

Change-Id: Idd2917230870d26f94473dabc1a2a6becc3ba738

eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/AbsoluteLayoutRule.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/BaseLayoutRule.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/LinearLayoutRule.java
eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ResizeState.java [new file with mode: 0644]

index 111434f..de03a19 100644 (file)
@@ -20,7 +20,6 @@ import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI;
 import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_X;
 import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_Y;
 import static com.android.ide.common.layout.LayoutConstants.VALUE_N_DP;
-import static com.android.ide.common.layout.LayoutConstants.VALUE_WRAP_CONTENT;
 
 import com.android.ide.common.api.DrawingStyle;
 import com.android.ide.common.api.DropFeedback;
@@ -239,9 +238,6 @@ public class AbsoluteLayoutRule extends BaseLayoutRule {
         return String.format("Set bounds to (x = %d, y = %d, width = %s, height = %s)",
                 mRulesEngine.pxToDp(newBounds.x - parentBounds.x),
                 mRulesEngine.pxToDp(newBounds.y - parentBounds.y),
-                resizeState.wrapWidth ?
-                        VALUE_WRAP_CONTENT : Integer.toString(mRulesEngine.pxToDp(newBounds.w)),
-                resizeState.wrapHeight ?
-                        VALUE_WRAP_CONTENT : Integer.toString(mRulesEngine.pxToDp(newBounds.h)));
+                resizeState.getWidthAttribute(), resizeState.getHeightAttribute());
     }
 }
index 4e6ea98..fa87fa4 100644 (file)
@@ -28,7 +28,6 @@ import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_WIDTH;
 import static com.android.ide.common.layout.LayoutConstants.ATTR_TEXT;
 import static com.android.ide.common.layout.LayoutConstants.VALUE_FILL_PARENT;
 import static com.android.ide.common.layout.LayoutConstants.VALUE_MATCH_PARENT;
-import static com.android.ide.common.layout.LayoutConstants.VALUE_N_DP;
 import static com.android.ide.common.layout.LayoutConstants.VALUE_WRAP_CONTENT;
 
 import com.android.ide.common.api.DrawingStyle;
@@ -44,10 +43,12 @@ import com.android.ide.common.api.INodeHandler;
 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.Segment;
 import com.android.ide.common.api.SegmentType;
 import com.android.ide.common.api.IAttributeInfo.Format;
 import com.android.ide.common.api.IDragElement.IDragAttribute;
 import com.android.ide.common.api.MenuAction.ChoiceProvider;
+import com.android.ide.common.layout.relative.MarginType;
 import com.android.sdklib.SdkConstants;
 import com.android.util.Pair;
 
@@ -575,31 +576,15 @@ public class BaseLayoutRule extends BaseViewRule {
 
     // ---- Resizing ----
 
-    /** State held during resizing operations */
-    protected static class ResizeState {
-        /** The proposed resized bounds of the node */
-        public Rect bounds;
-
-        /** The preferred wrap_content bounds of the node */
-        public Rect wrapBounds;
-
-        /** The type of horizontal edge being resized, or null */
-        public SegmentType horizontalEdgeType;
-
-        /** The type of vertical edge being resized, or null */
-        public SegmentType verticalEdgeType;
-
-        /** Whether the user has snapped to the wrap_content width */
-        public boolean wrapWidth;
-
-        /** Whether the user has snapped to the wrap_content height */
-        public boolean wrapHeight;
+    /** Creates a new {@link ResizeState} object to track resize state */
+    protected ResizeState createResizeState(INode layout, INode node) {
+        return new ResizeState(this, layout, node);
     }
 
     @Override
     public DropFeedback onResizeBegin(INode child, INode parent,
             SegmentType horizontalEdge, SegmentType verticalEdge) {
-        ResizeState state = new ResizeState();
+        ResizeState state = createResizeState(parent, child);
         state.horizontalEdgeType = horizontalEdge;
         state.verticalEdgeType = verticalEdge;
 
@@ -633,6 +618,17 @@ public class BaseLayoutRule extends BaseViewRule {
                     Rect b = resizeState.bounds;
                     gc.drawRect(b);
 
+                    if (resizeState.horizontalFillSegment != null) {
+                        gc.useStyle(DrawingStyle.GUIDELINE);
+                        Segment s = resizeState.horizontalFillSegment;
+                        gc.drawLine(s.from, s.at, s.to, s.at);
+                    }
+                    if (resizeState.verticalFillSegment != null) {
+                        gc.useStyle(DrawingStyle.GUIDELINE);
+                        Segment s = resizeState.verticalFillSegment;
+                        gc.drawLine(s.at, s.from, s.at, s.to);
+                    }
+
                     if (resizeState.wrapBounds != null) {
                         gc.useStyle(DrawingStyle.GUIDELINE);
                         int wrapWidth = resizeState.wrapBounds.w;
@@ -703,7 +699,6 @@ public class BaseLayoutRule extends BaseViewRule {
         return 20;
     }
 
-
     @Override
     public void onResizeUpdate(DropFeedback feedback, INode child, INode parent,
             Rect newBounds, int modifierMask) {
@@ -735,6 +730,32 @@ public class BaseLayoutRule extends BaseViewRule {
             }
         }
 
+        // Match on fill bounds
+        state.horizontalFillSegment = null;
+        state.fillHeight = false;
+        if (state.horizontalEdgeType == SegmentType.BOTTOM && !state.wrapHeight) {
+            Rect parentBounds = parent.getBounds();
+            state.horizontalFillSegment = new Segment(parentBounds.y2(), newBounds.x,
+                newBounds.x2(),
+                null /*node*/, null /*id*/, SegmentType.BOTTOM, MarginType.NO_MARGIN);
+            if (Math.abs(newBounds.y2() - parentBounds.y2()) < getMaxMatchDistance()) {
+                state.fillHeight = true;
+                newBounds.h = parentBounds.y2() - newBounds.y;
+            }
+        }
+        state.verticalFillSegment = null;
+        state.fillWidth = false;
+        if (state.verticalEdgeType == SegmentType.RIGHT && !state.wrapWidth) {
+            Rect parentBounds = parent.getBounds();
+            state.verticalFillSegment = new Segment(parentBounds.x2(), newBounds.y,
+                newBounds.y2(),
+                null /*node*/, null /*id*/, SegmentType.RIGHT, MarginType.NO_MARGIN);
+            if (Math.abs(newBounds.x2() - parentBounds.x2()) < getMaxMatchDistance()) {
+                state.fillWidth = true;
+                newBounds.w = parentBounds.x2() - newBounds.x;
+            }
+        }
+
         feedback.message = getResizeUpdateMessage(state, child, parent,
                 newBounds, state.horizontalEdgeType, state.verticalEdgeType);
     }
@@ -767,10 +788,8 @@ public class BaseLayoutRule extends BaseViewRule {
      */
     protected String getResizeUpdateMessage(ResizeState resizeState, INode child, INode parent,
             Rect newBounds, SegmentType horizontalEdge, SegmentType verticalEdge) {
-        String width = resizeState.wrapWidth ? VALUE_WRAP_CONTENT :
-                    String.format(VALUE_N_DP, mRulesEngine.pxToDp(newBounds.w));
-        String height = resizeState.wrapHeight ? VALUE_WRAP_CONTENT :
-            String.format(VALUE_N_DP, mRulesEngine.pxToDp(newBounds.h));
+        String width = resizeState.getWidthAttribute();
+        String height = resizeState.getHeightAttribute();
 
         // U+00D7: Unicode for multiplication sign
         return String.format("Resize to %s \u00D7 %s", width, height);
@@ -790,15 +809,13 @@ public class BaseLayoutRule extends BaseViewRule {
      */
     protected void setNewSizeBounds(ResizeState resizeState, INode node, INode layout,
             Rect oldBounds, Rect newBounds, SegmentType horizontalEdge, SegmentType verticalEdge) {
-        if (verticalEdge != null && (newBounds.w != oldBounds.w || resizeState.wrapWidth)) {
-            node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH,
-                    resizeState.wrapWidth ? VALUE_WRAP_CONTENT :
-                        String.format(VALUE_N_DP, mRulesEngine.pxToDp(newBounds.w)));
+        if (verticalEdge != null
+            && (newBounds.w != oldBounds.w || resizeState.wrapWidth || resizeState.fillWidth)) {
+            node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, resizeState.getWidthAttribute());
         }
-        if (horizontalEdge != null && (newBounds.h != oldBounds.h || resizeState.wrapHeight)) {
-            node.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT,
-                    resizeState.wrapHeight ? VALUE_WRAP_CONTENT :
-                        String.format(VALUE_N_DP, mRulesEngine.pxToDp(newBounds.h)));
+        if (horizontalEdge != null
+            && (newBounds.h != oldBounds.h || resizeState.wrapHeight || resizeState.fillHeight)) {
+            node.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, resizeState.getHeightAttribute());
         }
     }
 }
index 4b13b84..77cd7fe 100644 (file)
@@ -25,9 +25,7 @@ import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_WIDTH;
 import static com.android.ide.common.layout.LayoutConstants.ATTR_ORIENTATION;
 import static com.android.ide.common.layout.LayoutConstants.ATTR_WEIGHT_SUM;
 import static com.android.ide.common.layout.LayoutConstants.VALUE_HORIZONTAL;
-import static com.android.ide.common.layout.LayoutConstants.VALUE_N_DP;
 import static com.android.ide.common.layout.LayoutConstants.VALUE_VERTICAL;
-import static com.android.ide.common.layout.LayoutConstants.VALUE_WRAP_CONTENT;
 
 import com.android.annotations.VisibleForTesting;
 import com.android.ide.common.api.DrawingStyle;
@@ -685,60 +683,128 @@ public class LinearLayoutRule extends BaseLayoutRule {
         }
     }
 
-    @Override
-    public DropFeedback onResizeBegin(INode child, INode parent, SegmentType horizontalEdge,
-            SegmentType verticalEdge) {
-        return super.onResizeBegin(child, parent, horizontalEdge, verticalEdge);
-    }
+    /** Custom resize state used during linear layout resizing */
+    private class LinearResizeState extends ResizeState {
+        /** Whether the node should be assigned a new weight */
+        public boolean useWeight;
+        /** Weight sum to be applied to the parent */
+        private float mNewWeightSum;
+        /** The weight to be set on the node (provided {@link #useWeight} is true) */
+        private float mWeight;
+        /** Map from nodes to preferred bounds of nodes where the weights have been cleared */
+        public final Map<INode, Rect> unweightedSizes;
+        /** Total required size required by the siblings <b>without</b> weights */
+        public int totalLength;
+        /** List of nodes which should have their weights cleared */
+        public List<INode> mClearWeights;
+
+        private LinearResizeState(BaseLayoutRule rule, INode layout, INode node) {
+            super(rule, layout, node);
+
+            unweightedSizes = mRulesEngine.measureChildren(layout,
+                    new IClientRulesEngine.AttributeFilter() {
+                        public String getAttribute(INode n, String namespace, String localName) {
+                            // Clear out layout weights; we need to measure the unweighted sizes
+                            // of the children
+                            if (ATTR_LAYOUT_WEIGHT.equals(localName)
+                                    && SdkConstants.NS_RESOURCES.equals(namespace)) {
+                                return ""; //$NON-NLS-1$
+                            }
 
-    /**
-     * {@inheritDoc}
-     * <p>
-     * Overridden in this layout in order to make resizing affect the layout_weight
-     * attribute instead of the layout_width (for horizontal LinearLayouts) or
-     * layout_height (for vertical LinearLayouts).
-     */
-    @Override
-    protected void setNewSizeBounds(ResizeState resizeState, INode node, INode layout,
-            Rect previousBounds, Rect newBounds, SegmentType horizontalEdge,
-            SegmentType verticalEdge) {
-        final Rect oldBounds = node.getBounds();
-        if (oldBounds.equals(newBounds)) {
-            return;
-        }
-        // Handle resizing in the opposite dimension of the layout
-        boolean isVertical = isVertical(layout);
-        if (!isVertical && horizontalEdge != null) {
-            if (newBounds.h != oldBounds.h || resizeState.wrapHeight) {
-                node.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT,
-                        resizeState.wrapHeight ? VALUE_WRAP_CONTENT :
-                            String.format(VALUE_N_DP, mRulesEngine.pxToDp(newBounds.h)));
+                            return null;
+                        }
+                    });
+
+            // Compute total required size required by the siblings *without* weights
+            totalLength = 0;
+            final boolean isVertical = isVertical(layout);
+            for (Map.Entry<INode, Rect> entry : unweightedSizes.entrySet()) {
+                Rect preferredSize = entry.getValue();
+                if (isVertical) {
+                    totalLength += preferredSize.h;
+                } else {
+                    totalLength += preferredSize.w;
+                }
             }
-            if (verticalEdge == null) {
-                return;
+        }
+
+        /** Resets the computed state */
+        void reset() {
+            mNewWeightSum = -1;
+            useWeight = false;
+            mClearWeights = null;
+        }
+
+        /** Sets a weight to be applied to the node */
+        void setWeight(float weight) {
+            useWeight = true;
+            mWeight = weight;
+        }
+
+        /** Sets a weight sum to be applied to the parent layout */
+        void setWeightSum(float weightSum) {
+            mNewWeightSum = weightSum;
+        }
+
+        /** Marks that the given node should be cleared when applying the new size */
+        void clearWeight(INode n) {
+            if (mClearWeights == null) {
+                mClearWeights = new ArrayList<INode>();
             }
-            // else: fall through to compute a dynamic weight
+            mClearWeights.add(n);
         }
-        if (isVertical && verticalEdge != null) {
-            if (newBounds.w != oldBounds.w || resizeState.wrapWidth) {
-                node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH,
-                        resizeState.wrapWidth ? VALUE_WRAP_CONTENT :
-                            String.format(VALUE_N_DP, mRulesEngine.pxToDp(newBounds.w)));
+
+        /** Applies the state to the nodes */
+        public void apply() {
+            assert useWeight;
+
+            String value = mWeight > 0 ? formatFloatAttribute(mWeight) : null;
+            node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WEIGHT, value);
+
+            if (mClearWeights != null) {
+                for (INode n : mClearWeights) {
+                    if (getWeight(n) > 0.0f) {
+                        n.setAttribute(ANDROID_URI, ATTR_LAYOUT_WEIGHT, null);
+                    }
+                }
             }
-            if (horizontalEdge == null) {
-                return;
+
+            if (mNewWeightSum > 0.0) {
+                layout.setAttribute(ANDROID_URI, ATTR_WEIGHT_SUM,
+                        formatFloatAttribute(mNewWeightSum));
             }
         }
+    }
+
+    @Override
+    protected ResizeState createResizeState(INode layout, INode node) {
+        return new LinearResizeState(this, layout, node);
+    }
+
+    protected void updateResizeState(LinearResizeState resizeState, final INode node, INode layout,
+            Rect oldBounds, Rect newBounds, SegmentType horizontalEdge,
+            SegmentType verticalEdge) {
+        // Update the resize state.
+        // This method attempts to compute a new layout weight to be used in the direction
+        // of the linear layout. If the superclass has already determined that we can snap to
+        // a wrap_content or match_parent boundary, we prefer that. Otherwise, we attempt to
+        // compute a layout weight - which can fail if the size is too big (not enough room),
+        // or if the size is too small (smaller than the natural width of the node), and so on.
+        // In that case this method just aborts, which will leave the resize state object
+        // in such a state that it will call the superclass to resize instead, which will fall
+        // back to device independent pixel sizing.
+        resizeState.reset();
+
+        if (oldBounds.equals(newBounds)) {
+            return;
+        }
 
-        // If we're setting the width/height to wrap_content in the dimension of the
+        // If we're setting the width/height to wrap_content/match_parent in the dimension of the
         // linear layout, then just apply wrap_content and clear weights.
+        boolean isVertical = isVertical(layout);
         if (!isVertical && verticalEdge != null) {
-            if (resizeState.wrapWidth) {
-                node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, VALUE_WRAP_CONTENT);
-                // Clear weight
-                if (getWeight(node) > 0.0f) {
-                    node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WEIGHT, null);
-                }
+            if (resizeState.wrapWidth || resizeState.fillWidth) {
+                resizeState.clearWeight(node);
                 return;
             }
             if (newBounds.w == oldBounds.w) {
@@ -747,12 +813,8 @@ public class LinearLayoutRule extends BaseLayoutRule {
         }
 
         if (isVertical && horizontalEdge != null) {
-            if (resizeState.wrapHeight) {
-                node.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, VALUE_WRAP_CONTENT);
-                // Clear weight
-                if (getWeight(node) > 0.0f) {
-                    node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WEIGHT, null);
-                }
+            if (resizeState.wrapHeight || resizeState.fillHeight) {
+                resizeState.clearWeight(node);
                 return;
             }
             if (newBounds.h == oldBounds.h) {
@@ -760,44 +822,30 @@ public class LinearLayoutRule extends BaseLayoutRule {
             }
         }
 
+        // Compute weight sum
         float sum = getWeightSum(layout);
         if (sum <= 0.0f) {
             sum = 1.0f;
-            layout.setAttribute(ANDROID_URI, ATTR_WEIGHT_SUM, formatFloatAttribute(sum));
+            resizeState.setWeightSum(sum);
         }
 
-        Map<INode, Rect> sizes = mRulesEngine.measureChildren(layout,
-                new IClientRulesEngine.AttributeFilter() {
-                    public String getAttribute(INode n, String namespace, String localName) {
-                        // Clear out layout weights; we need to measure the unweighted sizes
-                        // of the children
-                        if (ATTR_LAYOUT_WEIGHT.equals(localName)
-                                && SdkConstants.NS_RESOURCES.equals(namespace)) {
-                            return ""; //$NON-NLS-1$
-                        }
-
-                        return null;
-                    }
-                });
-        int totalLength = 0;
-        for (Map.Entry<INode, Rect> entry : sizes.entrySet()) {
-            Rect preferredSize = entry.getValue();
-            if (isVertical) {
-                totalLength += preferredSize.h;
-            } else {
-                totalLength += preferredSize.w;
+        // If the new size of the node is smaller than its preferred/wrap_content size,
+        // then we cannot use weights to size it; switch to pixel-based sizing instead
+        Map<INode, Rect> sizes = resizeState.unweightedSizes;
+        Rect nodePreferredSize = sizes.get(node);
+        if (nodePreferredSize != null) {
+            if (horizontalEdge != null && newBounds.h < nodePreferredSize.h ||
+                    verticalEdge != null && newBounds.w < nodePreferredSize.w) {
+                return;
             }
         }
 
         Rect layoutBounds = layout.getBounds();
-        int remaining = (isVertical ? layoutBounds.h : layoutBounds.w) - totalLength;
+        int remaining = (isVertical ? layoutBounds.h : layoutBounds.w) - resizeState.totalLength;
         Rect nodeBounds = sizes.get(node);
         if (nodeBounds == null) {
-            super.setNewSizeBounds(resizeState, node, layout, oldBounds, newBounds, horizontalEdge,
-                    verticalEdge);
             return;
         }
-        assert nodeBounds != null;
 
         if (remaining > 0) {
             int missing = 0;
@@ -812,7 +860,7 @@ public class LinearLayoutRule extends BaseLayoutRule {
                     // smaller size.
                     missing = newBounds.h - resizeState.wrapBounds.h;
                     remaining += nodeBounds.h - resizeState.wrapBounds.h;
-                    node.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT, VALUE_WRAP_CONTENT);
+                    resizeState.wrapHeight = true;
                 }
             } else {
                 if (newBounds.w > nodeBounds.w) {
@@ -820,61 +868,85 @@ public class LinearLayoutRule extends BaseLayoutRule {
                 } else if (newBounds.w > resizeState.wrapBounds.w) {
                     missing = newBounds.w - resizeState.wrapBounds.w;
                     remaining += nodeBounds.w - resizeState.wrapBounds.w;
-                    node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH, VALUE_WRAP_CONTENT);
+                    resizeState.wrapWidth = true;
                 }
             }
             if (missing > 0) {
                 // (weight / weightSum) * remaining = missing, so
                 // weight = missing * weightSum / remaining
                 float weight = missing * sum / remaining;
-                String value = weight > 0 ? formatFloatAttribute(weight) : null;
-                node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WEIGHT, value);
+                resizeState.setWeight(weight);
             }
-        } else {
-            // TODO: This algorithm should be refined.
-            // One possible solution is to clear the weights and sizes of all children
-            // to the left or right of the resized node (depending on whether the right
-            // or left edge was resized - the key point being that the other edge should
-            // not move).
-
-            // There is no leftover space after adding up the wrap-content sizes of the
-            // children. In that case, just make the weight of this child the same proportion
-            // of the sum-of-weights as its new size is out of the parent size.
-
-            // Use actual sum of weights, not the declared sum on the parent layout,
-            // to get the proportions right
-            float otherSum = 0.0f;
-            for (INode child : layout.getChildren()) {
-                if (child != node) {
-                    otherSum += getWeight(child);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * Overridden in this layout in order to make resizing affect the layout_weight
+     * attribute instead of the layout_width (for horizontal LinearLayouts) or
+     * layout_height (for vertical LinearLayouts).
+     */
+    @Override
+    protected void setNewSizeBounds(ResizeState state, final INode node, INode layout,
+            Rect oldBounds, Rect newBounds, SegmentType horizontalEdge,
+            SegmentType verticalEdge) {
+        LinearResizeState resizeState = (LinearResizeState) state;
+        updateResizeState(resizeState, node, layout, oldBounds, newBounds,
+                horizontalEdge, verticalEdge);
+
+        if (resizeState.useWeight) {
+            resizeState.apply();
+
+            // Handle resizing in the opposite dimension of the layout
+            final boolean isVertical = isVertical(layout);
+            if (!isVertical && horizontalEdge != null) {
+                if (newBounds.h != oldBounds.h || resizeState.wrapHeight
+                        || resizeState.fillHeight) {
+                    node.setAttribute(ANDROID_URI, ATTR_LAYOUT_HEIGHT,
+                            resizeState.getHeightAttribute());
                 }
             }
-
-            float newSize = isVertical ? newBounds.h : newBounds.w;
-            float totalSize = isVertical ? layoutBounds.h : layoutBounds.w;
-            float weight;
-            if (newSize >= totalSize) {
-                // The new view was resized to something larger than the layout itself;
-                // that obviously can't be achieved with layout weights, so just pick
-                // something large to give it a lot of space but not all.
-                weight = 10 * otherSum;
-            } else {
-                weight = newSize * otherSum / (totalSize - newSize);
+            if (isVertical && verticalEdge != null) {
+                if (newBounds.w != oldBounds.w || resizeState.wrapWidth || resizeState.fillWidth) {
+                    node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WIDTH,
+                            resizeState.getWidthAttribute());
+                }
             }
-            String value = weight > 0 ? formatFloatAttribute(weight) : null;
-            node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WEIGHT, value);
-            String fill = getFillParentValueName();
-            node.setAttribute(ANDROID_URI, isVertical ? ATTR_LAYOUT_WEIGHT : ATTR_LAYOUT_WIDTH,
-                    fill);
+        } else {
+            node.setAttribute(ANDROID_URI, ATTR_LAYOUT_WEIGHT, null);
+            super.setNewSizeBounds(resizeState, node, layout, oldBounds, newBounds,
+                    horizontalEdge, verticalEdge);
         }
     }
 
     @Override
-    protected String getResizeUpdateMessage(ResizeState resizeState, INode child, INode parent,
+    protected String getResizeUpdateMessage(ResizeState state, INode child, INode parent,
             Rect newBounds, SegmentType horizontalEdge, SegmentType verticalEdge) {
-        return super.getResizeUpdateMessage(resizeState, child, parent, newBounds,
+        LinearResizeState resizeState = (LinearResizeState) state;
+        updateResizeState(resizeState, child, parent, child.getBounds(), newBounds,
                 horizontalEdge, verticalEdge);
-        // TODO: Change message to display the current layout weight instead
+
+        if (resizeState.useWeight) {
+            String weight = formatFloatAttribute(resizeState.mWeight);
+            String dimension = String.format("layout weight %1$s", weight);
+
+            String width;
+            String height;
+            if (isVertical(parent)) {
+                width = resizeState.getWidthAttribute();
+                height = dimension;
+            } else {
+                width = dimension;
+                height = resizeState.getHeightAttribute();
+            }
+
+            // U+00D7: Unicode for multiplication sign
+            return String.format("Resize to %s \u00D7 %s", width, height);
+        } else {
+            return super.getResizeUpdateMessage(state, child, parent, newBounds,
+                    horizontalEdge, verticalEdge);
+        }
     }
 
     /**
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ResizeState.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/ResizeState.java
new file mode 100644 (file)
index 0000000..11f3ec6
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.eclipse.org/org/documents/epl-v10.php
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ide.common.layout;
+
+import static com.android.ide.common.layout.LayoutConstants.VALUE_N_DP;
+import static com.android.ide.common.layout.LayoutConstants.VALUE_WRAP_CONTENT;
+
+import com.android.ide.common.api.INode;
+import com.android.ide.common.api.Rect;
+import com.android.ide.common.api.Segment;
+import com.android.ide.common.api.SegmentType;
+
+/** State held during resizing operations */
+class ResizeState {
+    /**
+     * The associated rule
+     */
+    private final BaseLayoutRule mRule;
+
+    /**
+     * The node being resized
+     */
+    public final INode node;
+
+     /**
+      * The layout containing the resized node
+      */
+    public final INode layout;
+
+    /** The proposed resized bounds of the node */
+    public Rect bounds;
+
+    /** The preferred wrap_content bounds of the node */
+    public Rect wrapBounds;
+
+    /** The suggested horizontal fill_parent guideline position */
+    public Segment horizontalFillSegment;
+
+    /** The suggested vertical fill_parent guideline position */
+    public Segment verticalFillSegment;
+
+    /** The type of horizontal edge being resized, or null */
+    public SegmentType horizontalEdgeType;
+
+    /** The type of vertical edge being resized, or null */
+    public SegmentType verticalEdgeType;
+
+    /** Whether the user has snapped to the wrap_content width */
+    public boolean wrapWidth;
+
+    /** Whether the user has snapped to the wrap_content height */
+    public boolean wrapHeight;
+
+    /** Whether the user has snapped to the match_parent width */
+    public boolean fillWidth;
+
+    /** Whether the user has snapped to the match_parent height */
+    public boolean fillHeight;
+
+    /**
+     * Constructs a new {@link ResizeState}
+     *
+     * @param rule the associated rule
+     * @param layout the parent layout containing the resized node
+     * @param node the node being resized
+     */
+    ResizeState(BaseLayoutRule rule, INode layout, INode node) {
+        mRule = rule;
+
+        this.layout = layout;
+        this.node = node;
+    }
+
+    /**
+     * Returns the width attribute to be set to match the new bounds
+     *
+     * @return the width string, never null
+     */
+    public String getWidthAttribute() {
+        if (wrapWidth) {
+            return VALUE_WRAP_CONTENT;
+        } else if (fillWidth) {
+            return mRule.getFillParentValueName();
+        } else {
+            return String.format(VALUE_N_DP, mRule.mRulesEngine.pxToDp(bounds.w));
+        }
+    }
+
+    /**
+     * Returns the height attribute to be set to match the new bounds
+     *
+     * @return the height string, never null
+     */
+    public String getHeightAttribute() {
+        if (wrapHeight) {
+            return VALUE_WRAP_CONTENT;
+        } else if (fillHeight) {
+            return mRule.getFillParentValueName();
+        } else {
+            return String.format(VALUE_N_DP, mRule.mRulesEngine.pxToDp(bounds.h));
+        }
+    }
+}
\ No newline at end of file