• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
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
7  *
8  *      http://www.eclipse.org/org/documents/epl-v10.php
9  *
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.
15  */
16 
17 package com.android.ide.common.layout;
18 
19 import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI;
20 import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_X;
21 import static com.android.ide.common.layout.LayoutConstants.ATTR_LAYOUT_Y;
22 import static com.android.ide.common.layout.LayoutConstants.VALUE_N_DP;
23 
24 import com.android.ide.common.api.DrawingStyle;
25 import com.android.ide.common.api.DropFeedback;
26 import com.android.ide.common.api.IDragElement;
27 import com.android.ide.common.api.IFeedbackPainter;
28 import com.android.ide.common.api.IGraphics;
29 import com.android.ide.common.api.INode;
30 import com.android.ide.common.api.INodeHandler;
31 import com.android.ide.common.api.IViewRule;
32 import com.android.ide.common.api.Point;
33 import com.android.ide.common.api.Rect;
34 import com.android.ide.common.api.SegmentType;
35 import com.android.util.Pair;
36 
37 import java.util.ArrayList;
38 import java.util.List;
39 import java.util.Map;
40 
41 /**
42  * An {@link IViewRule} for android.widget.AbsoluteLayout and all its derived
43  * classes.
44  */
45 public class AbsoluteLayoutRule extends BaseLayoutRule {
46 
47     @Override
getSelectionHint(INode parentNode, INode childNode)48     public List<String> getSelectionHint(INode parentNode, INode childNode) {
49         List<String> infos = new ArrayList<String>(2);
50         infos.add("AbsoluteLayout is deprecated.");
51         infos.add("Use other layouts instead.");
52         return infos;
53     }
54 
55     // ==== Drag'n'drop support ====
56     // The AbsoluteLayout accepts any drag'n'drop anywhere on its surface.
57 
58     @Override
onDropEnter(INode targetNode, Object targetView, final IDragElement[] elements)59     public DropFeedback onDropEnter(INode targetNode, Object targetView,
60             final IDragElement[] elements) {
61 
62         if (elements.length == 0) {
63             return null;
64         }
65 
66         DropFeedback df = new DropFeedback(null, new IFeedbackPainter() {
67             @Override
68             public void paint(IGraphics gc, INode node, DropFeedback feedback) {
69                 // Paint callback for the AbsoluteLayout.
70                 // This is called by the canvas when a draw is needed.
71                 drawFeedback(gc, node, elements, feedback);
72             }
73         });
74         df.errorMessage = "AbsoluteLayout is deprecated.";
75         return df;
76     }
77 
drawFeedback( IGraphics gc, INode targetNode, IDragElement[] elements, DropFeedback feedback)78     void drawFeedback(
79             IGraphics gc,
80             INode targetNode,
81             IDragElement[] elements,
82             DropFeedback feedback) {
83         Rect b = targetNode.getBounds();
84         if (!b.isValid()) {
85             return;
86         }
87 
88         // Highlight the receiver
89         gc.useStyle(DrawingStyle.DROP_RECIPIENT);
90         gc.drawRect(b);
91 
92         // Get the drop point
93         Point p = (Point) feedback.userData;
94 
95         if (p == null) {
96             return;
97         }
98 
99         int x = p.x;
100         int y = p.y;
101 
102         Rect be = elements[0].getBounds();
103 
104         if (be.isValid()) {
105             // At least the first element has a bound. Draw rectangles
106             // for all dropped elements with valid bounds, offset at
107             // the drop point.
108             int offsetX = x - be.x + (feedback.dragBounds != null ? feedback.dragBounds.x : 0);
109             int offsetY = y - be.y + (feedback.dragBounds != null ? feedback.dragBounds.y : 0);
110             gc.useStyle(DrawingStyle.DROP_PREVIEW);
111             for (IDragElement element : elements) {
112                 drawElement(gc, element, offsetX, offsetY);
113             }
114         } else {
115             // We don't have bounds for new elements. In this case
116             // just draw cross hairs to the drop point.
117             gc.useStyle(DrawingStyle.GUIDELINE);
118             gc.drawLine(x, b.y, x, b.y + b.h);
119             gc.drawLine(b.x, y, b.x + b.w, y);
120 
121             // Use preview lines to indicate the bottom quadrant as well (to
122             // indicate that you are looking at the top left position of the
123             // drop, not the center for example)
124             gc.useStyle(DrawingStyle.DROP_PREVIEW);
125             gc.drawLine(x, y, b.x + b.w, y);
126             gc.drawLine(x, y, x, b.y + b.h);
127         }
128     }
129 
130     @Override
onDropMove(INode targetNode, IDragElement[] elements, DropFeedback feedback, Point p)131     public DropFeedback onDropMove(INode targetNode, IDragElement[] elements,
132             DropFeedback feedback, Point p) {
133         // Update the data used by the DropFeedback.paintCallback above.
134         feedback.userData = p;
135         feedback.requestPaint = true;
136 
137         return feedback;
138     }
139 
140     @Override
onDropLeave(INode targetNode, IDragElement[] elements, DropFeedback feedback)141     public void onDropLeave(INode targetNode, IDragElement[] elements, DropFeedback feedback) {
142         // Nothing to do.
143     }
144 
145     @Override
onDropped(final INode targetNode, final IDragElement[] elements, final DropFeedback feedback, final Point p)146     public void onDropped(final INode targetNode, final IDragElement[] elements,
147             final DropFeedback feedback, final Point p) {
148 
149         final Rect b = targetNode.getBounds();
150         if (!b.isValid()) {
151             return;
152         }
153 
154         // Collect IDs from dropped elements and remap them to new IDs
155         // if this is a copy or from a different canvas.
156         final Map<String, Pair<String, String>> idMap = getDropIdMap(targetNode, elements,
157                 feedback.isCopy || !feedback.sameCanvas);
158 
159         targetNode.editXml("Add elements to AbsoluteLayout", new INodeHandler() {
160             @Override
161             public void handle(INode node) {
162                 boolean first = true;
163                 Point offset = null;
164 
165                 // Now write the new elements.
166                 for (IDragElement element : elements) {
167                     String fqcn = element.getFqcn();
168                     Rect be = element.getBounds();
169 
170                     INode newChild = targetNode.appendChild(fqcn);
171 
172                     // Copy all the attributes, modifying them as needed.
173                     addAttributes(newChild, element, idMap, DEFAULT_ATTR_FILTER);
174 
175                     int deltaX = (feedback.dragBounds != null ? feedback.dragBounds.x : 0);
176                     int deltaY = (feedback.dragBounds != null ? feedback.dragBounds.y : 0);
177 
178                     int x = p.x - b.x + deltaX;
179                     int y = p.y - b.y + deltaY;
180 
181                     if (first) {
182                         first = false;
183                         if (be.isValid()) {
184                             offset = new Point(x - be.x, y - be.y);
185                         }
186                     } else if (offset != null && be.isValid()) {
187                         x = offset.x + be.x;
188                         y = offset.y + be.y;
189                     } else {
190                         x += 10;
191                         y += be.isValid() ? be.h : 10;
192                     }
193 
194                     double scale = feedback.dipScale;
195                     if (scale != 1.0) {
196                         x *= scale;
197                         y *= scale;
198                     }
199 
200                     newChild.setAttribute(ANDROID_URI, ATTR_LAYOUT_X,
201                             String.format(VALUE_N_DP, x));
202                     newChild.setAttribute(ANDROID_URI, ATTR_LAYOUT_Y,
203                             String.format(VALUE_N_DP, y));
204 
205                     addInnerElements(newChild, element, idMap);
206                 }
207             }
208         });
209     }
210 
211     /**
212      * {@inheritDoc}
213      * <p>
214      * Overridden in this layout in order to let the top left coordinate be affected by
215      * the resize operation too. In other words, dragging the top left corner to resize a
216      * widget will not only change the size of the widget, it will also move it (though in
217      * this case, the bottom right corner will stay fixed).
218      */
219     @Override
setNewSizeBounds(ResizeState resizeState, INode node, INode layout, Rect previousBounds, Rect newBounds, SegmentType horizontalEdge, SegmentType verticalEdge)220     protected void setNewSizeBounds(ResizeState resizeState, INode node, INode layout,
221             Rect previousBounds, Rect newBounds, SegmentType horizontalEdge,
222             SegmentType verticalEdge) {
223         super.setNewSizeBounds(resizeState, node, layout, previousBounds, newBounds,
224                 horizontalEdge, verticalEdge);
225         if (verticalEdge != null && newBounds.x != previousBounds.x) {
226             node.setAttribute(ANDROID_URI, ATTR_LAYOUT_X,
227                     String.format(VALUE_N_DP,
228                             mRulesEngine.pxToDp(newBounds.x - node.getParent().getBounds().x)));
229         }
230         if (horizontalEdge != null && newBounds.y != previousBounds.y) {
231             node.setAttribute(ANDROID_URI, ATTR_LAYOUT_Y,
232                     String.format(VALUE_N_DP,
233                             mRulesEngine.pxToDp(newBounds.y - node.getParent().getBounds().y)));
234         }
235     }
236 
237     @Override
getResizeUpdateMessage(ResizeState resizeState, INode child, INode parent, Rect newBounds, SegmentType horizontalEdge, SegmentType verticalEdge)238     protected String getResizeUpdateMessage(ResizeState resizeState, INode child, INode parent,
239             Rect newBounds, SegmentType horizontalEdge, SegmentType verticalEdge) {
240         Rect parentBounds = parent.getBounds();
241         if (horizontalEdge == SegmentType.BOTTOM && verticalEdge == SegmentType.RIGHT) {
242             return super.getResizeUpdateMessage(resizeState, child, parent, newBounds,
243                     horizontalEdge, verticalEdge);
244         }
245         return String.format("x=%d, y=%d\nwidth=%s, height=%s",
246                 mRulesEngine.pxToDp(newBounds.x - parentBounds.x),
247                 mRulesEngine.pxToDp(newBounds.y - parentBounds.y),
248                 resizeState.getWidthAttribute(), resizeState.getHeightAttribute());
249     }
250 }
251