• 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             public void paint(IGraphics gc, INode node, DropFeedback feedback) {
68                 // Paint callback for the AbsoluteLayout.
69                 // This is called by the canvas when a draw is needed.
70                 drawFeedback(gc, node, elements, feedback);
71             }
72         });
73         df.errorMessage = "AbsoluteLayout is deprecated.";
74         return df;
75     }
76 
drawFeedback( IGraphics gc, INode targetNode, IDragElement[] elements, DropFeedback feedback)77     void drawFeedback(
78             IGraphics gc,
79             INode targetNode,
80             IDragElement[] elements,
81             DropFeedback feedback) {
82         Rect b = targetNode.getBounds();
83         if (!b.isValid()) {
84             return;
85         }
86 
87         // Highlight the receiver
88         gc.useStyle(DrawingStyle.DROP_RECIPIENT);
89         gc.drawRect(b);
90 
91         // Get the drop point
92         Point p = (Point) feedback.userData;
93 
94         if (p == null) {
95             return;
96         }
97 
98         int x = p.x;
99         int y = p.y;
100 
101         Rect be = elements[0].getBounds();
102 
103         if (be.isValid()) {
104             // At least the first element has a bound. Draw rectangles
105             // for all dropped elements with valid bounds, offset at
106             // the drop point.
107             int offsetX = x - be.x + (feedback.dragBounds != null ? feedback.dragBounds.x : 0);
108             int offsetY = y - be.y + (feedback.dragBounds != null ? feedback.dragBounds.y : 0);
109             gc.useStyle(DrawingStyle.DROP_PREVIEW);
110             for (IDragElement element : elements) {
111                 drawElement(gc, element, offsetX, offsetY);
112             }
113         } else {
114             // We don't have bounds for new elements. In this case
115             // just draw cross hairs to the drop point.
116             gc.useStyle(DrawingStyle.GUIDELINE);
117             gc.drawLine(x, b.y, x, b.y + b.h);
118             gc.drawLine(b.x, y, b.x + b.w, y);
119 
120             // Use preview lines to indicate the bottom quadrant as well (to
121             // indicate that you are looking at the top left position of the
122             // drop, not the center for example)
123             gc.useStyle(DrawingStyle.DROP_PREVIEW);
124             gc.drawLine(x, y, b.x + b.w, y);
125             gc.drawLine(x, y, x, b.y + b.h);
126         }
127     }
128 
129     @Override
onDropMove(INode targetNode, IDragElement[] elements, DropFeedback feedback, Point p)130     public DropFeedback onDropMove(INode targetNode, IDragElement[] elements,
131             DropFeedback feedback, Point p) {
132         // Update the data used by the DropFeedback.paintCallback above.
133         feedback.userData = p;
134         feedback.requestPaint = true;
135 
136         return feedback;
137     }
138 
139     @Override
onDropLeave(INode targetNode, IDragElement[] elements, DropFeedback feedback)140     public void onDropLeave(INode targetNode, IDragElement[] elements, DropFeedback feedback) {
141         // Nothing to do.
142     }
143 
144     @Override
onDropped(final INode targetNode, final IDragElement[] elements, final DropFeedback feedback, final Point p)145     public void onDropped(final INode targetNode, final IDragElement[] elements,
146             final DropFeedback feedback, final Point p) {
147 
148         final Rect b = targetNode.getBounds();
149         if (!b.isValid()) {
150             return;
151         }
152 
153         // Collect IDs from dropped elements and remap them to new IDs
154         // if this is a copy or from a different canvas.
155         final Map<String, Pair<String, String>> idMap = getDropIdMap(targetNode, elements,
156                 feedback.isCopy || !feedback.sameCanvas);
157 
158         targetNode.editXml("Add elements to AbsoluteLayout", new INodeHandler() {
159             public void handle(INode node) {
160                 boolean first = true;
161                 Point offset = null;
162 
163                 // Now write the new elements.
164                 for (IDragElement element : elements) {
165                     String fqcn = element.getFqcn();
166                     Rect be = element.getBounds();
167 
168                     INode newChild = targetNode.appendChild(fqcn);
169 
170                     // Copy all the attributes, modifying them as needed.
171                     addAttributes(newChild, element, idMap, DEFAULT_ATTR_FILTER);
172 
173                     int deltaX = (feedback.dragBounds != null ? feedback.dragBounds.x : 0);
174                     int deltaY = (feedback.dragBounds != null ? feedback.dragBounds.y : 0);
175 
176                     int x = p.x - b.x + deltaX;
177                     int y = p.y - b.y + deltaY;
178 
179                     if (first) {
180                         first = false;
181                         if (be.isValid()) {
182                             offset = new Point(x - be.x, y - be.y);
183                         }
184                     } else if (offset != null && be.isValid()) {
185                         x = offset.x + be.x;
186                         y = offset.y + be.y;
187                     } else {
188                         x += 10;
189                         y += be.isValid() ? be.h : 10;
190                     }
191 
192                     double scale = feedback.dipScale;
193                     if (scale != 1.0) {
194                         x *= scale;
195                         y *= scale;
196                     }
197 
198                     newChild.setAttribute(ANDROID_URI, ATTR_LAYOUT_X,
199                             String.format(VALUE_N_DP, x));
200                     newChild.setAttribute(ANDROID_URI, ATTR_LAYOUT_Y,
201                             String.format(VALUE_N_DP, y));
202 
203                     addInnerElements(newChild, element, idMap);
204                 }
205             }
206         });
207     }
208 
209     /**
210      * {@inheritDoc}
211      * <p>
212      * Overridden in this layout in order to let the top left coordinate be affected by
213      * the resize operation too. In other words, dragging the top left corner to resize a
214      * widget will not only change the size of the widget, it will also move it (though in
215      * this case, the bottom right corner will stay fixed).
216      */
217     @Override
setNewSizeBounds(ResizeState resizeState, INode node, INode layout, Rect previousBounds, Rect newBounds, SegmentType horizontalEdge, SegmentType verticalEdge)218     protected void setNewSizeBounds(ResizeState resizeState, INode node, INode layout,
219             Rect previousBounds, Rect newBounds, SegmentType horizontalEdge,
220             SegmentType verticalEdge) {
221         super.setNewSizeBounds(resizeState, node, layout, previousBounds, newBounds,
222                 horizontalEdge, verticalEdge);
223         if (verticalEdge != null && newBounds.x != previousBounds.x) {
224             node.setAttribute(ANDROID_URI, ATTR_LAYOUT_X,
225                     String.format(VALUE_N_DP,
226                             mRulesEngine.pxToDp(newBounds.x - node.getParent().getBounds().x)));
227         }
228         if (horizontalEdge != null && newBounds.y != previousBounds.y) {
229             node.setAttribute(ANDROID_URI, ATTR_LAYOUT_Y,
230                     String.format(VALUE_N_DP,
231                             mRulesEngine.pxToDp(newBounds.y - node.getParent().getBounds().y)));
232         }
233     }
234 
235     @Override
getResizeUpdateMessage(ResizeState resizeState, INode child, INode parent, Rect newBounds, SegmentType horizontalEdge, SegmentType verticalEdge)236     protected String getResizeUpdateMessage(ResizeState resizeState, INode child, INode parent,
237             Rect newBounds, SegmentType horizontalEdge, SegmentType verticalEdge) {
238         Rect parentBounds = parent.getBounds();
239         if (horizontalEdge == SegmentType.BOTTOM && verticalEdge == SegmentType.RIGHT) {
240             return super.getResizeUpdateMessage(resizeState, child, parent, newBounds,
241                     horizontalEdge, verticalEdge);
242         }
243         return String.format("x=%d, y=%d\nwidth=%s, height=%s",
244                 mRulesEngine.pxToDp(newBounds.x - parentBounds.x),
245                 mRulesEngine.pxToDp(newBounds.y - parentBounds.y),
246                 resizeState.getWidthAttribute(), resizeState.getHeightAttribute());
247     }
248 }
249