2 * Copyright (C) 2011 The Android Open Source Project
4 * Licensed under the Eclipse Public License, Version 1.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.eclipse.org/org/documents/epl-v10.php
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.ide.eclipse.adt.internal.editors.layout.gle2;
19 import com.android.ide.common.api.DropFeedback;
20 import com.android.ide.common.api.Rect;
21 import com.android.ide.common.api.ResizePolicy;
22 import com.android.ide.common.api.SegmentType;
23 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.SelectionHandle.Position;
24 import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy;
25 import com.android.ide.eclipse.adt.internal.editors.layout.gre.RulesEngine;
26 import com.android.util.Pair;
28 import org.eclipse.swt.events.KeyEvent;
29 import org.eclipse.swt.graphics.GC;
31 import java.util.Collections;
32 import java.util.List;
35 * A {@link ResizeGesture} is a gesture for resizing a selected widget. It is initiated
36 * by a drag of a {@link SelectionHandle}.
38 public class ResizeGesture extends Gesture {
39 /** The {@link Overlay} drawn for the gesture feedback. */
40 private ResizeOverlay mOverlay;
42 /** The canvas associated with this gesture. */
43 private LayoutCanvas mCanvas;
45 /** The selection handle we're dragging to perform this resize */
46 private SelectionHandle mHandle;
48 private NodeProxy mParentNode;
49 private NodeProxy mChildNode;
50 private DropFeedback mFeedback;
51 private ResizePolicy mResizePolicy;
52 private SegmentType mHorizontalEdge;
53 private SegmentType mVerticalEdge;
56 * Creates a new marquee selection (selection swiping).
58 * @param canvas The canvas where selection is performed.
59 * @param item The selected item the handle corresponds to
60 * @param handle The handle being dragged to perform the resize
62 public ResizeGesture(LayoutCanvas canvas, SelectionItem item, SelectionHandle handle) {
66 mChildNode = item.getNode();
67 mParentNode = (NodeProxy) mChildNode.getParent();
68 mResizePolicy = item.getResizePolicy();
69 mHorizontalEdge = getHorizontalEdgeType(mHandle);
70 mVerticalEdge = getVerticalEdgeType(mHandle);
74 public void begin(ControlPoint pos, int startMask) {
75 super.begin(pos, startMask);
77 mCanvas.getSelectionOverlay().setHidden(true);
79 RulesEngine rulesEngine = mCanvas.getRulesEngine();
80 Rect newBounds = getNewBounds(pos);
81 mFeedback = rulesEngine.callOnResizeBegin(mChildNode, mParentNode, newBounds,
82 mHorizontalEdge, mVerticalEdge);
83 mCanvas.getGestureManager().updateMessage(mFeedback);
87 public boolean keyPressed(KeyEvent event) {
88 update(mCanvas.getGestureManager().getCurrentControlPoint());
94 public boolean keyReleased(KeyEvent event) {
95 update(mCanvas.getGestureManager().getCurrentControlPoint());
101 public void update(ControlPoint pos) {
103 RulesEngine rulesEngine = mCanvas.getRulesEngine();
104 Rect newBounds = getNewBounds(pos);
105 int modifierMask = mCanvas.getGestureManager().getRuleModifierMask();
106 rulesEngine.callOnResizeUpdate(mFeedback, mChildNode, mParentNode, newBounds,
108 mCanvas.getGestureManager().updateMessage(mFeedback);
112 public void end(ControlPoint pos, boolean canceled) {
113 super.end(pos, canceled);
116 RulesEngine rulesEngine = mCanvas.getRulesEngine();
117 Rect newBounds = getNewBounds(pos);
118 rulesEngine.callOnResizeEnd(mFeedback, mChildNode, mParentNode, newBounds);
121 mCanvas.getSelectionOverlay().setHidden(false);
125 public Pair<Boolean, Boolean> getTooltipPosition() {
126 return Pair.of(mHorizontalEdge != SegmentType.TOP, mVerticalEdge != SegmentType.LEFT);
130 * For the new mouse position, compute the resized bounds (the bounding rectangle that
131 * the view should be resized to). This is not just a width or height, since in some
132 * cases resizing will change the x/y position of the view as well (for example, in
133 * RelativeLayout or in AbsoluteLayout).
135 private Rect getNewBounds(ControlPoint pos) {
136 LayoutPoint p = pos.toLayout();
137 LayoutPoint start = mStart.toLayout();
138 Rect b = mChildNode.getBounds();
139 Position direction = mHandle.getPosition();
145 int deltaX = p.x - start.x;
146 int deltaY = p.y - start.y;
148 if (deltaX == 0 && deltaY == 0) {
149 // No move - just use the existing bounds
153 if (mResizePolicy.isAspectPreserving() && w != 0 && h != 0) {
154 double aspectRatio = w / (double) h;
155 int newW = Math.abs(b.w + (direction.isLeft() ? -deltaX : deltaX));
156 int newH = Math.abs(b.h + (direction.isTop() ? -deltaY : deltaY));
157 double newAspectRatio = newW / (double) newH;
158 if (newH == 0 || newAspectRatio > aspectRatio) {
159 deltaY = (int) (deltaX / aspectRatio);
161 deltaX = (int) (deltaY * aspectRatio);
164 if (direction.isLeft()) {
165 // The user is dragging the left edge, so the position is anchored on the
168 int nx1 = b.x + deltaX;
176 } else if (direction.isRight()) {
177 // The user is dragging the right edge, so the position is anchored on the
179 int nx2 = b.x + b.w + deltaX;
186 assert direction == Position.BOTTOM_MIDDLE || direction == Position.TOP_MIDDLE;
189 if (direction.isTop()) {
190 // The user is dragging the top edge, so the position is anchored on the
193 int ny1 = b.y + deltaY;
201 } else if (direction.isBottom()) {
202 // The user is dragging the bottom edge, so the position is anchored on the
204 int ny2 = b.y + b.h + deltaY;
211 assert direction == Position.LEFT_MIDDLE || direction == Position.RIGHT_MIDDLE;
214 return new Rect(x, y, w, h);
217 private static SegmentType getHorizontalEdgeType(SelectionHandle handle) {
218 switch (handle.getPosition()) {
222 return SegmentType.BOTTOM;
229 return SegmentType.TOP;
230 default: assert false : handle.getPosition();
235 private static SegmentType getVerticalEdgeType(SelectionHandle handle) {
236 switch (handle.getPosition()) {
240 return SegmentType.LEFT;
247 return SegmentType.RIGHT;
248 default: assert false : handle.getPosition();
255 public List<Overlay> createOverlays() {
256 mOverlay = new ResizeOverlay();
257 return Collections.<Overlay> singletonList(mOverlay);
261 * An {@link Overlay} to paint the resize feedback. This just delegates to the
262 * layout rule for the parent which is handling the resizing.
264 private class ResizeOverlay extends Overlay {
266 public void paint(GC gc) {
267 if (mChildNode != null && mFeedback != null) {
268 RulesEngine rulesEngine = mCanvas.getRulesEngine();
269 rulesEngine.callDropFeedbackPaint(mCanvas.getGcWrapper(), mChildNode, mFeedback);