aboutsummaryrefslogtreecommitdiff
path: root/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/editors/layout/gle2/ResizeGesture.java
diff options
context:
space:
mode:
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.java279
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);
+ }
+ }
+ }
+}