diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/AbsoluteLayoutRule.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/AbsoluteLayoutRule.java | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/AbsoluteLayoutRule.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/AbsoluteLayoutRule.java new file mode 100644 index 000000000..3ec3b5f1a --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/AbsoluteLayoutRule.java @@ -0,0 +1,254 @@ +/* + * 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 static com.android.SdkConstants.ANDROID_URI; +import static com.android.SdkConstants.ATTR_LAYOUT_X; +import static com.android.SdkConstants.ATTR_LAYOUT_Y; +import static com.android.SdkConstants.VALUE_N_DP; + +import com.android.annotations.NonNull; +import com.android.annotations.Nullable; +import com.android.ide.common.api.DrawingStyle; +import com.android.ide.common.api.DropFeedback; +import com.android.ide.common.api.IDragElement; +import com.android.ide.common.api.IFeedbackPainter; +import com.android.ide.common.api.IGraphics; +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.Point; +import com.android.ide.common.api.Rect; +import com.android.ide.common.api.SegmentType; +import com.android.utils.Pair; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * An {@link IViewRule} for android.widget.AbsoluteLayout and all its derived + * classes. + */ +public class AbsoluteLayoutRule extends BaseLayoutRule { + + @Override + public List<String> getSelectionHint(@NonNull INode parentNode, @NonNull INode childNode) { + List<String> infos = new ArrayList<String>(2); + infos.add("AbsoluteLayout is deprecated."); + infos.add("Use other layouts instead."); + return infos; + } + + // ==== Drag'n'drop support ==== + // The AbsoluteLayout accepts any drag'n'drop anywhere on its surface. + + @Override + public DropFeedback onDropEnter(@NonNull INode targetNode, @Nullable Object targetView, + final @Nullable IDragElement[] elements) { + + if (elements.length == 0) { + return null; + } + + DropFeedback df = new DropFeedback(null, new IFeedbackPainter() { + @Override + public void paint(@NonNull IGraphics gc, @NonNull INode node, + @NonNull DropFeedback feedback) { + // Paint callback for the AbsoluteLayout. + // This is called by the canvas when a draw is needed. + drawFeedback(gc, node, elements, feedback); + } + }); + df.errorMessage = "AbsoluteLayout is deprecated."; + return df; + } + + void drawFeedback( + IGraphics gc, + INode targetNode, + IDragElement[] elements, + DropFeedback feedback) { + Rect b = targetNode.getBounds(); + if (!b.isValid()) { + return; + } + + // Highlight the receiver + gc.useStyle(DrawingStyle.DROP_RECIPIENT); + gc.drawRect(b); + + // Get the drop point + Point p = (Point) feedback.userData; + + if (p == null) { + return; + } + + int x = p.x; + int y = p.y; + + Rect be = elements[0].getBounds(); + + if (be.isValid()) { + // At least the first element has a bound. Draw rectangles + // for all dropped elements with valid bounds, offset at + // the drop point. + int offsetX = x - be.x + (feedback.dragBounds != null ? feedback.dragBounds.x : 0); + int offsetY = y - be.y + (feedback.dragBounds != null ? feedback.dragBounds.y : 0); + gc.useStyle(DrawingStyle.DROP_PREVIEW); + for (IDragElement element : elements) { + drawElement(gc, element, offsetX, offsetY); + } + } else { + // We don't have bounds for new elements. In this case + // just draw cross hairs to the drop point. + gc.useStyle(DrawingStyle.GUIDELINE); + gc.drawLine(x, b.y, x, b.y + b.h); + gc.drawLine(b.x, y, b.x + b.w, y); + + // Use preview lines to indicate the bottom quadrant as well (to + // indicate that you are looking at the top left position of the + // drop, not the center for example) + gc.useStyle(DrawingStyle.DROP_PREVIEW); + gc.drawLine(x, y, b.x + b.w, y); + gc.drawLine(x, y, x, b.y + b.h); + } + } + + @Override + public DropFeedback onDropMove(@NonNull INode targetNode, @NonNull IDragElement[] elements, + @Nullable DropFeedback feedback, @NonNull Point p) { + // Update the data used by the DropFeedback.paintCallback above. + feedback.userData = p; + feedback.requestPaint = true; + + return feedback; + } + + @Override + public void onDropLeave(@NonNull INode targetNode, @NonNull IDragElement[] elements, + @Nullable DropFeedback feedback) { + // Nothing to do. + } + + @Override + public void onDropped(final @NonNull INode targetNode, final @NonNull IDragElement[] elements, + final @Nullable DropFeedback feedback, final @NonNull Point p) { + + final Rect b = targetNode.getBounds(); + if (!b.isValid()) { + return; + } + + // Collect IDs from dropped elements and remap them to new IDs + // if this is a copy or from a different canvas. + final Map<String, Pair<String, String>> idMap = getDropIdMap(targetNode, elements, + feedback.isCopy || !feedback.sameCanvas); + + targetNode.editXml("Add elements to AbsoluteLayout", new INodeHandler() { + @Override + public void handle(@NonNull INode node) { + boolean first = true; + Point offset = null; + + // Now write the new elements. + for (IDragElement element : elements) { + String fqcn = element.getFqcn(); + Rect be = element.getBounds(); + + INode newChild = targetNode.appendChild(fqcn); + + // Copy all the attributes, modifying them as needed. + addAttributes(newChild, element, idMap, DEFAULT_ATTR_FILTER); + + int deltaX = (feedback.dragBounds != null ? feedback.dragBounds.x : 0); + int deltaY = (feedback.dragBounds != null ? feedback.dragBounds.y : 0); + + int x = p.x - b.x + deltaX; + int y = p.y - b.y + deltaY; + + if (first) { + first = false; + if (be.isValid()) { + offset = new Point(x - be.x, y - be.y); + } + } else if (offset != null && be.isValid()) { + x = offset.x + be.x; + y = offset.y + be.y; + } else { + x += 10; + y += be.isValid() ? be.h : 10; + } + + double scale = feedback.dipScale; + if (scale != 1.0) { + x *= scale; + y *= scale; + } + + newChild.setAttribute(ANDROID_URI, ATTR_LAYOUT_X, + String.format(VALUE_N_DP, x)); + newChild.setAttribute(ANDROID_URI, ATTR_LAYOUT_Y, + String.format(VALUE_N_DP, y)); + + addInnerElements(newChild, element, idMap); + } + } + }); + } + + /** + * {@inheritDoc} + * <p> + * Overridden in this layout in order to let the top left coordinate be affected by + * the resize operation too. In other words, dragging the top left corner to resize a + * widget will not only change the size of the widget, it will also move it (though in + * this case, the bottom right corner will stay fixed). + */ + @Override + protected void setNewSizeBounds(ResizeState resizeState, INode node, INode layout, + Rect previousBounds, Rect newBounds, SegmentType horizontalEdge, + SegmentType verticalEdge) { + super.setNewSizeBounds(resizeState, node, layout, previousBounds, newBounds, + horizontalEdge, verticalEdge); + if (verticalEdge != null && newBounds.x != previousBounds.x) { + node.setAttribute(ANDROID_URI, ATTR_LAYOUT_X, + String.format(VALUE_N_DP, + mRulesEngine.pxToDp(newBounds.x - node.getParent().getBounds().x))); + } + if (horizontalEdge != null && newBounds.y != previousBounds.y) { + node.setAttribute(ANDROID_URI, ATTR_LAYOUT_Y, + String.format(VALUE_N_DP, + mRulesEngine.pxToDp(newBounds.y - node.getParent().getBounds().y))); + } + } + + @Override + protected String getResizeUpdateMessage(ResizeState resizeState, INode child, INode parent, + Rect newBounds, SegmentType horizontalEdge, SegmentType verticalEdge) { + Rect parentBounds = parent.getBounds(); + if (horizontalEdge == SegmentType.BOTTOM && verticalEdge == SegmentType.RIGHT) { + return super.getResizeUpdateMessage(resizeState, child, parent, newBounds, + horizontalEdge, verticalEdge); + } + return String.format("x=%d, y=%d\nwidth=%s, height=%s", + mRulesEngine.pxToDp(newBounds.x - parentBounds.x), + mRulesEngine.pxToDp(newBounds.y - parentBounds.y), + resizeState.getWidthAttribute(), resizeState.getHeightAttribute()); + } +} |