aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/ResizeHandler.java
diff options
context:
space:
mode:
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/ResizeHandler.java')
-rw-r--r--eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/ResizeHandler.java265
1 files changed, 265 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/ResizeHandler.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/ResizeHandler.java
new file mode 100644
index 000000000..a5e071d74
--- /dev/null
+++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/common/layout/relative/ResizeHandler.java
@@ -0,0 +1,265 @@
+/*
+ * 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.relative;
+
+import static com.android.ide.common.api.MarginType.NO_MARGIN;
+import static com.android.ide.common.api.SegmentType.BASELINE;
+import static com.android.ide.common.api.SegmentType.BOTTOM;
+import static com.android.ide.common.api.SegmentType.CENTER_HORIZONTAL;
+import static com.android.ide.common.api.SegmentType.CENTER_VERTICAL;
+import static com.android.ide.common.api.SegmentType.LEFT;
+import static com.android.ide.common.api.SegmentType.RIGHT;
+import static com.android.ide.common.api.SegmentType.TOP;
+import static com.android.SdkConstants.ATTR_ID;
+
+import static java.lang.Math.abs;
+
+import com.android.SdkConstants;
+import static com.android.SdkConstants.ANDROID_URI;
+import com.android.ide.common.api.DropFeedback;
+import com.android.ide.common.api.IClientRulesEngine;
+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;
+import com.android.ide.common.layout.BaseLayoutRule;
+
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * A {@link ResizeHandler} is a {@link GuidelineHandler} which handles resizing of individual
+ * edges in a RelativeLayout.
+ */
+public class ResizeHandler extends GuidelineHandler {
+ private final SegmentType mHorizontalEdgeType;
+ private final SegmentType mVerticalEdgeType;
+
+ /**
+ * Creates a new {@link ResizeHandler}
+ *
+ * @param layout the layout containing the resized node
+ * @param resized the node being resized
+ * @param rulesEngine the applicable {@link IClientRulesEngine}
+ * @param horizontalEdgeType the type of horizontal edge being resized, or null
+ * @param verticalEdgeType the type of vertical edge being resized, or null
+ */
+ public ResizeHandler(INode layout, INode resized,
+ IClientRulesEngine rulesEngine,
+ SegmentType horizontalEdgeType, SegmentType verticalEdgeType) {
+ super(layout, rulesEngine);
+
+ assert horizontalEdgeType != null || verticalEdgeType != null;
+ assert horizontalEdgeType != BASELINE && verticalEdgeType != BASELINE;
+ assert horizontalEdgeType != CENTER_HORIZONTAL && verticalEdgeType != CENTER_HORIZONTAL;
+ assert horizontalEdgeType != CENTER_VERTICAL && verticalEdgeType != CENTER_VERTICAL;
+
+ mHorizontalEdgeType = horizontalEdgeType;
+ mVerticalEdgeType = verticalEdgeType;
+
+ Set<INode> nodes = Collections.singleton(resized);
+ mDraggedNodes = nodes;
+
+ mHorizontalDeps = mDependencyGraph.dependsOn(nodes, false /* vertical */);
+ mVerticalDeps = mDependencyGraph.dependsOn(nodes, true /* vertical */);
+
+ if (horizontalEdgeType != null) {
+ if (horizontalEdgeType == TOP) {
+ mMoveTop = true;
+ } else if (horizontalEdgeType == BOTTOM) {
+ mMoveBottom = true;
+ }
+ }
+ if (verticalEdgeType != null) {
+ if (verticalEdgeType == LEFT) {
+ mMoveLeft = true;
+ } else if (verticalEdgeType == RIGHT) {
+ mMoveRight = true;
+ }
+ }
+
+ for (INode child : layout.getChildren()) {
+ if (child != resized) {
+ String id = child.getStringAttr(ANDROID_URI, ATTR_ID);
+ addBounds(child, id,
+ !mHorizontalDeps.contains(child),
+ !mVerticalDeps.contains(child));
+ }
+ }
+
+ addBounds(layout, layout.getStringAttr(ANDROID_URI, ATTR_ID), true, true);
+ }
+
+ @Override
+ protected void snapVertical(Segment vEdge, int x, Rect newBounds) {
+ int maxDistance = BaseLayoutRule.getMaxMatchDistance();
+ if (vEdge.edgeType == LEFT) {
+ int margin = mSnap ? 0 : abs(newBounds.x - x);
+ if (margin > maxDistance) {
+ mLeftMargin = margin;
+ } else {
+ newBounds.w += newBounds.x - x;
+ newBounds.x = x;
+ }
+ } else if (vEdge.edgeType == RIGHT) {
+ int margin = mSnap ? 0 : abs(newBounds.x - (x - newBounds.w));
+ if (margin > maxDistance) {
+ mRightMargin = margin;
+ } else {
+ newBounds.w = x - newBounds.x;
+ }
+ } else {
+ assert false : vEdge;
+ }
+ }
+
+ @Override
+ protected void snapHorizontal(Segment hEdge, int y, Rect newBounds) {
+ int maxDistance = BaseLayoutRule.getMaxMatchDistance();
+ if (hEdge.edgeType == TOP) {
+ int margin = mSnap ? 0 : abs(newBounds.y - y);
+ if (margin > maxDistance) {
+ mTopMargin = margin;
+ } else {
+ newBounds.h += newBounds.y - y;
+ newBounds.y = y;
+ }
+ } else if (hEdge.edgeType == BOTTOM) {
+ int margin = mSnap ? 0 : abs(newBounds.y - (y - newBounds.h));
+ if (margin > maxDistance) {
+ mBottomMargin = margin;
+ } else {
+ newBounds.h = y - newBounds.y;
+ }
+ } else {
+ assert false : hEdge;
+ }
+ }
+
+ @Override
+ protected boolean isEdgeTypeCompatible(SegmentType edge, SegmentType dragged, int delta) {
+ boolean compatible = super.isEdgeTypeCompatible(edge, dragged, delta);
+
+ // When resizing and not snapping (e.g. using margins to pick a specific pixel
+ // width) we cannot use -negative- margins to jump back to a closer edge; we
+ // must always use positive margins, so mark closer edges that result in a negative
+ // margin as not compatible.
+ if (compatible && !mSnap) {
+ switch (dragged) {
+ case LEFT:
+ case TOP:
+ return delta <= 0;
+ default:
+ return delta >= 0;
+ }
+ }
+
+ return compatible;
+ }
+
+ /**
+ * Updates the handler for the given mouse resize
+ *
+ * @param feedback the feedback handler
+ * @param child the node being resized
+ * @param newBounds the new bounds of the resize rectangle
+ * @param modifierMask the keyboard modifiers pressed during the drag
+ */
+ public void updateResize(DropFeedback feedback, INode child, Rect newBounds,
+ int modifierMask) {
+ mSnap = (modifierMask & DropFeedback.MODIFIER2) == 0;
+ mBounds = newBounds;
+ clearSuggestions();
+
+ Rect b = newBounds;
+ Segment hEdge = null;
+ Segment vEdge = null;
+ String childId = child.getStringAttr(ANDROID_URI, ATTR_ID);
+
+ // TODO: MarginType=NO_MARGIN may not be right. Consider resizing a widget
+ // that has margins and how that should be handled.
+
+ if (mHorizontalEdgeType == TOP) {
+ hEdge = new Segment(b.y, b.x, b.x2(), child, childId, mHorizontalEdgeType, NO_MARGIN);
+ } else if (mHorizontalEdgeType == BOTTOM) {
+ hEdge = new Segment(b.y2(), b.x, b.x2(), child, childId, mHorizontalEdgeType,
+ NO_MARGIN);
+ } else {
+ assert mHorizontalEdgeType == null;
+ }
+
+ if (mVerticalEdgeType == LEFT) {
+ vEdge = new Segment(b.x, b.y, b.y2(), child, childId, mVerticalEdgeType, NO_MARGIN);
+ } else if (mVerticalEdgeType == RIGHT) {
+ vEdge = new Segment(b.x2(), b.y, b.y2(), child, childId, mVerticalEdgeType, NO_MARGIN);
+ } else {
+ assert mVerticalEdgeType == null;
+ }
+
+ mTopMargin = mBottomMargin = mLeftMargin = mRightMargin = 0;
+
+ if (hEdge != null && mHorizontalEdges.size() > 0) {
+ // Compute horizontal matches
+ mHorizontalSuggestions = findClosest(hEdge, mHorizontalEdges);
+
+ Match match = pickBestMatch(mHorizontalSuggestions);
+ if (match != null
+ && (!mSnap || Math.abs(match.delta) < BaseLayoutRule.getMaxMatchDistance())) {
+ if (mHorizontalDeps.contains(match.edge.node)) {
+ match.cycle = true;
+ }
+
+ snapHorizontal(hEdge, match.edge.at, newBounds);
+
+ if (hEdge.edgeType == TOP) {
+ mCurrentTopMatch = match;
+ } else if (hEdge.edgeType == BOTTOM) {
+ mCurrentBottomMatch = match;
+ } else {
+ assert hEdge.edgeType == CENTER_HORIZONTAL
+ || hEdge.edgeType == BASELINE : hEdge;
+ mCurrentTopMatch = match;
+ }
+ }
+ }
+
+ if (vEdge != null && mVerticalEdges.size() > 0) {
+ mVerticalSuggestions = findClosest(vEdge, mVerticalEdges);
+
+ Match match = pickBestMatch(mVerticalSuggestions);
+ if (match != null
+ && (!mSnap || Math.abs(match.delta) < BaseLayoutRule.getMaxMatchDistance())) {
+ if (mVerticalDeps.contains(match.edge.node)) {
+ match.cycle = true;
+ }
+
+ // Snap
+ snapVertical(vEdge, match.edge.at, newBounds);
+
+ if (vEdge.edgeType == LEFT) {
+ mCurrentLeftMatch = match;
+ } else if (vEdge.edgeType == RIGHT) {
+ mCurrentRightMatch = match;
+ } else {
+ assert vEdge.edgeType == CENTER_VERTICAL;
+ mCurrentLeftMatch = match;
+ }
+ }
+ }
+
+ checkCycles(feedback);
+ }
+}