diff options
Diffstat (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ResizeGesture.java')
-rw-r--r-- | eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ResizeGesture.java | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ResizeGesture.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ResizeGesture.java new file mode 100644 index 000000000..4d51c07de --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ResizeGesture.java @@ -0,0 +1,279 @@ +/* + * 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.eclipse.adt.internal.editors.layout.gle2; + +import com.android.ide.common.api.DropFeedback; +import com.android.ide.common.api.Rect; +import com.android.ide.common.api.ResizePolicy; +import com.android.ide.common.api.SegmentType; +import com.android.ide.eclipse.adt.internal.editors.layout.gle2.SelectionHandle.Position; +import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy; +import com.android.ide.eclipse.adt.internal.editors.layout.gre.RulesEngine; +import com.android.utils.Pair; + +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.graphics.GC; + +import java.util.Collections; +import java.util.List; + +/** + * A {@link ResizeGesture} is a gesture for resizing a selected widget. It is initiated + * by a drag of a {@link SelectionHandle}. + */ +public class ResizeGesture extends Gesture { + /** The {@link Overlay} drawn for the gesture feedback. */ + private ResizeOverlay mOverlay; + + /** The canvas associated with this gesture. */ + private LayoutCanvas mCanvas; + + /** The selection handle we're dragging to perform this resize */ + private SelectionHandle mHandle; + + private NodeProxy mParentNode; + private NodeProxy mChildNode; + private DropFeedback mFeedback; + private ResizePolicy mResizePolicy; + private SegmentType mHorizontalEdge; + private SegmentType mVerticalEdge; + + /** + * Creates a new marquee selection (selection swiping). + * + * @param canvas The canvas where selection is performed. + * @param item The selected item the handle corresponds to + * @param handle The handle being dragged to perform the resize + */ + public ResizeGesture(LayoutCanvas canvas, SelectionItem item, SelectionHandle handle) { + mCanvas = canvas; + mHandle = handle; + + mChildNode = item.getNode(); + mParentNode = (NodeProxy) mChildNode.getParent(); + mResizePolicy = item.getResizePolicy(); + mHorizontalEdge = getHorizontalEdgeType(mHandle); + mVerticalEdge = getVerticalEdgeType(mHandle); + } + + @Override + public void begin(ControlPoint pos, int startMask) { + super.begin(pos, startMask); + + mCanvas.getSelectionOverlay().setHidden(true); + + RulesEngine rulesEngine = mCanvas.getRulesEngine(); + Rect newBounds = getNewBounds(pos); + ViewHierarchy viewHierarchy = mCanvas.getViewHierarchy(); + CanvasViewInfo childInfo = viewHierarchy.findViewInfoFor(mChildNode); + CanvasViewInfo parentInfo = viewHierarchy.findViewInfoFor(mParentNode); + Object childView = childInfo != null ? childInfo.getViewObject() : null; + Object parentView = parentInfo != null ? parentInfo.getViewObject() : null; + mFeedback = rulesEngine.callOnResizeBegin(mChildNode, mParentNode, newBounds, + mHorizontalEdge, mVerticalEdge, childView, parentView); + update(pos); + mCanvas.getGestureManager().updateMessage(mFeedback); + } + + @Override + public boolean keyPressed(KeyEvent event) { + update(mCanvas.getGestureManager().getCurrentControlPoint()); + mCanvas.redraw(); + return true; + } + + @Override + public boolean keyReleased(KeyEvent event) { + update(mCanvas.getGestureManager().getCurrentControlPoint()); + mCanvas.redraw(); + return true; + } + + @Override + public void update(ControlPoint pos) { + super.update(pos); + RulesEngine rulesEngine = mCanvas.getRulesEngine(); + Rect newBounds = getNewBounds(pos); + int modifierMask = mCanvas.getGestureManager().getRuleModifierMask(); + rulesEngine.callOnResizeUpdate(mFeedback, mChildNode, mParentNode, newBounds, + modifierMask); + mCanvas.getGestureManager().updateMessage(mFeedback); + } + + @Override + public void end(ControlPoint pos, boolean canceled) { + super.end(pos, canceled); + + if (!canceled) { + RulesEngine rulesEngine = mCanvas.getRulesEngine(); + Rect newBounds = getNewBounds(pos); + rulesEngine.callOnResizeEnd(mFeedback, mChildNode, mParentNode, newBounds); + } + + mCanvas.getSelectionOverlay().setHidden(false); + } + + @Override + public Pair<Boolean, Boolean> getTooltipPosition() { + return Pair.of(mHorizontalEdge != SegmentType.TOP, mVerticalEdge != SegmentType.LEFT); + } + + /** + * For the new mouse position, compute the resized bounds (the bounding rectangle that + * the view should be resized to). This is not just a width or height, since in some + * cases resizing will change the x/y position of the view as well (for example, in + * RelativeLayout or in AbsoluteLayout). + */ + private Rect getNewBounds(ControlPoint pos) { + LayoutPoint p = pos.toLayout(); + LayoutPoint start = mStart.toLayout(); + Rect b = mChildNode.getBounds(); + Position direction = mHandle.getPosition(); + + int x = b.x; + int y = b.y; + int w = b.w; + int h = b.h; + int deltaX = p.x - start.x; + int deltaY = p.y - start.y; + + if (deltaX == 0 && deltaY == 0) { + // No move - just use the existing bounds + return b; + } + + if (mResizePolicy.isAspectPreserving() && w != 0 && h != 0) { + double aspectRatio = w / (double) h; + int newW = Math.abs(b.w + (direction.isLeft() ? -deltaX : deltaX)); + int newH = Math.abs(b.h + (direction.isTop() ? -deltaY : deltaY)); + double newAspectRatio = newW / (double) newH; + if (newH == 0 || newAspectRatio > aspectRatio) { + deltaY = (int) (deltaX / aspectRatio); + } else { + deltaX = (int) (deltaY * aspectRatio); + } + } + if (direction.isLeft()) { + // The user is dragging the left edge, so the position is anchored on the + // right. + int x2 = b.x + b.w; + int nx1 = b.x + deltaX; + if (nx1 <= x2) { + x = nx1; + w = x2 - x; + } else { + w = 0; + x = x2; + } + } else if (direction.isRight()) { + // The user is dragging the right edge, so the position is anchored on the + // left. + int nx2 = b.x + b.w + deltaX; + if (nx2 >= b.x) { + w = nx2 - b.x; + } else { + w = 0; + } + } else { + assert direction == Position.BOTTOM_MIDDLE || direction == Position.TOP_MIDDLE; + } + + if (direction.isTop()) { + // The user is dragging the top edge, so the position is anchored on the + // bottom. + int y2 = b.y + b.h; + int ny1 = b.y + deltaY; + if (ny1 < y2) { + y = ny1; + h = y2 - y; + } else { + h = 0; + y = y2; + } + } else if (direction.isBottom()) { + // The user is dragging the bottom edge, so the position is anchored on the + // top. + int ny2 = b.y + b.h + deltaY; + if (ny2 >= b.y) { + h = ny2 - b.y; + } else { + h = 0; + } + } else { + assert direction == Position.LEFT_MIDDLE || direction == Position.RIGHT_MIDDLE; + } + + return new Rect(x, y, w, h); + } + + private static SegmentType getHorizontalEdgeType(SelectionHandle handle) { + switch (handle.getPosition()) { + case BOTTOM_LEFT: + case BOTTOM_RIGHT: + case BOTTOM_MIDDLE: + return SegmentType.BOTTOM; + case LEFT_MIDDLE: + case RIGHT_MIDDLE: + return null; + case TOP_LEFT: + case TOP_MIDDLE: + case TOP_RIGHT: + return SegmentType.TOP; + default: assert false : handle.getPosition(); + } + return null; + } + + private static SegmentType getVerticalEdgeType(SelectionHandle handle) { + switch (handle.getPosition()) { + case TOP_LEFT: + case LEFT_MIDDLE: + case BOTTOM_LEFT: + return SegmentType.LEFT; + case BOTTOM_MIDDLE: + case TOP_MIDDLE: + return null; + case TOP_RIGHT: + case RIGHT_MIDDLE: + case BOTTOM_RIGHT: + return SegmentType.RIGHT; + default: assert false : handle.getPosition(); + } + return null; + } + + + @Override + public List<Overlay> createOverlays() { + mOverlay = new ResizeOverlay(); + return Collections.<Overlay> singletonList(mOverlay); + } + + /** + * An {@link Overlay} to paint the resize feedback. This just delegates to the + * layout rule for the parent which is handling the resizing. + */ + private class ResizeOverlay extends Overlay { + @Override + public void paint(GC gc) { + if (mChildNode != null && mFeedback != null) { + RulesEngine rulesEngine = mCanvas.getRulesEngine(); + rulesEngine.callDropFeedbackPaint(mCanvas.getGcWrapper(), mChildNode, mFeedback); + } + } + } +} |