diff options
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.java | 265 |
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); + } +} |